From c5c66ac8f2b680613ab19fd3c2bff1e127322853 Mon Sep 17 00:00:00 2001 From: Brett Bieber <brett.bieber@gmail.com> Date: Thu, 15 Apr 2010 14:18:29 +0000 Subject: [PATCH] Add htmlpurifier & unl_autoload --- .../configsnapshot-2010-04-15 09-10-17.xml | 2 + lib/.pear2registry | Bin 0 -> 266240 bytes lib/.xmlregistry/channels/channel-__uri.xml | 13 + .../channels/channel-doc.php.net.xml | 14 + .../channels/channel-htmlpurifier.org.xml | 14 + .../channels/channel-pear.php.net.xml | 32 + .../channels/channel-pear.unl.edu.xml | 15 + .../channels/channel-pear2.php.net.xml | 16 + .../channels/channel-pecl.php.net.xml | 25 + .../channels/channelalias-__uri.txt | 1 + lib/.xmlregistry/channels/channelalias-hp.txt | 1 + .../channels/channelalias-pear.txt | 1 + .../channels/channelalias-pear2.txt | 1 + .../channels/channelalias-pecl.txt | 1 + .../channels/channelalias-phpdocs.txt | 1 + .../channels/channelalias-unl.txt | 1 + .../HTMLPurifier/4.0.0-info.xml | 396 ++ .../pear.unl.edu/UNL_Autoload/0.5.0-info.xml | 58 + lib/downloads/HTMLPurifier-4.0.0.tgz | Bin 0 -> 252757 bytes lib/downloads/UNL_Autoload-0.5.0.tgz | Bin 0 -> 1716 bytes lib/php/HTMLPurifier.auto.php | 11 + lib/php/HTMLPurifier.autoload.php | 21 + lib/php/HTMLPurifier.func.php | 23 + lib/php/HTMLPurifier.includes.php | 208 + lib/php/HTMLPurifier.kses.php | 30 + lib/php/HTMLPurifier.php | 237 + lib/php/HTMLPurifier.safe-includes.php | 202 + lib/php/HTMLPurifier/AttrCollections.php | 128 + lib/php/HTMLPurifier/AttrDef.php | 87 + lib/php/HTMLPurifier/AttrDef/CSS.php | 87 + .../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 21 + .../HTMLPurifier/AttrDef/CSS/Background.php | 87 + .../AttrDef/CSS/BackgroundPosition.php | 126 + lib/php/HTMLPurifier/AttrDef/CSS/Border.php | 43 + lib/php/HTMLPurifier/AttrDef/CSS/Color.php | 78 + .../HTMLPurifier/AttrDef/CSS/Composite.php | 38 + .../AttrDef/CSS/DenyElementDecorator.php | 28 + lib/php/HTMLPurifier/AttrDef/CSS/Filter.php | 54 + lib/php/HTMLPurifier/AttrDef/CSS/Font.php | 149 + .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 90 + .../AttrDef/CSS/ImportantDecorator.php | 40 + lib/php/HTMLPurifier/AttrDef/CSS/Length.php | 47 + .../HTMLPurifier/AttrDef/CSS/ListStyle.php | 78 + lib/php/HTMLPurifier/AttrDef/CSS/Multiple.php | 58 + lib/php/HTMLPurifier/AttrDef/CSS/Number.php | 69 + .../HTMLPurifier/AttrDef/CSS/Percentage.php | 40 + .../AttrDef/CSS/TextDecoration.php | 38 + lib/php/HTMLPurifier/AttrDef/CSS/URI.php | 56 + lib/php/HTMLPurifier/AttrDef/Enum.php | 65 + lib/php/HTMLPurifier/AttrDef/HTML/Bool.php | 28 + lib/php/HTMLPurifier/AttrDef/HTML/Class.php | 34 + lib/php/HTMLPurifier/AttrDef/HTML/Color.php | 32 + .../HTMLPurifier/AttrDef/HTML/FrameTarget.php | 21 + lib/php/HTMLPurifier/AttrDef/HTML/ID.php | 70 + lib/php/HTMLPurifier/AttrDef/HTML/Length.php | 41 + .../HTMLPurifier/AttrDef/HTML/LinkTypes.php | 53 + .../HTMLPurifier/AttrDef/HTML/MultiLength.php | 41 + .../HTMLPurifier/AttrDef/HTML/Nmtokens.php | 52 + lib/php/HTMLPurifier/AttrDef/HTML/Pixels.php | 48 + lib/php/HTMLPurifier/AttrDef/Integer.php | 73 + lib/php/HTMLPurifier/AttrDef/Lang.php | 73 + lib/php/HTMLPurifier/AttrDef/Switch.php | 34 + lib/php/HTMLPurifier/AttrDef/Text.php | 15 + lib/php/HTMLPurifier/AttrDef/URI.php | 77 + lib/php/HTMLPurifier/AttrDef/URI/Email.php | 17 + .../AttrDef/URI/Email/SimpleCheck.php | 21 + lib/php/HTMLPurifier/AttrDef/URI/Host.php | 62 + lib/php/HTMLPurifier/AttrDef/URI/IPv4.php | 39 + lib/php/HTMLPurifier/AttrDef/URI/IPv6.php | 99 + lib/php/HTMLPurifier/AttrTransform.php | 56 + .../HTMLPurifier/AttrTransform/Background.php | 23 + lib/php/HTMLPurifier/AttrTransform/BdoDir.php | 19 + .../HTMLPurifier/AttrTransform/BgColor.php | 23 + .../HTMLPurifier/AttrTransform/BoolToCSS.php | 36 + lib/php/HTMLPurifier/AttrTransform/Border.php | 18 + .../HTMLPurifier/AttrTransform/EnumToCSS.php | 58 + .../AttrTransform/ImgRequired.php | 42 + .../HTMLPurifier/AttrTransform/ImgSpace.php | 44 + lib/php/HTMLPurifier/AttrTransform/Input.php | 40 + lib/php/HTMLPurifier/AttrTransform/Lang.php | 28 + lib/php/HTMLPurifier/AttrTransform/Length.php | 27 + lib/php/HTMLPurifier/AttrTransform/Name.php | 21 + .../HTMLPurifier/AttrTransform/NameSync.php | 27 + .../HTMLPurifier/AttrTransform/SafeEmbed.php | 15 + .../HTMLPurifier/AttrTransform/SafeObject.php | 16 + .../HTMLPurifier/AttrTransform/SafeParam.php | 50 + .../AttrTransform/ScriptRequired.php | 16 + .../HTMLPurifier/AttrTransform/Textarea.php | 18 + lib/php/HTMLPurifier/AttrTypes.php | 77 + lib/php/HTMLPurifier/AttrValidator.php | 162 + lib/php/HTMLPurifier/Bootstrap.php | 98 + lib/php/HTMLPurifier/CSSDefinition.php | 292 ++ lib/php/HTMLPurifier/ChildDef.php | 48 + lib/php/HTMLPurifier/ChildDef/Chameleon.php | 48 + lib/php/HTMLPurifier/ChildDef/Custom.php | 90 + lib/php/HTMLPurifier/ChildDef/Empty.php | 20 + lib/php/HTMLPurifier/ChildDef/Optional.php | 26 + lib/php/HTMLPurifier/ChildDef/Required.php | 117 + .../ChildDef/StrictBlockquote.php | 88 + lib/php/HTMLPurifier/ChildDef/Table.php | 142 + lib/php/HTMLPurifier/Config.php | 580 +++ lib/php/HTMLPurifier/ConfigSchema.php | 158 + .../ConfigSchema/Builder/ConfigSchema.php | 44 + .../HTMLPurifier/ConfigSchema/Builder/Xml.php | 106 + .../HTMLPurifier/ConfigSchema/Exception.php | 11 + .../HTMLPurifier/ConfigSchema/Interchange.php | 42 + .../ConfigSchema/Interchange/Directive.php | 77 + .../ConfigSchema/Interchange/Id.php | 37 + .../ConfigSchema/InterchangeBuilder.php | 180 + .../HTMLPurifier/ConfigSchema/Validator.php | 206 + .../ConfigSchema/ValidatorAtom.php | 66 + lib/php/HTMLPurifier/ConfigSchema/schema.ser | Bin 0 -> 12999 bytes .../schema/Attr.AllowedClasses.txt | 8 + .../schema/Attr.AllowedFrameTargets.txt | 12 + .../ConfigSchema/schema/Attr.AllowedRel.txt | 9 + .../ConfigSchema/schema/Attr.AllowedRev.txt | 9 + .../schema/Attr.ClassUseCDATA.txt | 19 + .../schema/Attr.DefaultImageAlt.txt | 11 + .../schema/Attr.DefaultInvalidImage.txt | 9 + .../schema/Attr.DefaultInvalidImageAlt.txt | 8 + .../schema/Attr.DefaultTextDir.txt | 10 + .../ConfigSchema/schema/Attr.EnableID.txt | 16 + .../schema/Attr.ForbiddenClasses.txt | 8 + .../ConfigSchema/schema/Attr.IDBlacklist.txt | 5 + .../schema/Attr.IDBlacklistRegexp.txt | 9 + .../ConfigSchema/schema/Attr.IDPrefix.txt | 12 + .../schema/Attr.IDPrefixLocal.txt | 14 + .../schema/AutoFormat.AutoParagraph.txt | 31 + .../ConfigSchema/schema/AutoFormat.Custom.txt | 12 + .../schema/AutoFormat.DisplayLinkURI.txt | 11 + .../schema/AutoFormat.Linkify.txt | 12 + .../AutoFormat.PurifierLinkify.DocURL.txt | 12 + .../schema/AutoFormat.PurifierLinkify.txt | 12 + ...rmat.RemoveEmpty.RemoveNbsp.Exceptions.txt | 11 + .../AutoFormat.RemoveEmpty.RemoveNbsp.txt | 15 + .../schema/AutoFormat.RemoveEmpty.txt | 46 + .../schema/CSS.AllowImportant.txt | 8 + .../ConfigSchema/schema/CSS.AllowTricky.txt | 11 + .../schema/CSS.AllowedProperties.txt | 18 + .../ConfigSchema/schema/CSS.DefinitionRev.txt | 11 + .../ConfigSchema/schema/CSS.MaxImgLength.txt | 16 + .../ConfigSchema/schema/CSS.Proprietary.txt | 10 + .../schema/Cache.DefinitionImpl.txt | 14 + .../schema/Cache.SerializerPath.txt | 13 + .../schema/Core.AggressivelyFixLt.txt | 18 + .../schema/Core.CollectErrors.txt | 12 + .../schema/Core.ColorKeywords.txt | 28 + .../schema/Core.ConvertDocumentToFragment.txt | 14 + .../Core.DirectLexLineNumberSyncInterval.txt | 17 + .../ConfigSchema/schema/Core.Encoding.txt | 15 + .../schema/Core.EscapeInvalidChildren.txt | 10 + .../schema/Core.EscapeInvalidTags.txt | 7 + .../schema/Core.EscapeNonASCIICharacters.txt | 13 + .../schema/Core.HiddenElements.txt | 19 + .../ConfigSchema/schema/Core.Language.txt | 10 + .../ConfigSchema/schema/Core.LexerImpl.txt | 34 + .../schema/Core.MaintainLineNumbers.txt | 16 + .../schema/Core.RemoveInvalidImg.txt | 12 + .../schema/Core.RemoveScriptContents.txt | 12 + .../ConfigSchema/schema/Filter.Custom.txt | 11 + .../Filter.ExtractStyleBlocks.Escaping.txt | 14 + .../Filter.ExtractStyleBlocks.Scope.txt | 29 + .../Filter.ExtractStyleBlocks.TidyImpl.txt | 16 + .../schema/Filter.ExtractStyleBlocks.txt | 74 + .../ConfigSchema/schema/Filter.YouTube.txt | 11 + .../ConfigSchema/schema/HTML.Allowed.txt | 22 + .../schema/HTML.AllowedAttributes.txt | 19 + .../schema/HTML.AllowedElements.txt | 18 + .../schema/HTML.AllowedModules.txt | 20 + .../schema/HTML.Attr.Name.UseCDATA.txt | 11 + .../ConfigSchema/schema/HTML.BlockWrapper.txt | 18 + .../ConfigSchema/schema/HTML.CoreModules.txt | 23 + .../schema/HTML.CustomDoctype.txt | 9 + .../ConfigSchema/schema/HTML.DefinitionID.txt | 33 + .../schema/HTML.DefinitionRev.txt | 16 + .../ConfigSchema/schema/HTML.Doctype.txt | 11 + .../schema/HTML.ForbiddenAttributes.txt | 21 + .../schema/HTML.ForbiddenElements.txt | 20 + .../ConfigSchema/schema/HTML.MaxImgLength.txt | 14 + .../ConfigSchema/schema/HTML.Parent.txt | 12 + .../ConfigSchema/schema/HTML.Proprietary.txt | 12 + .../ConfigSchema/schema/HTML.SafeEmbed.txt | 14 + .../ConfigSchema/schema/HTML.SafeObject.txt | 14 + .../ConfigSchema/schema/HTML.Strict.txt | 9 + .../ConfigSchema/schema/HTML.TidyAdd.txt | 8 + .../ConfigSchema/schema/HTML.TidyLevel.txt | 24 + .../ConfigSchema/schema/HTML.TidyRemove.txt | 8 + .../ConfigSchema/schema/HTML.Trusted.txt | 8 + .../ConfigSchema/schema/HTML.XHTML.txt | 11 + .../schema/Output.CommentScriptContents.txt | 10 + .../ConfigSchema/schema/Output.Newline.txt | 13 + .../ConfigSchema/schema/Output.SortAttr.txt | 14 + .../ConfigSchema/schema/Output.TidyFormat.txt | 25 + .../ConfigSchema/schema/Test.ForceNoIconv.txt | 7 + .../schema/URI.AllowedSchemes.txt | 15 + .../ConfigSchema/schema/URI.Base.txt | 17 + .../ConfigSchema/schema/URI.DefaultScheme.txt | 10 + .../ConfigSchema/schema/URI.DefinitionID.txt | 11 + .../ConfigSchema/schema/URI.DefinitionRev.txt | 11 + .../ConfigSchema/schema/URI.Disable.txt | 14 + .../schema/URI.DisableExternal.txt | 11 + .../schema/URI.DisableExternalResources.txt | 13 + .../schema/URI.DisableResources.txt | 12 + .../ConfigSchema/schema/URI.Host.txt | 19 + .../ConfigSchema/schema/URI.HostBlacklist.txt | 9 + .../ConfigSchema/schema/URI.MakeAbsolute.txt | 13 + .../ConfigSchema/schema/URI.Munge.txt | 83 + .../schema/URI.MungeResources.txt | 17 + .../schema/URI.MungeSecretKey.txt | 30 + .../schema/URI.OverrideAllowedSchemes.txt | 9 + .../HTMLPurifier/ConfigSchema/schema/info.ini | 3 + lib/php/HTMLPurifier/ContentSets.php | 155 + lib/php/HTMLPurifier/Context.php | 82 + lib/php/HTMLPurifier/Definition.php | 39 + lib/php/HTMLPurifier/DefinitionCache.php | 108 + .../DefinitionCache/Decorator.php | 62 + .../DefinitionCache/Decorator/Cleanup.php | 43 + .../DefinitionCache/Decorator/Memory.php | 46 + .../DefinitionCache/Decorator/Template.php.in | 47 + lib/php/HTMLPurifier/DefinitionCache/Null.php | 39 + .../DefinitionCache/Serializer.php | 172 + .../DefinitionCache/Serializer/README | 3 + .../HTMLPurifier/DefinitionCacheFactory.php | 91 + lib/php/HTMLPurifier/Doctype.php | 60 + lib/php/HTMLPurifier/DoctypeRegistry.php | 103 + lib/php/HTMLPurifier/ElementDef.php | 176 + lib/php/HTMLPurifier/Encoder.php | 426 ++ lib/php/HTMLPurifier/EntityLookup.php | 44 + .../HTMLPurifier/EntityLookup/entities.ser | 1 + lib/php/HTMLPurifier/EntityParser.php | 144 + lib/php/HTMLPurifier/ErrorCollector.php | 209 + lib/php/HTMLPurifier/ErrorStruct.php | 60 + lib/php/HTMLPurifier/Exception.php | 12 + lib/php/HTMLPurifier/Filter.php | 46 + .../Filter/ExtractStyleBlocks.php | 135 + lib/php/HTMLPurifier/Filter/YouTube.php | 39 + lib/php/HTMLPurifier/Generator.php | 183 + lib/php/HTMLPurifier/HTMLDefinition.php | 420 ++ lib/php/HTMLPurifier/HTMLModule.php | 244 + lib/php/HTMLPurifier/HTMLModule/Bdo.php | 31 + .../HTMLModule/CommonAttributes.php | 26 + lib/php/HTMLPurifier/HTMLModule/Edit.php | 38 + lib/php/HTMLPurifier/HTMLModule/Forms.php | 118 + lib/php/HTMLPurifier/HTMLModule/Hypertext.php | 31 + lib/php/HTMLPurifier/HTMLModule/Image.php | 40 + lib/php/HTMLPurifier/HTMLModule/Legacy.php | 143 + lib/php/HTMLPurifier/HTMLModule/List.php | 35 + lib/php/HTMLPurifier/HTMLModule/Name.php | 21 + .../HTMLModule/NonXMLCommonAttributes.php | 14 + lib/php/HTMLPurifier/HTMLModule/Object.php | 47 + .../HTMLPurifier/HTMLModule/Presentation.php | 36 + .../HTMLPurifier/HTMLModule/Proprietary.php | 33 + lib/php/HTMLPurifier/HTMLModule/Ruby.php | 27 + lib/php/HTMLPurifier/HTMLModule/SafeEmbed.php | 33 + .../HTMLPurifier/HTMLModule/SafeObject.php | 50 + lib/php/HTMLPurifier/HTMLModule/Scripting.php | 54 + .../HTMLModule/StyleAttribute.php | 24 + lib/php/HTMLPurifier/HTMLModule/Tables.php | 66 + lib/php/HTMLPurifier/HTMLModule/Target.php | 23 + lib/php/HTMLPurifier/HTMLModule/Text.php | 71 + lib/php/HTMLPurifier/HTMLModule/Tidy.php | 207 + lib/php/HTMLPurifier/HTMLModule/Tidy/Name.php | 24 + .../HTMLModule/Tidy/Proprietary.php | 23 + .../HTMLPurifier/HTMLModule/Tidy/Strict.php | 21 + .../HTMLModule/Tidy/Transitional.php | 9 + .../HTMLPurifier/HTMLModule/Tidy/XHTML.php | 17 + .../HTMLModule/Tidy/XHTMLAndHTML4.php | 161 + .../HTMLModule/XMLCommonAttributes.php | 14 + lib/php/HTMLPurifier/HTMLModuleManager.php | 403 ++ lib/php/HTMLPurifier/IDAccumulator.php | 53 + lib/php/HTMLPurifier/Injector.php | 239 + .../HTMLPurifier/Injector/AutoParagraph.php | 340 ++ .../HTMLPurifier/Injector/DisplayLinkURI.php | 26 + lib/php/HTMLPurifier/Injector/Linkify.php | 46 + .../HTMLPurifier/Injector/PurifierLinkify.php | 45 + lib/php/HTMLPurifier/Injector/RemoveEmpty.php | 51 + lib/php/HTMLPurifier/Injector/SafeObject.php | 87 + lib/php/HTMLPurifier/Language.php | 163 + .../Language/classes/en-x-test.php | 12 + .../Language/messages/en-x-test.php | 11 + .../Language/messages/en-x-testmini.php | 12 + lib/php/HTMLPurifier/Language/messages/en.php | 62 + lib/php/HTMLPurifier/LanguageFactory.php | 198 + lib/php/HTMLPurifier/Length.php | 115 + lib/php/HTMLPurifier/Lexer.php | 298 ++ lib/php/HTMLPurifier/Lexer/DOMLex.php | 213 + lib/php/HTMLPurifier/Lexer/DirectLex.php | 490 +++ lib/php/HTMLPurifier/Lexer/PEARSax3.php | 106 + lib/php/HTMLPurifier/Lexer/PH5P.php | 3906 +++++++++++++++++ lib/php/HTMLPurifier/PercentEncoder.php | 98 + lib/php/HTMLPurifier/Printer.php | 176 + .../HTMLPurifier/Printer/CSSDefinition.php | 38 + lib/php/HTMLPurifier/Printer/ConfigForm.css | 10 + lib/php/HTMLPurifier/Printer/ConfigForm.js | 5 + lib/php/HTMLPurifier/Printer/ConfigForm.php | 368 ++ .../HTMLPurifier/Printer/HTMLDefinition.php | 272 ++ lib/php/HTMLPurifier/PropertyList.php | 86 + lib/php/HTMLPurifier/PropertyListIterator.php | 32 + lib/php/HTMLPurifier/Strategy.php | 26 + lib/php/HTMLPurifier/Strategy/Composite.php | 25 + lib/php/HTMLPurifier/Strategy/Core.php | 18 + lib/php/HTMLPurifier/Strategy/FixNesting.php | 328 ++ .../HTMLPurifier/Strategy/MakeWellFormed.php | 457 ++ .../Strategy/RemoveForeignElements.php | 171 + .../Strategy/ValidateAttributes.php | 39 + lib/php/HTMLPurifier/StringHash.php | 39 + lib/php/HTMLPurifier/StringHashParser.php | 110 + lib/php/HTMLPurifier/TagTransform.php | 36 + lib/php/HTMLPurifier/TagTransform/Font.php | 96 + lib/php/HTMLPurifier/TagTransform/Simple.php | 35 + lib/php/HTMLPurifier/Token.php | 57 + lib/php/HTMLPurifier/Token/Comment.php | 22 + lib/php/HTMLPurifier/Token/Empty.php | 11 + lib/php/HTMLPurifier/Token/End.php | 19 + lib/php/HTMLPurifier/Token/Start.php | 11 + lib/php/HTMLPurifier/Token/Tag.php | 56 + lib/php/HTMLPurifier/Token/Text.php | 33 + lib/php/HTMLPurifier/TokenFactory.php | 94 + lib/php/HTMLPurifier/URI.php | 173 + lib/php/HTMLPurifier/URIDefinition.php | 93 + lib/php/HTMLPurifier/URIFilter.php | 45 + .../URIFilter/DisableExternal.php | 23 + .../URIFilter/DisableExternalResources.php | 12 + .../HTMLPurifier/URIFilter/HostBlacklist.php | 21 + .../HTMLPurifier/URIFilter/MakeAbsolute.php | 114 + lib/php/HTMLPurifier/URIFilter/Munge.php | 58 + lib/php/HTMLPurifier/URIParser.php | 70 + lib/php/HTMLPurifier/URIScheme.php | 42 + lib/php/HTMLPurifier/URIScheme/ftp.php | 43 + lib/php/HTMLPurifier/URIScheme/http.php | 20 + lib/php/HTMLPurifier/URIScheme/https.php | 12 + lib/php/HTMLPurifier/URIScheme/mailto.php | 27 + lib/php/HTMLPurifier/URIScheme/news.php | 22 + lib/php/HTMLPurifier/URIScheme/nntp.php | 20 + lib/php/HTMLPurifier/URISchemeRegistry.php | 68 + lib/php/HTMLPurifier/UnitConverter.php | 254 ++ lib/php/HTMLPurifier/VarParser.php | 154 + lib/php/HTMLPurifier/VarParser/Flexible.php | 96 + lib/php/HTMLPurifier/VarParser/Native.php | 26 + lib/php/HTMLPurifier/VarParserException.php | 11 + lib/php/HTMLPurifier/tags | 2972 +++++++++++++ lib/php/UNL/Autoload.php | 62 + 342 files changed, 27316 insertions(+) create mode 100644 lib/.configsnapshots/configsnapshot-2010-04-15 09-10-17.xml create mode 100644 lib/.pear2registry create mode 100644 lib/.xmlregistry/channels/channel-__uri.xml create mode 100644 lib/.xmlregistry/channels/channel-doc.php.net.xml create mode 100644 lib/.xmlregistry/channels/channel-htmlpurifier.org.xml create mode 100644 lib/.xmlregistry/channels/channel-pear.php.net.xml create mode 100644 lib/.xmlregistry/channels/channel-pear.unl.edu.xml create mode 100644 lib/.xmlregistry/channels/channel-pear2.php.net.xml create mode 100644 lib/.xmlregistry/channels/channel-pecl.php.net.xml create mode 100644 lib/.xmlregistry/channels/channelalias-__uri.txt create mode 100644 lib/.xmlregistry/channels/channelalias-hp.txt create mode 100644 lib/.xmlregistry/channels/channelalias-pear.txt create mode 100644 lib/.xmlregistry/channels/channelalias-pear2.txt create mode 100644 lib/.xmlregistry/channels/channelalias-pecl.txt create mode 100644 lib/.xmlregistry/channels/channelalias-phpdocs.txt create mode 100644 lib/.xmlregistry/channels/channelalias-unl.txt create mode 100644 lib/.xmlregistry/packages/htmlpurifier.org/HTMLPurifier/4.0.0-info.xml create mode 100644 lib/.xmlregistry/packages/pear.unl.edu/UNL_Autoload/0.5.0-info.xml create mode 100644 lib/downloads/HTMLPurifier-4.0.0.tgz create mode 100644 lib/downloads/UNL_Autoload-0.5.0.tgz create mode 100644 lib/php/HTMLPurifier.auto.php create mode 100644 lib/php/HTMLPurifier.autoload.php create mode 100644 lib/php/HTMLPurifier.func.php create mode 100644 lib/php/HTMLPurifier.includes.php create mode 100644 lib/php/HTMLPurifier.kses.php create mode 100644 lib/php/HTMLPurifier.php create mode 100644 lib/php/HTMLPurifier.safe-includes.php create mode 100644 lib/php/HTMLPurifier/AttrCollections.php create mode 100644 lib/php/HTMLPurifier/AttrDef.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/AlphaValue.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Background.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Border.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Color.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Composite.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Filter.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Font.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/FontFamily.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Length.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/ListStyle.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Multiple.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Number.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/Percentage.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/TextDecoration.php create mode 100644 lib/php/HTMLPurifier/AttrDef/CSS/URI.php create mode 100644 lib/php/HTMLPurifier/AttrDef/Enum.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/Bool.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/Class.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/Color.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/FrameTarget.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/ID.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/Length.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/LinkTypes.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/MultiLength.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/Nmtokens.php create mode 100644 lib/php/HTMLPurifier/AttrDef/HTML/Pixels.php create mode 100644 lib/php/HTMLPurifier/AttrDef/Integer.php create mode 100644 lib/php/HTMLPurifier/AttrDef/Lang.php create mode 100644 lib/php/HTMLPurifier/AttrDef/Switch.php create mode 100644 lib/php/HTMLPurifier/AttrDef/Text.php create mode 100644 lib/php/HTMLPurifier/AttrDef/URI.php create mode 100644 lib/php/HTMLPurifier/AttrDef/URI/Email.php create mode 100644 lib/php/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php create mode 100644 lib/php/HTMLPurifier/AttrDef/URI/Host.php create mode 100644 lib/php/HTMLPurifier/AttrDef/URI/IPv4.php create mode 100644 lib/php/HTMLPurifier/AttrDef/URI/IPv6.php create mode 100644 lib/php/HTMLPurifier/AttrTransform.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Background.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/BdoDir.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/BgColor.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/BoolToCSS.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Border.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/EnumToCSS.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/ImgRequired.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/ImgSpace.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Input.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Lang.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Length.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Name.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/NameSync.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/SafeEmbed.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/SafeObject.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/SafeParam.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/ScriptRequired.php create mode 100644 lib/php/HTMLPurifier/AttrTransform/Textarea.php create mode 100644 lib/php/HTMLPurifier/AttrTypes.php create mode 100644 lib/php/HTMLPurifier/AttrValidator.php create mode 100644 lib/php/HTMLPurifier/Bootstrap.php create mode 100644 lib/php/HTMLPurifier/CSSDefinition.php create mode 100644 lib/php/HTMLPurifier/ChildDef.php create mode 100644 lib/php/HTMLPurifier/ChildDef/Chameleon.php create mode 100644 lib/php/HTMLPurifier/ChildDef/Custom.php create mode 100644 lib/php/HTMLPurifier/ChildDef/Empty.php create mode 100644 lib/php/HTMLPurifier/ChildDef/Optional.php create mode 100644 lib/php/HTMLPurifier/ChildDef/Required.php create mode 100644 lib/php/HTMLPurifier/ChildDef/StrictBlockquote.php create mode 100644 lib/php/HTMLPurifier/ChildDef/Table.php create mode 100644 lib/php/HTMLPurifier/Config.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Builder/Xml.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Exception.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Interchange.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Interchange/Directive.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Interchange/Id.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/InterchangeBuilder.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/Validator.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/ValidatorAtom.php create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema.ser create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.Language.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.Base.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.Host.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt create mode 100644 lib/php/HTMLPurifier/ConfigSchema/schema/info.ini create mode 100644 lib/php/HTMLPurifier/ContentSets.php create mode 100644 lib/php/HTMLPurifier/Context.php create mode 100644 lib/php/HTMLPurifier/Definition.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Decorator.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Decorator/Memory.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Decorator/Template.php.in create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Null.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Serializer.php create mode 100644 lib/php/HTMLPurifier/DefinitionCache/Serializer/README create mode 100644 lib/php/HTMLPurifier/DefinitionCacheFactory.php create mode 100644 lib/php/HTMLPurifier/Doctype.php create mode 100644 lib/php/HTMLPurifier/DoctypeRegistry.php create mode 100644 lib/php/HTMLPurifier/ElementDef.php create mode 100644 lib/php/HTMLPurifier/Encoder.php create mode 100644 lib/php/HTMLPurifier/EntityLookup.php create mode 100644 lib/php/HTMLPurifier/EntityLookup/entities.ser create mode 100644 lib/php/HTMLPurifier/EntityParser.php create mode 100644 lib/php/HTMLPurifier/ErrorCollector.php create mode 100644 lib/php/HTMLPurifier/ErrorStruct.php create mode 100644 lib/php/HTMLPurifier/Exception.php create mode 100644 lib/php/HTMLPurifier/Filter.php create mode 100644 lib/php/HTMLPurifier/Filter/ExtractStyleBlocks.php create mode 100644 lib/php/HTMLPurifier/Filter/YouTube.php create mode 100644 lib/php/HTMLPurifier/Generator.php create mode 100644 lib/php/HTMLPurifier/HTMLDefinition.php create mode 100644 lib/php/HTMLPurifier/HTMLModule.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Bdo.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/CommonAttributes.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Edit.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Forms.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Hypertext.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Image.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Legacy.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/List.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Name.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Object.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Presentation.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Proprietary.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Ruby.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/SafeEmbed.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/SafeObject.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Scripting.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/StyleAttribute.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tables.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Target.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Text.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy/Name.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy/Proprietary.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy/Strict.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy/Transitional.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy/XHTML.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php create mode 100644 lib/php/HTMLPurifier/HTMLModule/XMLCommonAttributes.php create mode 100644 lib/php/HTMLPurifier/HTMLModuleManager.php create mode 100644 lib/php/HTMLPurifier/IDAccumulator.php create mode 100644 lib/php/HTMLPurifier/Injector.php create mode 100644 lib/php/HTMLPurifier/Injector/AutoParagraph.php create mode 100644 lib/php/HTMLPurifier/Injector/DisplayLinkURI.php create mode 100644 lib/php/HTMLPurifier/Injector/Linkify.php create mode 100644 lib/php/HTMLPurifier/Injector/PurifierLinkify.php create mode 100644 lib/php/HTMLPurifier/Injector/RemoveEmpty.php create mode 100644 lib/php/HTMLPurifier/Injector/SafeObject.php create mode 100644 lib/php/HTMLPurifier/Language.php create mode 100644 lib/php/HTMLPurifier/Language/classes/en-x-test.php create mode 100644 lib/php/HTMLPurifier/Language/messages/en-x-test.php create mode 100644 lib/php/HTMLPurifier/Language/messages/en-x-testmini.php create mode 100644 lib/php/HTMLPurifier/Language/messages/en.php create mode 100644 lib/php/HTMLPurifier/LanguageFactory.php create mode 100644 lib/php/HTMLPurifier/Length.php create mode 100644 lib/php/HTMLPurifier/Lexer.php create mode 100644 lib/php/HTMLPurifier/Lexer/DOMLex.php create mode 100644 lib/php/HTMLPurifier/Lexer/DirectLex.php create mode 100644 lib/php/HTMLPurifier/Lexer/PEARSax3.php create mode 100644 lib/php/HTMLPurifier/Lexer/PH5P.php create mode 100644 lib/php/HTMLPurifier/PercentEncoder.php create mode 100644 lib/php/HTMLPurifier/Printer.php create mode 100644 lib/php/HTMLPurifier/Printer/CSSDefinition.php create mode 100644 lib/php/HTMLPurifier/Printer/ConfigForm.css create mode 100644 lib/php/HTMLPurifier/Printer/ConfigForm.js create mode 100644 lib/php/HTMLPurifier/Printer/ConfigForm.php create mode 100644 lib/php/HTMLPurifier/Printer/HTMLDefinition.php create mode 100644 lib/php/HTMLPurifier/PropertyList.php create mode 100644 lib/php/HTMLPurifier/PropertyListIterator.php create mode 100644 lib/php/HTMLPurifier/Strategy.php create mode 100644 lib/php/HTMLPurifier/Strategy/Composite.php create mode 100644 lib/php/HTMLPurifier/Strategy/Core.php create mode 100644 lib/php/HTMLPurifier/Strategy/FixNesting.php create mode 100644 lib/php/HTMLPurifier/Strategy/MakeWellFormed.php create mode 100644 lib/php/HTMLPurifier/Strategy/RemoveForeignElements.php create mode 100644 lib/php/HTMLPurifier/Strategy/ValidateAttributes.php create mode 100644 lib/php/HTMLPurifier/StringHash.php create mode 100644 lib/php/HTMLPurifier/StringHashParser.php create mode 100644 lib/php/HTMLPurifier/TagTransform.php create mode 100644 lib/php/HTMLPurifier/TagTransform/Font.php create mode 100644 lib/php/HTMLPurifier/TagTransform/Simple.php create mode 100644 lib/php/HTMLPurifier/Token.php create mode 100644 lib/php/HTMLPurifier/Token/Comment.php create mode 100644 lib/php/HTMLPurifier/Token/Empty.php create mode 100644 lib/php/HTMLPurifier/Token/End.php create mode 100644 lib/php/HTMLPurifier/Token/Start.php create mode 100644 lib/php/HTMLPurifier/Token/Tag.php create mode 100644 lib/php/HTMLPurifier/Token/Text.php create mode 100644 lib/php/HTMLPurifier/TokenFactory.php create mode 100644 lib/php/HTMLPurifier/URI.php create mode 100644 lib/php/HTMLPurifier/URIDefinition.php create mode 100644 lib/php/HTMLPurifier/URIFilter.php create mode 100644 lib/php/HTMLPurifier/URIFilter/DisableExternal.php create mode 100644 lib/php/HTMLPurifier/URIFilter/DisableExternalResources.php create mode 100644 lib/php/HTMLPurifier/URIFilter/HostBlacklist.php create mode 100644 lib/php/HTMLPurifier/URIFilter/MakeAbsolute.php create mode 100644 lib/php/HTMLPurifier/URIFilter/Munge.php create mode 100644 lib/php/HTMLPurifier/URIParser.php create mode 100644 lib/php/HTMLPurifier/URIScheme.php create mode 100644 lib/php/HTMLPurifier/URIScheme/ftp.php create mode 100644 lib/php/HTMLPurifier/URIScheme/http.php create mode 100644 lib/php/HTMLPurifier/URIScheme/https.php create mode 100644 lib/php/HTMLPurifier/URIScheme/mailto.php create mode 100644 lib/php/HTMLPurifier/URIScheme/news.php create mode 100644 lib/php/HTMLPurifier/URIScheme/nntp.php create mode 100644 lib/php/HTMLPurifier/URISchemeRegistry.php create mode 100644 lib/php/HTMLPurifier/UnitConverter.php create mode 100644 lib/php/HTMLPurifier/VarParser.php create mode 100644 lib/php/HTMLPurifier/VarParser/Flexible.php create mode 100644 lib/php/HTMLPurifier/VarParser/Native.php create mode 100644 lib/php/HTMLPurifier/VarParserException.php create mode 100644 lib/php/HTMLPurifier/tags create mode 100644 lib/php/UNL/Autoload.php diff --git a/lib/.configsnapshots/configsnapshot-2010-04-15 09-10-17.xml b/lib/.configsnapshots/configsnapshot-2010-04-15 09-10-17.xml new file mode 100644 index 0000000..36af4d2 --- /dev/null +++ b/lib/.configsnapshots/configsnapshot-2010-04-15 09-10-17.xml @@ -0,0 +1,2 @@ +<?xml version="1.0"?> +<pearconfig version="1.0"><php_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/php</php_dir><ext_dir>/usr/local/php5/lib/php/extensions/no-debug-non-zts-20060613/</ext_dir><cfg_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/cfg</cfg_dir><doc_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/docs</doc_dir><bin_dir>/usr/local/bin</bin_dir><data_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/data</data_dir><www_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/www</www_dir><test_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/tests</test_dir><src_dir>/Users/bbieber/Documents/workspace/UNL_Search/lib/src</src_dir><php_bin>/usr/local/bin/php</php_bin><php_ini>/usr/local/lib/php.ini</php_ini><php_prefix></php_prefix><php_suffix></php_suffix></pearconfig> diff --git a/lib/.pear2registry b/lib/.pear2registry new file mode 100644 index 0000000000000000000000000000000000000000..19d0a89768d2696281c63c23a5cfe19015dc4a27 GIT binary patch literal 266240 zcmeFa349yZbuK=OAWGh3nU)z_Q4Cv_CB+gkJ7(-CHbv1ktzDFCE3!?`O2PsO8UVF; z4IsyH6K8MItj*T!`_d#$v$lOrx3s^uNt14QZC=;cHeLSRUfb9I`|e-{m;o^0fE*}- z4OkwMn0wE?_q*G<=bmNi@V;ExPCJF-oL)|g)GCUmsoT<NilQ#U|H02j{6Po42>u@U zy7ku-zFNorEO{};{wMpd>_4)9%l;+%L-vo^@3X(l{wDjY>@TrjXTMxa(wd*5p1z(Q z8rigoes-j+8+m&{H&5y_cFF(Aj31d8J35g*I<|Y?MB4v0z3rlO<1??%+3BMb_a5D* za@*4f4jxS(IJR%!j$o3!86%NLy?l1TBge}T<jh%pe%{X43Kit2WT)(6DOZ^Hl10@` zU&zHvSX!K$(~D<aa_yUG*(I}>TPXX5wog?8(z$%DT%)v2jc6q56*Fbg&f9v)wsZ`) zJ*2nV_JNnGoFlz8xql+uK{?Z&FL}7jQBdV;sTPYl7ld{c7WDFL>r1cf`9j$)wZ6^k zrE+c_jm_t6tGO>ih164>&Xs2uY`y3b+a_?^+a)7SH4F1jZl;zD?FuD3bYyb>*pcJu zy%WdN+iF7;Q{*-dk6T9ZP`2a2ZNcNF>+ra76CRtd#v?s~$EFQ<Tr-Tv$a*|3zYLE{ z$;=*FjmK(+%uSm8PxtBmz;8dc&(jO^Ra>?U?;IK4dv5>LS6{bj!#&i~i`2&BcOKop z?+`lF$=Su(@?3tw`+Z?{A^7KLp*SOra-$p`Q)PYL(u-DUr&*X=$mjI=a+;7yJGp$> zF1o+^B-6Q4TAvTTPM2q~9G=eQ^XZ~JS2$&-(e3Fu9b+q3SS+Q@f@Ql)$2P+*gIxOL zeBtzbS}&#VotoN?j2K@!h!l#2#hKas*|ffBVV$*hq)WEBShUk;?Q(j-F3#mjB}{CW z)ahBI$YVO8E>^8<JzpxI)RT6)<Yp=?7SmS2T%5D#%cwHwU_92E>5`~7b(BoXyj^<= z9MA3Kq@BFXX*cuS&3ty>9f$VKmdgt_XELWxpB|l=UnJ0FyqQzV<YyN0qXgND!Z-m} z7c121p+Yh3*m{}N%bPgZMa)Ia@HAGl+K65(>SUR@XtJEP?1iFjV)B;&i=DoqmSE>g zOLlqN)&r%jJJMVC<dC8aIPk{}r}X@yy**tzJ73n%kp1bR<}9Oa(%+Ic3V^<r3q_qS z6i=p&#WE@LBC^S$tK42K&lU@(({`~~D3(Uk>7%nY+CczyAukoM{LI^@{T7yPPN$0n zW3g1esI<7SP$-s3jnZVh$mGzeq1L8z^PrYP$C&mGfB4KBr3JjWh_wBPJEA7$7O=SL zi{(OX^TjCIkp_IlJn8;0MaxH~bW!U|WXWM*KH@?>dQn;+GeGBS6iV~@LTR>8-q{!g zX-?aT0l{P9Gc;KW81@?!`vW`%p8)~`1o{erO{=zSp%>@#(_|sZ7j%p88>9GQ)V3Cx z>sD>qNe6ex;O}I~r!OU~UDa1j9#nRKzyN`S5O5FqgZZBjngcQe1o{gBvi~#GM=9!~ z>~BE^_x0{;A44`5BgxX@yF`c#WPmk^=s*StA#z{K03md!$(OeWMA+Li%<7TxO*EC8 zx9l^ehatbU-IIa)_p~n=pXUAFSTg@tQLm<`SJOXYGLXc7l>ILI-L|X1_BE?Vc8ydY zWQRs}?xdfp$(DRMg<Wt3%BAMNSA|6L&2K{@`rc8lUkOad7x0&+W6JLvB%@{g3q{Bv z%bplG@+QEt9epuhzMDwC8fl}2!G0&Yt=iFOq*w7Bu6!yqJZ@dJdSq&6)bMETooP>o z1QDOtyQy;{siBe0o9S09y7I+2JqPhzZXQ<{fnV2z`aJL=)ZLBxHV}-wnyEK6W~USj zdH;HbkGQ#LlqzshR|!NMR~O#5zp${;KJSi)kEEl!V$JmUonuF~i4pY)$@%N;CADnV zwWMaDu9a7>9+})6)wO|6nC1h2tabN)c8X#@g2&)9KwyBtN)b59tl6?<!<Nn3aFkpQ z7opbfA})^7yK}aI#Z%9h&l<KqleZ1>^{C-~ecKFKTSjrIbJi{$qt|TNxncX&+Hg>Y z7yO;b^9bb$>oo4Y(k~lLAJ^w+>_^Vxsatco`P-UPgKSOy{|Soy9{Y)vHhhqOfWQEO z9wM+kwPnlZNNImy6=Alew(PvRqvXG~F3{xu|55q?p7g5Dx;gBp*^jXwWZ%oall>|7 zjqGdKXV`P>6YN>G%of;(Set#2J;C119%lEkcd)zJU98HA>?pgPy@9=!y@uVuUd~>` zu44Y1`FG}@n7?KIg838X518*TzsdXx^9#&Zm@hD&W<JXN4D-{>TbVa9&oWOlFJ~TM z%FH}7%NWdk%-zf(<}PLr^HN4-1ZD?wBa>#XVXkB@W7aSX{a^II(*HpJZ~D*ZAJE^Y ze~11J`j_c%&|juMPk)mBF#SIIo%CDi*VC_}pQ4|j&(K9WPdoI3^vmd@^nvWUtM>;N z#^CSnt@tspTBVFpTaz<;{Ok7&xmzYGNOtDffqm0cxOXvuB3&H=nE!wErFi~#^%gw; zZ&k(fKdUmH|EDV8`R~;Xp8r<ef#*M0iT=j_SG^I>f2wZA^B=2eJpaB*6gmE`dJUd` zTP3O-|GRo6o_}2>dK`aQC5jw>QN0+?|5aUs=MSr-EC000;Q52flX(7d<uN?}sB#w1 zKd79-^ZS(&p5Lo1;Q8Ik9G>5)JcQ@(R%Y=0?TUrxZ&h?Wf3xxcp5LyV!1LEC_u~1h zm1B7RO63Ti->MwM^M6$K;rWY|Nj!g{ayy>isEp(J^~!B{ezmd-&#zPn0$;9Zc>Y`^ zi{}?B1cxtFL_B}Cf=g)X^A&=|rz<=0{8Z&8JU>|>n0%tL70-`VZou=SmFw~Ra3ziB zhbk|^^8=L^;`uWbg3tRaFTnGCm8<al=?X#Vos}!_d`IPSJl|d+SiP-s37$VyAsgOX zDg?2gtPmBLH&uxA`;8TX+v_WMP_KOgkJk|Fo_z$5SDnG*CkTA6EaUNX5sz081kcUm zQO)D=asuL$vv@q};PD8-@vMQz=?C#xBv6*`!=v;vJc{?=Q8<dn{9!!i$lN=*7mwVX zc+5`VF|!*F`&K-xm*8O#^d403m?o$_KvaM3BWS(s#dsViDBVjyx|`_#93|jPZN%dU z0p-vJJPr~t_N~KXl7MjMT0HI`jlZ2G&1RYR;Ys~DX7n27eT+r_2mLyF8}(I+)W!Wc zsmdM0bS5+t@S^fqs#3!D_;sO_s_4y0I!~5a=$jZBsPJTh>OGlpR~Kr^oW;p1Q>y(K zE0>F-WBGjHv~3-+^P}Z6W#l7}=G#?m<>gMCF>P0%F^Y2Vk93z~q$;;2)5O_a-r8e3 zneka1@A7tG-YbSoB|Whg5!8+r_4yJ}Fv%Q1QB!B<O|K|2x=z@s%0wb93Kz9|W*m3X zMXw|>-|u-KRmm=|B;@|Ao~qn3%!J3^vO1ZJwEYEZF>hxk=b%FBRgwBrl=8%*sY)S{ zQrZn+Qu65Sg`$zOEPH+;Z@W5N-bkk2f$|?ek5lNBSMdF0{?qLH@uc3%zMoxSU(Ea= z^LFMg`fur1(l=1QPFd7B>ZRm$yU!l58HXEe29h}X>Mc7fLkxCN{IzhB{Dog$XOLgk zlCSH^d%@qyM!V!HFkUFyqc!EJDOZCPIG2eg9BB+yMKRADMTH)2RH#W^Fn%c$^U(0i z&Cd|ZWdE!@K<ZDgu>Mn1{vvj$Sb+9lIcL}Eehx*PdC)~}WtAU;76=9zbh$^#uZQ%a zK2y{eW=T78DCFVe0O-*bmO&Qdaj3(Vupf*=m3NF#C!5?udqI7AnbdbLVo|h<nS1B* zWL6Ssl=%|S8;eiRU4@{ST_Oa?=5_yFAaH+|1nMj1#Qfsi(ZV=-4dhgiJ2nP#d%Gh? zmiZ~@gPOC13fZs^>;{$n5mfNLHfc82#~QwR&%MZUy8cbG8f>g_?JhuM3f0!X+Q57B zrJ%lw0!P7t%YF^>CF;NNUx&|AnW3SPkrDctU9JEa%CvULeOwe0`nq48KXGqOa$UdC z+15c4Uo#zule8g27Ng|DP<(~f<*CM9QiqZY#oSDsVht$Cdm7|W`;52;DC?z@H68W# zQt3HMh9Q8PxqX6m(q>XSD6B$s9@aoVc3|@Gv5EAyC56&{6GMXO+O<)XVA>rh)4coR z;_Yk64O8c&f}8Y`>cmAuBRAbdKQ-o#!!T&2ravzWjm@T)=WmpvI|G*vk=H<h`Zan# zelM0HuY<huL%l=hKf`{RV*e12!DoQL0D(S1;93UKz;)}Q?ieze%`}*Xb?bs`cJOys zwv{2`5QhB<#r_c<gU<kg0Rnx4z_nxnh`bKSFq_Hzk2q)8MR)$Y`v0q_*Fx$^eVZ9( z?qq+C8mI2Ze_eboziepN#j8gS-xS5U1vnb;Af;)(>5FyM@k@qAuDgy-h|E)mU8WEw zXVWt<z*{Iaep?@61smUoxxjQ+$|-@=G1XJjX}4YtZZJ~u#Y;I0J)5PN!F0MEQy<L^ z)~gU?2-E59;-#xc?zk?hFB_0e^NruHI455=G$M-h)9YR6i6J6RMleB~_KX9e8P@XE zaHuodBnbCP==@PbBNDfI+L%33E8N$RTY|WkI3{V_yn9_2b&Q;Yg`;bl4in)d82xZ# zh<PG`-dAV~cK3F||3d&U_zVyjAn-gyfbjo=`TsoZ_(78g2mpb>{y!K70|cJG2n_cB z=WpK+T0THvu>TLnzyN{gF9L)8|M}bZgO(2vAp1W{e~6+#ME{8W2z@tw_W+ZXBd~XP z^~kgqb%kc1fz~qd`8$mZG#j28=1nL9PxI}+zxted`Ow6=)g#BnD1x>7ozQI?vE6UE zZ@ZF^8W|s_UvWC>mTuZUW9Anvc%*H~?Z4Wv)UBPL@HugN=izZ4@8@=;y)r`=b;s7P z9?6Y&GXgv+ZAXINMOO{@|CP`70fhkq{ffYV|L@nH8`OJ%z{(LI`#(c}nxa3=evUp! z6T9xgXZZ-6*syw}Fd4<AwP)~sUM%VW;J<$%s<rAV>WZNeO`~7A(dFRVQ*8h33qpK* z`!r#ey=XtY2+#9YdiTMD``Rm})%EOR=rY#R##EfES8+*oym<$q3lI?@x}HHLypTt= zpzdAh1!55t=3u02q5ZycUMN9bf3jY^(Cd&grjKO6l@|dl@TZ3fVg9|-;f{uqXy_!6 z=;3m}|1W>C4{{C==ywDL{C~gq;Gq5k1eT9LjsJfqMZc4M|A7BrK8{|_J4RNIoRFjV ze~$rf68pFN_FB+e_0*~>hejqR=~uEHczyqLP-h#%)IA~a#u&W6Pw6>(28buh?>So^ z@Yh{)#_rWCir??)nR3AYuXxT62n-PDTLcFDf8Tc9pyC4rR*XQ6|G$!=uVlYSk^lPR zbI;YQM@~+3#b?*mA=>fVKJR-yn!oB5>8plDWSRa6)8)I{rEmA$i$XkhyA&4`XARvW zua0&3b$by%62=tR6E|!>20?tTG+kOW{KiDz0W87UBS_1s90DWNPf~SU5|QgS@D7o4 zj{psbcObrUpfyovg2c|51OC517Q8_%2MF{6fdT*D3seU*2MF{B0yX|WOVL^OL)1Ux zzvsbc*TzoH$_k~h1f6gCVha#j_0-%8hDIhP=x6SaJR<l<igpJ9|J`*<9vS?UgQG** zSBtM}<Fr4ng!tzU8bM2(29~S76JYbx0k%F`=pU+Y6u;lI^X`EEe;zD&gC-6Th(KV# z|3?5b_<DfA^8f*||F5D~Q}k*?)_a`&D*e~gEdERUbL+L8B2ObSN)yxF{LPE2yIwdn zlF86dZFf0t1lyYpWoh|hYlydQc^zV?tMWCqXcE`C#G5$&rax~85pH@JA`98B$Nks5 z@YV&&?K#&#>X-uebh=OqDc(1Umcs)1rk5cOH&oAdYU#Ctf{N&H6}-ZeR8RZ#Jb(|U zx@EiQdH01Y;wFF-MBgi^3I4H7t4EGyqHe)jhWs?&^3}Eh|DX6$Hy}7bpl=Wu@c(_& zRfCES5J(&WSN_j_o?^d`$KW$SV1U2{kHGaL^4ACz75PgZ6-K!mN${@`Dj4u9_&W*y zBad>U9NGUF_8S!Y13U(w0RjUA`U-(ftF~;33<1Vmw`$AIjsd{D_+S4)QUAegV{+`L z=>J1Kfd3NuOl@90GBXw>pjfc=q8I7bE{248(|q*1Om#dxG_q?K{nXX9yZ2_<p{MBE zEJ5^d(sy{`yXs9Z8XDQTlYVM#TQberw}+&D&2P`Q*e}wZMLHen(4xEeU`?W?mCJ9F z>9wO{yZ23`o8kBSwz(U+6u&l-$CkgwR7!v_vO96BH;BW%kludqy454*uE+@$F%>YK zBHrvA$o~>r>IP&62=pNW1NmPccG{rI0|XL6U?BfX2*?4M0Rra<fhFXB{HQc4#*qKH z=?3z@^VDgBVg?BG0D)xWe{Z3vx6nUivg|w9-=aAD7x~F-TRk$qDM|q0iA_q=g8!Rb zoxFZ%WNeInYEw<j7Af-!O8l0UDJ()94a@5yi)|Jeg%sg>ps;t|C3n4%!Rj{*jT}Bq zKecm-UiBq&!QKsbl%Il+*<pVA=8kI!??M`pQL2`W<QJ-}&)l+l<n-aFskStrorLH< zh{8br7rE38UJMX;&Lc38|2^lsc+kKB0+9%i{eOr)L9y>-Ip%kn$C%CZH}U7-vvLHi zp*36V;o*&2uOqzLDAct^ZENw^fqm&icOFVl_+cK>`n;7MTUdbOEWM1NfaxRlLZOr^ z7m8<*1^+DD(HHaOLla|1rtjACIZH3wypZKZiPI$Q!PRSa9$&v6r8Mhzkql{{xpfM~ z^qoidqwK|E&dJ%ubUtSk_2SuCTn%?9L6-P?Qfo#IkBp#%>1kv>RI(SX!p<P!QW-Tj z8yq!B=BDjT_}Hp7o3)J_LD(voqo~K|yj>>P>?xRwh`d3l0*|t-&yh+N%#!z`GuadD znk{=bY#0`SyO|HtZD2l*&>oBPx$@ccz8VZ9Dfyot<$^#LiqnTC#`k$}1B{25HCty~ zFi58gb?O9b|LB+|V3T&pqvB0zU&F;p8)wsp&K4I-Zcg{nxgiF<X3PB>HuwW508pbL z(SwH+K>qJs2HF3I*k>vBv+P&ccd>uZKFj_w`x`4C`~wOD1O^Cnhrs3R7Mjdhf2$a} zk_~I`5v|?TYuORnU0hS^*v)ic<y=+E-&pe4_3T!cV11cl>P2{q30<52U#HmLWWUA! z8~b<cpTQE~>)q9Rka~c?0D&J10z>RD(f%D;&90|s7XoX;#q37f<Ca#fVK-nIF!L-~ zI$XYksePpvJ^kNrQ`EPaD)Tw!&)8G!&#^z()RyYi+g<bcb2(Sq>#=^*CA)aaE|$&- zHx7+#+C)E{4ZbaTsY+r<uIqmrmfrfzB#E=uUSL~VoSOr;>I&qeQp{X9HJPzXW!nO? zu9s?e2r{?5eYNAHIpuNv9)n3hCycQlm99=04yW9sLi&2|m+M0o=}lzXWDBysb~tlh zFO}yC7CAfA;^Bozhm>2r-VB?=A3HF4_}IiYx4A*B*6o?Cv2K)jf8<ErUqN*oz8+nF zG-ullSN~<#qt=}vod#_wj{`#4p230az(I`tWBc~idyxx4yih2XqtmM4#O?ZHN4hp) zL+F$9|61nvDE5D{KhM5}EwM*fkxeuIhOY*n0RnxBz_n{(AxDRp=^$fG9oo7k1=Ej^ zX$)bmn?)$pq4h(lkrBGaaJyVLb!gM-)aK1}kn;{O;nbmPQ(oB(Mm#8opblNPDz$ZM zt#Wn7+})LZVFGn%Gn?9S6V14F_BeIR9u59UU-}qP1G>p;q?c&16iS53zzom_!b=mR zLLDOf|61msC{|%_XD?^p!~T%{Ci741%UPpOyM9pZ0RkNna5ZM!N#IFjgTJrcvI^@$ z^D@x<_nLL9uqe38f%_QRxC$#mU=;}by4owR{c4c9b`{nFQiJwO!K(Bs%<<rqZCVG| zmIO^}0Mlr1n+1SQ0V|sQPlDJ4`=9J8=IhKOj7<Ln{Q>%A^g8O7s48_Ec^UP&?J_K8 z8^@wsaAacYC_l>2mdgt_XELF5nIgF4%x#y(qQFH{n7jlF;l{%qP^wizBqU0xMH7ZM zi;^tVDl>U;JW@goQWwP|<wCPWt+1WNX`qWyXHw&92&su6R!svYfEA!>dRaqhd?+3f z7o|udtK$*zhfe#NV6%<W9Dnq*C9-Q3Hpz{*1~Cc#9tI@%riR8YY}QL=_n~N$omlfP zRk{yCo9twVUn;xb{7`mG={=V*c+%`w$n+k^k1w&`z}|lwOEF(&%FHPJSJWaUke7|m z?$ukjjzPE(xn@OARBtV~Y{ewFOZTqca?{~jEqu1AX;v>iL%oi@Bsp5b#mUj)FG`LU zw{`+}O@#4knhRdHHGNwf;vQ0KlAt0CB|(K>odgvwb?fRaTW$^G5}YJ~vFs1C5F)Eq zmS133k{@$&_e%2fqv9%Z{!dYVLs5T2znHm;c>{Y*W2QgHe%tlpPHz93#6Z)&yvY~+ zsMSq34ULE*eNGRGIlb4@gp~=CaKF+zNR!+IiA$8qXBX@a0w+Vq^?fnlAw?(A(T=p2 ze@B{>6OtrN>{vZ=T#QmzZG%lS#4a8;S61(IVXlaE&EldxlPi^rXFa(mnV(Hxy(9#4 zn*?E)BL%1dFa6u!HZ?l5ar-9vv$=9HHv<8kCw+yZio9L6{mXvJFZ#&j9d}IBl>od{ z={*zsCXP;|4<4v%2L#H1SAg{G2alvD#>VeVA31nWdiTT~lLwj-q<tE<A33-m-W?z= z#QzWhx1_u0&WR%vE#DH45cNExezm8eB#rsKi_CwT{Wl8#2A=@}0|cHQ2n^B82vnU$ ziKWj=?ewm6A#aW5P1kZ_?7*J5i-@<Xd-=V;ZfVKwErRX>=H&8r{H4oH9a^+vRkkVH zGO9`TIZt!T%5|~UHs$NS>X8JN(TK*5wP4R%_Pm+v0{V8DyKhcsDhaj8w`OS>cu_lI zyIT1~l4)ZrwPm!m$+vv$<~8N()FRCPTiZ|gNFvK<NQ>X`*iC84*{Lz@8`6QqGMWM* zYRFYt?3OfV?7kt<6cT8MKVX;L9-qL<+to^GIqkzK!B@Qv8yv=OVI+xVv@r&)L|f^D zxI}9SyF-p4wGTNtwFk{7mt@OZX)XhSdbH-{&C2%KyKm)EbP{Ws584v_>h~y#npd{x ziM~(>AfHT9Ep4T@j20ta-n1K*FFu3V$kKgF+T~qFtx1@V+{|LpE-Vmxh4^)E&e(km zqAA2`$Haj><owUj^AtVLypn#1tOkQm&k$Huy(%-baqA}f+{2zs$?K+RbJjLbdViFf zMM|MWkP$&-G~o+{AXxIVg;s<%lB6k!qQ;M=kBm)DO>8@IaNoY&W8-^wq_>Xi^Yeu= zw9bfF%cq7o^z$(5$>*W%=W)xW^kT_Q=gM1yVn3`iE|cvZAibKTCl92zd8PSZxo1D0 z(Dv{V5yP;0igSf+ugEk3Q>&HV#3*FEx;KPveufYxON!cp=Twh^Fa8Y?^4i?=UE0Ua z&ok;bzIMF!{tCA}EZqwi>Iy`g3Tc0$X_GHb;r{%m9;fJM@HpR}>Kb8a!?o8!q<$)A zpI#^y$_2BKFV%nXyZi3RiF?xZZ_{H_Ay8@~E%X)H{X9%UI+X6NSkr=R{oWI8r=haz zEY6!`EpJWair10C6Ct-IxSreuTu+Gj6%N(Rod<2Y>#0S4Xv2mL^iLclP+(^&k^e&g zkS}7k-JfVg%4@gQvHsetNT`McBNXMOY0Zpxk<iXB=JTZKp7+|;%=M61t*_u1rOlW6 znNa0|%Secy38jLr@xibdJz|X1fjG}NEe9VT+qO8>U&h7D=huk--^JATDf)Nmx6oPY z`^@FceawfMe})dg%h_-A@BRM+t9S04+qk4Amv_J0tl*UypUvq-9k!paxt(&AxTnb- zc*fIUxT>e*xeYH_y?OJljS(7UUY!FvX2C0I{>l|(=7y8hIY^FHlDZH{>S&0UtnB`s zqgR%nXI7S9V9ETylrkyiTE=ER#{46_mihu^vRU>K!~*>z`knNBD`pCgK*hrLR4m%4 zTn1Gvyw^)iKk@3sN%Aj9oFq3=@0<3zpY?_}UWX>~6)VfntzWU4m3VnFN;XMQNk-u( z8Ra8!&5EJrE?qGRe&u>}!a8#PzYHqA>^IpeJ3i3=9q9jhx_;j3)K(pEp#NL*RZ?57 z`&Iwf)xIb5|6=Mp6#F&yF?JX8-^?#CPcz%-U!%{^H{$EQ{2aQPs6^YY9%mbdBRs%^ zsUrD~LobLyF{Got6en|NBnHKh+V)Zu@4E^nZ5!vBk21}FdnXugdN;p~MBu&+T~PPy z9h>^iSHh%j<F3GwCh%*x#(@{L>38$+l6n35wB+4&1?dH7{x?{#;P2s*gKxZ<zdlBG zzwp-Vw_P5~^2Ie<iLjkn7mGsB_@HiQRBHm6|11UlKbrnm{CK{6Dvz<3Y}t9bvUxb0 zU3c~V;6M%jPHaLm#~?5)Weg){8+I|X$Ma29%A76~PnIA-vNQ1M1t|g1GswWJRtER< z%gaO@bou_dMu`lP=$zwU+wa?cnbB|NZhrJ}htp(Dva@>DGGtXy49O66+Yt@TQL~b5 zRo5_X^KTly_0|it`TAnHkT2*~I|zdBLNEkhcu+`&k>zaJ&=rGIbU`#!iC0V$UulAj zmzBrp3w<PzRz<-;zHfnmeCaixa}>+a1x3{yDa)IpDhLv1s+M47RZTE-cmC6>Ddu13 zf5ngI>8CQc+8qQ}5AWQ0fzF3ED?xMDH+o%HL{29UUa@RTP$gNkc}}w}Q<N=H<SUP* zF7!cvvSh=Uy><EXzYh(1|4VPs3o<8(wj<k$C^)KWtCno4f~>1DujssKR@bb$(3ij5 zyqRCL+Cvb07lI-9!h^!%O^(lE7;uhe%DiDJJg<t9qS!{(mQ233|9_2Q{(}Bx>TCTr z{i~_9Zbxn(-nHw(oVS^=a=Ez2cG?W(;HzLa2EX@u(G+x3wha!umSc04W6NSz6Lnd{ zYK|pcscu^1;-C%h-+uw)FkXN@fqSvqZfd*jZc5UEOVfJ~AKBnJC9BG!oRtjSRs>5k ztt=;IImh8S-KmZYx%kM#lam)FKBFbwv3CY1aP#Wkk|2mpYj%Gr6g5sVB$4M#O>=b3 z(ly?29LcamLlDH;{tqL6=5OgQ;>VBSQxz_8`{s_}6DKarzVRnyW_)TYvs*V$5*@Yq zcARO4l)>KWkj(2gRp%9l7feF~%dS|O#2Z;%6&&7_I8_nMs&H{Xfi{+3nAPFCco9x) zTDJ5M8sU_YpoNoppgEi%3MSUdteh1kUNuyl*ks!=u?DE7q*T{m<buWzPfT3+BW($! z$m=M0$U6@c#nc^9S0p~mNv3TJx^3{XA>nBXYSz?h_y0Fi)Q!}m)OYCZbd~-dvz2)T z^XKd?_HF)ha5ROJGM&JoHgJ@o?@d*1N{ndWsK_K%+rSZ=O{}&}Yy7IjYU?w>=={{d zRORYKu&qtk)%T|=aw7Cvb}<w%G@YtwNl<rJk|quT^w->zs%%e;euJ4?dm>fglcY;F z7;oBN<fJNhBuzULUN!;E#bo}krQq=Y1odn5PWpB9zc3E-tL%%}H~4dZ*+1t~m3tCz zL1X(pM4aS9N6!Q7QX^HlHTmKS@Sk4vinvTqRdyv_1jcK@FJYaXs!a5tggG~&fWP@( zzL2V%NW7Fi_Waq2yiNS}@3GB7QAY#=54iO`>6_bg`EtE)u6Qt2xh3&7-Cme4du<r* z$%G^Y+^)~%@@KtbuI$OInZy7nmi5-2*-$(GTNLv-`c;&*?77}CV|DX7m+#s&eE9H% z&v#{l6Ecd!Y9v}Zg~y5E$R*g4jf1ITLlUcJb;UFwq7t$kXNW3Sz40;^rCW!mruqP- z-NBVH5`;#o7)d;!IEXDFp%Mj6*KM3ubsLgu$<#PYv?N7Ut9M=M0(H;u$&>v6RQ<r% zv|~jh(l$qz$vrS_)6^jQRV`k(ZC$ZdLF9Q}le4ldaTttb|G$hnN8#VkQ~yS1=#%vO z>EB~Un3pkcX1>F|h@EA>%>I1T^v<TX?97IgY@2V-g0&v?xqtc=<h#|+mk1Fzrp=Ot z`=<n^%QF3g02`|K(q#sD{Z(C->9g}G0bqrFQg&=7xVqK1A<LwX@6y$5VakVggTVgS z1lsZ4Yi<UKUEPuJSnIVfMb?R~v-;fgMU%*WVp-Xn803prK&io~lAZmM!>H5A<<+S{ zDY$@KIsuAP%cNLeyDua2{}SpM3jcna-h$cxDf<5~dzm*df52Xkx&K-At5I_w-`|NU z9H;lEYCnUcGWa{WL=P?lA$bo1F}b{H0wi4a_VOvzsjxg)qBz*~D}cJeq+T(OnjKtT z&3ry|cm)G8%xYe_f&tl{o7}Jhl=j*4Gv!$_QLb9SK-dQ_VN>O^d3PX;+yS`nSsvW` z5pFHFP^0sL6*TL>;v7^)NVBe9LB}1kizW$%HsiMIn*C^(xx98Ag)O*uHuDb78*BXk z5sLXu`cdjghq)g6RrSUz`iYNiOi*-F<TBMr6(fnqddZS4369~&ssly~N+=Ge3YMa) zj%t7p<Ew|(yFi^7E)@EJkBwR#JHZx}ClbV{3?8I9&kJ_e6co|nINs27lT!q+wUWT$ zTHmy)naf>BCx(w7?*mBv&CeIiM8XnE7%!ENn3<J%Ueq<yaBKzttConHMp2O@$q}+{ z{BIV*Kjx3=4-+w9f}iSL8~W*-)_zy(G`id8i0q#B$-REkbWN9RhZi-8<21!EaAw2( zwIxfY!O4nK6|Qu_?2}v4FtgDK#Bj<;@WRRb6;?BKQRNj*!_q1#l4dwr-cmW7cy%Z| zYSr|x3)(Hi2M_kaazNVXi2*y}K+-Ikz=O!-q2wcIq9Tf#!HJFl36vporX>rwBbB+D z{Qn7xdV=~hdXoM)Gt4}}{D8fMeOH1j0rgO-a<H9nwkP855E}@nMyyn2yj|&TjM*jh zcq4HR@`xzp1w9dQHyB71adl55&|&Tp#at8BSu298mGwkX8xy_wgY>}F&^UHv(wjvu zyf0M|6Ycql`NcVp_@=~fgUDFR>@F1Cn8kqSMTw6ow}f$oa`W}CuC4L^rzqxo^arR@ zNzVCBw5mH^;4aML!}r|N2Mcr4T#vOT#7g8X(t;_;3THZ;1*J7kw?)n5bXgX83Hoam zZ&WiQE?j-W);2G|kx+yZ#!KZPCR(bhnkFx2!8Pd$^jl5d)+Ec4p&}*lcJ;=qT*Uh1 zGNmE^z_gDBqAn$gk;DT^$wDKN6K&2AG$o7wdEJs6L+52vkh5yGHve@p|KCUHo#yN^ z-&J3_(d~(Y!=~8>Q@!mz6$y81>MoLcJtRwxf|Hmo;eyam6ieYX{Gk{+ZoPQXR;$7_ zE`WW)`M2;Bk%)v-#!TjcMwEJW3(AnV+mf^_cz@ZzuO^>$R8hB#>Wx?T)BF$dd67so zQpHH(0mV7I!x2WGhXx=g>AGRaf@Vsh!^@H=O11g_ev0`M`d6v<FLRFfHd}S`i`>5K z1N8yYFF_8jZ9jz)EuF&aNK?+@;HLAEY)Vqr;Z>D0voPntrMQ7L-mG4^$wf&VzT=Jy ze4Pz2(QTXWzwh|cUkD%)G;9b10gZyKAAed33|&M{b|l%+tD9cv!g7IIMzo$?+BTjj zpnJ!grQUn^=(a9eFnGjy-!v=-QfN~!H3vE<f@<)JLeBq}QVSIReVY0g`WCuEe;Yo) z?qS}`{2gL~K8|=`0jr=}arbaX_jeC|hi=-FS~-O+I}dkX1A<n4V!mjZS2`|{`swM~ z6;bh({?dB^>GU!ibbAr*{f_Fz8QYZ`UAK%|dou9cc1`Xf=i2LUM|F-ZvpNkfcJnf? zyWE=W%gvuWis(Y_8TE#h;Nza#8nxQ8%)3RmRtM(Fg_CfkOi;RUB`6)row4&SnXSw0 zugO}#a8i?P%bZX9^!XW*e|v5JFA@I#lT@j<8@Wfls$S<V$$N*Vr!VlG%I*24Sq9X| z*S?*8U7Mj@+Sm!b)ml?HL2<zJ<JeDb3}sPtL{5PnD=hsqL9E`GcA@KoW33CvlB$&9 zRMBwN33))-mI8A&g%@R5wOU!-;3UNoBpCUMwrpuQ|6ki*Km}xS9Z>L7#Yo}-<>(@> z!SvhWT$cx`rVElI;{rfcaWP=XHTnNz6!jSO=kx^q5$yeEng0n-VDD$&OU<S#dlMC( zy4_u$Pdz4I+Y~WfuS;Ca?6#w}V}kzmW~wrlJWh>`3uSChT=?&{A+*<`m>YW2CrukM z3fhwR)sNer;4Y0)ZtP9ZG`4V*u{H7071-ZFe_P_082;uD;@<xMBsu?oo_g}U?Ek?+ zs=^KKI^74tt+w-peA%882+gdR$?BUM4zUt1D7FX{A{jRTLROJE9lvt6Ei2XSn_bYx zhL0WV2L>v%kad8fvv|9Pe?5(d74H8vL4jg>R+Vf8`Vz4Fw>3+FY?~7VUaD@o-o>g9 z&i`%$>zhh6PPHUUz4!2eoj=S>I8%{LSyfEI;w{G^!e(7EaNlIs`2QJ-`5*L~sF~o@ zO!W8a_N{$2WJ4=_G}Mh`ang8Q0@qwnTH{1jg+DG`l_X7()U3`ss_w{^E><%)x>)tW zZ69fMrfJWOMk1InUMdeU)q$5a$oL6ZSxvJ=h_MA59<3Y=a!^IBs#{#d`sGB~$__=t z(UdlJLJv9~9%3}r5qJll-z-yy#hIx(SOg52w{Xu~oBtoL$^So|=$ucSSoNA0yWN}} z-n;h#-}k0Y=MWB@NLcW%ZJ#w#Eb+QN2TTWNUd_OY?g*x(St7PL9va0kk+ZW_^~&uo zIDH`h5AdID+wQ;b_|k&~S6aB$7FGDN;<8Ygur!@l;Q<KBfWU}zs++dCu=K(HA7=a8 zHa?JKsrMc}j*LqpSv0a{Ru`ZFh?_sC0CKt|aG3pqPR{=qQQIi|dj<8U^f>()<_e|+ z%RhmA2VT`Z93*}SAy?<#{J5!J$}${nw#A{L0=6S@EiKT_OdzDXucftfE>$^}=;^;+ zLS`yA2Q!p$5**CKYBX^>cT(D&g_7SHnVu9ddFYhnwSenY0g6`upOmu_SG0SysLz+2 zLeW?I6B3n>)Ty`nl@b$GTMZOs753zO&kWxE|EDPCuj%hnpGthoPMTnK^G<g;?*m<c z`YiT_Wz<5|SWK2q;VtT#tlA=%72pIgi~nWLP)!@2USZs=n{awnUBAOcsSg}o0i`AI zA+Mv5A@6)fK-L{AD;hQ=BnGTo3=?`m8V9#&(5eJD)hlo67yc-)HMVWI|Gwi(9~K>) zB5waNKqR<9gK>#s$S^js92o{Cwf+B6YM#QsPf~wJ3-meqTg=7GLFV<$AF(&EbL^L> zmqt(G2ph|NxJ1_Lm&gHY?q%Lq`VGD*Kx(nLq+NUcYR(&EJNBSn%TOK&&E%bjKqOah zoRhPQv5cMTM4C7C(Pi2N6N3J}oml}5e{1GeM#J5G`R*JKv~IA%GOqgrTU*_623Z)y zh|%edoVI4^0b2EKQd)-EK!DCn{q9n}3q$Ar?uJf)5DWv37CaAO%F5bSxBvI_e-|j` z3y}Xo4XD?jYMOJG;XW|QA`{G?93A$cdYZP&agd0&BwM;Hz{Jl|9NSQN!%}sq`D>PJ zTau{30I-_Q^b<t>8q^6QKTRY=egY37Q8h$Uhk_dXl?$+;hU+98V<F?0ahO$gqk6}v z3(>LRT<!vw1Vyg<o$zwcm(3eRBzSF8dq|pafl37Z5F9C@ZQ|IU6&x{ZD;g(*Px9n{ z&k+0HUnFioeq5ibD!M&-fs1eh%W-J0Y1f%iX<Pa+DxuefyoG*)?`GBJve~T4^PC_F z3MWgNMO<X*)e*sk?!t9$-MY_r#H%gGUk53`^F#O#58uQMx}~Xb`wKZYXTeFjCK|Ax z5bbK3@28a%n~g8SXc;3!Pt)5=+%!uj@CJ_rj$hJw&9*sQAB!qP$nXe`a0rsZaj-S1 z-~atK{qA1g|H+Em6O+UD-+uuw*!4l?>I&Dnt&XELoV2B1B1n4OV`On%Em@Lm*@EVn zGVb@G`KM*=tisufC08@Di`xZ$7Z_+{gWX*MwMN2Dm^D&)h}mY=fxS-_VJsBLI%G_C z;sd}mZABuBL3M}ZB6fkBmIe^3A2gN#te&nDUiCyCT%xVOQYtG7BIlSQZz1{<oJ|Ub zB?*Rso06LT-${x(N&POZ((hp|VjgC`%c{xQ|JPNpW>S^O#N^}w{DS8qK&bReZdyMy zp^$8k8bd54g3FGYOI7yw0JO=unIrbYi@BohhcVC+RWNLH4@#OMLDI;!jej@SheHx~ zJ%M887Z&|1j9U`5n{Sli-%FwyFX_=B3#m%&>QOrmz-q+nj+fT<fB66TZTf9V`~SH` zb64X1!@AxVdw%Pr=&;f@C61rcTbR|Xfvw-MV3Mgr_XkIRozpc5k=aa1g@m9gtNq0> zH7{Kq5D6uXo6f@x_N)*HsFrO&tp&R62;&FIwk*rU+7A&fsyniMw*LqBl@4HZaHZT> zq8f>OxO5Xi4<#LHK!O16Na&Ej^)#obFeJ5cSw!}K&;RFl>1%0+{y6>5%+1VG%x|$9 zS%>`s`-^cT0<P3HEcK5i7;jX3MVXuCqP7g{vqnvV8`jOsF!Tyg^7o`$mSHN`AmeX4 zFIkbyx3RPAT2XEM&Eusj0+~>mI$H~Hdh3eB;#2TuHvu=BE+2E-imnd`se{Hth)_m$ z$FUVx>yQrnOhRh+K?we5mMi$5GK;x|a*$mcUlI1?Hcc<uI;q*7+Wrs!KfjLi|M~O( zbK^_gMfw7_V{4s*fULt3?a$O0NxbEm&svr$W<?W5T3`lbQ<LGNQFe?hf_1`gXm$H7 zE>IV^s(%BhNq^1jj8r{Y2dL_4JgjsGN1!<EXbN=MOxaX)3(D;_;@Uczp-b7SaI=fm z1#U|cz$$z+>I6j~Wz1y$0OPW}1andWIuURtmDS;p)|53^l&UH(IC5?N!~XZT;s5hI z+5ZZ+_E$dzH>=LWI+(IUKLwL{{ltkTqWjs3YDt>t$f6`dRa3Ae*lBWbZ>3akd})8J zn52!~A#({NNU9h~JfLJmHHN_toLL|khyj%+*Z`P_hziFaz!2_|cI<M2>KplAgE8xj zLnB=Wuo{W{ab?0OD!l(fs4SYq27s3g=#AMb+)hcd=Gy;WL~SSj|NntLPJfdbMcj|S zXZKUv*$=s#KZ$!sgk2Ai2yo2GLjV#NjkwIn%?E>bQQz#5JT1f>0^i$uq`ett9Z}F& z4;xhHD^bqwe5x|l0|8+x(}}{ydn6+aa-=9`&+$}cXL4=wU5SA5M53y}2xywK0%6W> zPpsZ>0$MJWi~54Mg5Hr-^vTTlY%XtM{`pIDZT~+;F~35;hB_AKtREvyRo?Bc!F_Yv zN2YIZR&`imLrLSN^A=;UuOe(yHN}>7MZ*#-LHWlv5f}imSb0mYs$(v0eWL`?vg|DZ zNaJ)JJD~?140>f7#%!|6frr%)y&h(OCdbJJu#+{uD&N+BL#{5jz|yn?AoZki(|Nd= z8YF!{4Zc!12fpOtA<<BDgai;&%|M8?`uYFo=%>!(`Tybxx3_i<kBwc(R|A2qqZN1m zW%L`b4-CYxL;Q89q{C)U&pNoXM(jU$`Goc#ubS}ixyMDLZw{_a`*kY-zRgS~m@;NE z4>aQEP&GK*iolV-ZrJeb4?mw!V;8cfloizKo^cnnso|NK3wa3Dp=irdb-<)GeOHM+ z7!mc)K*V?>t5_I+25f*;U4*{`#FIA>H^sC6`8LJ;1^s>M+cEa`3pq)3&7JO;xM}#- zTQATt(ae7Z{f7kKh59=9!s}N-<!xPuvvAoK9Y<3Q1%5DP+c0$vnz9C0z4(p`eT8ji zazjlHy^Mb2p#kkFOBBEiz-J1iCO`(hz*VB`A}Sg#RI4MmyJ%eCDw53yicq`9%H?7x zLFcy~GP0;c|2M0^Ei$KRkeoomY{K`1B-y5ekRas#?=tEzMZJmo4t1DbPw%JSg!rE? zVD4w$2RQ)Go@753J>_45dyO%2uhFpk7{gbSOwc4I_<M`dMT<0m`euXHLR=%C5H7dZ zYn6{hAT-qO=q|!%=sb02mltdPv<49f%kp+Dmic6tULhM)7E9%Vd*5^y>4d%U@0;o* zCgv8(ZY1ixU7AYODI8oNG1+z3IC|f5i3IPu_IG(RT(8MgxtKG{yYmI}<im@FvMY={ zuv}n{>P9Ub;6V-0-qHoMg?T6Ey4pO{rSaRin{&#XwddTcy2D3N^26PhT+5%?y@+GK zT}0SwQk@3TBQ^g2?Zp4*7pb>L&-C;Erh3g@cg?<ly?xZjT7BL$ALLq7gjVg=*Z%e{ zIM~rds014b3?k#2OOfFqh=VV8Cu^!gb<<rgI2W*6i+bC}>+4b^I*lal;c2}0@Zm*x zLDzX*cX%DgD}=IuXCN54TD&UqhU8RloOJQIfG?OE_ykw~Xo!NTB2Wq@@qiLdS<YIV zY9c5CC-Y|3=Ak+*;y9qgR@`v)f7Vj3rSR`Z*!w@ltY;oY>|dUJE&Dc#g3v#kh#<D! z*x*dr+v1xeF;|N9@){!t$li6n+k@z@nLP!w>&RL4>Ib(|G+<u~Vk<5oxYweD{fQ|Y z*W1u;BSuLFlJAWAE)DVr?@m>=CNp;HTR6xbO3V>X^HvPvhaXN=?oY7JOKkoq(cAx_ z|MLg%|9SrOe{MP8F3%URYVcCSxBU{_Zmh>oSwFL>mXejT*vVk21)&!VH$~p&bz7EA zUDB(Y_q*s_z)n!==rt`b?Xd_ZS~`V?61n<Uv5Sd_o}}aQ#DW??7TOY)q)NCXP^z2v z^$ki*%Y1tbf{B(+;iCj0zhnU`g~R2)fG}ygCc?EW2WOL6&Twk{|F<aSf6+gnzIFb& z{DexZUU}H<><ifYU7Phj+>1>|vyLx)?k;PZMpl%0*vgxx3YRwq>|WvJ6<w^s#Xyxg z<ifIb_?BBPRDo!4cWu+=7VV%Kj^t|(1l=Z%6a<6M;(AqAEKb%W9r1vn>IOG}hE%=g zpbJ7j93+DrOc)IB-ab=LvBYZ+98EPv74f4T8!}SFMS>l9mJ=;FNs@TS<UIS|n<?g_ zbb-3Lo7q?uj=Hd3z_(VxDVLczW7@88%2#~|iQWT~FPt(Ab7V4qLh^<qV&Lhjl9dpa z+H!c_!OgX<i&+7lysPSz3)%&I!yf|8mmA$X=es+M#-k~1?1Ub4a7$r3P)(Bs3{*oz zcn@1wWfS|mYCsV(Tb(-MLU;f0!w+B3YuVCV_tMY=J0W5PT%s6%an!7A)`1Uj1+pYe zdmWKwEXy>ZVrqb)bnSnK=u0X3Qo2n4F>{Fd3cG{-N%mRl#m#HM=~N}R^0P@XRhjmv z%m#ihWeg(+S0&3lE(fP2@sV0IXA$7Y&cM+jtT}S0d`;B5Po^q|qZ^T!dRwsj!pjM^ zt)q$QAcl$wwA)+Zj$Lp7XHFN2CriW}ICJd4zUe7jM^9z)IU_^1^XO?2Xj^8|w^+WX zkg6Py?#u+r@>Vnt&U>NKc}FrTu)*;fy1N1l)K@V`5B9K}G4Ga$l=Zcm{ohuK`X+rb zeGmN!`oACr*b4uDZ(;tCJ;Hv(TLli_?!oNhR<CUX{er2l5!0#@D<wy|h5Bljrcj|N z6JiODc4<2nq7)dQq*}+0p@F4k-qi$Xg%_~9NiWz5P_0d&dyatI+{(!XX34!>+DnEo zEY+^MN_O%1%TTk&msztK8Q0~|=vY2qIBi>Q%tpi$ET1XA?7`Jrc2<Uxqg{_?WtZIo zPTY;UFD6I-h@B_(zAtgIrwG~mYv=!MB>qR9xB-v)RLAadxv#^+w%rfhS8x*5cZ_Z} zm~PT~yq2NB%R0Dy1rb(ZTg|~G6$1Jh@JXvVh)eHO@4nl`aC*2@>NgCVIjMN?gmNt- zi+`#z5WQOD;n^P%KLkXuhd4_VbP4v7@QQ|rDeAGlI#mS*r^j2w!iJ=cozR1hS8Nmd z<{b3>Ao$a>vZI(ByeaA;2kmpg^Z)lkiupA3e_t4${Z;#AF08rX#~<&9@!EOi?+9M! ztkIx%&gb<3E?relf@d!bUrVx83EnC!T~eS3jEGfPwYvSdi}M9+<QuV3$G~(H`}tD% zku)Aw@M&m4!NFEZv<ld+=^8Ivf^BlTBf%nCtM0$o#p?L*++075$-q<(@JStUYo8?= z!}jStJOvCiHEY8wB4VRheAZE61;Aq=fP+<slPt3TGxSXq^GZAhpH2u=kKgAGJ$-m_ zu^)zBo3+VfCOZzl=xotLFglMn3~gLz3OtYCJP2-#*oN@e$!W4AKr2<?pukf-b>gxu zJ5N_i!<C`RP@kgPd=%&w=H~oe;oY3s17647sEw;+4(29cOf9&V;A~#D;W$+kEkmu% z|63{MXXsly4gSHqwg^;>2i!GbX86%Z`)$;^r{ZHJdwkE>(J^uuj6Ggka>g5}E!n&= zD)I2=A-VfJtRfv<;&Ex66(IVTv$k#v)#LZ~|HA7Yw__vh2`-M<Znk*vhVyvXVv`q$ zv7~9@Knh>LJakEAxS3ZGB^^e9uKxcTa`AUF^%?rb^oN+O%)8n3?5o?3_RyE7Qk9wH zrLV!Aq-DGSz6Z{vDkpneEUcsT#e8{kPM@*I@?~#kOh1;w?nEcy03j$G4Yt=Wc>WYI zEO*QI3V-m)RORe>FTBxsJ(<g&CI$3No5mA^K@y&-wj3F~QHTqcJ26jG`w>&OR-$<} zRhjE8(zh3iMh-EF=H2mK6H8)bTjf;6>}|=Ddv@pHwmzRjw6|Jw?b`m2^Z%Rarqh3D zvOh2XsGfSTkD1cw)g<0_5|MvJnY~R3dV4quKwSwtk_Jl$C#!0fMQouB-2H1}b#~g_ z@J|ju`DDLwRX%Lt-J?6y0(qC6(SusVJ`ZbiMUe$~`++hk{D0f9tA;|5r1FaA|Ht+J z{VjSM{Ve^L%uATJGylXMW<QwRq<1e}PT*qX$pkJ&+8{ig*sYFFylD@9&?MJJmnK;I zKmp!9t0#5q<+UNOl^QFt9gEs6&HD)^bOM;P+YzmMFkDV2K)h+wvmYR}W|Jj+#GbLw zERa3bNeb!7J%@^LlXZp=ojDFH9!;dmHKO|pCQLLzdA7FyUqvxb&{ril-}{|X)i&G> zs2^`WT4#UkQ)g?|uC|}Rg5&Kz7H7cagv4WahaJ9<RYW*D$G$Hj5``>69r8)t<*?2U zS1zqw+OJ!E>r&n=;*+f;x<bB9ey;~~+2Rl`Ud&pi059_}AHh`$ah52FxL)Cu+WdF* zf4kED?e`hJ^7>cjEO+eA4Oi$2-EU*J-Ig1B)V9kUZ@jk4>8}-<0=FGp7Pr;#QO^sK z3>#Gk?ml7T%V#ycI%T?Ra=+;RMzKw?Q4UHb;-MSN;h_rWKbD1f2^!3P;WbqxM*suP zV^jf=qj_KcKT0w0rAJpjC!XsRstb-g=L^FXw!-$?oDXoq4Td%LaBQ19-neX=)f=EN zMK>il@s@Qc^N6MaUI9@KVf2XUZ^3?|dY|o1dvo~o={^SBlecw^7s<D*Odh-%9L!_N zo4Tx9(4lb1&5F%CvL?bHD66>k|7(c<-|f^p=qu>gF)8L8^B-)bWhVDt5*s}6U{ucZ z{(`|Tv3?vvv9rCE^DmYQ*aPSE^5__TB|b-I;O2GK8y>mERK@O{47gFa=N18j3r&9L z@l<8;e3ZH;S6ayHXZPjiPa>A3*XEPG?dL|DNwGQStXFKll1f!m=cCn!JTF#$`EUqv z?8rW^8goyiDyM%OwWxLdJcEh8mYO2d{cHH~HF^tVfPc>%XMTyjiG6F!DiHa_U7qbs zErI_RZkLNIGwmT>&u}ayUc)oHn?vqig3|UT8YImxs^Lc+^MeRR4kum=S=Hf>DPJgh zI-qG3b5CMUF?`^rPUp(ztXBjv>N;VkDieu}I8sC-n#Jx$s7_!eqa-~sd#Z3r!SD$v zis-F4iFHrib3w={8;Y4kG5Ow@ZZ%m>qA)Tc7ZMAV7_Gf|oXr2#)Su%?y^H#D+M@r1 zInDeldyaiM`6{{3sYg<kLL#HCO=D`t9&NcyVCj%n{Fuvsta5p(y0LfdZ|rhM?76}z zM8qPF{A#}+FiH!fbyFwTNCl`A0LoK6Ri+r%VbC**71fLNFgds2%A8Sn5rx0Hrz2v# zP_#!UN~XSGd&04!`i$T9M^XG6DnqI2Q1WJW-m(AoTpnhqfE`}^&GJ<FY~FTV5teF| z%FWMs3;^{iROZd-h+NO=LUtP86l#CLTFlpV4yZTQ<bM;y{(k}$8GM$5K-HLa*V z1=#Kk6!*Z{beXiwY~D!~Ui=h8#ub5KAi@{<r|O1@z|{)EenBiyoto)e`JWH6H|M(p zie&0OhleUutszB`;PwNu6kUbXUr<y9#wY^EK?z%|&HpV#|9{JJhW_&+Pj&hscj(!} zXV3PN464BugwFd*52kh*mzT?*+yZU@Em6{l;=hg2hp=6OU~3AA;Q@!8)l<1XRkdnB z)lQ((4QMU-SCrWUUJ)SWmvqTcVF2i;Jj|#>hlg({%@hpF7K#1;Mbu6T|DLA)ir!6s zoLPtHzdvI4vmdU_Z5{d&L^I`4X7*nEIJ-hMga%%XG5HWFqp~(J%B^yylSyb$G^*~4 za<DSzbENK9Csy}pY0;cPO1ijbC806%Wl}^Yu_9s}MhXcd7*Qw@cqO9y(-;Q9l0g1p zZ<>_n;yIpgcanvitH!G1v{WdT^?6rQmwJk5LoF`TV}^Gmsgphm$LJ?rIVkmt+Wvnn z#XLn{`@Go8yK7|C$oC~@-x#BTr3we{Znl&_&Shrv_8?g7+lVzIWDUV}PUskdrdqH| z!BxMhC|dRGNq6&K9ImXbtnE(*t98ri675z3-GJVj+r!>~6E(xI@K59=N!2;Q(gX$G z6L`tiRLQmfXQ;oUm^>cO^-uM&d3T_l9<E$mxwt<ES~Gt@wzcl&bsGxZ47E1d{dum6 zDrc%N^5sa3E=%EI)F<OF9S;3){5-dQj=+ywx60L(tDhV2hc5G00%L<vGE}@iv1E9e zFiZ{>7NP@V3!DZJEs?V|nU|phWE1{>4JA?d_w%$uf0EhE{4~3vZvW?Bl2%gQZ@8c~ z{V!L3yVs5bBF7-_iaf|))qZzn*|j0R&{O$wL@jDXKMZrYnVyMj>wH>>1;iw$o`Q*( z-urSJ{2?jy6~K9}_l4Tlp<-ddE|zl*>GV0M2G~6<x^X+<nc9*TKTQUCPmA5JpP8JS z@h+KbV~ToZ&pVd@T+G>Jz33}JP|vvhKlMe5*^5X2e9o<3==-gt<LcJUCJ;?187kgZ zqQfVT3aPxv$q?p|pqz#*DUxo&4OdoFvgd??+b0``E162BKezV?C|hn%696_$GjW1m zr`fUyH!E<E4xiKrpsFGmqGlR8hX|m=VZ2Z~|6flruc5E+pXq--Yg(Nxx*~u+Ge+%j z`@ZB@i^{v~jQ$D&DYRi`O}O-i7kVhAYgrSX=wX<G;1oEORu>+2&u9IoUfrH&?fz^Q zTz`;zNx-AoR1b2|5@bO%a3F&uKobm@VPP4^wW^MLR-6m!^Z(6*`QLt3sV<cJb5Ob} zFk>?_MY~jj3-tWi+jD33`Hml!99hx1SCF;74&W+U<=_UKvvdTb(H$M003;Hj4gpOZ z*#DRMcgdZyi#hmjdBiRr(#yVL_tKcT%2KU)mz~jr8WL7fP-Inx!5;!j!V@KotPn0n zl3;2DgR9#8-`x~-H}z?HEv?gEWv*t<Fu#K+0B>qPADX^x7_IwPZ1_ojJ-hPTik8zE z^)$JHJiT(t%?B{*l?h$2G@Y@iXA<yw5Vai_6QkWa?n+5ql|`>1Wq+`&<f@FQ_(6d> zj}Q+N#bTl8s`o7>NV!nlYoA5P0?Va*$|0Ad!z;TSbs>S+>l7p%eq_j_1vu}UnX_wm zeW&xJm}?U#rsjQlpM3^5Irf3YIfU1kIy-N={zBnmis%)csqOy?#k_-7nr8Ok&qxHS z#;Jb17W4)mOb&wMI?nY$`<zQ}PTmv^7wBzGxF`@+96}8pX8_*T1k1_lilfLRMm==@ zs>WhJgB)I#x&b+yGd9TKY(B`kVL{T*$$}v}Hlk7?B${c!7qFS-c!UM3$^X7jF^kmq zBZqq*zBqTy8CMu}@o?qZ%C&tYjB;mMjFmg~fQmsY-VlpH$QyJ#XK<R%Wo5AUlEn+; z`iV14OBD@A;!NY*n$zx)>dD~>{_D5S>9~8x?v{>J;&n(z5?)t0nl6xNbTZtk$twOb zE!bEi_NQVZ(wgMi|3dZWTI#LzF#RfqW?sRrtK0vc2Lq8--V+#>dV0C%&atSIak;1V zsQyM8UaZmL#q&~Zcq2x!rz%XU@?_5plf343V0Tf6)0H);>czdSLyW}>H96B$D|Z)+ ziFvbNHOzm`_SF8jR$|Z!$~A8++Vh?z!Xq{QAO8P8I`IF$1e;TR>=Ad@>O1YBB}YNL z?IvVR7aT_`%<k<}Bqohysj32Tm23(cyweK`!l^<~4IM$wsZO1Bj{|+?|EJxgj|o^{ zrRY9~hpHs1I8~XP2o)^Of)b#OKp>`^)o>XI#URiACq?f6QcH~g=kDussmI)z{rGTY zePw;W@uRhQd7v;qHZ?vu3A1%#aSFc+agEIa%R_r2F$bT!&9tZB4MsDoSrTU;5D3S^ z4J|JHB*9cA0|9C|)z-4rg-6|f=)3#B0EDg9#@)czlzRnPJ;)Jy4w}9)Cv&E(2^>OE zX&k2*nj@%&l~roze|k5?ycLhZXBh}or=IBBeQ6(}BrhV&L03yfFjKq%8qDF1OT;}> zV2DaWBO**<RuZtqn~Gt|I{Z{-1*2*|?xNaf9$4Ll;7-@O|3uy<BK_``;nuA25N^%q z;p`yxpGuDU0vvxTu>a#UkvL(6^iL4ra=mu{cP~ZV3;o|~=$F%f$Z*Wl%wMs0upeE< zWT<CJ!d9(yRSj~Tk0f?O6=H0CHxZB8<Qisu0@pB|_UFeM*QX1fo2c1<`PgWoIJ4XV z@OT2AsNEh2&fX_h64=<cLnD_0xRtVtzDJNJUHi|W6<+HSvitSid>Q{YZoJ(2N4?w~ zEOY`T)HHPJ4tHHQ9#q90?rRexKV=qk3uVan%Ki#MRSEJBudD>mDM`cdmimd>`F|(H ze2CszpS*)#+9OasRiRu4weP~zxf3~JazO4CZ;gq_zoN|EHU<A4nk0w_=4<gH4yQQw z+b|t66?`Ep2myBYlP){ZFQJnom%7+shZe|q5QnmOz$&JaMYK;-5P7&%vA|+Uh9q*9 z3NNd+WN|h7A9!n%sV~tS{XS*`^CtE(_BHhhP+66#(oMz?6*^UUytne&PMan$e5|Jd zsiL{K$9pdz4sN$-=@Z3NWxBUmhsF(xd=maZOHEx~%=p?Wr3^G;PL>v+y_Y$5VBhqV ztryMNOg?91kT(-=s75R$AV9smxAsPaOK7);lcFmS#xM4^;kBY$_jgi!6<vLQ&x&u^ z{7I4C{(l9<ypF!2KIfm;zf{jw7<ZHJJ9Bh*+3GOB;!Vvc0xQeyZAZ2tNE|P6utnh% zOEVP-2I)rDg8r{;b8rPPSD{_-`!1?N0Q~Ub0Mo1+=)#$opVQy}Ee)8f@TJb_mMA-> zrb~jYs;VuCS%d+p@&DV&{D%|p!6y=ds!>_xvY>tD<S){ic<G_l))mb%vw6c&0t>3b z3!SCFBNYOG!|KPe5#qy8InIQeD$IYjPv^gnRvSa+_?R_kUVcswa5z*{AW`MvjKQ{5 zKyBg9Mc_@#F*#FIJ^R1gDCYh2ZINSt@FEg{>U3qbyDapXZ$Ppvw3t`K+N#=Rj5SPK zit~mjZwio4i#iVdl7r)^2^SSMELKHG0wlxXtH&!T7h-*Qaj~B+XZ;l+<WC_MRBkBm zMz-bT@xX<hFJgXchGdyI_`?GT=jgftYi-Nn;0VO^|2IUvg`(a<zmWa_a|82M_9FIa z_Lc5{Z+G@j?jRWLB6;e^R<H>d6y9YR_3Fxx$wTzfoORY0jgv##vpqeQMt6hfnBp}h zB7tCf6#3jM{&->W=%P`_{<V;^oakAZWIp*0vHjkD-J`@oeXm*>ERh5=$Qg^}hM$wy zBk1_a^HO%xr6l!+8vj2|G4G+rBR9Fhi%tk&e_GS8`%{y-Vq6BGDO;@lsVR@QKWSOf z5M?N-*c{jZ1<olHg-5h%LorRb{g$iuRfb$}`z-z!;jfyFOyi>4oG%u<&6zxW6;Xi$ z5KA!4tijtn&i)qo07Eykh`{dH+1mVHPca{%*LND^&zpDWuB=={PN0YlQQ278*hl<k z^hWMprZjZUmmE*=C?!P3AE-8`X;A(p5$Y^BtCQfKUL@iI5nd~FRj5u?*18KppXvX4 zST^q5<E#QLnc`yID8_@;7B!;rEAy}bgx0U4SctBWbvOm*D+xhXYx+NTQq-N)XXs7P z|M?!XgL#(uYj%SDVDy}Skm$ywg31W9<eLq_Z;D+jvP2KNDW5N=UVV}jw6RwO1zB(E z*(9_&q8kPFntILM(CR29(wYDVO}&=r{uEY5mJGQdlT5vCnn2GcM>Wg{Q?I`dm3}fg z@<CpgdIRy5@OW~>195byH?~fP?gxJjZX69pNqDnS_|_cXGj?>098ah>CE!oPrSFCy zx(5L#;b_sN{FCnfPyIf{JdDS4>4SazlIPk!9$cNe*hbm}p$bxpySFSs#@lcZW7dJ5 zzbvYv36C>b*+jTm)gph(BF9PR;Cb2Y7kap|p|asQ!C%$4r<HX|oy-b)q)x-@4h>Ef zBq@u4zYZtaaJ7`>c~dhjLDftSKG^H`f9D4Ge;t><>fM#gT>i7qRM0&BBfuSWF-QZs z;*QBc7H<e<HHpWmUe&UW0zcroBq|8{BZz{*!7dGfz0OuHb?^N8PT`^>Z`TFPZB|xn z`*9I(pWC1RieQ<71Gg&hx29yxtO5fdL<3hX8$Q?!y*B?<iupLLb{zkMmmvhI3zf^= zLECq>YGhdXe0`V$k27MUb1ykuqqF)8g`^0w;~+E@XBb%lmO!@7aWD~r4XY*TLiJE( zox22|7%mk0h;axu+`De*3M`VR>kJ;I@cF~3JZIUuAsDdtQ#l@97a?VZhb0(Y*|qr( z`~ROD*#Cznc<UckyK;p){QAr$EjauF$JTCVdmw99gE5fL8<Q$w0t6F=sf0CEA>xDP zAGCt7E7_VP@zvSNdKc(EbGaM@Iw*YT7U^K#uAmNP^sqLwx|TJto{ET}sz|(~;dX_G z3jiICm~_L7|A7$dw^CoBMfxMmb<hB~oUOJF|L`|nWQNC5m4|zLaoJWR0!XPh^)xz! z+nLSs5>o6>_B1+#TP)Ef4NMzS>YIBSa->%3VyRr1gPMQ&Y(oj+ExiZ1p%*l{$J-vM zpXz;c8^s1ff4sH#dIwDazpban7H-EPnvUU`B)718)Z1(O|BV#$6ZDPY89w-{fdI7p zu5@>#KGX7xTExirejKk1%uJo!%#=B6&*>S@aII~|<>m4j(X0jMbL8A_nkMe}93`uf zuwbSt7&fc`s-?=Xd!TxFm;{^ZJN7<mn~Db-nVGGE|CQwRVCNiBlNE&iH6Vr+4Gk(5 z1~1B-EoqX4Lsjkm?@@|+l=?l`|Gk?26J|H_KIXsJL+nQo0hqYkP9UnOJDh5Fla1xC zS9HtNTavmjY3vcTt%>@nq$=H@?Cn0(Ta&u8Yam>v-j;-Az1w>cn+Wyx#BK%aB?Q@6 z>K#e-WwU||_0A~_?zsf61P|$Os8@^A`>q81)4OmtX-iQ*oxnx0OLz)eKND~TZn^h= z?`E*cT}+)ITisjn<7cS9p?PQle3Mzv6qxU@S@!jfU9kJ#_U{42hvWOuAj=?TMh4nb z*p}d@dKA3xI+S~0Hwf)t9-(7LCJC7rg3PfoklEWknW@vcvN=nrycksOzYA3E?~cmR zqCQ`83dOn1#Qfsi(E`yA134mdHD3a9V_lL1i(VoGCP3h!y&y0hpFn8C-@Ta2TXr#X z?_8eHn*zOu6L6vLZfN6qy;KU`WIq7vXWZNJp=DOr<4*!{Z^*Bhf%*)twcT(VITTV! z#NUUnz~5|@L!Cnr&m`qvCFl|KEC|1@w<<0{WiaYh4Eqpu2}OFH`Y-(W6nz<elKv%T z4ReHfC-XP#cJ?dmBjhDTu@6&sO<<tm=bl^f6JPy^OsC1_6%;Stoj^2=`f7F1cnA?5 z+~w*$p#6~qwqUX(xHcBH<?_8XRcz`LApf}pR&YXozkbplGfIU#6usTLe+k5YzRNwn z!6mpcKr(x5vryE_g<@vEJy$>&N6c92mqGZKNRLc3_e4DmeTyF<H(th6xwr^m361Z7 z#;=p!Jsh6~>7bgFBD2q)(al<Ieh5mxvwTWN_2P_OtJB|u((jX{_C)+TP0o|C0(`=& z0%_uZf!v>HAg9GA=XypU0MToa`>)7CeQW&Oz*jHZGcGRl2nbvroG<wMUv`1uSbTyQ zS6Jyv(A^<bg3}J_M_u;&(ZWf4J~J`5P_EVL#@hM+W}^RpGw2OIu@Qhl@Kvr%pwA3~ z-F3D3EEX$<c>A2>+ofd*Ed_e#Q%%QhpTHwX1q{^?m!8A|ksJ-70yroVaR&7NH@GPG znf`wbs}?zKOt8ZFmX*oF7haWlXjx=!3t_(y0#L9?z(ql`B?rC$Wwkc{vlR0#IvacF z4-#VXzo2g?e}k)NNWSBZv6h_6%;pVFc>GcfQ74+|f&n97RpM-2;V|=U$<%loV%L$r z6}vXDs$Xk#i&ib;@^X0)^I0t`DVhU+t0H1H!~e1^=uiZ5WXpzMaH}@|1)~2W45oD) z1mFbl8dp%=cTNDwKpL$}*;+1x;*GP2{42`rF9)#w$5LR(g31$?0LDNn90R~-z2RVS z5TXBbwYwbjqyA4oDhuX49$bMuU1#w3e-RFUWRvIM2pIg7&Z`^=x+*)+|AhwtPyY}8 zf1Vxa|8|`H*o$7+w|h~u>^t@dY|a>O7&hnf#v*1hui1{IX>dpnt3Ey}$&O`myd=w# zq^r=W+}OuDmGzY|cz+Qa>9)D!!QD2ihdEq*!xqCaEk(5r6IPYBW*U+r<Ni;Rc|)oB z|1>Ddpnjjei~a^9F&|;CVV~_d7b0ISq$<ao)q#4iK+-(ifI0Qfc2!Bthb8Hk(Z;n8 zDezsrR|{&o6C$PlbWeSAxXlb*7?CpHT~1Z>=3Y(~T2f|1>`MxLPw)E^;<{W-&P)n? zZ|^mMh!NJ9<zB>*65rSRz9c2?vk?p4*Ry(mjsHJLF`uFjMo#O&i?|3tT=gQCOXx>& zRqOf^Z~tk^x2#Owb_I167^z8|s)FAa6jOu+K-S{2*uNYY4C&SVl}#?b$A{<U`b|eD zv|+?VsjgbkZF&z-RmgG@VzFu}I4{Kg)GV17jVyS7+pqw0ZT?SE%m?YoxPyF<t_=e4 z0+e<~W}kVfBjakcWxtI#+QQkElgAsSvaa*8B1w|LiNrl6Y<?6N2dH=m7J`Js`0`p8 z+&(kDtb<$ExWPsrAE$b*Wn}S?wX(ofhCe08(liAIRTcvOATqNqIayV%`TxI|g6Ka> zy_)(HI!nKf{xc@ayo>o~c3(RWAl?ObQmRu8o{M^Kmv`eKEta6#YJjbx-j|fxRETq; z-k+3y10h>;xb5l){7hmP6rs%T>3DtM2yU5A#=m8b1omEcI1`5yA9RBwty>w*gNx;b z#q#KQVUEPfYITWEeK>*6^9ng&pU&szYl;{jNucvQvQvd(+0}F+pnNm|O+}9`85TIz z&*?R_myabt`KVng6XCCEA1F+kh51wNfctoD|35+ae^&{x&CWLXYIz7iTz$Q}Y4xMH zI<Sbw-m(ID;%!)g4BmF7sqg}5t6&Den<{YoC5a+pg~C=)loegF!T(>^xBP!&vx^OB zBTGC)8|ghj1x3g@61;s2vTh5qXv>NKcMRYGRD)L)&;S2`|6kq|599*>e?#B$|70RI z*pS$xGn_5n2o2})`G1vjY#Eo*2vntrq6NDk8JYmR!-=|J@>%fzoBNjkCoK*N-Qwd^ z&$Wy!9<mB27^Y?0nxXN6Dnb*$Hf`0ibj8#a)sO$RkD~TbU!zCq&oEn<x3lZnX96QT z_OItbV%lJ9F-WNo6jGIYdMk;p^Sq?I50b!VkM^v<r5IvTg%9^sNZiUTlQKWjQ-OaQ zR+<$0(VpHLwdJ!(u^;Pcv2B@fQtZcjYVX`$-JO*C3Bv!g)b&LF=K=g^^Qo$pt?t^p zcX)cbk3_Dyd8aU%o6n({OD>;&+Sm!b)zyNWT2gcaYHJRzu?;y3={4M{Wf99-Muhw7 zU6mVM?Cu#pd9shNBRGr(m%b%|3FqiCxre4<Suz6DTQXu*s|KRhLrqmS0H-1Ia296Z zK()n1vo8-+p{*+(Kt~&Sy3XM5|E3I4Kf*2X@U&)$w#C_5=m4mW#OY#I<Z9>tG{rnZ zr`t^a=lxgUJh%5F=h=2AUvdz&=2}J;Z{!J_!oks>frx*)hDmM9i20|QmZTXF_jA@c zrn1e|_IY%;vaYhOk5)$yrAWbLoE>K=8gX%ttc2GKS)8CG$22%qCmz*A8P1mw4}jNk z77#_r^Z!4H|FztO4r+lnx$8rpc?R*uA32*g1kGIx+;GOYqcxn%8=^e4eXXpjXo{j` zRnvyjuZFOI2E2m7b)6gk>%}g_eRi|zK^(jz=mOwiwz%*H^Z4^$6)Xu$F%NZrqDw7G zsxF$i6vUqrXK1zke_;R9<@|^J&rWwt_L=ct<VkSJA=W<UGP8Mu(@;cSMl^bnH&q@{ zfw^oJ5kGiD|C6(v3~it3%8q_qT%(TpOQO{_<MMKO5L*T(fdeyjl{d0kffpSE4i^<e z(nZNuRJEr6dk00`0r{Uqe~ft{^Jex1>>InB4?UHdkf8@_Dt$4JTVi;rsz-_YR8Mto zy<#CkDCY|Ebt5k7)90hHUa1WOJ?b+()qr&yn=9!?zK-r^&!j53o(Vex1;T-~U7XkJ zN~xdgt(dh<$w%x`VG&p3C0{T0^F58o7pOr?$v-=ns!a71^*amD1c1T<m=Wp=#Q)D~ zdY(-8-=OE|zhsWX>yN~~fz43wrrKEqvPk)dv#H8tB6Y0$YuJ-3E#&pH`*QOq2|B17 zsowuZVZWCcAY>u+999rUG~@c@aVIy%o>$_(pu~?SR${X%4TyiRr^0m7#8J|?&}|;v zI`y(u#BU(!wB35CzIc2RC4Q#o3ndxKyYsqvGM{UV=`W(_FP%tLGKq}Ph8eFvM1BGE zzj<$}vOO94p1)OZ{QU-q|MEkr%Kk)%*8&t|+C&KOiu+#8|KC#-^%V78IzvB0|Bz9b zH!}au9%Dbqeh8Imd=5d4Y-VLDU%^pAebU!z>KWm=x&xf9W0AQ_bTiLEeJUa6K#f7= z`4#-MkHyNX9OfLT&m^F#>4MQ<4yey25KJpTGr0d#pG!iuunqChMoWD@fmmaKLc+v< zHlYFSZ`9Nm5*i&X%O&;2+WG$&#e9iA)|l#p-&TYG&aIh#Jh!?dx_QyVPO;<(v~9-a z<?_z0qOBTGUDHhkUTS3yAuMf8gWr0dR}jpe<Ewirqb|f#!!t8|baz0I3h+s>F!IxP zmDs~k<8{fB4JiE}%rfkL`D_-}z#24ypkM_^Yx92>@&C1JMPvT?m>Kh&@8fwM<`lY^ z=i!X8hGm1}@kAQl_~bQH;B=V%sa%#ss^xT@gr?AW3z2GMQHMb#=R(|P29@55=pj`X z9(9RTOb0;i3iI$)p!KUOxJuDsPbHX=gx^FRPJvWYH6aSD&Hvkp{{QXI$0?jln-LmF zbVq5Qg$8m5PK~h-GU2h8g4V3@26$^ef7{P$hJeMu$?B4%m>O(<O=$nCPyx!?1}{MW zUvPo$GyC5F(4Oe68=y#*Zqs{!8Wz@5+ZMB&A&L0u;4%<UA_y3wF_^M9|1Tr@KbIvl z7y6kBB2UTg@anV3Q-R?XI*N8VkwbaA8E~PD^^u1dA2KHhQ0?bcn45wDRWx4W;rvIj zMG5*pQh)0I1km<Z%@6=xqTZH9H|Vz|;PrxSs<y0|vI5mg4ofODDd7hc5ka8~XbP57 zyZ?JN_5a%Y8fZzX`p&9qpTYp5c_;#+jW`0ro38H<h5_CT!!XPY1H2hVR2=K`O*8Lx zKf3!dGlq@nPEO)Eaa~P3arG>&=eQe}jT?74hb$2#;zu;N7}Q84YW#>M7>ys%V4_j? zcW>XSuIjGp*VR)G>gDk0^XAd_*1dIq_ul{g-~T7~J3P;?@J|S@7rs`xzVf~b5gCvE ztf#_lJ=>=8>EV#vm?!T3#!UBMA+pwCS$`-M-?!RWjKt!9vhH@?w7cQu$bIms>36|* zTYhJce<IW`J@Cev?y+@+cmJJ!t5x^>(Qxi$dOuX3>F!y#{zJ2I($o81JJXel>ZwB< zqWuod@IV^>)56pZSfw5RI1R8)8ve*L^1o3cchcWLE?`hLVuKk4l7nqTMvP@L(P1?5 z;B-&1b4zj+SMpTVR3W}XplaByK}aAumLiF83RWTi(<#IoQ2rOn_46P*@f9F!JUss` zQ%B}C2eE%h)WrZ%l>*VTG&lmN2)OP>`~N-9$p24z--iwKc6BYfI1kR&>l;is9cNc2 z+QHES`D(NEiHQFuLevKmqEVWz0n;=y2o%>tXnNQKYdYkACWUeX%KuXM&B-ttN*y!R zLkwR>vwTUkAw$82#}&+$Apesk4=KS(tToF2pGNFM{u}ose-r<H;d#Psi2n09Yye+d z`Ja_P443q{e|OAm4s-I{1RH_+$=e7-Yl{05w7f2j&i!gZubJSrxeta&oTMV3B9LIG zxd+^5Z=UO(cJi;C%`$Tz+KUN1d~#icTru~)oXKe@Z#rOnxj)^Cm)Vx<WkTMT`^c4e zcE{w;4)hbm%!m6Z*7V%IX&$GEnrIsz+lMynsoNMNd!ns<{2*Gpqkz_GJC=OAvARNA z{Y3x#S2^KbygF`=KT|e90LK4Y>6x?vjsHjK%A{QA=#!J3OGNyUAY|-hf<Rig)WDWS zB&F3{%O`4m!!QlcQC)lulrP`VC?^hvTqq}=oE+uEV+>_QL(ES{)g0LuefU}Frh^R3 zzAoE_8Tg{p-~ZQh!msn!4_5dyf8|G@CwI5e^}1{JHLuwSO#dzRQr#oT1j$J-c6}yl zX0OLYHMagdOF;lC6Jh8b6|w#mIHJ>lz6QC-JBri0t9u320vwy|p4L5WBjCnjO%h>A zHYYU}&xu;d#(AR08U9+Jcrf<y;9rINzwDcE{6!*m*_JI?L2Mu*|E~z|;DmQn&YU*t zfBwDl%oEQbFctx*E9$d+4Y`9`?V97%5lf|2Sc(A}v6JZwXo}*<@8{Kb&2-yONx#>& z>}9{7>@;*bdJRH5*-uI5H%AEoUQ5pZ3ilpPc#yvb|CH{hH`BeEo~oD3ZrfJg(;>Wf zrjY%xXqcXd-!nq(O1`J)200u}Sus2$X;YCL&oO;iZi=?rdw%yS3WPMfb7z@>7+l~} z!59>e0x~FLpdbmD3~9e7dWNH$8YEb<f;2XohabdTl6zNnucV;tpLN~xf)dmhspG0$ zi&&)jPIGy3zALOf4B4258G@FLgld83=<xnkb(mPnfu>oCr@BxJAmCzu|KH0A-{L>d z-CMftpYlSz^Se9foLjT|_LX$b2hyZS95lQcQmQ0oH>I3SsRw^<M^hY|{DQO`=CY1t z89u^gkUO2)dv13-1*kT=V@K%$O4|g3v6IpaSV$2=1X=B1%<3bFDB^`6o3Lh>4ia7< zl7Oxwhp^H+yE{)2(Pnq=E-4~A78a7j9v=Bfg#<1mrQu@HMTuxqJTUx5;Oioe6C3FO zEZcKURj~>Ge=1kw@OdluGk5@hO*kZcxN=G5Kc{Rxj(hz~*DA!wVKCPC4&gplnCs(E z2T5rvu;o79n(5xQ9%C7F6&JOVE<aINgw)K@)%N+z%@$4WPI~;a!d!5(dpzJT!*#pk zlMRW0i=^96`ZL`f>w@hKt4oou_NR`{boZ}IFAILF4(Z4n{7^Q+{dr+_V#7cV`l{W$ zbN-rrI|+afFV1v#t;f)=wOvSjqV@6V{`vnxPI!pFoqJ)p=Mib~T1qfW9BFYF$7_7I z*&^bs<Q7O!$w{8c{Q#r1CbB83h+Ly;itEaVx2{X@ywJ4(cI+hDT5qAdlVW<?>>YQM z^bpF%C#E#Q`kWA>*hBfBV6Y<lFOq;EZIy*HUb7_|HtYyv?RhSOgd?3zZ)^7&3hTaE z+umrf5|TMFoTTz(Fp`=KzCbcH+p`@=U_AwPzy^$g5Q0%8F+nA;Oa1+SDJR^`FLRfM z`#<$xT()*oq)U9+8eYHy;yMY`$=YMFPF9VWEyaPYh3y8i2EB`8VA0C9qy`eMtDYV7 zF6&-L5!^9*=+FjPtFZ%jEFfXMi7GL?eA(~<M>Rw-fD@qZBK8+nDpJ!T3ZMwrU)WLZ zqIi|qj&gX#4UVBvzGy5qnGa6)6oc4;AGN8PistL^s#0P9ht$7F1FX47!3^99|9=Lz z$l>!4zlpz<|3m&sK^E>5?h}4kkt=sqK3w?-s^knnFapZSJ|CU!L!fe>ybfj&ho(1+ z2$wDQsT<IcJAFe*g24TGPNSt^#0JeVu8@29dc5FkrhdT~<HvnEr(sQ)haSfY_y6)* z#`NRc-0UwRL{Fdb<Q~asoWl}o10zDkxzFT${hbZB>GfH8?k}zein}I9ac84b@0{4% zY#wD6Gmq{@1Gi4sKn-U7h&KgwL)&k0pPlMekVF&@Qyf0G9j~${$E&nj&DM_Q(vnZ^ z0JMqE_s{=3IpIG3joh87=l(i;390^GO4v&*)sHRu5yoo}vnffO2j4UD3wJBzUI(F7 zB*l_d-!Uwhud6arFWIoZc40KIhXPSzqk$pP6eA=fxcC8cc&9?0Sj{9%nbjC-k_U?c zHGtuO4oRnKC=R>^RUN`Sm<J&u2<)q`r>K?KzIq6?+6X~k1H91(#$h;GJA~4=R2lky zQ4tMQ^JIMRj}?f(62CNZ{%_(AaroTNpT#fppT_C`R^bPgeU<+)X5$~8=^CTri9yh{ z;^c&*#|JqA9vO6Xk_zJF*+Sa>OkvJev4e*+{1>;)bd^!mn(it|fZ47cYqpNIS8Ug> z9lT-h;RWArxkqYCb*F}hYl+z(K9xx8j|MZ{orSlaII>7LpDlP0gY%5E{<-vpH4Q`~ zhaGAE^M!fo?Ii6Z-QKDjIn({+t7p1%#mvBQ*Iyw~XW6Zl`@*4_?nQ+(-46*Ew)<HB z{Qo#7e3gHM`}mk0U*aNg6Z^}~O(aRd&}NR<)2#0qF%MiN@O^}IF&qshvZCtx2<hV3 zh@EfQmgzwAcLN2Y#G1bmdYc^c62dz-I*ijFQ^XK)L=mDEVk2N6zeS)Zh-_t9DvSeU z1P+rtD2w+|M9QozPCeohV`sc*{1UZRIRi*Uaw=PQVD9T7_Xnganj(89w8h8+0);7! z|5f4c=7cBs`|wY>etKthZ>F=S%<kG%Qh8bbC{CPWiK!VbA6v*K4I*3Ff<D^~3?i$M zRoPT+m;ot@j#v&zgjMU_L~+?Ud+V*G#-)F89%dp^Fi5F5SuF;c<|E044x0$ua79=2 zd>BTEzUwLwCCawuVkzCYF_zLm)&Pbj69aO9k1b@lNHCX@0^M_vUqKCUM%adCc#@8Y zfG_|O?f&`yASZkS=l_G{+Wu?5P4BesK{~5*v-9(1-3UXxTyoaph1n%+GIdS00yuJr zF!c#M-H;^Jk}NpS;*y69gbHi~4^TKtY$G_zHz$XkWUI%OF;Jjwg^MflT4=}_WSUso zo@zjepo1;2kw~a_Zg+u#V$AN@Q`R+-c3Bx6dqQ@%MsY|N5&X+kEu>C|@L!ii*HcUd znShXLDIgh_`uiW}|4;b`%6I;s+kI8pr;deKzqPq~VAY{EFvFC2v_@j;MvKF#b9Ko# z0?TvYssVc~Es&AR%@45G;lm-R(0m`F=#*IV%?5`ERV^5vEObsR`P-M8?$I!^O&SvE zl_#&s;4_H8)q*<FGhkaS1%V~%a5{1%7{3}={5_$23x)5Z*{xekN*DU@vCDc20wY|& z;DL}jDoC&9fR_xA@eAw5GA#T72s~T~EYa)h|K7}TZ{{B6XZQvF_xZmR&K7PF-YI;$ zGFNF;9;kdU<s6QbK$k-Wu~pcH-}5;X5%E)x`$E5sBb{Penb7fmfE4#wPDkyri<<kZ zoLU*yT8w~jU(AWbF2CVZ(<ibXxW{w809jW9X*F%)uX8HsNE7|cD)jL$P4NaSG0S$Z zNt^icek>_}@+CERgBnSR*=!IepSrW!fwGN&@|9@CPL>lQ*4|F9PDOm&-&})t+CSAh zksOQt7x`*Vg>~#jt~;^fw?elO?rYP(&F*CzA?XM#U+>%h{vOBu9`_^u2L6wPO~UQM zUscS?yDINa+4me*n1FV4-IEvk;;U!YzlVaPw25s;cV1zyPG&v1CrSH%U6|Hmu&T&1 zAZ>rC(05PnX43wbmu9**6(YeNynE_aC!KwzF#S`kvycvu4*tfT>2580G!c;?J$$t= z6YAIy5)_2=^0hwy{}3nq5dJ?8rSAB0e+ilQYbYC6Vwv|~2H0*RU);Fbst0wrwI)l) z6fu_0avjHTEfEggx(UUu3+*maW(I~~>r&urxbVKZ?6)Q|$`SfDGW2APHHt&FU$Yfo zRvb$UJOe?*WaPY*UBg1~0TKaEhsfwwicX0|M#Be7V&sX!>=FhClCH~0x9;1JB<oNe z8L&rp5fNP05L?|8+5P`6BLBOayX&dl>QK(Sj!s31l`{ljc9_NJe**TDDr=R8lcO3M z60yh#foCBk8)CGZs^ogQ4T~X|XR5vV?rjvHn`V8#q*v6~=+hW)95Pv8q*c#Tmw~95 z$R6z2qNRzx2<sr%giaKm7pkn7nkkAPhp*iT<Zy&#%EE$Ok=7_?=pg2gr7AMKe;r3B z5m;ng`eBO(J1P~9z-<4&i4(rezn^>4Q@j7;z4fYHo6geK+5P*=ztp1})R^@h)ybNv zr~;cSOM>B>sF-$O`+?(%fsQauil+y*iHNP53ipB6Q;bUNK0wCGR`M{jnFT|l-b9rc zUJhIbEYXF$-_}jv5^dR&paO8f6nTcE8dy_@H_)1jv;SEzM0M7zz%Vk9VI5xLx(YS` z2aOdt(2+o=>>7q>=?b;~Kb2eN@OhM%_}>*~gd@UdD^}%Q@!eU#-{eN-%yhREeQDb3 zFWPS8&-jh?xcKcQ$z`$8$%W}}*Q0|Q5DO;k;G2cHC6gt>w&D%V#;f-2O*?SOn7*|h z^RnN$9cJd?sQ#|7jM1@C-PrQmFcz}ohH`&jnB#1sqvlGh?sx2g<Lndbu?}utb)uF0 z?e)0-FW7;<bJ_8|a9;kQfB#4PugCfOxpx$>&x>gSR|Aid?u~IZh;863!W>pvt32cd z5EE09k-Sl|O&G8!1~^*Hg>GAM5Kzyw0Fb+Z0E&^&EC9kPYn3yAWXZ8z9Q?Xw`le)g zvLm|4A*g6lpopR?^)Bo>6rgRhH{DczhB9%CWkL|0b!*pPF!_q^sR0;$$1|ZoHi`Ye zBElBP(p=MXY5c#_xwmone1SiQZ}RsEys#G@0FPHLtSnVNn5z9t-{aG3Si-62{&vSq zF|FhZ-b}y@4@!0tbKlGvZaLmY=3&cyYbP{FH%+BM8l0!x-|ayww@uZ`@R7;=eNL~! z*f?)&B~C@|iRq^<Jovb8Pp=(IoNe4c<aE=Ejf*rCL;E`?@k(y14m1A7A~|lk@8(o9 z$6tvUPDC0_+S)%(zmyKx&Jt6Ley@N2@8*Pi_#U@AePd7bZBOeiQhK|4_O{!~Pj81e z0w##yog$R44OgACUPys8QBg#kR7k(78>SxUwgjt81hw!G>)P{hPz4+4pc?FqF^~-^ zWq~!Q!C->jF2bak3KIS3DmWe6Fnr_#aYO_VhF(KN_!OVQRO0X{u^l`1#+3z1_Cxuf z2-zgnk%%cYQh&hySF!@6{Y3^wgav^GkY<Se{eOZJ9!C7n6BF(9Vk$wHbvvEUjUmiR zoGxQlexhF1tR*V3iP9{jy&<uhT^X^l5wTG+MOgjI4hh{RIyxBsI>l>a+=#*xeGF7# zl`NFPat4rV5?e@U@MIINJ~G5t2n_1OB3eh>RL=|GN_~U^RAN_ZGE~NC4slOoa2l$a z1=moukgVE1e9uiu_96Q7Wy=FCAcmyAge(i5&EkK)juRe%{_l0gZ0$iSFi~Bmb5ddx z)!3X+`_u%RHD+2yD`(BjXiYXbutOD{K#^tFhi1ldB#pRc+vJ)i`Laaf1ubnP8X0GI z#~~N5mkC<D62l8F?2hOomaXn8sx4WTY&$x%+q!|3Z7Ul5qK;C$O6(UEUv7zu#yG$d z6*Cb_)MAi%i181b5Xbdk^6w!u6+8e86VWGp8O)Zh_U-?cIBtpi9KVf!k5Caj;S-fh zGva>^wswxc!kC`xBGHU56F|s)H|z~jQcf;(e0-)x?q8&<e_Ri_`QUO!y7=DubkSP$ zBhmc#*F%zhAeLkH2kX*7-HSx!KU|N6bpRdILZL00iho)U14i1*(8*0Y`_X!+F=%H9 zSx_J7Wq#b}|KCRB|MwCx;M4vCK6i!E@QuOe#uj=OT@I_PRn90jf-`G|Z#jyl+bT>o z9E7lTb<IN@Aq9B>h`gXl0ooXX?S766rf<rGzz!+W+BFzVHbU5|5)y_fmS{){?gggf zd6te~cfNwa_JFCep<oJSa@hXaAcU3HuEAh3JXrl97_EhilIekGLJBa1qtabf_Y80Z z{r!JCiU08d?)LTE{V-;0(+Sxa#%vjsITLttTnrKNJawa0EhCVB0PT!SayED{*+9|; z;=``VE^cwS5ws|X8$)U`e29)kBs^><s>JXL;K=S-rZ3qB;@Ts{9Z4n#^Z*^SgMp0G z<PM70202XzT+KKX2sK@+oB@Q;p9XIFj%XMNHz(PF1eucLBJ?lhtH>8Y`2W+n|HR?* zdH!tv_5A<le+K>E?+Z^<wpO~8k5%p)r3p;)a9ybH3ICnY^NzeZ6M}?CBliQ=MuAOr zrg$9&Klj7wbv}tD&;8T%Zft1;i2Kp>>N?uqfcxWqd?U1oi&JS4X;TMUcB74Gmd%Du zBl2_qJiW?|Hux&CFsN?p$(+Gc<132LVcODs!z16Tw8fv?jKLnAYOooGQ{2Byt$^ES zH{gHQ55W1a^LVk{lfT&RYvx_Vi(5s)1s1~dr&pkbt8%o^Ack4&;h*)-|NTV%-{ba= z?)9ACKmv0orMDYH0+U>KYf#nxvRaGw4r+xQSfGLC!YDm}1%@LkFinN@8gVUQiD7{) z>s`}brI0PmE-sc|ahkgK*FtWXR*;RErfN-XJY<bv)uUKGgrlmZz^@19tPWBE_$oM~ z0HQ$1|2q`NjUoT1LmiOgwGbTC9E;tc8beJ(HY5ayQ*0BlR5ec%!3a2pib!CJsv;{u zU;cNL<Bmf9H_!hc;VhvoJW|mzynpkMgIj01m&UDq3yV*gO76+N#D{ybutY7X-Na87 z(%ny*Gu=b+QBIwyGS*$jkwg0Xmvs;b#?B+s#lOC8rmGb)q>;mlH2>4#bul$!yJIIA zY58Y`3C+TmYxAp}CStqUi!FQQNVvrQtuSNesGn=<ZRAiovA5nhO7aS0G-N<OFU-q0 z>WK7E4<ga&FZ$>I)g=D+B6oG3?cJ{nG0O=`bvJ}GD!I}#C~2(H+BF!N#YyC=CbHIB zh&u(pQ%g1-62wn3JjCZz#NPGY;}oVtvqz7X|B%lnKpBYCGM>m;ouLV56~k6>qep@e zoL?|rLG~IC0jp^qJ6%Kk?_(6r63720BM)!6sdo}=@GNu&Y-#+s$yb{}3GXOm0QYUj zKx8yk4Un1&*;9SdbW~L`WTU_T-%Ij;KF+;2|Negpo&aNaSJ@f6h`y#B7>S9?t~p^f zHnH&Hgvo%RNyPVGl0`_pW!Mdvx}s>dgIq(WbbpnicIoU@SCy3FgjlfiE)@V$mRZ2C zu#jL$4Q$sS*@K8Lux-ISKyi^=2$qDZiu|8%pjebR|7RMnlpOc)&3}ApNJ?QyB8sXm zLyP3Y-qZv#o`{%{_~`H{?eG6ZPWUAMUXliAoql>|_l<N0_RJnWT=Lae3vZ@LUMUGT zV?ERAPF#^qkp}|}q-I7!U*8X4{f9-1^h_qgvuZk0U8`_o{WXeRiCv?^om`s)vsj&U ztYQ@yMkK@uab0)dxo#t%g%PNZq1&1&!k86`Kv<vu2Z~XNt<NKjQkng9Bu1-FT8{ze zi;fMubJa%_;6O!kN`y(ad>iKIl4PkamH(g0wK#mf2>YLR!2V}N_<Cj6*z6xUh(wC* zM^UL^BRp?L%G*2P*SOLN?=?K<&2;CBxx<D#J?R#LQ;FvbyUFgg8;h$fCL3v4C@iyW z*mBKXvfFLHjZoZk$LA0gFSP&RD}~v$M13v$?KVb}(%G|CX1cc)GB0B~TSgKh#!~Pz zh1mwBbr|;Y?388Rsv&BB?#wP>FR|Q7WI;Tqu>2o;{l;P^6lL(I^!0!5<hXZo-{N1% zALBnFoFeQL-Y$F-F#x-jPgMS_@*$4P8EP=vnR|13sznc8Q=k6HVMz-2^Qm8dAS~g2 zu@`EV!}--LX&eIg%bfbf2$vyJ$8)>zRtKket0Xn!`J76maG8<~zzaE3Cx#EF(<Hu< zQ)e1Amri#0XDtAu<;gJ`(_Tnf_?es&nARr6?%6pt-eE&A`oKRY44^xq#(O{l_*44) zKm7k61^-_n|Nm2Z0_EpQe0vS2f*k@0#w=czS-@B{MRj4B4V@rdTLRNUSbqcoa{?>S z;PEfR)&G(nPqCPvJ$SI}cc&y>0d+`>KB8mEYLnDqV2O|jB7(JTS)Qm$4*c2!#|li{ zQ5|Ff(%~}E<0x1qHjs)?7^3ExXbsg%hij-3!z<8bNf#0CA2Gj04T?WU24s*5YLaeR z0h9k-%n1+jC%B8V8T;P8-VDX{*4gFdk}kq<)73@~f{<_zLp;GF3A87u&w!V(7$sNs zkhxL{kk3*>aw^ppHCy!Xoon~zd(WbPme?F}%xK3Ey%hXO^+|ywbs30oRkcM$a}@;m zf^<Yi3JaM1%DB$@hUMu_@5)|<f>`2|^bv?zH?VYEQfud^8ts3C`O{4JokODJKpLQn z3Zlot4Ok8g1PAP&|M!sd{{!4T+57)#FX;)rQ|MHeI9<blu^L(K(XA^vh0)h+x)hkU z$VggcAW$Pxw%G{ttGMt1R!9yP%Z3+l?>Q8O65F$e3qHo8j=;g>9kD7&Tt>?oK*(Vy z>xzWzwW0)pzvbGhV!6JGSPH6Rc?!<|XH$SmeEx?3#p&dv^mHOZ8l#{Fm?BFN9Tjn- ze1wYjZAtWX$Mr=yur1B++y5Zc|GT+=<8R|XESw{p5I%?ae|J~@%lJKCSRUBD{&7UX zzGnYE{J~)0PwNX*o{Bzd2T$5lkd2_;f?;KB5St1Mso(G5#+~ywFWASG5Tw%!O9#N( zzfQX`Z2OGD-u*_u<swR7G65?8+`^J9-`J`*I(=(ekQx5W!pzKA!?hg?3&}v_q?=iH zrh8Q(g3PF!W+SLCk^rAu+;*Gv@w~!py;AzPop$kjq4E;$RL;Uy=Q&|scz@-0D+d2K ze}q33*}%xxV}5>G*cyS&(}H^hsb6ai|5%tCir<R9pDfI#3w^MZBm3;5{=Cy}E+IQi zxZV}?^@GClHy>yo^&7PVHVynlrt%Ur|DD1F?XdE+JO1K{+Aa1{-NT<z%z_BJ^U>iq z*K>_xRAgYkf_{q2JWiMZW;BzO5TGmWJLu}M<(ck6A=@t2RjSo7>G7Y@<2h-@e)yjI z>Mb+f&4s)QrM}@pJQYoUsXo))Q%KX1s3Bda&Hb{^|0DnR&-pKJ*!<sT^-iNaUy0KM z_fOaalau6lhnS$Tg^U+gbw~;g<aUN2Coo0a^3(v)K}FF=6d~N1aaq_@`pZIp+(WEW z+UVm|)+!G#zbXQ!B8MWpUnN`972Ag6E6fH0L>5y`PwT$1cPa&F^Q>i+Rj5S(V#Hx| z*rXu+TLyt`d8%leu4W_81pb2!knRSeE+HE@G6Vbl{eLgX|M4hyFQIE?|LL9EdoG={ z66bSG<#`kHG+JYg;&9I+@u!W90J?=t;4p5pEabe092|DX2pbUiz32AMpy-r19n#2c zBQf-3>6jvh2+|~&7KGcn3ZE7%bVGr#M1o<EW_cczDp)V4mwmmkF(>bLX+w|ISfiMs zgJ6mXDi<KsKkOn^6&S%(9k!#YjdZAiME!rx;C`FK=TrRY{C@r|{AYzTghRr+VF7Sy z<<83gt$b`OMSudjufn_<TGm}Tw5*8(@1!0E8+uw^HR|ZG!*AM-5$wr3g7~GJKRsvO z%;Z|<&&c@(qeO&%?p6$9+vJ0Y-Jba~b2{)wx60uQGCzAQ-eMv5TOglb{0_`NFQ<!b z^g@GXZg2C?KlzuTH=UfW*Wt^MeVUXX@Mq;*?j)`RGJnE7sBgB#zaXdiLA-(85SE0_ z!~6^T=l>sY!cX`|xIY-X)1St|-i5vAQyN}kFQah2V{4eoSQ2zPwbI%(7%@k#FbnCM zbfj{UMafXX(Hh7NWFRyMqPaO*?-jl0QJAipU05h7KbwL;U);483aB+f6^4`x@iqk9 zrUZXiRYS@f+thqdfeE~+AhnX*o9UfNk-B7d+qQBemB#xs5J$NG*!K(wO%Xk~`YPZQ zeT0{VGa3mnWBKs?RggSM>F@t#PIwIVKg;X9#d~t^EQ)%GU9`jXN!D7DY!6?xvTLTU z)6oXm6xpioYZ?r)k;6)bEfBUhJT!a_O4`6g%$8oQH%n0~aZH5}i!@nzDjFjdC#%IE zgYT1QL7{Eqm*l{@1&{$cQ#FWJyY7Jy{>la<gjiu&i!64kOjae_{}QbKJw!+|Tm@!7 zqJx0y@EU{ZKk=sw^M4`m_X+On{8jva=YJ`<!bd9?Ro=>76r%=(nbQ-|HPz}BCf5&_ z-st`UQ2beisSx8l6r;)f3ktI?kGCG@(8w5GSeTzds5l7U|Lnpv1Yyf0CHG1bae*Su z%8LpMClKqS6*YfOp-Yo$Rj#cczX1*sEN=&yfpgQ808`obM`IvLs6+nJ5>fa?`MCJ= z3JWI@b=0>DL^pnYv9WUrO14HPQWL$nfBwIp6P`f+ulL7xd<p;Tozgp-QuPuW1&6Sw zac%Lk#BmodV00VOd|82!i;B#jqNhNN>*I`53{&-0lFk`+E-$25l-SN?3{6W6KdatE zl_HM7@Zm5SK&UON;G9Gx3ovZka6}32fv(p(tM>wmSBZ5*aU9at&v1DrKICDBi-Oc> zhGqj8Sp7Ii{sDJr5s_tKPf6C1+u#3hA^iUX+*_XRO%GG|bLlLV*wj7TthJG2A~7Es z)h4OKrcO0s>uv|CY#=wdN1`{vkPC4^En6})S(bZe_s*eUmDsC2LSYBapR_eSQXw6n z@bQp3Dli3>An#W!!$sNz$hDCzQ8S<h2{b79!2iF9LQ`TvNNnUujwuU)Sgi@FFr<)C zLU!bU*#0A)isw3x6G)0A2db|llLEE>JA=D}!{>hPr~G!v|GzFs@BzM0c(QUu<u@x2 zRz8}f3ZchY&cKD?aB~V{!^e_*CI6x+dq*?<b;zF+j$=Znspv(L+nzr+r=w!j&L{)O zpO-UAWz<$CkH^0_r!!sDWV&e)e|}C42igN4eVyH=E~orUa=Ky;9!C@|*l7|H!oM`9 z_#AdvGyq{hZ0kq^7v$6{#2ctx+iXzZY5rw7S5Tsrh5GW!5~Yazg%*@0*5t|(YFtLg z@$#Izpl_B&(m>-5KwH?{KmSz{|KkLwCU<()|G{<Td`ewQ?7FhXI>stUr=jV~K2P0{ z@j{FxL;_P>-;!*_Q%t0#g|Ut#$&kjt38;5{@5L0v5=R7!K+L2snK))Xkq%{Mb%v%6 zA(jIPmaICi3&}oWrg#ogT0xX8$-1WWwBC6X%@W7|h|nCR{FyM0R!zrmv|hLjB?JIb zC0$cZQI<U0gx91310bURcU_;x|JcOc#NqS5_%r!s{!u{_-Yk5la$RLK8rUQjkA>N! z4@f_uRQT{ODJ&e~pzXo2LV9><Vba1lu@B$>g2L>%<E=*r5*fhD3X_H<x)_{Rq>~FL zSqCe&>(>t6u=ntSZ@1hdwWYdK!^5@2`i+h<((KC%6BLe|bENsrubt^ig&5WLn@_}> zc}2md<2xH(xN0sc_`Mf8b}I`0a&iCse~c5p$3M(HmUZH9wC}qsy(&Ep&YP{(N~+%N z*o(vkBe5UIcOzdgN)G=v!$4vi*!sGJd?TfsMAToJWh%%TDD|qnm(nqpI80au-AxQR zt<IVi7)G8fs-o+_x)(A(Uy)SR@+8?XB%tN$t^@1Hmr#sKY#limKcfH{gD0h9iWnjm zqDw$<j4QtnuhPKs5iDJ_WXTs@6K0cofB(Oo$p0VW-d>J<4k_S;be2ji1<XDk6LXSL zZIU`{0yPA<4?M{?J#cc6;aY);guo`u*fmX&eIKS#FQZ_U*fc7ELU?~3UcRZ7vVjVl zVKCXq0VKh^Ulbip(U5T=Fg)0|5S6ztA~-7K{})i0N-Y16BapWDhs!g;ArCWL6vX+k zB~wNiVB%Vi;D4Iwijt(Lx`>b|%>L&Q;{Wpv?vZlr|Eb@i_x#=~=p;+CJ9n1!*dJvc zlha5&2P58cTp62T7`=;@E^7$upy(2k;fjd!05fl7#fIw(34Yt#OhGBJnjno~PZ@el zay7n`L4!EJ$PW^@zKtudDM<($;=<}p!d)09S84$NpO;f;O6>nbp-H6%WTlKS0a0}Z zflGw`vgsjbx(LaWCnJxf@0bWPD<M~682<zN|A+AZ+wlAUwB99jKIdlV=S#i+LCQ1d zH1r!zS>o(5Hn%bai@t|^IG*KtzK6`%$bzR^l87vifh9X2hZk=Qau~mprVKkO9#_Ud z0p&B{?<Z?6awlqJp9eVc6wy)@DL?>>-f6vyHpUtm$N*A?%?h(i7#zBXdynU+zGWiz zPk>-)jw2zKlA*|o?%EcK|8WNQW)7c+`Az(F{BQH05;$SI@aw{7Dw`{JR6bPsbmh}y zPvm`2U)Txt#Ws91pZMlTEF0gJW@|C=-tf$)Vz``-v6HkfDd8`=3Z3qpywm>E(aPd4 zo~n1YA^A&ks>S-fhv^UbSLW4d5!xRMp2J^iVD#GLqi0EG__>@KBs`ZiisrZE#4QTL zK<FP|8(lnWEfp5T0g<t7WW(Kg9PTVE10gTo0u<W0P)Kj>X20EBMc5K*C&x=URlLy< zYu7c~o$X7udvq!E+>vv75e?cRBK~;P3SP<S^A>ArwXxXOJ*s5?pTd2g<G#=D;qT#p zA>1b1U%9aIj*3zFuY+A*m{32t?#T*xWpN_+p-n+rzx2RN_kx(v_utJx>QiUU!ZKj2 z2R~Ps#2t+$=>h?NOJPFsXz7M;Ub}s!yDv7cCk0134G6mu+cVw6>)Ta=+z{lX!t9MB z<VI-`>4?Sudw|IQALAYv?D!4v7cRD!QBJPJ7u$o4dIzo>cfxx!J&MCfg(PP*{5a$4 zEYI~k7rrhCFp7wn2=NBj7nlg!p6U4b4Qbb<6q+(iyGF@%V(j#&i19dM3mGnO@go+K zTHsrrW9u63Mxv|AA}nAHIJ_v3|G$#rQeyf4$Pqa@@LI??jwuSSLlVNK!eYY4?FaII zUsn(&0?v_vDI;;ZWB29%k8_m@{{;8=2H5qJz9Q~<wX)yyBI3->n#6P_3bRYXd!7u- zBKQnSur1O+4shXBpyh$vtEJ!;EP+^VDLt`FVIqbHp5!vdmojKHq*+E3Rs83`BUtw= z$wN$4><ZHtk*v|_p6Ja{XfBx5w314VuoyDd2>EJ#&nJdC5^^z2KPZAgJ^!G_#3 z5RN*~Ek_MJ#q00?Eu8QW@_%kwYjeTON~Tbi*vu;0H>n)YFjS298@Z@WS}!EC$g5;3 z5`xemBA2adzNiGA4v<9<TBPyn-O`gNaEE8x?eea%bWV7fJ<Y&o=tiD+YKlh~vVox? zgPm@vvg#p>ZJ>)5LLERKO1K}-H+x%qB1LxJtZi>JWHa`3`ZT2N^z51pKFbRvD2fA7 zlw1!7gpV8m2m@|tiiPxPhDPrH&%xH`7a{k%Q1~_BE0t}PcMIRG?63UMTGSv`9_BwP z^2XPX2+APh^&;=8EZAt!SqYmr)*+VZkxtZph|fuf8Xi!$eW4JC+~d?Dflt~*YDG5v z?`ckXv&dOxOOmXa$UE06%-|KvhIO!P*OxlYaM@qpKmXsw2@mu4aPNxk=%@WpFz^~B z>?Ky_0oKH389wF)h1n&HP<!y~Bi3sHj58!$Vu{%byjgKYLplJYZ$U6mmBLZt$p7iY zX?W=D%!=sOTIC^&1(l}fft9x%SC?V_ghNe+AOyL7p#=401eZ}LKqU??69Z^SnxDeO zhd2$@%z|sETF5@>B0{BrFNOcOA&Uy|b97k#A#|i`f<q<e|CyY~ao<Juza{<y{Eq}v z_*LP-%CjohR(_LvANP7_|4<zM)Mhqso)cCZONZ^%PIIYgdn>T--hwZ-_}(h)y=x=` zHu;{A7=C+BcfVWgR)5dzJns4{oqCg6IPmj1-NvG3YB$)O`kj4U$JO~YRfFS-*Yj)2 z7{z5=25;ny?l+*>AtS+?`Bgr{=L&D-#B%T&gQJ7LeCn};0p9to(>KxR^Huy6IsN#e zq1@~*LLt|p>0<e9d1rQ5D9c}#b7e)#lkSl#r`;p9YnS}vbtpk;Ggqlle%2;ees*AM z&R6^A|NX@N=P`HyZ<rrMbu}rqFL4y$Xt~wG4V*&dWBZd#<1lp}Dh_XTzGeoX;)v=5 z^KLK*nqzCSD~qmS;$}=j0~i#YGOI=+mUM6p%NRYYFus&Q16x-WKJOMWk5&z22Sj=x zWL}e1gattC1RZRpPN69`TiKs?vLZ4UMfP$bB@7N#l95yr0b>G5a-j-Ad^sC@0OF%K zwut0F{r!J8Cwvw0KkhEs_QxjQx^Xsnc(P?qSd`o(V<CqPu&I+J)9_`-g48y^1prC! zWW;Q79r(avI#nGGKbKQ<O6>46Ab|tsAse4ToeYEq6&OYi^q#m6h^FnimLi#^C@RQq z<#|Y`s^~IFfnZUL%6)5!QTg!lpMgLmA6ToL0c3l~j0SnBWLb`EAu5sr+ka#Mz`fE( z{C}qZNBpmEVE>mr{?}=}E6Yy06Rh3vN>3CfCNNRLrcRd;`pbm37E$4;o(fBD3o-u@ zzZGG@u*~84vyH-0V$Yvw|HnD*VOUZtCE!V|!C*pM01wV=eqaVbDG(jxPzDD;0;(c4 zisj-Exq`y9AvnbbyW3=9R10gQ0~8g9=(x}YBLyh3&IZr}!unr@DTJmufg-!2=8*gU zsoWoPi23t2;m4H){t^Bt{~XBv|F~qlK;m_7oavr3tx49fi~>mW+Y5_>8Exc*0E6@~ zUzjjA>LVuIAU#}N{3sGvOwz|y>!2qIS2St=io#+fq|yx1#kS%!NyCx=()?wG=}V${ zX4e4z%A01o=S{or6ZC>y{>jU-`~RCr{J(p-HzhXNQ~E!M*fvk8bcrLjMe{Mdw-eB& z*P5UTqu5~9OgQ!<i5G&~d5WaKs>jeBQI~xknXf&t{Z~_@O3e007$hlFItZ~kYgS+w z1-5M=ri}un2@W$^hB1o;4@e!JK8SGRk@(+NZJ5<GOk*=Zhzt8BsKStfB?_WY!EORK zBe*{~F7m1S5Q=Dufw%~+L+<}P_a8a(dD?$|3$d;@Z=Rhydv?!n>^tY23(kMRA?~+U zx$|y~k2mfg0umPgU*cJ$ZV|VL_yy0NhG)0D_MD4|)Jt`{(W#PVssZ$$evAGYCPl2) z+f};}FRpfu*q!RJ`qEOh<u5ny^s6@hTt>Q0x87WBSKX%PZ>r+wV#n{Gx$4nI^H`&5 zx2vyOShxfac8*kSG}3G}R~L^gov7NY9%5p9msQ)oyV~-rC;UzoIbD|P?Kboiw5el9 zP=h#%;9b0a-}X|wiC&NT)i!+yPAOHd>8{ck&Q%-jw8LT2CZeHk!S@gh_kSZ-h&U&z zb1((7E|<j1C1dZd8~2i%<mI*6v17-!EH+jNbcoS|OucQtU0YgQS=vI7-IT-$xKga} zPPaE()xftq<h@${l5e;DYJgX3`(Cx#06-*@&nhy_K!>wwcc<$4D@c!wT%~Qm;#XhZ zuP}FI+wWX_(GBg3E~{R24NdU}IPgC&ztdh?^)IQmPc%CAakSjpH2lbxl=@q%P7~1g zpF&s1nysT%XSGB6+(hF4VXEk?c8;`~$Etp-)oiu5RI3M$_!tKPltSKaF8fu8W5N-( zFRxZxO=q>;+0<TLS!uR9<c+Eg8yRcsnAF5*tMvw2t7BqZ|FUrTG@SMd3O13k_k*2L zA8)LSPLsTsO;*umRlwI;BGaF2(F>7jZyGJcBXumyH&CdzY^us+2iQ`-g?7VUX&-5J z<_3$PDq3?`5R$Rw`0^6n|ANQi^UTjP2&@YNzj2Q60+991XP<pXZ|>}~FF60gMQ#pM z9XGMVp|&`oQHIbvrc@TUXj?@3YaSGqX_v8&X#`C|3F*S88xk#6NkX<NqK_T57yb5u zBOvYUf2u33=ACs9Q~|U;)(+Xp?vmXG;j21O3^p2wSh}fC!^taBa*Spmd~AS{Qwm3( z37bIes0$n}XeDShm#d7v#z#`yVlS;6vFWeDek4R$zg*O|FI-dI+o!+-+AM{t_1ZD7 zQ9D*YTCXiMgU&IKs>7`EE34PmTR1fs72cA6|5s)?e4hDv27zY~;J9wRxBc|Zn~y=n zG&cu};sj}m(WTnKX{<S?duYh627K?Z7;E_d8UHUM^p~qb`KiER6pkQ)B|$qXNpK*8 IXj!!XKN;XL4*&oF literal 0 HcmV?d00001 diff --git a/lib/.xmlregistry/channels/channel-__uri.xml b/lib/.xmlregistry/channels/channel-__uri.xml new file mode 100644 index 0000000..987a593 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-__uri.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>__uri</name> + <suggestedalias>__uri</suggestedalias> + <summary>Pseudo-channel for static packages</summary> + <servers> + <primary> + <xmlrpc> + <function version="1.0">****</function> + </xmlrpc> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-doc.php.net.xml b/lib/.xmlregistry/channels/channel-doc.php.net.xml new file mode 100644 index 0000000..d221a21 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-doc.php.net.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>doc.php.net</name> + <summary>PHP Documentation team</summary> + <suggestedalias>phpdocs</suggestedalias> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://doc.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://doc.php.net/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-htmlpurifier.org.xml b/lib/.xmlregistry/channels/channel-htmlpurifier.org.xml new file mode 100644 index 0000000..0c057dd --- /dev/null +++ b/lib/.xmlregistry/channels/channel-htmlpurifier.org.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>htmlpurifier.org</name> + <summary>PEAR channel for HTML Purifier library</summary> + <suggestedalias>hp</suggestedalias> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://htmlpurifier.org/Chiara_PEAR_Server_REST/</baseurl> + <baseurl type="REST1.1">http://htmlpurifier.org/Chiara_PEAR_Server_REST/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pear.php.net.xml b/lib/.xmlregistry/channels/channel-pear.php.net.xml new file mode 100644 index 0000000..c709891 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pear.php.net.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pear.php.net</name> + <suggestedalias>pear</suggestedalias> + <summary>PHP Extension and Application Repository</summary> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://pear.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://pear.php.net/rest/</baseurl> + <baseurl type="REST1.2">http://pear.php.net/rest/</baseurl> + <baseurl type="REST1.3">http://pear.php.net/rest/</baseurl> + </rest> + </primary> + <mirror host="us.pear.php.net"> + <rest> + <baseurl type="REST1.0">http://us.pear.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://us.pear.php.net/rest/</baseurl> + <baseurl type="REST1.2">http://us.pear.php.net/rest/</baseurl> + <baseurl type="REST1.3">http://us.pear.php.net/rest/</baseurl> + </rest> + </mirror> + <mirror host="de.pear.php.net" ssl="yes" port="3452"> + <rest> + <baseurl type="REST1.0">https://de.pear.php.net:3452/rest/</baseurl> + <baseurl type="REST1.1">https://de.pear.php.net:3452/rest/</baseurl> + <baseurl type="REST1.2">https://de.pear.php.net:3452/rest/</baseurl> + <baseurl type="REST1.3">https://de.pear.php.net:3452/rest/</baseurl> + </rest> + </mirror> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pear.unl.edu.xml b/lib/.xmlregistry/channels/channel-pear.unl.edu.xml new file mode 100644 index 0000000..54bc511 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pear.unl.edu.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pear.unl.edu</name> + <summary>UNL PHP Extension and Application Repository</summary> + <suggestedalias>unl</suggestedalias> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://pear.unl.edu/Chiara_PEAR_Server_REST/</baseurl> + <baseurl type="REST1.1">http://pear.unl.edu/Chiara_PEAR_Server_REST/</baseurl> + <baseurl type="REST1.3">http://pear.unl.edu/Chiara_PEAR_Server_REST/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pear2.php.net.xml b/lib/.xmlregistry/channels/channel-pear2.php.net.xml new file mode 100644 index 0000000..dbcaccb --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pear2.php.net.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pear2.php.net</name> + <suggestedalias>pear2</suggestedalias> + <summary>PEAR packages for PHP 5.3+ installed by Pyrus</summary> + <servers> + <primary> + <rest> + <baseurl type="REST1.0">http://pear2.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://pear2.php.net/rest/</baseurl> + <baseurl type="REST1.2">http://pear2.php.net/rest/</baseurl> + <baseurl type="REST1.3">http://pear2.php.net/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channel-pecl.php.net.xml b/lib/.xmlregistry/channels/channel-pecl.php.net.xml new file mode 100644 index 0000000..cd6b257 --- /dev/null +++ b/lib/.xmlregistry/channels/channel-pecl.php.net.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd"> + <name>pecl.php.net</name> + <suggestedalias>pecl</suggestedalias> + <summary>PHP Extension Community Library</summary> + <validatepackage version="1.0">PEAR_Validator_PECL</validatepackage> + <servers> + <primary> + <xmlrpc> + <function version="1.0">logintest</function> + <function version="1.0">package.listLatestReleases</function> + <function version="1.0">package.listAll</function> + <function version="1.0">package.info</function> + <function version="1.0">package.getDownloadURL</function> + <function version="1.0">package.getDepDownloadURL</function> + <function version="1.0">package.search</function> + <function version="1.0">channel.listAll</function> + </xmlrpc> + <rest> + <baseurl type="REST1.0">http://pecl.php.net/rest/</baseurl> + <baseurl type="REST1.1">http://pecl.php.net/rest/</baseurl> + </rest> + </primary> + </servers> +</channel> diff --git a/lib/.xmlregistry/channels/channelalias-__uri.txt b/lib/.xmlregistry/channels/channelalias-__uri.txt new file mode 100644 index 0000000..9e550d0 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-__uri.txt @@ -0,0 +1 @@ +__uri \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-hp.txt b/lib/.xmlregistry/channels/channelalias-hp.txt new file mode 100644 index 0000000..652f390 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-hp.txt @@ -0,0 +1 @@ +htmlpurifier.org \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pear.txt b/lib/.xmlregistry/channels/channelalias-pear.txt new file mode 100644 index 0000000..f4730b9 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pear.txt @@ -0,0 +1 @@ +pear.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pear2.txt b/lib/.xmlregistry/channels/channelalias-pear2.txt new file mode 100644 index 0000000..7911749 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pear2.txt @@ -0,0 +1 @@ +pear2.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-pecl.txt b/lib/.xmlregistry/channels/channelalias-pecl.txt new file mode 100644 index 0000000..2de48f1 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-pecl.txt @@ -0,0 +1 @@ +pecl.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-phpdocs.txt b/lib/.xmlregistry/channels/channelalias-phpdocs.txt new file mode 100644 index 0000000..1e733d9 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-phpdocs.txt @@ -0,0 +1 @@ +doc.php.net \ No newline at end of file diff --git a/lib/.xmlregistry/channels/channelalias-unl.txt b/lib/.xmlregistry/channels/channelalias-unl.txt new file mode 100644 index 0000000..674f012 --- /dev/null +++ b/lib/.xmlregistry/channels/channelalias-unl.txt @@ -0,0 +1 @@ +pear.unl.edu \ No newline at end of file diff --git a/lib/.xmlregistry/packages/htmlpurifier.org/HTMLPurifier/4.0.0-info.xml b/lib/.xmlregistry/packages/htmlpurifier.org/HTMLPurifier/4.0.0-info.xml new file mode 100644 index 0000000..923a353 --- /dev/null +++ b/lib/.xmlregistry/packages/htmlpurifier.org/HTMLPurifier/4.0.0-info.xml @@ -0,0 +1,396 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.8.1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>HTMLPurifier</name> + <channel>htmlpurifier.org</channel> + <summary>Standards-compliant HTML filter</summary> + <description>HTML Purifier is an HTML filter that will remove all malicious code + (better known as XSS) with a thoroughly audited, secure yet permissive + whitelist and will also make sure your documents are standards + compliant.</description> + <lead> + <name>Edward Z. Yang</name> + <user>ezyang</user> + <email>admin@htmlpurifier.org</email> + <active>yes</active> + </lead> + <date>2010-04-15</date> + <time>09:10:17</time> + <version> + <release>4.0.0</release> + <api>4.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.gnu.org/licenses/lgpl.html">LGPL</license> + <notes> +HTML Purifier 4.0 is a major feature release focused on configuration +It deprecates the $config->set('Ns', 'Directive', $value) syntax for +$config->set('Ns.Directive', $value); both syntaxes work but the +former will throw errors. There are also some new features: robust +support for name/id, configuration inheritance, remove nbsp in +the RemoveEmpty autoformatter, userland configuration directives +and configuration serialization. + </notes> + <contents> + <dir name="/"> + <file baseinstalldir="/" md5sum="8b1819c0ed397d313e5c76b062e6264d" name="HTMLPurifier/VarParserException.php" role="php"/> + <file baseinstalldir="/" md5sum="faa91653d527c5229b49253658ca80dd" name="HTMLPurifier/VarParser/Native.php" role="php"/> + <file baseinstalldir="/" md5sum="5e05d7b4f800f3cbca03efb3537277cb" name="HTMLPurifier/VarParser/Flexible.php" role="php"/> + <file baseinstalldir="/" md5sum="629508f0959354d4ebd99c527e704036" name="HTMLPurifier/VarParser.php" role="php"/> + <file baseinstalldir="/" md5sum="dbcd0c8ef8f54df0bf8945d5a61b2a9c" name="HTMLPurifier/URISchemeRegistry.php" role="php"/> + <file baseinstalldir="/" md5sum="dc9834550ddfe8edfc4a7c55bf32c05b" name="HTMLPurifier/URIScheme/nntp.php" role="php"/> + <file baseinstalldir="/" md5sum="2187e2b929cf26764e2a4acb24beaf46" name="HTMLPurifier/URIScheme/news.php" role="php"/> + <file baseinstalldir="/" md5sum="b847abc08adc931880036bcddde2362a" name="HTMLPurifier/URIScheme/mailto.php" role="php"/> + <file baseinstalldir="/" md5sum="add878b1199b2907f31135e89baebc12" name="HTMLPurifier/URIScheme/https.php" role="php"/> + <file baseinstalldir="/" md5sum="9e58f88917c4cbc385ba52c20c78e832" name="HTMLPurifier/URIScheme/http.php" role="php"/> + <file baseinstalldir="/" md5sum="18245d29655347bfb39c3342476a2d96" name="HTMLPurifier/URIScheme/ftp.php" role="php"/> + <file baseinstalldir="/" md5sum="4122f8aeaf11cc424c9a720c07ba0724" name="HTMLPurifier/URIScheme.php" role="php"/> + <file baseinstalldir="/" md5sum="d52d2040f01112af0e00f8b9bac38267" name="HTMLPurifier/URIParser.php" role="php"/> + <file baseinstalldir="/" md5sum="af92dd990abc627653f029dca94ed2d2" name="HTMLPurifier/URIFilter/Munge.php" role="php"/> + <file baseinstalldir="/" md5sum="f60d09064deb7203990b68895c6c8d66" name="HTMLPurifier/URIFilter/MakeAbsolute.php" role="php"/> + <file baseinstalldir="/" md5sum="a520831064417a45e03fd669b0f8256c" name="HTMLPurifier/URIFilter/HostBlacklist.php" role="php"/> + <file baseinstalldir="/" md5sum="f75e2069424d0b4f60988b1e8344cde8" name="HTMLPurifier/URIFilter/DisableExternalResources.php" role="php"/> + <file baseinstalldir="/" md5sum="9df1553f001cd2e5e574be075396a575" name="HTMLPurifier/URIFilter/DisableExternal.php" role="php"/> + <file baseinstalldir="/" md5sum="62112e5c17a05698309321ea85935061" name="HTMLPurifier/URIFilter.php" role="php"/> + <file baseinstalldir="/" md5sum="549903c3c3bfa86d91d3c2e2c8e065db" name="HTMLPurifier/URIDefinition.php" role="php"/> + <file baseinstalldir="/" md5sum="b150e8e19a71f29c19d7fdff69b4fd3d" name="HTMLPurifier/URI.php" role="php"/> + <file baseinstalldir="/" md5sum="9dcddc344c2d6d44698479dead8beead" name="HTMLPurifier/UnitConverter.php" role="php"/> + <file baseinstalldir="/" md5sum="ed32af45fe9dba652840c42075a7cdf3" name="HTMLPurifier/TokenFactory.php" role="php"/> + <file baseinstalldir="/" md5sum="a1ee2cf2a31f87fe63b5702ff82ed9ba" name="HTMLPurifier/Token/Text.php" role="php"/> + <file baseinstalldir="/" md5sum="21a3ed284d9cded0eba7e6910192f92b" name="HTMLPurifier/Token/Tag.php" role="php"/> + <file baseinstalldir="/" md5sum="abb859525c52a506e5f58c309431f3ca" name="HTMLPurifier/Token/Start.php" role="php"/> + <file baseinstalldir="/" md5sum="6dc6cdb4e980b6ffb9bb18ae73236ca9" name="HTMLPurifier/Token/End.php" role="php"/> + <file baseinstalldir="/" md5sum="b38096253b7361aa606beabbd361e744" name="HTMLPurifier/Token/Empty.php" role="php"/> + <file baseinstalldir="/" md5sum="0a93e4fcea3cf8114eec37914806e54f" name="HTMLPurifier/Token/Comment.php" role="php"/> + <file baseinstalldir="/" md5sum="cebdc28d8a702d0d2dfceb8c27196812" name="HTMLPurifier/Token.php" role="php"/> + <file baseinstalldir="/" md5sum="5e45fd1e8d10be6b9f4da28c765532ee" name="HTMLPurifier/TagTransform/Simple.php" role="php"/> + <file baseinstalldir="/" md5sum="a8e11a0600b535514f7d790fcb0e008e" name="HTMLPurifier/TagTransform/Font.php" role="php"/> + <file baseinstalldir="/" md5sum="3516a182bdf938206958c01134604b23" name="HTMLPurifier/TagTransform.php" role="php"/> + <file baseinstalldir="/" md5sum="74b777f59ffc04a9bcd19c815983d9f1" name="HTMLPurifier/tags" role="php"/> + <file baseinstalldir="/" md5sum="d2040cbc74c4ce31449d127719bcc6cb" name="HTMLPurifier/StringHashParser.php" role="php"/> + <file baseinstalldir="/" md5sum="934cbbd131e387f9e2cf0921e0c88936" name="HTMLPurifier/StringHash.php" role="php"/> + <file baseinstalldir="/" md5sum="3e085d73ce91a3d38f3017b112d07a0d" name="HTMLPurifier/Strategy/ValidateAttributes.php" role="php"/> + <file baseinstalldir="/" md5sum="6c6dbc3f24bccf2fdbfdbe9fe0912653" name="HTMLPurifier/Strategy/RemoveForeignElements.php" role="php"/> + <file baseinstalldir="/" md5sum="310ba416548021da80413cf04a90cce8" name="HTMLPurifier/Strategy/MakeWellFormed.php" role="php"/> + <file baseinstalldir="/" md5sum="48272af4271d1d6cc193052d9a175222" name="HTMLPurifier/Strategy/FixNesting.php" role="php"/> + <file baseinstalldir="/" md5sum="0324f1ab3397cb1544a51257994f576a" name="HTMLPurifier/Strategy/Core.php" role="php"/> + <file baseinstalldir="/" md5sum="55d2b2fd0577ebdcbbbf1312f4e6eb6e" name="HTMLPurifier/Strategy/Composite.php" role="php"/> + <file baseinstalldir="/" md5sum="05823f704d950d3a9debbd3784e4aaea" name="HTMLPurifier/Strategy.php" role="php"/> + <file baseinstalldir="/" md5sum="d17e7d5f8bc24cfc07f97aa7b014c4cd" name="HTMLPurifier/PropertyListIterator.php" role="php"/> + <file baseinstalldir="/" md5sum="319d75a5f8e6bbc8c644056a68a4aaec" name="HTMLPurifier/PropertyList.php" role="php"/> + <file baseinstalldir="/" md5sum="a46e55a618e944c96bbd09e5cfcf8cc1" name="HTMLPurifier/Printer/HTMLDefinition.php" role="php"/> + <file baseinstalldir="/" md5sum="d1d09c86a9c81b526bbd44c847344b6d" name="HTMLPurifier/Printer/CSSDefinition.php" role="php"/> + <file baseinstalldir="/" md5sum="512c6d0717c3fcb0bb0997594cdf7f3f" name="HTMLPurifier/Printer/ConfigForm.php" role="php"/> + <file baseinstalldir="/" md5sum="ee5990d6bb62017463a7a8d72c8288b5" name="HTMLPurifier/Printer/ConfigForm.js" role="php"/> + <file baseinstalldir="/" md5sum="c02f2fa8100745b88a85ed30e883fbc2" name="HTMLPurifier/Printer/ConfigForm.css" role="php"/> + <file baseinstalldir="/" md5sum="530db343c69ec3d4ba27e09e4b837903" name="HTMLPurifier/Printer.php" role="php"/> + <file baseinstalldir="/" md5sum="4d14a6fa6959c5c6993391b9946b57fe" name="HTMLPurifier/PercentEncoder.php" role="php"/> + <file baseinstalldir="/" md5sum="0f6893d064ab38c573384140159dd275" name="HTMLPurifier/Lexer/PH5P.php" role="php"/> + <file baseinstalldir="/" md5sum="8643ddf9638ba444abb3f0d14a906f1a" name="HTMLPurifier/Lexer/PEARSax3.php" role="php"/> + <file baseinstalldir="/" md5sum="461417b2a89ff805874fb3200edecc3a" name="HTMLPurifier/Lexer/DOMLex.php" role="php"/> + <file baseinstalldir="/" md5sum="f4a371e75951a563aa06c76afec748d5" name="HTMLPurifier/Lexer/DirectLex.php" role="php"/> + <file baseinstalldir="/" md5sum="a9d40a8c5ae52525e876cf061358bc1e" name="HTMLPurifier/Lexer.php" role="php"/> + <file baseinstalldir="/" md5sum="39382c387dc2a7dac903c2b63849a9a6" name="HTMLPurifier/Length.php" role="php"/> + <file baseinstalldir="/" md5sum="58a26316f701ac79ca439ce4d874b9d3" name="HTMLPurifier/LanguageFactory.php" role="php"/> + <file baseinstalldir="/" md5sum="7246930b43b3e3caea10e6bce02fa95e" name="HTMLPurifier/Language/messages/en.php" role="php"/> + <file baseinstalldir="/" md5sum="146f1c2d41e1fdf85f334a05a0dd41ca" name="HTMLPurifier/Language/messages/en-x-testmini.php" role="php"/> + <file baseinstalldir="/" md5sum="c1ea035c3a68aee24f6d4c264ee79d6b" name="HTMLPurifier/Language/messages/en-x-test.php" role="php"/> + <file baseinstalldir="/" md5sum="f645558786c86be0b603b7cfc82f7e12" name="HTMLPurifier/Language/classes/en-x-test.php" role="php"/> + <file baseinstalldir="/" md5sum="34c9de227562f8967141cbf9f565c289" name="HTMLPurifier/Language.php" role="php"/> + <file baseinstalldir="/" md5sum="027296ea99d041b96bfb9f86b4b2246f" name="HTMLPurifier/Injector/SafeObject.php" role="php"/> + <file baseinstalldir="/" md5sum="f6216052673d05748b26bfcbd51689f7" name="HTMLPurifier/Injector/RemoveEmpty.php" role="php"/> + <file baseinstalldir="/" md5sum="dbfe4673baeefc27fb505c867d56c5d4" name="HTMLPurifier/Injector/PurifierLinkify.php" role="php"/> + <file baseinstalldir="/" md5sum="a205c290b398a25a7f4b76a5401e54c4" name="HTMLPurifier/Injector/Linkify.php" role="php"/> + <file baseinstalldir="/" md5sum="e0ba2c64ec1ff45452069ea4a61194c8" name="HTMLPurifier/Injector/DisplayLinkURI.php" role="php"/> + <file baseinstalldir="/" md5sum="0d1dc975105ef1208e3228586335abaf" name="HTMLPurifier/Injector/AutoParagraph.php" role="php"/> + <file baseinstalldir="/" md5sum="9f1d4fac7ab8e8ba0a94a13cdfb5c644" name="HTMLPurifier/Injector.php" role="php"/> + <file baseinstalldir="/" md5sum="41e2004626f0e87d894ad091f91f6554" name="HTMLPurifier/IDAccumulator.php" role="php"/> + <file baseinstalldir="/" md5sum="b3fe48fa29465d75928b5a2917d4ba30" name="HTMLPurifier/HTMLModuleManager.php" role="php"/> + <file baseinstalldir="/" md5sum="4927a47ebe2721507baaddf7f66da265" name="HTMLPurifier/HTMLModule/XMLCommonAttributes.php" role="php"/> + <file baseinstalldir="/" md5sum="cbdf3deab781644fcc25dde08b441727" name="HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php" role="php"/> + <file baseinstalldir="/" md5sum="7f514a82cfd85b03d712274b5d249954" name="HTMLPurifier/HTMLModule/Tidy/XHTML.php" role="php"/> + <file baseinstalldir="/" md5sum="a00c39ef28493892552b3f3e452cf992" name="HTMLPurifier/HTMLModule/Tidy/Transitional.php" role="php"/> + <file baseinstalldir="/" md5sum="5215ba83a8f645d50077b748ab344b8a" name="HTMLPurifier/HTMLModule/Tidy/Strict.php" role="php"/> + <file baseinstalldir="/" md5sum="ad27e0f4952db6bd52a6798ce252c812" name="HTMLPurifier/HTMLModule/Tidy/Proprietary.php" role="php"/> + <file baseinstalldir="/" md5sum="f6bed98e1a7ebae0e09764956bdac4f2" name="HTMLPurifier/HTMLModule/Tidy/Name.php" role="php"/> + <file baseinstalldir="/" md5sum="4837b4cb0776cc1af14a21c1b877e078" name="HTMLPurifier/HTMLModule/Tidy.php" role="php"/> + <file baseinstalldir="/" md5sum="65b57e9b356a4d13b6f0c65dfcd337dd" name="HTMLPurifier/HTMLModule/Text.php" role="php"/> + <file baseinstalldir="/" md5sum="ad4c41177390713e568bdd96848e37f5" name="HTMLPurifier/HTMLModule/Target.php" role="php"/> + <file baseinstalldir="/" md5sum="7d6e6c2595498da192d69006c46d769d" name="HTMLPurifier/HTMLModule/Tables.php" role="php"/> + <file baseinstalldir="/" md5sum="4770bf322cefdc9b155d9faf2851e21f" name="HTMLPurifier/HTMLModule/StyleAttribute.php" role="php"/> + <file baseinstalldir="/" md5sum="074b0eb6b6c34a1992ab65aa67e29a35" name="HTMLPurifier/HTMLModule/Scripting.php" role="php"/> + <file baseinstalldir="/" md5sum="d02bfe5bcd269deac0cd7886ae288364" name="HTMLPurifier/HTMLModule/SafeObject.php" role="php"/> + <file baseinstalldir="/" md5sum="59f3c3ddec2793fdb18084a2b6da20b6" name="HTMLPurifier/HTMLModule/SafeEmbed.php" role="php"/> + <file baseinstalldir="/" md5sum="c87f9de5628a72270943de5496d36ddf" name="HTMLPurifier/HTMLModule/Ruby.php" role="php"/> + <file baseinstalldir="/" md5sum="aae031812e9fbe29dff05f23e1b4cda5" name="HTMLPurifier/HTMLModule/Proprietary.php" role="php"/> + <file baseinstalldir="/" md5sum="e503304a642c6786f6a11eb3ead87ddd" name="HTMLPurifier/HTMLModule/Presentation.php" role="php"/> + <file baseinstalldir="/" md5sum="a7cca883348645c6a77ec7db0932a610" name="HTMLPurifier/HTMLModule/Object.php" role="php"/> + <file baseinstalldir="/" md5sum="24b18ff07b334f19fc8c1e5f184d2570" name="HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php" role="php"/> + <file baseinstalldir="/" md5sum="9765121a41a8e2a372a65c6bd3b32789" name="HTMLPurifier/HTMLModule/Name.php" role="php"/> + <file baseinstalldir="/" md5sum="21bac4a6658e819d2b0c807351f300c1" name="HTMLPurifier/HTMLModule/List.php" role="php"/> + <file baseinstalldir="/" md5sum="e36d4f596bd85f285d243013495c8dcb" name="HTMLPurifier/HTMLModule/Legacy.php" role="php"/> + <file baseinstalldir="/" md5sum="911df038afaefbc0f662a3d6fa8d9554" name="HTMLPurifier/HTMLModule/Image.php" role="php"/> + <file baseinstalldir="/" md5sum="bb4790cbd4a68e90a2d1fb8b820d7147" name="HTMLPurifier/HTMLModule/Hypertext.php" role="php"/> + <file baseinstalldir="/" md5sum="73153b86492552a9fbb88340c770963f" name="HTMLPurifier/HTMLModule/Forms.php" role="php"/> + <file baseinstalldir="/" md5sum="5475ee52a8791d170b841cd6ad905094" name="HTMLPurifier/HTMLModule/Edit.php" role="php"/> + <file baseinstalldir="/" md5sum="381c8ba0950e07c6c82c42a642385800" name="HTMLPurifier/HTMLModule/CommonAttributes.php" role="php"/> + <file baseinstalldir="/" md5sum="e129432b77bb64fdc290d13987c45596" name="HTMLPurifier/HTMLModule/Bdo.php" role="php"/> + <file baseinstalldir="/" md5sum="9cbb7c9019e68203ca205eff77a61fe5" name="HTMLPurifier/HTMLModule.php" role="php"/> + <file baseinstalldir="/" md5sum="75a3e23b95fd758922132c79fdca9e56" name="HTMLPurifier/HTMLDefinition.php" role="php"/> + <file baseinstalldir="/" md5sum="51925aaea7436e6a75f4ccae2597d806" name="HTMLPurifier/Generator.php" role="php"/> + <file baseinstalldir="/" md5sum="5c61e7f9db9239ad5f23a4ef94e83d14" name="HTMLPurifier/Filter/YouTube.php" role="php"/> + <file baseinstalldir="/" md5sum="739249b8ed7c1e1ff906931b13d477f6" name="HTMLPurifier/Filter/ExtractStyleBlocks.php" role="php"/> + <file baseinstalldir="/" md5sum="33181bbe7f6e7e839796e73d4d2a790d" name="HTMLPurifier/Filter.php" role="php"/> + <file baseinstalldir="/" md5sum="6549f2e00060fd671149191f1e94eaf3" name="HTMLPurifier/Exception.php" role="php"/> + <file baseinstalldir="/" md5sum="3cc9e13fb56fb8816bd478e46522a926" name="HTMLPurifier/ErrorStruct.php" role="php"/> + <file baseinstalldir="/" md5sum="5aee849a4ef4f610cd383752697f6966" name="HTMLPurifier/ErrorCollector.php" role="php"/> + <file baseinstalldir="/" md5sum="85a29be7cf7434024284aeb333659c0b" name="HTMLPurifier/EntityParser.php" role="php"/> + <file baseinstalldir="/" md5sum="5e2066baba7c0c0de8ff0ecc38ce2a5c" name="HTMLPurifier/EntityLookup/entities.ser" role="php"/> + <file baseinstalldir="/" md5sum="1ebc1f6db1134f98ba757d16bec67d0a" name="HTMLPurifier/EntityLookup.php" role="php"/> + <file baseinstalldir="/" md5sum="fc3fee6ae7b5cfa04ce2c4ba8699a98b" name="HTMLPurifier/Encoder.php" role="php"/> + <file baseinstalldir="/" md5sum="b0025522437a72f339de994d0e26dd1d" name="HTMLPurifier/ElementDef.php" role="php"/> + <file baseinstalldir="/" md5sum="0ab0a6b2a7d358ab8532d897e8bfe729" name="HTMLPurifier/DoctypeRegistry.php" role="php"/> + <file baseinstalldir="/" md5sum="512fcd900313924eb7902442186a06cc" name="HTMLPurifier/Doctype.php" role="php"/> + <file baseinstalldir="/" md5sum="1df8a4f27f64b5473b41e4666f787c6d" name="HTMLPurifier/DefinitionCacheFactory.php" role="php"/> + <file baseinstalldir="/" md5sum="1e9429a2030745d25d245d9f06f7f065" name="HTMLPurifier/DefinitionCache/Serializer/README" role="php"/> + <file baseinstalldir="/" md5sum="d63b0aeacf255afb75982a14f1dd0daf" name="HTMLPurifier/DefinitionCache/Serializer.php" role="php"/> + <file baseinstalldir="/" md5sum="e27dfb4700f20a6ccd1ab8e498ce0ac6" name="HTMLPurifier/DefinitionCache/Null.php" role="php"/> + <file baseinstalldir="/" md5sum="9e8b84835e638f4f835e43405c50900f" name="HTMLPurifier/DefinitionCache/Decorator/Template.php.in" role="php"/> + <file baseinstalldir="/" md5sum="8acd4ef5453cc2c25612a665f46ca836" name="HTMLPurifier/DefinitionCache/Decorator/Memory.php" role="php"/> + <file baseinstalldir="/" md5sum="31e3ea9d5988c5a1ae3f369ddccbfead" name="HTMLPurifier/DefinitionCache/Decorator/Cleanup.php" role="php"/> + <file baseinstalldir="/" md5sum="f185adab8aad39c577d9d4900ed61b81" name="HTMLPurifier/DefinitionCache/Decorator.php" role="php"/> + <file baseinstalldir="/" md5sum="db24b53bab53b8ffa40b197e06078fe4" name="HTMLPurifier/DefinitionCache.php" role="php"/> + <file baseinstalldir="/" md5sum="a188cbe72b81f021578b989d1566f0b3" name="HTMLPurifier/Definition.php" role="php"/> + <file baseinstalldir="/" md5sum="d6018966da297fe60eeba65b9adc843a" name="HTMLPurifier/CSSDefinition.php" role="php"/> + <file baseinstalldir="/" md5sum="c1ed36707d08237daa646b3b8eeb30a4" name="HTMLPurifier/Context.php" role="php"/> + <file baseinstalldir="/" md5sum="1bd2243d0d7b68504723ebc5dddd0ea2" name="HTMLPurifier/ContentSets.php" role="php"/> + <file baseinstalldir="/" md5sum="9a57bfdb6b774679e2f5593d4c67d7f5" name="HTMLPurifier/ConfigSchema/ValidatorAtom.php" role="php"/> + <file baseinstalldir="/" md5sum="d9327f8aaab4fdc364107d0779310b3b" name="HTMLPurifier/ConfigSchema/Validator.php" role="php"/> + <file baseinstalldir="/" md5sum="5f3ddeed8119e52e8a83d9a2a292302b" name="HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt" role="php"/> + <file baseinstalldir="/" md5sum="0b4694fa209765c9eb4017787db4cfa2" name="HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt" role="php"/> + <file baseinstalldir="/" md5sum="35af9b45b2e0e12d68b23ce62a78fe0b" name="HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt" role="php"/> + <file baseinstalldir="/" md5sum="63a87a1f19518c7dc68ff3cd05896656" name="HTMLPurifier/ConfigSchema/schema/URI.Munge.txt" role="php"/> + <file baseinstalldir="/" md5sum="b59e10aecfd047b3a16a321d10d0f3ef" name="HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt" role="php"/> + <file baseinstalldir="/" md5sum="1c3c1d44844d5a053480f31b93102434" name="HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt" role="php"/> + <file baseinstalldir="/" md5sum="e9867d9bf1a9e50f832f0d95ed04106c" name="HTMLPurifier/ConfigSchema/schema/URI.Host.txt" role="php"/> + <file baseinstalldir="/" md5sum="bd2f650220fe184f7b3569141e506e34" name="HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt" role="php"/> + <file baseinstalldir="/" md5sum="ec8e59c56d5c044651291bc2fd3ec2df" name="HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt" role="php"/> + <file baseinstalldir="/" md5sum="379b2f38f7a446c97d0cecf80b57eb12" name="HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt" role="php"/> + <file baseinstalldir="/" md5sum="bdacaee370b43ee3f42057e1ce9acc5d" name="HTMLPurifier/ConfigSchema/schema/URI.Disable.txt" role="php"/> + <file baseinstalldir="/" md5sum="19c20a0f38079e050aaca40a1d80153a" name="HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt" role="php"/> + <file baseinstalldir="/" md5sum="6ae2a6306a4742c4406a3a7978c8cb8d" name="HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt" role="php"/> + <file baseinstalldir="/" md5sum="3e8b627ac6dd450053e991531169b401" name="HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt" role="php"/> + <file baseinstalldir="/" md5sum="91a4d5beceb354651773fc9dee5eae99" name="HTMLPurifier/ConfigSchema/schema/URI.Base.txt" role="php"/> + <file baseinstalldir="/" md5sum="b631543031c811300774cd15ef5705ba" name="HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt" role="php"/> + <file baseinstalldir="/" md5sum="db04b24ca81b77213fb8dc6b4a34e688" name="HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt" role="php"/> + <file baseinstalldir="/" md5sum="1812d786966978cef85926fb306081f1" name="HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt" role="php"/> + <file baseinstalldir="/" md5sum="782d42061de75233348a3c756e7540b9" name="HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt" role="php"/> + <file baseinstalldir="/" md5sum="20606c0bb24a9ed5e51908cd49b5aa0d" name="HTMLPurifier/ConfigSchema/schema/Output.Newline.txt" role="php"/> + <file baseinstalldir="/" md5sum="703a4fcb08a72ac358bf54d1a8363347" name="HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt" role="php"/> + <file baseinstalldir="/" md5sum="d88546ab0eaf014b57e260d070c65ebe" name="HTMLPurifier/ConfigSchema/schema/info.ini" role="php"/> + <file baseinstalldir="/" md5sum="bdd5c7ed524c8b4e529e5b0d0b512b0b" name="HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt" role="php"/> + <file baseinstalldir="/" md5sum="6c5f0ed641319d3ed0e70e98f40a3730" name="HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt" role="php"/> + <file baseinstalldir="/" md5sum="580fe57f57c2633dcbf5ea61f03a2c17" name="HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt" role="php"/> + <file baseinstalldir="/" md5sum="602bcdee9b9128369fece8dda6ca68d0" name="HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt" role="php"/> + <file baseinstalldir="/" md5sum="98c27e81560886a8243301ecf1356a4e" name="HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt" role="php"/> + <file baseinstalldir="/" md5sum="2627f4dfc25ae253e56241c1208b1861" name="HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt" role="php"/> + <file baseinstalldir="/" md5sum="5aa156444b034221be9647486472d38c" name="HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt" role="php"/> + <file baseinstalldir="/" md5sum="d7564565bfd9962ead012075a4f785d0" name="HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt" role="php"/> + <file baseinstalldir="/" md5sum="0f3e26408858326c3ea07d07cd6f32ba" name="HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt" role="php"/> + <file baseinstalldir="/" md5sum="827049a0989ef1af9d513b7cf0ebd088" name="HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt" role="php"/> + <file baseinstalldir="/" md5sum="bf719ef499e5092b1745fdc0145544a8" name="HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt" role="php"/> + <file baseinstalldir="/" md5sum="aaa953fcd68ebc340e9cb465e49951b6" name="HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt" role="php"/> + <file baseinstalldir="/" md5sum="44bc257b528121bd62f852331ebf7583" name="HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt" role="php"/> + <file baseinstalldir="/" md5sum="239a45c10f6eff0a180e0da5f89cf05a" name="HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt" role="php"/> + <file baseinstalldir="/" md5sum="4236b3a52572b275840ea600ffe4c91e" name="HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt" role="php"/> + <file baseinstalldir="/" md5sum="1797469f7d736a99d4e2aa67f5de7acd" name="HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt" role="php"/> + <file baseinstalldir="/" md5sum="9a9c021bd1610663aee54f9d830fff0b" name="HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt" role="php"/> + <file baseinstalldir="/" md5sum="27a1c037eda91bea2eee5229e4f1a058" name="HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt" role="php"/> + <file baseinstalldir="/" md5sum="f7d9ac0023cccb32f6792cbfc82be402" name="HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt" role="php"/> + <file baseinstalldir="/" md5sum="0f39567afbe1f3b9764b1350e494dd4a" name="HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt" role="php"/> + <file baseinstalldir="/" md5sum="8bfa42cf1474478dc3069884a58e9441" name="HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt" role="php"/> + <file baseinstalldir="/" md5sum="c7a97b0bb36b164149166153ca40cabe" name="HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt" role="php"/> + <file baseinstalldir="/" md5sum="74625ff69c0bb72ac2ea00be938fd4a2" name="HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt" role="php"/> + <file baseinstalldir="/" md5sum="fe80810deab2b76f801ce09c658a426e" name="HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt" role="php"/> + <file baseinstalldir="/" md5sum="7941c66897f6cbfa436c00232b0c0319" name="HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt" role="php"/> + <file baseinstalldir="/" md5sum="962dc2f6f680a67c76e90ca58defa8ba" name="HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt" role="php"/> + <file baseinstalldir="/" md5sum="afffc795383c3d375ce1f8d538353004" name="HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt" role="php"/> + <file baseinstalldir="/" md5sum="9df7447f59f04e26a711c9cd289c0d02" name="HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt" role="php"/> + <file baseinstalldir="/" md5sum="6e099862fb32d89340d934cdc392ba82" name="HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt" role="php"/> + <file baseinstalldir="/" md5sum="e39e05351052579f6db967f06eb34124" name="HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt" role="php"/> + <file baseinstalldir="/" md5sum="973b359f1e0c27649c661665b6cce6b5" name="HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt" role="php"/> + <file baseinstalldir="/" md5sum="636354d2cc7b1e142ed4b4bac719afe7" name="HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt" role="php"/> + <file baseinstalldir="/" md5sum="1c27523a1bf4fb4bc1b5efeb31374bf1" name="HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt" role="php"/> + <file baseinstalldir="/" md5sum="a610159b4dc4b4f090faab367df05913" name="HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt" role="php"/> + <file baseinstalldir="/" md5sum="4cdc8843d64b8954826fe63ab800c4a4" name="HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt" role="php"/> + <file baseinstalldir="/" md5sum="5766c211d61ee8e63d84221cdfc0c382" name="HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt" role="php"/> + <file baseinstalldir="/" md5sum="eb240130a1906d9c6402db7da045e069" name="HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt" role="php"/> + <file baseinstalldir="/" md5sum="6d9eb286dd0ad35fc9a42e888e437a11" name="HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt" role="php"/> + <file baseinstalldir="/" md5sum="b1994238ead13791bfae55ac6ead56e2" name="HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt" role="php"/> + <file baseinstalldir="/" md5sum="6c673a7c23196cdc6b4b430d8d77e4d0" name="HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt" role="php"/> + <file baseinstalldir="/" md5sum="7d76686be9f82a742690c6bc5ac2172b" name="HTMLPurifier/ConfigSchema/schema/Core.Language.txt" role="php"/> + <file baseinstalldir="/" md5sum="f6e08c75224c0681a0933aaa70122db1" name="HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt" role="php"/> + <file baseinstalldir="/" md5sum="c79d40b21101ebe0f842c84b7f308e97" name="HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt" role="php"/> + <file baseinstalldir="/" md5sum="e9f14e5050c5920c7e9006b9f28bd76f" name="HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt" role="php"/> + <file baseinstalldir="/" md5sum="27a34d8862655c293db261bfa3b6690f" name="HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt" role="php"/> + <file baseinstalldir="/" md5sum="438f7ac032cb0dd31e053c5791975eb3" name="HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt" role="php"/> + <file baseinstalldir="/" md5sum="10b09a0750da4d12c0780bcd83bf40cb" name="HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt" role="php"/> + <file baseinstalldir="/" md5sum="f9a2ba865c9358f9acdcb1af76c75248" name="HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt" role="php"/> + <file baseinstalldir="/" md5sum="89a38cc8baee51e92df7a6f656c958ab" name="HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt" role="php"/> + <file baseinstalldir="/" md5sum="8abd158052b5fe867664cb53dc7016ff" name="HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt" role="php"/> + <file baseinstalldir="/" md5sum="abfa880ace0da126afa0404db057ccf3" name="HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt" role="php"/> + <file baseinstalldir="/" md5sum="6ec326585143d337e8aa91a8449076aa" name="HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt" role="php"/> + <file baseinstalldir="/" md5sum="ab8bbc7c368a70b8997cee521f0f8266" name="HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt" role="php"/> + <file baseinstalldir="/" md5sum="3d2523b07cbea92b6928a2c32a3bbbc0" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt" role="php"/> + <file baseinstalldir="/" md5sum="a8a28c98305dd6a9abb5464acd5c7367" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt" role="php"/> + <file baseinstalldir="/" md5sum="e53542febfba19782c49cba0a61cc8c2" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt" role="php"/> + <file baseinstalldir="/" md5sum="28380c89a107c47d614155a10a2cbfdf" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt" role="php"/> + <file baseinstalldir="/" md5sum="d5f5ebc07893f09271b5910e9515827e" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt" role="php"/> + <file baseinstalldir="/" md5sum="21aea6427b2a3c2dfb298d02e96ec669" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt" role="php"/> + <file baseinstalldir="/" md5sum="bea8bbdbbd31448a02d92663214e984c" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt" role="php"/> + <file baseinstalldir="/" md5sum="12dd6d349d980eee22f922e963c001b4" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt" role="php"/> + <file baseinstalldir="/" md5sum="6271b4a4b8b7af81af43f19969c2bde3" name="HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt" role="php"/> + <file baseinstalldir="/" md5sum="1b26b5763bb96c96c98abc3236ee6b19" name="HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt" role="php"/> + <file baseinstalldir="/" md5sum="41ddfd49fafdfa843b26864ee200d983" name="HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt" role="php"/> + <file baseinstalldir="/" md5sum="376c4ef45a5ca3cb19d77774856ddfdf" name="HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt" role="php"/> + <file baseinstalldir="/" md5sum="97ddc74ca5ad0affb327e1f593ede768" name="HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt" role="php"/> + <file baseinstalldir="/" md5sum="d96aa90796e69c12456daf563af1179a" name="HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt" role="php"/> + <file baseinstalldir="/" md5sum="b08ddfb891716ac35c74b281827371b8" name="HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt" role="php"/> + <file baseinstalldir="/" md5sum="d0bb87419dd6922763aaa8c4331d53fd" name="HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt" role="php"/> + <file baseinstalldir="/" md5sum="a5d0f2c37dc21f4ca4c0aea1143bc906" name="HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt" role="php"/> + <file baseinstalldir="/" md5sum="f8d19a8c404092f2f789ddb18cbe2c93" name="HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt" role="php"/> + <file baseinstalldir="/" md5sum="35d99a66652595c744ebe8b0ffe48165" name="HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt" role="php"/> + <file baseinstalldir="/" md5sum="417ac415d2e698fa41b3272c3357eae2" name="HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt" role="php"/> + <file baseinstalldir="/" md5sum="cf02eb79ccac04001536539185d9112b" name="HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt" role="php"/> + <file baseinstalldir="/" md5sum="e7572119a13e8d41d980954283020b1b" name="HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt" role="php"/> + <file baseinstalldir="/" md5sum="a2a1e573097a562d5bf0e15e87033db8" name="HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt" role="php"/> + <file baseinstalldir="/" md5sum="c7e804eef84fd4e84555da46f5e67d78" name="HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt" role="php"/> + <file baseinstalldir="/" md5sum="9d2d7c8e8a29bc9ba915d2ec0af4ed89" name="HTMLPurifier/ConfigSchema/schema.ser" role="php"/> + <file baseinstalldir="/" md5sum="2da487ed0053dd17f8f4c13a7b3cf04d" name="HTMLPurifier/ConfigSchema/InterchangeBuilder.php" role="php"/> + <file baseinstalldir="/" md5sum="b67c7f7562c9eac082a960b3be9f5f1f" name="HTMLPurifier/ConfigSchema/Interchange/Id.php" role="php"/> + <file baseinstalldir="/" md5sum="3b11731a184f921ad82632a4d5ca8ea3" name="HTMLPurifier/ConfigSchema/Interchange/Directive.php" role="php"/> + <file baseinstalldir="/" md5sum="16ec96e01b7a93a75f6c0d27a3044d2c" name="HTMLPurifier/ConfigSchema/Interchange.php" role="php"/> + <file baseinstalldir="/" md5sum="31bf3afba867409fdf11f75eaa3725fd" name="HTMLPurifier/ConfigSchema/Exception.php" role="php"/> + <file baseinstalldir="/" md5sum="2f3ef23752e2cb399fea85c3368b2d47" name="HTMLPurifier/ConfigSchema/Builder/Xml.php" role="php"/> + <file baseinstalldir="/" md5sum="a9d77f36c69fcdeeaffdddccf53b1aea" name="HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php" role="php"/> + <file baseinstalldir="/" md5sum="37ccb7351a8bcc8c49bba3c2d312996d" name="HTMLPurifier/ConfigSchema.php" role="php"/> + <file baseinstalldir="/" md5sum="aefeb82b17623a6d0594a084afb664b4" name="HTMLPurifier/Config.php" role="php"/> + <file baseinstalldir="/" md5sum="98c3856ffede2c519fa703dee541fc01" name="HTMLPurifier/ChildDef/Table.php" role="php"/> + <file baseinstalldir="/" md5sum="538d675731b05c7483ec31e1ae55ca4a" name="HTMLPurifier/ChildDef/StrictBlockquote.php" role="php"/> + <file baseinstalldir="/" md5sum="682f8da336f2a1dad1cd7880d4848076" name="HTMLPurifier/ChildDef/Required.php" role="php"/> + <file baseinstalldir="/" md5sum="6b324d68260f872dc1a9312608af70bf" name="HTMLPurifier/ChildDef/Optional.php" role="php"/> + <file baseinstalldir="/" md5sum="22f2d339fcb60c536e651f80f03f7c82" name="HTMLPurifier/ChildDef/Empty.php" role="php"/> + <file baseinstalldir="/" md5sum="13c44a1aaf1ab1b25744758cd1831b4f" name="HTMLPurifier/ChildDef/Custom.php" role="php"/> + <file baseinstalldir="/" md5sum="3c557d08cadb051c7e1711376da3acb8" name="HTMLPurifier/ChildDef/Chameleon.php" role="php"/> + <file baseinstalldir="/" md5sum="281eaa00a5e3ff98b611b5ebca97bcb0" name="HTMLPurifier/ChildDef.php" role="php"/> + <file baseinstalldir="/" md5sum="de2d3205b26e42deaa94d8d74e3692c4" name="HTMLPurifier/Bootstrap.php" role="php"/> + <file baseinstalldir="/" md5sum="53aebf76f83099026c95c03844ecf39b" name="HTMLPurifier/AttrValidator.php" role="php"/> + <file baseinstalldir="/" md5sum="b1ba10c4bcd37a7f5e9a6c97a81b18c8" name="HTMLPurifier/AttrTypes.php" role="php"/> + <file baseinstalldir="/" md5sum="45c48b09a03a9abef719748ce27c4728" name="HTMLPurifier/AttrTransform/Textarea.php" role="php"/> + <file baseinstalldir="/" md5sum="aeeb086b76d4bcb87d8a36a61b7ca644" name="HTMLPurifier/AttrTransform/ScriptRequired.php" role="php"/> + <file baseinstalldir="/" md5sum="e6239d696e5a698544d8aecef02871da" name="HTMLPurifier/AttrTransform/SafeParam.php" role="php"/> + <file baseinstalldir="/" md5sum="6575e1225807af9eb79e3c005b028591" name="HTMLPurifier/AttrTransform/SafeObject.php" role="php"/> + <file baseinstalldir="/" md5sum="c9814258f610f4cbc17b6a511289b329" name="HTMLPurifier/AttrTransform/SafeEmbed.php" role="php"/> + <file baseinstalldir="/" md5sum="03c2f1e68d493f53430c42d4c8806166" name="HTMLPurifier/AttrTransform/NameSync.php" role="php"/> + <file baseinstalldir="/" md5sum="563026acbdd46b682f91cdfea1103ce8" name="HTMLPurifier/AttrTransform/Name.php" role="php"/> + <file baseinstalldir="/" md5sum="0c9bfa9cdc93cff8714b237e854f4595" name="HTMLPurifier/AttrTransform/Length.php" role="php"/> + <file baseinstalldir="/" md5sum="17dd8373c191efd5c95bfe5fb79ed8fe" name="HTMLPurifier/AttrTransform/Lang.php" role="php"/> + <file baseinstalldir="/" md5sum="a0ee62ef96c7ac5c6add36ef47f9ba47" name="HTMLPurifier/AttrTransform/Input.php" role="php"/> + <file baseinstalldir="/" md5sum="107148ff2b640ff7afbfc59e638812f5" name="HTMLPurifier/AttrTransform/ImgSpace.php" role="php"/> + <file baseinstalldir="/" md5sum="87bd7efd4aaaa1f439a0a948d2c48a52" name="HTMLPurifier/AttrTransform/ImgRequired.php" role="php"/> + <file baseinstalldir="/" md5sum="f1f2fb8deb98f319592e4f55545facee" name="HTMLPurifier/AttrTransform/EnumToCSS.php" role="php"/> + <file baseinstalldir="/" md5sum="faca234a22422d5cf69ca7068145eac8" name="HTMLPurifier/AttrTransform/Border.php" role="php"/> + <file baseinstalldir="/" md5sum="b7d7e4d45de29fc50eefdc97e760e6d5" name="HTMLPurifier/AttrTransform/BoolToCSS.php" role="php"/> + <file baseinstalldir="/" md5sum="ec7f74671186c8e5e93a381cce6470fa" name="HTMLPurifier/AttrTransform/BgColor.php" role="php"/> + <file baseinstalldir="/" md5sum="3e6afa73230fc31d74eb382bd42bb18a" name="HTMLPurifier/AttrTransform/BdoDir.php" role="php"/> + <file baseinstalldir="/" md5sum="1decd9fa8e0777811024260549d29aaa" name="HTMLPurifier/AttrTransform/Background.php" role="php"/> + <file baseinstalldir="/" md5sum="18870fa532d982a78f20d00e93b6ba3e" name="HTMLPurifier/AttrTransform.php" role="php"/> + <file baseinstalldir="/" md5sum="44004a19ee045f386993905c84600d61" name="HTMLPurifier/AttrDef/URI/IPv6.php" role="php"/> + <file baseinstalldir="/" md5sum="da52096d6feb81bd8a40f9d5ed439139" name="HTMLPurifier/AttrDef/URI/IPv4.php" role="php"/> + <file baseinstalldir="/" md5sum="38b3ca8b1c3522fbbd27bce785bc58ab" name="HTMLPurifier/AttrDef/URI/Host.php" role="php"/> + <file baseinstalldir="/" md5sum="cebebf0d99b63ecd2072f37e69055b6c" name="HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php" role="php"/> + <file baseinstalldir="/" md5sum="958e3072379737950b8ce0f6823ac8eb" name="HTMLPurifier/AttrDef/URI/Email.php" role="php"/> + <file baseinstalldir="/" md5sum="05afd73beee5b644bc49790b7a0dde0f" name="HTMLPurifier/AttrDef/URI.php" role="php"/> + <file baseinstalldir="/" md5sum="0ad9dcd58ce4f104006b59a9df51802a" name="HTMLPurifier/AttrDef/Text.php" role="php"/> + <file baseinstalldir="/" md5sum="8fcf6999bbd3f2cd9d3780182d91be7d" name="HTMLPurifier/AttrDef/Switch.php" role="php"/> + <file baseinstalldir="/" md5sum="04a72938e4c07553d9a18ae6649cd90f" name="HTMLPurifier/AttrDef/Lang.php" role="php"/> + <file baseinstalldir="/" md5sum="f57bc53b7c7a29b0c4324e0ad2039c2a" name="HTMLPurifier/AttrDef/Integer.php" role="php"/> + <file baseinstalldir="/" md5sum="95962e03883b9a84496487a1f8af5da3" name="HTMLPurifier/AttrDef/HTML/Pixels.php" role="php"/> + <file baseinstalldir="/" md5sum="8face464a844799e3aab2e9f4f918868" name="HTMLPurifier/AttrDef/HTML/Nmtokens.php" role="php"/> + <file baseinstalldir="/" md5sum="122498f21ff87dcab5ec8f1dfb51d39c" name="HTMLPurifier/AttrDef/HTML/MultiLength.php" role="php"/> + <file baseinstalldir="/" md5sum="ed11d4126e34e45fd127650a45f0ee56" name="HTMLPurifier/AttrDef/HTML/LinkTypes.php" role="php"/> + <file baseinstalldir="/" md5sum="a38926a6e784e67d5cd7e9d657802214" name="HTMLPurifier/AttrDef/HTML/Length.php" role="php"/> + <file baseinstalldir="/" md5sum="bd36cb0c65c586c2d1dff7a44a4bf0dc" name="HTMLPurifier/AttrDef/HTML/ID.php" role="php"/> + <file baseinstalldir="/" md5sum="96026f3e8ffdc70e533f306e63322923" name="HTMLPurifier/AttrDef/HTML/FrameTarget.php" role="php"/> + <file baseinstalldir="/" md5sum="ed699f61359c5d7ab046d248253e5d9e" name="HTMLPurifier/AttrDef/HTML/Color.php" role="php"/> + <file baseinstalldir="/" md5sum="fa319109d0c0e5989a24f550c884c1b5" name="HTMLPurifier/AttrDef/HTML/Class.php" role="php"/> + <file baseinstalldir="/" md5sum="c579b5a145c447f1880c7bc6d3db774c" name="HTMLPurifier/AttrDef/HTML/Bool.php" role="php"/> + <file baseinstalldir="/" md5sum="1a11be0015a161c2a11d6c51305f45fa" name="HTMLPurifier/AttrDef/Enum.php" role="php"/> + <file baseinstalldir="/" md5sum="aea3d16cd52cbdf70ec2c9fd6e28b166" name="HTMLPurifier/AttrDef/CSS/URI.php" role="php"/> + <file baseinstalldir="/" md5sum="54f6f27a44bdb66d6197a46ba89213e8" name="HTMLPurifier/AttrDef/CSS/TextDecoration.php" role="php"/> + <file baseinstalldir="/" md5sum="10afadc403e14974645b3e1c17f83adb" name="HTMLPurifier/AttrDef/CSS/Percentage.php" role="php"/> + <file baseinstalldir="/" md5sum="0f1f03450117004aabb529c43f154334" name="HTMLPurifier/AttrDef/CSS/Number.php" role="php"/> + <file baseinstalldir="/" md5sum="2c4560cf0dd700ae39c0a553140ffd1b" name="HTMLPurifier/AttrDef/CSS/Multiple.php" role="php"/> + <file baseinstalldir="/" md5sum="3d888cc157a4ea616dc1e94dd5f1c21e" name="HTMLPurifier/AttrDef/CSS/ListStyle.php" role="php"/> + <file baseinstalldir="/" md5sum="6774b03e0b29672961ad4fba15c25787" name="HTMLPurifier/AttrDef/CSS/Length.php" role="php"/> + <file baseinstalldir="/" md5sum="9a9a4ef13940096bbe18037d54cb056f" name="HTMLPurifier/AttrDef/CSS/ImportantDecorator.php" role="php"/> + <file baseinstalldir="/" md5sum="59ca381609f084649bf71d8093eab3a9" name="HTMLPurifier/AttrDef/CSS/FontFamily.php" role="php"/> + <file baseinstalldir="/" md5sum="c14c12936339b03f24e9b50cd5273450" name="HTMLPurifier/AttrDef/CSS/Font.php" role="php"/> + <file baseinstalldir="/" md5sum="54e42fbf58fe9c70ef082d6a8f8be821" name="HTMLPurifier/AttrDef/CSS/Filter.php" role="php"/> + <file baseinstalldir="/" md5sum="a112e7c263f001b9ac062ea64201cccd" name="HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php" role="php"/> + <file baseinstalldir="/" md5sum="ac75113a9cbfe6bfe8d3afe36444f272" name="HTMLPurifier/AttrDef/CSS/Composite.php" role="php"/> + <file baseinstalldir="/" md5sum="4bd2e4e9cd6e71ca7a6cb5e62700b380" name="HTMLPurifier/AttrDef/CSS/Color.php" role="php"/> + <file baseinstalldir="/" md5sum="0301346329aae91bae8924c90d3d4688" name="HTMLPurifier/AttrDef/CSS/Border.php" role="php"/> + <file baseinstalldir="/" md5sum="ecc93e18d1aeea6e8231119575e40e9c" name="HTMLPurifier/AttrDef/CSS/BackgroundPosition.php" role="php"/> + <file baseinstalldir="/" md5sum="8a16f12cb9b9f6d941b7a82f1c40862c" name="HTMLPurifier/AttrDef/CSS/Background.php" role="php"/> + <file baseinstalldir="/" md5sum="f0232cc2a7573418b8ebe5efcf108c46" name="HTMLPurifier/AttrDef/CSS/AlphaValue.php" role="php"/> + <file baseinstalldir="/" md5sum="6caf3a641704cee2aeb15b4eb1c287c9" name="HTMLPurifier/AttrDef/CSS.php" role="php"/> + <file baseinstalldir="/" md5sum="c2ac5eb00e5fe0dfe53793a532243826" name="HTMLPurifier/AttrDef.php" role="php"/> + <file baseinstalldir="/" md5sum="5b1067853574bae62d9cd70370ff10af" name="HTMLPurifier/AttrCollections.php" role="php"/> + <file baseinstalldir="/" md5sum="23904b4310c99fa9da91bff4bd3b223a" name="HTMLPurifier.safe-includes.php" role="php"/> + <file baseinstalldir="/" md5sum="a630aa63016ded28453e109edc35d331" name="HTMLPurifier.php" role="php"/> + <file baseinstalldir="/" md5sum="25043ef5e632f88e8d5c825a8516a1cc" name="HTMLPurifier.kses.php" role="php"/> + <file baseinstalldir="/" md5sum="d1c01716d0f9c51bc61183466eb7e4c1" name="HTMLPurifier.includes.php" role="php"/> + <file baseinstalldir="/" md5sum="f09594e7a7db5826b4b3aef3b9f874ed" name="HTMLPurifier.func.php" role="php"/> + <file baseinstalldir="/" md5sum="4bb70e5ba6b06a23b8416cc0e59254bb" name="HTMLPurifier.autoload.php" role="php"/> + <file baseinstalldir="/" md5sum="0f6dba2689f471c382240c8d2d7892ba" name="HTMLPurifier.auto.php" role="php"/> + </dir> + </contents> + <dependencies> + <required> + <php> + <min>5.0.0</min> + </php> + <pearinstaller> + <min>1.4.3</min> + </pearinstaller> + </required> + </dependencies> + <phprelease> + <changelog> + <release> + <version> + <release>4.0.0</release> + <api>4.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <date>2009-07-07</date> + <license uri="http://www.gnu.org/licenses/lgpl.html">LGPL</license> + <notes> +HTML Purifier 4.0 is a major feature release focused on configuration +It deprecates the $config->set('Ns', 'Directive', $value) syntax for +$config->set('Ns.Directive', $value); both syntaxes work but the +former will throw errors. There are also some new features: robust +support for name/id, configuration inheritance, remove nbsp in +the RemoveEmpty autoformatter, userland configuration directives +and configuration serialization. + </notes> + </release> + </changelog> + </phprelease> +</package> diff --git a/lib/.xmlregistry/packages/pear.unl.edu/UNL_Autoload/0.5.0-info.xml b/lib/.xmlregistry/packages/pear.unl.edu/UNL_Autoload/0.5.0-info.xml new file mode 100644 index 0000000..e42cd50 --- /dev/null +++ b/lib/.xmlregistry/packages/pear.unl.edu/UNL_Autoload/0.5.0-info.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.8.0alpha1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> + <name>UNL_Autoload</name> + <channel>pear.unl.edu</channel> + <summary>An autoloader implementation for UNL PEAR packages</summary> + <description>This package provides an autoloader for classes beginning + with UNL_ and is mainly used for autoloading package files from http://pear.unl.edu/.</description> + <lead> + <name>Brett Bieber</name> + <user>saltybeagle</user> + <email>brett.bieber@gmail.com</email> + <active>yes</active> + </lead> + <date>2010-04-15</date> + <time>09:16:05</time> + <version> + <release>0.5.0</release> + <api>0.5.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <license uri="http://www1.unl.edu/wdn/wiki/Software_License">BSD License</license> + <notes>* First release.</notes> + <contents> + <dir name="/"> + <file baseinstalldir="/" md5sum="2d13c44763ebe506f915d211dcd8f00a" name="UNL/Autoload.php" role="php"/> + </dir> + </contents> + <dependencies> + <required> + <php> + <min>5.2.0</min> + </php> + <pearinstaller> + <min>1.4.3</min> + </pearinstaller> + </required> + </dependencies> + <phprelease> + <changelog> + <release> + <version> + <release>0.5.0</release> + <api>0.5.0</api> + </version> + <stability> + <release>alpha</release> + <api>alpha</api> + </stability> + <date>2008-11-10</date> + <license uri="http://www1.unl.edu/wdn/wiki/Software_License">BSD License</license> + <notes>* First release.</notes> + </release> + </changelog> + </phprelease> +</package> diff --git a/lib/downloads/HTMLPurifier-4.0.0.tgz b/lib/downloads/HTMLPurifier-4.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..da30c2df3f6e70b7e748b2ae22d0c9d74d6c6de6 GIT binary patch literal 252757 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 S00030{{sLFFHD{Qx)lH%4aq40 literal 0 HcmV?d00001 diff --git a/lib/downloads/UNL_Autoload-0.5.0.tgz b/lib/downloads/UNL_Autoload-0.5.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f5b5373556dd6878b8276748534baeee14745a10 GIT binary patch literal 1716 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 KCJU1Q9smF;fI{g2 literal 0 HcmV?d00001 diff --git a/lib/php/HTMLPurifier.auto.php b/lib/php/HTMLPurifier.auto.php new file mode 100644 index 0000000..1960c39 --- /dev/null +++ b/lib/php/HTMLPurifier.auto.php @@ -0,0 +1,11 @@ +<?php + +/** + * This is a stub include that automatically configures the include path. + */ + +set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() ); +require_once 'HTMLPurifier/Bootstrap.php'; +require_once 'HTMLPurifier.autoload.php'; + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier.autoload.php b/lib/php/HTMLPurifier.autoload.php new file mode 100644 index 0000000..8d40176 --- /dev/null +++ b/lib/php/HTMLPurifier.autoload.php @@ -0,0 +1,21 @@ +<?php + +/** + * @file + * Convenience file that registers autoload handler for HTML Purifier. + */ + +if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) { + // We need unregister for our pre-registering functionality + HTMLPurifier_Bootstrap::registerAutoload(); + if (function_exists('__autoload')) { + // Be polite and ensure that userland autoload gets retained + spl_autoload_register('__autoload'); + } +} elseif (!function_exists('__autoload')) { + function __autoload($class) { + return HTMLPurifier_Bootstrap::autoload($class); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier.func.php b/lib/php/HTMLPurifier.func.php new file mode 100644 index 0000000..56a55b2 --- /dev/null +++ b/lib/php/HTMLPurifier.func.php @@ -0,0 +1,23 @@ +<?php + +/** + * @file + * Defines a function wrapper for HTML Purifier for quick use. + * @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()'' + */ + +/** + * Purify HTML. + * @param $html String HTML to purify + * @param $config Configuration to use, can be any value accepted by + * HTMLPurifier_Config::create() + */ +function HTMLPurifier($html, $config = null) { + static $purifier = false; + if (!$purifier) { + $purifier = new HTMLPurifier(); + } + return $purifier->purify($html, $config); +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier.includes.php b/lib/php/HTMLPurifier.includes.php new file mode 100644 index 0000000..7cfb970 --- /dev/null +++ b/lib/php/HTMLPurifier.includes.php @@ -0,0 +1,208 @@ +<?php + +/** + * @file + * This file was auto-generated by generate-includes.php and includes all of + * the core files required by HTML Purifier. Use this if performance is a + * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS + * FILE, changes will be overwritten the next time the script is run. + * + * @version 4.0.0 + * + * @warning + * You must *not* include any other HTML Purifier files before this file, + * because 'require' not 'require_once' is used. + * + * @warning + * This file requires that the include path contains the HTML Purifier + * library directory; this is not auto-set. + */ + +require 'HTMLPurifier.php'; +require 'HTMLPurifier/AttrCollections.php'; +require 'HTMLPurifier/AttrDef.php'; +require 'HTMLPurifier/AttrTransform.php'; +require 'HTMLPurifier/AttrTypes.php'; +require 'HTMLPurifier/AttrValidator.php'; +require 'HTMLPurifier/Bootstrap.php'; +require 'HTMLPurifier/Definition.php'; +require 'HTMLPurifier/CSSDefinition.php'; +require 'HTMLPurifier/ChildDef.php'; +require 'HTMLPurifier/Config.php'; +require 'HTMLPurifier/ConfigSchema.php'; +require 'HTMLPurifier/ContentSets.php'; +require 'HTMLPurifier/Context.php'; +require 'HTMLPurifier/DefinitionCache.php'; +require 'HTMLPurifier/DefinitionCacheFactory.php'; +require 'HTMLPurifier/Doctype.php'; +require 'HTMLPurifier/DoctypeRegistry.php'; +require 'HTMLPurifier/ElementDef.php'; +require 'HTMLPurifier/Encoder.php'; +require 'HTMLPurifier/EntityLookup.php'; +require 'HTMLPurifier/EntityParser.php'; +require 'HTMLPurifier/ErrorCollector.php'; +require 'HTMLPurifier/ErrorStruct.php'; +require 'HTMLPurifier/Exception.php'; +require 'HTMLPurifier/Filter.php'; +require 'HTMLPurifier/Generator.php'; +require 'HTMLPurifier/HTMLDefinition.php'; +require 'HTMLPurifier/HTMLModule.php'; +require 'HTMLPurifier/HTMLModuleManager.php'; +require 'HTMLPurifier/IDAccumulator.php'; +require 'HTMLPurifier/Injector.php'; +require 'HTMLPurifier/Language.php'; +require 'HTMLPurifier/LanguageFactory.php'; +require 'HTMLPurifier/Length.php'; +require 'HTMLPurifier/Lexer.php'; +require 'HTMLPurifier/PercentEncoder.php'; +require 'HTMLPurifier/PropertyList.php'; +require 'HTMLPurifier/PropertyListIterator.php'; +require 'HTMLPurifier/Strategy.php'; +require 'HTMLPurifier/StringHash.php'; +require 'HTMLPurifier/StringHashParser.php'; +require 'HTMLPurifier/TagTransform.php'; +require 'HTMLPurifier/Token.php'; +require 'HTMLPurifier/TokenFactory.php'; +require 'HTMLPurifier/URI.php'; +require 'HTMLPurifier/URIDefinition.php'; +require 'HTMLPurifier/URIFilter.php'; +require 'HTMLPurifier/URIParser.php'; +require 'HTMLPurifier/URIScheme.php'; +require 'HTMLPurifier/URISchemeRegistry.php'; +require 'HTMLPurifier/UnitConverter.php'; +require 'HTMLPurifier/VarParser.php'; +require 'HTMLPurifier/VarParserException.php'; +require 'HTMLPurifier/AttrDef/CSS.php'; +require 'HTMLPurifier/AttrDef/Enum.php'; +require 'HTMLPurifier/AttrDef/Integer.php'; +require 'HTMLPurifier/AttrDef/Lang.php'; +require 'HTMLPurifier/AttrDef/Switch.php'; +require 'HTMLPurifier/AttrDef/Text.php'; +require 'HTMLPurifier/AttrDef/URI.php'; +require 'HTMLPurifier/AttrDef/CSS/Number.php'; +require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php'; +require 'HTMLPurifier/AttrDef/CSS/Background.php'; +require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php'; +require 'HTMLPurifier/AttrDef/CSS/Border.php'; +require 'HTMLPurifier/AttrDef/CSS/Color.php'; +require 'HTMLPurifier/AttrDef/CSS/Composite.php'; +require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php'; +require 'HTMLPurifier/AttrDef/CSS/Filter.php'; +require 'HTMLPurifier/AttrDef/CSS/Font.php'; +require 'HTMLPurifier/AttrDef/CSS/FontFamily.php'; +require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php'; +require 'HTMLPurifier/AttrDef/CSS/Length.php'; +require 'HTMLPurifier/AttrDef/CSS/ListStyle.php'; +require 'HTMLPurifier/AttrDef/CSS/Multiple.php'; +require 'HTMLPurifier/AttrDef/CSS/Percentage.php'; +require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php'; +require 'HTMLPurifier/AttrDef/CSS/URI.php'; +require 'HTMLPurifier/AttrDef/HTML/Bool.php'; +require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php'; +require 'HTMLPurifier/AttrDef/HTML/Class.php'; +require 'HTMLPurifier/AttrDef/HTML/Color.php'; +require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php'; +require 'HTMLPurifier/AttrDef/HTML/ID.php'; +require 'HTMLPurifier/AttrDef/HTML/Pixels.php'; +require 'HTMLPurifier/AttrDef/HTML/Length.php'; +require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php'; +require 'HTMLPurifier/AttrDef/HTML/MultiLength.php'; +require 'HTMLPurifier/AttrDef/URI/Email.php'; +require 'HTMLPurifier/AttrDef/URI/Host.php'; +require 'HTMLPurifier/AttrDef/URI/IPv4.php'; +require 'HTMLPurifier/AttrDef/URI/IPv6.php'; +require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php'; +require 'HTMLPurifier/AttrTransform/Background.php'; +require 'HTMLPurifier/AttrTransform/BdoDir.php'; +require 'HTMLPurifier/AttrTransform/BgColor.php'; +require 'HTMLPurifier/AttrTransform/BoolToCSS.php'; +require 'HTMLPurifier/AttrTransform/Border.php'; +require 'HTMLPurifier/AttrTransform/EnumToCSS.php'; +require 'HTMLPurifier/AttrTransform/ImgRequired.php'; +require 'HTMLPurifier/AttrTransform/ImgSpace.php'; +require 'HTMLPurifier/AttrTransform/Input.php'; +require 'HTMLPurifier/AttrTransform/Lang.php'; +require 'HTMLPurifier/AttrTransform/Length.php'; +require 'HTMLPurifier/AttrTransform/Name.php'; +require 'HTMLPurifier/AttrTransform/NameSync.php'; +require 'HTMLPurifier/AttrTransform/SafeEmbed.php'; +require 'HTMLPurifier/AttrTransform/SafeObject.php'; +require 'HTMLPurifier/AttrTransform/SafeParam.php'; +require 'HTMLPurifier/AttrTransform/ScriptRequired.php'; +require 'HTMLPurifier/AttrTransform/Textarea.php'; +require 'HTMLPurifier/ChildDef/Chameleon.php'; +require 'HTMLPurifier/ChildDef/Custom.php'; +require 'HTMLPurifier/ChildDef/Empty.php'; +require 'HTMLPurifier/ChildDef/Required.php'; +require 'HTMLPurifier/ChildDef/Optional.php'; +require 'HTMLPurifier/ChildDef/StrictBlockquote.php'; +require 'HTMLPurifier/ChildDef/Table.php'; +require 'HTMLPurifier/DefinitionCache/Decorator.php'; +require 'HTMLPurifier/DefinitionCache/Null.php'; +require 'HTMLPurifier/DefinitionCache/Serializer.php'; +require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php'; +require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php'; +require 'HTMLPurifier/HTMLModule/Bdo.php'; +require 'HTMLPurifier/HTMLModule/CommonAttributes.php'; +require 'HTMLPurifier/HTMLModule/Edit.php'; +require 'HTMLPurifier/HTMLModule/Forms.php'; +require 'HTMLPurifier/HTMLModule/Hypertext.php'; +require 'HTMLPurifier/HTMLModule/Image.php'; +require 'HTMLPurifier/HTMLModule/Legacy.php'; +require 'HTMLPurifier/HTMLModule/List.php'; +require 'HTMLPurifier/HTMLModule/Name.php'; +require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; +require 'HTMLPurifier/HTMLModule/Object.php'; +require 'HTMLPurifier/HTMLModule/Presentation.php'; +require 'HTMLPurifier/HTMLModule/Proprietary.php'; +require 'HTMLPurifier/HTMLModule/Ruby.php'; +require 'HTMLPurifier/HTMLModule/SafeEmbed.php'; +require 'HTMLPurifier/HTMLModule/SafeObject.php'; +require 'HTMLPurifier/HTMLModule/Scripting.php'; +require 'HTMLPurifier/HTMLModule/StyleAttribute.php'; +require 'HTMLPurifier/HTMLModule/Tables.php'; +require 'HTMLPurifier/HTMLModule/Target.php'; +require 'HTMLPurifier/HTMLModule/Text.php'; +require 'HTMLPurifier/HTMLModule/Tidy.php'; +require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; +require 'HTMLPurifier/HTMLModule/Tidy/Name.php'; +require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php'; +require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php'; +require 'HTMLPurifier/HTMLModule/Tidy/Strict.php'; +require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php'; +require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php'; +require 'HTMLPurifier/Injector/AutoParagraph.php'; +require 'HTMLPurifier/Injector/DisplayLinkURI.php'; +require 'HTMLPurifier/Injector/Linkify.php'; +require 'HTMLPurifier/Injector/PurifierLinkify.php'; +require 'HTMLPurifier/Injector/RemoveEmpty.php'; +require 'HTMLPurifier/Injector/SafeObject.php'; +require 'HTMLPurifier/Lexer/DOMLex.php'; +require 'HTMLPurifier/Lexer/DirectLex.php'; +require 'HTMLPurifier/Strategy/Composite.php'; +require 'HTMLPurifier/Strategy/Core.php'; +require 'HTMLPurifier/Strategy/FixNesting.php'; +require 'HTMLPurifier/Strategy/MakeWellFormed.php'; +require 'HTMLPurifier/Strategy/RemoveForeignElements.php'; +require 'HTMLPurifier/Strategy/ValidateAttributes.php'; +require 'HTMLPurifier/TagTransform/Font.php'; +require 'HTMLPurifier/TagTransform/Simple.php'; +require 'HTMLPurifier/Token/Comment.php'; +require 'HTMLPurifier/Token/Tag.php'; +require 'HTMLPurifier/Token/Empty.php'; +require 'HTMLPurifier/Token/End.php'; +require 'HTMLPurifier/Token/Start.php'; +require 'HTMLPurifier/Token/Text.php'; +require 'HTMLPurifier/URIFilter/DisableExternal.php'; +require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; +require 'HTMLPurifier/URIFilter/HostBlacklist.php'; +require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; +require 'HTMLPurifier/URIFilter/Munge.php'; +require 'HTMLPurifier/URIScheme/ftp.php'; +require 'HTMLPurifier/URIScheme/http.php'; +require 'HTMLPurifier/URIScheme/https.php'; +require 'HTMLPurifier/URIScheme/mailto.php'; +require 'HTMLPurifier/URIScheme/news.php'; +require 'HTMLPurifier/URIScheme/nntp.php'; +require 'HTMLPurifier/VarParser/Flexible.php'; +require 'HTMLPurifier/VarParser/Native.php'; diff --git a/lib/php/HTMLPurifier.kses.php b/lib/php/HTMLPurifier.kses.php new file mode 100644 index 0000000..3143feb --- /dev/null +++ b/lib/php/HTMLPurifier.kses.php @@ -0,0 +1,30 @@ +<?php + +/** + * @file + * Emulation layer for code that used kses(), substituting in HTML Purifier. + */ + +require_once dirname(__FILE__) . '/HTMLPurifier.auto.php'; + +function kses($string, $allowed_html, $allowed_protocols = null) { + $config = HTMLPurifier_Config::createDefault(); + $allowed_elements = array(); + $allowed_attributes = array(); + foreach ($allowed_html as $element => $attributes) { + $allowed_elements[$element] = true; + foreach ($attributes as $attribute => $x) { + $allowed_attributes["$element.$attribute"] = true; + } + } + $config->set('HTML.AllowedElements', $allowed_elements); + $config->set('HTML.AllowedAttributes', $allowed_attributes); + $allowed_schemes = array(); + if ($allowed_protocols !== null) { + $config->set('URI.AllowedSchemes', $allowed_protocols); + } + $purifier = new HTMLPurifier($config); + return $purifier->purify($string); +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier.php b/lib/php/HTMLPurifier.php new file mode 100644 index 0000000..e3fce9c --- /dev/null +++ b/lib/php/HTMLPurifier.php @@ -0,0 +1,237 @@ +<?php + +/*! @mainpage + * + * HTML Purifier is an HTML filter that will take an arbitrary snippet of + * HTML and rigorously test, validate and filter it into a version that + * is safe for output onto webpages. It achieves this by: + * + * -# Lexing (parsing into tokens) the document, + * -# Executing various strategies on the tokens: + * -# Removing all elements not in the whitelist, + * -# Making the tokens well-formed, + * -# Fixing the nesting of the nodes, and + * -# Validating attributes of the nodes; and + * -# Generating HTML from the purified tokens. + * + * However, most users will only need to interface with the HTMLPurifier + * and HTMLPurifier_Config. + */ + +/* + HTML Purifier 4.0.0 - Standards Compliant HTML Filtering + Copyright (C) 2006-2008 Edward Z. Yang + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * Facade that coordinates HTML Purifier's subsystems in order to purify HTML. + * + * @note There are several points in which configuration can be specified + * for HTML Purifier. The precedence of these (from lowest to + * highest) is as follows: + * -# Instance: new HTMLPurifier($config) + * -# Invocation: purify($html, $config) + * These configurations are entirely independent of each other and + * are *not* merged (this behavior may change in the future). + * + * @todo We need an easier way to inject strategies using the configuration + * object. + */ +class HTMLPurifier +{ + + /** Version of HTML Purifier */ + public $version = '4.0.0'; + + /** Constant with version of HTML Purifier */ + const VERSION = '4.0.0'; + + /** Global configuration object */ + public $config; + + /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */ + private $filters = array(); + + /** Single instance of HTML Purifier */ + private static $instance; + + protected $strategy, $generator; + + /** + * Resultant HTMLPurifier_Context of last run purification. Is an array + * of contexts if the last called method was purifyArray(). + */ + public $context; + + /** + * Initializes the purifier. + * @param $config Optional HTMLPurifier_Config object for all instances of + * the purifier, if omitted, a default configuration is + * supplied (which can be overridden on a per-use basis). + * The parameter can also be any type that + * HTMLPurifier_Config::create() supports. + */ + public function __construct($config = null) { + + $this->config = HTMLPurifier_Config::create($config); + + $this->strategy = new HTMLPurifier_Strategy_Core(); + + } + + /** + * Adds a filter to process the output. First come first serve + * @param $filter HTMLPurifier_Filter object + */ + public function addFilter($filter) { + trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING); + $this->filters[] = $filter; + } + + /** + * Filters an HTML snippet/document to be XSS-free and standards-compliant. + * + * @param $html String of HTML to purify + * @param $config HTMLPurifier_Config object for this operation, if omitted, + * defaults to the config object specified during this + * object's construction. The parameter can also be any type + * that HTMLPurifier_Config::create() supports. + * @return Purified HTML + */ + public function purify($html, $config = null) { + + // :TODO: make the config merge in, instead of replace + $config = $config ? HTMLPurifier_Config::create($config) : $this->config; + + // implementation is partially environment dependant, partially + // configuration dependant + $lexer = HTMLPurifier_Lexer::create($config); + + $context = new HTMLPurifier_Context(); + + // setup HTML generator + $this->generator = new HTMLPurifier_Generator($config, $context); + $context->register('Generator', $this->generator); + + // set up global context variables + if ($config->get('Core.CollectErrors')) { + // may get moved out if other facilities use it + $language_factory = HTMLPurifier_LanguageFactory::instance(); + $language = $language_factory->create($config, $context); + $context->register('Locale', $language); + + $error_collector = new HTMLPurifier_ErrorCollector($context); + $context->register('ErrorCollector', $error_collector); + } + + // setup id_accumulator context, necessary due to the fact that + // AttrValidator can be called from many places + $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); + $context->register('IDAccumulator', $id_accumulator); + + $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context); + + // setup filters + $filter_flags = $config->getBatch('Filter'); + $custom_filters = $filter_flags['Custom']; + unset($filter_flags['Custom']); + $filters = array(); + foreach ($filter_flags as $filter => $flag) { + if (!$flag) continue; + if (strpos($filter, '.') !== false) continue; + $class = "HTMLPurifier_Filter_$filter"; + $filters[] = new $class; + } + foreach ($custom_filters as $filter) { + // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat + $filters[] = $filter; + } + $filters = array_merge($filters, $this->filters); + // maybe prepare(), but later + + for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) { + $html = $filters[$i]->preFilter($html, $config, $context); + } + + // purified HTML + $html = + $this->generator->generateFromTokens( + // list of tokens + $this->strategy->execute( + // list of un-purified tokens + $lexer->tokenizeHTML( + // un-purified HTML + $html, $config, $context + ), + $config, $context + ) + ); + + for ($i = $filter_size - 1; $i >= 0; $i--) { + $html = $filters[$i]->postFilter($html, $config, $context); + } + + $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context); + $this->context =& $context; + return $html; + } + + /** + * Filters an array of HTML snippets + * @param $config Optional HTMLPurifier_Config object for this operation. + * See HTMLPurifier::purify() for more details. + * @return Array of purified HTML + */ + public function purifyArray($array_of_html, $config = null) { + $context_array = array(); + foreach ($array_of_html as $key => $html) { + $array_of_html[$key] = $this->purify($html, $config); + $context_array[$key] = $this->context; + } + $this->context = $context_array; + return $array_of_html; + } + + /** + * Singleton for enforcing just one HTML Purifier in your system + * @param $prototype Optional prototype HTMLPurifier instance to + * overload singleton with, or HTMLPurifier_Config + * instance to configure the generated version with. + */ + public static function instance($prototype = null) { + if (!self::$instance || $prototype) { + if ($prototype instanceof HTMLPurifier) { + self::$instance = $prototype; + } elseif ($prototype) { + self::$instance = new HTMLPurifier($prototype); + } else { + self::$instance = new HTMLPurifier(); + } + } + return self::$instance; + } + + /** + * @note Backwards compatibility, see instance() + */ + public static function getInstance($prototype = null) { + return HTMLPurifier::instance($prototype); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier.safe-includes.php b/lib/php/HTMLPurifier.safe-includes.php new file mode 100644 index 0000000..cf2c1d6 --- /dev/null +++ b/lib/php/HTMLPurifier.safe-includes.php @@ -0,0 +1,202 @@ +<?php + +/** + * @file + * This file was auto-generated by generate-includes.php and includes all of + * the core files required by HTML Purifier. This is a convenience stub that + * includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT + * EDIT THIS FILE, changes will be overwritten the next time the script is run. + * + * Changes to include_path are not necessary. + */ + +$__dir = dirname(__FILE__); + +require_once $__dir . '/HTMLPurifier.php'; +require_once $__dir . '/HTMLPurifier/AttrCollections.php'; +require_once $__dir . '/HTMLPurifier/AttrDef.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform.php'; +require_once $__dir . '/HTMLPurifier/AttrTypes.php'; +require_once $__dir . '/HTMLPurifier/AttrValidator.php'; +require_once $__dir . '/HTMLPurifier/Bootstrap.php'; +require_once $__dir . '/HTMLPurifier/Definition.php'; +require_once $__dir . '/HTMLPurifier/CSSDefinition.php'; +require_once $__dir . '/HTMLPurifier/ChildDef.php'; +require_once $__dir . '/HTMLPurifier/Config.php'; +require_once $__dir . '/HTMLPurifier/ConfigSchema.php'; +require_once $__dir . '/HTMLPurifier/ContentSets.php'; +require_once $__dir . '/HTMLPurifier/Context.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCache.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php'; +require_once $__dir . '/HTMLPurifier/Doctype.php'; +require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php'; +require_once $__dir . '/HTMLPurifier/ElementDef.php'; +require_once $__dir . '/HTMLPurifier/Encoder.php'; +require_once $__dir . '/HTMLPurifier/EntityLookup.php'; +require_once $__dir . '/HTMLPurifier/EntityParser.php'; +require_once $__dir . '/HTMLPurifier/ErrorCollector.php'; +require_once $__dir . '/HTMLPurifier/ErrorStruct.php'; +require_once $__dir . '/HTMLPurifier/Exception.php'; +require_once $__dir . '/HTMLPurifier/Filter.php'; +require_once $__dir . '/HTMLPurifier/Generator.php'; +require_once $__dir . '/HTMLPurifier/HTMLDefinition.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule.php'; +require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php'; +require_once $__dir . '/HTMLPurifier/IDAccumulator.php'; +require_once $__dir . '/HTMLPurifier/Injector.php'; +require_once $__dir . '/HTMLPurifier/Language.php'; +require_once $__dir . '/HTMLPurifier/LanguageFactory.php'; +require_once $__dir . '/HTMLPurifier/Length.php'; +require_once $__dir . '/HTMLPurifier/Lexer.php'; +require_once $__dir . '/HTMLPurifier/PercentEncoder.php'; +require_once $__dir . '/HTMLPurifier/PropertyList.php'; +require_once $__dir . '/HTMLPurifier/PropertyListIterator.php'; +require_once $__dir . '/HTMLPurifier/Strategy.php'; +require_once $__dir . '/HTMLPurifier/StringHash.php'; +require_once $__dir . '/HTMLPurifier/StringHashParser.php'; +require_once $__dir . '/HTMLPurifier/TagTransform.php'; +require_once $__dir . '/HTMLPurifier/Token.php'; +require_once $__dir . '/HTMLPurifier/TokenFactory.php'; +require_once $__dir . '/HTMLPurifier/URI.php'; +require_once $__dir . '/HTMLPurifier/URIDefinition.php'; +require_once $__dir . '/HTMLPurifier/URIFilter.php'; +require_once $__dir . '/HTMLPurifier/URIParser.php'; +require_once $__dir . '/HTMLPurifier/URIScheme.php'; +require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php'; +require_once $__dir . '/HTMLPurifier/UnitConverter.php'; +require_once $__dir . '/HTMLPurifier/VarParser.php'; +require_once $__dir . '/HTMLPurifier/VarParserException.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/Text.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/URI.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php'; +require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php'; +require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/Required.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php'; +require_once $__dir . '/HTMLPurifier/ChildDef/Table.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php'; +require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/List.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php'; +require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php'; +require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php'; +require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php'; +require_once $__dir . '/HTMLPurifier/Injector/Linkify.php'; +require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php'; +require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php'; +require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php'; +require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php'; +require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php'; +require_once $__dir . '/HTMLPurifier/Strategy/Composite.php'; +require_once $__dir . '/HTMLPurifier/Strategy/Core.php'; +require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php'; +require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php'; +require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php'; +require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php'; +require_once $__dir . '/HTMLPurifier/TagTransform/Font.php'; +require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php'; +require_once $__dir . '/HTMLPurifier/Token/Comment.php'; +require_once $__dir . '/HTMLPurifier/Token/Tag.php'; +require_once $__dir . '/HTMLPurifier/Token/Empty.php'; +require_once $__dir . '/HTMLPurifier/Token/End.php'; +require_once $__dir . '/HTMLPurifier/Token/Start.php'; +require_once $__dir . '/HTMLPurifier/Token/Text.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/news.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php'; +require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php'; +require_once $__dir . '/HTMLPurifier/VarParser/Native.php'; diff --git a/lib/php/HTMLPurifier/AttrCollections.php b/lib/php/HTMLPurifier/AttrCollections.php new file mode 100644 index 0000000..555b86d --- /dev/null +++ b/lib/php/HTMLPurifier/AttrCollections.php @@ -0,0 +1,128 @@ +<?php + +/** + * Defines common attribute collections that modules reference + */ + +class HTMLPurifier_AttrCollections +{ + + /** + * Associative array of attribute collections, indexed by name + */ + public $info = array(); + + /** + * Performs all expansions on internal data for use by other inclusions + * It also collects all attribute collection extensions from + * modules + * @param $attr_types HTMLPurifier_AttrTypes instance + * @param $modules Hash array of HTMLPurifier_HTMLModule members + */ + public function __construct($attr_types, $modules) { + // load extensions from the modules + foreach ($modules as $module) { + foreach ($module->attr_collections as $coll_i => $coll) { + if (!isset($this->info[$coll_i])) { + $this->info[$coll_i] = array(); + } + foreach ($coll as $attr_i => $attr) { + if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) { + // merge in includes + $this->info[$coll_i][$attr_i] = array_merge( + $this->info[$coll_i][$attr_i], $attr); + continue; + } + $this->info[$coll_i][$attr_i] = $attr; + } + } + } + // perform internal expansions and inclusions + foreach ($this->info as $name => $attr) { + // merge attribute collections that include others + $this->performInclusions($this->info[$name]); + // replace string identifiers with actual attribute objects + $this->expandIdentifiers($this->info[$name], $attr_types); + } + } + + /** + * Takes a reference to an attribute associative array and performs + * all inclusions specified by the zero index. + * @param &$attr Reference to attribute array + */ + public function performInclusions(&$attr) { + if (!isset($attr[0])) return; + $merge = $attr[0]; + $seen = array(); // recursion guard + // loop through all the inclusions + for ($i = 0; isset($merge[$i]); $i++) { + if (isset($seen[$merge[$i]])) continue; + $seen[$merge[$i]] = true; + // foreach attribute of the inclusion, copy it over + if (!isset($this->info[$merge[$i]])) continue; + foreach ($this->info[$merge[$i]] as $key => $value) { + if (isset($attr[$key])) continue; // also catches more inclusions + $attr[$key] = $value; + } + if (isset($this->info[$merge[$i]][0])) { + // recursion + $merge = array_merge($merge, $this->info[$merge[$i]][0]); + } + } + unset($attr[0]); + } + + /** + * Expands all string identifiers in an attribute array by replacing + * them with the appropriate values inside HTMLPurifier_AttrTypes + * @param &$attr Reference to attribute array + * @param $attr_types HTMLPurifier_AttrTypes instance + */ + public function expandIdentifiers(&$attr, $attr_types) { + + // because foreach will process new elements we add, make sure we + // skip duplicates + $processed = array(); + + foreach ($attr as $def_i => $def) { + // skip inclusions + if ($def_i === 0) continue; + + if (isset($processed[$def_i])) continue; + + // determine whether or not attribute is required + if ($required = (strpos($def_i, '*') !== false)) { + // rename the definition + unset($attr[$def_i]); + $def_i = trim($def_i, '*'); + $attr[$def_i] = $def; + } + + $processed[$def_i] = true; + + // if we've already got a literal object, move on + if (is_object($def)) { + // preserve previous required + $attr[$def_i]->required = ($required || $attr[$def_i]->required); + continue; + } + + if ($def === false) { + unset($attr[$def_i]); + continue; + } + + if ($t = $attr_types->get($def)) { + $attr[$def_i] = $t; + $attr[$def_i]->required = $required; + } else { + unset($attr[$def_i]); + } + } + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef.php b/lib/php/HTMLPurifier/AttrDef.php new file mode 100644 index 0000000..d32fa62 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef.php @@ -0,0 +1,87 @@ +<?php + +/** + * Base class for all validating attribute definitions. + * + * This family of classes forms the core for not only HTML attribute validation, + * but also any sort of string that needs to be validated or cleaned (which + * means CSS properties and composite definitions are defined here too). + * Besides defining (through code) what precisely makes the string valid, + * subclasses are also responsible for cleaning the code if possible. + */ + +abstract class HTMLPurifier_AttrDef +{ + + /** + * Tells us whether or not an HTML attribute is minimized. Has no + * meaning in other contexts. + */ + public $minimized = false; + + /** + * Tells us whether or not an HTML attribute is required. Has no + * meaning in other contexts + */ + public $required = false; + + /** + * Validates and cleans passed string according to a definition. + * + * @param $string String to be validated and cleaned. + * @param $config Mandatory HTMLPurifier_Config object. + * @param $context Mandatory HTMLPurifier_AttrContext object. + */ + abstract public function validate($string, $config, $context); + + /** + * Convenience method that parses a string as if it were CDATA. + * + * This method process a string in the manner specified at + * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing + * leading and trailing whitespace, ignoring line feeds, and replacing + * carriage returns and tabs with spaces. While most useful for HTML + * attributes specified as CDATA, it can also be applied to most CSS + * values. + * + * @note This method is not entirely standards compliant, as trim() removes + * more types of whitespace than specified in the spec. In practice, + * this is rarely a problem, as those extra characters usually have + * already been removed by HTMLPurifier_Encoder. + * + * @warning This processing is inconsistent with XML's whitespace handling + * as specified by section 3.3.3 and referenced XHTML 1.0 section + * 4.7. However, note that we are NOT necessarily + * parsing XML, thus, this behavior may still be correct. We + * assume that newlines have been normalized. + */ + public function parseCDATA($string) { + $string = trim($string); + $string = str_replace(array("\n", "\t", "\r"), ' ', $string); + return $string; + } + + /** + * Factory method for creating this class from a string. + * @param $string String construction info + * @return Created AttrDef object corresponding to $string + */ + public function make($string) { + // default implementation, return a flyweight of this object. + // If $string has an effect on the returned object (i.e. you + // need to overload this method), it is best + // to clone or instantiate new copies. (Instantiation is safer.) + return $this; + } + + /** + * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work + * properly. THIS IS A HACK! + */ + protected function mungeRgb($string) { + return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS.php b/lib/php/HTMLPurifier/AttrDef/CSS.php new file mode 100644 index 0000000..953e706 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS.php @@ -0,0 +1,87 @@ +<?php + +/** + * Validates the HTML attribute style, otherwise known as CSS. + * @note We don't implement the whole CSS specification, so it might be + * difficult to reuse this component in the context of validating + * actual stylesheet declarations. + * @note If we were really serious about validating the CSS, we would + * tokenize the styles and then parse the tokens. Obviously, we + * are not doing that. Doing that could seriously harm performance, + * but would make these components a lot more viable for a CSS + * filtering solution. + */ +class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef +{ + + public function validate($css, $config, $context) { + + $css = $this->parseCDATA($css); + + $definition = $config->getCSSDefinition(); + + // we're going to break the spec and explode by semicolons. + // This is because semicolon rarely appears in escaped form + // Doing this is generally flaky but fast + // IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI + // for details + + $declarations = explode(';', $css); + $propvalues = array(); + + /** + * Name of the current CSS property being validated. + */ + $property = false; + $context->register('CurrentCSSProperty', $property); + + foreach ($declarations as $declaration) { + if (!$declaration) continue; + if (!strpos($declaration, ':')) continue; + list($property, $value) = explode(':', $declaration, 2); + $property = trim($property); + $value = trim($value); + $ok = false; + do { + if (isset($definition->info[$property])) { + $ok = true; + break; + } + if (ctype_lower($property)) break; + $property = strtolower($property); + if (isset($definition->info[$property])) { + $ok = true; + break; + } + } while(0); + if (!$ok) continue; + // inefficient call, since the validator will do this again + if (strtolower(trim($value)) !== 'inherit') { + // inherit works for everything (but only on the base property) + $result = $definition->info[$property]->validate( + $value, $config, $context ); + } else { + $result = 'inherit'; + } + if ($result === false) continue; + $propvalues[$property] = $result; + } + + $context->destroy('CurrentCSSProperty'); + + // procedure does not write the new CSS simultaneously, so it's + // slightly inefficient, but it's the only way of getting rid of + // duplicates. Perhaps config to optimize it, but not now. + + $new_declarations = ''; + foreach ($propvalues as $prop => $value) { + $new_declarations .= "$prop:$value;"; + } + + return $new_declarations ? $new_declarations : false; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/lib/php/HTMLPurifier/AttrDef/CSS/AlphaValue.php new file mode 100644 index 0000000..292c040 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/AlphaValue.php @@ -0,0 +1,21 @@ +<?php + +class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number +{ + + public function __construct() { + parent::__construct(false); // opacity is non-negative, but we will clamp it + } + + public function validate($number, $config, $context) { + $result = parent::validate($number, $config, $context); + if ($result === false) return $result; + $float = (float) $result; + if ($float < 0.0) $result = '0'; + if ($float > 1.0) $result = '1'; + return $result; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Background.php b/lib/php/HTMLPurifier/AttrDef/CSS/Background.php new file mode 100644 index 0000000..3a3d20c --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Background.php @@ -0,0 +1,87 @@ +<?php + +/** + * Validates shorthand CSS property background. + * @warning Does not support url tokens that have internal spaces. + */ +class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef +{ + + /** + * Local copy of component validators. + * @note See HTMLPurifier_AttrDef_Font::$info for a similar impl. + */ + protected $info; + + public function __construct($config) { + $def = $config->getCSSDefinition(); + $this->info['background-color'] = $def->info['background-color']; + $this->info['background-image'] = $def->info['background-image']; + $this->info['background-repeat'] = $def->info['background-repeat']; + $this->info['background-attachment'] = $def->info['background-attachment']; + $this->info['background-position'] = $def->info['background-position']; + } + + public function validate($string, $config, $context) { + + // regular pre-processing + $string = $this->parseCDATA($string); + if ($string === '') return false; + + // munge rgb() decl if necessary + $string = $this->mungeRgb($string); + + // assumes URI doesn't have spaces in it + $bits = explode(' ', strtolower($string)); // bits to process + + $caught = array(); + $caught['color'] = false; + $caught['image'] = false; + $caught['repeat'] = false; + $caught['attachment'] = false; + $caught['position'] = false; + + $i = 0; // number of catches + $none = false; + + foreach ($bits as $bit) { + if ($bit === '') continue; + foreach ($caught as $key => $status) { + if ($key != 'position') { + if ($status !== false) continue; + $r = $this->info['background-' . $key]->validate($bit, $config, $context); + } else { + $r = $bit; + } + if ($r === false) continue; + if ($key == 'position') { + if ($caught[$key] === false) $caught[$key] = ''; + $caught[$key] .= $r . ' '; + } else { + $caught[$key] = $r; + } + $i++; + break; + } + } + + if (!$i) return false; + if ($caught['position'] !== false) { + $caught['position'] = $this->info['background-position']-> + validate($caught['position'], $config, $context); + } + + $ret = array(); + foreach ($caught as $value) { + if ($value === false) continue; + $ret[] = $value; + } + + if (empty($ret)) return false; + return implode(' ', $ret); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/lib/php/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php new file mode 100644 index 0000000..35df398 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php @@ -0,0 +1,126 @@ +<?php + +/* W3C says: + [ // adjective and number must be in correct order, even if + // you could switch them without introducing ambiguity. + // some browsers support that syntax + [ + <percentage> | <length> | left | center | right + ] + [ + <percentage> | <length> | top | center | bottom + ]? + ] | + [ // this signifies that the vertical and horizontal adjectives + // can be arbitrarily ordered, however, there can only be two, + // one of each, or none at all + [ + left | center | right + ] || + [ + top | center | bottom + ] + ] + top, left = 0% + center, (none) = 50% + bottom, right = 100% +*/ + +/* QuirksMode says: + keyword + length/percentage must be ordered correctly, as per W3C + + Internet Explorer and Opera, however, support arbitrary ordering. We + should fix it up. + + Minor issue though, not strictly necessary. +*/ + +// control freaks may appreciate the ability to convert these to +// percentages or something, but it's not necessary + +/** + * Validates the value of background-position. + */ +class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef +{ + + protected $length; + protected $percentage; + + public function __construct() { + $this->length = new HTMLPurifier_AttrDef_CSS_Length(); + $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage(); + } + + public function validate($string, $config, $context) { + $string = $this->parseCDATA($string); + $bits = explode(' ', $string); + + $keywords = array(); + $keywords['h'] = false; // left, right + $keywords['v'] = false; // top, bottom + $keywords['c'] = false; // center + $measures = array(); + + $i = 0; + + $lookup = array( + 'top' => 'v', + 'bottom' => 'v', + 'left' => 'h', + 'right' => 'h', + 'center' => 'c' + ); + + foreach ($bits as $bit) { + if ($bit === '') continue; + + // test for keyword + $lbit = ctype_lower($bit) ? $bit : strtolower($bit); + if (isset($lookup[$lbit])) { + $status = $lookup[$lbit]; + $keywords[$status] = $lbit; + $i++; + } + + // test for length + $r = $this->length->validate($bit, $config, $context); + if ($r !== false) { + $measures[] = $r; + $i++; + } + + // test for percentage + $r = $this->percentage->validate($bit, $config, $context); + if ($r !== false) { + $measures[] = $r; + $i++; + } + + } + + if (!$i) return false; // no valid values were caught + + + $ret = array(); + + // first keyword + if ($keywords['h']) $ret[] = $keywords['h']; + elseif (count($measures)) $ret[] = array_shift($measures); + elseif ($keywords['c']) { + $ret[] = $keywords['c']; + $keywords['c'] = false; // prevent re-use: center = center center + } + + if ($keywords['v']) $ret[] = $keywords['v']; + elseif (count($measures)) $ret[] = array_shift($measures); + elseif ($keywords['c']) $ret[] = $keywords['c']; + + if (empty($ret)) return false; + return implode(' ', $ret); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Border.php b/lib/php/HTMLPurifier/AttrDef/CSS/Border.php new file mode 100644 index 0000000..42a1d1b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Border.php @@ -0,0 +1,43 @@ +<?php + +/** + * Validates the border property as defined by CSS. + */ +class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef +{ + + /** + * Local copy of properties this property is shorthand for. + */ + protected $info = array(); + + public function __construct($config) { + $def = $config->getCSSDefinition(); + $this->info['border-width'] = $def->info['border-width']; + $this->info['border-style'] = $def->info['border-style']; + $this->info['border-top-color'] = $def->info['border-top-color']; + } + + public function validate($string, $config, $context) { + $string = $this->parseCDATA($string); + $string = $this->mungeRgb($string); + $bits = explode(' ', $string); + $done = array(); // segments we've finished + $ret = ''; // return value + foreach ($bits as $bit) { + foreach ($this->info as $propname => $validator) { + if (isset($done[$propname])) continue; + $r = $validator->validate($bit, $config, $context); + if ($r !== false) { + $ret .= $r . ' '; + $done[$propname] = true; + break; + } + } + } + return rtrim($ret); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Color.php b/lib/php/HTMLPurifier/AttrDef/CSS/Color.php new file mode 100644 index 0000000..07f95a6 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Color.php @@ -0,0 +1,78 @@ +<?php + +/** + * Validates Color as defined by CSS. + */ +class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef +{ + + public function validate($color, $config, $context) { + + static $colors = null; + if ($colors === null) $colors = $config->get('Core.ColorKeywords'); + + $color = trim($color); + if ($color === '') return false; + + $lower = strtolower($color); + if (isset($colors[$lower])) return $colors[$lower]; + + if (strpos($color, 'rgb(') !== false) { + // rgb literal handling + $length = strlen($color); + if (strpos($color, ')') !== $length - 1) return false; + $triad = substr($color, 4, $length - 4 - 1); + $parts = explode(',', $triad); + if (count($parts) !== 3) return false; + $type = false; // to ensure that they're all the same type + $new_parts = array(); + foreach ($parts as $part) { + $part = trim($part); + if ($part === '') return false; + $length = strlen($part); + if ($part[$length - 1] === '%') { + // handle percents + if (!$type) { + $type = 'percentage'; + } elseif ($type !== 'percentage') { + return false; + } + $num = (float) substr($part, 0, $length - 1); + if ($num < 0) $num = 0; + if ($num > 100) $num = 100; + $new_parts[] = "$num%"; + } else { + // handle integers + if (!$type) { + $type = 'integer'; + } elseif ($type !== 'integer') { + return false; + } + $num = (int) $part; + if ($num < 0) $num = 0; + if ($num > 255) $num = 255; + $new_parts[] = (string) $num; + } + } + $new_triad = implode(',', $new_parts); + $color = "rgb($new_triad)"; + } else { + // hexadecimal handling + if ($color[0] === '#') { + $hex = substr($color, 1); + } else { + $hex = $color; + $color = '#' . $color; + } + $length = strlen($hex); + if ($length !== 3 && $length !== 6) return false; + if (!ctype_xdigit($hex)) return false; + } + + return $color; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Composite.php b/lib/php/HTMLPurifier/AttrDef/CSS/Composite.php new file mode 100644 index 0000000..de1289c --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Composite.php @@ -0,0 +1,38 @@ +<?php + +/** + * Allows multiple validators to attempt to validate attribute. + * + * Composite is just what it sounds like: a composite of many validators. + * This means that multiple HTMLPurifier_AttrDef objects will have a whack + * at the string. If one of them passes, that's what is returned. This is + * especially useful for CSS values, which often are a choice between + * an enumerated set of predefined values or a flexible data type. + */ +class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef +{ + + /** + * List of HTMLPurifier_AttrDef objects that may process strings + * @todo Make protected + */ + public $defs; + + /** + * @param $defs List of HTMLPurifier_AttrDef objects + */ + public function __construct($defs) { + $this->defs = $defs; + } + + public function validate($string, $config, $context) { + foreach ($this->defs as $i => $def) { + $result = $this->defs[$i]->validate($string, $config, $context); + if ($result !== false) return $result; + } + return false; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/lib/php/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php new file mode 100644 index 0000000..6599c5b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php @@ -0,0 +1,28 @@ +<?php + +/** + * Decorator which enables CSS properties to be disabled for specific elements. + */ +class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef +{ + public $def, $element; + + /** + * @param $def Definition to wrap + * @param $element Element to deny + */ + public function __construct($def, $element) { + $this->def = $def; + $this->element = $element; + } + /** + * Checks if CurrentToken is set and equal to $this->element + */ + public function validate($string, $config, $context) { + $token = $context->get('CurrentToken', true); + if ($token && $token->name == $this->element) return false; + return $this->def->validate($string, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Filter.php b/lib/php/HTMLPurifier/AttrDef/CSS/Filter.php new file mode 100644 index 0000000..147894b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Filter.php @@ -0,0 +1,54 @@ +<?php + +/** + * Microsoft's proprietary filter: CSS property + * @note Currently supports the alpha filter. In the future, this will + * probably need an extensible framework + */ +class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef +{ + + protected $intValidator; + + public function __construct() { + $this->intValidator = new HTMLPurifier_AttrDef_Integer(); + } + + public function validate($value, $config, $context) { + $value = $this->parseCDATA($value); + if ($value === 'none') return $value; + // if we looped this we could support multiple filters + $function_length = strcspn($value, '('); + $function = trim(substr($value, 0, $function_length)); + if ($function !== 'alpha' && + $function !== 'Alpha' && + $function !== 'progid:DXImageTransform.Microsoft.Alpha' + ) return false; + $cursor = $function_length + 1; + $parameters_length = strcspn($value, ')', $cursor); + $parameters = substr($value, $cursor, $parameters_length); + $params = explode(',', $parameters); + $ret_params = array(); + $lookup = array(); + foreach ($params as $param) { + list($key, $value) = explode('=', $param); + $key = trim($key); + $value = trim($value); + if (isset($lookup[$key])) continue; + if ($key !== 'opacity') continue; + $value = $this->intValidator->validate($value, $config, $context); + if ($value === false) continue; + $int = (int) $value; + if ($int > 100) $value = '100'; + if ($int < 0) $value = '0'; + $ret_params[] = "$key=$value"; + $lookup[$key] = true; + } + $ret_parameters = implode(',', $ret_params); + $ret_function = "$function($ret_parameters)"; + return $ret_function; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Font.php b/lib/php/HTMLPurifier/AttrDef/CSS/Font.php new file mode 100644 index 0000000..699ee0b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Font.php @@ -0,0 +1,149 @@ +<?php + +/** + * Validates shorthand CSS property font. + */ +class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef +{ + + /** + * Local copy of component validators. + * + * @note If we moved specific CSS property definitions to their own + * classes instead of having them be assembled at run time by + * CSSDefinition, this wouldn't be necessary. We'd instantiate + * our own copies. + */ + protected $info = array(); + + public function __construct($config) { + $def = $config->getCSSDefinition(); + $this->info['font-style'] = $def->info['font-style']; + $this->info['font-variant'] = $def->info['font-variant']; + $this->info['font-weight'] = $def->info['font-weight']; + $this->info['font-size'] = $def->info['font-size']; + $this->info['line-height'] = $def->info['line-height']; + $this->info['font-family'] = $def->info['font-family']; + } + + public function validate($string, $config, $context) { + + static $system_fonts = array( + 'caption' => true, + 'icon' => true, + 'menu' => true, + 'message-box' => true, + 'small-caption' => true, + 'status-bar' => true + ); + + // regular pre-processing + $string = $this->parseCDATA($string); + if ($string === '') return false; + + // check if it's one of the keywords + $lowercase_string = strtolower($string); + if (isset($system_fonts[$lowercase_string])) { + return $lowercase_string; + } + + $bits = explode(' ', $string); // bits to process + $stage = 0; // this indicates what we're looking for + $caught = array(); // which stage 0 properties have we caught? + $stage_1 = array('font-style', 'font-variant', 'font-weight'); + $final = ''; // output + + for ($i = 0, $size = count($bits); $i < $size; $i++) { + if ($bits[$i] === '') continue; + switch ($stage) { + + // attempting to catch font-style, font-variant or font-weight + case 0: + foreach ($stage_1 as $validator_name) { + if (isset($caught[$validator_name])) continue; + $r = $this->info[$validator_name]->validate( + $bits[$i], $config, $context); + if ($r !== false) { + $final .= $r . ' '; + $caught[$validator_name] = true; + break; + } + } + // all three caught, continue on + if (count($caught) >= 3) $stage = 1; + if ($r !== false) break; + + // attempting to catch font-size and perhaps line-height + case 1: + $found_slash = false; + if (strpos($bits[$i], '/') !== false) { + list($font_size, $line_height) = + explode('/', $bits[$i]); + if ($line_height === '') { + // ooh, there's a space after the slash! + $line_height = false; + $found_slash = true; + } + } else { + $font_size = $bits[$i]; + $line_height = false; + } + $r = $this->info['font-size']->validate( + $font_size, $config, $context); + if ($r !== false) { + $final .= $r; + // attempt to catch line-height + if ($line_height === false) { + // we need to scroll forward + for ($j = $i + 1; $j < $size; $j++) { + if ($bits[$j] === '') continue; + if ($bits[$j] === '/') { + if ($found_slash) { + return false; + } else { + $found_slash = true; + continue; + } + } + $line_height = $bits[$j]; + break; + } + } else { + // slash already found + $found_slash = true; + $j = $i; + } + if ($found_slash) { + $i = $j; + $r = $this->info['line-height']->validate( + $line_height, $config, $context); + if ($r !== false) { + $final .= '/' . $r; + } + } + $final .= ' '; + $stage = 2; + break; + } + return false; + + // attempting to catch font-family + case 2: + $font_family = + implode(' ', array_slice($bits, $i, $size - $i)); + $r = $this->info['font-family']->validate( + $font_family, $config, $context); + if ($r !== false) { + $final .= $r . ' '; + // processing completed successfully + return rtrim($final); + } + return false; + } + } + return false; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/FontFamily.php b/lib/php/HTMLPurifier/AttrDef/CSS/FontFamily.php new file mode 100644 index 0000000..705ac89 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/FontFamily.php @@ -0,0 +1,90 @@ +<?php + +/** + * Validates a font family list according to CSS spec + * @todo whitelisting allowed fonts would be nice + */ +class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef +{ + + public function validate($string, $config, $context) { + static $generic_names = array( + 'serif' => true, + 'sans-serif' => true, + 'monospace' => true, + 'fantasy' => true, + 'cursive' => true + ); + + // assume that no font names contain commas in them + $fonts = explode(',', $string); + $final = ''; + foreach($fonts as $font) { + $font = trim($font); + if ($font === '') continue; + // match a generic name + if (isset($generic_names[$font])) { + $final .= $font . ', '; + continue; + } + // match a quoted name + if ($font[0] === '"' || $font[0] === "'") { + $length = strlen($font); + if ($length <= 2) continue; + $quote = $font[0]; + if ($font[$length - 1] !== $quote) continue; + $font = substr($font, 1, $length - 2); + + $new_font = ''; + for ($i = 0, $c = strlen($font); $i < $c; $i++) { + if ($font[$i] === '\\') { + $i++; + if ($i >= $c) { + $new_font .= '\\'; + break; + } + if (ctype_xdigit($font[$i])) { + $code = $font[$i]; + for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { + if (!ctype_xdigit($font[$i])) break; + $code .= $font[$i]; + } + // We have to be extremely careful when adding + // new characters, to make sure we're not breaking + // the encoding. + $char = HTMLPurifier_Encoder::unichr(hexdec($code)); + if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; + $new_font .= $char; + if ($i < $c && trim($font[$i]) !== '') $i--; + continue; + } + if ($font[$i] === "\n") continue; + } + $new_font .= $font[$i]; + } + + $font = $new_font; + } + // $font is a pure representation of the font name + + if (ctype_alnum($font) && $font !== '') { + // very simple font, allow it in unharmed + $final .= $font . ', '; + continue; + } + + // complicated font, requires quoting + + // armor single quotes and new lines + $font = str_replace("\\", "\\\\", $font); + $font = str_replace("'", "\\'", $font); + $final .= "'$font', "; + } + $final = rtrim($final, ', '); + if ($final === '') return false; + return $final; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/lib/php/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php new file mode 100644 index 0000000..4e6b35e --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php @@ -0,0 +1,40 @@ +<?php + +/** + * Decorator which enables !important to be used in CSS values. + */ +class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef +{ + public $def, $allow; + + /** + * @param $def Definition to wrap + * @param $allow Whether or not to allow !important + */ + public function __construct($def, $allow = false) { + $this->def = $def; + $this->allow = $allow; + } + /** + * Intercepts and removes !important if necessary + */ + public function validate($string, $config, $context) { + // test for ! and important tokens + $string = trim($string); + $is_important = false; + // :TODO: optimization: test directly for !important and ! important + if (strlen($string) >= 9 && substr($string, -9) === 'important') { + $temp = rtrim(substr($string, 0, -9)); + // use a temp, because we might want to restore important + if (strlen($temp) >= 1 && substr($temp, -1) === '!') { + $string = rtrim(substr($temp, 0, -1)); + $is_important = true; + } + } + $string = $this->def->validate($string, $config, $context); + if ($this->allow && $is_important) $string .= ' !important'; + return $string; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Length.php b/lib/php/HTMLPurifier/AttrDef/CSS/Length.php new file mode 100644 index 0000000..a07ec58 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Length.php @@ -0,0 +1,47 @@ +<?php + +/** + * Represents a Length as defined by CSS. + */ +class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef +{ + + protected $min, $max; + + /** + * @param HTMLPurifier_Length $max Minimum length, or null for no bound. String is also acceptable. + * @param HTMLPurifier_Length $max Maximum length, or null for no bound. String is also acceptable. + */ + public function __construct($min = null, $max = null) { + $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null; + $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null; + } + + public function validate($string, $config, $context) { + $string = $this->parseCDATA($string); + + // Optimizations + if ($string === '') return false; + if ($string === '0') return '0'; + if (strlen($string) === 1) return false; + + $length = HTMLPurifier_Length::make($string); + if (!$length->isValid()) return false; + + if ($this->min) { + $c = $length->compareTo($this->min); + if ($c === false) return false; + if ($c < 0) return false; + } + if ($this->max) { + $c = $length->compareTo($this->max); + if ($c === false) return false; + if ($c > 0) return false; + } + + return $length->toString(); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/ListStyle.php b/lib/php/HTMLPurifier/AttrDef/CSS/ListStyle.php new file mode 100644 index 0000000..4406868 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/ListStyle.php @@ -0,0 +1,78 @@ +<?php + +/** + * Validates shorthand CSS property list-style. + * @warning Does not support url tokens that have internal spaces. + */ +class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef +{ + + /** + * Local copy of component validators. + * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl. + */ + protected $info; + + public function __construct($config) { + $def = $config->getCSSDefinition(); + $this->info['list-style-type'] = $def->info['list-style-type']; + $this->info['list-style-position'] = $def->info['list-style-position']; + $this->info['list-style-image'] = $def->info['list-style-image']; + } + + public function validate($string, $config, $context) { + + // regular pre-processing + $string = $this->parseCDATA($string); + if ($string === '') return false; + + // assumes URI doesn't have spaces in it + $bits = explode(' ', strtolower($string)); // bits to process + + $caught = array(); + $caught['type'] = false; + $caught['position'] = false; + $caught['image'] = false; + + $i = 0; // number of catches + $none = false; + + foreach ($bits as $bit) { + if ($i >= 3) return; // optimization bit + if ($bit === '') continue; + foreach ($caught as $key => $status) { + if ($status !== false) continue; + $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); + if ($r === false) continue; + if ($r === 'none') { + if ($none) continue; + else $none = true; + if ($key == 'image') continue; + } + $caught[$key] = $r; + $i++; + break; + } + } + + if (!$i) return false; + + $ret = array(); + + // construct type + if ($caught['type']) $ret[] = $caught['type']; + + // construct image + if ($caught['image']) $ret[] = $caught['image']; + + // construct position + if ($caught['position']) $ret[] = $caught['position']; + + if (empty($ret)) return false; + return implode(' ', $ret); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Multiple.php b/lib/php/HTMLPurifier/AttrDef/CSS/Multiple.php new file mode 100644 index 0000000..4d62a40 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Multiple.php @@ -0,0 +1,58 @@ +<?php + +/** + * Framework class for strings that involve multiple values. + * + * Certain CSS properties such as border-width and margin allow multiple + * lengths to be specified. This class can take a vanilla border-width + * definition and multiply it, usually into a max of four. + * + * @note Even though the CSS specification isn't clear about it, inherit + * can only be used alone: it will never manifest as part of a multi + * shorthand declaration. Thus, this class does not allow inherit. + */ +class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef +{ + + /** + * Instance of component definition to defer validation to. + * @todo Make protected + */ + public $single; + + /** + * Max number of values allowed. + * @todo Make protected + */ + public $max; + + /** + * @param $single HTMLPurifier_AttrDef to multiply + * @param $max Max number of values allowed (usually four) + */ + public function __construct($single, $max = 4) { + $this->single = $single; + $this->max = $max; + } + + public function validate($string, $config, $context) { + $string = $this->parseCDATA($string); + if ($string === '') return false; + $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n + $length = count($parts); + $final = ''; + for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) { + if (ctype_space($parts[$i])) continue; + $result = $this->single->validate($parts[$i], $config, $context); + if ($result !== false) { + $final .= $result . ' '; + $num++; + } + } + if ($final === '') return false; + return rtrim($final); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Number.php b/lib/php/HTMLPurifier/AttrDef/CSS/Number.php new file mode 100644 index 0000000..3f99e12 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Number.php @@ -0,0 +1,69 @@ +<?php + +/** + * Validates a number as defined by the CSS spec. + */ +class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef +{ + + /** + * Bool indicating whether or not only positive values allowed. + */ + protected $non_negative = false; + + /** + * @param $non_negative Bool indicating whether negatives are forbidden + */ + public function __construct($non_negative = false) { + $this->non_negative = $non_negative; + } + + /** + * @warning Some contexts do not pass $config, $context. These + * variables should not be used without checking HTMLPurifier_Length + */ + public function validate($number, $config, $context) { + + $number = $this->parseCDATA($number); + + if ($number === '') return false; + if ($number === '0') return '0'; + + $sign = ''; + switch ($number[0]) { + case '-': + if ($this->non_negative) return false; + $sign = '-'; + case '+': + $number = substr($number, 1); + } + + if (ctype_digit($number)) { + $number = ltrim($number, '0'); + return $number ? $sign . $number : '0'; + } + + // Period is the only non-numeric character allowed + if (strpos($number, '.') === false) return false; + + list($left, $right) = explode('.', $number, 2); + + if ($left === '' && $right === '') return false; + if ($left !== '' && !ctype_digit($left)) return false; + + $left = ltrim($left, '0'); + $right = rtrim($right, '0'); + + if ($right === '') { + return $left ? $sign . $left : '0'; + } elseif (!ctype_digit($right)) { + return false; + } + + return $sign . $left . '.' . $right; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/Percentage.php b/lib/php/HTMLPurifier/AttrDef/CSS/Percentage.php new file mode 100644 index 0000000..c34b8fc --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/Percentage.php @@ -0,0 +1,40 @@ +<?php + +/** + * Validates a Percentage as defined by the CSS spec. + */ +class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef +{ + + /** + * Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation + */ + protected $number_def; + + /** + * @param Bool indicating whether to forbid negative values + */ + public function __construct($non_negative = false) { + $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative); + } + + public function validate($string, $config, $context) { + + $string = $this->parseCDATA($string); + + if ($string === '') return false; + $length = strlen($string); + if ($length === 1) return false; + if ($string[$length - 1] !== '%') return false; + + $number = substr($string, 0, $length - 1); + $number = $this->number_def->validate($number, $config, $context); + + if ($number === false) return false; + return "$number%"; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/lib/php/HTMLPurifier/AttrDef/CSS/TextDecoration.php new file mode 100644 index 0000000..772c922 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/TextDecoration.php @@ -0,0 +1,38 @@ +<?php + +/** + * Validates the value for the CSS property text-decoration + * @note This class could be generalized into a version that acts sort of + * like Enum except you can compound the allowed values. + */ +class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef +{ + + public function validate($string, $config, $context) { + + static $allowed_values = array( + 'line-through' => true, + 'overline' => true, + 'underline' => true, + ); + + $string = strtolower($this->parseCDATA($string)); + + if ($string === 'none') return $string; + + $parts = explode(' ', $string); + $final = ''; + foreach ($parts as $part) { + if (isset($allowed_values[$part])) { + $final .= $part . ' '; + } + } + $final = rtrim($final); + if ($final === '') return false; + return $final; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/CSS/URI.php b/lib/php/HTMLPurifier/AttrDef/CSS/URI.php new file mode 100644 index 0000000..435d793 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/CSS/URI.php @@ -0,0 +1,56 @@ +<?php + +/** + * Validates a URI in CSS syntax, which uses url('http://example.com') + * @note While theoretically speaking a URI in a CSS document could + * be non-embedded, as of CSS2 there is no such usage so we're + * generalizing it. This may need to be changed in the future. + * @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as + * the separator, you cannot put a literal semicolon in + * in the URI. Try percent encoding it, in that case. + */ +class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI +{ + + public function __construct() { + parent::__construct(true); // always embedded + } + + public function validate($uri_string, $config, $context) { + // parse the URI out of the string and then pass it onto + // the parent object + + $uri_string = $this->parseCDATA($uri_string); + if (strpos($uri_string, 'url(') !== 0) return false; + $uri_string = substr($uri_string, 4); + $new_length = strlen($uri_string) - 1; + if ($uri_string[$new_length] != ')') return false; + $uri = trim(substr($uri_string, 0, $new_length)); + + if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) { + $quote = $uri[0]; + $new_length = strlen($uri) - 1; + if ($uri[$new_length] !== $quote) return false; + $uri = substr($uri, 1, $new_length - 1); + } + + $keys = array( '(', ')', ',', ' ', '"', "'"); + $values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'"); + $uri = str_replace($values, $keys, $uri); + + $result = parent::validate($uri, $config, $context); + + if ($result === false) return false; + + // escape necessary characters according to CSS spec + // except for the comma, none of these should appear in the + // URI at all + $result = str_replace($keys, $values, $result); + + return "url($result)"; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/Enum.php b/lib/php/HTMLPurifier/AttrDef/Enum.php new file mode 100644 index 0000000..5d603eb --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/Enum.php @@ -0,0 +1,65 @@ +<?php + +// Enum = Enumerated +/** + * Validates a keyword against a list of valid values. + * @warning The case-insensitive compare of this function uses PHP's + * built-in strtolower and ctype_lower functions, which may + * cause problems with international comparisons + */ +class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef +{ + + /** + * Lookup table of valid values. + * @todo Make protected + */ + public $valid_values = array(); + + /** + * Bool indicating whether or not enumeration is case sensitive. + * @note In general this is always case insensitive. + */ + protected $case_sensitive = false; // values according to W3C spec + + /** + * @param $valid_values List of valid values + * @param $case_sensitive Bool indicating whether or not case sensitive + */ + public function __construct( + $valid_values = array(), $case_sensitive = false + ) { + $this->valid_values = array_flip($valid_values); + $this->case_sensitive = $case_sensitive; + } + + public function validate($string, $config, $context) { + $string = trim($string); + if (!$this->case_sensitive) { + // we may want to do full case-insensitive libraries + $string = ctype_lower($string) ? $string : strtolower($string); + } + $result = isset($this->valid_values[$string]); + + return $result ? $string : false; + } + + /** + * @param $string In form of comma-delimited list of case-insensitive + * valid values. Example: "foo,bar,baz". Prepend "s:" to make + * case sensitive + */ + public function make($string) { + if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') { + $string = substr($string, 2); + $sensitive = true; + } else { + $sensitive = false; + } + $values = explode(',', $string); + return new HTMLPurifier_AttrDef_Enum($values, $sensitive); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/Bool.php b/lib/php/HTMLPurifier/AttrDef/HTML/Bool.php new file mode 100644 index 0000000..e06987e --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/Bool.php @@ -0,0 +1,28 @@ +<?php + +/** + * Validates a boolean attribute + */ +class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef +{ + + protected $name; + public $minimized = true; + + public function __construct($name = false) {$this->name = $name;} + + public function validate($string, $config, $context) { + if (empty($string)) return false; + return $this->name; + } + + /** + * @param $string Name of attribute + */ + public function make($string) { + return new HTMLPurifier_AttrDef_HTML_Bool($string); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/Class.php b/lib/php/HTMLPurifier/AttrDef/HTML/Class.php new file mode 100644 index 0000000..370068d --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/Class.php @@ -0,0 +1,34 @@ +<?php + +/** + * Implements special behavior for class attribute (normally NMTOKENS) + */ +class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens +{ + protected function split($string, $config, $context) { + // really, this twiddle should be lazy loaded + $name = $config->getDefinition('HTML')->doctype->name; + if ($name == "XHTML 1.1" || $name == "XHTML 2.0") { + return parent::split($string, $config, $context); + } else { + return preg_split('/\s+/', $string); + } + } + protected function filter($tokens, $config, $context) { + $allowed = $config->get('Attr.AllowedClasses'); + $forbidden = $config->get('Attr.ForbiddenClasses'); + $ret = array(); + foreach ($tokens as $token) { + if ( + ($allowed === null || isset($allowed[$token])) && + !isset($forbidden[$token]) && + // We need this O(n) check because of PHP's array + // implementation that casts -0 to 0. + !in_array($token, $ret, true) + ) { + $ret[] = $token; + } + } + return $ret; + } +} diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/Color.php b/lib/php/HTMLPurifier/AttrDef/HTML/Color.php new file mode 100644 index 0000000..d01e204 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/Color.php @@ -0,0 +1,32 @@ +<?php + +/** + * Validates a color according to the HTML spec. + */ +class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef +{ + + public function validate($string, $config, $context) { + + static $colors = null; + if ($colors === null) $colors = $config->get('Core.ColorKeywords'); + + $string = trim($string); + + if (empty($string)) return false; + if (isset($colors[$string])) return $colors[$string]; + if ($string[0] === '#') $hex = substr($string, 1); + else $hex = $string; + + $length = strlen($hex); + if ($length !== 3 && $length !== 6) return false; + if (!ctype_xdigit($hex)) return false; + if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2]; + + return "#$hex"; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/lib/php/HTMLPurifier/AttrDef/HTML/FrameTarget.php new file mode 100644 index 0000000..ae6ea7c --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/FrameTarget.php @@ -0,0 +1,21 @@ +<?php + +/** + * Special-case enum attribute definition that lazy loads allowed frame targets + */ +class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum +{ + + public $valid_values = false; // uninitialized value + protected $case_sensitive = false; + + public function __construct() {} + + public function validate($string, $config, $context) { + if ($this->valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets'); + return parent::validate($string, $config, $context); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/ID.php b/lib/php/HTMLPurifier/AttrDef/HTML/ID.php new file mode 100644 index 0000000..81d0376 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/ID.php @@ -0,0 +1,70 @@ +<?php + +/** + * Validates the HTML attribute ID. + * @warning Even though this is the id processor, it + * will ignore the directive Attr:IDBlacklist, since it will only + * go according to the ID accumulator. Since the accumulator is + * automatically generated, it will have already absorbed the + * blacklist. If you're hacking around, make sure you use load()! + */ + +class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef +{ + + // ref functionality disabled, since we also have to verify + // whether or not the ID it refers to exists + + public function validate($id, $config, $context) { + + if (!$config->get('Attr.EnableID')) return false; + + $id = trim($id); // trim it first + + if ($id === '') return false; + + $prefix = $config->get('Attr.IDPrefix'); + if ($prefix !== '') { + $prefix .= $config->get('Attr.IDPrefixLocal'); + // prevent re-appending the prefix + if (strpos($id, $prefix) !== 0) $id = $prefix . $id; + } elseif ($config->get('Attr.IDPrefixLocal') !== '') { + trigger_error('%Attr.IDPrefixLocal cannot be used unless '. + '%Attr.IDPrefix is set', E_USER_WARNING); + } + + //if (!$this->ref) { + $id_accumulator =& $context->get('IDAccumulator'); + if (isset($id_accumulator->ids[$id])) return false; + //} + + // we purposely avoid using regex, hopefully this is faster + + if (ctype_alpha($id)) { + $result = true; + } else { + if (!ctype_alpha(@$id[0])) return false; + $trim = trim( // primitive style of regexps, I suppose + $id, + 'A..Za..z0..9:-._' + ); + $result = ($trim === ''); + } + + $regexp = $config->get('Attr.IDBlacklistRegexp'); + if ($regexp && preg_match($regexp, $id)) { + return false; + } + + if (/*!$this->ref && */$result) $id_accumulator->add($id); + + // if no change was made to the ID, return the result + // else, return the new id if stripping whitespace made it + // valid, or return false. + return $result ? $id : false; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/Length.php b/lib/php/HTMLPurifier/AttrDef/HTML/Length.php new file mode 100644 index 0000000..a242f9c --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/Length.php @@ -0,0 +1,41 @@ +<?php + +/** + * Validates the HTML type length (not to be confused with CSS's length). + * + * This accepts integer pixels or percentages as lengths for certain + * HTML attributes. + */ + +class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels +{ + + public function validate($string, $config, $context) { + + $string = trim($string); + if ($string === '') return false; + + $parent_result = parent::validate($string, $config, $context); + if ($parent_result !== false) return $parent_result; + + $length = strlen($string); + $last_char = $string[$length - 1]; + + if ($last_char !== '%') return false; + + $points = substr($string, 0, $length - 1); + + if (!is_numeric($points)) return false; + + $points = (int) $points; + + if ($points < 0) return '0%'; + if ($points > 100) return '100%'; + + return ((string) $points) . '%'; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/lib/php/HTMLPurifier/AttrDef/HTML/LinkTypes.php new file mode 100644 index 0000000..76d25ed --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/LinkTypes.php @@ -0,0 +1,53 @@ +<?php + +/** + * Validates a rel/rev link attribute against a directive of allowed values + * @note We cannot use Enum because link types allow multiple + * values. + * @note Assumes link types are ASCII text + */ +class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef +{ + + /** Name config attribute to pull. */ + protected $name; + + public function __construct($name) { + $configLookup = array( + 'rel' => 'AllowedRel', + 'rev' => 'AllowedRev' + ); + if (!isset($configLookup[$name])) { + trigger_error('Unrecognized attribute name for link '. + 'relationship.', E_USER_ERROR); + return; + } + $this->name = $configLookup[$name]; + } + + public function validate($string, $config, $context) { + + $allowed = $config->get('Attr.' . $this->name); + if (empty($allowed)) return false; + + $string = $this->parseCDATA($string); + $parts = explode(' ', $string); + + // lookup to prevent duplicates + $ret_lookup = array(); + foreach ($parts as $part) { + $part = strtolower(trim($part)); + if (!isset($allowed[$part])) continue; + $ret_lookup[$part] = true; + } + + if (empty($ret_lookup)) return false; + $string = implode(' ', array_keys($ret_lookup)); + + return $string; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/MultiLength.php b/lib/php/HTMLPurifier/AttrDef/HTML/MultiLength.php new file mode 100644 index 0000000..c72fc76 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/MultiLength.php @@ -0,0 +1,41 @@ +<?php + +/** + * Validates a MultiLength as defined by the HTML spec. + * + * A multilength is either a integer (pixel count), a percentage, or + * a relative number. + */ +class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length +{ + + public function validate($string, $config, $context) { + + $string = trim($string); + if ($string === '') return false; + + $parent_result = parent::validate($string, $config, $context); + if ($parent_result !== false) return $parent_result; + + $length = strlen($string); + $last_char = $string[$length - 1]; + + if ($last_char !== '*') return false; + + $int = substr($string, 0, $length - 1); + + if ($int == '') return '*'; + if (!is_numeric($int)) return false; + + $int = (int) $int; + + if ($int < 0) return false; + if ($int == 0) return '0'; + if ($int == 1) return '*'; + return ((string) $int) . '*'; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/lib/php/HTMLPurifier/AttrDef/HTML/Nmtokens.php new file mode 100644 index 0000000..aa34120 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/Nmtokens.php @@ -0,0 +1,52 @@ +<?php + +/** + * Validates contents based on NMTOKENS attribute type. + */ +class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef +{ + + public function validate($string, $config, $context) { + + $string = trim($string); + + // early abort: '' and '0' (strings that convert to false) are invalid + if (!$string) return false; + + $tokens = $this->split($string, $config, $context); + $tokens = $this->filter($tokens, $config, $context); + if (empty($tokens)) return false; + return implode(' ', $tokens); + + } + + /** + * Splits a space separated list of tokens into its constituent parts. + */ + protected function split($string, $config, $context) { + // OPTIMIZABLE! + // do the preg_match, capture all subpatterns for reformulation + + // we don't support U+00A1 and up codepoints or + // escaping because I don't know how to do that with regexps + // and plus it would complicate optimization efforts (you never + // see that anyway). + $pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start + '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'. + '(?:(?=\s)|\z)/'; // look ahead for space or string end + preg_match_all($pattern, $string, $matches); + return $matches[1]; + } + + /** + * Template method for removing certain tokens based on arbitrary criteria. + * @note If we wanted to be really functional, we'd do an array_filter + * with a callback. But... we're not. + */ + protected function filter($tokens, $config, $context) { + return $tokens; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/HTML/Pixels.php b/lib/php/HTMLPurifier/AttrDef/HTML/Pixels.php new file mode 100644 index 0000000..4cb2c1b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/HTML/Pixels.php @@ -0,0 +1,48 @@ +<?php + +/** + * Validates an integer representation of pixels according to the HTML spec. + */ +class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef +{ + + protected $max; + + public function __construct($max = null) { + $this->max = $max; + } + + public function validate($string, $config, $context) { + + $string = trim($string); + if ($string === '0') return $string; + if ($string === '') return false; + $length = strlen($string); + if (substr($string, $length - 2) == 'px') { + $string = substr($string, 0, $length - 2); + } + if (!is_numeric($string)) return false; + $int = (int) $string; + + if ($int < 0) return '0'; + + // upper-bound value, extremely high values can + // crash operating systems, see <http://ha.ckers.org/imagecrash.html> + // WARNING, above link WILL crash you if you're using Windows + + if ($this->max !== null && $int > $this->max) return (string) $this->max; + + return (string) $int; + + } + + public function make($string) { + if ($string === '') $max = null; + else $max = (int) $string; + $class = get_class($this); + return new $class($max); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/Integer.php b/lib/php/HTMLPurifier/AttrDef/Integer.php new file mode 100644 index 0000000..d59738d --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/Integer.php @@ -0,0 +1,73 @@ +<?php + +/** + * Validates an integer. + * @note While this class was modeled off the CSS definition, no currently + * allowed CSS uses this type. The properties that do are: widows, + * orphans, z-index, counter-increment, counter-reset. Some of the + * HTML attributes, however, find use for a non-negative version of this. + */ +class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef +{ + + /** + * Bool indicating whether or not negative values are allowed + */ + protected $negative = true; + + /** + * Bool indicating whether or not zero is allowed + */ + protected $zero = true; + + /** + * Bool indicating whether or not positive values are allowed + */ + protected $positive = true; + + /** + * @param $negative Bool indicating whether or not negative values are allowed + * @param $zero Bool indicating whether or not zero is allowed + * @param $positive Bool indicating whether or not positive values are allowed + */ + public function __construct( + $negative = true, $zero = true, $positive = true + ) { + $this->negative = $negative; + $this->zero = $zero; + $this->positive = $positive; + } + + public function validate($integer, $config, $context) { + + $integer = $this->parseCDATA($integer); + if ($integer === '') return false; + + // we could possibly simply typecast it to integer, but there are + // certain fringe cases that must not return an integer. + + // clip leading sign + if ( $this->negative && $integer[0] === '-' ) { + $digits = substr($integer, 1); + if ($digits === '0') $integer = '0'; // rm minus sign for zero + } elseif( $this->positive && $integer[0] === '+' ) { + $digits = $integer = substr($integer, 1); // rm unnecessary plus + } else { + $digits = $integer; + } + + // test if it's numeric + if (!ctype_digit($digits)) return false; + + // perform scope tests + if (!$this->zero && $integer == 0) return false; + if (!$this->positive && $integer > 0) return false; + if (!$this->negative && $integer < 0) return false; + + return $integer; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/Lang.php b/lib/php/HTMLPurifier/AttrDef/Lang.php new file mode 100644 index 0000000..10e6da5 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/Lang.php @@ -0,0 +1,73 @@ +<?php + +/** + * Validates the HTML attribute lang, effectively a language code. + * @note Built according to RFC 3066, which obsoleted RFC 1766 + */ +class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef +{ + + public function validate($string, $config, $context) { + + $string = trim($string); + if (!$string) return false; + + $subtags = explode('-', $string); + $num_subtags = count($subtags); + + if ($num_subtags == 0) return false; // sanity check + + // process primary subtag : $subtags[0] + $length = strlen($subtags[0]); + switch ($length) { + case 0: + return false; + case 1: + if (! ($subtags[0] == 'x' || $subtags[0] == 'i') ) { + return false; + } + break; + case 2: + case 3: + if (! ctype_alpha($subtags[0]) ) { + return false; + } elseif (! ctype_lower($subtags[0]) ) { + $subtags[0] = strtolower($subtags[0]); + } + break; + default: + return false; + } + + $new_string = $subtags[0]; + if ($num_subtags == 1) return $new_string; + + // process second subtag : $subtags[1] + $length = strlen($subtags[1]); + if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) { + return $new_string; + } + if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]); + + $new_string .= '-' . $subtags[1]; + if ($num_subtags == 2) return $new_string; + + // process all other subtags, index 2 and up + for ($i = 2; $i < $num_subtags; $i++) { + $length = strlen($subtags[$i]); + if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) { + return $new_string; + } + if (!ctype_lower($subtags[$i])) { + $subtags[$i] = strtolower($subtags[$i]); + } + $new_string .= '-' . $subtags[$i]; + } + + return $new_string; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/Switch.php b/lib/php/HTMLPurifier/AttrDef/Switch.php new file mode 100644 index 0000000..c9e3ed1 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/Switch.php @@ -0,0 +1,34 @@ +<?php + +/** + * Decorator that, depending on a token, switches between two definitions. + */ +class HTMLPurifier_AttrDef_Switch +{ + + protected $tag; + protected $withTag, $withoutTag; + + /** + * @param string $tag Tag name to switch upon + * @param HTMLPurifier_AttrDef $with_tag Call if token matches tag + * @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token + */ + public function __construct($tag, $with_tag, $without_tag) { + $this->tag = $tag; + $this->withTag = $with_tag; + $this->withoutTag = $without_tag; + } + + public function validate($string, $config, $context) { + $token = $context->get('CurrentToken', true); + if (!$token || $token->name !== $this->tag) { + return $this->withoutTag->validate($string, $config, $context); + } else { + return $this->withTag->validate($string, $config, $context); + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/Text.php b/lib/php/HTMLPurifier/AttrDef/Text.php new file mode 100644 index 0000000..c6216cc --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/Text.php @@ -0,0 +1,15 @@ +<?php + +/** + * Validates arbitrary text according to the HTML spec. + */ +class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef +{ + + public function validate($string, $config, $context) { + return $this->parseCDATA($string); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/URI.php b/lib/php/HTMLPurifier/AttrDef/URI.php new file mode 100644 index 0000000..01a6d83 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/URI.php @@ -0,0 +1,77 @@ +<?php + +/** + * Validates a URI as defined by RFC 3986. + * @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme + */ +class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef +{ + + protected $parser; + protected $embedsResource; + + /** + * @param $embeds_resource_resource Does the URI here result in an extra HTTP request? + */ + public function __construct($embeds_resource = false) { + $this->parser = new HTMLPurifier_URIParser(); + $this->embedsResource = (bool) $embeds_resource; + } + + public function make($string) { + $embeds = (bool) $string; + return new HTMLPurifier_AttrDef_URI($embeds); + } + + public function validate($uri, $config, $context) { + + if ($config->get('URI.Disable')) return false; + + $uri = $this->parseCDATA($uri); + + // parse the URI + $uri = $this->parser->parse($uri); + if ($uri === false) return false; + + // add embedded flag to context for validators + $context->register('EmbeddedURI', $this->embedsResource); + + $ok = false; + do { + + // generic validation + $result = $uri->validate($config, $context); + if (!$result) break; + + // chained filtering + $uri_def = $config->getDefinition('URI'); + $result = $uri_def->filter($uri, $config, $context); + if (!$result) break; + + // scheme-specific validation + $scheme_obj = $uri->getSchemeObj($config, $context); + if (!$scheme_obj) break; + if ($this->embedsResource && !$scheme_obj->browsable) break; + $result = $scheme_obj->validate($uri, $config, $context); + if (!$result) break; + + // Post chained filtering + $result = $uri_def->postFilter($uri, $config, $context); + if (!$result) break; + + // survived gauntlet + $ok = true; + + } while (false); + + $context->destroy('EmbeddedURI'); + if (!$ok) return false; + + // back to string + return $uri->toString(); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/URI/Email.php b/lib/php/HTMLPurifier/AttrDef/URI/Email.php new file mode 100644 index 0000000..bfee9d1 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/URI/Email.php @@ -0,0 +1,17 @@ +<?php + +abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef +{ + + /** + * Unpacks a mailbox into its display-name and address + */ + function unpack($string) { + // needs to be implemented + } + +} + +// sub-implementations + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/lib/php/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php new file mode 100644 index 0000000..94c715a --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php @@ -0,0 +1,21 @@ +<?php + +/** + * Primitive email validation class based on the regexp found at + * http://www.regular-expressions.info/email.html + */ +class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email +{ + + public function validate($string, $config, $context) { + // no support for named mailboxes i.e. "Bob <bob@example.com>" + // that needs more percent encoding to be done + if ($string == '') return false; + $string = trim($string); + $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string); + return $result ? $string : false; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/URI/Host.php b/lib/php/HTMLPurifier/AttrDef/URI/Host.php new file mode 100644 index 0000000..2156c10 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/URI/Host.php @@ -0,0 +1,62 @@ +<?php + +/** + * Validates a host according to the IPv4, IPv6 and DNS (future) specifications. + */ +class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef +{ + + /** + * Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator + */ + protected $ipv4; + + /** + * Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator + */ + protected $ipv6; + + public function __construct() { + $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4(); + $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6(); + } + + public function validate($string, $config, $context) { + $length = strlen($string); + if ($string === '') return ''; + if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') { + //IPv6 + $ip = substr($string, 1, $length - 2); + $valid = $this->ipv6->validate($ip, $config, $context); + if ($valid === false) return false; + return '['. $valid . ']'; + } + + // need to do checks on unusual encodings too + $ipv4 = $this->ipv4->validate($string, $config, $context); + if ($ipv4 !== false) return $ipv4; + + // A regular domain name. + + // This breaks I18N domain names, but we don't have proper IRI support, + // so force users to insert Punycode. If there's complaining we'll + // try to fix things into an international friendly form. + + // The productions describing this are: + $a = '[a-z]'; // alpha + $an = '[a-z0-9]'; // alphanum + $and = '[a-z0-9-]'; // alphanum | "-" + // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + $domainlabel = "$an($and*$an)?"; + // toplabel = alpha | alpha *( alphanum | "-" ) alphanum + $toplabel = "$a($and*$an)?"; + // hostname = *( domainlabel "." ) toplabel [ "." ] + $match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string); + if (!$match) return false; + + return $string; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/URI/IPv4.php b/lib/php/HTMLPurifier/AttrDef/URI/IPv4.php new file mode 100644 index 0000000..ec4cf59 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/URI/IPv4.php @@ -0,0 +1,39 @@ +<?php + +/** + * Validates an IPv4 address + * @author Feyd @ forums.devnetwork.net (public domain) + */ +class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef +{ + + /** + * IPv4 regex, protected so that IPv6 can reuse it + */ + protected $ip4; + + public function validate($aIP, $config, $context) { + + if (!$this->ip4) $this->_loadRegex(); + + if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) + { + return $aIP; + } + + return false; + + } + + /** + * Lazy load function to prevent regex from being stuffed in + * cache. + */ + protected function _loadRegex() { + $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255 + $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})"; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrDef/URI/IPv6.php b/lib/php/HTMLPurifier/AttrDef/URI/IPv6.php new file mode 100644 index 0000000..9454e9b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrDef/URI/IPv6.php @@ -0,0 +1,99 @@ +<?php + +/** + * Validates an IPv6 address. + * @author Feyd @ forums.devnetwork.net (public domain) + * @note This function requires brackets to have been removed from address + * in URI. + */ +class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4 +{ + + public function validate($aIP, $config, $context) { + + if (!$this->ip4) $this->_loadRegex(); + + $original = $aIP; + + $hex = '[0-9a-fA-F]'; + $blk = '(?:' . $hex . '{1,4})'; + $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128 + + // prefix check + if (strpos($aIP, '/') !== false) + { + if (preg_match('#' . $pre . '$#s', $aIP, $find)) + { + $aIP = substr($aIP, 0, 0-strlen($find[0])); + unset($find); + } + else + { + return false; + } + } + + // IPv4-compatiblity check + if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find)) + { + $aIP = substr($aIP, 0, 0-strlen($find[0])); + $ip = explode('.', $find[0]); + $ip = array_map('dechex', $ip); + $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3]; + unset($find, $ip); + } + + // compression check + $aIP = explode('::', $aIP); + $c = count($aIP); + if ($c > 2) + { + return false; + } + elseif ($c == 2) + { + list($first, $second) = $aIP; + $first = explode(':', $first); + $second = explode(':', $second); + + if (count($first) + count($second) > 8) + { + return false; + } + + while(count($first) < 8) + { + array_push($first, '0'); + } + + array_splice($first, 8 - count($second), 8, $second); + $aIP = $first; + unset($first,$second); + } + else + { + $aIP = explode(':', $aIP[0]); + } + $c = count($aIP); + + if ($c != 8) + { + return false; + } + + // All the pieces should be 16-bit hex strings. Are they? + foreach ($aIP as $piece) + { + if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) + { + return false; + } + } + + return $original; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform.php b/lib/php/HTMLPurifier/AttrTransform.php new file mode 100644 index 0000000..e61d3e0 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform.php @@ -0,0 +1,56 @@ +<?php + +/** + * Processes an entire attribute array for corrections needing multiple values. + * + * Occasionally, a certain attribute will need to be removed and popped onto + * another value. Instead of creating a complex return syntax for + * HTMLPurifier_AttrDef, we just pass the whole attribute array to a + * specialized object and have that do the special work. That is the + * family of HTMLPurifier_AttrTransform. + * + * An attribute transformation can be assigned to run before or after + * HTMLPurifier_AttrDef validation. See HTMLPurifier_HTMLDefinition for + * more details. + */ + +abstract class HTMLPurifier_AttrTransform +{ + + /** + * Abstract: makes changes to the attributes dependent on multiple values. + * + * @param $attr Assoc array of attributes, usually from + * HTMLPurifier_Token_Tag::$attr + * @param $config Mandatory HTMLPurifier_Config object. + * @param $context Mandatory HTMLPurifier_Context object + * @returns Processed attribute array. + */ + abstract public function transform($attr, $config, $context); + + /** + * Prepends CSS properties to the style attribute, creating the + * attribute if it doesn't exist. + * @param $attr Attribute array to process (passed by reference) + * @param $css CSS to prepend + */ + public function prependCSS(&$attr, $css) { + $attr['style'] = isset($attr['style']) ? $attr['style'] : ''; + $attr['style'] = $css . $attr['style']; + } + + /** + * Retrieves and removes an attribute + * @param $attr Attribute array to process (passed by reference) + * @param $key Key of attribute to confiscate + */ + public function confiscateAttr(&$attr, $key) { + if (!isset($attr[$key])) return null; + $value = $attr[$key]; + unset($attr[$key]); + return $value; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Background.php b/lib/php/HTMLPurifier/AttrTransform/Background.php new file mode 100644 index 0000000..0e1ff24 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Background.php @@ -0,0 +1,23 @@ +<?php + +/** + * Pre-transform that changes proprietary background attribute to CSS. + */ +class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform { + + public function transform($attr, $config, $context) { + + if (!isset($attr['background'])) return $attr; + + $background = $this->confiscateAttr($attr, 'background'); + // some validation should happen here + + $this->prependCSS($attr, "background-image:url($background);"); + + return $attr; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/BdoDir.php b/lib/php/HTMLPurifier/AttrTransform/BdoDir.php new file mode 100644 index 0000000..4d1a056 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/BdoDir.php @@ -0,0 +1,19 @@ +<?php + +// this MUST be placed in post, as it assumes that any value in dir is valid + +/** + * Post-trasnform that ensures that bdo tags have the dir attribute set. + */ +class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform +{ + + public function transform($attr, $config, $context) { + if (isset($attr['dir'])) return $attr; + $attr['dir'] = $config->get('Attr.DefaultTextDir'); + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/BgColor.php b/lib/php/HTMLPurifier/AttrTransform/BgColor.php new file mode 100644 index 0000000..ad3916b --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/BgColor.php @@ -0,0 +1,23 @@ +<?php + +/** + * Pre-transform that changes deprecated bgcolor attribute to CSS. + */ +class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform { + + public function transform($attr, $config, $context) { + + if (!isset($attr['bgcolor'])) return $attr; + + $bgcolor = $this->confiscateAttr($attr, 'bgcolor'); + // some validation should happen here + + $this->prependCSS($attr, "background-color:$bgcolor;"); + + return $attr; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/BoolToCSS.php b/lib/php/HTMLPurifier/AttrTransform/BoolToCSS.php new file mode 100644 index 0000000..51159b6 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/BoolToCSS.php @@ -0,0 +1,36 @@ +<?php + +/** + * Pre-transform that changes converts a boolean attribute to fixed CSS + */ +class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform { + + /** + * Name of boolean attribute that is trigger + */ + protected $attr; + + /** + * CSS declarations to add to style, needs trailing semicolon + */ + protected $css; + + /** + * @param $attr string attribute name to convert from + * @param $css string CSS declarations to add to style (needs semicolon) + */ + public function __construct($attr, $css) { + $this->attr = $attr; + $this->css = $css; + } + + public function transform($attr, $config, $context) { + if (!isset($attr[$this->attr])) return $attr; + unset($attr[$this->attr]); + $this->prependCSS($attr, $this->css); + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Border.php b/lib/php/HTMLPurifier/AttrTransform/Border.php new file mode 100644 index 0000000..476b0b0 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Border.php @@ -0,0 +1,18 @@ +<?php + +/** + * Pre-transform that changes deprecated border attribute to CSS. + */ +class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform { + + public function transform($attr, $config, $context) { + if (!isset($attr['border'])) return $attr; + $border_width = $this->confiscateAttr($attr, 'border'); + // some validation should happen here + $this->prependCSS($attr, "border:{$border_width}px solid;"); + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/EnumToCSS.php b/lib/php/HTMLPurifier/AttrTransform/EnumToCSS.php new file mode 100644 index 0000000..2a5b451 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/EnumToCSS.php @@ -0,0 +1,58 @@ +<?php + +/** + * Generic pre-transform that converts an attribute with a fixed number of + * values (enumerated) to CSS. + */ +class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform { + + /** + * Name of attribute to transform from + */ + protected $attr; + + /** + * Lookup array of attribute values to CSS + */ + protected $enumToCSS = array(); + + /** + * Case sensitivity of the matching + * @warning Currently can only be guaranteed to work with ASCII + * values. + */ + protected $caseSensitive = false; + + /** + * @param $attr String attribute name to transform from + * @param $enumToCSS Lookup array of attribute values to CSS + * @param $case_sensitive Boolean case sensitivity indicator, default false + */ + public function __construct($attr, $enum_to_css, $case_sensitive = false) { + $this->attr = $attr; + $this->enumToCSS = $enum_to_css; + $this->caseSensitive = (bool) $case_sensitive; + } + + public function transform($attr, $config, $context) { + + if (!isset($attr[$this->attr])) return $attr; + + $value = trim($attr[$this->attr]); + unset($attr[$this->attr]); + + if (!$this->caseSensitive) $value = strtolower($value); + + if (!isset($this->enumToCSS[$value])) { + return $attr; + } + + $this->prependCSS($attr, $this->enumToCSS[$value]); + + return $attr; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/ImgRequired.php b/lib/php/HTMLPurifier/AttrTransform/ImgRequired.php new file mode 100644 index 0000000..a219479 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/ImgRequired.php @@ -0,0 +1,42 @@ +<?php + +// must be called POST validation + +/** + * Transform that supplies default values for the src and alt attributes + * in img tags, as well as prevents the img tag from being removed + * because of a missing alt tag. This needs to be registered as both + * a pre and post attribute transform. + */ +class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform +{ + + public function transform($attr, $config, $context) { + + $src = true; + if (!isset($attr['src'])) { + if ($config->get('Core.RemoveInvalidImg')) return $attr; + $attr['src'] = $config->get('Attr.DefaultInvalidImage'); + $src = false; + } + + if (!isset($attr['alt'])) { + if ($src) { + $alt = $config->get('Attr.DefaultImageAlt'); + if ($alt === null) { + $attr['alt'] = basename($attr['src']); + } else { + $attr['alt'] = $alt; + } + } else { + $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt'); + } + } + + return $attr; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/ImgSpace.php b/lib/php/HTMLPurifier/AttrTransform/ImgSpace.php new file mode 100644 index 0000000..fd84c10 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/ImgSpace.php @@ -0,0 +1,44 @@ +<?php + +/** + * Pre-transform that changes deprecated hspace and vspace attributes to CSS + */ +class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform { + + protected $attr; + protected $css = array( + 'hspace' => array('left', 'right'), + 'vspace' => array('top', 'bottom') + ); + + public function __construct($attr) { + $this->attr = $attr; + if (!isset($this->css[$attr])) { + trigger_error(htmlspecialchars($attr) . ' is not valid space attribute'); + } + } + + public function transform($attr, $config, $context) { + + if (!isset($attr[$this->attr])) return $attr; + + $width = $this->confiscateAttr($attr, $this->attr); + // some validation could happen here + + if (!isset($this->css[$this->attr])) return $attr; + + $style = ''; + foreach ($this->css[$this->attr] as $suffix) { + $property = "margin-$suffix"; + $style .= "$property:{$width}px;"; + } + + $this->prependCSS($attr, $style); + + return $attr; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Input.php b/lib/php/HTMLPurifier/AttrTransform/Input.php new file mode 100644 index 0000000..1682955 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Input.php @@ -0,0 +1,40 @@ +<?php + +/** + * Performs miscellaneous cross attribute validation and filtering for + * input elements. This is meant to be a post-transform. + */ +class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform { + + protected $pixels; + + public function __construct() { + $this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels(); + } + + public function transform($attr, $config, $context) { + if (!isset($attr['type'])) $t = 'text'; + else $t = strtolower($attr['type']); + if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') { + unset($attr['checked']); + } + if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') { + unset($attr['maxlength']); + } + if (isset($attr['size']) && $t !== 'text' && $t !== 'password') { + $result = $this->pixels->validate($attr['size'], $config, $context); + if ($result === false) unset($attr['size']); + else $attr['size'] = $result; + } + if (isset($attr['src']) && $t !== 'image') { + unset($attr['src']); + } + if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) { + $attr['value'] = ''; + } + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Lang.php b/lib/php/HTMLPurifier/AttrTransform/Lang.php new file mode 100644 index 0000000..5869e7f --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Lang.php @@ -0,0 +1,28 @@ +<?php + +/** + * Post-transform that copies lang's value to xml:lang (and vice-versa) + * @note Theoretically speaking, this could be a pre-transform, but putting + * post is more efficient. + */ +class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform +{ + + public function transform($attr, $config, $context) { + + $lang = isset($attr['lang']) ? $attr['lang'] : false; + $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false; + + if ($lang !== false && $xml_lang === false) { + $attr['xml:lang'] = $lang; + } elseif ($xml_lang !== false) { + $attr['lang'] = $xml_lang; + } + + return $attr; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Length.php b/lib/php/HTMLPurifier/AttrTransform/Length.php new file mode 100644 index 0000000..ea2f304 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Length.php @@ -0,0 +1,27 @@ +<?php + +/** + * Class for handling width/height length attribute transformations to CSS + */ +class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform +{ + + protected $name; + protected $cssName; + + public function __construct($name, $css_name = null) { + $this->name = $name; + $this->cssName = $css_name ? $css_name : $name; + } + + public function transform($attr, $config, $context) { + if (!isset($attr[$this->name])) return $attr; + $length = $this->confiscateAttr($attr, $this->name); + if(ctype_digit($length)) $length .= 'px'; + $this->prependCSS($attr, $this->cssName . ":$length;"); + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Name.php b/lib/php/HTMLPurifier/AttrTransform/Name.php new file mode 100644 index 0000000..15315bc --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Name.php @@ -0,0 +1,21 @@ +<?php + +/** + * Pre-transform that changes deprecated name attribute to ID if necessary + */ +class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform +{ + + public function transform($attr, $config, $context) { + // Abort early if we're using relaxed definition of name + if ($config->get('HTML.Attr.Name.UseCDATA')) return $attr; + if (!isset($attr['name'])) return $attr; + $id = $this->confiscateAttr($attr, 'name'); + if ( isset($attr['id'])) return $attr; + $attr['id'] = $id; + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/NameSync.php b/lib/php/HTMLPurifier/AttrTransform/NameSync.php new file mode 100644 index 0000000..a95638c --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/NameSync.php @@ -0,0 +1,27 @@ +<?php + +/** + * Post-transform that performs validation to the name attribute; if + * it is present with an equivalent id attribute, it is passed through; + * otherwise validation is performed. + */ +class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform +{ + + public function __construct() { + $this->idDef = new HTMLPurifier_AttrDef_HTML_ID(); + } + + public function transform($attr, $config, $context) { + if (!isset($attr['name'])) return $attr; + $name = $attr['name']; + if (isset($attr['id']) && $attr['id'] === $name) return $attr; + $result = $this->idDef->validate($name, $config, $context); + if ($result === false) unset($attr['name']); + else $attr['name'] = $result; + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/SafeEmbed.php b/lib/php/HTMLPurifier/AttrTransform/SafeEmbed.php new file mode 100644 index 0000000..4da4499 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/SafeEmbed.php @@ -0,0 +1,15 @@ +<?php + +class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform +{ + public $name = "SafeEmbed"; + + public function transform($attr, $config, $context) { + $attr['allowscriptaccess'] = 'never'; + $attr['allownetworking'] = 'internal'; + $attr['type'] = 'application/x-shockwave-flash'; + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/SafeObject.php b/lib/php/HTMLPurifier/AttrTransform/SafeObject.php new file mode 100644 index 0000000..1ed7489 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/SafeObject.php @@ -0,0 +1,16 @@ +<?php + +/** + * Writes default type for all objects. Currently only supports flash. + */ +class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform +{ + public $name = "SafeObject"; + + function transform($attr, $config, $context) { + if (!isset($attr['type'])) $attr['type'] = 'application/x-shockwave-flash'; + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/SafeParam.php b/lib/php/HTMLPurifier/AttrTransform/SafeParam.php new file mode 100644 index 0000000..94e8052 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/SafeParam.php @@ -0,0 +1,50 @@ +<?php + +/** + * Validates name/value pairs in param tags to be used in safe objects. This + * will only allow name values it recognizes, and pre-fill certain attributes + * with required values. + * + * @note + * This class only supports Flash. In the future, Quicktime support + * may be added. + * + * @warning + * This class expects an injector to add the necessary parameters tags. + */ +class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform +{ + public $name = "SafeParam"; + private $uri; + + public function __construct() { + $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded + } + + public function transform($attr, $config, $context) { + // If we add support for other objects, we'll need to alter the + // transforms. + switch ($attr['name']) { + // application/x-shockwave-flash + // Keep this synchronized with Injector/SafeObject.php + case 'allowScriptAccess': + $attr['value'] = 'never'; + break; + case 'allowNetworking': + $attr['value'] = 'internal'; + break; + case 'wmode': + $attr['value'] = 'window'; + break; + case 'movie': + $attr['value'] = $this->uri->validate($attr['value'], $config, $context); + break; + // add other cases to support other param name/value pairs + default: + $attr['name'] = $attr['value'] = null; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/ScriptRequired.php b/lib/php/HTMLPurifier/AttrTransform/ScriptRequired.php new file mode 100644 index 0000000..4499050 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/ScriptRequired.php @@ -0,0 +1,16 @@ +<?php + +/** + * Implements required attribute stipulation for <script> + */ +class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform +{ + public function transform($attr, $config, $context) { + if (!isset($attr['type'])) { + $attr['type'] = 'text/javascript'; + } + return $attr; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTransform/Textarea.php b/lib/php/HTMLPurifier/AttrTransform/Textarea.php new file mode 100644 index 0000000..81ac348 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTransform/Textarea.php @@ -0,0 +1,18 @@ +<?php + +/** + * Sets height/width defaults for <textarea> + */ +class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform +{ + + public function transform($attr, $config, $context) { + // Calculated from Firefox + if (!isset($attr['cols'])) $attr['cols'] = '22'; + if (!isset($attr['rows'])) $attr['rows'] = '3'; + return $attr; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrTypes.php b/lib/php/HTMLPurifier/AttrTypes.php new file mode 100644 index 0000000..fc2ea4e --- /dev/null +++ b/lib/php/HTMLPurifier/AttrTypes.php @@ -0,0 +1,77 @@ +<?php + +/** + * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects + */ +class HTMLPurifier_AttrTypes +{ + /** + * Lookup array of attribute string identifiers to concrete implementations + */ + protected $info = array(); + + /** + * Constructs the info array, supplying default implementations for attribute + * types. + */ + public function __construct() { + // pseudo-types, must be instantiated via shorthand + $this->info['Enum'] = new HTMLPurifier_AttrDef_Enum(); + $this->info['Bool'] = new HTMLPurifier_AttrDef_HTML_Bool(); + + $this->info['CDATA'] = new HTMLPurifier_AttrDef_Text(); + $this->info['ID'] = new HTMLPurifier_AttrDef_HTML_ID(); + $this->info['Length'] = new HTMLPurifier_AttrDef_HTML_Length(); + $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength(); + $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens(); + $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels(); + $this->info['Text'] = new HTMLPurifier_AttrDef_Text(); + $this->info['URI'] = new HTMLPurifier_AttrDef_URI(); + $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang(); + $this->info['Color'] = new HTMLPurifier_AttrDef_HTML_Color(); + + // unimplemented aliases + $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text(); + $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text(); + $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text(); + $this->info['Character'] = new HTMLPurifier_AttrDef_Text(); + + // "proprietary" types + $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class(); + + // number is really a positive integer (one or more digits) + // FIXME: ^^ not always, see start and value of list items + $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); + } + + /** + * Retrieves a type + * @param $type String type name + * @return Object AttrDef for type + */ + public function get($type) { + + // determine if there is any extra info tacked on + if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2); + else $string = ''; + + if (!isset($this->info[$type])) { + trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR); + return; + } + + return $this->info[$type]->make($string); + + } + + /** + * Sets a new implementation for a type + * @param $type String type name + * @param $impl Object AttrDef for type + */ + public function set($type, $impl) { + $this->info[$type] = $impl; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/AttrValidator.php b/lib/php/HTMLPurifier/AttrValidator.php new file mode 100644 index 0000000..829a0f8 --- /dev/null +++ b/lib/php/HTMLPurifier/AttrValidator.php @@ -0,0 +1,162 @@ +<?php + +/** + * Validates the attributes of a token. Doesn't manage required attributes + * very well. The only reason we factored this out was because RemoveForeignElements + * also needed it besides ValidateAttributes. + */ +class HTMLPurifier_AttrValidator +{ + + /** + * Validates the attributes of a token, returning a modified token + * that has valid tokens + * @param $token Reference to token to validate. We require a reference + * because the operation this class performs on the token are + * not atomic, so the context CurrentToken to be updated + * throughout + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + */ + public function validateToken(&$token, &$config, $context) { + + $definition = $config->getHTMLDefinition(); + $e =& $context->get('ErrorCollector', true); + + // initialize IDAccumulator if necessary + $ok =& $context->get('IDAccumulator', true); + if (!$ok) { + $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); + $context->register('IDAccumulator', $id_accumulator); + } + + // initialize CurrentToken if necessary + $current_token =& $context->get('CurrentToken', true); + if (!$current_token) $context->register('CurrentToken', $token); + + if ( + !$token instanceof HTMLPurifier_Token_Start && + !$token instanceof HTMLPurifier_Token_Empty + ) return $token; + + // create alias to global definition array, see also $defs + // DEFINITION CALL + $d_defs = $definition->info_global_attr; + + // don't update token until the very end, to ensure an atomic update + $attr = $token->attr; + + // do global transformations (pre) + // nothing currently utilizes this + foreach ($definition->info_attr_transform_pre as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + + // do local transformations only applicable to this element (pre) + // ex. <p align="right"> to <p style="text-align:right;"> + foreach ($definition->info[$token->name]->attr_transform_pre as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + + // create alias to this element's attribute definition array, see + // also $d_defs (global attribute definition array) + // DEFINITION CALL + $defs = $definition->info[$token->name]->attr; + + $attr_key = false; + $context->register('CurrentAttr', $attr_key); + + // iterate through all the attribute keypairs + // Watch out for name collisions: $key has previously been used + foreach ($attr as $attr_key => $value) { + + // call the definition + if ( isset($defs[$attr_key]) ) { + // there is a local definition defined + if ($defs[$attr_key] === false) { + // We've explicitly been told not to allow this element. + // This is usually when there's a global definition + // that must be overridden. + // Theoretically speaking, we could have a + // AttrDef_DenyAll, but this is faster! + $result = false; + } else { + // validate according to the element's definition + $result = $defs[$attr_key]->validate( + $value, $config, $context + ); + } + } elseif ( isset($d_defs[$attr_key]) ) { + // there is a global definition defined, validate according + // to the global definition + $result = $d_defs[$attr_key]->validate( + $value, $config, $context + ); + } else { + // system never heard of the attribute? DELETE! + $result = false; + } + + // put the results into effect + if ($result === false || $result === null) { + // this is a generic error message that should replaced + // with more specific ones when possible + if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed'); + + // remove the attribute + unset($attr[$attr_key]); + } elseif (is_string($result)) { + // generally, if a substitution is happening, there + // was some sort of implicit correction going on. We'll + // delegate it to the attribute classes to say exactly what. + + // simple substitution + $attr[$attr_key] = $result; + } else { + // nothing happens + } + + // we'd also want slightly more complicated substitution + // involving an array as the return value, + // although we're not sure how colliding attributes would + // resolve (certain ones would be completely overriden, + // others would prepend themselves). + } + + $context->destroy('CurrentAttr'); + + // post transforms + + // global (error reporting untested) + foreach ($definition->info_attr_transform_post as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + + // local (error reporting untested) + foreach ($definition->info[$token->name]->attr_transform_post as $transform) { + $attr = $transform->transform($o = $attr, $config, $context); + if ($e) { + if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } + } + + $token->attr = $attr; + + // destroy CurrentToken if we made it ourselves + if (!$current_token) $context->destroy('CurrentToken'); + + } + + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Bootstrap.php b/lib/php/HTMLPurifier/Bootstrap.php new file mode 100644 index 0000000..559f61a --- /dev/null +++ b/lib/php/HTMLPurifier/Bootstrap.php @@ -0,0 +1,98 @@ +<?php + +// constants are slow, so we use as few as possible +if (!defined('HTMLPURIFIER_PREFIX')) { + define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..')); +} + +// accomodations for versions earlier than 5.0.2 +// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net> +if (!defined('PHP_EOL')) { + switch (strtoupper(substr(PHP_OS, 0, 3))) { + case 'WIN': + define('PHP_EOL', "\r\n"); + break; + case 'DAR': + define('PHP_EOL', "\r"); + break; + default: + define('PHP_EOL', "\n"); + } +} + +/** + * Bootstrap class that contains meta-functionality for HTML Purifier such as + * the autoload function. + * + * @note + * This class may be used without any other files from HTML Purifier. + */ +class HTMLPurifier_Bootstrap +{ + + /** + * Autoload function for HTML Purifier + * @param $class Class to load + */ + public static function autoload($class) { + $file = HTMLPurifier_Bootstrap::getPath($class); + if (!$file) return false; + require HTMLPURIFIER_PREFIX . '/' . $file; + return true; + } + + /** + * Returns the path for a specific class. + */ + public static function getPath($class) { + if (strncmp('HTMLPurifier', $class, 12) !== 0) return false; + // Custom implementations + if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) { + $code = str_replace('_', '-', substr($class, 22)); + $file = 'HTMLPurifier/Language/classes/' . $code . '.php'; + } else { + $file = str_replace('_', '/', $class) . '.php'; + } + if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false; + return $file; + } + + /** + * "Pre-registers" our autoloader on the SPL stack. + */ + public static function registerAutoload() { + $autoload = array('HTMLPurifier_Bootstrap', 'autoload'); + if ( ($funcs = spl_autoload_functions()) === false ) { + spl_autoload_register($autoload); + } elseif (function_exists('spl_autoload_unregister')) { + $compat = version_compare(PHP_VERSION, '5.1.2', '<=') && + version_compare(PHP_VERSION, '5.1.0', '>='); + foreach ($funcs as $func) { + if (is_array($func)) { + // :TRICKY: There are some compatibility issues and some + // places where we need to error out + $reflector = new ReflectionMethod($func[0], $func[1]); + if (!$reflector->isStatic()) { + throw new Exception(' + HTML Purifier autoloader registrar is not compatible + with non-static object methods due to PHP Bug #44144; + Please do not use HTMLPurifier.autoload.php (or any + file that includes this file); instead, place the code: + spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\')) + after your own autoloaders. + '); + } + // Suprisingly, spl_autoload_register supports the + // Class::staticMethod callback format, although call_user_func doesn't + if ($compat) $func = implode('::', $func); + } + spl_autoload_unregister($func); + } + spl_autoload_register($autoload); + foreach ($funcs as $func) spl_autoload_register($func); + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/CSSDefinition.php b/lib/php/HTMLPurifier/CSSDefinition.php new file mode 100644 index 0000000..6a2e6f5 --- /dev/null +++ b/lib/php/HTMLPurifier/CSSDefinition.php @@ -0,0 +1,292 @@ +<?php + +/** + * Defines allowed CSS attributes and what their values are. + * @see HTMLPurifier_HTMLDefinition + */ +class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition +{ + + public $type = 'CSS'; + + /** + * Assoc array of attribute name to definition object. + */ + public $info = array(); + + /** + * Constructs the info array. The meat of this class. + */ + protected function doSetup($config) { + + $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum( + array('left', 'right', 'center', 'justify'), false); + + $border_style = + $this->info['border-bottom-style'] = + $this->info['border-right-style'] = + $this->info['border-left-style'] = + $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( + array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', + 'groove', 'ridge', 'inset', 'outset'), false); + + $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); + + $this->info['clear'] = new HTMLPurifier_AttrDef_Enum( + array('none', 'left', 'right', 'both'), false); + $this->info['float'] = new HTMLPurifier_AttrDef_Enum( + array('none', 'left', 'right'), false); + $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( + array('normal', 'italic', 'oblique'), false); + $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( + array('normal', 'small-caps'), false); + + $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('none')), + new HTMLPurifier_AttrDef_CSS_URI() + ) + ); + + $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( + array('inside', 'outside'), false); + $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( + array('disc', 'circle', 'square', 'decimal', 'lower-roman', + 'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false); + $this->info['list-style-image'] = $uri_or_none; + + $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); + + $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( + array('capitalize', 'uppercase', 'lowercase', 'none'), false); + $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); + + $this->info['background-image'] = $uri_or_none; + $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum( + array('repeat', 'repeat-x', 'repeat-y', 'no-repeat') + ); + $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( + array('scroll', 'fixed') + ); + $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); + + $border_color = + $this->info['border-top-color'] = + $this->info['border-bottom-color'] = + $this->info['border-left-color'] = + $this->info['border-right-color'] = + $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('transparent')), + new HTMLPurifier_AttrDef_CSS_Color() + )); + + $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); + + $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); + + $border_width = + $this->info['border-top-width'] = + $this->info['border-bottom-width'] = + $this->info['border-left-width'] = + $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), + new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative + )); + + $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); + + $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Length() + )); + + $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Length() + )); + + $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small', + 'small', 'medium', 'large', 'x-large', 'xx-large', + 'larger', 'smaller')), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_CSS_Length() + )); + + $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true) + )); + + $margin = + $this->info['margin-top'] = + $this->info['margin-bottom'] = + $this->info['margin-left'] = + $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_Enum(array('auto')) + )); + + $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); + + // non-negative + $padding = + $this->info['padding-top'] = + $this->info['padding-bottom'] = + $this->info['padding-left'] = + $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true) + )); + + $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); + + $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + + $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true), + new HTMLPurifier_AttrDef_Enum(array('auto')) + )); + $max = $config->get('CSS.MaxImgLength'); + + $this->info['width'] = + $this->info['height'] = + $max === null ? + $trusted_wh : + new HTMLPurifier_AttrDef_Switch('img', + // For img tags: + new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length('0', $max), + new HTMLPurifier_AttrDef_Enum(array('auto')) + )), + // For everyone else: + $trusted_wh + ); + + $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); + + $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); + + // this could use specialized code + $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( + array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', + '400', '500', '600', '700', '800', '900'), false); + + // MUST be called after other font properties, as it references + // a CSSDefinition object + $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config); + + // same here + $this->info['border'] = + $this->info['border-bottom'] = + $this->info['border-top'] = + $this->info['border-left'] = + $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config); + + $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array( + 'collapse', 'separate')); + + $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array( + 'top', 'bottom')); + + $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array( + 'auto', 'fixed')); + + $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super', + 'top', 'text-top', 'middle', 'bottom', 'text-bottom')), + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + + $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); + + // partial support + $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap')); + + if ($config->get('CSS.Proprietary')) { + $this->doSetupProprietary($config); + } + + if ($config->get('CSS.AllowTricky')) { + $this->doSetupTricky($config); + } + + $allow_important = $config->get('CSS.AllowImportant'); + // wrap all attr-defs with decorator that handles !important + foreach ($this->info as $k => $v) { + $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); + } + + $this->setupConfigStuff($config); + } + + protected function doSetupProprietary($config) { + // Internet Explorer only scrollbar colors + $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + + // technically not proprietary, but CSS3, and no one supports it + $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + + // only opacity, for now + $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); + + } + + protected function doSetupTricky($config) { + $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array( + 'inline', 'block', 'list-item', 'run-in', 'compact', + 'marker', 'table', 'inline-table', 'table-row-group', + 'table-header-group', 'table-footer-group', 'table-row', + 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none' + )); + $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array( + 'visible', 'hidden', 'collapse' + )); + $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); + } + + + /** + * Performs extra config-based processing. Based off of + * HTMLPurifier_HTMLDefinition. + * @todo Refactor duplicate elements into common class (probably using + * composition, not inheritance). + */ + protected function setupConfigStuff($config) { + + // setup allowed elements + $support = "(for information on implementing this, see the ". + "support forums) "; + $allowed_attributes = $config->get('CSS.AllowedProperties'); + if ($allowed_attributes !== null) { + foreach ($this->info as $name => $d) { + if(!isset($allowed_attributes[$name])) unset($this->info[$name]); + unset($allowed_attributes[$name]); + } + // emit errors + foreach ($allowed_attributes as $name => $d) { + // :TODO: Is this htmlspecialchars() call really necessary? + $name = htmlspecialchars($name); + trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); + } + } + + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef.php b/lib/php/HTMLPurifier/ChildDef.php new file mode 100644 index 0000000..c5d5216 --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef.php @@ -0,0 +1,48 @@ +<?php + +/** + * Defines allowed child nodes and validates tokens against it. + */ +abstract class HTMLPurifier_ChildDef +{ + /** + * Type of child definition, usually right-most part of class name lowercase. + * Used occasionally in terms of context. + */ + public $type; + + /** + * Bool that indicates whether or not an empty array of children is okay + * + * This is necessary for redundant checking when changes affecting + * a child node may cause a parent node to now be disallowed. + */ + public $allow_empty; + + /** + * Lookup array of all elements that this definition could possibly allow + */ + public $elements = array(); + + /** + * Get lookup of tag names that should not close this element automatically. + * All other elements will do so. + */ + public function getAllowedElements($config) { + return $this->elements; + } + + /** + * Validates nodes according to definition and returns modification. + * + * @param $tokens_of_children Array of HTMLPurifier_Token + * @param $config HTMLPurifier_Config object + * @param $context HTMLPurifier_Context object + * @return bool true to leave nodes as is + * @return bool false to remove parent node + * @return array of replacement child tokens + */ + abstract public function validateChildren($tokens_of_children, $config, $context); +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/Chameleon.php b/lib/php/HTMLPurifier/ChildDef/Chameleon.php new file mode 100644 index 0000000..15c364e --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/Chameleon.php @@ -0,0 +1,48 @@ +<?php + +/** + * Definition that uses different definitions depending on context. + * + * The del and ins tags are notable because they allow different types of + * elements depending on whether or not they're in a block or inline context. + * Chameleon allows this behavior to happen by using two different + * definitions depending on context. While this somewhat generalized, + * it is specifically intended for those two tags. + */ +class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef +{ + + /** + * Instance of the definition object to use when inline. Usually stricter. + */ + public $inline; + + /** + * Instance of the definition object to use when block. + */ + public $block; + + public $type = 'chameleon'; + + /** + * @param $inline List of elements to allow when inline. + * @param $block List of elements to allow when block. + */ + public function __construct($inline, $block) { + $this->inline = new HTMLPurifier_ChildDef_Optional($inline); + $this->block = new HTMLPurifier_ChildDef_Optional($block); + $this->elements = $this->block->elements; + } + + public function validateChildren($tokens_of_children, $config, $context) { + if ($context->get('IsInline') === false) { + return $this->block->validateChildren( + $tokens_of_children, $config, $context); + } else { + return $this->inline->validateChildren( + $tokens_of_children, $config, $context); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/Custom.php b/lib/php/HTMLPurifier/ChildDef/Custom.php new file mode 100644 index 0000000..b68047b --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/Custom.php @@ -0,0 +1,90 @@ +<?php + +/** + * Custom validation class, accepts DTD child definitions + * + * @warning Currently this class is an all or nothing proposition, that is, + * it will only give a bool return value. + */ +class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef +{ + public $type = 'custom'; + public $allow_empty = false; + /** + * Allowed child pattern as defined by the DTD + */ + public $dtd_regex; + /** + * PCRE regex derived from $dtd_regex + * @private + */ + private $_pcre_regex; + /** + * @param $dtd_regex Allowed child pattern from the DTD + */ + public function __construct($dtd_regex) { + $this->dtd_regex = $dtd_regex; + $this->_compileRegex(); + } + /** + * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex) + */ + protected function _compileRegex() { + $raw = str_replace(' ', '', $this->dtd_regex); + if ($raw{0} != '(') { + $raw = "($raw)"; + } + $el = '[#a-zA-Z0-9_.-]+'; + $reg = $raw; + + // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M + // DOING! Seriously: if there's problems, please report them. + + // collect all elements into the $elements array + preg_match_all("/$el/", $reg, $matches); + foreach ($matches[0] as $match) { + $this->elements[$match] = true; + } + + // setup all elements as parentheticals with leading commas + $reg = preg_replace("/$el/", '(,\\0)', $reg); + + // remove commas when they were not solicited + $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg); + + // remove all non-paranthetical commas: they are handled by first regex + $reg = preg_replace("/,\(/", '(', $reg); + + $this->_pcre_regex = $reg; + } + public function validateChildren($tokens_of_children, $config, $context) { + $list_of_children = ''; + $nesting = 0; // depth into the nest + foreach ($tokens_of_children as $token) { + if (!empty($token->is_whitespace)) continue; + + $is_child = ($nesting == 0); // direct + + if ($token instanceof HTMLPurifier_Token_Start) { + $nesting++; + } elseif ($token instanceof HTMLPurifier_Token_End) { + $nesting--; + } + + if ($is_child) { + $list_of_children .= $token->name . ','; + } + } + // add leading comma to deal with stray comma declarations + $list_of_children = ',' . rtrim($list_of_children, ','); + $okay = + preg_match( + '/^,?'.$this->_pcre_regex.'$/', + $list_of_children + ); + + return (bool) $okay; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/Empty.php b/lib/php/HTMLPurifier/ChildDef/Empty.php new file mode 100644 index 0000000..13171f6 --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/Empty.php @@ -0,0 +1,20 @@ +<?php + +/** + * Definition that disallows all elements. + * @warning validateChildren() in this class is actually never called, because + * empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed + * before child definitions are parsed in earnest by + * HTMLPurifier_Strategy_FixNesting. + */ +class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef +{ + public $allow_empty = true; + public $type = 'empty'; + public function __construct() {} + public function validateChildren($tokens_of_children, $config, $context) { + return array(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/Optional.php b/lib/php/HTMLPurifier/ChildDef/Optional.php new file mode 100644 index 0000000..32bcb98 --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/Optional.php @@ -0,0 +1,26 @@ +<?php + +/** + * Definition that allows a set of elements, and allows no children. + * @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required, + * really, one shouldn't inherit from the other. Only altered behavior + * is to overload a returned false with an array. Thus, it will never + * return false. + */ +class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required +{ + public $allow_empty = true; + public $type = 'optional'; + public function validateChildren($tokens_of_children, $config, $context) { + $result = parent::validateChildren($tokens_of_children, $config, $context); + // we assume that $tokens_of_children is not modified + if ($result === false) { + if (empty($tokens_of_children)) return true; + elseif ($this->whitespace) return $tokens_of_children; + else return array(); + } + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/Required.php b/lib/php/HTMLPurifier/ChildDef/Required.php new file mode 100644 index 0000000..4889f24 --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/Required.php @@ -0,0 +1,117 @@ +<?php + +/** + * Definition that allows a set of elements, but disallows empty children. + */ +class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef +{ + /** + * Lookup table of allowed elements. + * @public + */ + public $elements = array(); + /** + * Whether or not the last passed node was all whitespace. + */ + protected $whitespace = false; + /** + * @param $elements List of allowed element names (lowercase). + */ + public function __construct($elements) { + if (is_string($elements)) { + $elements = str_replace(' ', '', $elements); + $elements = explode('|', $elements); + } + $keys = array_keys($elements); + if ($keys == array_keys($keys)) { + $elements = array_flip($elements); + foreach ($elements as $i => $x) { + $elements[$i] = true; + if (empty($i)) unset($elements[$i]); // remove blank + } + } + $this->elements = $elements; + } + public $allow_empty = false; + public $type = 'required'; + public function validateChildren($tokens_of_children, $config, $context) { + // Flag for subclasses + $this->whitespace = false; + + // if there are no tokens, delete parent node + if (empty($tokens_of_children)) return false; + + // the new set of children + $result = array(); + + // current depth into the nest + $nesting = 0; + + // whether or not we're deleting a node + $is_deleting = false; + + // whether or not parsed character data is allowed + // this controls whether or not we silently drop a tag + // or generate escaped HTML from it + $pcdata_allowed = isset($this->elements['#PCDATA']); + + // a little sanity check to make sure it's not ALL whitespace + $all_whitespace = true; + + // some configuration + $escape_invalid_children = $config->get('Core.EscapeInvalidChildren'); + + // generator + $gen = new HTMLPurifier_Generator($config, $context); + + foreach ($tokens_of_children as $token) { + if (!empty($token->is_whitespace)) { + $result[] = $token; + continue; + } + $all_whitespace = false; // phew, we're not talking about whitespace + + $is_child = ($nesting == 0); + + if ($token instanceof HTMLPurifier_Token_Start) { + $nesting++; + } elseif ($token instanceof HTMLPurifier_Token_End) { + $nesting--; + } + + if ($is_child) { + $is_deleting = false; + if (!isset($this->elements[$token->name])) { + $is_deleting = true; + if ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text) { + $result[] = $token; + } elseif ($pcdata_allowed && $escape_invalid_children) { + $result[] = new HTMLPurifier_Token_Text( + $gen->generateFromToken($token) + ); + } + continue; + } + } + if (!$is_deleting || ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text)) { + $result[] = $token; + } elseif ($pcdata_allowed && $escape_invalid_children) { + $result[] = + new HTMLPurifier_Token_Text( + $gen->generateFromToken($token) + ); + } else { + // drop silently + } + } + if (empty($result)) return false; + if ($all_whitespace) { + $this->whitespace = true; + return false; + } + if ($tokens_of_children == $result) return true; + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/StrictBlockquote.php b/lib/php/HTMLPurifier/ChildDef/StrictBlockquote.php new file mode 100644 index 0000000..dfae8a6 --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/StrictBlockquote.php @@ -0,0 +1,88 @@ +<?php + +/** + * Takes the contents of blockquote when in strict and reformats for validation. + */ +class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required +{ + protected $real_elements; + protected $fake_elements; + public $allow_empty = true; + public $type = 'strictblockquote'; + protected $init = false; + + /** + * @note We don't want MakeWellFormed to auto-close inline elements since + * they might be allowed. + */ + public function getAllowedElements($config) { + $this->init($config); + return $this->fake_elements; + } + + public function validateChildren($tokens_of_children, $config, $context) { + + $this->init($config); + + // trick the parent class into thinking it allows more + $this->elements = $this->fake_elements; + $result = parent::validateChildren($tokens_of_children, $config, $context); + $this->elements = $this->real_elements; + + if ($result === false) return array(); + if ($result === true) $result = $tokens_of_children; + + $def = $config->getHTMLDefinition(); + $block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper); + $block_wrap_end = new HTMLPurifier_Token_End( $def->info_block_wrapper); + $is_inline = false; + $depth = 0; + $ret = array(); + + // assuming that there are no comment tokens + foreach ($result as $i => $token) { + $token = $result[$i]; + // ifs are nested for readability + if (!$is_inline) { + if (!$depth) { + if ( + ($token instanceof HTMLPurifier_Token_Text && !$token->is_whitespace) || + (!$token instanceof HTMLPurifier_Token_Text && !isset($this->elements[$token->name])) + ) { + $is_inline = true; + $ret[] = $block_wrap_start; + } + } + } else { + if (!$depth) { + // starting tokens have been inline text / empty + if ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) { + if (isset($this->elements[$token->name])) { + // ended + $ret[] = $block_wrap_end; + $is_inline = false; + } + } + } + } + $ret[] = $token; + if ($token instanceof HTMLPurifier_Token_Start) $depth++; + if ($token instanceof HTMLPurifier_Token_End) $depth--; + } + if ($is_inline) $ret[] = $block_wrap_end; + return $ret; + } + + private function init($config) { + if (!$this->init) { + $def = $config->getHTMLDefinition(); + // allow all inline elements + $this->real_elements = $this->elements; + $this->fake_elements = $def->info_content_sets['Flow']; + $this->fake_elements['#PCDATA'] = true; + $this->init = true; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ChildDef/Table.php b/lib/php/HTMLPurifier/ChildDef/Table.php new file mode 100644 index 0000000..34f0227 --- /dev/null +++ b/lib/php/HTMLPurifier/ChildDef/Table.php @@ -0,0 +1,142 @@ +<?php + +/** + * Definition for tables + */ +class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef +{ + public $allow_empty = false; + public $type = 'table'; + public $elements = array('tr' => true, 'tbody' => true, 'thead' => true, + 'tfoot' => true, 'caption' => true, 'colgroup' => true, 'col' => true); + public function __construct() {} + public function validateChildren($tokens_of_children, $config, $context) { + if (empty($tokens_of_children)) return false; + + // this ensures that the loop gets run one last time before closing + // up. It's a little bit of a hack, but it works! Just make sure you + // get rid of the token later. + $tokens_of_children[] = false; + + // only one of these elements is allowed in a table + $caption = false; + $thead = false; + $tfoot = false; + + // as many of these as you want + $cols = array(); + $content = array(); + + $nesting = 0; // current depth so we can determine nodes + $is_collecting = false; // are we globbing together tokens to package + // into one of the collectors? + $collection = array(); // collected nodes + $tag_index = 0; // the first node might be whitespace, + // so this tells us where the start tag is + + foreach ($tokens_of_children as $token) { + $is_child = ($nesting == 0); + + if ($token === false) { + // terminating sequence started + } elseif ($token instanceof HTMLPurifier_Token_Start) { + $nesting++; + } elseif ($token instanceof HTMLPurifier_Token_End) { + $nesting--; + } + + // handle node collection + if ($is_collecting) { + if ($is_child) { + // okay, let's stash the tokens away + // first token tells us the type of the collection + switch ($collection[$tag_index]->name) { + case 'tr': + case 'tbody': + $content[] = $collection; + break; + case 'caption': + if ($caption !== false) break; + $caption = $collection; + break; + case 'thead': + case 'tfoot': + // access the appropriate variable, $thead or $tfoot + $var = $collection[$tag_index]->name; + if ($$var === false) { + $$var = $collection; + } else { + // transmutate the first and less entries into + // tbody tags, and then put into content + $collection[$tag_index]->name = 'tbody'; + $collection[count($collection)-1]->name = 'tbody'; + $content[] = $collection; + } + break; + case 'colgroup': + $cols[] = $collection; + break; + } + $collection = array(); + $is_collecting = false; + $tag_index = 0; + } else { + // add the node to the collection + $collection[] = $token; + } + } + + // terminate + if ($token === false) break; + + if ($is_child) { + // determine what we're dealing with + if ($token->name == 'col') { + // the only empty tag in the possie, we can handle it + // immediately + $cols[] = array_merge($collection, array($token)); + $collection = array(); + $tag_index = 0; + continue; + } + switch($token->name) { + case 'caption': + case 'colgroup': + case 'thead': + case 'tfoot': + case 'tbody': + case 'tr': + $is_collecting = true; + $collection[] = $token; + continue; + default: + if (!empty($token->is_whitespace)) { + $collection[] = $token; + $tag_index++; + } + continue; + } + } + } + + if (empty($content)) return false; + + $ret = array(); + if ($caption !== false) $ret = array_merge($ret, $caption); + if ($cols !== false) foreach ($cols as $token_array) $ret = array_merge($ret, $token_array); + if ($thead !== false) $ret = array_merge($ret, $thead); + if ($tfoot !== false) $ret = array_merge($ret, $tfoot); + foreach ($content as $token_array) $ret = array_merge($ret, $token_array); + if (!empty($collection) && $is_collecting == false){ + // grab the trailing space + $ret = array_merge($ret, $collection); + } + + array_pop($tokens_of_children); // remove phantom token + + return ($ret === $tokens_of_children) ? true : $ret; + + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Config.php b/lib/php/HTMLPurifier/Config.php new file mode 100644 index 0000000..a017060 --- /dev/null +++ b/lib/php/HTMLPurifier/Config.php @@ -0,0 +1,580 @@ +<?php + +/** + * Configuration object that triggers customizable behavior. + * + * @warning This class is strongly defined: that means that the class + * will fail if an undefined directive is retrieved or set. + * + * @note Many classes that could (although many times don't) use the + * configuration object make it a mandatory parameter. This is + * because a configuration object should always be forwarded, + * otherwise, you run the risk of missing a parameter and then + * being stumped when a configuration directive doesn't work. + * + * @todo Reconsider some of the public member variables + */ +class HTMLPurifier_Config +{ + + /** + * HTML Purifier's version + */ + public $version = '4.0.0'; + + /** + * Bool indicator whether or not to automatically finalize + * the object if a read operation is done + */ + public $autoFinalize = true; + + // protected member variables + + /** + * Namespace indexed array of serials for specific namespaces (see + * getSerial() for more info). + */ + protected $serials = array(); + + /** + * Serial for entire configuration object + */ + protected $serial; + + /** + * Parser for variables + */ + protected $parser; + + /** + * Reference HTMLPurifier_ConfigSchema for value checking + * @note This is public for introspective purposes. Please don't + * abuse! + */ + public $def; + + /** + * Indexed array of definitions + */ + protected $definitions; + + /** + * Bool indicator whether or not config is finalized + */ + protected $finalized = false; + + /** + * Property list containing configuration directives. + */ + protected $plist; + + /** + * Whether or not a set is taking place due to an + * alias lookup. + */ + private $aliasMode; + + /** + * Set to false if you do not want line and file numbers in errors + * (useful when unit testing) + */ + public $chatty = true; + + /** + * Current lock; only gets to this namespace are allowed. + */ + private $lock; + + /** + * @param $definition HTMLPurifier_ConfigSchema that defines what directives + * are allowed. + */ + public function __construct($definition, $parent = null) { + $parent = $parent ? $parent : $definition->defaultPlist; + $this->plist = new HTMLPurifier_PropertyList($parent); + $this->def = $definition; // keep a copy around for checking + $this->parser = new HTMLPurifier_VarParser_Flexible(); + } + + /** + * Convenience constructor that creates a config object based on a mixed var + * @param mixed $config Variable that defines the state of the config + * object. Can be: a HTMLPurifier_Config() object, + * an array of directives based on loadArray(), + * or a string filename of an ini file. + * @param HTMLPurifier_ConfigSchema Schema object + * @return Configured HTMLPurifier_Config object + */ + public static function create($config, $schema = null) { + if ($config instanceof HTMLPurifier_Config) { + // pass-through + return $config; + } + if (!$schema) { + $ret = HTMLPurifier_Config::createDefault(); + } else { + $ret = new HTMLPurifier_Config($schema); + } + if (is_string($config)) $ret->loadIni($config); + elseif (is_array($config)) $ret->loadArray($config); + return $ret; + } + + /** + * Creates a new config object that inherits from a previous one. + * @param HTMLPurifier_Config $config Configuration object to inherit + * from. + * @return HTMLPurifier_Config object with $config as its parent. + */ + public static function inherit(HTMLPurifier_Config $config) { + return new HTMLPurifier_Config($config->def, $config->plist); + } + + /** + * Convenience constructor that creates a default configuration object. + * @return Default HTMLPurifier_Config object. + */ + public static function createDefault() { + $definition = HTMLPurifier_ConfigSchema::instance(); + $config = new HTMLPurifier_Config($definition); + return $config; + } + + /** + * Retreives a value from the configuration. + * @param $key String key + */ + public function get($key, $a = null) { + if ($a !== null) { + $this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING); + $key = "$key.$a"; + } + if (!$this->finalized) $this->autoFinalize(); + if (!isset($this->def->info[$key])) { + // can't add % due to SimpleTest bug + $this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key), + E_USER_WARNING); + return; + } + if (isset($this->def->info[$key]->isAlias)) { + $d = $this->def->info[$key]; + $this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key, + E_USER_ERROR); + return; + } + if ($this->lock) { + list($ns) = explode('.', $key); + if ($ns !== $this->lock) { + $this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR); + return; + } + } + return $this->plist->get($key); + } + + /** + * Retreives an array of directives to values from a given namespace + * @param $namespace String namespace + */ + public function getBatch($namespace) { + if (!$this->finalized) $this->autoFinalize(); + $full = $this->getAll(); + if (!isset($full[$namespace])) { + $this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace), + E_USER_WARNING); + return; + } + return $full[$namespace]; + } + + /** + * Returns a md5 signature of a segment of the configuration object + * that uniquely identifies that particular configuration + * @note Revision is handled specially and is removed from the batch + * before processing! + * @param $namespace Namespace to get serial for + */ + public function getBatchSerial($namespace) { + if (empty($this->serials[$namespace])) { + $batch = $this->getBatch($namespace); + unset($batch['DefinitionRev']); + $this->serials[$namespace] = md5(serialize($batch)); + } + return $this->serials[$namespace]; + } + + /** + * Returns a md5 signature for the entire configuration object + * that uniquely identifies that particular configuration + */ + public function getSerial() { + if (empty($this->serial)) { + $this->serial = md5(serialize($this->getAll())); + } + return $this->serial; + } + + /** + * Retrieves all directives, organized by namespace + * @warning This is a pretty inefficient function, avoid if you can + */ + public function getAll() { + if (!$this->finalized) $this->autoFinalize(); + $ret = array(); + foreach ($this->plist->squash() as $name => $value) { + list($ns, $key) = explode('.', $name, 2); + $ret[$ns][$key] = $value; + } + return $ret; + } + + /** + * Sets a value to configuration. + * @param $key String key + * @param $value Mixed value + */ + public function set($key, $value, $a = null) { + if (strpos($key, '.') === false) { + $namespace = $key; + $directive = $value; + $value = $a; + $key = "$key.$directive"; + $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE); + } else { + list($namespace) = explode('.', $key); + } + if ($this->isFinalized('Cannot set directive after finalization')) return; + if (!isset($this->def->info[$key])) { + $this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value', + E_USER_WARNING); + return; + } + $def = $this->def->info[$key]; + + if (isset($def->isAlias)) { + if ($this->aliasMode) { + $this->triggerError('Double-aliases not allowed, please fix '. + 'ConfigSchema bug with' . $key, E_USER_ERROR); + return; + } + $this->aliasMode = true; + $this->set($def->key, $value); + $this->aliasMode = false; + $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE); + return; + } + + // Raw type might be negative when using the fully optimized form + // of stdclass, which indicates allow_null == true + $rtype = is_int($def) ? $def : $def->type; + if ($rtype < 0) { + $type = -$rtype; + $allow_null = true; + } else { + $type = $rtype; + $allow_null = isset($def->allow_null); + } + + try { + $value = $this->parser->parse($value, $type, $allow_null); + } catch (HTMLPurifier_VarParserException $e) { + $this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING); + return; + } + if (is_string($value) && is_object($def)) { + // resolve value alias if defined + if (isset($def->aliases[$value])) { + $value = $def->aliases[$value]; + } + // check to see if the value is allowed + if (isset($def->allowed) && !isset($def->allowed[$value])) { + $this->triggerError('Value not supported, valid values are: ' . + $this->_listify($def->allowed), E_USER_WARNING); + return; + } + } + $this->plist->set($key, $value); + + // reset definitions if the directives they depend on changed + // this is a very costly process, so it's discouraged + // with finalization + if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') { + $this->definitions[$namespace] = null; + } + + $this->serials[$namespace] = false; + } + + /** + * Convenience function for error reporting + */ + private function _listify($lookup) { + $list = array(); + foreach ($lookup as $name => $b) $list[] = $name; + return implode(', ', $list); + } + + /** + * Retrieves object reference to the HTML definition. + * @param $raw Return a copy that has not been setup yet. Must be + * called before it's been setup, otherwise won't work. + */ + public function getHTMLDefinition($raw = false) { + return $this->getDefinition('HTML', $raw); + } + + /** + * Retrieves object reference to the CSS definition + * @param $raw Return a copy that has not been setup yet. Must be + * called before it's been setup, otherwise won't work. + */ + public function getCSSDefinition($raw = false) { + return $this->getDefinition('CSS', $raw); + } + + /** + * Retrieves a definition + * @param $type Type of definition: HTML, CSS, etc + * @param $raw Whether or not definition should be returned raw + */ + public function getDefinition($type, $raw = false) { + if (!$this->finalized) $this->autoFinalize(); + // temporarily suspend locks, so we can handle recursive definition calls + $lock = $this->lock; + $this->lock = null; + $factory = HTMLPurifier_DefinitionCacheFactory::instance(); + $cache = $factory->create($type, $this); + $this->lock = $lock; + if (!$raw) { + // see if we can quickly supply a definition + if (!empty($this->definitions[$type])) { + if (!$this->definitions[$type]->setup) { + $this->definitions[$type]->setup($this); + $cache->set($this->definitions[$type], $this); + } + return $this->definitions[$type]; + } + // memory check missed, try cache + $this->definitions[$type] = $cache->get($this); + if ($this->definitions[$type]) { + // definition in cache, return it + return $this->definitions[$type]; + } + } elseif ( + !empty($this->definitions[$type]) && + !$this->definitions[$type]->setup + ) { + // raw requested, raw in memory, quick return + return $this->definitions[$type]; + } + // quick checks failed, let's create the object + if ($type == 'HTML') { + $this->definitions[$type] = new HTMLPurifier_HTMLDefinition(); + } elseif ($type == 'CSS') { + $this->definitions[$type] = new HTMLPurifier_CSSDefinition(); + } elseif ($type == 'URI') { + $this->definitions[$type] = new HTMLPurifier_URIDefinition(); + } else { + throw new HTMLPurifier_Exception("Definition of $type type not supported"); + } + // quick abort if raw + if ($raw) { + if (is_null($this->get($type . '.DefinitionID'))) { + // fatally error out if definition ID not set + throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID"); + } + return $this->definitions[$type]; + } + // set it up + $this->lock = $type; + $this->definitions[$type]->setup($this); + $this->lock = null; + // save in cache + $cache->set($this->definitions[$type], $this); + return $this->definitions[$type]; + } + + /** + * Loads configuration values from an array with the following structure: + * Namespace.Directive => Value + * @param $config_array Configuration associative array + */ + public function loadArray($config_array) { + if ($this->isFinalized('Cannot load directives after finalization')) return; + foreach ($config_array as $key => $value) { + $key = str_replace('_', '.', $key); + if (strpos($key, '.') !== false) { + $this->set($key, $value); + } else { + $namespace = $key; + $namespace_values = $value; + foreach ($namespace_values as $directive => $value) { + $this->set($namespace .'.'. $directive, $value); + } + } + } + } + + /** + * Returns a list of array(namespace, directive) for all directives + * that are allowed in a web-form context as per an allowed + * namespaces/directives list. + * @param $allowed List of allowed namespaces/directives + */ + public static function getAllowedDirectivesForForm($allowed, $schema = null) { + if (!$schema) { + $schema = HTMLPurifier_ConfigSchema::instance(); + } + if ($allowed !== true) { + if (is_string($allowed)) $allowed = array($allowed); + $allowed_ns = array(); + $allowed_directives = array(); + $blacklisted_directives = array(); + foreach ($allowed as $ns_or_directive) { + if (strpos($ns_or_directive, '.') !== false) { + // directive + if ($ns_or_directive[0] == '-') { + $blacklisted_directives[substr($ns_or_directive, 1)] = true; + } else { + $allowed_directives[$ns_or_directive] = true; + } + } else { + // namespace + $allowed_ns[$ns_or_directive] = true; + } + } + } + $ret = array(); + foreach ($schema->info as $key => $def) { + list($ns, $directive) = explode('.', $key, 2); + if ($allowed !== true) { + if (isset($blacklisted_directives["$ns.$directive"])) continue; + if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue; + } + if (isset($def->isAlias)) continue; + if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue; + $ret[] = array($ns, $directive); + } + return $ret; + } + + /** + * Loads configuration values from $_GET/$_POST that were posted + * via ConfigForm + * @param $array $_GET or $_POST array to import + * @param $index Index/name that the config variables are in + * @param $allowed List of allowed namespaces/directives + * @param $mq_fix Boolean whether or not to enable magic quotes fix + * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy + */ + public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) { + $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema); + $config = HTMLPurifier_Config::create($ret, $schema); + return $config; + } + + /** + * Merges in configuration values from $_GET/$_POST to object. NOT STATIC. + * @note Same parameters as loadArrayFromForm + */ + public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) { + $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def); + $this->loadArray($ret); + } + + /** + * Prepares an array from a form into something usable for the more + * strict parts of HTMLPurifier_Config + */ + public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) { + if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array(); + $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc(); + + $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema); + $ret = array(); + foreach ($allowed as $key) { + list($ns, $directive) = $key; + $skey = "$ns.$directive"; + if (!empty($array["Null_$skey"])) { + $ret[$ns][$directive] = null; + continue; + } + if (!isset($array[$skey])) continue; + $value = $mq ? stripslashes($array[$skey]) : $array[$skey]; + $ret[$ns][$directive] = $value; + } + return $ret; + } + + /** + * Loads configuration values from an ini file + * @param $filename Name of ini file + */ + public function loadIni($filename) { + if ($this->isFinalized('Cannot load directives after finalization')) return; + $array = parse_ini_file($filename, true); + $this->loadArray($array); + } + + /** + * Checks whether or not the configuration object is finalized. + * @param $error String error message, or false for no error + */ + public function isFinalized($error = false) { + if ($this->finalized && $error) { + $this->triggerError($error, E_USER_ERROR); + } + return $this->finalized; + } + + /** + * Finalizes configuration only if auto finalize is on and not + * already finalized + */ + public function autoFinalize() { + if ($this->autoFinalize) { + $this->finalize(); + } else { + $this->plist->squash(true); + } + } + + /** + * Finalizes a configuration object, prohibiting further change + */ + public function finalize() { + $this->finalized = true; + unset($this->parser); + } + + /** + * Produces a nicely formatted error message by supplying the + * stack frame information from two levels up and OUTSIDE of + * HTMLPurifier_Config. + */ + protected function triggerError($msg, $no) { + // determine previous stack frame + $backtrace = debug_backtrace(); + if ($this->chatty && isset($backtrace[1])) { + $frame = $backtrace[1]; + $extra = " on line {$frame['line']} in file {$frame['file']}"; + } else { + $extra = ''; + } + trigger_error($msg . $extra, $no); + } + + /** + * Returns a serialized form of the configuration object that can + * be reconstituted. + */ + public function serialize() { + $this->getDefinition('HTML'); + $this->getDefinition('CSS'); + $this->getDefinition('URI'); + return serialize($this); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema.php b/lib/php/HTMLPurifier/ConfigSchema.php new file mode 100644 index 0000000..67be5c7 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema.php @@ -0,0 +1,158 @@ +<?php + +/** + * Configuration definition, defines directives and their defaults. + */ +class HTMLPurifier_ConfigSchema { + + /** + * Defaults of the directives and namespaces. + * @note This shares the exact same structure as HTMLPurifier_Config::$conf + */ + public $defaults = array(); + + /** + * The default property list. Do not edit this property list. + */ + public $defaultPlist; + + /** + * Definition of the directives. The structure of this is: + * + * array( + * 'Namespace' => array( + * 'Directive' => new stdclass(), + * ) + * ) + * + * The stdclass may have the following properties: + * + * - If isAlias isn't set: + * - type: Integer type of directive, see HTMLPurifier_VarParser for definitions + * - allow_null: If set, this directive allows null values + * - aliases: If set, an associative array of value aliases to real values + * - allowed: If set, a lookup array of allowed (string) values + * - If isAlias is set: + * - namespace: Namespace this directive aliases to + * - name: Directive name this directive aliases to + * + * In certain degenerate cases, stdclass will actually be an integer. In + * that case, the value is equivalent to an stdclass with the type + * property set to the integer. If the integer is negative, type is + * equal to the absolute value of integer, and allow_null is true. + * + * This class is friendly with HTMLPurifier_Config. If you need introspection + * about the schema, you're better of using the ConfigSchema_Interchange, + * which uses more memory but has much richer information. + */ + public $info = array(); + + /** + * Application-wide singleton + */ + static protected $singleton; + + public function __construct() { + $this->defaultPlist = new HTMLPurifier_PropertyList(); + } + + /** + * Unserializes the default ConfigSchema. + */ + public static function makeFromSerial() { + return unserialize(file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser')); + } + + /** + * Retrieves an instance of the application-wide configuration definition. + */ + public static function instance($prototype = null) { + if ($prototype !== null) { + HTMLPurifier_ConfigSchema::$singleton = $prototype; + } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) { + HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial(); + } + return HTMLPurifier_ConfigSchema::$singleton; + } + + /** + * Defines a directive for configuration + * @warning Will fail of directive's namespace is defined. + * @warning This method's signature is slightly different from the legacy + * define() static method! Beware! + * @param $namespace Namespace the directive is in + * @param $name Key of directive + * @param $default Default value of directive + * @param $type Allowed type of the directive. See + * HTMLPurifier_DirectiveDef::$type for allowed values + * @param $allow_null Whether or not to allow null values + */ + public function add($key, $default, $type, $allow_null) { + $obj = new stdclass(); + $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type]; + if ($allow_null) $obj->allow_null = true; + $this->info[$key] = $obj; + $this->defaults[$key] = $default; + $this->defaultPlist->set($key, $default); + } + + /** + * Defines a directive value alias. + * + * Directive value aliases are convenient for developers because it lets + * them set a directive to several values and get the same result. + * @param $namespace Directive's namespace + * @param $name Name of Directive + * @param $aliases Hash of aliased values to the real alias + */ + public function addValueAliases($key, $aliases) { + if (!isset($this->info[$key]->aliases)) { + $this->info[$key]->aliases = array(); + } + foreach ($aliases as $alias => $real) { + $this->info[$key]->aliases[$alias] = $real; + } + } + + /** + * Defines a set of allowed values for a directive. + * @warning This is slightly different from the corresponding static + * method definition. + * @param $namespace Namespace of directive + * @param $name Name of directive + * @param $allowed Lookup array of allowed values + */ + public function addAllowedValues($key, $allowed) { + $this->info[$key]->allowed = $allowed; + } + + /** + * Defines a directive alias for backwards compatibility + * @param $namespace + * @param $name Directive that will be aliased + * @param $new_namespace + * @param $new_name Directive that the alias will be to + */ + public function addAlias($key, $new_key) { + $obj = new stdclass; + $obj->key = $new_key; + $obj->isAlias = true; + $this->info[$key] = $obj; + } + + /** + * Replaces any stdclass that only has the type property with type integer. + */ + public function postProcess() { + foreach ($this->info as $key => $v) { + if (count((array) $v) == 1) { + $this->info[$key] = $v->type; + } elseif (count((array) $v) == 2 && isset($v->allow_null)) { + $this->info[$key] = -$v->type; + } + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/lib/php/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php new file mode 100644 index 0000000..c05668a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php @@ -0,0 +1,44 @@ +<?php + +/** + * Converts HTMLPurifier_ConfigSchema_Interchange to our runtime + * representation used to perform checks on user configuration. + */ +class HTMLPurifier_ConfigSchema_Builder_ConfigSchema +{ + + public function build($interchange) { + $schema = new HTMLPurifier_ConfigSchema(); + foreach ($interchange->directives as $d) { + $schema->add( + $d->id->key, + $d->default, + $d->type, + $d->typeAllowsNull + ); + if ($d->allowed !== null) { + $schema->addAllowedValues( + $d->id->key, + $d->allowed + ); + } + foreach ($d->aliases as $alias) { + $schema->addAlias( + $alias->key, + $d->id->key + ); + } + if ($d->valueAliases !== null) { + $schema->addValueAliases( + $d->id->key, + $d->valueAliases + ); + } + } + $schema->postProcess(); + return $schema; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Builder/Xml.php b/lib/php/HTMLPurifier/ConfigSchema/Builder/Xml.php new file mode 100644 index 0000000..244561a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Builder/Xml.php @@ -0,0 +1,106 @@ +<?php + +/** + * Converts HTMLPurifier_ConfigSchema_Interchange to an XML format, + * which can be further processed to generate documentation. + */ +class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter +{ + + protected $interchange; + private $namespace; + + protected function writeHTMLDiv($html) { + $this->startElement('div'); + + $purifier = HTMLPurifier::getInstance(); + $html = $purifier->purify($html); + $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); + $this->writeRaw($html); + + $this->endElement(); // div + } + + protected function export($var) { + if ($var === array()) return 'array()'; + return var_export($var, true); + } + + public function build($interchange) { + // global access, only use as last resort + $this->interchange = $interchange; + + $this->setIndent(true); + $this->startDocument('1.0', 'UTF-8'); + $this->startElement('configdoc'); + $this->writeElement('title', $interchange->name); + + foreach ($interchange->directives as $directive) { + $this->buildDirective($directive); + } + + if ($this->namespace) $this->endElement(); // namespace + + $this->endElement(); // configdoc + $this->flush(); + } + + public function buildDirective($directive) { + + // Kludge, although I suppose having a notion of a "root namespace" + // certainly makes things look nicer when documentation is built. + // Depends on things being sorted. + if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) { + if ($this->namespace) $this->endElement(); // namespace + $this->namespace = $directive->id->getRootNamespace(); + $this->startElement('namespace'); + $this->writeAttribute('id', $this->namespace); + $this->writeElement('name', $this->namespace); + } + + $this->startElement('directive'); + $this->writeAttribute('id', $directive->id->toString()); + + $this->writeElement('name', $directive->id->getDirective()); + + $this->startElement('aliases'); + foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString()); + $this->endElement(); // aliases + + $this->startElement('constraints'); + if ($directive->version) $this->writeElement('version', $directive->version); + $this->startElement('type'); + if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes'); + $this->text($directive->type); + $this->endElement(); // type + if ($directive->allowed) { + $this->startElement('allowed'); + foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value); + $this->endElement(); // allowed + } + $this->writeElement('default', $this->export($directive->default)); + $this->writeAttribute('xml:space', 'preserve'); + if ($directive->external) { + $this->startElement('external'); + foreach ($directive->external as $project) $this->writeElement('project', $project); + $this->endElement(); + } + $this->endElement(); // constraints + + if ($directive->deprecatedVersion) { + $this->startElement('deprecated'); + $this->writeElement('version', $directive->deprecatedVersion); + $this->writeElement('use', $directive->deprecatedUse->toString()); + $this->endElement(); // deprecated + } + + $this->startElement('description'); + $this->writeHTMLDiv($directive->description); + $this->endElement(); // description + + $this->endElement(); // directive + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Exception.php b/lib/php/HTMLPurifier/ConfigSchema/Exception.php new file mode 100644 index 0000000..2671516 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Exception.php @@ -0,0 +1,11 @@ +<?php + +/** + * Exceptions related to configuration schema + */ +class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception +{ + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Interchange.php b/lib/php/HTMLPurifier/ConfigSchema/Interchange.php new file mode 100644 index 0000000..91a5aa7 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Interchange.php @@ -0,0 +1,42 @@ +<?php + +/** + * Generic schema interchange format that can be converted to a runtime + * representation (HTMLPurifier_ConfigSchema) or HTML documentation. Members + * are completely validated. + */ +class HTMLPurifier_ConfigSchema_Interchange +{ + + /** + * Name of the application this schema is describing. + */ + public $name; + + /** + * Array of Directive ID => array(directive info) + */ + public $directives = array(); + + /** + * Adds a directive array to $directives + */ + public function addDirective($directive) { + if (isset($this->directives[$i = $directive->id->toString()])) { + throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'"); + } + $this->directives[$i] = $directive; + } + + /** + * Convenience function to perform standard validation. Throws exception + * on failed validation. + */ + public function validate() { + $validator = new HTMLPurifier_ConfigSchema_Validator(); + return $validator->validate($this); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/lib/php/HTMLPurifier/ConfigSchema/Interchange/Directive.php new file mode 100644 index 0000000..ac8be0d --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Interchange/Directive.php @@ -0,0 +1,77 @@ +<?php + +/** + * Interchange component class describing configuration directives. + */ +class HTMLPurifier_ConfigSchema_Interchange_Directive +{ + + /** + * ID of directive, instance of HTMLPurifier_ConfigSchema_Interchange_Id. + */ + public $id; + + /** + * String type, e.g. 'integer' or 'istring'. + */ + public $type; + + /** + * Default value, e.g. 3 or 'DefaultVal'. + */ + public $default; + + /** + * HTML description. + */ + public $description; + + /** + * Boolean whether or not null is allowed as a value. + */ + public $typeAllowsNull = false; + + /** + * Lookup table of allowed scalar values, e.g. array('allowed' => true). + * Null if all values are allowed. + */ + public $allowed; + + /** + * List of aliases for the directive, + * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))). + */ + public $aliases = array(); + + /** + * Hash of value aliases, e.g. array('alt' => 'real'). Null if value + * aliasing is disabled (necessary for non-scalar types). + */ + public $valueAliases; + + /** + * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'. + * Null if the directive has always existed. + */ + public $version; + + /** + * ID of directive that supercedes this old directive, is an instance + * of HTMLPurifier_ConfigSchema_Interchange_Id. Null if not deprecated. + */ + public $deprecatedUse; + + /** + * Version of HTML Purifier this directive was deprecated. Null if not + * deprecated. + */ + public $deprecatedVersion; + + /** + * List of external projects this directive depends on, e.g. array('CSSTidy'). + */ + public $external = array(); + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Interchange/Id.php b/lib/php/HTMLPurifier/ConfigSchema/Interchange/Id.php new file mode 100644 index 0000000..b9b3c6f --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Interchange/Id.php @@ -0,0 +1,37 @@ +<?php + +/** + * Represents a directive ID in the interchange format. + */ +class HTMLPurifier_ConfigSchema_Interchange_Id +{ + + public $key; + + public function __construct($key) { + $this->key = $key; + } + + /** + * @warning This is NOT magic, to ensure that people don't abuse SPL and + * cause problems for PHP 5.0 support. + */ + public function toString() { + return $this->key; + } + + public function getRootNamespace() { + return substr($this->key, 0, strpos($this->key, ".")); + } + + public function getDirective() { + return substr($this->key, strpos($this->key, ".") + 1); + } + + public static function make($id) { + return new HTMLPurifier_ConfigSchema_Interchange_Id($id); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/lib/php/HTMLPurifier/ConfigSchema/InterchangeBuilder.php new file mode 100644 index 0000000..785b72c --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/InterchangeBuilder.php @@ -0,0 +1,180 @@ +<?php + +class HTMLPurifier_ConfigSchema_InterchangeBuilder +{ + + /** + * Used for processing DEFAULT, nothing else. + */ + protected $varParser; + + public function __construct($varParser = null) { + $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native(); + } + + public static function buildFromDirectory($dir = null) { + $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); + $interchange = new HTMLPurifier_ConfigSchema_Interchange(); + return $builder->buildDir($interchange, $dir); + } + + public function buildDir($interchange, $dir = null) { + if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema'; + if (file_exists($dir . '/info.ini')) { + $info = parse_ini_file($dir . '/info.ini'); + $interchange->name = $info['name']; + } + + $files = array(); + $dh = opendir($dir); + while (false !== ($file = readdir($dh))) { + if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') { + continue; + } + $files[] = $file; + } + closedir($dh); + + sort($files); + foreach ($files as $file) { + $this->buildFile($interchange, $dir . '/' . $file); + } + + return $interchange; + } + + public function buildFile($interchange, $file) { + $parser = new HTMLPurifier_StringHashParser(); + $this->build( + $interchange, + new HTMLPurifier_StringHash( $parser->parseFile($file) ) + ); + } + + /** + * Builds an interchange object based on a hash. + * @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build + * @param $hash HTMLPurifier_ConfigSchema_StringHash source data + */ + public function build($interchange, $hash) { + if (!$hash instanceof HTMLPurifier_StringHash) { + $hash = new HTMLPurifier_StringHash($hash); + } + if (!isset($hash['ID'])) { + throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID'); + } + if (strpos($hash['ID'], '.') === false) { + if (count($hash) == 2 && isset($hash['DESCRIPTION'])) { + $hash->offsetGet('DESCRIPTION'); // prevent complaining + } else { + throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace'); + } + } else { + $this->buildDirective($interchange, $hash); + } + $this->_findUnused($hash); + } + + public function buildDirective($interchange, $hash) { + $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive(); + + // These are required elements: + $directive->id = $this->id($hash->offsetGet('ID')); + $id = $directive->id->toString(); // convenience + + if (isset($hash['TYPE'])) { + $type = explode('/', $hash->offsetGet('TYPE')); + if (isset($type[1])) $directive->typeAllowsNull = true; + $directive->type = $type[0]; + } else { + throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined"); + } + + if (isset($hash['DEFAULT'])) { + try { + $directive->default = $this->varParser->parse($hash->offsetGet('DEFAULT'), $directive->type, $directive->typeAllowsNull); + } catch (HTMLPurifier_VarParserException $e) { + throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'"); + } + } + + if (isset($hash['DESCRIPTION'])) { + $directive->description = $hash->offsetGet('DESCRIPTION'); + } + + if (isset($hash['ALLOWED'])) { + $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED'))); + } + + if (isset($hash['VALUE-ALIASES'])) { + $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES')); + } + + if (isset($hash['ALIASES'])) { + $raw_aliases = trim($hash->offsetGet('ALIASES')); + $aliases = preg_split('/\s*,\s*/', $raw_aliases); + foreach ($aliases as $alias) { + $directive->aliases[] = $this->id($alias); + } + } + + if (isset($hash['VERSION'])) { + $directive->version = $hash->offsetGet('VERSION'); + } + + if (isset($hash['DEPRECATED-USE'])) { + $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE')); + } + + if (isset($hash['DEPRECATED-VERSION'])) { + $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION'); + } + + if (isset($hash['EXTERNAL'])) { + $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL'))); + } + + $interchange->addDirective($directive); + } + + /** + * Evaluates an array PHP code string without array() wrapper + */ + protected function evalArray($contents) { + return eval('return array('. $contents .');'); + } + + /** + * Converts an array list into a lookup array. + */ + protected function lookup($array) { + $ret = array(); + foreach ($array as $val) $ret[$val] = true; + return $ret; + } + + /** + * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id + * object based on a string Id. + */ + protected function id($id) { + return HTMLPurifier_ConfigSchema_Interchange_Id::make($id); + } + + /** + * Triggers errors for any unused keys passed in the hash; such keys + * may indicate typos, missing values, etc. + * @param $hash Instance of ConfigSchema_StringHash to check. + */ + protected function _findUnused($hash) { + $accessed = $hash->getAccessed(); + foreach ($hash as $k => $v) { + if (!isset($accessed[$k])) { + trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE); + } + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/Validator.php b/lib/php/HTMLPurifier/ConfigSchema/Validator.php new file mode 100644 index 0000000..f374f6a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/Validator.php @@ -0,0 +1,206 @@ +<?php + +/** + * Performs validations on HTMLPurifier_ConfigSchema_Interchange + * + * @note If you see '// handled by InterchangeBuilder', that means a + * design decision in that class would prevent this validation from + * ever being necessary. We have them anyway, however, for + * redundancy. + */ +class HTMLPurifier_ConfigSchema_Validator +{ + + /** + * Easy to access global objects. + */ + protected $interchange, $aliases; + + /** + * Context-stack to provide easy to read error messages. + */ + protected $context = array(); + + /** + * HTMLPurifier_VarParser to test default's type. + */ + protected $parser; + + public function __construct() { + $this->parser = new HTMLPurifier_VarParser(); + } + + /** + * Validates a fully-formed interchange object. Throws an + * HTMLPurifier_ConfigSchema_Exception if there's a problem. + */ + public function validate($interchange) { + $this->interchange = $interchange; + $this->aliases = array(); + // PHP is a bit lax with integer <=> string conversions in + // arrays, so we don't use the identical !== comparison + foreach ($interchange->directives as $i => $directive) { + $id = $directive->id->toString(); + if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'"); + $this->validateDirective($directive); + } + return true; + } + + /** + * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object. + */ + public function validateId($id) { + $id_string = $id->toString(); + $this->context[] = "id '$id_string'"; + if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) { + // handled by InterchangeBuilder + $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id'); + } + // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.) + // we probably should check that it has at least one namespace + $this->with($id, 'key') + ->assertNotEmpty() + ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder + array_pop($this->context); + } + + /** + * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object. + */ + public function validateDirective($d) { + $id = $d->id->toString(); + $this->context[] = "directive '$id'"; + $this->validateId($d->id); + + $this->with($d, 'description') + ->assertNotEmpty(); + + // BEGIN - handled by InterchangeBuilder + $this->with($d, 'type') + ->assertNotEmpty(); + $this->with($d, 'typeAllowsNull') + ->assertIsBool(); + try { + // This also tests validity of $d->type + $this->parser->parse($d->default, $d->type, $d->typeAllowsNull); + } catch (HTMLPurifier_VarParserException $e) { + $this->error('default', 'had error: ' . $e->getMessage()); + } + // END - handled by InterchangeBuilder + + if (!is_null($d->allowed) || !empty($d->valueAliases)) { + // allowed and valueAliases require that we be dealing with + // strings, so check for that early. + $d_int = HTMLPurifier_VarParser::$types[$d->type]; + if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) { + $this->error('type', 'must be a string type when used with allowed or value aliases'); + } + } + + $this->validateDirectiveAllowed($d); + $this->validateDirectiveValueAliases($d); + $this->validateDirectiveAliases($d); + + array_pop($this->context); + } + + /** + * Extra validation if $allowed member variable of + * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + */ + public function validateDirectiveAllowed($d) { + if (is_null($d->allowed)) return; + $this->with($d, 'allowed') + ->assertNotEmpty() + ->assertIsLookup(); // handled by InterchangeBuilder + if (is_string($d->default) && !isset($d->allowed[$d->default])) { + $this->error('default', 'must be an allowed value'); + } + $this->context[] = 'allowed'; + foreach ($d->allowed as $val => $x) { + if (!is_string($val)) $this->error("value $val", 'must be a string'); + } + array_pop($this->context); + } + + /** + * Extra validation if $valueAliases member variable of + * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + */ + public function validateDirectiveValueAliases($d) { + if (is_null($d->valueAliases)) return; + $this->with($d, 'valueAliases') + ->assertIsArray(); // handled by InterchangeBuilder + $this->context[] = 'valueAliases'; + foreach ($d->valueAliases as $alias => $real) { + if (!is_string($alias)) $this->error("alias $alias", 'must be a string'); + if (!is_string($real)) $this->error("alias target $real from alias '$alias'", 'must be a string'); + if ($alias === $real) { + $this->error("alias '$alias'", "must not be an alias to itself"); + } + } + if (!is_null($d->allowed)) { + foreach ($d->valueAliases as $alias => $real) { + if (isset($d->allowed[$alias])) { + $this->error("alias '$alias'", 'must not be an allowed value'); + } elseif (!isset($d->allowed[$real])) { + $this->error("alias '$alias'", 'must be an alias to an allowed value'); + } + } + } + array_pop($this->context); + } + + /** + * Extra validation if $aliases member variable of + * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + */ + public function validateDirectiveAliases($d) { + $this->with($d, 'aliases') + ->assertIsArray(); // handled by InterchangeBuilder + $this->context[] = 'aliases'; + foreach ($d->aliases as $alias) { + $this->validateId($alias); + $s = $alias->toString(); + if (isset($this->interchange->directives[$s])) { + $this->error("alias '$s'", 'collides with another directive'); + } + if (isset($this->aliases[$s])) { + $other_directive = $this->aliases[$s]; + $this->error("alias '$s'", "collides with alias for directive '$other_directive'"); + } + $this->aliases[$s] = $d->id->toString(); + } + array_pop($this->context); + } + + // protected helper functions + + /** + * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom + * for validating simple member variables of objects. + */ + protected function with($obj, $member) { + return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member); + } + + /** + * Emits an error, providing helpful context. + */ + protected function error($target, $msg) { + if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext(); + else $prefix = ucfirst($this->getFormattedContext()); + throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg)); + } + + /** + * Returns a formatted context string. + */ + protected function getFormattedContext() { + return implode(' in ', array_reverse($this->context)); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/lib/php/HTMLPurifier/ConfigSchema/ValidatorAtom.php new file mode 100644 index 0000000..b95aea1 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/ValidatorAtom.php @@ -0,0 +1,66 @@ +<?php + +/** + * Fluent interface for validating the contents of member variables. + * This should be immutable. See HTMLPurifier_ConfigSchema_Validator for + * use-cases. We name this an 'atom' because it's ONLY for validations that + * are independent and usually scalar. + */ +class HTMLPurifier_ConfigSchema_ValidatorAtom +{ + + protected $context, $obj, $member, $contents; + + public function __construct($context, $obj, $member) { + $this->context = $context; + $this->obj = $obj; + $this->member = $member; + $this->contents =& $obj->$member; + } + + public function assertIsString() { + if (!is_string($this->contents)) $this->error('must be a string'); + return $this; + } + + public function assertIsBool() { + if (!is_bool($this->contents)) $this->error('must be a boolean'); + return $this; + } + + public function assertIsArray() { + if (!is_array($this->contents)) $this->error('must be an array'); + return $this; + } + + public function assertNotNull() { + if ($this->contents === null) $this->error('must not be null'); + return $this; + } + + public function assertAlnum() { + $this->assertIsString(); + if (!ctype_alnum($this->contents)) $this->error('must be alphanumeric'); + return $this; + } + + public function assertNotEmpty() { + if (empty($this->contents)) $this->error('must not be empty'); + return $this; + } + + public function assertIsLookup() { + $this->assertIsArray(); + foreach ($this->contents as $v) { + if ($v !== true) $this->error('must be a lookup array'); + } + return $this; + } + + protected function error($msg) { + throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema.ser b/lib/php/HTMLPurifier/ConfigSchema/schema.ser new file mode 100644 index 0000000000000000000000000000000000000000..bbf12f9c3e7392aa8143727d2485f6f9ad1f97e1 GIT binary patch literal 12999 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 literal 0 HcmV?d00001 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt new file mode 100644 index 0000000..0517fed --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt @@ -0,0 +1,8 @@ +Attr.AllowedClasses +TYPE: lookup/null +VERSION: 4.0.0 +DEFAULT: null +--DESCRIPTION-- +List of allowed class values in the class attribute. By default, this is null, +which means all classes are allowed. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt new file mode 100644 index 0000000..249edd6 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt @@ -0,0 +1,12 @@ +Attr.AllowedFrameTargets +TYPE: lookup +DEFAULT: array() +--DESCRIPTION-- +Lookup table of all allowed link frame targets. Some commonly used link +targets include _blank, _self, _parent and _top. Values should be +lowercase, as validation will be done in a case-sensitive manner despite +W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute +so this directive will have no effect in that doctype. XHTML 1.1 does not +enable the Target module by default, you will have to manually enable it +(see the module documentation for more details.) +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt new file mode 100644 index 0000000..9a8fa6a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt @@ -0,0 +1,9 @@ +Attr.AllowedRel +TYPE: lookup +VERSION: 1.6.0 +DEFAULT: array() +--DESCRIPTION-- +List of allowed forward document relationships in the rel attribute. Common +values may be nofollow or print. By default, this is empty, meaning that no +document relationships are allowed. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt new file mode 100644 index 0000000..b017883 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt @@ -0,0 +1,9 @@ +Attr.AllowedRev +TYPE: lookup +VERSION: 1.6.0 +DEFAULT: array() +--DESCRIPTION-- +List of allowed reverse document relationships in the rev attribute. This +attribute is a bit of an edge-case; if you don't know what it is for, stay +away. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt new file mode 100644 index 0000000..e774b82 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt @@ -0,0 +1,19 @@ +Attr.ClassUseCDATA +TYPE: bool/null +DEFAULT: null +VERSION: 4.0.0 +--DESCRIPTION-- +If null, class will auto-detect the doctype and, if matching XHTML 1.1 or +XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise, +it will use a relaxed CDATA definition. If true, the relaxed CDATA definition +is forced; if false, the NMTOKENS definition is forced. To get behavior +of HTML Purifier prior to 4.0.0, set this directive to false. + +Some rational behind the auto-detection: +in previous versions of HTML Purifier, it was assumed that the form of +class was NMTOKENS, as specified by the XHTML Modularization (representing +XHTML 1.1 and XHTML 2.0). The DTDs for HTML 4.01 and XHTML 1.0, however +specify class as CDATA. HTML 5 effectively defines it as CDATA, but +with the additional constraint that each name should be unique (this is not +explicitly outlined in previous specifications). +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt new file mode 100644 index 0000000..533165e --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt @@ -0,0 +1,11 @@ +Attr.DefaultImageAlt +TYPE: string/null +DEFAULT: null +VERSION: 3.2.0 +--DESCRIPTION-- +This is the content of the alt tag of an image if the user had not +previously specified an alt attribute. This applies to all images without +a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which +only applies to invalid images, and overrides in the case of an invalid image. +Default behavior with null is to use the basename of the src tag for the alt. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt new file mode 100644 index 0000000..9eb7e38 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt @@ -0,0 +1,9 @@ +Attr.DefaultInvalidImage +TYPE: string +DEFAULT: '' +--DESCRIPTION-- +This is the default image an img tag will be pointed to if it does not have +a valid src attribute. In future versions, we may allow the image tag to +be removed completely, but due to design issues, this is not possible right +now. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt new file mode 100644 index 0000000..2f17bf4 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt @@ -0,0 +1,8 @@ +Attr.DefaultInvalidImageAlt +TYPE: string +DEFAULT: 'Invalid image' +--DESCRIPTION-- +This is the content of the alt tag of an invalid image if the user had not +previously specified an alt attribute. It has no effect when the image is +valid but there was no alt attribute present. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt new file mode 100644 index 0000000..52654b5 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt @@ -0,0 +1,10 @@ +Attr.DefaultTextDir +TYPE: string +DEFAULT: 'ltr' +--DESCRIPTION-- +Defines the default text direction (ltr or rtl) of the document being +parsed. This generally is the same as the value of the dir attribute in +HTML, or ltr if that is not specified. +--ALLOWED-- +'ltr', 'rtl' +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt new file mode 100644 index 0000000..6440d21 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt @@ -0,0 +1,16 @@ +Attr.EnableID +TYPE: bool +DEFAULT: false +VERSION: 1.2.0 +--DESCRIPTION-- +Allows the ID attribute in HTML. This is disabled by default due to the +fact that without proper configuration user input can easily break the +validation of a webpage by specifying an ID that is already on the +surrounding HTML. If you don't mind throwing caution to the wind, enable +this directive, but I strongly recommend you also consider blacklisting IDs +you use (%Attr.IDBlacklist) or prefixing all user supplied IDs +(%Attr.IDPrefix). When set to true HTML Purifier reverts to the behavior of +pre-1.2.0 versions. +--ALIASES-- +HTML.EnableAttrID +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt new file mode 100644 index 0000000..f31d226 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt @@ -0,0 +1,8 @@ +Attr.ForbiddenClasses +TYPE: lookup +VERSION: 4.0.0 +DEFAULT: array() +--DESCRIPTION-- +List of forbidden class values in the class attribute. By default, this is +empty, which means that no classes are forbidden. See also %Attr.AllowedClasses. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt new file mode 100644 index 0000000..5f2b5e3 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt @@ -0,0 +1,5 @@ +Attr.IDBlacklist +TYPE: list +DEFAULT: array() +DESCRIPTION: Array of IDs not allowed in the document. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt new file mode 100644 index 0000000..6f58245 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt @@ -0,0 +1,9 @@ +Attr.IDBlacklistRegexp +TYPE: string/null +VERSION: 1.6.0 +DEFAULT: NULL +--DESCRIPTION-- +PCRE regular expression to be matched against all IDs. If the expression is +matches, the ID is rejected. Use this with care: may cause significant +degradation. ID matching is done after all other validation. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt new file mode 100644 index 0000000..cc49d43 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt @@ -0,0 +1,12 @@ +Attr.IDPrefix +TYPE: string +VERSION: 1.2.0 +DEFAULT: '' +--DESCRIPTION-- +String to prefix to IDs. If you have no idea what IDs your pages may use, +you may opt to simply add a prefix to all user-submitted ID attributes so +that they are still usable, but will not conflict with core page IDs. +Example: setting the directive to 'user_' will result in a user submitted +'foo' to become 'user_foo' Be sure to set %HTML.EnableAttrID to true +before using this. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt new file mode 100644 index 0000000..2c5924a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt @@ -0,0 +1,14 @@ +Attr.IDPrefixLocal +TYPE: string +VERSION: 1.2.0 +DEFAULT: '' +--DESCRIPTION-- +Temporary prefix for IDs used in conjunction with %Attr.IDPrefix. If you +need to allow multiple sets of user content on web page, you may need to +have a seperate prefix that changes with each iteration. This way, +seperately submitted user content displayed on the same page doesn't +clobber each other. Ideal values are unique identifiers for the content it +represents (i.e. the id of the row in the database). Be sure to add a +seperator (like an underscore) at the end. Warning: this directive will +not work unless %Attr.IDPrefix is set to a non-empty value! +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt new file mode 100644 index 0000000..d5caa1b --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt @@ -0,0 +1,31 @@ +AutoFormat.AutoParagraph +TYPE: bool +VERSION: 2.0.1 +DEFAULT: false +--DESCRIPTION-- + +<p> + This directive turns on auto-paragraphing, where double newlines are + converted in to paragraphs whenever possible. Auto-paragraphing: +</p> +<ul> + <li>Always applies to inline elements or text in the root node,</li> + <li>Applies to inline elements or text with double newlines in nodes + that allow paragraph tags,</li> + <li>Applies to double newlines in paragraph tags</li> +</ul> +<p> + <code>p</code> tags must be allowed for this directive to take effect. + We do not use <code>br</code> tags for paragraphing, as that is + semantically incorrect. +</p> +<p> + To prevent auto-paragraphing as a content-producer, refrain from using + double-newlines except to specify a new paragraph or in contexts where + it has special meaning (whitespace usually has no meaning except in + tags like <code>pre</code>, so this should not be difficult.) To prevent + the paragraphing of inline text adjacent to block elements, wrap them + in <code>div</code> tags (the behavior is slightly different outside of + the root node.) +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt new file mode 100644 index 0000000..2a47648 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt @@ -0,0 +1,12 @@ +AutoFormat.Custom +TYPE: list +VERSION: 2.0.1 +DEFAULT: array() +--DESCRIPTION-- + +<p> + This directive can be used to add custom auto-format injectors. + Specify an array of injector names (class name minus the prefix) + or concrete implementations. Injector class must exist. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt new file mode 100644 index 0000000..663064a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt @@ -0,0 +1,11 @@ +AutoFormat.DisplayLinkURI +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +<p> + This directive turns on the in-text display of URIs in <a> tags, and disables + those links. For example, <a href="http://example.com">example</a> becomes + example (<a>http://example.com</a>). +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt new file mode 100644 index 0000000..3a48ba9 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt @@ -0,0 +1,12 @@ +AutoFormat.Linkify +TYPE: bool +VERSION: 2.0.1 +DEFAULT: false +--DESCRIPTION-- + +<p> + This directive turns on linkification, auto-linking http, ftp and + https URLs. <code>a</code> tags with the <code>href</code> attribute + must be allowed. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt new file mode 100644 index 0000000..db58b13 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt @@ -0,0 +1,12 @@ +AutoFormat.PurifierLinkify.DocURL +TYPE: string +VERSION: 2.0.1 +DEFAULT: '#%s' +ALIASES: AutoFormatParam.PurifierLinkifyDocURL +--DESCRIPTION-- +<p> + Location of configuration documentation to link to, let %s substitute + into the configuration's namespace and directive names sans the percent + sign. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt new file mode 100644 index 0000000..7996488 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt @@ -0,0 +1,12 @@ +AutoFormat.PurifierLinkify +TYPE: bool +VERSION: 2.0.1 +DEFAULT: false +--DESCRIPTION-- + +<p> + Internal auto-formatter that converts configuration directives in + syntax <a>%Namespace.Directive</a> to links. <code>a</code> tags + with the <code>href</code> attribute must be allowed. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt new file mode 100644 index 0000000..35c393b --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt @@ -0,0 +1,11 @@ +AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions +TYPE: lookup +VERSION: 4.0.0 +DEFAULT: array('td' => true, 'th' => true) +--DESCRIPTION-- +<p> + When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp + are enabled, this directive defines what HTML elements should not be + removede if they have only a non-breaking space in them. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt new file mode 100644 index 0000000..ca17eb1 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt @@ -0,0 +1,15 @@ +AutoFormat.RemoveEmpty.RemoveNbsp +TYPE: bool +VERSION: 4.0.0 +DEFAULT: false +--DESCRIPTION-- +<p> + When enabled, HTML Purifier will treat any elements that contain only + non-breaking spaces as well as regular whitespace as empty, and remove + them when %AutoForamt.RemoveEmpty is enabled. +</p> +<p> + See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements + that don't have this behavior applied to them. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt new file mode 100644 index 0000000..34657ba --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt @@ -0,0 +1,46 @@ +AutoFormat.RemoveEmpty +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +<p> + When enabled, HTML Purifier will attempt to remove empty elements that + contribute no semantic information to the document. The following types + of nodes will be removed: +</p> +<ul><li> + Tags with no attributes and no content, and that are not empty + elements (remove <code><a></a></code> but not + <code><br /></code>), and + </li> + <li> + Tags with no content, except for:<ul> + <li>The <code>colgroup</code> element, or</li> + <li> + Elements with the <code>id</code> or <code>name</code> attribute, + when those attributes are permitted on those elements. + </li> + </ul></li> +</ul> +<p> + Please be very careful when using this functionality; while it may not + seem that empty elements contain useful information, they can alter the + layout of a document given appropriate styling. This directive is most + useful when you are processing machine-generated HTML, please avoid using + it on regular user HTML. +</p> +<p> + Elements that contain only whitespace will be treated as empty. Non-breaking + spaces, however, do not count as whitespace. See + %AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior. +</p> +<p> + This algorithm is not perfect; you may still notice some empty tags, + particularly if a node had elements, but those elements were later removed + because they were not permitted in that context, or tags that, after + being auto-closed by another tag, where empty. This is for safety reasons + to prevent clever code from breaking validation. The general rule of thumb: + if a tag looked empty on the way in, it will get removed; if HTML Purifier + made it empty, it will stay. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt new file mode 100644 index 0000000..b324608 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt @@ -0,0 +1,8 @@ +CSS.AllowImportant +TYPE: bool +DEFAULT: false +VERSION: 3.1.0 +--DESCRIPTION-- +This parameter determines whether or not !important cascade modifiers should +be allowed in user CSS. If false, !important will stripped. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt new file mode 100644 index 0000000..748be0e --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt @@ -0,0 +1,11 @@ +CSS.AllowTricky +TYPE: bool +DEFAULT: false +VERSION: 3.1.0 +--DESCRIPTION-- +This parameter determines whether or not to allow "tricky" CSS properties and +values. Tricky CSS properties/values can drastically modify page layout or +be used for deceptive practices but do not directly constitute a security risk. +For example, <code>display:none;</code> is considered a tricky property that +will only be allowed if this directive is set to true. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt new file mode 100644 index 0000000..460112e --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt @@ -0,0 +1,18 @@ +CSS.AllowedProperties +TYPE: lookup/null +VERSION: 3.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + If HTML Purifier's style attributes set is unsatisfactory for your needs, + you can overload it with your own list of tags to allow. Note that this + method is subtractive: it does its job by taking away from HTML Purifier + usual feature set, so you cannot add an attribute that HTML Purifier never + supported in the first place. +</p> +<p> + <strong>Warning:</strong> If another directive conflicts with the + elements here, <em>that</em> directive will win and override. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt new file mode 100644 index 0000000..5cb7dda --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt @@ -0,0 +1,11 @@ +CSS.DefinitionRev +TYPE: int +VERSION: 2.0.0 +DEFAULT: 1 +--DESCRIPTION-- + +<p> + Revision identifier for your custom definition. See + %HTML.DefinitionRev for details. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt new file mode 100644 index 0000000..7a32914 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt @@ -0,0 +1,16 @@ +CSS.MaxImgLength +TYPE: string/null +DEFAULT: '1200px' +VERSION: 3.1.1 +--DESCRIPTION-- +<p> + This parameter sets the maximum allowed length on <code>img</code> tags, + effectively the <code>width</code> and <code>height</code> properties. + Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is + in place to prevent imagecrash attacks, disable with null at your own risk. + This directive is similar to %HTML.MaxImgLength, and both should be + concurrently edited, although there are + subtle differences in the input format (the CSS max is a number with + a unit). +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt new file mode 100644 index 0000000..148eedb --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt @@ -0,0 +1,10 @@ +CSS.Proprietary +TYPE: bool +VERSION: 3.0.0 +DEFAULT: false +--DESCRIPTION-- + +<p> + Whether or not to allow safe, proprietary CSS values. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt new file mode 100644 index 0000000..c486724 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt @@ -0,0 +1,14 @@ +Cache.DefinitionImpl +TYPE: string/null +VERSION: 2.0.0 +DEFAULT: 'Serializer' +--DESCRIPTION-- + +This directive defines which method to use when caching definitions, +the complex data-type that makes HTML Purifier tick. Set to null +to disable caching (not recommended, as you will see a definite +performance degradation). + +--ALIASES-- +Core.DefinitionCache +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt new file mode 100644 index 0000000..5403650 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt @@ -0,0 +1,13 @@ +Cache.SerializerPath +TYPE: string/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + Absolute path with no trailing slash to store serialized definitions in. + Default is within the + HTML Purifier library inside DefinitionCache/Serializer. This + path must be writable by the webserver. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt new file mode 100644 index 0000000..568cbf3 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt @@ -0,0 +1,18 @@ +Core.AggressivelyFixLt +TYPE: bool +VERSION: 2.1.0 +DEFAULT: true +--DESCRIPTION-- +<p> + This directive enables aggressive pre-filter fixes HTML Purifier can + perform in order to ensure that open angled-brackets do not get killed + during parsing stage. Enabling this will result in two preg_replace_callback + calls and at least two preg_replace calls for every HTML document parsed; + if your users make very well-formed HTML, you can set this directive false. + This has no effect when DirectLex is used. +</p> +<p> + <strong>Notice:</strong> This directive's default turned from false to true + in HTML Purifier 3.2.0. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt new file mode 100644 index 0000000..d731791 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt @@ -0,0 +1,12 @@ +Core.CollectErrors +TYPE: bool +VERSION: 2.0.0 +DEFAULT: false +--DESCRIPTION-- + +Whether or not to collect errors found while filtering the document. This +is a useful way to give feedback to your users. <strong>Warning:</strong> +Currently this feature is very patchy and experimental, with lots of +possible error messages not yet implemented. It will not cause any +problems, but it may not help your users either. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt new file mode 100644 index 0000000..08b381d --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt @@ -0,0 +1,28 @@ +Core.ColorKeywords +TYPE: hash +VERSION: 2.0.0 +--DEFAULT-- +array ( + 'maroon' => '#800000', + 'red' => '#FF0000', + 'orange' => '#FFA500', + 'yellow' => '#FFFF00', + 'olive' => '#808000', + 'purple' => '#800080', + 'fuchsia' => '#FF00FF', + 'white' => '#FFFFFF', + 'lime' => '#00FF00', + 'green' => '#008000', + 'navy' => '#000080', + 'blue' => '#0000FF', + 'aqua' => '#00FFFF', + 'teal' => '#008080', + 'black' => '#000000', + 'silver' => '#C0C0C0', + 'gray' => '#808080', +) +--DESCRIPTION-- + +Lookup array of color names to six digit hexadecimal number corresponding +to color, with preceding hash mark. Used when parsing colors. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt new file mode 100644 index 0000000..64b114f --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt @@ -0,0 +1,14 @@ +Core.ConvertDocumentToFragment +TYPE: bool +DEFAULT: true +--DESCRIPTION-- + +This parameter determines whether or not the filter should convert +input that is a full document with html and body tags to a fragment +of just the contents of a body tag. This parameter is simply something +HTML Purifier can do during an edge-case: for most inputs, this +processing is not necessary. + +--ALIASES-- +Core.AcceptFullDocuments +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt new file mode 100644 index 0000000..36f16e0 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt @@ -0,0 +1,17 @@ +Core.DirectLexLineNumberSyncInterval +TYPE: int +VERSION: 2.0.0 +DEFAULT: 0 +--DESCRIPTION-- + +<p> + Specifies the number of tokens the DirectLex line number tracking + implementations should process before attempting to resyncronize the + current line count by manually counting all previous new-lines. When + at 0, this functionality is disabled. Lower values will decrease + performance, and this is only strictly necessary if the counting + algorithm is buggy (in which case you should report it as a bug). + This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is + not being used. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt new file mode 100644 index 0000000..8bfb47c --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt @@ -0,0 +1,15 @@ +Core.Encoding +TYPE: istring +DEFAULT: 'utf-8' +--DESCRIPTION-- +If for some reason you are unable to convert all webpages to UTF-8, you can +use this directive as a stop-gap compatibility change to let HTML Purifier +deal with non UTF-8 input. This technique has notable deficiencies: +absolutely no characters outside of the selected character encoding will be +preserved, not even the ones that have been ampersand escaped (this is due +to a UTF-8 specific <em>feature</em> that automatically resolves all +entities), making it pretty useless for anything except the most I18N-blind +applications, although %Core.EscapeNonASCIICharacters offers fixes this +trouble with another tradeoff. This directive only accepts ISO-8859-1 if +iconv is not enabled. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt new file mode 100644 index 0000000..4d5b505 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt @@ -0,0 +1,10 @@ +Core.EscapeInvalidChildren +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +When true, a child is found that is not allowed in the context of the +parent element will be transformed into text as if it were ASCII. When +false, that element and all internal tags will be dropped, though text will +be preserved. There is no option for dropping the element but preserving +child nodes. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt new file mode 100644 index 0000000..a7a5b24 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt @@ -0,0 +1,7 @@ +Core.EscapeInvalidTags +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +When true, invalid tags will be written back to the document as plain text. +Otherwise, they are silently dropped. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt new file mode 100644 index 0000000..abb4999 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt @@ -0,0 +1,13 @@ +Core.EscapeNonASCIICharacters +TYPE: bool +VERSION: 1.4.0 +DEFAULT: false +--DESCRIPTION-- +This directive overcomes a deficiency in %Core.Encoding by blindly +converting all non-ASCII characters into decimal numeric entities before +converting it to its native encoding. This means that even characters that +can be expressed in the non-UTF-8 encoding will be entity-ized, which can +be a real downer for encodings like Big5. It also assumes that the ASCII +repetoire is available, although this is the case for almost all encodings. +Anyway, use UTF-8! +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt new file mode 100644 index 0000000..915391e --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt @@ -0,0 +1,19 @@ +Core.HiddenElements +TYPE: lookup +--DEFAULT-- +array ( + 'script' => true, + 'style' => true, +) +--DESCRIPTION-- + +<p> + This directive is a lookup array of elements which should have their + contents removed when they are not allowed by the HTML definition. + For example, the contents of a <code>script</code> tag are not + normally shown in a document, so if script tags are to be removed, + their contents should be removed to. This is opposed to a <code>b</code> + tag, which defines some presentational changes but does not hide its + contents. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.Language.txt new file mode 100644 index 0000000..233fca1 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.Language.txt @@ -0,0 +1,10 @@ +Core.Language +TYPE: string +VERSION: 2.0.0 +DEFAULT: 'en' +--DESCRIPTION-- + +ISO 639 language code for localizable things in HTML Purifier to use, +which is mainly error reporting. There is currently only an English (en) +translation, so this directive is currently useless. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt new file mode 100644 index 0000000..8983e2c --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt @@ -0,0 +1,34 @@ +Core.LexerImpl +TYPE: mixed/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + This parameter determines what lexer implementation can be used. The + valid values are: +</p> +<dl> + <dt><em>null</em></dt> + <dd> + Recommended, the lexer implementation will be auto-detected based on + your PHP-version and configuration. + </dd> + <dt><em>string</em> lexer identifier</dt> + <dd> + This is a slim way of manually overridding the implementation. + Currently recognized values are: DOMLex (the default PHP5 +implementation) + and DirectLex (the default PHP4 implementation). Only use this if + you know what you are doing: usually, the auto-detection will + manage things for cases you aren't even aware of. + </dd> + <dt><em>object</em> lexer instance</dt> + <dd> + Super-advanced: you can specify your own, custom, implementation that + implements the interface defined by <code>HTMLPurifier_Lexer</code>. + I may remove this option simply because I don't expect anyone + to use it. + </dd> +</dl> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt new file mode 100644 index 0000000..eb841a7 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt @@ -0,0 +1,16 @@ +Core.MaintainLineNumbers +TYPE: bool/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + If true, HTML Purifier will add line number information to all tokens. + This is useful when error reporting is turned on, but can result in + significant performance degradation and should not be used when + unnecessary. This directive must be used with the DirectLex lexer, + as the DOMLex lexer does not (yet) support this functionality. + If the value is null, an appropriate value will be selected based + on other configuration. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt new file mode 100644 index 0000000..4070c2a --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt @@ -0,0 +1,12 @@ +Core.RemoveInvalidImg +TYPE: bool +DEFAULT: true +VERSION: 1.3.0 +--DESCRIPTION-- + +<p> + This directive enables pre-emptive URI checking in <code>img</code> + tags, as the attribute validation strategy is not authorized to + remove elements from the document. Revert to pre-1.3.0 behavior by setting to false. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt new file mode 100644 index 0000000..a4cd966 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt @@ -0,0 +1,12 @@ +Core.RemoveScriptContents +TYPE: bool/null +DEFAULT: NULL +VERSION: 2.0.0 +DEPRECATED-VERSION: 2.1.0 +DEPRECATED-USE: Core.HiddenElements +--DESCRIPTION-- +<p> + This directive enables HTML Purifier to remove not only script tags + but all of their contents. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt new file mode 100644 index 0000000..3db50ef --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt @@ -0,0 +1,11 @@ +Filter.Custom +TYPE: list +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +<p> + This directive can be used to add custom filters; it is nearly the + equivalent of the now deprecated <code>HTMLPurifier->addFilter()</code> + method. Specify an array of concrete implementations. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt new file mode 100644 index 0000000..16829bc --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt @@ -0,0 +1,14 @@ +Filter.ExtractStyleBlocks.Escaping +TYPE: bool +VERSION: 3.0.0 +DEFAULT: true +ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping +--DESCRIPTION-- + +<p> + Whether or not to escape the dangerous characters <, > and & + as \3C, \3E and \26, respectively. This is can be safely set to false + if the contents of StyleBlocks will be placed in an external stylesheet, + where there is no risk of it being interpreted as HTML. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt new file mode 100644 index 0000000..7f95f54 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt @@ -0,0 +1,29 @@ +Filter.ExtractStyleBlocks.Scope +TYPE: string/null +VERSION: 3.0.0 +DEFAULT: NULL +ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope +--DESCRIPTION-- + +<p> + If you would like users to be able to define external stylesheets, but + only allow them to specify CSS declarations for a specific node and + prevent them from fiddling with other elements, use this directive. + It accepts any valid CSS selector, and will prepend this to any + CSS declaration extracted from the document. For example, if this + directive is set to <code>#user-content</code> and a user uses the + selector <code>a:hover</code>, the final selector will be + <code>#user-content a:hover</code>. +</p> +<p> + The comma shorthand may be used; consider the above example, with + <code>#user-content, #user-content2</code>, the final selector will + be <code>#user-content a:hover, #user-content2 a:hover</code>. +</p> +<p> + <strong>Warning:</strong> It is possible for users to bypass this measure + using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML + Purifier, and I am working to get it fixed. Until then, HTML Purifier + performs a basic check to prevent this. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt new file mode 100644 index 0000000..6c231b2 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt @@ -0,0 +1,16 @@ +Filter.ExtractStyleBlocks.TidyImpl +TYPE: mixed/null +VERSION: 3.1.0 +DEFAULT: NULL +ALIASES: FilterParam.ExtractStyleBlocksTidyImpl +--DESCRIPTION-- +<p> + If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code> + class to use for internal cleaning. This will usually be good enough. +</p> +<p> + However, for trusted user input, you can set this to <code>false</code> to + disable cleaning. In addition, you can supply your own concrete implementation + of Tidy's interface to use, although I don't know why you'd want to do that. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt new file mode 100644 index 0000000..078d087 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt @@ -0,0 +1,74 @@ +Filter.ExtractStyleBlocks +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +EXTERNAL: CSSTidy +--DESCRIPTION-- +<p> + This directive turns on the style block extraction filter, which removes + <code>style</code> blocks from input HTML, cleans them up with CSSTidy, + and places them in the <code>StyleBlocks</code> context variable, for further + use by you, usually to be placed in an external stylesheet, or a + <code>style</code> block in the <code>head</code> of your document. +</p> +<p> + Sample usage: +</p> +<pre><![CDATA[ +<?php + header('Content-type: text/html; charset=utf-8'); + echo '<?xml version="1.0" encoding="UTF-8"?>'; +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> +<head> + <title>Filter.ExtractStyleBlocks</title> +<?php + require_once '/path/to/library/HTMLPurifier.auto.php'; + require_once '/path/to/csstidy.class.php'; + + $dirty = '<style>body {color:#F00;}</style> Some text'; + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Filter', 'ExtractStyleBlocks', true); + $purifier = new HTMLPurifier($config); + + $html = $purifier->purify($dirty); + + // This implementation writes the stylesheets to the styles/ directory. + // You can also echo the styles inside the document, but it's a bit + // more difficult to make sure they get interpreted properly by + // browsers; try the usual CSS armoring techniques. + $styles = $purifier->context->get('StyleBlocks'); + $dir = 'styles/'; + if (!is_dir($dir)) mkdir($dir); + $hash = sha1($_GET['html']); + foreach ($styles as $i => $style) { + file_put_contents($name = $dir . $hash . "_$i"); + echo '<link rel="stylesheet" type="text/css" href="'.$name.'" />'; + } +?> +</head> +<body> + <div> + <?php echo $html; ?> + </div> +</b]]><![CDATA[ody> +</html> +]]></pre> +<p> + <strong>Warning:</strong> It is possible for a user to mount an + imagecrash attack using this CSS. Counter-measures are difficult; + it is not simply enough to limit the range of CSS lengths (using + relative lengths with many nesting levels allows for large values + to be attained without actually specifying them in the stylesheet), + and the flexible nature of selectors makes it difficult to selectively + disable lengths on image tags (HTML Purifier, however, does disable + CSS width and height in inline styling). There are probably two effective + counter measures: an explicit width and height set to auto in all + images in your document (unlikely) or the disabling of width and + height (somewhat reasonable). Whether or not these measures should be + used is left to the reader. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt new file mode 100644 index 0000000..7fa6536 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -0,0 +1,11 @@ +Filter.YouTube +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +<p> + This directive enables YouTube video embedding in HTML Purifier. Check + <a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document + on embedding videos</a> for more information on what this filter does. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt new file mode 100644 index 0000000..3e231d2 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -0,0 +1,22 @@ +HTML.Allowed +TYPE: itext/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + This is a convenience directive that rolls the functionality of + %HTML.AllowedElements and %HTML.AllowedAttributes into one directive. + Specify elements and attributes that are allowed using: + <code>element1[attr1|attr2],element2...</code>. You can also use + newlines instead of commas to separate elements. +</p> +<p> + <strong>Warning</strong>: + All of the constraints on the component directives are still enforced. + The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code> + whitelist: directly copy-pasting it here will probably result in + broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes + are set, this directive has no effect. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt new file mode 100644 index 0000000..fcf093f --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt @@ -0,0 +1,19 @@ +HTML.AllowedAttributes +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + If HTML Purifier's attribute set is unsatisfactory, overload it! + The syntax is "tag.attr" or "*.attr" for the global attributes + (style, id, class, dir, lang, xml:lang). +</p> +<p> + <strong>Warning:</strong> If another directive conflicts with the + elements here, <em>that</em> directive will win and override. For + example, %HTML.EnableAttrID will take precedence over *.id in this + directive. You must set that directive to true before you can use + IDs at all. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt new file mode 100644 index 0000000..888d558 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -0,0 +1,18 @@ +HTML.AllowedElements +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- +<p> + If HTML Purifier's tag set is unsatisfactory for your needs, you + can overload it with your own list of tags to allow. Note that this + method is subtractive: it does its job by taking away from HTML Purifier + usual feature set, so you cannot add a tag that HTML Purifier never + supported in the first place (like embed, form or head). If you + change this, you probably also want to change %HTML.AllowedAttributes. +</p> +<p> + <strong>Warning:</strong> If another directive conflicts with the + elements here, <em>that</em> directive will win and override. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt new file mode 100644 index 0000000..5a59a55 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt @@ -0,0 +1,20 @@ +HTML.AllowedModules +TYPE: lookup/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + A doctype comes with a set of usual modules to use. Without having + to mucking about with the doctypes, you can quickly activate or + disable these modules by specifying which modules you wish to allow + with this directive. This is most useful for unit testing specific + modules, although end users may find it useful for their own ends. +</p> +<p> + If you specify a module that does not exist, the manager will silently + fail to use it, so be careful! User-defined modules are not affected + by this directive. Modules defined in %HTML.CoreModules are not + affected by this directive. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt new file mode 100644 index 0000000..151fb7b --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt @@ -0,0 +1,11 @@ +HTML.Attr.Name.UseCDATA +TYPE: bool +DEFAULT: false +VERSION: 4.0.0 +--DESCRIPTION-- +The W3C specification DTD defines the name attribute to be CDATA, not ID, due +to limitations of DTD. In certain documents, this relaxed behavior is desired, +whether it is to specify duplicate names, or to specify names that would be +illegal IDs (for example, names that begin with a digit.) Set this configuration +directive to true to use the relaxed parsing rules. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt new file mode 100644 index 0000000..45ae469 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt @@ -0,0 +1,18 @@ +HTML.BlockWrapper +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'p' +--DESCRIPTION-- + +<p> + String name of element to wrap inline elements that are inside a block + context. This only occurs in the children of blockquote in strict mode. +</p> +<p> + Example: by default value, + <code><blockquote>Foo</blockquote></code> would become + <code><blockquote><p>Foo</p></blockquote></code>. + The <code><p></code> tags can be replaced with whatever you desire, + as long as it is a block level element. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt new file mode 100644 index 0000000..5246188 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt @@ -0,0 +1,23 @@ +HTML.CoreModules +TYPE: lookup +VERSION: 2.0.0 +--DEFAULT-- +array ( + 'Structure' => true, + 'Text' => true, + 'Hypertext' => true, + 'List' => true, + 'NonXMLCommonAttributes' => true, + 'XMLCommonAttributes' => true, + 'CommonAttributes' => true, +) +--DESCRIPTION-- + +<p> + Certain modularized doctypes (XHTML, namely), have certain modules + that must be included for the doctype to be an conforming document + type: put those modules here. By default, XHTML's core modules + are used. You can set this to a blank array to disable core module + protection, but this is not recommended. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt new file mode 100644 index 0000000..a64e3d7 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt @@ -0,0 +1,9 @@ +HTML.CustomDoctype +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +A custom doctype for power-users who defined there own document +type. This directive only applies when %HTML.Doctype is blank. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt new file mode 100644 index 0000000..103db75 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt @@ -0,0 +1,33 @@ +HTML.DefinitionID +TYPE: string/null +DEFAULT: NULL +VERSION: 2.0.0 +--DESCRIPTION-- + +<p> + Unique identifier for a custom-built HTML definition. If you edit + the raw version of the HTMLDefinition, introducing changes that the + configuration object does not reflect, you must specify this variable. + If you change your custom edits, you should change this directive, or + clear your cache. Example: +</p> +<pre> +$config = HTMLPurifier_Config::createDefault(); +$config->set('HTML', 'DefinitionID', '1'); +$def = $config->getHTMLDefinition(); +$def->addAttribute('a', 'tabindex', 'Number'); +</pre> +<p> + In the above example, the configuration is still at the defaults, but + using the advanced API, an extra attribute has been added. The + configuration object normally has no way of knowing that this change + has taken place, so it needs an extra directive: %HTML.DefinitionID. + If someone else attempts to use the default configuration, these two + pieces of code will not clobber each other in the cache, since one has + an extra directive attached to it. +</p> +<p> + You <em>must</em> specify a value to this directive to use the + advanced API features. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt new file mode 100644 index 0000000..229ae02 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt @@ -0,0 +1,16 @@ +HTML.DefinitionRev +TYPE: int +VERSION: 2.0.0 +DEFAULT: 1 +--DESCRIPTION-- + +<p> + Revision identifier for your custom definition specified in + %HTML.DefinitionID. This serves the same purpose: uniquely identifying + your custom definition, but this one does so in a chronological + context: revision 3 is more up-to-date then revision 2. Thus, when + this gets incremented, the cache handling is smart enough to clean + up any older revisions of your definition as well as flush the + cache. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt new file mode 100644 index 0000000..9dab497 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt @@ -0,0 +1,11 @@ +HTML.Doctype +TYPE: string/null +DEFAULT: NULL +--DESCRIPTION-- +Doctype to use during filtering. Technically speaking this is not actually +a doctype (as it does not identify a corresponding DTD), but we are using +this name for sake of simplicity. When non-blank, this will override any +older directives like %HTML.XHTML or %HTML.Strict. +--ALLOWED-- +'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1' +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt new file mode 100644 index 0000000..57358f9 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt @@ -0,0 +1,21 @@ +HTML.ForbiddenAttributes +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +<p> + While this directive is similar to %HTML.AllowedAttributes, for + forwards-compatibility with XML, this attribute has a different syntax. Instead of + <code>tag.attr</code>, use <code>tag@attr</code>. To disallow <code>href</code> + attributes in <code>a</code> tags, set this directive to + <code>a@href</code>. You can also disallow an attribute globally with + <code>attr</code> or <code>*@attr</code> (either syntax is fine; the latter + is provided for consistency with %HTML.AllowedAttributes). +</p> +<p> + <strong>Warning:</strong> This directive complements %HTML.ForbiddenElements, + accordingly, check + out that directive for a discussion of why you + should think twice before using this directive. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt new file mode 100644 index 0000000..93a53e1 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt @@ -0,0 +1,20 @@ +HTML.ForbiddenElements +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +<p> + This was, perhaps, the most requested feature ever in HTML + Purifier. Please don't abuse it! This is the logical inverse of + %HTML.AllowedElements, and it will override that directive, or any + other directive. +</p> +<p> + If possible, %HTML.Allowed is recommended over this directive, because it + can sometimes be difficult to tell whether or not you've forbidden all of + the behavior you would like to disallow. If you forbid <code>img</code> + with the expectation of preventing images on your site, you'll be in for + a nasty surprise when people start using the <code>background-image</code> + CSS property. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt new file mode 100644 index 0000000..e424c38 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt @@ -0,0 +1,14 @@ +HTML.MaxImgLength +TYPE: int/null +DEFAULT: 1200 +VERSION: 3.1.1 +--DESCRIPTION-- +<p> + This directive controls the maximum number of pixels in the width and + height attributes in <code>img</code> tags. This is + in place to prevent imagecrash attacks, disable with null at your own risk. + This directive is similar to %CSS.MaxImgLength, and both should be + concurrently edited, although there are + subtle differences in the input format (the HTML max is an integer). +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt new file mode 100644 index 0000000..62e8e16 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt @@ -0,0 +1,12 @@ +HTML.Parent +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'div' +--DESCRIPTION-- + +<p> + String name of element that HTML fragment passed to library will be + inserted in. An interesting variation would be using span as the + parent element, meaning that only inline tags would be allowed. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt new file mode 100644 index 0000000..dfb7204 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt @@ -0,0 +1,12 @@ +HTML.Proprietary +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +<p> + Whether or not to allow proprietary elements and attributes in your + documents, as per <code>HTMLPurifier_HTMLModule_Proprietary</code>. + <strong>Warning:</strong> This can cause your documents to stop + validating! +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt new file mode 100644 index 0000000..f635a68 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt @@ -0,0 +1,14 @@ +HTML.SafeEmbed +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +<p> + Whether or not to permit embed tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to embed tags. Embed is a proprietary + element and will cause your website to stop validating. You probably want + to enable this with %HTML.SafeObject. + <strong>Highly experimental.</strong> +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt new file mode 100644 index 0000000..32967b8 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt @@ -0,0 +1,14 @@ +HTML.SafeObject +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +<p> + Whether or not to permit object tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to object tags. You may also want to + enable %HTML.SafeEmbed for maximum interoperability with Internet Explorer, + although embed tags will cause your website to stop validating. + <strong>Highly experimental.</strong> +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt new file mode 100644 index 0000000..a8b1de5 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt @@ -0,0 +1,9 @@ +HTML.Strict +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +DEPRECATED-VERSION: 1.7.0 +DEPRECATED-USE: HTML.Doctype +--DESCRIPTION-- +Determines whether or not to use Transitional (loose) or Strict rulesets. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt new file mode 100644 index 0000000..b4c271b --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt @@ -0,0 +1,8 @@ +HTML.TidyAdd +TYPE: lookup +VERSION: 2.0.0 +DEFAULT: array() +--DESCRIPTION-- + +Fixes to add to the default set of Tidy fixes as per your level. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt new file mode 100644 index 0000000..4186ccd --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt @@ -0,0 +1,24 @@ +HTML.TidyLevel +TYPE: string +VERSION: 2.0.0 +DEFAULT: 'medium' +--DESCRIPTION-- + +<p>General level of cleanliness the Tidy module should enforce. +There are four allowed values:</p> +<dl> + <dt>none</dt> + <dd>No extra tidying should be done</dd> + <dt>light</dt> + <dd>Only fix elements that would be discarded otherwise due to + lack of support in doctype</dd> + <dt>medium</dt> + <dd>Enforce best practices</dd> + <dt>heavy</dt> + <dd>Transform all deprecated elements and attributes to standards + compliant equivalents</dd> +</dl> + +--ALLOWED-- +'none', 'light', 'medium', 'heavy' +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt new file mode 100644 index 0000000..996762b --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt @@ -0,0 +1,8 @@ +HTML.TidyRemove +TYPE: lookup +VERSION: 2.0.0 +DEFAULT: array() +--DESCRIPTION-- + +Fixes to remove from the default set of Tidy fixes as per your level. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt new file mode 100644 index 0000000..89133b1 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt @@ -0,0 +1,8 @@ +HTML.Trusted +TYPE: bool +VERSION: 2.0.0 +DEFAULT: false +--DESCRIPTION-- +Indicates whether or not the user input is trusted or not. If the input is +trusted, a more expansive set of allowed tags and attributes will be used. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt new file mode 100644 index 0000000..2a47e38 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt @@ -0,0 +1,11 @@ +HTML.XHTML +TYPE: bool +DEFAULT: true +VERSION: 1.1.0 +DEPRECATED-VERSION: 1.7.0 +DEPRECATED-USE: HTML.Doctype +--DESCRIPTION-- +Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor. +--ALIASES-- +Core.XHTML +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt new file mode 100644 index 0000000..08921fd --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt @@ -0,0 +1,10 @@ +Output.CommentScriptContents +TYPE: bool +VERSION: 2.0.0 +DEFAULT: true +--DESCRIPTION-- +Determines whether or not HTML Purifier should attempt to fix up the +contents of script tags for legacy browsers with comments. +--ALIASES-- +Core.CommentScriptContents +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt new file mode 100644 index 0000000..79f8ad8 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt @@ -0,0 +1,13 @@ +Output.Newline +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + Newline string to format final output with. If left null, HTML Purifier + will auto-detect the default newline type of the system and use that; + you can manually override it here. Remember, \r\n is Windows, \r + is Mac, and \n is Unix. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt new file mode 100644 index 0000000..232b023 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt @@ -0,0 +1,14 @@ +Output.SortAttr +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +<p> + If true, HTML Purifier will sort attributes by name before writing them back + to the document, converting a tag like: <code><el b="" a="" c="" /></code> + to <code><el a="" b="" c="" /></code>. This is a workaround for + a bug in FCKeditor which causes it to swap attributes order, adding noise + to text diffs. If you're not seeing this bug, chances are, you don't need + this directive. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt new file mode 100644 index 0000000..06bab00 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt @@ -0,0 +1,25 @@ +Output.TidyFormat +TYPE: bool +VERSION: 1.1.1 +DEFAULT: false +--DESCRIPTION-- +<p> + Determines whether or not to run Tidy on the final output for pretty + formatting reasons, such as indentation and wrap. +</p> +<p> + This can greatly improve readability for editors who are hand-editing + the HTML, but is by no means necessary as HTML Purifier has already + fixed all major errors the HTML may have had. Tidy is a non-default + extension, and this directive will silently fail if Tidy is not + available. +</p> +<p> + If you are looking to make the overall look of your page's source + better, I recommend running Tidy on the entire page rather than just + user-content (after all, the indentation relative to the containing + blocks will be incorrect). +</p> +--ALIASES-- +Core.TidyFormat +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt new file mode 100644 index 0000000..071bc02 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt @@ -0,0 +1,7 @@ +Test.ForceNoIconv +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +When set to true, HTMLPurifier_Encoder will act as if iconv does not exist +and use only pure PHP implementations. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt new file mode 100644 index 0000000..98fdfe9 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -0,0 +1,15 @@ +URI.AllowedSchemes +TYPE: lookup +--DEFAULT-- +array ( + 'http' => true, + 'https' => true, + 'mailto' => true, + 'ftp' => true, + 'nntp' => true, + 'news' => true, +) +--DESCRIPTION-- +Whitelist that defines the schemes that a URI is allowed to have. This +prevents XSS attacks from using pseudo-schemes like javascript or mocha. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Base.txt new file mode 100644 index 0000000..876f068 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Base.txt @@ -0,0 +1,17 @@ +URI.Base +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + The base URI is the URI of the document this purified HTML will be + inserted into. This information is important if HTML Purifier needs + to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute + is on. You may use a non-absolute URI for this value, but behavior + may vary (%URI.MakeAbsolute deals nicely with both absolute and + relative paths, but forwards-compatibility is not guaranteed). + <strong>Warning:</strong> If set, the scheme on this URI + overrides the one specified by %URI.DefaultScheme. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt new file mode 100644 index 0000000..728e378 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt @@ -0,0 +1,10 @@ +URI.DefaultScheme +TYPE: string +DEFAULT: 'http' +--DESCRIPTION-- + +<p> + Defines through what scheme the output will be served, in order to + select the proper object validator when no scheme information is present. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt new file mode 100644 index 0000000..f05312b --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt @@ -0,0 +1,11 @@ +URI.DefinitionID +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + Unique identifier for a custom-built URI definition. If you want + to add custom URIFilters, you must specify this value. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt new file mode 100644 index 0000000..80cfea9 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt @@ -0,0 +1,11 @@ +URI.DefinitionRev +TYPE: int +VERSION: 2.1.0 +DEFAULT: 1 +--DESCRIPTION-- + +<p> + Revision identifier for your custom definition. See + %HTML.DefinitionRev for details. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt new file mode 100644 index 0000000..71ce025 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt @@ -0,0 +1,14 @@ +URI.Disable +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- + +<p> + Disables all URIs in all forms. Not sure why you'd want to do that + (after all, the Internet's founded on the notion of a hyperlink). +</p> + +--ALIASES-- +Attr.DisableURI +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt new file mode 100644 index 0000000..13c122c --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt @@ -0,0 +1,11 @@ +URI.DisableExternal +TYPE: bool +VERSION: 1.2.0 +DEFAULT: false +--DESCRIPTION-- +Disables links to external websites. This is a highly effective anti-spam +and anti-pagerank-leech measure, but comes at a hefty price: nolinks or +images outside of your domain will be allowed. Non-linkified URIs will +still be preserved. If you want to be able to link to subdomains or use +absolute URIs, specify %URI.Host for your website. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt new file mode 100644 index 0000000..abcc1ef --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt @@ -0,0 +1,13 @@ +URI.DisableExternalResources +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- +Disables the embedding of external resources, preventing users from +embedding things like images from other hosts. This prevents access +tracking (good for email viewers), bandwidth leeching, cross-site request +forging, goatse.cx posting, and other nasties, but also results in a loss +of end-user functionality (they can't directly post a pic they posted from +Flickr anymore). Use it if you don't have a robust user-content moderation +team. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt new file mode 100644 index 0000000..51e6ea9 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -0,0 +1,12 @@ +URI.DisableResources +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- + +<p> + Disables embedding resources, essentially meaning no pictures. You can + still link to them though. See %URI.DisableExternalResources for why + this might be a good idea. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Host.txt new file mode 100644 index 0000000..ee83b12 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Host.txt @@ -0,0 +1,19 @@ +URI.Host +TYPE: string/null +VERSION: 1.2.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + Defines the domain name of the server, so we can determine whether or + an absolute URI is from your website or not. Not strictly necessary, + as users should be using relative URIs to reference resources on your + website. It will, however, let you use absolute URIs to link to + subdomains of the domain you post here: i.e. example.com will allow + sub.example.com. However, higher up domains will still be excluded: + if you set %URI.Host to sub.example.com, example.com will be blocked. + <strong>Note:</strong> This directive overrides %URI.Base because + a given page may be on a sub-domain, but you wish HTML Purifier to be + more relaxed and allow some of the parent domains too. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt new file mode 100644 index 0000000..0b6df76 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt @@ -0,0 +1,9 @@ +URI.HostBlacklist +TYPE: list +VERSION: 1.3.0 +DEFAULT: array() +--DESCRIPTION-- +List of strings that are forbidden in the host of any URI. Use it to kill +domain names of spam, etc. Note that it will catch anything in the domain, +so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt new file mode 100644 index 0000000..4214900 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt @@ -0,0 +1,13 @@ +URI.MakeAbsolute +TYPE: bool +VERSION: 2.1.0 +DEFAULT: false +--DESCRIPTION-- + +<p> + Converts all URIs into absolute forms. This is useful when the HTML + being filtered assumes a specific base path, but will actually be + viewed in a different context (and setting an alternate base URI is + not possible). %URI.Base must be set for this directive to work. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt new file mode 100644 index 0000000..58c81dc --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt @@ -0,0 +1,83 @@ +URI.Munge +TYPE: string/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +<p> + Munges all browsable (usually http, https and ftp) + absolute URIs into another URI, usually a URI redirection service. + This directive accepts a URI, formatted with a <code>%s</code> where + the url-encoded original URI should be inserted (sample: + <code>http://www.google.com/url?q=%s</code>). +</p> +<p> + Uses for this directive: +</p> +<ul> + <li> + Prevent PageRank leaks, while being fairly transparent + to users (you may also want to add some client side JavaScript to + override the text in the statusbar). <strong>Notice</strong>: + Many security experts believe that this form of protection does not deter spam-bots. + </li> + <li> + Redirect users to a splash page telling them they are leaving your + website. While this is poor usability practice, it is often mandated + in corporate environments. + </li> +</ul> +<p> + Prior to HTML Purifier 3.1.1, this directive also enabled the munging + of browsable external resources, which could break things if your redirection + script was a splash page or used <code>meta</code> tags. To revert to + previous behavior, please use %URI.MungeResources. +</p> +<p> + You may want to also use %URI.MungeSecretKey along with this directive + in order to enforce what URIs your redirector script allows. Open + redirector scripts can be a security risk and negatively affect the + reputation of your domain name. +</p> +<p> + Starting with HTML Purifier 3.1.1, there is also these substitutions: +</p> +<table> + <thead> + <tr> + <th>Key</th> + <th>Description</th> + <th>Example <code><a href=""></code></th> + </tr> + </thead> + <tbody> + <tr> + <td>%r</td> + <td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td> + <td></td> + </tr> + <tr> + <td>%n</td> + <td>The name of the tag this URI came from</td> + <td>a</td> + </tr> + <tr> + <td>%m</td> + <td>The name of the attribute this URI came from</td> + <td>href</td> + </tr> + <tr> + <td>%p</td> + <td>The name of the CSS property this URI came from, or blank if irrelevant</td> + <td></td> + </tr> + </tbody> +</table> +<p> + Admittedly, these letters are somewhat arbitrary; the only stipulation + was that they couldn't be a through f. r is for resource (I would have preferred + e, but you take what you can get), n is for name, m + was picked because it came after n (and I couldn't use a), p is for + property. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt new file mode 100644 index 0000000..6fce0fd --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt @@ -0,0 +1,17 @@ +URI.MungeResources +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +<p> + If true, any URI munging directives like %URI.Munge + will also apply to embedded resources, such as <code><img src=""></code>. + Be careful enabling this directive if you have a redirector script + that does not use the <code>Location</code> HTTP header; all of your images + and other embedded resources will break. +</p> +<p> + <strong>Warning:</strong> It is strongly advised you use this in conjunction + %URI.MungeSecretKey to mitigate the security risk of an open redirector. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt new file mode 100644 index 0000000..0d00f62 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt @@ -0,0 +1,30 @@ +URI.MungeSecretKey +TYPE: string/null +VERSION: 3.1.1 +DEFAULT: NULL +--DESCRIPTION-- +<p> + This directive enables secure checksum generation along with %URI.Munge. + It should be set to a secure key that is not shared with anyone else. + The checksum can be placed in the URI using %t. Use of this checksum + affords an additional level of protection by allowing a redirector + to check if a URI has passed through HTML Purifier with this line: +</p> + +<pre>$checksum === sha1($secret_key . ':' . $url)</pre> + +<p> + If the output is TRUE, the redirector script should accept the URI. +</p> + +<p> + Please note that it would still be possible for an attacker to procure + secure hashes en-mass by abusing your website's Preview feature or the + like, but this service affords an additional level of protection + that should be combined with website blacklisting. +</p> + +<p> + Remember this has no effect if %URI.Munge is not on. +</p> +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt new file mode 100644 index 0000000..23331a4 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt @@ -0,0 +1,9 @@ +URI.OverrideAllowedSchemes +TYPE: bool +DEFAULT: true +--DESCRIPTION-- +If this is set to true (which it is by default), you can override +%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the +registry. If false, you will also have to update that directive in order +to add more schemes. +--# vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ConfigSchema/schema/info.ini b/lib/php/HTMLPurifier/ConfigSchema/schema/info.ini new file mode 100644 index 0000000..5de4505 --- /dev/null +++ b/lib/php/HTMLPurifier/ConfigSchema/schema/info.ini @@ -0,0 +1,3 @@ +name = "HTML Purifier" + +; vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ContentSets.php b/lib/php/HTMLPurifier/ContentSets.php new file mode 100644 index 0000000..3b6e96f --- /dev/null +++ b/lib/php/HTMLPurifier/ContentSets.php @@ -0,0 +1,155 @@ +<?php + +/** + * @todo Unit test + */ +class HTMLPurifier_ContentSets +{ + + /** + * List of content set strings (pipe seperators) indexed by name. + */ + public $info = array(); + + /** + * List of content set lookups (element => true) indexed by name. + * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets + */ + public $lookup = array(); + + /** + * Synchronized list of defined content sets (keys of info) + */ + protected $keys = array(); + /** + * Synchronized list of defined content values (values of info) + */ + protected $values = array(); + + /** + * Merges in module's content sets, expands identifiers in the content + * sets and populates the keys, values and lookup member variables. + * @param $modules List of HTMLPurifier_HTMLModule + */ + public function __construct($modules) { + if (!is_array($modules)) $modules = array($modules); + // populate content_sets based on module hints + // sorry, no way of overloading + foreach ($modules as $module_i => $module) { + foreach ($module->content_sets as $key => $value) { + $temp = $this->convertToLookup($value); + if (isset($this->lookup[$key])) { + // add it into the existing content set + $this->lookup[$key] = array_merge($this->lookup[$key], $temp); + } else { + $this->lookup[$key] = $temp; + } + } + } + $old_lookup = false; + while ($old_lookup !== $this->lookup) { + $old_lookup = $this->lookup; + foreach ($this->lookup as $i => $set) { + $add = array(); + foreach ($set as $element => $x) { + if (isset($this->lookup[$element])) { + $add += $this->lookup[$element]; + unset($this->lookup[$i][$element]); + } + } + $this->lookup[$i] += $add; + } + } + + foreach ($this->lookup as $key => $lookup) { + $this->info[$key] = implode(' | ', array_keys($lookup)); + } + $this->keys = array_keys($this->info); + $this->values = array_values($this->info); + } + + /** + * Accepts a definition; generates and assigns a ChildDef for it + * @param $def HTMLPurifier_ElementDef reference + * @param $module Module that defined the ElementDef + */ + public function generateChildDef(&$def, $module) { + if (!empty($def->child)) return; // already done! + $content_model = $def->content_model; + if (is_string($content_model)) { + // Assume that $this->keys is alphanumeric + $def->content_model = preg_replace_callback( + '/\b(' . implode('|', $this->keys) . ')\b/', + array($this, 'generateChildDefCallback'), + $content_model + ); + //$def->content_model = str_replace( + // $this->keys, $this->values, $content_model); + } + $def->child = $this->getChildDef($def, $module); + } + + public function generateChildDefCallback($matches) { + return $this->info[$matches[0]]; + } + + /** + * Instantiates a ChildDef based on content_model and content_model_type + * member variables in HTMLPurifier_ElementDef + * @note This will also defer to modules for custom HTMLPurifier_ChildDef + * subclasses that need content set expansion + * @param $def HTMLPurifier_ElementDef to have ChildDef extracted + * @return HTMLPurifier_ChildDef corresponding to ElementDef + */ + public function getChildDef($def, $module) { + $value = $def->content_model; + if (is_object($value)) { + trigger_error( + 'Literal object child definitions should be stored in '. + 'ElementDef->child not ElementDef->content_model', + E_USER_NOTICE + ); + return $value; + } + switch ($def->content_model_type) { + case 'required': + return new HTMLPurifier_ChildDef_Required($value); + case 'optional': + return new HTMLPurifier_ChildDef_Optional($value); + case 'empty': + return new HTMLPurifier_ChildDef_Empty(); + case 'custom': + return new HTMLPurifier_ChildDef_Custom($value); + } + // defer to its module + $return = false; + if ($module->defines_child_def) { // save a func call + $return = $module->getChildDef($def); + } + if ($return !== false) return $return; + // error-out + trigger_error( + 'Could not determine which ChildDef class to instantiate', + E_USER_ERROR + ); + return false; + } + + /** + * Converts a string list of elements separated by pipes into + * a lookup array. + * @param $string List of elements + * @return Lookup array of elements + */ + protected function convertToLookup($string) { + $array = explode('|', str_replace(' ', '', $string)); + $ret = array(); + foreach ($array as $i => $k) { + $ret[$k] = true; + } + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Context.php b/lib/php/HTMLPurifier/Context.php new file mode 100644 index 0000000..9ddf0c5 --- /dev/null +++ b/lib/php/HTMLPurifier/Context.php @@ -0,0 +1,82 @@ +<?php + +/** + * Registry object that contains information about the current context. + * @warning Is a bit buggy when variables are set to null: it thinks + * they don't exist! So use false instead, please. + * @note Since the variables Context deals with may not be objects, + * references are very important here! Do not remove! + */ +class HTMLPurifier_Context +{ + + /** + * Private array that stores the references. + */ + private $_storage = array(); + + /** + * Registers a variable into the context. + * @param $name String name + * @param $ref Reference to variable to be registered + */ + public function register($name, &$ref) { + if (isset($this->_storage[$name])) { + trigger_error("Name $name produces collision, cannot re-register", + E_USER_ERROR); + return; + } + $this->_storage[$name] =& $ref; + } + + /** + * Retrieves a variable reference from the context. + * @param $name String name + * @param $ignore_error Boolean whether or not to ignore error + */ + public function &get($name, $ignore_error = false) { + if (!isset($this->_storage[$name])) { + if (!$ignore_error) { + trigger_error("Attempted to retrieve non-existent variable $name", + E_USER_ERROR); + } + $var = null; // so we can return by reference + return $var; + } + return $this->_storage[$name]; + } + + /** + * Destorys a variable in the context. + * @param $name String name + */ + public function destroy($name) { + if (!isset($this->_storage[$name])) { + trigger_error("Attempted to destroy non-existent variable $name", + E_USER_ERROR); + return; + } + unset($this->_storage[$name]); + } + + /** + * Checks whether or not the variable exists. + * @param $name String name + */ + public function exists($name) { + return isset($this->_storage[$name]); + } + + /** + * Loads a series of variables from an associative array + * @param $context_array Assoc array of variables to load + */ + public function loadArray($context_array) { + foreach ($context_array as $key => $discard) { + $this->register($key, $context_array[$key]); + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Definition.php b/lib/php/HTMLPurifier/Definition.php new file mode 100644 index 0000000..a7408c9 --- /dev/null +++ b/lib/php/HTMLPurifier/Definition.php @@ -0,0 +1,39 @@ +<?php + +/** + * Super-class for definition datatype objects, implements serialization + * functions for the class. + */ +abstract class HTMLPurifier_Definition +{ + + /** + * Has setup() been called yet? + */ + public $setup = false; + + /** + * What type of definition is it? + */ + public $type; + + /** + * Sets up the definition object into the final form, something + * not done by the constructor + * @param $config HTMLPurifier_Config instance + */ + abstract protected function doSetup($config); + + /** + * Setup function that aborts if already setup + * @param $config HTMLPurifier_Config instance + */ + public function setup($config) { + if ($this->setup) return; + $this->setup = true; + $this->doSetup($config); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache.php b/lib/php/HTMLPurifier/DefinitionCache.php new file mode 100644 index 0000000..c6e1e38 --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache.php @@ -0,0 +1,108 @@ +<?php + +/** + * Abstract class representing Definition cache managers that implements + * useful common methods and is a factory. + * @todo Create a separate maintenance file advanced users can use to + * cache their custom HTMLDefinition, which can be loaded + * via a configuration directive + * @todo Implement memcached + */ +abstract class HTMLPurifier_DefinitionCache +{ + + public $type; + + /** + * @param $name Type of definition objects this instance of the + * cache will handle. + */ + public function __construct($type) { + $this->type = $type; + } + + /** + * Generates a unique identifier for a particular configuration + * @param Instance of HTMLPurifier_Config + */ + public function generateKey($config) { + return $config->version . ',' . // possibly replace with function calls + $config->getBatchSerial($this->type) . ',' . + $config->get($this->type . '.DefinitionRev'); + } + + /** + * Tests whether or not a key is old with respect to the configuration's + * version and revision number. + * @param $key Key to test + * @param $config Instance of HTMLPurifier_Config to test against + */ + public function isOld($key, $config) { + if (substr_count($key, ',') < 2) return true; + list($version, $hash, $revision) = explode(',', $key, 3); + $compare = version_compare($version, $config->version); + // version mismatch, is always old + if ($compare != 0) return true; + // versions match, ids match, check revision number + if ( + $hash == $config->getBatchSerial($this->type) && + $revision < $config->get($this->type . '.DefinitionRev') + ) return true; + return false; + } + + /** + * Checks if a definition's type jives with the cache's type + * @note Throws an error on failure + * @param $def Definition object to check + * @return Boolean true if good, false if not + */ + public function checkDefType($def) { + if ($def->type !== $this->type) { + trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}"); + return false; + } + return true; + } + + /** + * Adds a definition object to the cache + */ + abstract public function add($def, $config); + + /** + * Unconditionally saves a definition object to the cache + */ + abstract public function set($def, $config); + + /** + * Replace an object in the cache + */ + abstract public function replace($def, $config); + + /** + * Retrieves a definition object from the cache + */ + abstract public function get($config); + + /** + * Removes a definition object to the cache + */ + abstract public function remove($config); + + /** + * Clears all objects from cache + */ + abstract public function flush($config); + + /** + * Clears all expired (older version or revision) objects from cache + * @note Be carefuly implementing this method as flush. Flush must + * not interfere with other Definition types, and cleanup() + * should not be repeatedly called by userland code. + */ + abstract public function cleanup($config); + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Decorator.php b/lib/php/HTMLPurifier/DefinitionCache/Decorator.php new file mode 100644 index 0000000..b0fb6d0 --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Decorator.php @@ -0,0 +1,62 @@ +<?php + +class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache +{ + + /** + * Cache object we are decorating + */ + public $cache; + + public function __construct() {} + + /** + * Lazy decorator function + * @param $cache Reference to cache object to decorate + */ + public function decorate(&$cache) { + $decorator = $this->copy(); + // reference is necessary for mocks in PHP 4 + $decorator->cache =& $cache; + $decorator->type = $cache->type; + return $decorator; + } + + /** + * Cross-compatible clone substitute + */ + public function copy() { + return new HTMLPurifier_DefinitionCache_Decorator(); + } + + public function add($def, $config) { + return $this->cache->add($def, $config); + } + + public function set($def, $config) { + return $this->cache->set($def, $config); + } + + public function replace($def, $config) { + return $this->cache->replace($def, $config); + } + + public function get($config) { + return $this->cache->get($config); + } + + public function remove($config) { + return $this->cache->remove($config); + } + + public function flush($config) { + return $this->cache->flush($config); + } + + public function cleanup($config) { + return $this->cache->cleanup($config); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/lib/php/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php new file mode 100644 index 0000000..d4cc35c --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php @@ -0,0 +1,43 @@ +<?php + +/** + * Definition cache decorator class that cleans up the cache + * whenever there is a cache miss. + */ +class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends + HTMLPurifier_DefinitionCache_Decorator +{ + + public $name = 'Cleanup'; + + public function copy() { + return new HTMLPurifier_DefinitionCache_Decorator_Cleanup(); + } + + public function add($def, $config) { + $status = parent::add($def, $config); + if (!$status) parent::cleanup($config); + return $status; + } + + public function set($def, $config) { + $status = parent::set($def, $config); + if (!$status) parent::cleanup($config); + return $status; + } + + public function replace($def, $config) { + $status = parent::replace($def, $config); + if (!$status) parent::cleanup($config); + return $status; + } + + public function get($config) { + $ret = parent::get($config); + if (!$ret) parent::cleanup($config); + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Decorator/Memory.php b/lib/php/HTMLPurifier/DefinitionCache/Decorator/Memory.php new file mode 100644 index 0000000..18f16d3 --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Decorator/Memory.php @@ -0,0 +1,46 @@ +<?php + +/** + * Definition cache decorator class that saves all cache retrievals + * to PHP's memory; good for unit tests or circumstances where + * there are lots of configuration objects floating around. + */ +class HTMLPurifier_DefinitionCache_Decorator_Memory extends + HTMLPurifier_DefinitionCache_Decorator +{ + + protected $definitions; + public $name = 'Memory'; + + public function copy() { + return new HTMLPurifier_DefinitionCache_Decorator_Memory(); + } + + public function add($def, $config) { + $status = parent::add($def, $config); + if ($status) $this->definitions[$this->generateKey($config)] = $def; + return $status; + } + + public function set($def, $config) { + $status = parent::set($def, $config); + if ($status) $this->definitions[$this->generateKey($config)] = $def; + return $status; + } + + public function replace($def, $config) { + $status = parent::replace($def, $config); + if ($status) $this->definitions[$this->generateKey($config)] = $def; + return $status; + } + + public function get($config) { + $key = $this->generateKey($config); + if (isset($this->definitions[$key])) return $this->definitions[$key]; + $this->definitions[$key] = parent::get($config); + return $this->definitions[$key]; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/lib/php/HTMLPurifier/DefinitionCache/Decorator/Template.php.in new file mode 100644 index 0000000..21a8fcf --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Decorator/Template.php.in @@ -0,0 +1,47 @@ +<?php + +require_once 'HTMLPurifier/DefinitionCache/Decorator.php'; + +/** + * Definition cache decorator template. + */ +class HTMLPurifier_DefinitionCache_Decorator_Template extends + HTMLPurifier_DefinitionCache_Decorator +{ + + var $name = 'Template'; // replace this + + function copy() { + // replace class name with yours + return new HTMLPurifier_DefinitionCache_Decorator_Template(); + } + + // remove methods you don't need + + function add($def, $config) { + return parent::add($def, $config); + } + + function set($def, $config) { + return parent::set($def, $config); + } + + function replace($def, $config) { + return parent::replace($def, $config); + } + + function get($config) { + return parent::get($config); + } + + function flush() { + return parent::flush(); + } + + function cleanup($config) { + return parent::cleanup($config); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Null.php b/lib/php/HTMLPurifier/DefinitionCache/Null.php new file mode 100644 index 0000000..41d97e7 --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Null.php @@ -0,0 +1,39 @@ +<?php + +/** + * Null cache object to use when no caching is on. + */ +class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache +{ + + public function add($def, $config) { + return false; + } + + public function set($def, $config) { + return false; + } + + public function replace($def, $config) { + return false; + } + + public function remove($config) { + return false; + } + + public function get($config) { + return false; + } + + public function flush($config) { + return false; + } + + public function cleanup($config) { + return false; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Serializer.php b/lib/php/HTMLPurifier/DefinitionCache/Serializer.php new file mode 100644 index 0000000..7a6aa93 --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Serializer.php @@ -0,0 +1,172 @@ +<?php + +class HTMLPurifier_DefinitionCache_Serializer extends + HTMLPurifier_DefinitionCache +{ + + public function add($def, $config) { + if (!$this->checkDefType($def)) return; + $file = $this->generateFilePath($config); + if (file_exists($file)) return false; + if (!$this->_prepareDir($config)) return false; + return $this->_write($file, serialize($def)); + } + + public function set($def, $config) { + if (!$this->checkDefType($def)) return; + $file = $this->generateFilePath($config); + if (!$this->_prepareDir($config)) return false; + return $this->_write($file, serialize($def)); + } + + public function replace($def, $config) { + if (!$this->checkDefType($def)) return; + $file = $this->generateFilePath($config); + if (!file_exists($file)) return false; + if (!$this->_prepareDir($config)) return false; + return $this->_write($file, serialize($def)); + } + + public function get($config) { + $file = $this->generateFilePath($config); + if (!file_exists($file)) return false; + return unserialize(file_get_contents($file)); + } + + public function remove($config) { + $file = $this->generateFilePath($config); + if (!file_exists($file)) return false; + return unlink($file); + } + + public function flush($config) { + if (!$this->_prepareDir($config)) return false; + $dir = $this->generateDirectoryPath($config); + $dh = opendir($dir); + while (false !== ($filename = readdir($dh))) { + if (empty($filename)) continue; + if ($filename[0] === '.') continue; + unlink($dir . '/' . $filename); + } + } + + public function cleanup($config) { + if (!$this->_prepareDir($config)) return false; + $dir = $this->generateDirectoryPath($config); + $dh = opendir($dir); + while (false !== ($filename = readdir($dh))) { + if (empty($filename)) continue; + if ($filename[0] === '.') continue; + $key = substr($filename, 0, strlen($filename) - 4); + if ($this->isOld($key, $config)) unlink($dir . '/' . $filename); + } + } + + /** + * Generates the file path to the serial file corresponding to + * the configuration and definition name + * @todo Make protected + */ + public function generateFilePath($config) { + $key = $this->generateKey($config); + return $this->generateDirectoryPath($config) . '/' . $key . '.ser'; + } + + /** + * Generates the path to the directory contain this cache's serial files + * @note No trailing slash + * @todo Make protected + */ + public function generateDirectoryPath($config) { + $base = $this->generateBaseDirectoryPath($config); + return $base . '/' . $this->type; + } + + /** + * Generates path to base directory that contains all definition type + * serials + * @todo Make protected + */ + public function generateBaseDirectoryPath($config) { + $base = $config->get('Cache.SerializerPath'); + $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base; + return $base; + } + + /** + * Convenience wrapper function for file_put_contents + * @param $file File name to write to + * @param $data Data to write into file + * @return Number of bytes written if success, or false if failure. + */ + private function _write($file, $data) { + return file_put_contents($file, $data); + } + + /** + * Prepares the directory that this type stores the serials in + * @return True if successful + */ + private function _prepareDir($config) { + $directory = $this->generateDirectoryPath($config); + if (!is_dir($directory)) { + $base = $this->generateBaseDirectoryPath($config); + if (!is_dir($base)) { + trigger_error('Base directory '.$base.' does not exist, + please create or change using %Cache.SerializerPath', + E_USER_WARNING); + return false; + } elseif (!$this->_testPermissions($base)) { + return false; + } + $old = umask(0022); // disable group and world writes + mkdir($directory); + umask($old); + } elseif (!$this->_testPermissions($directory)) { + return false; + } + return true; + } + + /** + * Tests permissions on a directory and throws out friendly + * error messages and attempts to chmod it itself if possible + */ + private function _testPermissions($dir) { + // early abort, if it is writable, everything is hunky-dory + if (is_writable($dir)) return true; + if (!is_dir($dir)) { + // generally, you'll want to handle this beforehand + // so a more specific error message can be given + trigger_error('Directory '.$dir.' does not exist', + E_USER_WARNING); + return false; + } + if (function_exists('posix_getuid')) { + // POSIX system, we can give more specific advice + if (fileowner($dir) === posix_getuid()) { + // we can chmod it ourselves + chmod($dir, 0755); + return true; + } elseif (filegroup($dir) === posix_getgid()) { + $chmod = '775'; + } else { + // PHP's probably running as nobody, so we'll + // need to give global permissions + $chmod = '777'; + } + trigger_error('Directory '.$dir.' not writable, '. + 'please chmod to ' . $chmod, + E_USER_WARNING); + } else { + // generic error message + trigger_error('Directory '.$dir.' not writable, '. + 'please alter file permissions', + E_USER_WARNING); + } + return false; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCache/Serializer/README b/lib/php/HTMLPurifier/DefinitionCache/Serializer/README new file mode 100644 index 0000000..2e35c1c --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCache/Serializer/README @@ -0,0 +1,3 @@ +This is a dummy file to prevent Git from ignoring this empty directory. + + vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DefinitionCacheFactory.php b/lib/php/HTMLPurifier/DefinitionCacheFactory.php new file mode 100644 index 0000000..a6ead62 --- /dev/null +++ b/lib/php/HTMLPurifier/DefinitionCacheFactory.php @@ -0,0 +1,91 @@ +<?php + +/** + * Responsible for creating definition caches. + */ +class HTMLPurifier_DefinitionCacheFactory +{ + + protected $caches = array('Serializer' => array()); + protected $implementations = array(); + protected $decorators = array(); + + /** + * Initialize default decorators + */ + public function setup() { + $this->addDecorator('Cleanup'); + } + + /** + * Retrieves an instance of global definition cache factory. + */ + public static function instance($prototype = null) { + static $instance; + if ($prototype !== null) { + $instance = $prototype; + } elseif ($instance === null || $prototype === true) { + $instance = new HTMLPurifier_DefinitionCacheFactory(); + $instance->setup(); + } + return $instance; + } + + /** + * Registers a new definition cache object + * @param $short Short name of cache object, for reference + * @param $long Full class name of cache object, for construction + */ + public function register($short, $long) { + $this->implementations[$short] = $long; + } + + /** + * Factory method that creates a cache object based on configuration + * @param $name Name of definitions handled by cache + * @param $config Instance of HTMLPurifier_Config + */ + public function create($type, $config) { + $method = $config->get('Cache.DefinitionImpl'); + if ($method === null) { + return new HTMLPurifier_DefinitionCache_Null($type); + } + if (!empty($this->caches[$method][$type])) { + return $this->caches[$method][$type]; + } + if ( + isset($this->implementations[$method]) && + class_exists($class = $this->implementations[$method], false) + ) { + $cache = new $class($type); + } else { + if ($method != 'Serializer') { + trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING); + } + $cache = new HTMLPurifier_DefinitionCache_Serializer($type); + } + foreach ($this->decorators as $decorator) { + $new_cache = $decorator->decorate($cache); + // prevent infinite recursion in PHP 4 + unset($cache); + $cache = $new_cache; + } + $this->caches[$method][$type] = $cache; + return $this->caches[$method][$type]; + } + + /** + * Registers a decorator to add to all new cache objects + * @param + */ + public function addDecorator($decorator) { + if (is_string($decorator)) { + $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator"; + $decorator = new $class; + } + $this->decorators[$decorator->name] = $decorator; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Doctype.php b/lib/php/HTMLPurifier/Doctype.php new file mode 100644 index 0000000..1e3c574 --- /dev/null +++ b/lib/php/HTMLPurifier/Doctype.php @@ -0,0 +1,60 @@ +<?php + +/** + * Represents a document type, contains information on which modules + * need to be loaded. + * @note This class is inspected by Printer_HTMLDefinition->renderDoctype. + * If structure changes, please update that function. + */ +class HTMLPurifier_Doctype +{ + /** + * Full name of doctype + */ + public $name; + + /** + * List of standard modules (string identifiers or literal objects) + * that this doctype uses + */ + public $modules = array(); + + /** + * List of modules to use for tidying up code + */ + public $tidyModules = array(); + + /** + * Is the language derived from XML (i.e. XHTML)? + */ + public $xml = true; + + /** + * List of aliases for this doctype + */ + public $aliases = array(); + + /** + * Public DTD identifier + */ + public $dtdPublic; + + /** + * System DTD identifier + */ + public $dtdSystem; + + public function __construct($name = null, $xml = true, $modules = array(), + $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null + ) { + $this->name = $name; + $this->xml = $xml; + $this->modules = $modules; + $this->tidyModules = $tidyModules; + $this->aliases = $aliases; + $this->dtdPublic = $dtd_public; + $this->dtdSystem = $dtd_system; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/DoctypeRegistry.php b/lib/php/HTMLPurifier/DoctypeRegistry.php new file mode 100644 index 0000000..86049e9 --- /dev/null +++ b/lib/php/HTMLPurifier/DoctypeRegistry.php @@ -0,0 +1,103 @@ +<?php + +class HTMLPurifier_DoctypeRegistry +{ + + /** + * Hash of doctype names to doctype objects + */ + protected $doctypes; + + /** + * Lookup table of aliases to real doctype names + */ + protected $aliases; + + /** + * Registers a doctype to the registry + * @note Accepts a fully-formed doctype object, or the + * parameters for constructing a doctype object + * @param $doctype Name of doctype or literal doctype object + * @param $modules Modules doctype will load + * @param $modules_for_modes Modules doctype will load for certain modes + * @param $aliases Alias names for doctype + * @return Editable registered doctype + */ + public function register($doctype, $xml = true, $modules = array(), + $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null + ) { + if (!is_array($modules)) $modules = array($modules); + if (!is_array($tidy_modules)) $tidy_modules = array($tidy_modules); + if (!is_array($aliases)) $aliases = array($aliases); + if (!is_object($doctype)) { + $doctype = new HTMLPurifier_Doctype( + $doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system + ); + } + $this->doctypes[$doctype->name] = $doctype; + $name = $doctype->name; + // hookup aliases + foreach ($doctype->aliases as $alias) { + if (isset($this->doctypes[$alias])) continue; + $this->aliases[$alias] = $name; + } + // remove old aliases + if (isset($this->aliases[$name])) unset($this->aliases[$name]); + return $doctype; + } + + /** + * Retrieves reference to a doctype of a certain name + * @note This function resolves aliases + * @note When possible, use the more fully-featured make() + * @param $doctype Name of doctype + * @return Editable doctype object + */ + public function get($doctype) { + if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype]; + if (!isset($this->doctypes[$doctype])) { + trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR); + $anon = new HTMLPurifier_Doctype($doctype); + return $anon; + } + return $this->doctypes[$doctype]; + } + + /** + * Creates a doctype based on a configuration object, + * will perform initialization on the doctype + * @note Use this function to get a copy of doctype that config + * can hold on to (this is necessary in order to tell + * Generator whether or not the current document is XML + * based or not). + */ + public function make($config) { + return clone $this->get($this->getDoctypeFromConfig($config)); + } + + /** + * Retrieves the doctype from the configuration object + */ + public function getDoctypeFromConfig($config) { + // recommended test + $doctype = $config->get('HTML.Doctype'); + if (!empty($doctype)) return $doctype; + $doctype = $config->get('HTML.CustomDoctype'); + if (!empty($doctype)) return $doctype; + // backwards-compatibility + if ($config->get('HTML.XHTML')) { + $doctype = 'XHTML 1.0'; + } else { + $doctype = 'HTML 4.01'; + } + if ($config->get('HTML.Strict')) { + $doctype .= ' Strict'; + } else { + $doctype .= ' Transitional'; + } + return $doctype; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ElementDef.php b/lib/php/HTMLPurifier/ElementDef.php new file mode 100644 index 0000000..aede2c3 --- /dev/null +++ b/lib/php/HTMLPurifier/ElementDef.php @@ -0,0 +1,176 @@ +<?php + +/** + * Structure that stores an HTML element definition. Used by + * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule. + * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition. + * Please update that class too. + * @warning If you add new properties to this class, you MUST update + * the mergeIn() method. + */ +class HTMLPurifier_ElementDef +{ + + /** + * Does the definition work by itself, or is it created solely + * for the purpose of merging into another definition? + */ + public $standalone = true; + + /** + * Associative array of attribute name to HTMLPurifier_AttrDef + * @note Before being processed by HTMLPurifier_AttrCollections + * when modules are finalized during + * HTMLPurifier_HTMLDefinition->setup(), this array may also + * contain an array at index 0 that indicates which attribute + * collections to load into the full array. It may also + * contain string indentifiers in lieu of HTMLPurifier_AttrDef, + * see HTMLPurifier_AttrTypes on how they are expanded during + * HTMLPurifier_HTMLDefinition->setup() processing. + */ + public $attr = array(); + + /** + * Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation + */ + public $attr_transform_pre = array(); + + /** + * Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation + */ + public $attr_transform_post = array(); + + /** + * HTMLPurifier_ChildDef of this tag. + */ + public $child; + + /** + * Abstract string representation of internal ChildDef rules. See + * HTMLPurifier_ContentSets for how this is parsed and then transformed + * into an HTMLPurifier_ChildDef. + * @warning This is a temporary variable that is not available after + * being processed by HTMLDefinition + */ + public $content_model; + + /** + * Value of $child->type, used to determine which ChildDef to use, + * used in combination with $content_model. + * @warning This must be lowercase + * @warning This is a temporary variable that is not available after + * being processed by HTMLDefinition + */ + public $content_model_type; + + + + /** + * Does the element have a content model (#PCDATA | Inline)*? This + * is important for chameleon ins and del processing in + * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't + * have to worry about this one. + */ + public $descendants_are_inline = false; + + /** + * List of the names of required attributes this element has. Dynamically + * populated by HTMLPurifier_HTMLDefinition::getElement + */ + public $required_attr = array(); + + /** + * Lookup table of tags excluded from all descendants of this tag. + * @note SGML permits exclusions for all descendants, but this is + * not possible with DTDs or XML Schemas. W3C has elected to + * use complicated compositions of content_models to simulate + * exclusion for children, but we go the simpler, SGML-style + * route of flat-out exclusions, which correctly apply to + * all descendants and not just children. Note that the XHTML + * Modularization Abstract Modules are blithely unaware of such + * distinctions. + */ + public $excludes = array(); + + /** + * This tag is explicitly auto-closed by the following tags. + */ + public $autoclose = array(); + + /** + * Whether or not this is a formatting element affected by the + * "Active Formatting Elements" algorithm. + */ + public $formatting; + + /** + * Low-level factory constructor for creating new standalone element defs + */ + public static function create($content_model, $content_model_type, $attr) { + $def = new HTMLPurifier_ElementDef(); + $def->content_model = $content_model; + $def->content_model_type = $content_model_type; + $def->attr = $attr; + return $def; + } + + /** + * Merges the values of another element definition into this one. + * Values from the new element def take precedence if a value is + * not mergeable. + */ + public function mergeIn($def) { + + // later keys takes precedence + foreach($def->attr as $k => $v) { + if ($k === 0) { + // merge in the includes + // sorry, no way to override an include + foreach ($v as $v2) { + $this->attr[0][] = $v2; + } + continue; + } + if ($v === false) { + if (isset($this->attr[$k])) unset($this->attr[$k]); + continue; + } + $this->attr[$k] = $v; + } + $this->_mergeAssocArray($this->attr_transform_pre, $def->attr_transform_pre); + $this->_mergeAssocArray($this->attr_transform_post, $def->attr_transform_post); + $this->_mergeAssocArray($this->excludes, $def->excludes); + + if(!empty($def->content_model)) { + $this->content_model = + str_replace("#SUPER", $this->content_model, $def->content_model); + $this->child = false; + } + if(!empty($def->content_model_type)) { + $this->content_model_type = $def->content_model_type; + $this->child = false; + } + if(!is_null($def->child)) $this->child = $def->child; + if(!is_null($def->formatting)) $this->formatting = $def->formatting; + if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline; + + } + + /** + * Merges one array into another, removes values which equal false + * @param $a1 Array by reference that is merged into + * @param $a2 Array that merges into $a1 + */ + private function _mergeAssocArray(&$a1, $a2) { + foreach ($a2 as $k => $v) { + if ($v === false) { + if (isset($a1[$k])) unset($a1[$k]); + continue; + } + $a1[$k] = $v; + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Encoder.php b/lib/php/HTMLPurifier/Encoder.php new file mode 100644 index 0000000..2b3140c --- /dev/null +++ b/lib/php/HTMLPurifier/Encoder.php @@ -0,0 +1,426 @@ +<?php + +/** + * A UTF-8 specific character encoder that handles cleaning and transforming. + * @note All functions in this class should be static. + */ +class HTMLPurifier_Encoder +{ + + /** + * Constructor throws fatal error if you attempt to instantiate class + */ + private function __construct() { + trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR); + } + + /** + * Error-handler that mutes errors, alternative to shut-up operator. + */ + public static function muteErrorHandler() {} + + /** + * Cleans a UTF-8 string for well-formedness and SGML validity + * + * It will parse according to UTF-8 and return a valid UTF8 string, with + * non-SGML codepoints excluded. + * + * @note Just for reference, the non-SGML code points are 0 to 31 and + * 127 to 159, inclusive. However, we allow code points 9, 10 + * and 13, which are the tab, line feed and carriage return + * respectively. 128 and above the code points map to multibyte + * UTF-8 representations. + * + * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and + * hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the + * LGPL license. Notes on what changed are inside, but in general, + * the original code transformed UTF-8 text into an array of integer + * Unicode codepoints. Understandably, transforming that back to + * a string would be somewhat expensive, so the function was modded to + * directly operate on the string. However, this discourages code + * reuse, and the logic enumerated here would be useful for any + * function that needs to be able to understand UTF-8 characters. + * As of right now, only smart lossless character encoding converters + * would need that, and I'm probably not going to implement them. + * Once again, PHP 6 should solve all our problems. + */ + public static function cleanUTF8($str, $force_php = false) { + + // UTF-8 validity is checked since PHP 4.3.5 + // This is an optimization: if the string is already valid UTF-8, no + // need to do PHP stuff. 99% of the time, this will be the case. + // The regexp matches the XML char production, as well as well as excluding + // non-SGML codepoints U+007F to U+009F + if (preg_match('/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', $str)) { + return $str; + } + + $mState = 0; // cached expected number of octets after the current octet + // until the beginning of the next UTF8 character sequence + $mUcs4 = 0; // cached Unicode character + $mBytes = 1; // cached expected number of octets in the current sequence + + // original code involved an $out that was an array of Unicode + // codepoints. Instead of having to convert back into UTF-8, we've + // decided to directly append valid UTF-8 characters onto a string + // $out once they're done. $char accumulates raw bytes, while $mUcs4 + // turns into the Unicode code point, so there's some redundancy. + + $out = ''; + $char = ''; + + $len = strlen($str); + for($i = 0; $i < $len; $i++) { + $in = ord($str{$i}); + $char .= $str[$i]; // append byte to char + if (0 == $mState) { + // When mState is zero we expect either a US-ASCII character + // or a multi-octet sequence. + if (0 == (0x80 & ($in))) { + // US-ASCII, pass straight through. + if (($in <= 31 || $in == 127) && + !($in == 9 || $in == 13 || $in == 10) // save \r\t\n + ) { + // control characters, remove + } else { + $out .= $char; + } + // reset + $char = ''; + $mBytes = 1; + } elseif (0xC0 == (0xE0 & ($in))) { + // First octet of 2 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x1F) << 6; + $mState = 1; + $mBytes = 2; + } elseif (0xE0 == (0xF0 & ($in))) { + // First octet of 3 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x0F) << 12; + $mState = 2; + $mBytes = 3; + } elseif (0xF0 == (0xF8 & ($in))) { + // First octet of 4 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x07) << 18; + $mState = 3; + $mBytes = 4; + } elseif (0xF8 == (0xFC & ($in))) { + // First octet of 5 octet sequence. + // + // This is illegal because the encoded codepoint must be + // either: + // (a) not the shortest form or + // (b) outside the Unicode range of 0-0x10FFFF. + // Rather than trying to resynchronize, we will carry on + // until the end of the sequence and let the later error + // handling code catch it. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x03) << 24; + $mState = 4; + $mBytes = 5; + } elseif (0xFC == (0xFE & ($in))) { + // First octet of 6 octet sequence, see comments for 5 + // octet sequence. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 1) << 30; + $mState = 5; + $mBytes = 6; + } else { + // Current octet is neither in the US-ASCII range nor a + // legal first octet of a multi-octet sequence. + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char = ''; + } + } else { + // When mState is non-zero, we expect a continuation of the + // multi-octet sequence + if (0x80 == (0xC0 & ($in))) { + // Legal continuation. + $shift = ($mState - 1) * 6; + $tmp = $in; + $tmp = ($tmp & 0x0000003F) << $shift; + $mUcs4 |= $tmp; + + if (0 == --$mState) { + // End of the multi-octet sequence. mUcs4 now contains + // the final Unicode codepoint to be output + + // Check for illegal sequences and codepoints. + + // From Unicode 3.1, non-shortest form is illegal + if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || + ((3 == $mBytes) && ($mUcs4 < 0x0800)) || + ((4 == $mBytes) && ($mUcs4 < 0x10000)) || + (4 < $mBytes) || + // From Unicode 3.2, surrogate characters = illegal + (($mUcs4 & 0xFFFFF800) == 0xD800) || + // Codepoints outside the Unicode range are illegal + ($mUcs4 > 0x10FFFF) + ) { + + } elseif (0xFEFF != $mUcs4 && // omit BOM + // check for valid Char unicode codepoints + ( + 0x9 == $mUcs4 || + 0xA == $mUcs4 || + 0xD == $mUcs4 || + (0x20 <= $mUcs4 && 0x7E >= $mUcs4) || + // 7F-9F is not strictly prohibited by XML, + // but it is non-SGML, and thus we don't allow it + (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) || + (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4) + ) + ) { + $out .= $char; + } + // initialize UTF8 cache (reset) + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char = ''; + } + } else { + // ((0xC0 & (*in) != 0x80) && (mState != 0)) + // Incomplete multi-octet sequence. + // used to result in complete fail, but we'll reset + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char =''; + } + } + } + return $out; + } + + /** + * Translates a Unicode codepoint into its corresponding UTF-8 character. + * @note Based on Feyd's function at + * <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>, + * which is in public domain. + * @note While we're going to do code point parsing anyway, a good + * optimization would be to refuse to translate code points that + * are non-SGML characters. However, this could lead to duplication. + * @note This is very similar to the unichr function in + * maintenance/generate-entity-file.php (although this is superior, + * due to its sanity checks). + */ + + // +----------+----------+----------+----------+ + // | 33222222 | 22221111 | 111111 | | + // | 10987654 | 32109876 | 54321098 | 76543210 | bit + // +----------+----------+----------+----------+ + // | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F + // | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF + // | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF + // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF + // +----------+----------+----------+----------+ + // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF) + // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes + // +----------+----------+----------+----------+ + + public static function unichr($code) { + if($code > 1114111 or $code < 0 or + ($code >= 55296 and $code <= 57343) ) { + // bits are set outside the "valid" range as defined + // by UNICODE 4.1.0 + return ''; + } + + $x = $y = $z = $w = 0; + if ($code < 128) { + // regular ASCII character + $x = $code; + } else { + // set up bits for UTF-8 + $x = ($code & 63) | 128; + if ($code < 2048) { + $y = (($code & 2047) >> 6) | 192; + } else { + $y = (($code & 4032) >> 6) | 128; + if($code < 65536) { + $z = (($code >> 12) & 15) | 224; + } else { + $z = (($code >> 12) & 63) | 128; + $w = (($code >> 18) & 7) | 240; + } + } + } + // set up the actual character + $ret = ''; + if($w) $ret .= chr($w); + if($z) $ret .= chr($z); + if($y) $ret .= chr($y); + $ret .= chr($x); + + return $ret; + } + + /** + * Converts a string to UTF-8 based on configuration. + */ + public static function convertToUTF8($str, $config, $context) { + $encoding = $config->get('Core.Encoding'); + if ($encoding === 'utf-8') return $str; + static $iconv = null; + if ($iconv === null) $iconv = function_exists('iconv'); + set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); + if ($iconv && !$config->get('Test.ForceNoIconv')) { + $str = iconv($encoding, 'utf-8//IGNORE', $str); + if ($str === false) { + // $encoding is not a valid encoding + restore_error_handler(); + trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR); + return ''; + } + // If the string is bjorked by Shift_JIS or a similar encoding + // that doesn't support all of ASCII, convert the naughty + // characters to their true byte-wise ASCII/UTF-8 equivalents. + $str = strtr($str, HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding)); + restore_error_handler(); + return $str; + } elseif ($encoding === 'iso-8859-1') { + $str = utf8_encode($str); + restore_error_handler(); + return $str; + } + trigger_error('Encoding not supported, please install iconv', E_USER_ERROR); + } + + /** + * Converts a string from UTF-8 based on configuration. + * @note Currently, this is a lossy conversion, with unexpressable + * characters being omitted. + */ + public static function convertFromUTF8($str, $config, $context) { + $encoding = $config->get('Core.Encoding'); + if ($encoding === 'utf-8') return $str; + static $iconv = null; + if ($iconv === null) $iconv = function_exists('iconv'); + if ($escape = $config->get('Core.EscapeNonASCIICharacters')) { + $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str); + } + set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); + if ($iconv && !$config->get('Test.ForceNoIconv')) { + // Undo our previous fix in convertToUTF8, otherwise iconv will barf + $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding); + if (!$escape && !empty($ascii_fix)) { + $clear_fix = array(); + foreach ($ascii_fix as $utf8 => $native) $clear_fix[$utf8] = ''; + $str = strtr($str, $clear_fix); + } + $str = strtr($str, array_flip($ascii_fix)); + // Normal stuff + $str = iconv('utf-8', $encoding . '//IGNORE', $str); + restore_error_handler(); + return $str; + } elseif ($encoding === 'iso-8859-1') { + $str = utf8_decode($str); + restore_error_handler(); + return $str; + } + trigger_error('Encoding not supported', E_USER_ERROR); + } + + /** + * Lossless (character-wise) conversion of HTML to ASCII + * @param $str UTF-8 string to be converted to ASCII + * @returns ASCII encoded string with non-ASCII character entity-ized + * @warning Adapted from MediaWiki, claiming fair use: this is a common + * algorithm. If you disagree with this license fudgery, + * implement it yourself. + * @note Uses decimal numeric entities since they are best supported. + * @note This is a DUMB function: it has no concept of keeping + * character entities that the projected character encoding + * can allow. We could possibly implement a smart version + * but that would require it to also know which Unicode + * codepoints the charset supported (not an easy task). + * @note Sort of with cleanUTF8() but it assumes that $str is + * well-formed UTF-8 + */ + public static function convertToASCIIDumbLossless($str) { + $bytesleft = 0; + $result = ''; + $working = 0; + $len = strlen($str); + for( $i = 0; $i < $len; $i++ ) { + $bytevalue = ord( $str[$i] ); + if( $bytevalue <= 0x7F ) { //0xxx xxxx + $result .= chr( $bytevalue ); + $bytesleft = 0; + } elseif( $bytevalue <= 0xBF ) { //10xx xxxx + $working = $working << 6; + $working += ($bytevalue & 0x3F); + $bytesleft--; + if( $bytesleft <= 0 ) { + $result .= "&#" . $working . ";"; + } + } elseif( $bytevalue <= 0xDF ) { //110x xxxx + $working = $bytevalue & 0x1F; + $bytesleft = 1; + } elseif( $bytevalue <= 0xEF ) { //1110 xxxx + $working = $bytevalue & 0x0F; + $bytesleft = 2; + } else { //1111 0xxx + $working = $bytevalue & 0x07; + $bytesleft = 3; + } + } + return $result; + } + + /** + * This expensive function tests whether or not a given character + * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will + * fail this test, and require special processing. Variable width + * encodings shouldn't ever fail. + * + * @param string $encoding Encoding name to test, as per iconv format + * @param bool $bypass Whether or not to bypass the precompiled arrays. + * @return Array of UTF-8 characters to their corresponding ASCII, + * which can be used to "undo" any overzealous iconv action. + */ + public static function testEncodingSupportsASCII($encoding, $bypass = false) { + static $encodings = array(); + if (!$bypass) { + if (isset($encodings[$encoding])) return $encodings[$encoding]; + $lenc = strtolower($encoding); + switch ($lenc) { + case 'shift_jis': + return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~'); + case 'johab': + return array("\xE2\x82\xA9" => '\\'); + } + if (strpos($lenc, 'iso-8859-') === 0) return array(); + } + $ret = array(); + set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); + if (iconv('UTF-8', $encoding, 'a') === false) return false; + for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars + $c = chr($i); // UTF-8 char + $r = iconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion + if ( + $r === '' || + // This line is needed for iconv implementations that do not + // omit characters that do not exist in the target character set + ($r === $c && iconv($encoding, 'UTF-8//IGNORE', $r) !== $c) + ) { + // Reverse engineer: what's the UTF-8 equiv of this byte + // sequence? This assumes that there's no variable width + // encoding that doesn't support ASCII. + $ret[iconv($encoding, 'UTF-8//IGNORE', $c)] = $c; + } + } + restore_error_handler(); + $encodings[$encoding] = $ret; + return $ret; + } + + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/EntityLookup.php b/lib/php/HTMLPurifier/EntityLookup.php new file mode 100644 index 0000000..b4dfce9 --- /dev/null +++ b/lib/php/HTMLPurifier/EntityLookup.php @@ -0,0 +1,44 @@ +<?php + +/** + * Object that provides entity lookup table from entity name to character + */ +class HTMLPurifier_EntityLookup { + + /** + * Assoc array of entity name to character represented. + */ + public $table; + + /** + * Sets up the entity lookup table from the serialized file contents. + * @note The serialized contents are versioned, but were generated + * using the maintenance script generate_entity_file.php + * @warning This is not in constructor to help enforce the Singleton + */ + public function setup($file = false) { + if (!$file) { + $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser'; + } + $this->table = unserialize(file_get_contents($file)); + } + + /** + * Retrieves sole instance of the object. + * @param Optional prototype of custom lookup table to overload with. + */ + public static function instance($prototype = false) { + // no references, since PHP doesn't copy unless modified + static $instance = null; + if ($prototype) { + $instance = $prototype; + } elseif (!$instance) { + $instance = new HTMLPurifier_EntityLookup(); + $instance->setup(); + } + return $instance; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/EntityLookup/entities.ser b/lib/php/HTMLPurifier/EntityLookup/entities.ser new file mode 100644 index 0000000..f2b8b8f --- /dev/null +++ b/lib/php/HTMLPurifier/EntityLookup/entities.ser @@ -0,0 +1 @@ +a:246:{s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"";s:3:"zwj";s:3:"";s:3:"lrm";s:3:"";s:3:"rlm";s:3:"";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";} \ No newline at end of file diff --git a/lib/php/HTMLPurifier/EntityParser.php b/lib/php/HTMLPurifier/EntityParser.php new file mode 100644 index 0000000..8c38447 --- /dev/null +++ b/lib/php/HTMLPurifier/EntityParser.php @@ -0,0 +1,144 @@ +<?php + +// if want to implement error collecting here, we'll need to use some sort +// of global data (probably trigger_error) because it's impossible to pass +// $config or $context to the callback functions. + +/** + * Handles referencing and derefencing character entities + */ +class HTMLPurifier_EntityParser +{ + + /** + * Reference to entity lookup table. + */ + protected $_entity_lookup; + + /** + * Callback regex string for parsing entities. + */ + protected $_substituteEntitiesRegex = +'/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/'; +// 1. hex 2. dec 3. string (XML style) + + + /** + * Decimal to parsed string conversion table for special entities. + */ + protected $_special_dec2str = + array( + 34 => '"', + 38 => '&', + 39 => "'", + 60 => '<', + 62 => '>' + ); + + /** + * Stripped entity names to decimal conversion table for special entities. + */ + protected $_special_ent2dec = + array( + 'quot' => 34, + 'amp' => 38, + 'lt' => 60, + 'gt' => 62 + ); + + /** + * Substitutes non-special entities with their parsed equivalents. Since + * running this whenever you have parsed character is t3h 5uck, we run + * it before everything else. + * + * @param $string String to have non-special entities parsed. + * @returns Parsed string. + */ + public function substituteNonSpecialEntities($string) { + // it will try to detect missing semicolons, but don't rely on it + return preg_replace_callback( + $this->_substituteEntitiesRegex, + array($this, 'nonSpecialEntityCallback'), + $string + ); + } + + /** + * Callback function for substituteNonSpecialEntities() that does the work. + * + * @param $matches PCRE matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @returns Replacement string. + */ + + protected function nonSpecialEntityCallback($matches) { + // replaces all but big five + $entity = $matches[0]; + $is_num = (@$matches[0][1] === '#'); + if ($is_num) { + $is_hex = (@$entity[2] === 'x'); + $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; + + // abort for special characters + if (isset($this->_special_dec2str[$code])) return $entity; + + return HTMLPurifier_Encoder::unichr($code); + } else { + if (isset($this->_special_ent2dec[$matches[3]])) return $entity; + if (!$this->_entity_lookup) { + $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); + } + if (isset($this->_entity_lookup->table[$matches[3]])) { + return $this->_entity_lookup->table[$matches[3]]; + } else { + return $entity; + } + } + } + + /** + * Substitutes only special entities with their parsed equivalents. + * + * @notice We try to avoid calling this function because otherwise, it + * would have to be called a lot (for every parsed section). + * + * @param $string String to have non-special entities parsed. + * @returns Parsed string. + */ + public function substituteSpecialEntities($string) { + return preg_replace_callback( + $this->_substituteEntitiesRegex, + array($this, 'specialEntityCallback'), + $string); + } + + /** + * Callback function for substituteSpecialEntities() that does the work. + * + * This callback has same syntax as nonSpecialEntityCallback(). + * + * @param $matches PCRE-style matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @returns Replacement string. + */ + protected function specialEntityCallback($matches) { + $entity = $matches[0]; + $is_num = (@$matches[0][1] === '#'); + if ($is_num) { + $is_hex = (@$entity[2] === 'x'); + $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; + return isset($this->_special_dec2str[$int]) ? + $this->_special_dec2str[$int] : + $entity; + } else { + return isset($this->_special_ent2dec[$matches[3]]) ? + $this->_special_ent2dec[$matches[3]] : + $entity; + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ErrorCollector.php b/lib/php/HTMLPurifier/ErrorCollector.php new file mode 100644 index 0000000..6713eaf --- /dev/null +++ b/lib/php/HTMLPurifier/ErrorCollector.php @@ -0,0 +1,209 @@ +<?php + +/** + * Error collection class that enables HTML Purifier to report HTML + * problems back to the user + */ +class HTMLPurifier_ErrorCollector +{ + + /** + * Identifiers for the returned error array. These are purposely numeric + * so list() can be used. + */ + const LINENO = 0; + const SEVERITY = 1; + const MESSAGE = 2; + const CHILDREN = 3; + + protected $errors; + protected $_current; + protected $_stacks = array(array()); + protected $locale; + protected $generator; + protected $context; + + protected $lines = array(); + + public function __construct($context) { + $this->locale =& $context->get('Locale'); + $this->context = $context; + $this->_current =& $this->_stacks[0]; + $this->errors =& $this->_stacks[0]; + } + + /** + * Sends an error message to the collector for later use + * @param $severity int Error severity, PHP error style (don't use E_USER_) + * @param $msg string Error message text + * @param $subst1 string First substitution for $msg + * @param $subst2 string ... + */ + public function send($severity, $msg) { + + $args = array(); + if (func_num_args() > 2) { + $args = func_get_args(); + array_shift($args); + unset($args[0]); + } + + $token = $this->context->get('CurrentToken', true); + $line = $token ? $token->line : $this->context->get('CurrentLine', true); + $col = $token ? $token->col : $this->context->get('CurrentCol', true); + $attr = $this->context->get('CurrentAttr', true); + + // perform special substitutions, also add custom parameters + $subst = array(); + if (!is_null($token)) { + $args['CurrentToken'] = $token; + } + if (!is_null($attr)) { + $subst['$CurrentAttr.Name'] = $attr; + if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr]; + } + + if (empty($args)) { + $msg = $this->locale->getMessage($msg); + } else { + $msg = $this->locale->formatMessage($msg, $args); + } + + if (!empty($subst)) $msg = strtr($msg, $subst); + + // (numerically indexed) + $error = array( + self::LINENO => $line, + self::SEVERITY => $severity, + self::MESSAGE => $msg, + self::CHILDREN => array() + ); + $this->_current[] = $error; + + + // NEW CODE BELOW ... + + $struct = null; + // Top-level errors are either: + // TOKEN type, if $value is set appropriately, or + // "syntax" type, if $value is null + $new_struct = new HTMLPurifier_ErrorStruct(); + $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; + if ($token) $new_struct->value = clone $token; + if (is_int($line) && is_int($col)) { + if (isset($this->lines[$line][$col])) { + $struct = $this->lines[$line][$col]; + } else { + $struct = $this->lines[$line][$col] = $new_struct; + } + // These ksorts may present a performance problem + ksort($this->lines[$line], SORT_NUMERIC); + } else { + if (isset($this->lines[-1])) { + $struct = $this->lines[-1]; + } else { + $struct = $this->lines[-1] = $new_struct; + } + } + ksort($this->lines, SORT_NUMERIC); + + // Now, check if we need to operate on a lower structure + if (!empty($attr)) { + $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); + if (!$struct->value) { + $struct->value = array($attr, 'PUT VALUE HERE'); + } + } + if (!empty($cssprop)) { + $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop); + if (!$struct->value) { + // if we tokenize CSS this might be a little more difficult to do + $struct->value = array($cssprop, 'PUT VALUE HERE'); + } + } + + // Ok, structs are all setup, now time to register the error + $struct->addError($severity, $msg); + } + + /** + * Retrieves raw error data for custom formatter to use + * @param List of arrays in format of array(line of error, + * error severity, error message, + * recursive sub-errors array) + */ + public function getRaw() { + return $this->errors; + } + + /** + * Default HTML formatting implementation for error messages + * @param $config Configuration array, vital for HTML output nature + * @param $errors Errors array to display; used for recursion. + */ + public function getHTMLFormatted($config, $errors = null) { + $ret = array(); + + $this->generator = new HTMLPurifier_Generator($config, $this->context); + if ($errors === null) $errors = $this->errors; + + // 'At line' message needs to be removed + + // generation code for new structure goes here. It needs to be recursive. + foreach ($this->lines as $line => $col_array) { + if ($line == -1) continue; + foreach ($col_array as $col => $struct) { + $this->_renderStruct($ret, $struct, $line, $col); + } + } + if (isset($this->lines[-1])) { + $this->_renderStruct($ret, $this->lines[-1]); + } + + if (empty($errors)) { + return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>'; + } else { + return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>'; + } + + } + + private function _renderStruct(&$ret, $struct, $line = null, $col = null) { + $stack = array($struct); + $context_stack = array(array()); + while ($current = array_pop($stack)) { + $context = array_pop($context_stack); + foreach ($current->errors as $error) { + list($severity, $msg) = $error; + $string = ''; + $string .= '<div>'; + // W3C uses an icon to indicate the severity of the error. + $error = $this->locale->getErrorName($severity); + $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> "; + if (!is_null($line) && !is_null($col)) { + $string .= "<em class=\"location\">Line $line, Column $col: </em> "; + } else { + $string .= '<em class="location">End of Document: </em> '; + } + $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> '; + $string .= '</div>'; + // Here, have a marker for the character on the column appropriate. + // Be sure to clip extremely long lines. + //$string .= '<pre>'; + //$string .= ''; + //$string .= '</pre>'; + $ret[] = $string; + } + foreach ($current->children as $type => $array) { + $context[] = $current; + $stack = array_merge($stack, array_reverse($array, true)); + for ($i = count($array); $i > 0; $i--) { + $context_stack[] = $context; + } + } + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/ErrorStruct.php b/lib/php/HTMLPurifier/ErrorStruct.php new file mode 100644 index 0000000..9bc8996 --- /dev/null +++ b/lib/php/HTMLPurifier/ErrorStruct.php @@ -0,0 +1,60 @@ +<?php + +/** + * Records errors for particular segments of an HTML document such as tokens, + * attributes or CSS properties. They can contain error structs (which apply + * to components of what they represent), but their main purpose is to hold + * errors applying to whatever struct is being used. + */ +class HTMLPurifier_ErrorStruct +{ + + /** + * Possible values for $children first-key. Note that top-level structures + * are automatically token-level. + */ + const TOKEN = 0; + const ATTR = 1; + const CSSPROP = 2; + + /** + * Type of this struct. + */ + public $type; + + /** + * Value of the struct we are recording errors for. There are various + * values for this: + * - TOKEN: Instance of HTMLPurifier_Token + * - ATTR: array('attr-name', 'value') + * - CSSPROP: array('prop-name', 'value') + */ + public $value; + + /** + * Errors registered for this structure. + */ + public $errors = array(); + + /** + * Child ErrorStructs that are from this structure. For example, a TOKEN + * ErrorStruct would contain ATTR ErrorStructs. This is a multi-dimensional + * array in structure: [TYPE]['identifier'] + */ + public $children = array(); + + public function getChild($type, $id) { + if (!isset($this->children[$type][$id])) { + $this->children[$type][$id] = new HTMLPurifier_ErrorStruct(); + $this->children[$type][$id]->type = $type; + } + return $this->children[$type][$id]; + } + + public function addError($severity, $message) { + $this->errors[] = array($severity, $message); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Exception.php b/lib/php/HTMLPurifier/Exception.php new file mode 100644 index 0000000..be85b4c --- /dev/null +++ b/lib/php/HTMLPurifier/Exception.php @@ -0,0 +1,12 @@ +<?php + +/** + * Global exception class for HTML Purifier; any exceptions we throw + * are from here. + */ +class HTMLPurifier_Exception extends Exception +{ + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Filter.php b/lib/php/HTMLPurifier/Filter.php new file mode 100644 index 0000000..9a0e7b0 --- /dev/null +++ b/lib/php/HTMLPurifier/Filter.php @@ -0,0 +1,46 @@ +<?php + +/** + * Represents a pre or post processing filter on HTML Purifier's output + * + * Sometimes, a little ad-hoc fixing of HTML has to be done before + * it gets sent through HTML Purifier: you can use filters to acheive + * this effect. For instance, YouTube videos can be preserved using + * this manner. You could have used a decorator for this task, but + * PHP's support for them is not terribly robust, so we're going + * to just loop through the filters. + * + * Filters should be exited first in, last out. If there are three filters, + * named 1, 2 and 3, the order of execution should go 1->preFilter, + * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter, + * 1->postFilter. + * + * @note Methods are not declared abstract as it is perfectly legitimate + * for an implementation not to want anything to happen on a step + */ + +class HTMLPurifier_Filter +{ + + /** + * Name of the filter for identification purposes + */ + public $name; + + /** + * Pre-processor function, handles HTML before HTML Purifier + */ + public function preFilter($html, $config, $context) { + return $html; + } + + /** + * Post-processor function, handles HTML after HTML Purifier + */ + public function postFilter($html, $config, $context) { + return $html; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Filter/ExtractStyleBlocks.php b/lib/php/HTMLPurifier/Filter/ExtractStyleBlocks.php new file mode 100644 index 0000000..bbf78a6 --- /dev/null +++ b/lib/php/HTMLPurifier/Filter/ExtractStyleBlocks.php @@ -0,0 +1,135 @@ +<?php + +/** + * This filter extracts <style> blocks from input HTML, cleans them up + * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks') + * so they can be used elsewhere in the document. + * + * @note + * See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for + * sample usage. + * + * @note + * This filter can also be used on stylesheets not included in the + * document--something purists would probably prefer. Just directly + * call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS() + */ +class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter +{ + + public $name = 'ExtractStyleBlocks'; + private $_styleMatches = array(); + private $_tidy; + + public function __construct() { + $this->_tidy = new csstidy(); + } + + /** + * Save the contents of CSS blocks to style matches + * @param $matches preg_replace style $matches array + */ + protected function styleCallback($matches) { + $this->_styleMatches[] = $matches[1]; + } + + /** + * Removes inline <style> tags from HTML, saves them for later use + * @todo Extend to indicate non-text/css style blocks + */ + public function preFilter($html, $config, $context) { + $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl'); + if ($tidy !== null) $this->_tidy = $tidy; + $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html); + $style_blocks = $this->_styleMatches; + $this->_styleMatches = array(); // reset + $context->register('StyleBlocks', $style_blocks); // $context must not be reused + if ($this->_tidy) { + foreach ($style_blocks as &$style) { + $style = $this->cleanCSS($style, $config, $context); + } + } + return $html; + } + + /** + * Takes CSS (the stuff found in <style>) and cleans it. + * @warning Requires CSSTidy <http://csstidy.sourceforge.net/> + * @param $css CSS styling to clean + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return Cleaned CSS + */ + public function cleanCSS($css, $config, $context) { + // prepare scope + $scope = $config->get('Filter.ExtractStyleBlocks.Scope'); + if ($scope !== null) { + $scopes = array_map('trim', explode(',', $scope)); + } else { + $scopes = array(); + } + // remove comments from CSS + $css = trim($css); + if (strncmp('<!--', $css, 4) === 0) { + $css = substr($css, 4); + } + if (strlen($css) > 3 && substr($css, -3) == '-->') { + $css = substr($css, 0, -3); + } + $css = trim($css); + $this->_tidy->parse($css); + $css_definition = $config->getDefinition('CSS'); + foreach ($this->_tidy->css as $k => $decls) { + // $decls are all CSS declarations inside an @ selector + $new_decls = array(); + foreach ($decls as $selector => $style) { + $selector = trim($selector); + if ($selector === '') continue; // should not happen + if ($selector[0] === '+') { + if ($selector !== '' && $selector[0] === '+') continue; + } + if (!empty($scopes)) { + $new_selector = array(); // because multiple ones are possible + $selectors = array_map('trim', explode(',', $selector)); + foreach ($scopes as $s1) { + foreach ($selectors as $s2) { + $new_selector[] = "$s1 $s2"; + } + } + $selector = implode(', ', $new_selector); // now it's a string + } + foreach ($style as $name => $value) { + if (!isset($css_definition->info[$name])) { + unset($style[$name]); + continue; + } + $def = $css_definition->info[$name]; + $ret = $def->validate($value, $config, $context); + if ($ret === false) unset($style[$name]); + else $style[$name] = $ret; + } + $new_decls[$selector] = $style; + } + $this->_tidy->css[$k] = $new_decls; + } + // remove stuff that shouldn't be used, could be reenabled + // after security risks are analyzed + $this->_tidy->import = array(); + $this->_tidy->charset = null; + $this->_tidy->namespace = null; + $css = $this->_tidy->print->plain(); + // we are going to escape any special characters <>& to ensure + // that no funny business occurs (i.e. </style> in a font-family prop). + if ($config->get('Filter.ExtractStyleBlocks.Escaping')) { + $css = str_replace( + array('<', '>', '&'), + array('\3C ', '\3E ', '\26 '), + $css + ); + } + return $css; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Filter/YouTube.php b/lib/php/HTMLPurifier/Filter/YouTube.php new file mode 100644 index 0000000..aca972f --- /dev/null +++ b/lib/php/HTMLPurifier/Filter/YouTube.php @@ -0,0 +1,39 @@ +<?php + +class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter +{ + + public $name = 'YouTube'; + + public function preFilter($html, $config, $context) { + $pre_regex = '#<object[^>]+>.+?'. + 'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?</object>#s'; + $pre_replace = '<span class="youtube-embed">\1</span>'; + return preg_replace($pre_regex, $pre_replace, $html); + } + + public function postFilter($html, $config, $context) { + $post_regex = '#<span class="youtube-embed">([A-Za-z0-9\-_]+)</span>#'; + return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); + } + + protected function armorUrl($url) { + return str_replace('--', '--', $url); + } + + protected function postFilterCallback($matches) { + $url = $this->armorUrl($matches[1]); + return '<object width="425" height="350" type="application/x-shockwave-flash" '. + 'data="http://www.youtube.com/v/'.$url.'">'. + '<param name="movie" value="http://www.youtube.com/v/'.$url.'"></param>'. + '<!--[if IE]>'. + '<embed src="http://www.youtube.com/v/'.$url.'"'. + 'type="application/x-shockwave-flash"'. + 'wmode="transparent" width="425" height="350" />'. + '<![endif]-->'. + '</object>'; + + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Generator.php b/lib/php/HTMLPurifier/Generator.php new file mode 100644 index 0000000..24bd8a5 --- /dev/null +++ b/lib/php/HTMLPurifier/Generator.php @@ -0,0 +1,183 @@ +<?php + +/** + * Generates HTML from tokens. + * @todo Refactor interface so that configuration/context is determined + * upon instantiation, no need for messy generateFromTokens() calls + * @todo Make some of the more internal functions protected, and have + * unit tests work around that + */ +class HTMLPurifier_Generator +{ + + /** + * Whether or not generator should produce XML output + */ + private $_xhtml = true; + + /** + * :HACK: Whether or not generator should comment the insides of <script> tags + */ + private $_scriptFix = false; + + /** + * Cache of HTMLDefinition during HTML output to determine whether or + * not attributes should be minimized. + */ + private $_def; + + /** + * Cache of %Output.SortAttr + */ + private $_sortAttr; + + /** + * Configuration for the generator + */ + protected $config; + + /** + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + */ + public function __construct($config, $context) { + $this->config = $config; + $this->_scriptFix = $config->get('Output.CommentScriptContents'); + $this->_sortAttr = $config->get('Output.SortAttr'); + $this->_def = $config->getHTMLDefinition(); + $this->_xhtml = $this->_def->doctype->xml; + } + + /** + * Generates HTML from an array of tokens. + * @param $tokens Array of HTMLPurifier_Token + * @param $config HTMLPurifier_Config object + * @return Generated HTML + */ + public function generateFromTokens($tokens) { + if (!$tokens) return ''; + + // Basic algorithm + $html = ''; + for ($i = 0, $size = count($tokens); $i < $size; $i++) { + if ($this->_scriptFix && $tokens[$i]->name === 'script' + && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { + // script special case + // the contents of the script block must be ONE token + // for this to work. + $html .= $this->generateFromToken($tokens[$i++]); + $html .= $this->generateScriptFromToken($tokens[$i++]); + } + $html .= $this->generateFromToken($tokens[$i]); + } + + // Tidy cleanup + if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) { + $tidy = new Tidy; + $tidy->parseString($html, array( + 'indent'=> true, + 'output-xhtml' => $this->_xhtml, + 'show-body-only' => true, + 'indent-spaces' => 2, + 'wrap' => 68, + ), 'utf8'); + $tidy->cleanRepair(); + $html = (string) $tidy; // explicit cast necessary + } + + // Normalize newlines to system defined value + $nl = $this->config->get('Output.Newline'); + if ($nl === null) $nl = PHP_EOL; + if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + return $html; + } + + /** + * Generates HTML from a single token. + * @param $token HTMLPurifier_Token object. + * @return Generated HTML + */ + public function generateFromToken($token) { + if (!$token instanceof HTMLPurifier_Token) { + trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING); + return ''; + + } elseif ($token instanceof HTMLPurifier_Token_Start) { + $attr = $this->generateAttributes($token->attr, $token->name); + return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_End) { + return '</' . $token->name . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + $attr = $this->generateAttributes($token->attr, $token->name); + return '<' . $token->name . ($attr ? ' ' : '') . $attr . + ( $this->_xhtml ? ' /': '' ) // <br /> v. <br> + . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Text) { + return $this->escape($token->data, ENT_NOQUOTES); + + } elseif ($token instanceof HTMLPurifier_Token_Comment) { + return '<!--' . $token->data . '-->'; + } else { + return ''; + + } + } + + /** + * Special case processor for the contents of script tags + * @warning This runs into problems if there's already a literal + * --> somewhere inside the script contents. + */ + public function generateScriptFromToken($token) { + if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token); + // Thanks <http://lachy.id.au/log/2005/05/script-comments> + $data = preg_replace('#//\s*$#', '', $token->data); + return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>'; + } + + /** + * Generates attribute declarations from attribute array. + * @note This does not include the leading or trailing space. + * @param $assoc_array_of_attributes Attribute array + * @param $element Name of element attributes are for, used to check + * attribute minimization. + * @return Generate HTML fragment for insertion. + */ + public function generateAttributes($assoc_array_of_attributes, $element = false) { + $html = ''; + if ($this->_sortAttr) ksort($assoc_array_of_attributes); + foreach ($assoc_array_of_attributes as $key => $value) { + if (!$this->_xhtml) { + // Remove namespaced attributes + if (strpos($key, ':') !== false) continue; + // Check if we should minimize the attribute: val="val" -> val + if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) { + $html .= $key . ' '; + continue; + } + } + $html .= $key.'="'.$this->escape($value).'" '; + } + return rtrim($html); + } + + /** + * Escapes raw text data. + * @todo This really ought to be protected, but until we have a facility + * for properly generating HTML here w/o using tokens, it stays + * public. + * @param $string String data to escape for HTML. + * @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is + * permissible for non-attribute output. + * @return String escaped data. + */ + public function escape($string, $quote = ENT_COMPAT) { + return htmlspecialchars($string, $quote, 'UTF-8'); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLDefinition.php b/lib/php/HTMLPurifier/HTMLDefinition.php new file mode 100644 index 0000000..c99ac11 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLDefinition.php @@ -0,0 +1,420 @@ +<?php + +/** + * Definition of the purified HTML that describes allowed children, + * attributes, and many other things. + * + * Conventions: + * + * All member variables that are prefixed with info + * (including the main $info array) are used by HTML Purifier internals + * and should not be directly edited when customizing the HTMLDefinition. + * They can usually be set via configuration directives or custom + * modules. + * + * On the other hand, member variables without the info prefix are used + * internally by the HTMLDefinition and MUST NOT be used by other HTML + * Purifier internals. Many of them, however, are public, and may be + * edited by userspace code to tweak the behavior of HTMLDefinition. + * + * @note This class is inspected by Printer_HTMLDefinition; please + * update that class if things here change. + * + * @warning Directives that change this object's structure must be in + * the HTML or Attr namespace! + */ +class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition +{ + + // FULLY-PUBLIC VARIABLES --------------------------------------------- + + /** + * Associative array of element names to HTMLPurifier_ElementDef + */ + public $info = array(); + + /** + * Associative array of global attribute name to attribute definition. + */ + public $info_global_attr = array(); + + /** + * String name of parent element HTML will be going into. + */ + public $info_parent = 'div'; + + /** + * Definition for parent element, allows parent element to be a + * tag that's not allowed inside the HTML fragment. + */ + public $info_parent_def; + + /** + * String name of element used to wrap inline elements in block context + * @note This is rarely used except for BLOCKQUOTEs in strict mode + */ + public $info_block_wrapper = 'p'; + + /** + * Associative array of deprecated tag name to HTMLPurifier_TagTransform + */ + public $info_tag_transform = array(); + + /** + * Indexed list of HTMLPurifier_AttrTransform to be performed before validation. + */ + public $info_attr_transform_pre = array(); + + /** + * Indexed list of HTMLPurifier_AttrTransform to be performed after validation. + */ + public $info_attr_transform_post = array(); + + /** + * Nested lookup array of content set name (Block, Inline) to + * element name to whether or not it belongs in that content set. + */ + public $info_content_sets = array(); + + /** + * Indexed list of HTMLPurifier_Injector to be used. + */ + public $info_injector = array(); + + /** + * Doctype object + */ + public $doctype; + + + + // RAW CUSTOMIZATION STUFF -------------------------------------------- + + /** + * Adds a custom attribute to a pre-existing element + * @note This is strictly convenience, and does not have a corresponding + * method in HTMLPurifier_HTMLModule + * @param $element_name String element name to add attribute to + * @param $attr_name String name of attribute + * @param $def Attribute definition, can be string or object, see + * HTMLPurifier_AttrTypes for details + */ + public function addAttribute($element_name, $attr_name, $def) { + $module = $this->getAnonymousModule(); + if (!isset($module->info[$element_name])) { + $element = $module->addBlankElement($element_name); + } else { + $element = $module->info[$element_name]; + } + $element->attr[$attr_name] = $def; + } + + /** + * Adds a custom element to your HTML definition + * @note See HTMLPurifier_HTMLModule::addElement for detailed + * parameter and return value descriptions. + */ + public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) { + $module = $this->getAnonymousModule(); + // assume that if the user is calling this, the element + // is safe. This may not be a good idea + $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); + return $element; + } + + /** + * Adds a blank element to your HTML definition, for overriding + * existing behavior + * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed + * parameter and return value descriptions. + */ + public function addBlankElement($element_name) { + $module = $this->getAnonymousModule(); + $element = $module->addBlankElement($element_name); + return $element; + } + + /** + * Retrieves a reference to the anonymous module, so you can + * bust out advanced features without having to make your own + * module. + */ + public function getAnonymousModule() { + if (!$this->_anonModule) { + $this->_anonModule = new HTMLPurifier_HTMLModule(); + $this->_anonModule->name = 'Anonymous'; + } + return $this->_anonModule; + } + + private $_anonModule; + + + // PUBLIC BUT INTERNAL VARIABLES -------------------------------------- + + public $type = 'HTML'; + public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */ + + /** + * Performs low-cost, preliminary initialization. + */ + public function __construct() { + $this->manager = new HTMLPurifier_HTMLModuleManager(); + } + + protected function doSetup($config) { + $this->processModules($config); + $this->setupConfigStuff($config); + unset($this->manager); + + // cleanup some of the element definitions + foreach ($this->info as $k => $v) { + unset($this->info[$k]->content_model); + unset($this->info[$k]->content_model_type); + } + } + + /** + * Extract out the information from the manager + */ + protected function processModules($config) { + + if ($this->_anonModule) { + // for user specific changes + // this is late-loaded so we don't have to deal with PHP4 + // reference wonky-ness + $this->manager->addModule($this->_anonModule); + unset($this->_anonModule); + } + + $this->manager->setup($config); + $this->doctype = $this->manager->doctype; + + foreach ($this->manager->modules as $module) { + foreach($module->info_tag_transform as $k => $v) { + if ($v === false) unset($this->info_tag_transform[$k]); + else $this->info_tag_transform[$k] = $v; + } + foreach($module->info_attr_transform_pre as $k => $v) { + if ($v === false) unset($this->info_attr_transform_pre[$k]); + else $this->info_attr_transform_pre[$k] = $v; + } + foreach($module->info_attr_transform_post as $k => $v) { + if ($v === false) unset($this->info_attr_transform_post[$k]); + else $this->info_attr_transform_post[$k] = $v; + } + foreach ($module->info_injector as $k => $v) { + if ($v === false) unset($this->info_injector[$k]); + else $this->info_injector[$k] = $v; + } + } + + $this->info = $this->manager->getElements(); + $this->info_content_sets = $this->manager->contentSets->lookup; + + } + + /** + * Sets up stuff based on config. We need a better way of doing this. + */ + protected function setupConfigStuff($config) { + + $block_wrapper = $config->get('HTML.BlockWrapper'); + if (isset($this->info_content_sets['Block'][$block_wrapper])) { + $this->info_block_wrapper = $block_wrapper; + } else { + trigger_error('Cannot use non-block element as block wrapper', + E_USER_ERROR); + } + + $parent = $config->get('HTML.Parent'); + $def = $this->manager->getElement($parent, true); + if ($def) { + $this->info_parent = $parent; + $this->info_parent_def = $def; + } else { + trigger_error('Cannot use unrecognized element as parent', + E_USER_ERROR); + $this->info_parent_def = $this->manager->getElement($this->info_parent, true); + } + + // support template text + $support = "(for information on implementing this, see the ". + "support forums) "; + + // setup allowed elements ----------------------------------------- + + $allowed_elements = $config->get('HTML.AllowedElements'); + $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early + + if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { + $allowed = $config->get('HTML.Allowed'); + if (is_string($allowed)) { + list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); + } + } + + if (is_array($allowed_elements)) { + foreach ($this->info as $name => $d) { + if(!isset($allowed_elements[$name])) unset($this->info[$name]); + unset($allowed_elements[$name]); + } + // emit errors + foreach ($allowed_elements as $element => $d) { + $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! + trigger_error("Element '$element' is not supported $support", E_USER_WARNING); + } + } + + // setup allowed attributes --------------------------------------- + + $allowed_attributes_mutable = $allowed_attributes; // by copy! + if (is_array($allowed_attributes)) { + + // This actually doesn't do anything, since we went away from + // global attributes. It's possible that userland code uses + // it, but HTMLModuleManager doesn't! + foreach ($this->info_global_attr as $attr => $x) { + $keys = array($attr, "*@$attr", "*.$attr"); + $delete = true; + foreach ($keys as $key) { + if ($delete && isset($allowed_attributes[$key])) { + $delete = false; + } + if (isset($allowed_attributes_mutable[$key])) { + unset($allowed_attributes_mutable[$key]); + } + } + if ($delete) unset($this->info_global_attr[$attr]); + } + + foreach ($this->info as $tag => $info) { + foreach ($info->attr as $attr => $x) { + $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); + $delete = true; + foreach ($keys as $key) { + if ($delete && isset($allowed_attributes[$key])) { + $delete = false; + } + if (isset($allowed_attributes_mutable[$key])) { + unset($allowed_attributes_mutable[$key]); + } + } + if ($delete) unset($this->info[$tag]->attr[$attr]); + } + } + // emit errors + foreach ($allowed_attributes_mutable as $elattr => $d) { + $bits = preg_split('/[.@]/', $elattr, 2); + $c = count($bits); + switch ($c) { + case 2: + if ($bits[0] !== '*') { + $element = htmlspecialchars($bits[0]); + $attribute = htmlspecialchars($bits[1]); + if (!isset($this->info[$element])) { + trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support"); + } else { + trigger_error("Attribute '$attribute' in element '$element' not supported $support", + E_USER_WARNING); + } + break; + } + // otherwise fall through + case 1: + $attribute = htmlspecialchars($bits[0]); + trigger_error("Global attribute '$attribute' is not ". + "supported in any elements $support", + E_USER_WARNING); + break; + } + } + + } + + // setup forbidden elements --------------------------------------- + + $forbidden_elements = $config->get('HTML.ForbiddenElements'); + $forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); + + foreach ($this->info as $tag => $info) { + if (isset($forbidden_elements[$tag])) { + unset($this->info[$tag]); + continue; + } + foreach ($info->attr as $attr => $x) { + if ( + isset($forbidden_attributes["$tag@$attr"]) || + isset($forbidden_attributes["*@$attr"]) || + isset($forbidden_attributes[$attr]) + ) { + unset($this->info[$tag]->attr[$attr]); + continue; + } // this segment might get removed eventually + elseif (isset($forbidden_attributes["$tag.$attr"])) { + // $tag.$attr are not user supplied, so no worries! + trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING); + } + } + } + foreach ($forbidden_attributes as $key => $v) { + if (strlen($key) < 2) continue; + if ($key[0] != '*') continue; + if ($key[1] == '.') { + trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING); + } + } + + // setup injectors ----------------------------------------------------- + foreach ($this->info_injector as $i => $injector) { + if ($injector->checkNeeded($config) !== false) { + // remove injector that does not have it's required + // elements/attributes present, and is thus not needed. + unset($this->info_injector[$i]); + } + } + } + + /** + * Parses a TinyMCE-flavored Allowed Elements and Attributes list into + * separate lists for processing. Format is element[attr1|attr2],element2... + * @warning Although it's largely drawn from TinyMCE's implementation, + * it is different, and you'll probably have to modify your lists + * @param $list String list to parse + * @param array($allowed_elements, $allowed_attributes) + * @todo Give this its own class, probably static interface + */ + public function parseTinyMCEAllowedList($list) { + + $list = str_replace(array(' ', "\t"), '', $list); + + $elements = array(); + $attributes = array(); + + $chunks = preg_split('/(,|[\n\r]+)/', $list); + foreach ($chunks as $chunk) { + if (empty($chunk)) continue; + // remove TinyMCE element control characters + if (!strpos($chunk, '[')) { + $element = $chunk; + $attr = false; + } else { + list($element, $attr) = explode('[', $chunk); + } + if ($element !== '*') $elements[$element] = true; + if (!$attr) continue; + $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] + $attr = explode('|', $attr); + foreach ($attr as $key) { + $attributes["$element.$key"] = true; + } + } + + return array($elements, $attributes); + + } + + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule.php b/lib/php/HTMLPurifier/HTMLModule.php new file mode 100644 index 0000000..072cf68 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule.php @@ -0,0 +1,244 @@ +<?php + +/** + * Represents an XHTML 1.1 module, with information on elements, tags + * and attributes. + * @note Even though this is technically XHTML 1.1, it is also used for + * regular HTML parsing. We are using modulization as a convenient + * way to represent the internals of HTMLDefinition, and our + * implementation is by no means conforming and does not directly + * use the normative DTDs or XML schemas. + * @note The public variables in a module should almost directly + * correspond to the variables in HTMLPurifier_HTMLDefinition. + * However, the prefix info carries no special meaning in these + * objects (include it anyway if that's the correspondence though). + * @todo Consider making some member functions protected + */ + +class HTMLPurifier_HTMLModule +{ + + // -- Overloadable ---------------------------------------------------- + + /** + * Short unique string identifier of the module + */ + public $name; + + /** + * Informally, a list of elements this module changes. Not used in + * any significant way. + */ + public $elements = array(); + + /** + * Associative array of element names to element definitions. + * Some definitions may be incomplete, to be merged in later + * with the full definition. + */ + public $info = array(); + + /** + * Associative array of content set names to content set additions. + * This is commonly used to, say, add an A element to the Inline + * content set. This corresponds to an internal variable $content_sets + * and NOT info_content_sets member variable of HTMLDefinition. + */ + public $content_sets = array(); + + /** + * Associative array of attribute collection names to attribute + * collection additions. More rarely used for adding attributes to + * the global collections. Example is the StyleAttribute module adding + * the style attribute to the Core. Corresponds to HTMLDefinition's + * attr_collections->info, since the object's data is only info, + * with extra behavior associated with it. + */ + public $attr_collections = array(); + + /** + * Associative array of deprecated tag name to HTMLPurifier_TagTransform + */ + public $info_tag_transform = array(); + + /** + * List of HTMLPurifier_AttrTransform to be performed before validation. + */ + public $info_attr_transform_pre = array(); + + /** + * List of HTMLPurifier_AttrTransform to be performed after validation. + */ + public $info_attr_transform_post = array(); + + /** + * List of HTMLPurifier_Injector to be performed during well-formedness fixing. + * An injector will only be invoked if all of it's pre-requisites are met; + * if an injector fails setup, there will be no error; it will simply be + * silently disabled. + */ + public $info_injector = array(); + + /** + * Boolean flag that indicates whether or not getChildDef is implemented. + * For optimization reasons: may save a call to a function. Be sure + * to set it if you do implement getChildDef(), otherwise it will have + * no effect! + */ + public $defines_child_def = false; + + /** + * Boolean flag whether or not this module is safe. If it is not safe, all + * of its members are unsafe. Modules are safe by default (this might be + * slightly dangerous, but it doesn't make much sense to force HTML Purifier, + * which is based off of safe HTML, to explicitly say, "This is safe," even + * though there are modules which are "unsafe") + * + * @note Previously, safety could be applied at an element level granularity. + * We've removed this ability, so in order to add "unsafe" elements + * or attributes, a dedicated module with this property set to false + * must be used. + */ + public $safe = true; + + /** + * Retrieves a proper HTMLPurifier_ChildDef subclass based on + * content_model and content_model_type member variables of + * the HTMLPurifier_ElementDef class. There is a similar function + * in HTMLPurifier_HTMLDefinition. + * @param $def HTMLPurifier_ElementDef instance + * @return HTMLPurifier_ChildDef subclass + */ + public function getChildDef($def) {return false;} + + // -- Convenience ----------------------------------------------------- + + /** + * Convenience function that sets up a new element + * @param $element Name of element to add + * @param $type What content set should element be registered to? + * Set as false to skip this step. + * @param $contents Allowed children in form of: + * "$content_model_type: $content_model" + * @param $attr_includes What attribute collections to register to + * element? + * @param $attr What unique attributes does the element define? + * @note See ElementDef for in-depth descriptions of these parameters. + * @return Created element definition object, so you + * can set advanced parameters + */ + public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) { + $this->elements[] = $element; + // parse content_model + list($content_model_type, $content_model) = $this->parseContents($contents); + // merge in attribute inclusions + $this->mergeInAttrIncludes($attr, $attr_includes); + // add element to content sets + if ($type) $this->addElementToContentSet($element, $type); + // create element + $this->info[$element] = HTMLPurifier_ElementDef::create( + $content_model, $content_model_type, $attr + ); + // literal object $contents means direct child manipulation + if (!is_string($contents)) $this->info[$element]->child = $contents; + return $this->info[$element]; + } + + /** + * Convenience function that creates a totally blank, non-standalone + * element. + * @param $element Name of element to create + * @return Created element + */ + public function addBlankElement($element) { + if (!isset($this->info[$element])) { + $this->elements[] = $element; + $this->info[$element] = new HTMLPurifier_ElementDef(); + $this->info[$element]->standalone = false; + } else { + trigger_error("Definition for $element already exists in module, cannot redefine"); + } + return $this->info[$element]; + } + + /** + * Convenience function that registers an element to a content set + * @param Element to register + * @param Name content set (warning: case sensitive, usually upper-case + * first letter) + */ + public function addElementToContentSet($element, $type) { + if (!isset($this->content_sets[$type])) $this->content_sets[$type] = ''; + else $this->content_sets[$type] .= ' | '; + $this->content_sets[$type] .= $element; + } + + /** + * Convenience function that transforms single-string contents + * into separate content model and content model type + * @param $contents Allowed children in form of: + * "$content_model_type: $content_model" + * @note If contents is an object, an array of two nulls will be + * returned, and the callee needs to take the original $contents + * and use it directly. + */ + public function parseContents($contents) { + if (!is_string($contents)) return array(null, null); // defer + switch ($contents) { + // check for shorthand content model forms + case 'Empty': + return array('empty', ''); + case 'Inline': + return array('optional', 'Inline | #PCDATA'); + case 'Flow': + return array('optional', 'Flow | #PCDATA'); + } + list($content_model_type, $content_model) = explode(':', $contents); + $content_model_type = strtolower(trim($content_model_type)); + $content_model = trim($content_model); + return array($content_model_type, $content_model); + } + + /** + * Convenience function that merges a list of attribute includes into + * an attribute array. + * @param $attr Reference to attr array to modify + * @param $attr_includes Array of includes / string include to merge in + */ + public function mergeInAttrIncludes(&$attr, $attr_includes) { + if (!is_array($attr_includes)) { + if (empty($attr_includes)) $attr_includes = array(); + else $attr_includes = array($attr_includes); + } + $attr[0] = $attr_includes; + } + + /** + * Convenience function that generates a lookup table with boolean + * true as value. + * @param $list List of values to turn into a lookup + * @note You can also pass an arbitrary number of arguments in + * place of the regular argument + * @return Lookup array equivalent of list + */ + public function makeLookup($list) { + if (is_string($list)) $list = func_get_args(); + $ret = array(); + foreach ($list as $value) { + if (is_null($value)) continue; + $ret[$value] = true; + } + return $ret; + } + + /** + * Lazy load construction of the module after determining whether + * or not it's needed, and also when a finalized configuration object + * is available. + * @param $config Instance of HTMLPurifier_Config + */ + public function setup($config) {} + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Bdo.php b/lib/php/HTMLPurifier/HTMLModule/Bdo.php new file mode 100644 index 0000000..3d66f1b --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Bdo.php @@ -0,0 +1,31 @@ +<?php + +/** + * XHTML 1.1 Bi-directional Text Module, defines elements that + * declare directionality of content. Text Extension Module. + */ +class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule +{ + + public $name = 'Bdo'; + public $attr_collections = array( + 'I18N' => array('dir' => false) + ); + + public function setup($config) { + $bdo = $this->addElement( + 'bdo', 'Inline', 'Inline', array('Core', 'Lang'), + array( + 'dir' => 'Enum#ltr,rtl', // required + // The Abstract Module specification has the attribute + // inclusions wrong for bdo: bdo allows Lang + ) + ); + $bdo->attr_transform_post['required-dir'] = new HTMLPurifier_AttrTransform_BdoDir(); + + $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl'; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/CommonAttributes.php b/lib/php/HTMLPurifier/HTMLModule/CommonAttributes.php new file mode 100644 index 0000000..7c15da8 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/CommonAttributes.php @@ -0,0 +1,26 @@ +<?php + +class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule +{ + public $name = 'CommonAttributes'; + + public $attr_collections = array( + 'Core' => array( + 0 => array('Style'), + // 'xml:space' => false, + 'class' => 'Class', + 'id' => 'ID', + 'title' => 'CDATA', + ), + 'Lang' => array(), + 'I18N' => array( + 0 => array('Lang'), // proprietary, for xml:lang/lang + ), + 'Common' => array( + 0 => array('Core', 'I18N') + ) + ); + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Edit.php b/lib/php/HTMLPurifier/HTMLModule/Edit.php new file mode 100644 index 0000000..ff93690 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Edit.php @@ -0,0 +1,38 @@ +<?php + +/** + * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension + * Module. + */ +class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule +{ + + public $name = 'Edit'; + + public function setup($config) { + $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow'; + $attr = array( + 'cite' => 'URI', + // 'datetime' => 'Datetime', // not implemented + ); + $this->addElement('del', 'Inline', $contents, 'Common', $attr); + $this->addElement('ins', 'Inline', $contents, 'Common', $attr); + } + + // HTML 4.01 specifies that ins/del must not contain block + // elements when used in an inline context, chameleon is + // a complicated workaround to acheive this effect + + // Inline context ! Block context (exclamation mark is + // separator, see getChildDef for parsing) + + public $defines_child_def = true; + public function getChildDef($def) { + if ($def->content_model_type != 'chameleon') return false; + $value = explode('!', $def->content_model); + return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Forms.php b/lib/php/HTMLPurifier/HTMLModule/Forms.php new file mode 100644 index 0000000..44c22f6 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Forms.php @@ -0,0 +1,118 @@ +<?php + +/** + * XHTML 1.1 Forms module, defines all form-related elements found in HTML 4. + */ +class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule +{ + public $name = 'Forms'; + public $safe = false; + + public $content_sets = array( + 'Block' => 'Form', + 'Inline' => 'Formctrl', + ); + + public function setup($config) { + $form = $this->addElement('form', 'Form', + 'Required: Heading | List | Block | fieldset', 'Common', array( + 'accept' => 'ContentTypes', + 'accept-charset' => 'Charsets', + 'action*' => 'URI', + 'method' => 'Enum#get,post', + // really ContentType, but these two are the only ones used today + 'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data', + )); + $form->excludes = array('form' => true); + + $input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array( + 'accept' => 'ContentTypes', + 'accesskey' => 'Character', + 'alt' => 'Text', + 'checked' => 'Bool#checked', + 'disabled' => 'Bool#disabled', + 'maxlength' => 'Number', + 'name' => 'CDATA', + 'readonly' => 'Bool#readonly', + 'size' => 'Number', + 'src' => 'URI#embeds', + 'tabindex' => 'Number', + 'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image', + 'value' => 'CDATA', + )); + $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input(); + + $this->addElement('select', 'Formctrl', 'Required: optgroup | option', 'Common', array( + 'disabled' => 'Bool#disabled', + 'multiple' => 'Bool#multiple', + 'name' => 'CDATA', + 'size' => 'Number', + 'tabindex' => 'Number', + )); + + $this->addElement('option', false, 'Optional: #PCDATA', 'Common', array( + 'disabled' => 'Bool#disabled', + 'label' => 'Text', + 'selected' => 'Bool#selected', + 'value' => 'CDATA', + )); + // It's illegal for there to be more than one selected, but not + // be multiple. Also, no selected means undefined behavior. This might + // be difficult to implement; perhaps an injector, or a context variable. + + $textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array( + 'accesskey' => 'Character', + 'cols*' => 'Number', + 'disabled' => 'Bool#disabled', + 'name' => 'CDATA', + 'readonly' => 'Bool#readonly', + 'rows*' => 'Number', + 'tabindex' => 'Number', + )); + $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea(); + + $button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array( + 'accesskey' => 'Character', + 'disabled' => 'Bool#disabled', + 'name' => 'CDATA', + 'tabindex' => 'Number', + 'type' => 'Enum#button,submit,reset', + 'value' => 'CDATA', + )); + + // For exclusions, ideally we'd specify content sets, not literal elements + $button->excludes = $this->makeLookup( + 'form', 'fieldset', // Form + 'input', 'select', 'textarea', 'label', 'button', // Formctrl + 'a' // as per HTML 4.01 spec, this is omitted by modularization + ); + + // Extra exclusion: img usemap="" is not permitted within this element. + // We'll omit this for now, since we don't have any good way of + // indicating it yet. + + // This is HIGHLY user-unfriendly; we need a custom child-def for this + $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common'); + + $label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array( + 'accesskey' => 'Character', + // 'for' => 'IDREF', // IDREF not implemented, cannot allow + )); + $label->excludes = array('label' => true); + + $this->addElement('legend', false, 'Optional: #PCDATA | Inline', 'Common', array( + 'accesskey' => 'Character', + )); + + $this->addElement('optgroup', false, 'Required: option', 'Common', array( + 'disabled' => 'Bool#disabled', + 'label*' => 'Text', + )); + + // Don't forget an injector for <isindex>. This one's a little complex + // because it maps to multiple elements. + + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Hypertext.php b/lib/php/HTMLPurifier/HTMLModule/Hypertext.php new file mode 100644 index 0000000..d7e9bdd --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Hypertext.php @@ -0,0 +1,31 @@ +<?php + +/** + * XHTML 1.1 Hypertext Module, defines hypertext links. Core Module. + */ +class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule +{ + + public $name = 'Hypertext'; + + public function setup($config) { + $a = $this->addElement( + 'a', 'Inline', 'Inline', 'Common', + array( + // 'accesskey' => 'Character', + // 'charset' => 'Charset', + 'href' => 'URI', + // 'hreflang' => 'LanguageCode', + 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), + 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), + // 'tabindex' => 'Number', + // 'type' => 'ContentType', + ) + ); + $a->formatting = true; + $a->excludes = array('a' => true); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Image.php b/lib/php/HTMLPurifier/HTMLModule/Image.php new file mode 100644 index 0000000..948d435 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Image.php @@ -0,0 +1,40 @@ +<?php + +/** + * XHTML 1.1 Image Module provides basic image embedding. + * @note There is specialized code for removing empty images in + * HTMLPurifier_Strategy_RemoveForeignElements + */ +class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule +{ + + public $name = 'Image'; + + public function setup($config) { + $max = $config->get('HTML.MaxImgLength'); + $img = $this->addElement( + 'img', 'Inline', 'Empty', 'Common', + array( + 'alt*' => 'Text', + // According to the spec, it's Length, but percents can + // be abused, so we allow only Pixels. + 'height' => 'Pixels#' . $max, + 'width' => 'Pixels#' . $max, + 'longdesc' => 'URI', + 'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded + ) + ); + if ($max === null || $config->get('HTML.Trusted')) { + $img->attr['height'] = + $img->attr['width'] = 'Length'; + } + + // kind of strange, but splitting things up would be inefficient + $img->attr_transform_pre[] = + $img->attr_transform_post[] = + new HTMLPurifier_AttrTransform_ImgRequired(); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Legacy.php b/lib/php/HTMLPurifier/HTMLModule/Legacy.php new file mode 100644 index 0000000..df33927 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Legacy.php @@ -0,0 +1,143 @@ +<?php + +/** + * XHTML 1.1 Legacy module defines elements that were previously + * deprecated. + * + * @note Not all legacy elements have been implemented yet, which + * is a bit of a reverse problem as compared to browsers! In + * addition, this legacy module may implement a bit more than + * mandated by XHTML 1.1. + * + * This module can be used in combination with TransformToStrict in order + * to transform as many deprecated elements as possible, but retain + * questionably deprecated elements that do not have good alternatives + * as well as transform elements that don't have an implementation. + * See docs/ref-strictness.txt for more details. + */ + +class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule +{ + + public $name = 'Legacy'; + + public function setup($config) { + + $this->addElement('basefont', 'Inline', 'Empty', false, array( + 'color' => 'Color', + 'face' => 'Text', // extremely broad, we should + 'size' => 'Text', // tighten it + 'id' => 'ID' + )); + $this->addElement('center', 'Block', 'Flow', 'Common'); + $this->addElement('dir', 'Block', 'Required: li', 'Common', array( + 'compact' => 'Bool#compact' + )); + $this->addElement('font', 'Inline', 'Inline', array('Core', 'I18N'), array( + 'color' => 'Color', + 'face' => 'Text', // extremely broad, we should + 'size' => 'Text', // tighten it + )); + $this->addElement('menu', 'Block', 'Required: li', 'Common', array( + 'compact' => 'Bool#compact' + )); + + $s = $this->addElement('s', 'Inline', 'Inline', 'Common'); + $s->formatting = true; + + $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common'); + $strike->formatting = true; + + $u = $this->addElement('u', 'Inline', 'Inline', 'Common'); + $u->formatting = true; + + // setup modifications to old elements + + $align = 'Enum#left,right,center,justify'; + + $address = $this->addBlankElement('address'); + $address->content_model = 'Inline | #PCDATA | p'; + $address->content_model_type = 'optional'; + $address->child = false; + + $blockquote = $this->addBlankElement('blockquote'); + $blockquote->content_model = 'Flow | #PCDATA'; + $blockquote->content_model_type = 'optional'; + $blockquote->child = false; + + $br = $this->addBlankElement('br'); + $br->attr['clear'] = 'Enum#left,all,right,none'; + + $caption = $this->addBlankElement('caption'); + $caption->attr['align'] = 'Enum#top,bottom,left,right'; + + $div = $this->addBlankElement('div'); + $div->attr['align'] = $align; + + $dl = $this->addBlankElement('dl'); + $dl->attr['compact'] = 'Bool#compact'; + + for ($i = 1; $i <= 6; $i++) { + $h = $this->addBlankElement("h$i"); + $h->attr['align'] = $align; + } + + $hr = $this->addBlankElement('hr'); + $hr->attr['align'] = $align; + $hr->attr['noshade'] = 'Bool#noshade'; + $hr->attr['size'] = 'Pixels'; + $hr->attr['width'] = 'Length'; + + $img = $this->addBlankElement('img'); + $img->attr['align'] = 'Enum#top,middle,bottom,left,right'; + $img->attr['border'] = 'Pixels'; + $img->attr['hspace'] = 'Pixels'; + $img->attr['vspace'] = 'Pixels'; + + // figure out this integer business + + $li = $this->addBlankElement('li'); + $li->attr['value'] = new HTMLPurifier_AttrDef_Integer(); + $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle'; + + $ol = $this->addBlankElement('ol'); + $ol->attr['compact'] = 'Bool#compact'; + $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer(); + $ol->attr['type'] = 'Enum#s:1,i,I,a,A'; + + $p = $this->addBlankElement('p'); + $p->attr['align'] = $align; + + $pre = $this->addBlankElement('pre'); + $pre->attr['width'] = 'Number'; + + // script omitted + + $table = $this->addBlankElement('table'); + $table->attr['align'] = 'Enum#left,center,right'; + $table->attr['bgcolor'] = 'Color'; + + $tr = $this->addBlankElement('tr'); + $tr->attr['bgcolor'] = 'Color'; + + $th = $this->addBlankElement('th'); + $th->attr['bgcolor'] = 'Color'; + $th->attr['height'] = 'Length'; + $th->attr['nowrap'] = 'Bool#nowrap'; + $th->attr['width'] = 'Length'; + + $td = $this->addBlankElement('td'); + $td->attr['bgcolor'] = 'Color'; + $td->attr['height'] = 'Length'; + $td->attr['nowrap'] = 'Bool#nowrap'; + $td->attr['width'] = 'Length'; + + $ul = $this->addBlankElement('ul'); + $ul->attr['compact'] = 'Bool#compact'; + $ul->attr['type'] = 'Enum#square,disc,circle'; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/List.php b/lib/php/HTMLPurifier/HTMLModule/List.php new file mode 100644 index 0000000..1d15f27 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/List.php @@ -0,0 +1,35 @@ +<?php + +/** + * XHTML 1.1 List Module, defines list-oriented elements. Core Module. + */ +class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule +{ + + public $name = 'List'; + + // According to the abstract schema, the List content set is a fully formed + // one or more expr, but it invariably occurs in an optional declaration + // so we're not going to do that subtlety. It might cause trouble + // if a user defines "List" and expects that multiple lists are + // allowed to be specified, but then again, that's not very intuitive. + // Furthermore, the actual XML Schema may disagree. Regardless, + // we don't have support for such nested expressions without using + // the incredibly inefficient and draconic Custom ChildDef. + + public $content_sets = array('Flow' => 'List'); + + public function setup($config) { + $this->addElement('ol', 'List', 'Required: li', 'Common'); + $this->addElement('ul', 'List', 'Required: li', 'Common'); + $this->addElement('dl', 'List', 'Required: dt | dd', 'Common'); + + $this->addElement('li', false, 'Flow', 'Common'); + + $this->addElement('dd', false, 'Flow', 'Common'); + $this->addElement('dt', false, 'Inline', 'Common'); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Name.php b/lib/php/HTMLPurifier/HTMLModule/Name.php new file mode 100644 index 0000000..05694b4 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Name.php @@ -0,0 +1,21 @@ +<?php + +class HTMLPurifier_HTMLModule_Name extends HTMLPurifier_HTMLModule +{ + + public $name = 'Name'; + + public function setup($config) { + $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map'); + foreach ($elements as $name) { + $element = $this->addBlankElement($name); + $element->attr['name'] = 'CDATA'; + if (!$config->get('HTML.Attr.Name.UseCDATA')) { + $element->attr_transform_post['NameSync'] = new HTMLPurifier_AttrTransform_NameSync(); + } + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/lib/php/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php new file mode 100644 index 0000000..5f1b14a --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php @@ -0,0 +1,14 @@ +<?php + +class HTMLPurifier_HTMLModule_NonXMLCommonAttributes extends HTMLPurifier_HTMLModule +{ + public $name = 'NonXMLCommonAttributes'; + + public $attr_collections = array( + 'Lang' => array( + 'lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Object.php b/lib/php/HTMLPurifier/HTMLModule/Object.php new file mode 100644 index 0000000..193c101 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Object.php @@ -0,0 +1,47 @@ +<?php + +/** + * XHTML 1.1 Object Module, defines elements for generic object inclusion + * @warning Users will commonly use <embed> to cater to legacy browsers: this + * module does not allow this sort of behavior + */ +class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule +{ + + public $name = 'Object'; + public $safe = false; + + public function setup($config) { + + $this->addElement('object', 'Inline', 'Optional: #PCDATA | Flow | param', 'Common', + array( + 'archive' => 'URI', + 'classid' => 'URI', + 'codebase' => 'URI', + 'codetype' => 'Text', + 'data' => 'URI', + 'declare' => 'Bool#declare', + 'height' => 'Length', + 'name' => 'CDATA', + 'standby' => 'Text', + 'tabindex' => 'Number', + 'type' => 'ContentType', + 'width' => 'Length' + ) + ); + + $this->addElement('param', false, 'Empty', false, + array( + 'id' => 'ID', + 'name*' => 'Text', + 'type' => 'Text', + 'value' => 'Text', + 'valuetype' => 'Enum#data,ref,object' + ) + ); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Presentation.php b/lib/php/HTMLPurifier/HTMLModule/Presentation.php new file mode 100644 index 0000000..8ff0b5e --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Presentation.php @@ -0,0 +1,36 @@ +<?php + +/** + * XHTML 1.1 Presentation Module, defines simple presentation-related + * markup. Text Extension Module. + * @note The official XML Schema and DTD specs further divide this into + * two modules: + * - Block Presentation (hr) + * - Inline Presentation (b, big, i, small, sub, sup, tt) + * We have chosen not to heed this distinction, as content_sets + * provides satisfactory disambiguation. + */ +class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule +{ + + public $name = 'Presentation'; + + public function setup($config) { + $this->addElement('hr', 'Block', 'Empty', 'Common'); + $this->addElement('sub', 'Inline', 'Inline', 'Common'); + $this->addElement('sup', 'Inline', 'Inline', 'Common'); + $b = $this->addElement('b', 'Inline', 'Inline', 'Common'); + $b->formatting = true; + $big = $this->addElement('big', 'Inline', 'Inline', 'Common'); + $big->formatting = true; + $i = $this->addElement('i', 'Inline', 'Inline', 'Common'); + $i->formatting = true; + $small = $this->addElement('small', 'Inline', 'Inline', 'Common'); + $small->formatting = true; + $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common'); + $tt->formatting = true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Proprietary.php b/lib/php/HTMLPurifier/HTMLModule/Proprietary.php new file mode 100644 index 0000000..dd36a3d --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Proprietary.php @@ -0,0 +1,33 @@ +<?php + +/** + * Module defines proprietary tags and attributes in HTML. + * @warning If this module is enabled, standards-compliance is off! + */ +class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule +{ + + public $name = 'Proprietary'; + + public function setup($config) { + + $this->addElement('marquee', 'Inline', 'Flow', 'Common', + array( + 'direction' => 'Enum#left,right,up,down', + 'behavior' => 'Enum#alternate', + 'width' => 'Length', + 'height' => 'Length', + 'scrolldelay' => 'Number', + 'scrollamount' => 'Number', + 'loop' => 'Number', + 'bgcolor' => 'Color', + 'hspace' => 'Pixels', + 'vspace' => 'Pixels', + ) + ); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Ruby.php b/lib/php/HTMLPurifier/HTMLModule/Ruby.php new file mode 100644 index 0000000..b26a0a3 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Ruby.php @@ -0,0 +1,27 @@ +<?php + +/** + * XHTML 1.1 Ruby Annotation Module, defines elements that indicate + * short runs of text alongside base text for annotation or pronounciation. + */ +class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule +{ + + public $name = 'Ruby'; + + public function setup($config) { + $this->addElement('ruby', 'Inline', + 'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))', + 'Common'); + $this->addElement('rbc', false, 'Required: rb', 'Common'); + $this->addElement('rtc', false, 'Required: rt', 'Common'); + $rb = $this->addElement('rb', false, 'Inline', 'Common'); + $rb->excludes = array('ruby' => true); + $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number')); + $rt->excludes = array('ruby' => true); + $this->addElement('rp', false, 'Optional: #PCDATA', 'Common'); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/SafeEmbed.php b/lib/php/HTMLPurifier/HTMLModule/SafeEmbed.php new file mode 100644 index 0000000..8fc03cb --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -0,0 +1,33 @@ +<?php + +/** + * A "safe" embed module. See SafeObject. This is a proprietary element. + */ +class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule +{ + + public $name = 'SafeEmbed'; + + public function setup($config) { + + $max = $config->get('HTML.MaxImgLength'); + $embed = $this->addElement( + 'embed', 'Inline', 'Empty', 'Common', + array( + 'src*' => 'URI#embedded', + 'type' => 'Enum#application/x-shockwave-flash', + 'width' => 'Pixels#' . $max, + 'height' => 'Pixels#' . $max, + 'allowscriptaccess' => 'Enum#never', + 'allownetworking' => 'Enum#internal', + 'wmode' => 'Enum#window', + 'name' => 'ID', + ) + ); + $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed(); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/SafeObject.php b/lib/php/HTMLPurifier/HTMLModule/SafeObject.php new file mode 100644 index 0000000..33bac00 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/SafeObject.php @@ -0,0 +1,50 @@ +<?php + +/** + * A "safe" object module. In theory, objects permitted by this module will + * be safe, and untrusted users can be allowed to embed arbitrary flash objects + * (maybe other types too, but only Flash is supported as of right now). + * Highly experimental. + */ +class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule +{ + + public $name = 'SafeObject'; + + public function setup($config) { + + // These definitions are not intrinsically safe: the attribute transforms + // are a vital part of ensuring safety. + + $max = $config->get('HTML.MaxImgLength'); + $object = $this->addElement( + 'object', + 'Inline', + 'Optional: param | Flow | #PCDATA', + 'Common', + array( + // While technically not required by the spec, we're forcing + // it to this value. + 'type' => 'Enum#application/x-shockwave-flash', + 'width' => 'Pixels#' . $max, + 'height' => 'Pixels#' . $max, + 'data' => 'URI#embedded' + ) + ); + $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject(); + + $param = $this->addElement('param', false, 'Empty', false, + array( + 'id' => 'ID', + 'name*' => 'Text', + 'value' => 'Text' + ) + ); + $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam(); + $this->info_injector[] = 'SafeObject'; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Scripting.php b/lib/php/HTMLPurifier/HTMLModule/Scripting.php new file mode 100644 index 0000000..cecdea6 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Scripting.php @@ -0,0 +1,54 @@ +<?php + +/* + +WARNING: THIS MODULE IS EXTREMELY DANGEROUS AS IT ENABLES INLINE SCRIPTING +INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!! + +*/ + +/** + * XHTML 1.1 Scripting module, defines elements that are used to contain + * information pertaining to executable scripts or the lack of support + * for executable scripts. + * @note This module does not contain inline scripting elements + */ +class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule +{ + public $name = 'Scripting'; + public $elements = array('script', 'noscript'); + public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript'); + public $safe = false; + + public function setup($config) { + // TODO: create custom child-definition for noscript that + // auto-wraps stray #PCDATA in a similar manner to + // blockquote's custom definition (we would use it but + // blockquote's contents are optional while noscript's contents + // are required) + + // TODO: convert this to new syntax, main problem is getting + // both content sets working + + // In theory, this could be safe, but I don't see any reason to + // allow it. + $this->info['noscript'] = new HTMLPurifier_ElementDef(); + $this->info['noscript']->attr = array( 0 => array('Common') ); + $this->info['noscript']->content_model = 'Heading | List | Block'; + $this->info['noscript']->content_model_type = 'required'; + + $this->info['script'] = new HTMLPurifier_ElementDef(); + $this->info['script']->attr = array( + 'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')), + 'src' => new HTMLPurifier_AttrDef_URI(true), + 'type' => new HTMLPurifier_AttrDef_Enum(array('text/javascript')) + ); + $this->info['script']->content_model = '#PCDATA'; + $this->info['script']->content_model_type = 'optional'; + $this->info['script']->attr_transform_pre['type'] = + $this->info['script']->attr_transform_post['type'] = + new HTMLPurifier_AttrTransform_ScriptRequired(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/StyleAttribute.php b/lib/php/HTMLPurifier/HTMLModule/StyleAttribute.php new file mode 100644 index 0000000..eb78464 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/StyleAttribute.php @@ -0,0 +1,24 @@ +<?php + +/** + * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension + * Module. + */ +class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule +{ + + public $name = 'StyleAttribute'; + public $attr_collections = array( + // The inclusion routine differs from the Abstract Modules but + // is in line with the DTD and XML Schemas. + 'Style' => array('style' => false), // see constructor + 'Core' => array(0 => array('Style')) + ); + + public function setup($config) { + $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS(); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tables.php b/lib/php/HTMLPurifier/HTMLModule/Tables.php new file mode 100644 index 0000000..f314ced --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tables.php @@ -0,0 +1,66 @@ +<?php + +/** + * XHTML 1.1 Tables Module, fully defines accessible table elements. + */ +class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule +{ + + public $name = 'Tables'; + + public function setup($config) { + + $this->addElement('caption', false, 'Inline', 'Common'); + + $this->addElement('table', 'Block', + new HTMLPurifier_ChildDef_Table(), 'Common', + array( + 'border' => 'Pixels', + 'cellpadding' => 'Length', + 'cellspacing' => 'Length', + 'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border', + 'rules' => 'Enum#none,groups,rows,cols,all', + 'summary' => 'Text', + 'width' => 'Length' + ) + ); + + // common attributes + $cell_align = array( + 'align' => 'Enum#left,center,right,justify,char', + 'charoff' => 'Length', + 'valign' => 'Enum#top,middle,bottom,baseline', + ); + + $cell_t = array_merge( + array( + 'abbr' => 'Text', + 'colspan' => 'Number', + 'rowspan' => 'Number', + ), + $cell_align + ); + $this->addElement('td', false, 'Flow', 'Common', $cell_t); + $this->addElement('th', false, 'Flow', 'Common', $cell_t); + + $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align); + + $cell_col = array_merge( + array( + 'span' => 'Number', + 'width' => 'MultiLength', + ), + $cell_align + ); + $this->addElement('col', false, 'Empty', 'Common', $cell_col); + $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col); + + $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align); + $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align); + $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Target.php b/lib/php/HTMLPurifier/HTMLModule/Target.php new file mode 100644 index 0000000..2b844ec --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Target.php @@ -0,0 +1,23 @@ +<?php + +/** + * XHTML 1.1 Target Module, defines target attribute in link elements. + */ +class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule +{ + + public $name = 'Target'; + + public function setup($config) { + $elements = array('a'); + foreach ($elements as $name) { + $e = $this->addBlankElement($name); + $e->attr = array( + 'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget() + ); + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Text.php b/lib/php/HTMLPurifier/HTMLModule/Text.php new file mode 100644 index 0000000..ae77c71 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Text.php @@ -0,0 +1,71 @@ +<?php + +/** + * XHTML 1.1 Text Module, defines basic text containers. Core Module. + * @note In the normative XML Schema specification, this module + * is further abstracted into the following modules: + * - Block Phrasal (address, blockquote, pre, h1, h2, h3, h4, h5, h6) + * - Block Structural (div, p) + * - Inline Phrasal (abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var) + * - Inline Structural (br, span) + * This module, functionally, does not distinguish between these + * sub-modules, but the code is internally structured to reflect + * these distinctions. + */ +class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule +{ + + public $name = 'Text'; + public $content_sets = array( + 'Flow' => 'Heading | Block | Inline' + ); + + public function setup($config) { + + // Inline Phrasal ------------------------------------------------- + $this->addElement('abbr', 'Inline', 'Inline', 'Common'); + $this->addElement('acronym', 'Inline', 'Inline', 'Common'); + $this->addElement('cite', 'Inline', 'Inline', 'Common'); + $this->addElement('dfn', 'Inline', 'Inline', 'Common'); + $this->addElement('kbd', 'Inline', 'Inline', 'Common'); + $this->addElement('q', 'Inline', 'Inline', 'Common', array('cite' => 'URI')); + $this->addElement('samp', 'Inline', 'Inline', 'Common'); + $this->addElement('var', 'Inline', 'Inline', 'Common'); + + $em = $this->addElement('em', 'Inline', 'Inline', 'Common'); + $em->formatting = true; + + $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common'); + $strong->formatting = true; + + $code = $this->addElement('code', 'Inline', 'Inline', 'Common'); + $code->formatting = true; + + // Inline Structural ---------------------------------------------- + $this->addElement('span', 'Inline', 'Inline', 'Common'); + $this->addElement('br', 'Inline', 'Empty', 'Core'); + + // Block Phrasal -------------------------------------------------- + $this->addElement('address', 'Block', 'Inline', 'Common'); + $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI') ); + $pre = $this->addElement('pre', 'Block', 'Inline', 'Common'); + $pre->excludes = $this->makeLookup( + 'img', 'big', 'small', 'object', 'applet', 'font', 'basefont' ); + $this->addElement('h1', 'Heading', 'Inline', 'Common'); + $this->addElement('h2', 'Heading', 'Inline', 'Common'); + $this->addElement('h3', 'Heading', 'Inline', 'Common'); + $this->addElement('h4', 'Heading', 'Inline', 'Common'); + $this->addElement('h5', 'Heading', 'Inline', 'Common'); + $this->addElement('h6', 'Heading', 'Inline', 'Common'); + + // Block Structural ----------------------------------------------- + $p = $this->addElement('p', 'Block', 'Inline', 'Common'); + $p->autoclose = array_flip(array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul")); + + $this->addElement('div', 'Block', 'Flow', 'Common'); + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy.php b/lib/php/HTMLPurifier/HTMLModule/Tidy.php new file mode 100644 index 0000000..21783f1 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy.php @@ -0,0 +1,207 @@ +<?php + +/** + * Abstract class for a set of proprietary modules that clean up (tidy) + * poorly written HTML. + * @todo Figure out how to protect some of these methods/properties + */ +class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule +{ + + /** + * List of supported levels. Index zero is a special case "no fixes" + * level. + */ + public $levels = array(0 => 'none', 'light', 'medium', 'heavy'); + + /** + * Default level to place all fixes in. Disabled by default + */ + public $defaultLevel = null; + + /** + * Lists of fixes used by getFixesForLevel(). Format is: + * HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2'); + */ + public $fixesForLevel = array( + 'light' => array(), + 'medium' => array(), + 'heavy' => array() + ); + + /** + * Lazy load constructs the module by determining the necessary + * fixes to create and then delegating to the populate() function. + * @todo Wildcard matching and error reporting when an added or + * subtracted fix has no effect. + */ + public function setup($config) { + + // create fixes, initialize fixesForLevel + $fixes = $this->makeFixes(); + $this->makeFixesForLevel($fixes); + + // figure out which fixes to use + $level = $config->get('HTML.TidyLevel'); + $fixes_lookup = $this->getFixesForLevel($level); + + // get custom fix declarations: these need namespace processing + $add_fixes = $config->get('HTML.TidyAdd'); + $remove_fixes = $config->get('HTML.TidyRemove'); + + foreach ($fixes as $name => $fix) { + // needs to be refactored a little to implement globbing + if ( + isset($remove_fixes[$name]) || + (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name])) + ) { + unset($fixes[$name]); + } + } + + // populate this module with necessary fixes + $this->populate($fixes); + + } + + /** + * Retrieves all fixes per a level, returning fixes for that specific + * level as well as all levels below it. + * @param $level String level identifier, see $levels for valid values + * @return Lookup up table of fixes + */ + public function getFixesForLevel($level) { + if ($level == $this->levels[0]) { + return array(); + } + $activated_levels = array(); + for ($i = 1, $c = count($this->levels); $i < $c; $i++) { + $activated_levels[] = $this->levels[$i]; + if ($this->levels[$i] == $level) break; + } + if ($i == $c) { + trigger_error( + 'Tidy level ' . htmlspecialchars($level) . ' not recognized', + E_USER_WARNING + ); + return array(); + } + $ret = array(); + foreach ($activated_levels as $level) { + foreach ($this->fixesForLevel[$level] as $fix) { + $ret[$fix] = true; + } + } + return $ret; + } + + /** + * Dynamically populates the $fixesForLevel member variable using + * the fixes array. It may be custom overloaded, used in conjunction + * with $defaultLevel, or not used at all. + */ + public function makeFixesForLevel($fixes) { + if (!isset($this->defaultLevel)) return; + if (!isset($this->fixesForLevel[$this->defaultLevel])) { + trigger_error( + 'Default level ' . $this->defaultLevel . ' does not exist', + E_USER_ERROR + ); + return; + } + $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes); + } + + /** + * Populates the module with transforms and other special-case code + * based on a list of fixes passed to it + * @param $lookup Lookup table of fixes to activate + */ + public function populate($fixes) { + foreach ($fixes as $name => $fix) { + // determine what the fix is for + list($type, $params) = $this->getFixType($name); + switch ($type) { + case 'attr_transform_pre': + case 'attr_transform_post': + $attr = $params['attr']; + if (isset($params['element'])) { + $element = $params['element']; + if (empty($this->info[$element])) { + $e = $this->addBlankElement($element); + } else { + $e = $this->info[$element]; + } + } else { + $type = "info_$type"; + $e = $this; + } + // PHP does some weird parsing when I do + // $e->$type[$attr], so I have to assign a ref. + $f =& $e->$type; + $f[$attr] = $fix; + break; + case 'tag_transform': + $this->info_tag_transform[$params['element']] = $fix; + break; + case 'child': + case 'content_model_type': + $element = $params['element']; + if (empty($this->info[$element])) { + $e = $this->addBlankElement($element); + } else { + $e = $this->info[$element]; + } + $e->$type = $fix; + break; + default: + trigger_error("Fix type $type not supported", E_USER_ERROR); + break; + } + } + } + + /** + * Parses a fix name and determines what kind of fix it is, as well + * as other information defined by the fix + * @param $name String name of fix + * @return array(string $fix_type, array $fix_parameters) + * @note $fix_parameters is type dependant, see populate() for usage + * of these parameters + */ + public function getFixType($name) { + // parse it + $property = $attr = null; + if (strpos($name, '#') !== false) list($name, $property) = explode('#', $name); + if (strpos($name, '@') !== false) list($name, $attr) = explode('@', $name); + + // figure out the parameters + $params = array(); + if ($name !== '') $params['element'] = $name; + if (!is_null($attr)) $params['attr'] = $attr; + + // special case: attribute transform + if (!is_null($attr)) { + if (is_null($property)) $property = 'pre'; + $type = 'attr_transform_' . $property; + return array($type, $params); + } + + // special case: tag transform + if (is_null($property)) { + return array('tag_transform', $params); + } + + return array($property, $params); + + } + + /** + * Defines all fixes the module will perform in a compact + * associative array of fix name to fix implementation. + */ + public function makeFixes() {} + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy/Name.php b/lib/php/HTMLPurifier/HTMLModule/Tidy/Name.php new file mode 100644 index 0000000..61ff85c --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy/Name.php @@ -0,0 +1,24 @@ +<?php + +/** + * Name is deprecated, but allowed in strict doctypes, so onl + */ +class HTMLPurifier_HTMLModule_Tidy_Name extends HTMLPurifier_HTMLModule_Tidy +{ + public $name = 'Tidy_Name'; + public $defaultLevel = 'heavy'; + public function makeFixes() { + + $r = array(); + + // @name for img, a ----------------------------------------------- + // Technically, it's allowed even on strict, so we allow authors to use + // it. However, it's deprecated in future versions of XHTML. + $r['img@name'] = + $r['a@name'] = new HTMLPurifier_AttrTransform_Name(); + + return $r; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/lib/php/HTMLPurifier/HTMLModule/Tidy/Proprietary.php new file mode 100644 index 0000000..85fa90a --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy/Proprietary.php @@ -0,0 +1,23 @@ +<?php + +class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_Tidy +{ + + public $name = 'Tidy_Proprietary'; + public $defaultLevel = 'light'; + + public function makeFixes() { + $r = array(); + $r['table@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['td@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['th@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['tr@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['thead@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background(); + return $r; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy/Strict.php b/lib/php/HTMLPurifier/HTMLModule/Tidy/Strict.php new file mode 100644 index 0000000..c73dc3c --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy/Strict.php @@ -0,0 +1,21 @@ +<?php + +class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 +{ + public $name = 'Tidy_Strict'; + public $defaultLevel = 'light'; + + public function makeFixes() { + $r = parent::makeFixes(); + $r['blockquote#content_model_type'] = 'strictblockquote'; + return $r; + } + + public $defines_child_def = true; + public function getChildDef($def) { + if ($def->content_model_type != 'strictblockquote') return parent::getChildDef($def); + return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/lib/php/HTMLPurifier/HTMLModule/Tidy/Transitional.php new file mode 100644 index 0000000..9960b1d --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy/Transitional.php @@ -0,0 +1,9 @@ +<?php + +class HTMLPurifier_HTMLModule_Tidy_Transitional extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 +{ + public $name = 'Tidy_Transitional'; + public $defaultLevel = 'heavy'; +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy/XHTML.php b/lib/php/HTMLPurifier/HTMLModule/Tidy/XHTML.php new file mode 100644 index 0000000..db5a378 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy/XHTML.php @@ -0,0 +1,17 @@ +<?php + +class HTMLPurifier_HTMLModule_Tidy_XHTML extends HTMLPurifier_HTMLModule_Tidy +{ + + public $name = 'Tidy_XHTML'; + public $defaultLevel = 'medium'; + + public function makeFixes() { + $r = array(); + $r['@lang'] = new HTMLPurifier_AttrTransform_Lang(); + return $r; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php b/lib/php/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php new file mode 100644 index 0000000..02e9438 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php @@ -0,0 +1,161 @@ +<?php + +class HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 extends HTMLPurifier_HTMLModule_Tidy +{ + + public function makeFixes() { + + $r = array(); + + // == deprecated tag transforms =================================== + + $r['font'] = new HTMLPurifier_TagTransform_Font(); + $r['menu'] = new HTMLPurifier_TagTransform_Simple('ul'); + $r['dir'] = new HTMLPurifier_TagTransform_Simple('ul'); + $r['center'] = new HTMLPurifier_TagTransform_Simple('div', 'text-align:center;'); + $r['u'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:underline;'); + $r['s'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;'); + $r['strike'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;'); + + // == deprecated attribute transforms ============================= + + $r['caption@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS('align', array( + // we're following IE's behavior, not Firefox's, due + // to the fact that no one supports caption-side:right, + // W3C included (with CSS 2.1). This is a slightly + // unreasonable attribute! + 'left' => 'text-align:left;', + 'right' => 'text-align:right;', + 'top' => 'caption-side:top;', + 'bottom' => 'caption-side:bottom;' // not supported by IE + )); + + // @align for img ------------------------------------------------- + $r['img@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS('align', array( + 'left' => 'float:left;', + 'right' => 'float:right;', + 'top' => 'vertical-align:top;', + 'middle' => 'vertical-align:middle;', + 'bottom' => 'vertical-align:baseline;', + )); + + // @align for table ----------------------------------------------- + $r['table@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS('align', array( + 'left' => 'float:left;', + 'center' => 'margin-left:auto;margin-right:auto;', + 'right' => 'float:right;' + )); + + // @align for hr ----------------------------------------------- + $r['hr@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS('align', array( + // we use both text-align and margin because these work + // for different browsers (IE and Firefox, respectively) + // and the melange makes for a pretty cross-compatible + // solution + 'left' => 'margin-left:0;margin-right:auto;text-align:left;', + 'center' => 'margin-left:auto;margin-right:auto;text-align:center;', + 'right' => 'margin-left:auto;margin-right:0;text-align:right;' + )); + + // @align for h1, h2, h3, h4, h5, h6, p, div ---------------------- + // {{{ + $align_lookup = array(); + $align_values = array('left', 'right', 'center', 'justify'); + foreach ($align_values as $v) $align_lookup[$v] = "text-align:$v;"; + // }}} + $r['h1@align'] = + $r['h2@align'] = + $r['h3@align'] = + $r['h4@align'] = + $r['h5@align'] = + $r['h6@align'] = + $r['p@align'] = + $r['div@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup); + + // @bgcolor for table, tr, td, th --------------------------------- + $r['table@bgcolor'] = + $r['td@bgcolor'] = + $r['th@bgcolor'] = + new HTMLPurifier_AttrTransform_BgColor(); + + // @border for img ------------------------------------------------ + $r['img@border'] = new HTMLPurifier_AttrTransform_Border(); + + // @clear for br -------------------------------------------------- + $r['br@clear'] = + new HTMLPurifier_AttrTransform_EnumToCSS('clear', array( + 'left' => 'clear:left;', + 'right' => 'clear:right;', + 'all' => 'clear:both;', + 'none' => 'clear:none;', + )); + + // @height for td, th --------------------------------------------- + $r['td@height'] = + $r['th@height'] = + new HTMLPurifier_AttrTransform_Length('height'); + + // @hspace for img ------------------------------------------------ + $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace'); + + // @noshade for hr ------------------------------------------------ + // this transformation is not precise but often good enough. + // different browsers use different styles to designate noshade + $r['hr@noshade'] = + new HTMLPurifier_AttrTransform_BoolToCSS( + 'noshade', + 'color:#808080;background-color:#808080;border:0;' + ); + + // @nowrap for td, th --------------------------------------------- + $r['td@nowrap'] = + $r['th@nowrap'] = + new HTMLPurifier_AttrTransform_BoolToCSS( + 'nowrap', + 'white-space:nowrap;' + ); + + // @size for hr -------------------------------------------------- + $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height'); + + // @type for li, ol, ul ------------------------------------------- + // {{{ + $ul_types = array( + 'disc' => 'list-style-type:disc;', + 'square' => 'list-style-type:square;', + 'circle' => 'list-style-type:circle;' + ); + $ol_types = array( + '1' => 'list-style-type:decimal;', + 'i' => 'list-style-type:lower-roman;', + 'I' => 'list-style-type:upper-roman;', + 'a' => 'list-style-type:lower-alpha;', + 'A' => 'list-style-type:upper-alpha;' + ); + $li_types = $ul_types + $ol_types; + // }}} + + $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types); + $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true); + $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true); + + // @vspace for img ------------------------------------------------ + $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace'); + + // @width for hr, td, th ------------------------------------------ + $r['td@width'] = + $r['th@width'] = + $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width'); + + return $r; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/lib/php/HTMLPurifier/HTMLModule/XMLCommonAttributes.php new file mode 100644 index 0000000..9c0e031 --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModule/XMLCommonAttributes.php @@ -0,0 +1,14 @@ +<?php + +class HTMLPurifier_HTMLModule_XMLCommonAttributes extends HTMLPurifier_HTMLModule +{ + public $name = 'XMLCommonAttributes'; + + public $attr_collections = array( + 'Lang' => array( + 'xml:lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/HTMLModuleManager.php b/lib/php/HTMLPurifier/HTMLModuleManager.php new file mode 100644 index 0000000..f5c4a1d --- /dev/null +++ b/lib/php/HTMLPurifier/HTMLModuleManager.php @@ -0,0 +1,403 @@ +<?php + +class HTMLPurifier_HTMLModuleManager +{ + + /** + * Instance of HTMLPurifier_DoctypeRegistry + */ + public $doctypes; + + /** + * Instance of current doctype + */ + public $doctype; + + /** + * Instance of HTMLPurifier_AttrTypes + */ + public $attrTypes; + + /** + * Active instances of modules for the specified doctype are + * indexed, by name, in this array. + */ + public $modules = array(); + + /** + * Array of recognized HTMLPurifier_Module instances, indexed by + * module's class name. This array is usually lazy loaded, but a + * user can overload a module by pre-emptively registering it. + */ + public $registeredModules = array(); + + /** + * List of extra modules that were added by the user using addModule(). + * These get unconditionally merged into the current doctype, whatever + * it may be. + */ + public $userModules = array(); + + /** + * Associative array of element name to list of modules that have + * definitions for the element; this array is dynamically filled. + */ + public $elementLookup = array(); + + /** List of prefixes we should use for registering small names */ + public $prefixes = array('HTMLPurifier_HTMLModule_'); + + public $contentSets; /**< Instance of HTMLPurifier_ContentSets */ + public $attrCollections; /**< Instance of HTMLPurifier_AttrCollections */ + + /** If set to true, unsafe elements and attributes will be allowed */ + public $trusted = false; + + public function __construct() { + + // editable internal objects + $this->attrTypes = new HTMLPurifier_AttrTypes(); + $this->doctypes = new HTMLPurifier_DoctypeRegistry(); + + // setup basic modules + $common = array( + 'CommonAttributes', 'Text', 'Hypertext', 'List', + 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image', + 'StyleAttribute', + // Unsafe: + 'Scripting', 'Object', 'Forms', + // Sorta legacy, but present in strict: + 'Name', + ); + $transitional = array('Legacy', 'Target'); + $xml = array('XMLCommonAttributes'); + $non_xml = array('NonXMLCommonAttributes'); + + // setup basic doctypes + $this->doctypes->register( + 'HTML 4.01 Transitional', false, + array_merge($common, $transitional, $non_xml), + array('Tidy_Transitional', 'Tidy_Proprietary'), + array(), + '-//W3C//DTD HTML 4.01 Transitional//EN', + 'http://www.w3.org/TR/html4/loose.dtd' + ); + + $this->doctypes->register( + 'HTML 4.01 Strict', false, + array_merge($common, $non_xml), + array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), + array(), + '-//W3C//DTD HTML 4.01//EN', + 'http://www.w3.org/TR/html4/strict.dtd' + ); + + $this->doctypes->register( + 'XHTML 1.0 Transitional', true, + array_merge($common, $transitional, $xml, $non_xml), + array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'), + array(), + '-//W3C//DTD XHTML 1.0 Transitional//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' + ); + + $this->doctypes->register( + 'XHTML 1.0 Strict', true, + array_merge($common, $xml, $non_xml), + array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), + array(), + '-//W3C//DTD XHTML 1.0 Strict//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' + ); + + $this->doctypes->register( + 'XHTML 1.1', true, + array_merge($common, $xml, array('Ruby')), + array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1 + array(), + '-//W3C//DTD XHTML 1.1//EN', + 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' + ); + + } + + /** + * Registers a module to the recognized module list, useful for + * overloading pre-existing modules. + * @param $module Mixed: string module name, with or without + * HTMLPurifier_HTMLModule prefix, or instance of + * subclass of HTMLPurifier_HTMLModule. + * @param $overload Boolean whether or not to overload previous modules. + * If this is not set, and you do overload a module, + * HTML Purifier will complain with a warning. + * @note This function will not call autoload, you must instantiate + * (and thus invoke) autoload outside the method. + * @note If a string is passed as a module name, different variants + * will be tested in this order: + * - Check for HTMLPurifier_HTMLModule_$name + * - Check all prefixes with $name in order they were added + * - Check for literal object name + * - Throw fatal error + * If your object name collides with an internal class, specify + * your module manually. All modules must have been included + * externally: registerModule will not perform inclusions for you! + */ + public function registerModule($module, $overload = false) { + if (is_string($module)) { + // attempt to load the module + $original_module = $module; + $ok = false; + foreach ($this->prefixes as $prefix) { + $module = $prefix . $original_module; + if (class_exists($module)) { + $ok = true; + break; + } + } + if (!$ok) { + $module = $original_module; + if (!class_exists($module)) { + trigger_error($original_module . ' module does not exist', + E_USER_ERROR); + return; + } + } + $module = new $module(); + } + if (empty($module->name)) { + trigger_error('Module instance of ' . get_class($module) . ' must have name'); + return; + } + if (!$overload && isset($this->registeredModules[$module->name])) { + trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING); + } + $this->registeredModules[$module->name] = $module; + } + + /** + * Adds a module to the current doctype by first registering it, + * and then tacking it on to the active doctype + */ + public function addModule($module) { + $this->registerModule($module); + if (is_object($module)) $module = $module->name; + $this->userModules[] = $module; + } + + /** + * Adds a class prefix that registerModule() will use to resolve a + * string name to a concrete class + */ + public function addPrefix($prefix) { + $this->prefixes[] = $prefix; + } + + /** + * Performs processing on modules, after being called you may + * use getElement() and getElements() + * @param $config Instance of HTMLPurifier_Config + */ + public function setup($config) { + + $this->trusted = $config->get('HTML.Trusted'); + + // generate + $this->doctype = $this->doctypes->make($config); + $modules = $this->doctype->modules; + + // take out the default modules that aren't allowed + $lookup = $config->get('HTML.AllowedModules'); + $special_cases = $config->get('HTML.CoreModules'); + + if (is_array($lookup)) { + foreach ($modules as $k => $m) { + if (isset($special_cases[$m])) continue; + if (!isset($lookup[$m])) unset($modules[$k]); + } + } + + // add proprietary module (this gets special treatment because + // it is completely removed from doctypes, etc.) + if ($config->get('HTML.Proprietary')) { + $modules[] = 'Proprietary'; + } + + // add SafeObject/Safeembed modules + if ($config->get('HTML.SafeObject')) { + $modules[] = 'SafeObject'; + } + if ($config->get('HTML.SafeEmbed')) { + $modules[] = 'SafeEmbed'; + } + + // merge in custom modules + $modules = array_merge($modules, $this->userModules); + + foreach ($modules as $module) { + $this->processModule($module); + $this->modules[$module]->setup($config); + } + + foreach ($this->doctype->tidyModules as $module) { + $this->processModule($module); + $this->modules[$module]->setup($config); + } + + // prepare any injectors + foreach ($this->modules as $module) { + $n = array(); + foreach ($module->info_injector as $i => $injector) { + if (!is_object($injector)) { + $class = "HTMLPurifier_Injector_$injector"; + $injector = new $class; + } + $n[$injector->name] = $injector; + } + $module->info_injector = $n; + } + + // setup lookup table based on all valid modules + foreach ($this->modules as $module) { + foreach ($module->info as $name => $def) { + if (!isset($this->elementLookup[$name])) { + $this->elementLookup[$name] = array(); + } + $this->elementLookup[$name][] = $module->name; + } + } + + // note the different choice + $this->contentSets = new HTMLPurifier_ContentSets( + // content set assembly deals with all possible modules, + // not just ones deemed to be "safe" + $this->modules + ); + $this->attrCollections = new HTMLPurifier_AttrCollections( + $this->attrTypes, + // there is no way to directly disable a global attribute, + // but using AllowedAttributes or simply not including + // the module in your custom doctype should be sufficient + $this->modules + ); + } + + /** + * Takes a module and adds it to the active module collection, + * registering it if necessary. + */ + public function processModule($module) { + if (!isset($this->registeredModules[$module]) || is_object($module)) { + $this->registerModule($module); + } + $this->modules[$module] = $this->registeredModules[$module]; + } + + /** + * Retrieves merged element definitions. + * @return Array of HTMLPurifier_ElementDef + */ + public function getElements() { + + $elements = array(); + foreach ($this->modules as $module) { + if (!$this->trusted && !$module->safe) continue; + foreach ($module->info as $name => $v) { + if (isset($elements[$name])) continue; + $elements[$name] = $this->getElement($name); + } + } + + // remove dud elements, this happens when an element that + // appeared to be safe actually wasn't + foreach ($elements as $n => $v) { + if ($v === false) unset($elements[$n]); + } + + return $elements; + + } + + /** + * Retrieves a single merged element definition + * @param $name Name of element + * @param $trusted Boolean trusted overriding parameter: set to true + * if you want the full version of an element + * @return Merged HTMLPurifier_ElementDef + * @note You may notice that modules are getting iterated over twice (once + * in getElements() and once here). This + * is because + */ + public function getElement($name, $trusted = null) { + + if (!isset($this->elementLookup[$name])) { + return false; + } + + // setup global state variables + $def = false; + if ($trusted === null) $trusted = $this->trusted; + + // iterate through each module that has registered itself to this + // element + foreach($this->elementLookup[$name] as $module_name) { + + $module = $this->modules[$module_name]; + + // refuse to create/merge from a module that is deemed unsafe-- + // pretend the module doesn't exist--when trusted mode is not on. + if (!$trusted && !$module->safe) { + continue; + } + + // clone is used because, ideally speaking, the original + // definition should not be modified. Usually, this will + // make no difference, but for consistency's sake + $new_def = clone $module->info[$name]; + + if (!$def && $new_def->standalone) { + $def = $new_def; + } elseif ($def) { + // This will occur even if $new_def is standalone. In practice, + // this will usually result in a full replacement. + $def->mergeIn($new_def); + } else { + // :TODO: + // non-standalone definitions that don't have a standalone + // to merge into could be deferred to the end + continue; + } + + // attribute value expansions + $this->attrCollections->performInclusions($def->attr); + $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes); + + // descendants_are_inline, for ChildDef_Chameleon + if (is_string($def->content_model) && + strpos($def->content_model, 'Inline') !== false) { + if ($name != 'del' && $name != 'ins') { + // this is for you, ins/del + $def->descendants_are_inline = true; + } + } + + $this->contentSets->generateChildDef($def, $module); + } + + // This can occur if there is a blank definition, but no base to + // mix it in with + if (!$def) return false; + + // add information on required attributes + foreach ($def->attr as $attr_name => $attr_def) { + if ($attr_def->required) { + $def->required_attr[] = $attr_name; + } + } + + return $def; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/IDAccumulator.php b/lib/php/HTMLPurifier/IDAccumulator.php new file mode 100644 index 0000000..7321529 --- /dev/null +++ b/lib/php/HTMLPurifier/IDAccumulator.php @@ -0,0 +1,53 @@ +<?php + +/** + * Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes + * @note In Slashdot-speak, dupe means duplicate. + * @note The default constructor does not accept $config or $context objects: + * use must use the static build() factory method to perform initialization. + */ +class HTMLPurifier_IDAccumulator +{ + + /** + * Lookup table of IDs we've accumulated. + * @public + */ + public $ids = array(); + + /** + * Builds an IDAccumulator, also initializing the default blacklist + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return Fully initialized HTMLPurifier_IDAccumulator + */ + public static function build($config, $context) { + $id_accumulator = new HTMLPurifier_IDAccumulator(); + $id_accumulator->load($config->get('Attr.IDBlacklist')); + return $id_accumulator; + } + + /** + * Add an ID to the lookup table. + * @param $id ID to be added. + * @return Bool status, true if success, false if there's a dupe + */ + public function add($id) { + if (isset($this->ids[$id])) return false; + return $this->ids[$id] = true; + } + + /** + * Load a list of IDs into the lookup table + * @param $array_of_ids Array of IDs to load + * @note This function doesn't care about duplicates + */ + public function load($array_of_ids) { + foreach ($array_of_ids as $id) { + $this->ids[$id] = true; + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector.php b/lib/php/HTMLPurifier/Injector.php new file mode 100644 index 0000000..5922f81 --- /dev/null +++ b/lib/php/HTMLPurifier/Injector.php @@ -0,0 +1,239 @@ +<?php + +/** + * Injects tokens into the document while parsing for well-formedness. + * This enables "formatter-like" functionality such as auto-paragraphing, + * smiley-ification and linkification to take place. + * + * A note on how handlers create changes; this is done by assigning a new + * value to the $token reference. These values can take a variety of forms and + * are best described HTMLPurifier_Strategy_MakeWellFormed->processToken() + * documentation. + * + * @todo Allow injectors to request a re-run on their output. This + * would help if an operation is recursive. + */ +abstract class HTMLPurifier_Injector +{ + + /** + * Advisory name of injector, this is for friendly error messages + */ + public $name; + + /** + * Instance of HTMLPurifier_HTMLDefinition + */ + protected $htmlDefinition; + + /** + * Reference to CurrentNesting variable in Context. This is an array + * list of tokens that we are currently "inside" + */ + protected $currentNesting; + + /** + * Reference to InputTokens variable in Context. This is an array + * list of the input tokens that are being processed. + */ + protected $inputTokens; + + /** + * Reference to InputIndex variable in Context. This is an integer + * array index for $this->inputTokens that indicates what token + * is currently being processed. + */ + protected $inputIndex; + + /** + * Array of elements and attributes this injector creates and therefore + * need to be allowed by the definition. Takes form of + * array('element' => array('attr', 'attr2'), 'element2') + */ + public $needed = array(); + + /** + * Index of inputTokens to rewind to. + */ + protected $rewind = false; + + /** + * Rewind to a spot to re-perform processing. This is useful if you + * deleted a node, and now need to see if this change affected any + * earlier nodes. Rewinding does not affect other injectors, and can + * result in infinite loops if not used carefully. + * @warning HTML Purifier will prevent you from fast-forwarding with this + * function. + */ + public function rewind($index) { + $this->rewind = $index; + } + + /** + * Retrieves rewind, and then unsets it. + */ + public function getRewind() { + $r = $this->rewind; + $this->rewind = false; + return $r; + } + + /** + * Prepares the injector by giving it the config and context objects: + * this allows references to important variables to be made within + * the injector. This function also checks if the HTML environment + * will work with the Injector (see checkNeeded()). + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return Boolean false if success, string of missing needed element/attribute if failure + */ + public function prepare($config, $context) { + $this->htmlDefinition = $config->getHTMLDefinition(); + // Even though this might fail, some unit tests ignore this and + // still test checkNeeded, so be careful. Maybe get rid of that + // dependency. + $result = $this->checkNeeded($config); + if ($result !== false) return $result; + $this->currentNesting =& $context->get('CurrentNesting'); + $this->inputTokens =& $context->get('InputTokens'); + $this->inputIndex =& $context->get('InputIndex'); + return false; + } + + /** + * This function checks if the HTML environment + * will work with the Injector: if p tags are not allowed, the + * Auto-Paragraphing injector should not be enabled. + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return Boolean false if success, string of missing needed element/attribute if failure + */ + public function checkNeeded($config) { + $def = $config->getHTMLDefinition(); + foreach ($this->needed as $element => $attributes) { + if (is_int($element)) $element = $attributes; + if (!isset($def->info[$element])) return $element; + if (!is_array($attributes)) continue; + foreach ($attributes as $name) { + if (!isset($def->info[$element]->attr[$name])) return "$element.$name"; + } + } + return false; + } + + /** + * Tests if the context node allows a certain element + * @param $name Name of element to test for + * @return True if element is allowed, false if it is not + */ + public function allowsElement($name) { + if (!empty($this->currentNesting)) { + $parent_token = array_pop($this->currentNesting); + $this->currentNesting[] = $parent_token; + $parent = $this->htmlDefinition->info[$parent_token->name]; + } else { + $parent = $this->htmlDefinition->info_parent_def; + } + if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) { + return false; + } + // check for exclusion + for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) { + $node = $this->currentNesting[$i]; + $def = $this->htmlDefinition->info[$node->name]; + if (isset($def->excludes[$name])) return false; + } + return true; + } + + /** + * Iterator function, which starts with the next token and continues until + * you reach the end of the input tokens. + * @warning Please prevent previous references from interfering with this + * functions by setting $i = null beforehand! + * @param &$i Current integer index variable for inputTokens + * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference + */ + protected function forward(&$i, &$current) { + if ($i === null) $i = $this->inputIndex + 1; + else $i++; + if (!isset($this->inputTokens[$i])) return false; + $current = $this->inputTokens[$i]; + return true; + } + + /** + * Similar to _forward, but accepts a third parameter $nesting (which + * should be initialized at 0) and stops when we hit the end tag + * for the node $this->inputIndex starts in. + */ + protected function forwardUntilEndToken(&$i, &$current, &$nesting) { + $result = $this->forward($i, $current); + if (!$result) return false; + if ($nesting === null) $nesting = 0; + if ($current instanceof HTMLPurifier_Token_Start) $nesting++; + elseif ($current instanceof HTMLPurifier_Token_End) { + if ($nesting <= 0) return false; + $nesting--; + } + return true; + } + + /** + * Iterator function, starts with the previous token and continues until + * you reach the beginning of input tokens. + * @warning Please prevent previous references from interfering with this + * functions by setting $i = null beforehand! + * @param &$i Current integer index variable for inputTokens + * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference + */ + protected function backward(&$i, &$current) { + if ($i === null) $i = $this->inputIndex - 1; + else $i--; + if ($i < 0) return false; + $current = $this->inputTokens[$i]; + return true; + } + + /** + * Initializes the iterator at the current position. Use in a do {} while; + * loop to force the _forward and _backward functions to start at the + * current location. + * @warning Please prevent previous references from interfering with this + * functions by setting $i = null beforehand! + * @param &$i Current integer index variable for inputTokens + * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference + */ + protected function current(&$i, &$current) { + if ($i === null) $i = $this->inputIndex; + $current = $this->inputTokens[$i]; + } + + /** + * Handler that is called when a text token is processed + */ + public function handleText(&$token) {} + + /** + * Handler that is called when a start or empty token is processed + */ + public function handleElement(&$token) {} + + /** + * Handler that is called when an end token is processed + */ + public function handleEnd(&$token) { + $this->notifyEnd($token); + } + + /** + * Notifier that is called when an end token is processed + * @note This differs from handlers in that the token is read-only + * @deprecated + */ + public function notifyEnd($token) {} + + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector/AutoParagraph.php b/lib/php/HTMLPurifier/Injector/AutoParagraph.php new file mode 100644 index 0000000..8cc9525 --- /dev/null +++ b/lib/php/HTMLPurifier/Injector/AutoParagraph.php @@ -0,0 +1,340 @@ +<?php + +/** + * Injector that auto paragraphs text in the root node based on + * double-spacing. + * @todo Ensure all states are unit tested, including variations as well. + * @todo Make a graph of the flow control for this Injector. + */ +class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector +{ + + public $name = 'AutoParagraph'; + public $needed = array('p'); + + private function _pStart() { + $par = new HTMLPurifier_Token_Start('p'); + $par->armor['MakeWellFormed_TagClosedError'] = true; + return $par; + } + + public function handleText(&$token) { + $text = $token->data; + // Does the current parent allow <p> tags? + if ($this->allowsElement('p')) { + if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) { + // Note that we have differing behavior when dealing with text + // in the anonymous root node, or a node inside the document. + // If the text as a double-newline, the treatment is the same; + // if it doesn't, see the next if-block if you're in the document. + + $i = $nesting = null; + if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) { + // State 1.1: ... ^ (whitespace, then document end) + // ---- + // This is a degenerate case + } else { + // State 1.2: PAR1 + // ---- + + // State 1.3: PAR1\n\nPAR2 + // ------------ + + // State 1.4: <div>PAR1\n\nPAR2 (see State 2) + // ------------ + $token = array($this->_pStart()); + $this->_splitText($text, $token); + } + } else { + // State 2: <div>PAR1... (similar to 1.4) + // ---- + + // We're in an element that allows paragraph tags, but we're not + // sure if we're going to need them. + if ($this->_pLookAhead()) { + // State 2.1: <div>PAR1<b>PAR1\n\nPAR2 + // ---- + // Note: This will always be the first child, since any + // previous inline element would have triggered this very + // same routine, and found the double newline. One possible + // exception would be a comment. + $token = array($this->_pStart(), $token); + } else { + // State 2.2.1: <div>PAR1<div> + // ---- + + // State 2.2.2: <div>PAR1<b>PAR1</b></div> + // ---- + } + } + // Is the current parent a <p> tag? + } elseif ( + !empty($this->currentNesting) && + $this->currentNesting[count($this->currentNesting)-1]->name == 'p' + ) { + // State 3.1: ...<p>PAR1 + // ---- + + // State 3.2: ...<p>PAR1\n\nPAR2 + // ------------ + $token = array(); + $this->_splitText($text, $token); + // Abort! + } else { + // State 4.1: ...<b>PAR1 + // ---- + + // State 4.2: ...<b>PAR1\n\nPAR2 + // ------------ + } + } + + public function handleElement(&$token) { + // We don't have to check if we're already in a <p> tag for block + // tokens, because the tag would have been autoclosed by MakeWellFormed. + if ($this->allowsElement('p')) { + if (!empty($this->currentNesting)) { + if ($this->_isInline($token)) { + // State 1: <div>...<b> + // --- + + // Check if this token is adjacent to the parent token + // (seek backwards until token isn't whitespace) + $i = null; + $this->backward($i, $prev); + + if (!$prev instanceof HTMLPurifier_Token_Start) { + // Token wasn't adjacent + + if ( + $prev instanceof HTMLPurifier_Token_Text && + substr($prev->data, -2) === "\n\n" + ) { + // State 1.1.4: <div><p>PAR1</p>\n\n<b> + // --- + + // Quite frankly, this should be handled by splitText + $token = array($this->_pStart(), $token); + } else { + // State 1.1.1: <div><p>PAR1</p><b> + // --- + + // State 1.1.2: <div><br /><b> + // --- + + // State 1.1.3: <div>PAR<b> + // --- + } + + } else { + // State 1.2.1: <div><b> + // --- + + // Lookahead to see if <p> is needed. + if ($this->_pLookAhead()) { + // State 1.3.1: <div><b>PAR1\n\nPAR2 + // --- + $token = array($this->_pStart(), $token); + } else { + // State 1.3.2: <div><b>PAR1</b></div> + // --- + + // State 1.3.3: <div><b>PAR1</b><div></div>\n\n</div> + // --- + } + } + } else { + // State 2.3: ...<div> + // ----- + } + } else { + if ($this->_isInline($token)) { + // State 3.1: <b> + // --- + // This is where the {p} tag is inserted, not reflected in + // inputTokens yet, however. + $token = array($this->_pStart(), $token); + } else { + // State 3.2: <div> + // ----- + } + + $i = null; + if ($this->backward($i, $prev)) { + if ( + !$prev instanceof HTMLPurifier_Token_Text + ) { + // State 3.1.1: ...</p>{p}<b> + // --- + + // State 3.2.1: ...</p><div> + // ----- + + if (!is_array($token)) $token = array($token); + array_unshift($token, new HTMLPurifier_Token_Text("\n\n")); + } else { + // State 3.1.2: ...</p>\n\n{p}<b> + // --- + + // State 3.2.2: ...</p>\n\n<div> + // ----- + + // Note: PAR<ELEM> cannot occur because PAR would have been + // wrapped in <p> tags. + } + } + } + } else { + // State 2.2: <ul><li> + // ---- + + // State 2.4: <p><b> + // --- + } + } + + /** + * Splits up a text in paragraph tokens and appends them + * to the result stream that will replace the original + * @param $data String text data that will be processed + * into paragraphs + * @param $result Reference to array of tokens that the + * tags will be appended onto + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + */ + private function _splitText($data, &$result) { + $raw_paragraphs = explode("\n\n", $data); + $paragraphs = array(); // without empty paragraphs + $needs_start = false; + $needs_end = false; + + $c = count($raw_paragraphs); + if ($c == 1) { + // There were no double-newlines, abort quickly. In theory this + // should never happen. + $result[] = new HTMLPurifier_Token_Text($data); + return; + } + for ($i = 0; $i < $c; $i++) { + $par = $raw_paragraphs[$i]; + if (trim($par) !== '') { + $paragraphs[] = $par; + } else { + if ($i == 0) { + // Double newline at the front + if (empty($result)) { + // The empty result indicates that the AutoParagraph + // injector did not add any start paragraph tokens. + // This means that we have been in a paragraph for + // a while, and the newline means we should start a new one. + $result[] = new HTMLPurifier_Token_End('p'); + $result[] = new HTMLPurifier_Token_Text("\n\n"); + // However, the start token should only be added if + // there is more processing to be done (i.e. there are + // real paragraphs in here). If there are none, the + // next start paragraph tag will be handled by the + // next call to the injector + $needs_start = true; + } else { + // We just started a new paragraph! + // Reinstate a double-newline for presentation's sake, since + // it was in the source code. + array_unshift($result, new HTMLPurifier_Token_Text("\n\n")); + } + } elseif ($i + 1 == $c) { + // Double newline at the end + // There should be a trailing </p> when we're finally done. + $needs_end = true; + } + } + } + + // Check if this was just a giant blob of whitespace. Move this earlier, + // perhaps? + if (empty($paragraphs)) { + return; + } + + // Add the start tag indicated by \n\n at the beginning of $data + if ($needs_start) { + $result[] = $this->_pStart(); + } + + // Append the paragraphs onto the result + foreach ($paragraphs as $par) { + $result[] = new HTMLPurifier_Token_Text($par); + $result[] = new HTMLPurifier_Token_End('p'); + $result[] = new HTMLPurifier_Token_Text("\n\n"); + $result[] = $this->_pStart(); + } + + // Remove trailing start token; Injector will handle this later if + // it was indeed needed. This prevents from needing to do a lookahead, + // at the cost of a lookbehind later. + array_pop($result); + + // If there is no need for an end tag, remove all of it and let + // MakeWellFormed close it later. + if (!$needs_end) { + array_pop($result); // removes \n\n + array_pop($result); // removes </p> + } + + } + + /** + * Returns true if passed token is inline (and, ergo, allowed in + * paragraph tags) + */ + private function _isInline($token) { + return isset($this->htmlDefinition->info['p']->child->elements[$token->name]); + } + + /** + * Looks ahead in the token list and determines whether or not we need + * to insert a <p> tag. + */ + private function _pLookAhead() { + $this->current($i, $current); + if ($current instanceof HTMLPurifier_Token_Start) $nesting = 1; + else $nesting = 0; + $ok = false; + while ($this->forwardUntilEndToken($i, $current, $nesting)) { + $result = $this->_checkNeedsP($current); + if ($result !== null) { + $ok = $result; + break; + } + } + return $ok; + } + + /** + * Determines if a particular token requires an earlier inline token + * to get a paragraph. This should be used with _forwardUntilEndToken + */ + private function _checkNeedsP($current) { + if ($current instanceof HTMLPurifier_Token_Start){ + if (!$this->_isInline($current)) { + // <div>PAR1<div> + // ---- + // Terminate early, since we hit a block element + return false; + } + } elseif ($current instanceof HTMLPurifier_Token_Text) { + if (strpos($current->data, "\n\n") !== false) { + // <div>PAR1<b>PAR1\n\nPAR2 + // ---- + return true; + } else { + // <div>PAR1<b>PAR1... + // ---- + } + } + return null; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector/DisplayLinkURI.php b/lib/php/HTMLPurifier/Injector/DisplayLinkURI.php new file mode 100644 index 0000000..9dce9bd --- /dev/null +++ b/lib/php/HTMLPurifier/Injector/DisplayLinkURI.php @@ -0,0 +1,26 @@ +<?php + +/** + * Injector that displays the URL of an anchor instead of linking to it, in addition to showing the text of the link. + */ +class HTMLPurifier_Injector_DisplayLinkURI extends HTMLPurifier_Injector +{ + + public $name = 'DisplayLinkURI'; + public $needed = array('a'); + + public function handleElement(&$token) { + } + + public function handleEnd(&$token) { + if (isset($token->start->attr['href'])){ + $url = $token->start->attr['href']; + unset($token->start->attr['href']); + $token = array($token, new HTMLPurifier_Token_Text(" ($url)")); + } else { + // nothing to display + } + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector/Linkify.php b/lib/php/HTMLPurifier/Injector/Linkify.php new file mode 100644 index 0000000..296dac2 --- /dev/null +++ b/lib/php/HTMLPurifier/Injector/Linkify.php @@ -0,0 +1,46 @@ +<?php + +/** + * Injector that converts http, https and ftp text URLs to actual links. + */ +class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector +{ + + public $name = 'Linkify'; + public $needed = array('a' => array('href')); + + public function handleText(&$token) { + if (!$this->allowsElement('a')) return; + + if (strpos($token->data, '://') === false) { + // our really quick heuristic failed, abort + // this may not work so well if we want to match things like + // "google.com", but then again, most people don't + return; + } + + // there is/are URL(s). Let's split the string: + // Note: this regex is extremely permissive + $bits = preg_split('#((?:https?|ftp)://[^\s\'"<>()]+)#S', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); + + $token = array(); + + // $i = index + // $c = count + // $l = is link + for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) { + if (!$l) { + if ($bits[$i] === '') continue; + $token[] = new HTMLPurifier_Token_Text($bits[$i]); + } else { + $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i])); + $token[] = new HTMLPurifier_Token_Text($bits[$i]); + $token[] = new HTMLPurifier_Token_End('a'); + } + } + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector/PurifierLinkify.php b/lib/php/HTMLPurifier/Injector/PurifierLinkify.php new file mode 100644 index 0000000..ad2455a --- /dev/null +++ b/lib/php/HTMLPurifier/Injector/PurifierLinkify.php @@ -0,0 +1,45 @@ +<?php + +/** + * Injector that converts configuration directive syntax %Namespace.Directive + * to links + */ +class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector +{ + + public $name = 'PurifierLinkify'; + public $docURL; + public $needed = array('a' => array('href')); + + public function prepare($config, $context) { + $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL'); + return parent::prepare($config, $context); + } + + public function handleText(&$token) { + if (!$this->allowsElement('a')) return; + if (strpos($token->data, '%') === false) return; + + $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); + $token = array(); + + // $i = index + // $c = count + // $l = is link + for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) { + if (!$l) { + if ($bits[$i] === '') continue; + $token[] = new HTMLPurifier_Token_Text($bits[$i]); + } else { + $token[] = new HTMLPurifier_Token_Start('a', + array('href' => str_replace('%s', $bits[$i], $this->docURL))); + $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]); + $token[] = new HTMLPurifier_Token_End('a'); + } + } + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector/RemoveEmpty.php b/lib/php/HTMLPurifier/Injector/RemoveEmpty.php new file mode 100644 index 0000000..638bfca --- /dev/null +++ b/lib/php/HTMLPurifier/Injector/RemoveEmpty.php @@ -0,0 +1,51 @@ +<?php + +class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector +{ + + private $context, $config, $attrValidator, $removeNbsp, $removeNbspExceptions; + + public function prepare($config, $context) { + parent::prepare($config, $context); + $this->config = $config; + $this->context = $context; + $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp'); + $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions'); + $this->attrValidator = new HTMLPurifier_AttrValidator(); + } + + public function handleElement(&$token) { + if (!$token instanceof HTMLPurifier_Token_Start) return; + $next = false; + for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) { + $next = $this->inputTokens[$i]; + if ($next instanceof HTMLPurifier_Token_Text) { + if ($next->is_whitespace) continue; + if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) { + $plain = str_replace("\xC2\xA0", "", $next->data); + $isWsOrNbsp = $plain === '' || ctype_space($plain); + if ($isWsOrNbsp) continue; + } + } + break; + } + if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) { + if ($token->name == 'colgroup') return; + $this->attrValidator->validateToken($token, $this->config, $this->context); + $token->armor['ValidateAttributes'] = true; + if (isset($token->attr['id']) || isset($token->attr['name'])) return; + $token = $i - $this->inputIndex + 1; + for ($b = $this->inputIndex - 1; $b > 0; $b--) { + $prev = $this->inputTokens[$b]; + if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) continue; + break; + } + // This is safe because we removed the token that triggered this. + $this->rewind($b - 1); + return; + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Injector/SafeObject.php b/lib/php/HTMLPurifier/Injector/SafeObject.php new file mode 100644 index 0000000..3415828 --- /dev/null +++ b/lib/php/HTMLPurifier/Injector/SafeObject.php @@ -0,0 +1,87 @@ +<?php + +/** + * Adds important param elements to inside of object in order to make + * things safe. + */ +class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector +{ + public $name = 'SafeObject'; + public $needed = array('object', 'param'); + + protected $objectStack = array(); + protected $paramStack = array(); + + // Keep this synchronized with AttrTransform/SafeParam.php + protected $addParam = array( + 'allowScriptAccess' => 'never', + 'allowNetworking' => 'internal', + ); + protected $allowedParam = array( + 'wmode' => true, + 'movie' => true, + ); + + public function prepare($config, $context) { + parent::prepare($config, $context); + } + + public function handleElement(&$token) { + if ($token->name == 'object') { + $this->objectStack[] = $token; + $this->paramStack[] = array(); + $new = array($token); + foreach ($this->addParam as $name => $value) { + $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value)); + } + $token = $new; + } elseif ($token->name == 'param') { + $nest = count($this->currentNesting) - 1; + if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') { + $i = count($this->objectStack) - 1; + if (!isset($token->attr['name'])) { + $token = false; + return; + } + $n = $token->attr['name']; + // We need this fix because YouTube doesn't supply a data + // attribute, which we need if a type is specified. This is + // *very* Flash specific. + if (!isset($this->objectStack[$i]->attr['data']) && $token->attr['name'] == 'movie') { + $this->objectStack[$i]->attr['data'] = $token->attr['value']; + } + // Check if the parameter is the correct value but has not + // already been added + if ( + !isset($this->paramStack[$i][$n]) && + isset($this->addParam[$n]) && + $token->attr['name'] === $this->addParam[$n] + ) { + // keep token, and add to param stack + $this->paramStack[$i][$n] = true; + } elseif (isset($this->allowedParam[$n])) { + // keep token, don't do anything to it + // (could possibly check for duplicates here) + } else { + $token = false; + } + } else { + // not directly inside an object, DENY! + $token = false; + } + } + } + + public function handleEnd(&$token) { + // This is the WRONG way of handling the object and param stacks; + // we should be inserting them directly on the relevant object tokens + // so that the global stack handling handles it. + if ($token->name == 'object') { + array_pop($this->objectStack); + array_pop($this->paramStack); + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Language.php b/lib/php/HTMLPurifier/Language.php new file mode 100644 index 0000000..3e2be03 --- /dev/null +++ b/lib/php/HTMLPurifier/Language.php @@ -0,0 +1,163 @@ +<?php + +/** + * Represents a language and defines localizable string formatting and + * other functions, as well as the localized messages for HTML Purifier. + */ +class HTMLPurifier_Language +{ + + /** + * ISO 639 language code of language. Prefers shortest possible version + */ + public $code = 'en'; + + /** + * Fallback language code + */ + public $fallback = false; + + /** + * Array of localizable messages + */ + public $messages = array(); + + /** + * Array of localizable error codes + */ + public $errorNames = array(); + + /** + * True if no message file was found for this language, so English + * is being used instead. Check this if you'd like to notify the + * user that they've used a non-supported language. + */ + public $error = false; + + /** + * Has the language object been loaded yet? + * @todo Make it private, fix usage in HTMLPurifier_LanguageTest + */ + public $_loaded = false; + + /** + * Instances of HTMLPurifier_Config and HTMLPurifier_Context + */ + protected $config, $context; + + public function __construct($config, $context) { + $this->config = $config; + $this->context = $context; + } + + /** + * Loads language object with necessary info from factory cache + * @note This is a lazy loader + */ + public function load() { + if ($this->_loaded) return; + $factory = HTMLPurifier_LanguageFactory::instance(); + $factory->loadLanguage($this->code); + foreach ($factory->keys as $key) { + $this->$key = $factory->cache[$this->code][$key]; + } + $this->_loaded = true; + } + + /** + * Retrieves a localised message. + * @param $key string identifier of message + * @return string localised message + */ + public function getMessage($key) { + if (!$this->_loaded) $this->load(); + if (!isset($this->messages[$key])) return "[$key]"; + return $this->messages[$key]; + } + + /** + * Retrieves a localised error name. + * @param $int integer error number, corresponding to PHP's error + * reporting + * @return string localised message + */ + public function getErrorName($int) { + if (!$this->_loaded) $this->load(); + if (!isset($this->errorNames[$int])) return "[Error: $int]"; + return $this->errorNames[$int]; + } + + /** + * Converts an array list into a string readable representation + */ + public function listify($array) { + $sep = $this->getMessage('Item separator'); + $sep_last = $this->getMessage('Item separator last'); + $ret = ''; + for ($i = 0, $c = count($array); $i < $c; $i++) { + if ($i == 0) { + } elseif ($i + 1 < $c) { + $ret .= $sep; + } else { + $ret .= $sep_last; + } + $ret .= $array[$i]; + } + return $ret; + } + + /** + * Formats a localised message with passed parameters + * @param $key string identifier of message + * @param $args Parameters to substitute in + * @return string localised message + * @todo Implement conditionals? Right now, some messages make + * reference to line numbers, but those aren't always available + */ + public function formatMessage($key, $args = array()) { + if (!$this->_loaded) $this->load(); + if (!isset($this->messages[$key])) return "[$key]"; + $raw = $this->messages[$key]; + $subst = array(); + $generator = false; + foreach ($args as $i => $value) { + if (is_object($value)) { + if ($value instanceof HTMLPurifier_Token) { + // factor this out some time + if (!$generator) $generator = $this->context->get('Generator'); + if (isset($value->name)) $subst['$'.$i.'.Name'] = $value->name; + if (isset($value->data)) $subst['$'.$i.'.Data'] = $value->data; + $subst['$'.$i.'.Compact'] = + $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value); + // a more complex algorithm for compact representation + // could be introduced for all types of tokens. This + // may need to be factored out into a dedicated class + if (!empty($value->attr)) { + $stripped_token = clone $value; + $stripped_token->attr = array(); + $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token); + } + $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown'; + } + continue; + } elseif (is_array($value)) { + $keys = array_keys($value); + if (array_keys($keys) === $keys) { + // list + $subst['$'.$i] = $this->listify($value); + } else { + // associative array + // no $i implementation yet, sorry + $subst['$'.$i.'.Keys'] = $this->listify($keys); + $subst['$'.$i.'.Values'] = $this->listify(array_values($value)); + } + continue; + } + $subst['$' . $i] = $value; + } + return strtr($raw, $subst); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Language/classes/en-x-test.php b/lib/php/HTMLPurifier/Language/classes/en-x-test.php new file mode 100644 index 0000000..d52fcb7 --- /dev/null +++ b/lib/php/HTMLPurifier/Language/classes/en-x-test.php @@ -0,0 +1,12 @@ +<?php + +// private class for unit testing + +class HTMLPurifier_Language_en_x_test extends HTMLPurifier_Language +{ + + + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Language/messages/en-x-test.php b/lib/php/HTMLPurifier/Language/messages/en-x-test.php new file mode 100644 index 0000000..1c046f3 --- /dev/null +++ b/lib/php/HTMLPurifier/Language/messages/en-x-test.php @@ -0,0 +1,11 @@ +<?php + +// private language message file for unit testing purposes + +$fallback = 'en'; + +$messages = array( + 'HTMLPurifier' => 'HTML Purifier X' +); + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Language/messages/en-x-testmini.php b/lib/php/HTMLPurifier/Language/messages/en-x-testmini.php new file mode 100644 index 0000000..806c83f --- /dev/null +++ b/lib/php/HTMLPurifier/Language/messages/en-x-testmini.php @@ -0,0 +1,12 @@ +<?php + +// private language message file for unit testing purposes +// this language file has no class associated with it + +$fallback = 'en'; + +$messages = array( + 'HTMLPurifier' => 'HTML Purifier XNone' +); + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Language/messages/en.php b/lib/php/HTMLPurifier/Language/messages/en.php new file mode 100644 index 0000000..aab2e52 --- /dev/null +++ b/lib/php/HTMLPurifier/Language/messages/en.php @@ -0,0 +1,62 @@ +<?php + +$fallback = false; + +$messages = array( + +'HTMLPurifier' => 'HTML Purifier', + +// for unit testing purposes +'LanguageFactoryTest: Pizza' => 'Pizza', +'LanguageTest: List' => '$1', +'LanguageTest: Hash' => '$1.Keys; $1.Values', + +'Item separator' => ', ', +'Item separator last' => ' and ', // non-Harvard style + +'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.', +'ErrorCollector: At line' => ' at line $line', +'ErrorCollector: Incidental errors' => 'Incidental errors', + +'Lexer: Unclosed comment' => 'Unclosed comment', +'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <', +'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', +'Lexer: Missing attribute key' => 'Attribute declaration has no key', +'Lexer: Missing end quote' => 'Attribute declaration has no end quote', + +'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', +'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', +'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text', +'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed', +'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed', +'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed', +'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end', +'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed', +'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens', + +'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed', +'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text', +'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact', +'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact', +'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed', +'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text', +'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized', +'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document', + +'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed', +'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element', +'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model', +'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed', + +'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys', +'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed', + +); + +$errorNames = array( + E_ERROR => 'Error', + E_WARNING => 'Warning', + E_NOTICE => 'Notice' +); + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/LanguageFactory.php b/lib/php/HTMLPurifier/LanguageFactory.php new file mode 100644 index 0000000..134ef8c --- /dev/null +++ b/lib/php/HTMLPurifier/LanguageFactory.php @@ -0,0 +1,198 @@ +<?php + +/** + * Class responsible for generating HTMLPurifier_Language objects, managing + * caching and fallbacks. + * @note Thanks to MediaWiki for the general logic, although this version + * has been entirely rewritten + * @todo Serialized cache for languages + */ +class HTMLPurifier_LanguageFactory +{ + + /** + * Cache of language code information used to load HTMLPurifier_Language objects + * Structure is: $factory->cache[$language_code][$key] = $value + * @value array map + */ + public $cache; + + /** + * Valid keys in the HTMLPurifier_Language object. Designates which + * variables to slurp out of a message file. + * @value array list + */ + public $keys = array('fallback', 'messages', 'errorNames'); + + /** + * Instance of HTMLPurifier_AttrDef_Lang to validate language codes + * @value object HTMLPurifier_AttrDef_Lang + */ + protected $validator; + + /** + * Cached copy of dirname(__FILE__), directory of current file without + * trailing slash + * @value string filename + */ + protected $dir; + + /** + * Keys whose contents are a hash map and can be merged + * @value array lookup + */ + protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true); + + /** + * Keys whose contents are a list and can be merged + * @value array lookup + */ + protected $mergeable_keys_list = array(); + + /** + * Retrieve sole instance of the factory. + * @param $prototype Optional prototype to overload sole instance with, + * or bool true to reset to default factory. + */ + public static function instance($prototype = null) { + static $instance = null; + if ($prototype !== null) { + $instance = $prototype; + } elseif ($instance === null || $prototype == true) { + $instance = new HTMLPurifier_LanguageFactory(); + $instance->setup(); + } + return $instance; + } + + /** + * Sets up the singleton, much like a constructor + * @note Prevents people from getting this outside of the singleton + */ + public function setup() { + $this->validator = new HTMLPurifier_AttrDef_Lang(); + $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier'; + } + + /** + * Creates a language object, handles class fallbacks + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @param $code Code to override configuration with. Private parameter. + */ + public function create($config, $context, $code = false) { + + // validate language code + if ($code === false) { + $code = $this->validator->validate( + $config->get('Core.Language'), $config, $context + ); + } else { + $code = $this->validator->validate($code, $config, $context); + } + if ($code === false) $code = 'en'; // malformed code becomes English + + $pcode = str_replace('-', '_', $code); // make valid PHP classname + static $depth = 0; // recursion protection + + if ($code == 'en') { + $lang = new HTMLPurifier_Language($config, $context); + } else { + $class = 'HTMLPurifier_Language_' . $pcode; + $file = $this->dir . '/Language/classes/' . $code . '.php'; + if (file_exists($file) || class_exists($class, false)) { + $lang = new $class($config, $context); + } else { + // Go fallback + $raw_fallback = $this->getFallbackFor($code); + $fallback = $raw_fallback ? $raw_fallback : 'en'; + $depth++; + $lang = $this->create($config, $context, $fallback); + if (!$raw_fallback) { + $lang->error = true; + } + $depth--; + } + } + + $lang->code = $code; + + return $lang; + + } + + /** + * Returns the fallback language for language + * @note Loads the original language into cache + * @param $code string language code + */ + public function getFallbackFor($code) { + $this->loadLanguage($code); + return $this->cache[$code]['fallback']; + } + + /** + * Loads language into the cache, handles message file and fallbacks + * @param $code string language code + */ + public function loadLanguage($code) { + static $languages_seen = array(); // recursion guard + + // abort if we've already loaded it + if (isset($this->cache[$code])) return; + + // generate filename + $filename = $this->dir . '/Language/messages/' . $code . '.php'; + + // default fallback : may be overwritten by the ensuing include + $fallback = ($code != 'en') ? 'en' : false; + + // load primary localisation + if (!file_exists($filename)) { + // skip the include: will rely solely on fallback + $filename = $this->dir . '/Language/messages/en.php'; + $cache = array(); + } else { + include $filename; + $cache = compact($this->keys); + } + + // load fallback localisation + if (!empty($fallback)) { + + // infinite recursion guard + if (isset($languages_seen[$code])) { + trigger_error('Circular fallback reference in language ' . + $code, E_USER_ERROR); + $fallback = 'en'; + } + $language_seen[$code] = true; + + // load the fallback recursively + $this->loadLanguage($fallback); + $fallback_cache = $this->cache[$fallback]; + + // merge fallback with current language + foreach ( $this->keys as $key ) { + if (isset($cache[$key]) && isset($fallback_cache[$key])) { + if (isset($this->mergeable_keys_map[$key])) { + $cache[$key] = $cache[$key] + $fallback_cache[$key]; + } elseif (isset($this->mergeable_keys_list[$key])) { + $cache[$key] = array_merge( $fallback_cache[$key], $cache[$key] ); + } + } else { + $cache[$key] = $fallback_cache[$key]; + } + } + + } + + // save to cache for later retrieval + $this->cache[$code] = $cache; + + return; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Length.php b/lib/php/HTMLPurifier/Length.php new file mode 100644 index 0000000..8d2a46b --- /dev/null +++ b/lib/php/HTMLPurifier/Length.php @@ -0,0 +1,115 @@ +<?php + +/** + * Represents a measurable length, with a string numeric magnitude + * and a unit. This object is immutable. + */ +class HTMLPurifier_Length +{ + + /** + * String numeric magnitude. + */ + protected $n; + + /** + * String unit. False is permitted if $n = 0. + */ + protected $unit; + + /** + * Whether or not this length is valid. Null if not calculated yet. + */ + protected $isValid; + + /** + * Lookup array of units recognized by CSS 2.1 + */ + protected static $allowedUnits = array( + 'em' => true, 'ex' => true, 'px' => true, 'in' => true, + 'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true + ); + + /** + * @param number $n Magnitude + * @param string $u Unit + */ + public function __construct($n = '0', $u = false) { + $this->n = (string) $n; + $this->unit = $u !== false ? (string) $u : false; + } + + /** + * @param string $s Unit string, like '2em' or '3.4in' + * @warning Does not perform validation. + */ + static public function make($s) { + if ($s instanceof HTMLPurifier_Length) return $s; + $n_length = strspn($s, '1234567890.+-'); + $n = substr($s, 0, $n_length); + $unit = substr($s, $n_length); + if ($unit === '') $unit = false; + return new HTMLPurifier_Length($n, $unit); + } + + /** + * Validates the number and unit. + */ + protected function validate() { + // Special case: + if ($this->n === '+0' || $this->n === '-0') $this->n = '0'; + if ($this->n === '0' && $this->unit === false) return true; + if (!ctype_lower($this->unit)) $this->unit = strtolower($this->unit); + if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) return false; + // Hack: + $def = new HTMLPurifier_AttrDef_CSS_Number(); + $result = $def->validate($this->n, false, false); + if ($result === false) return false; + $this->n = $result; + return true; + } + + /** + * Returns string representation of number. + */ + public function toString() { + if (!$this->isValid()) return false; + return $this->n . $this->unit; + } + + /** + * Retrieves string numeric magnitude. + */ + public function getN() {return $this->n;} + + /** + * Retrieves string unit. + */ + public function getUnit() {return $this->unit;} + + /** + * Returns true if this length unit is valid. + */ + public function isValid() { + if ($this->isValid === null) $this->isValid = $this->validate(); + return $this->isValid; + } + + /** + * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal. + * @warning If both values are too large or small, this calculation will + * not work properly + */ + public function compareTo($l) { + if ($l === false) return false; + if ($l->unit !== $this->unit) { + $converter = new HTMLPurifier_UnitConverter(); + $l = $converter->convert($l, $this->unit); + if ($l === false) return false; + } + return $this->n - $l->n; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Lexer.php b/lib/php/HTMLPurifier/Lexer.php new file mode 100644 index 0000000..8cce008 --- /dev/null +++ b/lib/php/HTMLPurifier/Lexer.php @@ -0,0 +1,298 @@ +<?php + +/** + * Forgivingly lexes HTML (SGML-style) markup into tokens. + * + * A lexer parses a string of SGML-style markup and converts them into + * corresponding tokens. It doesn't check for well-formedness, although its + * internal mechanism may make this automatic (such as the case of + * HTMLPurifier_Lexer_DOMLex). There are several implementations to choose + * from. + * + * A lexer is HTML-oriented: it might work with XML, but it's not + * recommended, as we adhere to a subset of the specification for optimization + * reasons. This might change in the future. Also, most tokenizers are not + * expected to handle DTDs or PIs. + * + * This class should not be directly instantiated, but you may use create() to + * retrieve a default copy of the lexer. Being a supertype, this class + * does not actually define any implementation, but offers commonly used + * convenience functions for subclasses. + * + * @note The unit tests will instantiate this class for testing purposes, as + * many of the utility functions require a class to be instantiated. + * This means that, even though this class is not runnable, it will + * not be declared abstract. + * + * @par + * + * @note + * We use tokens rather than create a DOM representation because DOM would: + * + * @par + * -# Require more processing and memory to create, + * -# Is not streamable, and + * -# Has the entire document structure (html and body not needed). + * + * @par + * However, DOM is helpful in that it makes it easy to move around nodes + * without a lot of lookaheads to see when a tag is closed. This is a + * limitation of the token system and some workarounds would be nice. + */ +class HTMLPurifier_Lexer +{ + + /** + * Whether or not this lexer implements line-number/column-number tracking. + * If it does, set to true. + */ + public $tracksLineNumbers = false; + + // -- STATIC ---------------------------------------------------------- + + /** + * Retrieves or sets the default Lexer as a Prototype Factory. + * + * By default HTMLPurifier_Lexer_DOMLex will be returned. There are + * a few exceptions involving special features that only DirectLex + * implements. + * + * @note The behavior of this class has changed, rather than accepting + * a prototype object, it now accepts a configuration object. + * To specify your own prototype, set %Core.LexerImpl to it. + * This change in behavior de-singletonizes the lexer object. + * + * @param $config Instance of HTMLPurifier_Config + * @return Concrete lexer. + */ + public static function create($config) { + + if (!($config instanceof HTMLPurifier_Config)) { + $lexer = $config; + trigger_error("Passing a prototype to + HTMLPurifier_Lexer::create() is deprecated, please instead + use %Core.LexerImpl", E_USER_WARNING); + } else { + $lexer = $config->get('Core.LexerImpl'); + } + + $needs_tracking = + $config->get('Core.MaintainLineNumbers') || + $config->get('Core.CollectErrors'); + + $inst = null; + if (is_object($lexer)) { + $inst = $lexer; + } else { + + if (is_null($lexer)) { do { + // auto-detection algorithm + + if ($needs_tracking) { + $lexer = 'DirectLex'; + break; + } + + if ( + class_exists('DOMDocument') && + method_exists('DOMDocument', 'loadHTML') && + !extension_loaded('domxml') + ) { + // check for DOM support, because while it's part of the + // core, it can be disabled compile time. Also, the PECL + // domxml extension overrides the default DOM, and is evil + // and nasty and we shan't bother to support it + $lexer = 'DOMLex'; + } else { + $lexer = 'DirectLex'; + } + + } while(0); } // do..while so we can break + + // instantiate recognized string names + switch ($lexer) { + case 'DOMLex': + $inst = new HTMLPurifier_Lexer_DOMLex(); + break; + case 'DirectLex': + $inst = new HTMLPurifier_Lexer_DirectLex(); + break; + case 'PH5P': + $inst = new HTMLPurifier_Lexer_PH5P(); + break; + default: + throw new HTMLPurifier_Exception("Cannot instantiate unrecognized Lexer type " . htmlspecialchars($lexer)); + } + } + + if (!$inst) throw new HTMLPurifier_Exception('No lexer was instantiated'); + + // once PHP DOM implements native line numbers, or we + // hack out something using XSLT, remove this stipulation + if ($needs_tracking && !$inst->tracksLineNumbers) { + throw new HTMLPurifier_Exception('Cannot use lexer that does not support line numbers with Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'); + } + + return $inst; + + } + + // -- CONVENIENCE MEMBERS --------------------------------------------- + + public function __construct() { + $this->_entity_parser = new HTMLPurifier_EntityParser(); + } + + /** + * Most common entity to raw value conversion table for special entities. + */ + protected $_special_entity2str = + array( + '"' => '"', + '&' => '&', + '<' => '<', + '>' => '>', + ''' => "'", + ''' => "'", + ''' => "'" + ); + + /** + * Parses special entities into the proper characters. + * + * This string will translate escaped versions of the special characters + * into the correct ones. + * + * @warning + * You should be able to treat the output of this function as + * completely parsed, but that's only because all other entities should + * have been handled previously in substituteNonSpecialEntities() + * + * @param $string String character data to be parsed. + * @returns Parsed character data. + */ + public function parseData($string) { + + // following functions require at least one character + if ($string === '') return ''; + + // subtracts amps that cannot possibly be escaped + $num_amp = substr_count($string, '&') - substr_count($string, '& ') - + ($string[strlen($string)-1] === '&' ? 1 : 0); + + if (!$num_amp) return $string; // abort if no entities + $num_esc_amp = substr_count($string, '&'); + $string = strtr($string, $this->_special_entity2str); + + // code duplication for sake of optimization, see above + $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') - + ($string[strlen($string)-1] === '&' ? 1 : 0); + + if ($num_amp_2 <= $num_esc_amp) return $string; + + // hmm... now we have some uncommon entities. Use the callback. + $string = $this->_entity_parser->substituteSpecialEntities($string); + return $string; + } + + /** + * Lexes an HTML string into tokens. + * + * @param $string String HTML. + * @return HTMLPurifier_Token array representation of HTML. + */ + public function tokenizeHTML($string, $config, $context) { + trigger_error('Call to abstract class', E_USER_ERROR); + } + + /** + * Translates CDATA sections into regular sections (through escaping). + * + * @param $string HTML string to process. + * @returns HTML with CDATA sections escaped. + */ + protected static function escapeCDATA($string) { + return preg_replace_callback( + '/<!\[CDATA\[(.+?)\]\]>/s', + array('HTMLPurifier_Lexer', 'CDATACallback'), + $string + ); + } + + /** + * Special CDATA case that is especially convoluted for <script> + */ + protected static function escapeCommentedCDATA($string) { + return preg_replace_callback( + '#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s', + array('HTMLPurifier_Lexer', 'CDATACallback'), + $string + ); + } + + /** + * Callback function for escapeCDATA() that does the work. + * + * @warning Though this is public in order to let the callback happen, + * calling it directly is not recommended. + * @params $matches PCRE matches array, with index 0 the entire match + * and 1 the inside of the CDATA section. + * @returns Escaped internals of the CDATA section. + */ + protected static function CDATACallback($matches) { + // not exactly sure why the character set is needed, but whatever + return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8'); + } + + /** + * Takes a piece of HTML and normalizes it by converting entities, fixing + * encoding, extracting bits, and other good stuff. + * @todo Consider making protected + */ + public function normalize($html, $config, $context) { + + // normalize newlines to \n + $html = str_replace("\r\n", "\n", $html); + $html = str_replace("\r", "\n", $html); + + if ($config->get('HTML.Trusted')) { + // escape convoluted CDATA + $html = $this->escapeCommentedCDATA($html); + } + + // escape CDATA + $html = $this->escapeCDATA($html); + + // extract body from document if applicable + if ($config->get('Core.ConvertDocumentToFragment')) { + $html = $this->extractBody($html); + } + + // expand entities that aren't the big five + $html = $this->_entity_parser->substituteNonSpecialEntities($html); + + // clean into wellformed UTF-8 string for an SGML context: this has + // to be done after entity expansion because the entities sometimes + // represent non-SGML characters (horror, horror!) + $html = HTMLPurifier_Encoder::cleanUTF8($html); + + return $html; + } + + /** + * Takes a string of HTML (fragment or document) and returns the content + * @todo Consider making protected + */ + public function extractBody($html) { + $matches = array(); + $result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches); + if ($result) { + return $matches[1]; + } else { + return $html; + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Lexer/DOMLex.php b/lib/php/HTMLPurifier/Lexer/DOMLex.php new file mode 100644 index 0000000..20dc2ed --- /dev/null +++ b/lib/php/HTMLPurifier/Lexer/DOMLex.php @@ -0,0 +1,213 @@ +<?php + +/** + * Parser that uses PHP 5's DOM extension (part of the core). + * + * In PHP 5, the DOM XML extension was revamped into DOM and added to the core. + * It gives us a forgiving HTML parser, which we use to transform the HTML + * into a DOM, and then into the tokens. It is blazingly fast (for large + * documents, it performs twenty times faster than + * HTMLPurifier_Lexer_DirectLex,and is the default choice for PHP 5. + * + * @note Any empty elements will have empty tokens associated with them, even if + * this is prohibited by the spec. This is cannot be fixed until the spec + * comes into play. + * + * @note PHP's DOM extension does not actually parse any entities, we use + * our own function to do that. + * + * @warning DOM tends to drop whitespace, which may wreak havoc on indenting. + * If this is a huge problem, due to the fact that HTML is hand + * edited and you are unable to get a parser cache that caches the + * the output of HTML Purifier while keeping the original HTML lying + * around, you may want to run Tidy on the resulting output or use + * HTMLPurifier_DirectLex + */ + +class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer +{ + + private $factory; + + public function __construct() { + // setup the factory + parent::__construct(); + $this->factory = new HTMLPurifier_TokenFactory(); + } + + public function tokenizeHTML($html, $config, $context) { + + $html = $this->normalize($html, $config, $context); + + // attempt to armor stray angled brackets that cannot possibly + // form tags and thus are probably being used as emoticons + if ($config->get('Core.AggressivelyFixLt')) { + $char = '[^a-z!\/]'; + $comment = "/<!--(.*?)(-->|\z)/is"; + $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html); + do { + $old = $html; + $html = preg_replace("/<($char)/i", '<\\1', $html); + } while ($html !== $old); + $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments + } + + // preprocess html, essential for UTF-8 + $html = $this->wrapHTML($html, $config, $context); + + $doc = new DOMDocument(); + $doc->encoding = 'UTF-8'; // theoretically, the above has this covered + + set_error_handler(array($this, 'muteErrorHandler')); + $doc->loadHTML($html); + restore_error_handler(); + + $tokens = array(); + $this->tokenizeDOM( + $doc->getElementsByTagName('html')->item(0)-> // <html> + getElementsByTagName('body')->item(0)-> // <body> + getElementsByTagName('div')->item(0) // <div> + , $tokens); + return $tokens; + } + + /** + * Recursive function that tokenizes a node, putting it into an accumulator. + * + * @param $node DOMNode to be tokenized. + * @param $tokens Array-list of already tokenized tokens. + * @param $collect Says whether or start and close are collected, set to + * false at first recursion because it's the implicit DIV + * tag you're dealing with. + * @returns Tokens of node appended to previously passed tokens. + */ + protected function tokenizeDOM($node, &$tokens, $collect = false) { + + // intercept non element nodes. WE MUST catch all of them, + // but we're not getting the character reference nodes because + // those should have been preprocessed + if ($node->nodeType === XML_TEXT_NODE) { + $tokens[] = $this->factory->createText($node->data); + return; + } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) { + // undo libxml's special treatment of <script> and <style> tags + $last = end($tokens); + $data = $node->data; + // (note $node->tagname is already normalized) + if ($last instanceof HTMLPurifier_Token_Start && ($last->name == 'script' || $last->name == 'style')) { + $new_data = trim($data); + if (substr($new_data, 0, 4) === '<!--') { + $data = substr($new_data, 4); + if (substr($data, -3) === '-->') { + $data = substr($data, 0, -3); + } else { + // Highly suspicious! Not sure what to do... + } + } + } + $tokens[] = $this->factory->createText($this->parseData($data)); + return; + } elseif ($node->nodeType === XML_COMMENT_NODE) { + // this is code is only invoked for comments in script/style in versions + // of libxml pre-2.6.28 (regular comments, of course, are still + // handled regularly) + $tokens[] = $this->factory->createComment($node->data); + return; + } elseif ( + // not-well tested: there may be other nodes we have to grab + $node->nodeType !== XML_ELEMENT_NODE + ) { + return; + } + + $attr = $node->hasAttributes() ? + $this->transformAttrToAssoc($node->attributes) : + array(); + + // We still have to make sure that the element actually IS empty + if (!$node->childNodes->length) { + if ($collect) { + $tokens[] = $this->factory->createEmpty($node->tagName, $attr); + } + } else { + if ($collect) { // don't wrap on first iteration + $tokens[] = $this->factory->createStart( + $tag_name = $node->tagName, // somehow, it get's dropped + $attr + ); + } + foreach ($node->childNodes as $node) { + // remember, it's an accumulator. Otherwise, we'd have + // to use array_merge + $this->tokenizeDOM($node, $tokens, true); + } + if ($collect) { + $tokens[] = $this->factory->createEnd($tag_name); + } + } + + } + + /** + * Converts a DOMNamedNodeMap of DOMAttr objects into an assoc array. + * + * @param $attribute_list DOMNamedNodeMap of DOMAttr objects. + * @returns Associative array of attributes. + */ + protected function transformAttrToAssoc($node_map) { + // NamedNodeMap is documented very well, so we're using undocumented + // features, namely, the fact that it implements Iterator and + // has a ->length attribute + if ($node_map->length === 0) return array(); + $array = array(); + foreach ($node_map as $attr) { + $array[$attr->name] = $attr->value; + } + return $array; + } + + /** + * An error handler that mutes all errors + */ + public function muteErrorHandler($errno, $errstr) {} + + /** + * Callback function for undoing escaping of stray angled brackets + * in comments + */ + public function callbackUndoCommentSubst($matches) { + return '<!--' . strtr($matches[1], array('&'=>'&','<'=>'<')) . $matches[2]; + } + + /** + * Callback function that entity-izes ampersands in comments so that + * callbackUndoCommentSubst doesn't clobber them + */ + public function callbackArmorCommentEntities($matches) { + return '<!--' . str_replace('&', '&', $matches[1]) . $matches[2]; + } + + /** + * Wraps an HTML fragment in the necessary HTML + */ + protected function wrapHTML($html, $config, $context) { + $def = $config->getDefinition('HTML'); + $ret = ''; + + if (!empty($def->doctype->dtdPublic) || !empty($def->doctype->dtdSystem)) { + $ret .= '<!DOCTYPE html '; + if (!empty($def->doctype->dtdPublic)) $ret .= 'PUBLIC "' . $def->doctype->dtdPublic . '" '; + if (!empty($def->doctype->dtdSystem)) $ret .= '"' . $def->doctype->dtdSystem . '" '; + $ret .= '>'; + } + + $ret .= '<html><head>'; + $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; + // No protection if $html contains a stray </div>! + $ret .= '</head><body><div>'.$html.'</div></body></html>'; + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Lexer/DirectLex.php b/lib/php/HTMLPurifier/Lexer/DirectLex.php new file mode 100644 index 0000000..439409d --- /dev/null +++ b/lib/php/HTMLPurifier/Lexer/DirectLex.php @@ -0,0 +1,490 @@ +<?php + +/** + * Our in-house implementation of a parser. + * + * A pure PHP parser, DirectLex has absolutely no dependencies, making + * it a reasonably good default for PHP4. Written with efficiency in mind, + * it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it + * pales in comparison to HTMLPurifier_Lexer_DOMLex. + * + * @todo Reread XML spec and document differences. + */ +class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer +{ + + public $tracksLineNumbers = true; + + /** + * Whitespace characters for str(c)spn. + */ + protected $_whitespace = "\x20\x09\x0D\x0A"; + + /** + * Callback function for script CDATA fudge + * @param $matches, in form of array(opening tag, contents, closing tag) + */ + protected function scriptCallback($matches) { + return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3]; + } + + public function tokenizeHTML($html, $config, $context) { + + // special normalization for script tags without any armor + // our "armor" heurstic is a < sign any number of whitespaces after + // the first script tag + if ($config->get('HTML.Trusted')) { + $html = preg_replace_callback('#(<script[^>]*>)(\s*[^<].+?)(</script>)#si', + array($this, 'scriptCallback'), $html); + } + + $html = $this->normalize($html, $config, $context); + + $cursor = 0; // our location in the text + $inside_tag = false; // whether or not we're parsing the inside of a tag + $array = array(); // result array + + // This is also treated to mean maintain *column* numbers too + $maintain_line_numbers = $config->get('Core.MaintainLineNumbers'); + + if ($maintain_line_numbers === null) { + // automatically determine line numbering by checking + // if error collection is on + $maintain_line_numbers = $config->get('Core.CollectErrors'); + } + + if ($maintain_line_numbers) { + $current_line = 1; + $current_col = 0; + $length = strlen($html); + } else { + $current_line = false; + $current_col = false; + $length = false; + } + $context->register('CurrentLine', $current_line); + $context->register('CurrentCol', $current_col); + $nl = "\n"; + // how often to manually recalculate. This will ALWAYS be right, + // but it's pretty wasteful. Set to 0 to turn off + $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval'); + + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + + // for testing synchronization + $loops = 0; + + while(++$loops) { + + // $cursor is either at the start of a token, or inside of + // a tag (i.e. there was a < immediately before it), as indicated + // by $inside_tag + + if ($maintain_line_numbers) { + + // $rcursor, however, is always at the start of a token. + $rcursor = $cursor - (int) $inside_tag; + + // Column number is cheap, so we calculate it every round. + // We're interested at the *end* of the newline string, so + // we need to add strlen($nl) == 1 to $nl_pos before subtracting it + // from our "rcursor" position. + $nl_pos = strrpos($html, $nl, $rcursor - $length); + $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); + + // recalculate lines + if ( + $synchronize_interval && // synchronization is on + $cursor > 0 && // cursor is further than zero + $loops % $synchronize_interval === 0 // time to synchronize! + ) { + $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); + } + + } + + $position_next_lt = strpos($html, '<', $cursor); + $position_next_gt = strpos($html, '>', $cursor); + + // triggers on "<b>asdf</b>" but not "asdf <b></b>" + // special case to set up context + if ($position_next_lt === $cursor) { + $inside_tag = true; + $cursor++; + } + + if (!$inside_tag && $position_next_lt !== false) { + // We are not inside tag and there still is another tag to parse + $token = new + HTMLPurifier_Token_Text( + $this->parseData( + substr( + $html, $cursor, $position_next_lt - $cursor + ) + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); + } + $array[] = $token; + $cursor = $position_next_lt + 1; + $inside_tag = true; + continue; + } elseif (!$inside_tag) { + // We are not inside tag but there are no more tags + // If we're already at the end, break + if ($cursor === strlen($html)) break; + // Create Text of rest of string + $token = new + HTMLPurifier_Token_Text( + $this->parseData( + substr( + $html, $cursor + ) + ) + ); + if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); + $array[] = $token; + break; + } elseif ($inside_tag && $position_next_gt !== false) { + // We are in tag and it is well formed + // Grab the internals of the tag + $strlen_segment = $position_next_gt - $cursor; + + if ($strlen_segment < 1) { + // there's nothing to process! + $token = new HTMLPurifier_Token_Text('<'); + $cursor++; + continue; + } + + $segment = substr($html, $cursor, $strlen_segment); + + if ($segment === false) { + // somehow, we attempted to access beyond the end of + // the string, defense-in-depth, reported by Nate Abele + break; + } + + // Check if it's a comment + if ( + substr($segment, 0, 3) === '!--' + ) { + // re-determine segment length, looking for --> + $position_comment_end = strpos($html, '-->', $cursor); + if ($position_comment_end === false) { + // uh oh, we have a comment that extends to + // infinity. Can't be helped: set comment + // end position to end of string + if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment'); + $position_comment_end = strlen($html); + $end = true; + } else { + $end = false; + } + $strlen_segment = $position_comment_end - $cursor; + $segment = substr($html, $cursor, $strlen_segment); + $token = new + HTMLPurifier_Token_Comment( + substr( + $segment, 3, $strlen_segment - 3 + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); + } + $array[] = $token; + $cursor = $end ? $position_comment_end : $position_comment_end + 3; + $inside_tag = false; + continue; + } + + // Check if it's an end tag + $is_end_tag = (strpos($segment,'/') === 0); + if ($is_end_tag) { + $type = substr($segment, 1); + $token = new HTMLPurifier_Token_End($type); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $inside_tag = false; + $cursor = $position_next_gt + 1; + continue; + } + + // Check leading character is alnum, if not, we may + // have accidently grabbed an emoticon. Translate into + // text and go our merry way + if (!ctype_alpha($segment[0])) { + // XML: $segment[0] !== '_' && $segment[0] !== ':' + if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt'); + $token = new HTMLPurifier_Token_Text('<'); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $inside_tag = false; + continue; + } + + // Check if it is explicitly self closing, if so, remove + // trailing slash. Remember, we could have a tag like <br>, so + // any later token processing scripts must convert improperly + // classified EmptyTags from StartTags. + $is_self_closing = (strrpos($segment,'/') === $strlen_segment-1); + if ($is_self_closing) { + $strlen_segment--; + $segment = substr($segment, 0, $strlen_segment); + } + + // Check if there are any attributes + $position_first_space = strcspn($segment, $this->_whitespace); + + if ($position_first_space >= $strlen_segment) { + if ($is_self_closing) { + $token = new HTMLPurifier_Token_Empty($segment); + } else { + $token = new HTMLPurifier_Token_Start($segment); + } + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $inside_tag = false; + $cursor = $position_next_gt + 1; + continue; + } + + // Grab out all the data + $type = substr($segment, 0, $position_first_space); + $attribute_string = + trim( + substr( + $segment, $position_first_space + ) + ); + if ($attribute_string) { + $attr = $this->parseAttributeString( + $attribute_string + , $config, $context + ); + } else { + $attr = array(); + } + + if ($is_self_closing) { + $token = new HTMLPurifier_Token_Empty($type, $attr); + } else { + $token = new HTMLPurifier_Token_Start($type, $attr); + } + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); + } + $array[] = $token; + $cursor = $position_next_gt + 1; + $inside_tag = false; + continue; + } else { + // inside tag, but there's no ending > sign + if ($e) $e->send(E_WARNING, 'Lexer: Missing gt'); + $token = new + HTMLPurifier_Token_Text( + '<' . + $this->parseData( + substr($html, $cursor) + ) + ); + if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); + // no cursor scroll? Hmm... + $array[] = $token; + break; + } + break; + } + + $context->destroy('CurrentLine'); + $context->destroy('CurrentCol'); + return $array; + } + + /** + * PHP 5.0.x compatible substr_count that implements offset and length + */ + protected function substrCount($haystack, $needle, $offset, $length) { + static $oldVersion; + if ($oldVersion === null) { + $oldVersion = version_compare(PHP_VERSION, '5.1', '<'); + } + if ($oldVersion) { + $haystack = substr($haystack, $offset, $length); + return substr_count($haystack, $needle); + } else { + return substr_count($haystack, $needle, $offset, $length); + } + } + + /** + * Takes the inside of an HTML tag and makes an assoc array of attributes. + * + * @param $string Inside of tag excluding name. + * @returns Assoc array of attributes. + */ + public function parseAttributeString($string, $config, $context) { + $string = (string) $string; // quick typecast + + if ($string == '') return array(); // no attributes + + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + + // let's see if we can abort as quickly as possible + // one equal sign, no spaces => one attribute + $num_equal = substr_count($string, '='); + $has_space = strpos($string, ' '); + if ($num_equal === 0 && !$has_space) { + // bool attribute + return array($string => $string); + } elseif ($num_equal === 1 && !$has_space) { + // only one attribute + list($key, $quoted_value) = explode('=', $string); + $quoted_value = trim($quoted_value); + if (!$key) { + if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + return array(); + } + if (!$quoted_value) return array($key => ''); + $first_char = @$quoted_value[0]; + $last_char = @$quoted_value[strlen($quoted_value)-1]; + + $same_quote = ($first_char == $last_char); + $open_quote = ($first_char == '"' || $first_char == "'"); + + if ( $same_quote && $open_quote) { + // well behaved + $value = substr($quoted_value, 1, strlen($quoted_value) - 2); + } else { + // not well behaved + if ($open_quote) { + if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote'); + $value = substr($quoted_value, 1); + } else { + $value = $quoted_value; + } + } + if ($value === false) $value = ''; + return array($key => $value); + } + + // setup loop environment + $array = array(); // return assoc array of attributes + $cursor = 0; // current position in string (moves forward) + $size = strlen($string); // size of the string (stays the same) + + // if we have unquoted attributes, the parser expects a terminating + // space, so let's guarantee that there's always a terminating space. + $string .= ' '; + + while(true) { + + if ($cursor >= $size) { + break; + } + + $cursor += ($value = strspn($string, $this->_whitespace, $cursor)); + // grab the key + + $key_begin = $cursor; //we're currently at the start of the key + + // scroll past all characters that are the key (not whitespace or =) + $cursor += strcspn($string, $this->_whitespace . '=', $cursor); + + $key_end = $cursor; // now at the end of the key + + $key = substr($string, $key_begin, $key_end - $key_begin); + + if (!$key) { + if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + $cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop + continue; // empty key + } + + // scroll past all whitespace + $cursor += strspn($string, $this->_whitespace, $cursor); + + if ($cursor >= $size) { + $array[$key] = $key; + break; + } + + // if the next character is an equal sign, we've got a regular + // pair, otherwise, it's a bool attribute + $first_char = @$string[$cursor]; + + if ($first_char == '=') { + // key="value" + + $cursor++; + $cursor += strspn($string, $this->_whitespace, $cursor); + + if ($cursor === false) { + $array[$key] = ''; + break; + } + + // we might be in front of a quote right now + + $char = @$string[$cursor]; + + if ($char == '"' || $char == "'") { + // it's quoted, end bound is $char + $cursor++; + $value_begin = $cursor; + $cursor = strpos($string, $char, $cursor); + $value_end = $cursor; + } else { + // it's not quoted, end bound is whitespace + $value_begin = $cursor; + $cursor += strcspn($string, $this->_whitespace, $cursor); + $value_end = $cursor; + } + + // we reached a premature end + if ($cursor === false) { + $cursor = $size; + $value_end = $cursor; + } + + $value = substr($string, $value_begin, $value_end - $value_begin); + if ($value === false) $value = ''; + $array[$key] = $this->parseData($value); + $cursor++; + + } else { + // boolattr + if ($key !== '') { + $array[$key] = $key; + } else { + // purely theoretical + if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } + + } + } + return $array; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Lexer/PEARSax3.php b/lib/php/HTMLPurifier/Lexer/PEARSax3.php new file mode 100644 index 0000000..57cffa8 --- /dev/null +++ b/lib/php/HTMLPurifier/Lexer/PEARSax3.php @@ -0,0 +1,106 @@ +<?php + +/** + * Proof-of-concept lexer that uses the PEAR package XML_HTMLSax3 to parse HTML. + * + * PEAR, not suprisingly, also has a SAX parser for HTML. I don't know + * very much about implementation, but it's fairly well written. However, that + * abstraction comes at a price: performance. You need to have it installed, + * and if the API changes, it might break our adapter. Not sure whether or not + * it's UTF-8 aware, but it has some entity parsing trouble (in all areas, + * text and attributes). + * + * Quite personally, I don't recommend using the PEAR class, and the defaults + * don't use it. The unit tests do perform the tests on the SAX parser too, but + * whatever it does for poorly formed HTML is up to it. + * + * @todo Generalize so that XML_HTMLSax is also supported. + * + * @warning Entity-resolution inside attributes is broken. + */ + +class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer +{ + + /** + * Internal accumulator array for SAX parsers. + */ + protected $tokens = array(); + + public function tokenizeHTML($string, $config, $context) { + + $this->tokens = array(); + + $string = $this->normalize($string, $config, $context); + + $parser = new XML_HTMLSax3(); + $parser->set_object($this); + $parser->set_element_handler('openHandler','closeHandler'); + $parser->set_data_handler('dataHandler'); + $parser->set_escape_handler('escapeHandler'); + + // doesn't seem to work correctly for attributes + $parser->set_option('XML_OPTION_ENTITIES_PARSED', 1); + + $parser->parse($string); + + return $this->tokens; + + } + + /** + * Open tag event handler, interface is defined by PEAR package. + */ + public function openHandler(&$parser, $name, $attrs, $closed) { + // entities are not resolved in attrs + foreach ($attrs as $key => $attr) { + $attrs[$key] = $this->parseData($attr); + } + if ($closed) { + $this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs); + } else { + $this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs); + } + return true; + } + + /** + * Close tag event handler, interface is defined by PEAR package. + */ + public function closeHandler(&$parser, $name) { + // HTMLSax3 seems to always send empty tags an extra close tag + // check and ignore if you see it: + // [TESTME] to make sure it doesn't overreach + if ($this->tokens[count($this->tokens)-1] instanceof HTMLPurifier_Token_Empty) { + return true; + } + $this->tokens[] = new HTMLPurifier_Token_End($name); + return true; + } + + /** + * Data event handler, interface is defined by PEAR package. + */ + public function dataHandler(&$parser, $data) { + $this->tokens[] = new HTMLPurifier_Token_Text($data); + return true; + } + + /** + * Escaped text handler, interface is defined by PEAR package. + */ + public function escapeHandler(&$parser, $data) { + if (strpos($data, '--') === 0) { + $this->tokens[] = new HTMLPurifier_Token_Comment($data); + } + // CDATA is handled elsewhere, but if it was handled here: + //if (strpos($data, '[CDATA[') === 0) { + // $this->tokens[] = new HTMLPurifier_Token_Text( + // substr($data, 7, strlen($data) - 9) ); + //} + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Lexer/PH5P.php b/lib/php/HTMLPurifier/Lexer/PH5P.php new file mode 100644 index 0000000..fa1bf97 --- /dev/null +++ b/lib/php/HTMLPurifier/Lexer/PH5P.php @@ -0,0 +1,3906 @@ +<?php + +/** + * Experimental HTML5-based parser using Jeroen van der Meer's PH5P library. + * Occupies space in the HTML5 pseudo-namespace, which may cause conflicts. + * + * @note + * Recent changes to PHP's DOM extension have resulted in some fatal + * error conditions with the original version of PH5P. Pending changes, + * this lexer will punt to DirectLex if DOM throughs an exception. + */ + +class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex { + + public function tokenizeHTML($html, $config, $context) { + $new_html = $this->normalize($html, $config, $context); + $new_html = $this->wrapHTML($new_html, $config, $context); + try { + $parser = new HTML5($new_html); + $doc = $parser->save(); + } catch (DOMException $e) { + // Uh oh, it failed. Punt to DirectLex. + $lexer = new HTMLPurifier_Lexer_DirectLex(); + $context->register('PH5PError', $e); // save the error, so we can detect it + return $lexer->tokenizeHTML($html, $config, $context); // use original HTML + } + $tokens = array(); + $this->tokenizeDOM( + $doc->getElementsByTagName('html')->item(0)-> // <html> + getElementsByTagName('body')->item(0)-> // <body> + getElementsByTagName('div')->item(0) // <div> + , $tokens); + return $tokens; + } + +} + +/* + +Copyright 2007 Jeroen van der Meer <http://jero.net/> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +class HTML5 { + private $data; + private $char; + private $EOF; + private $state; + private $tree; + private $token; + private $content_model; + private $escape = false; + private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute', + 'Acirc;','Acirc','Agrave;','Agrave','Alpha;','Aring;','Aring','Atilde;', + 'Atilde','Auml;','Auml','Beta;','COPY;','COPY','Ccedil;','Ccedil','Chi;', + 'Dagger;','Delta;','ETH;','ETH','Eacute;','Eacute','Ecirc;','Ecirc','Egrave;', + 'Egrave','Epsilon;','Eta;','Euml;','Euml','GT;','GT','Gamma;','Iacute;', + 'Iacute','Icirc;','Icirc','Igrave;','Igrave','Iota;','Iuml;','Iuml','Kappa;', + 'LT;','LT','Lambda;','Mu;','Ntilde;','Ntilde','Nu;','OElig;','Oacute;', + 'Oacute','Ocirc;','Ocirc','Ograve;','Ograve','Omega;','Omicron;','Oslash;', + 'Oslash','Otilde;','Otilde','Ouml;','Ouml','Phi;','Pi;','Prime;','Psi;', + 'QUOT;','QUOT','REG;','REG','Rho;','Scaron;','Sigma;','THORN;','THORN', + 'TRADE;','Tau;','Theta;','Uacute;','Uacute','Ucirc;','Ucirc','Ugrave;', + 'Ugrave','Upsilon;','Uuml;','Uuml','Xi;','Yacute;','Yacute','Yuml;','Zeta;', + 'aacute;','aacute','acirc;','acirc','acute;','acute','aelig;','aelig', + 'agrave;','agrave','alefsym;','alpha;','amp;','amp','and;','ang;','apos;', + 'aring;','aring','asymp;','atilde;','atilde','auml;','auml','bdquo;','beta;', + 'brvbar;','brvbar','bull;','cap;','ccedil;','ccedil','cedil;','cedil', + 'cent;','cent','chi;','circ;','clubs;','cong;','copy;','copy','crarr;', + 'cup;','curren;','curren','dArr;','dagger;','darr;','deg;','deg','delta;', + 'diams;','divide;','divide','eacute;','eacute','ecirc;','ecirc','egrave;', + 'egrave','empty;','emsp;','ensp;','epsilon;','equiv;','eta;','eth;','eth', + 'euml;','euml','euro;','exist;','fnof;','forall;','frac12;','frac12', + 'frac14;','frac14','frac34;','frac34','frasl;','gamma;','ge;','gt;','gt', + 'hArr;','harr;','hearts;','hellip;','iacute;','iacute','icirc;','icirc', + 'iexcl;','iexcl','igrave;','igrave','image;','infin;','int;','iota;', + 'iquest;','iquest','isin;','iuml;','iuml','kappa;','lArr;','lambda;','lang;', + 'laquo;','laquo','larr;','lceil;','ldquo;','le;','lfloor;','lowast;','loz;', + 'lrm;','lsaquo;','lsquo;','lt;','lt','macr;','macr','mdash;','micro;','micro', + 'middot;','middot','minus;','mu;','nabla;','nbsp;','nbsp','ndash;','ne;', + 'ni;','not;','not','notin;','nsub;','ntilde;','ntilde','nu;','oacute;', + 'oacute','ocirc;','ocirc','oelig;','ograve;','ograve','oline;','omega;', + 'omicron;','oplus;','or;','ordf;','ordf','ordm;','ordm','oslash;','oslash', + 'otilde;','otilde','otimes;','ouml;','ouml','para;','para','part;','permil;', + 'perp;','phi;','pi;','piv;','plusmn;','plusmn','pound;','pound','prime;', + 'prod;','prop;','psi;','quot;','quot','rArr;','radic;','rang;','raquo;', + 'raquo','rarr;','rceil;','rdquo;','real;','reg;','reg','rfloor;','rho;', + 'rlm;','rsaquo;','rsquo;','sbquo;','scaron;','sdot;','sect;','sect','shy;', + 'shy','sigma;','sigmaf;','sim;','spades;','sub;','sube;','sum;','sup1;', + 'sup1','sup2;','sup2','sup3;','sup3','sup;','supe;','szlig;','szlig','tau;', + 'there4;','theta;','thetasym;','thinsp;','thorn;','thorn','tilde;','times;', + 'times','trade;','uArr;','uacute;','uacute','uarr;','ucirc;','ucirc', + 'ugrave;','ugrave','uml;','uml','upsih;','upsilon;','uuml;','uuml','weierp;', + 'xi;','yacute;','yacute','yen;','yen','yuml;','yuml','zeta;','zwj;','zwnj;'); + + const PCDATA = 0; + const RCDATA = 1; + const CDATA = 2; + const PLAINTEXT = 3; + + const DOCTYPE = 0; + const STARTTAG = 1; + const ENDTAG = 2; + const COMMENT = 3; + const CHARACTR = 4; + const EOF = 5; + + public function __construct($data) { + $data = str_replace("\r\n", "\n", $data); + $data = str_replace("\r", null, $data); + + $this->data = $data; + $this->char = -1; + $this->EOF = strlen($data); + $this->tree = new HTML5TreeConstructer; + $this->content_model = self::PCDATA; + + $this->state = 'data'; + + while($this->state !== null) { + $this->{$this->state.'State'}(); + } + } + + public function save() { + return $this->tree->save(); + } + + private function char() { + return ($this->char < $this->EOF) + ? $this->data[$this->char] + : false; + } + + private function character($s, $l = 0) { + if($s + $l < $this->EOF) { + if($l === 0) { + return $this->data[$s]; + } else { + return substr($this->data, $s, $l); + } + } + } + + private function characters($char_class, $start) { + return preg_replace('#^(['.$char_class.']+).*#s', '\\1', substr($this->data, $start)); + } + + private function dataState() { + // Consume the next input character + $this->char++; + $char = $this->char(); + + if($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) { + /* U+0026 AMPERSAND (&) + When the content model flag is set to one of the PCDATA or RCDATA + states: switch to the entity data state. Otherwise: treat it as per + the "anything else" entry below. */ + $this->state = 'entityData'; + + } elseif($char === '-') { + /* If the content model flag is set to either the RCDATA state or + the CDATA state, and the escape flag is false, and there are at + least three characters before this one in the input stream, and the + last four characters in the input stream, including this one, are + U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, + and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */ + if(($this->content_model === self::RCDATA || $this->content_model === + self::CDATA) && $this->escape === false && + $this->char >= 3 && $this->character($this->char - 4, 4) === '<!--') { + $this->escape = true; + } + + /* In any case, emit the input character as a character token. Stay + in the data state. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => $char + )); + + /* U+003C LESS-THAN SIGN (<) */ + } elseif($char === '<' && ($this->content_model === self::PCDATA || + (($this->content_model === self::RCDATA || + $this->content_model === self::CDATA) && $this->escape === false))) { + /* When the content model flag is set to the PCDATA state: switch + to the tag open state. + + When the content model flag is set to either the RCDATA state or + the CDATA state and the escape flag is false: switch to the tag + open state. + + Otherwise: treat it as per the "anything else" entry below. */ + $this->state = 'tagOpen'; + + /* U+003E GREATER-THAN SIGN (>) */ + } elseif($char === '>') { + /* If the content model flag is set to either the RCDATA state or + the CDATA state, and the escape flag is true, and the last three + characters in the input stream including this one are U+002D + HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"), + set the escape flag to false. */ + if(($this->content_model === self::RCDATA || + $this->content_model === self::CDATA) && $this->escape === true && + $this->character($this->char, 3) === '-->') { + $this->escape = false; + } + + /* In any case, emit the input character as a character token. + Stay in the data state. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => $char + )); + + } elseif($this->char === $this->EOF) { + /* EOF + Emit an end-of-file token. */ + $this->EOF(); + + } elseif($this->content_model === self::PLAINTEXT) { + /* When the content model flag is set to the PLAINTEXT state + THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of + the text and emit it as a character token. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => substr($this->data, $this->char) + )); + + $this->EOF(); + + } else { + /* Anything else + THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that + otherwise would also be treated as a character token and emit it + as a single character token. Stay in the data state. */ + $len = strcspn($this->data, '<&', $this->char); + $char = substr($this->data, $this->char, $len); + $this->char += $len - 1; + + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => $char + )); + + $this->state = 'data'; + } + } + + private function entityDataState() { + // Attempt to consume an entity. + $entity = $this->entity(); + + // If nothing is returned, emit a U+0026 AMPERSAND character token. + // Otherwise, emit the character token that was returned. + $char = (!$entity) ? '&' : $entity; + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => $char + )); + + // Finally, switch to the data state. + $this->state = 'data'; + } + + private function tagOpenState() { + switch($this->content_model) { + case self::RCDATA: + case self::CDATA: + /* If the next input character is a U+002F SOLIDUS (/) character, + consume it and switch to the close tag open state. If the next + input character is not a U+002F SOLIDUS (/) character, emit a + U+003C LESS-THAN SIGN character token and switch to the data + state to process the next input character. */ + if($this->character($this->char + 1) === '/') { + $this->char++; + $this->state = 'closeTagOpen'; + + } else { + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => '<' + )); + + $this->state = 'data'; + } + break; + + case self::PCDATA: + // If the content model flag is set to the PCDATA state + // Consume the next input character: + $this->char++; + $char = $this->char(); + + if($char === '!') { + /* U+0021 EXCLAMATION MARK (!) + Switch to the markup declaration open state. */ + $this->state = 'markupDeclarationOpen'; + + } elseif($char === '/') { + /* U+002F SOLIDUS (/) + Switch to the close tag open state. */ + $this->state = 'closeTagOpen'; + + } elseif(preg_match('/^[A-Za-z]$/', $char)) { + /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z + Create a new start tag token, set its tag name to the lowercase + version of the input character (add 0x0020 to the character's code + point), then switch to the tag name state. (Don't emit the token + yet; further details will be filled in before it is emitted.) */ + $this->token = array( + 'name' => strtolower($char), + 'type' => self::STARTTAG, + 'attr' => array() + ); + + $this->state = 'tagName'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Parse error. Emit a U+003C LESS-THAN SIGN character token and a + U+003E GREATER-THAN SIGN character token. Switch to the data state. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => '<>' + )); + + $this->state = 'data'; + + } elseif($char === '?') { + /* U+003F QUESTION MARK (?) + Parse error. Switch to the bogus comment state. */ + $this->state = 'bogusComment'; + + } else { + /* Anything else + Parse error. Emit a U+003C LESS-THAN SIGN character token and + reconsume the current input character in the data state. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => '<' + )); + + $this->char--; + $this->state = 'data'; + } + break; + } + } + + private function closeTagOpenState() { + $next_node = strtolower($this->characters('A-Za-z', $this->char + 1)); + $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName; + + if(($this->content_model === self::RCDATA || $this->content_model === self::CDATA) && + (!$the_same || ($the_same && (!preg_match('/[\t\n\x0b\x0c >\/]/', + $this->character($this->char + 1 + strlen($next_node))) || $this->EOF === $this->char)))) { + /* If the content model flag is set to the RCDATA or CDATA states then + examine the next few characters. If they do not match the tag name of + the last start tag token emitted (case insensitively), or if they do but + they are not immediately followed by one of the following characters: + * U+0009 CHARACTER TABULATION + * U+000A LINE FEED (LF) + * U+000B LINE TABULATION + * U+000C FORM FEED (FF) + * U+0020 SPACE + * U+003E GREATER-THAN SIGN (>) + * U+002F SOLIDUS (/) + * EOF + ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character + token, a U+002F SOLIDUS character token, and switch to the data state + to process the next input character. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => '</' + )); + + $this->state = 'data'; + + } else { + /* Otherwise, if the content model flag is set to the PCDATA state, + or if the next few characters do match that tag name, consume the + next input character: */ + $this->char++; + $char = $this->char(); + + if(preg_match('/^[A-Za-z]$/', $char)) { + /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z + Create a new end tag token, set its tag name to the lowercase version + of the input character (add 0x0020 to the character's code point), then + switch to the tag name state. (Don't emit the token yet; further details + will be filled in before it is emitted.) */ + $this->token = array( + 'name' => strtolower($char), + 'type' => self::ENDTAG + ); + + $this->state = 'tagName'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Parse error. Switch to the data state. */ + $this->state = 'data'; + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F + SOLIDUS character token. Reconsume the EOF character in the data state. */ + $this->emitToken(array( + 'type' => self::CHARACTR, + 'data' => '</' + )); + + $this->char--; + $this->state = 'data'; + + } else { + /* Parse error. Switch to the bogus comment state. */ + $this->state = 'bogusComment'; + } + } + } + + private function tagNameState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } elseif($char === '/') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } else { + /* Anything else + Append the current input character to the current tag token's tag name. + Stay in the tag name state. */ + $this->token['name'] .= strtolower($char); + $this->state = 'tagName'; + } + } + + private function beforeAttributeNameState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($char === '/') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Stay in the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Start a new attribute in the current tag token. Set that attribute's + name to the current input character, and its value to the empty string. + Switch to the attribute name state. */ + $this->token['attr'][] = array( + 'name' => strtolower($char), + 'value' => null + ); + + $this->state = 'attributeName'; + } + } + + private function attributeNameState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute name state. */ + $this->state = 'afterAttributeName'; + + } elseif($char === '=') { + /* U+003D EQUALS SIGN (=) + Switch to the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($char === '/' && $this->character($this->char + 1) !== '>') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's name. + Stay in the attribute name state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['name'] .= strtolower($char); + + $this->state = 'attributeName'; + } + } + + private function afterAttributeNameState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the after attribute name state. */ + $this->state = 'afterAttributeName'; + + } elseif($char === '=') { + /* U+003D EQUALS SIGN (=) + Switch to the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($char === '/' && $this->character($this->char + 1) !== '>') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the + before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Start a new attribute in the current tag token. Set that attribute's + name to the current input character, and its value to the empty string. + Switch to the attribute name state. */ + $this->token['attr'][] = array( + 'name' => strtolower($char), + 'value' => null + ); + + $this->state = 'attributeName'; + } + } + + private function beforeAttributeValueState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif($char === '"') { + /* U+0022 QUOTATION MARK (") + Switch to the attribute value (double-quoted) state. */ + $this->state = 'attributeValueDoubleQuoted'; + + } elseif($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the attribute value (unquoted) state and reconsume + this input character. */ + $this->char--; + $this->state = 'attributeValueUnquoted'; + + } elseif($char === '\'') { + /* U+0027 APOSTROPHE (') + Switch to the attribute value (single-quoted) state. */ + $this->state = 'attributeValueSingleQuoted'; + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Switch to the attribute value (unquoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueUnquoted'; + } + } + + private function attributeValueDoubleQuotedState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if($char === '"') { + /* U+0022 QUOTATION MARK (") + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState('double'); + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the character + in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (double-quoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueDoubleQuoted'; + } + } + + private function attributeValueSingleQuotedState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if($char === '\'') { + /* U+0022 QUOTATION MARK (') + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState('single'); + + } elseif($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the character + in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (single-quoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueSingleQuoted'; + } + } + + private function attributeValueUnquotedState() { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState(); + + } elseif($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (unquoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueUnquoted'; + } + } + + private function entityInAttributeValueState() { + // Attempt to consume an entity. + $entity = $this->entity(); + + // If nothing is returned, append a U+0026 AMPERSAND character to the + // current attribute's value. Otherwise, emit the character token that + // was returned. + $char = (!$entity) + ? '&' + : $entity; + + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + } + + private function bogusCommentState() { + /* Consume every character up to the first U+003E GREATER-THAN SIGN + character (>) or the end of the file (EOF), whichever comes first. Emit + a comment token whose data is the concatenation of all the characters + starting from and including the character that caused the state machine + to switch into the bogus comment state, up to and including the last + consumed character before the U+003E character, if any, or up to the + end of the file otherwise. (If the comment was started by the end of + the file (EOF), the token is empty.) */ + $data = $this->characters('^>', $this->char); + $this->emitToken(array( + 'data' => $data, + 'type' => self::COMMENT + )); + + $this->char += strlen($data); + + /* Switch to the data state. */ + $this->state = 'data'; + + /* If the end of the file was reached, reconsume the EOF character. */ + if($this->char === $this->EOF) { + $this->char = $this->EOF - 1; + } + } + + private function markupDeclarationOpenState() { + /* If the next two characters are both U+002D HYPHEN-MINUS (-) + characters, consume those two characters, create a comment token whose + data is the empty string, and switch to the comment state. */ + if($this->character($this->char + 1, 2) === '--') { + $this->char += 2; + $this->state = 'comment'; + $this->token = array( + 'data' => null, + 'type' => self::COMMENT + ); + + /* Otherwise if the next seven chacacters are a case-insensitive match + for the word "DOCTYPE", then consume those characters and switch to the + DOCTYPE state. */ + } elseif(strtolower($this->character($this->char + 1, 7)) === 'doctype') { + $this->char += 7; + $this->state = 'doctype'; + + /* Otherwise, is is a parse error. Switch to the bogus comment state. + The next character that is consumed, if any, is the first character + that will be in the comment. */ + } else { + $this->char++; + $this->state = 'bogusComment'; + } + } + + private function commentState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + /* U+002D HYPHEN-MINUS (-) */ + if($char === '-') { + /* Switch to the comment dash state */ + $this->state = 'commentDash'; + + /* EOF */ + } elseif($this->char === $this->EOF) { + /* Parse error. Emit the comment token. Reconsume the EOF character + in the data state. */ + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + /* Anything else */ + } else { + /* Append the input character to the comment token's data. Stay in + the comment state. */ + $this->token['data'] .= $char; + } + } + + private function commentDashState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + /* U+002D HYPHEN-MINUS (-) */ + if($char === '-') { + /* Switch to the comment end state */ + $this->state = 'commentEnd'; + + /* EOF */ + } elseif($this->char === $this->EOF) { + /* Parse error. Emit the comment token. Reconsume the EOF character + in the data state. */ + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + /* Anything else */ + } else { + /* Append a U+002D HYPHEN-MINUS (-) character and the input + character to the comment token's data. Switch to the comment state. */ + $this->token['data'] .= '-'.$char; + $this->state = 'comment'; + } + } + + private function commentEndState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($char === '-') { + $this->token['data'] .= '-'; + + } elseif($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['data'] .= '--'.$char; + $this->state = 'comment'; + } + } + + private function doctypeState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + $this->state = 'beforeDoctypeName'; + + } else { + $this->char--; + $this->state = 'beforeDoctypeName'; + } + } + + private function beforeDoctypeNameState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + // Stay in the before DOCTYPE name state. + + } elseif(preg_match('/^[a-z]$/', $char)) { + $this->token = array( + 'name' => strtoupper($char), + 'type' => self::DOCTYPE, + 'error' => true + ); + + $this->state = 'doctypeName'; + + } elseif($char === '>') { + $this->emitToken(array( + 'name' => null, + 'type' => self::DOCTYPE, + 'error' => true + )); + + $this->state = 'data'; + + } elseif($this->char === $this->EOF) { + $this->emitToken(array( + 'name' => null, + 'type' => self::DOCTYPE, + 'error' => true + )); + + $this->char--; + $this->state = 'data'; + + } else { + $this->token = array( + 'name' => $char, + 'type' => self::DOCTYPE, + 'error' => true + ); + + $this->state = 'doctypeName'; + } + } + + private function doctypeNameState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + $this->state = 'AfterDoctypeName'; + + } elseif($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif(preg_match('/^[a-z]$/', $char)) { + $this->token['name'] .= strtoupper($char); + + } elseif($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['name'] .= $char; + } + + $this->token['error'] = ($this->token['name'] === 'HTML') + ? false + : true; + } + + private function afterDoctypeNameState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + // Stay in the DOCTYPE name state. + + } elseif($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['error'] = true; + $this->state = 'bogusDoctype'; + } + } + + private function bogusDoctypeState() { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + // Stay in the bogus DOCTYPE state. + } + } + + private function entity() { + $start = $this->char; + + // This section defines how to consume an entity. This definition is + // used when parsing entities in text and in attributes. + + // The behaviour depends on the identity of the next character (the + // one immediately after the U+0026 AMPERSAND character): + + switch($this->character($this->char + 1)) { + // U+0023 NUMBER SIGN (#) + case '#': + + // The behaviour further depends on the character after the + // U+0023 NUMBER SIGN: + switch($this->character($this->char + 1)) { + // U+0078 LATIN SMALL LETTER X + // U+0058 LATIN CAPITAL LETTER X + case 'x': + case 'X': + // Follow the steps below, but using the range of + // characters U+0030 DIGIT ZERO through to U+0039 DIGIT + // NINE, U+0061 LATIN SMALL LETTER A through to U+0066 + // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER + // A, through to U+0046 LATIN CAPITAL LETTER F (in other + // words, 0-9, A-F, a-f). + $char = 1; + $char_class = '0-9A-Fa-f'; + break; + + // Anything else + default: + // Follow the steps below, but using the range of + // characters U+0030 DIGIT ZERO through to U+0039 DIGIT + // NINE (i.e. just 0-9). + $char = 0; + $char_class = '0-9'; + break; + } + + // Consume as many characters as match the range of characters + // given above. + $this->char++; + $e_name = $this->characters($char_class, $this->char + $char + 1); + $entity = $this->character($start, $this->char); + $cond = strlen($e_name) > 0; + + // The rest of the parsing happens bellow. + break; + + // Anything else + default: + // Consume the maximum number of characters possible, with the + // consumed characters case-sensitively matching one of the + // identifiers in the first column of the entities table. + $e_name = $this->characters('0-9A-Za-z;', $this->char + 1); + $len = strlen($e_name); + + for($c = 1; $c <= $len; $c++) { + $id = substr($e_name, 0, $c); + $this->char++; + + if(in_array($id, $this->entities)) { + if ($e_name[$c-1] !== ';') { + if ($c < $len && $e_name[$c] == ';') { + $this->char++; // consume extra semicolon + } + } + $entity = $id; + break; + } + } + + $cond = isset($entity); + // The rest of the parsing happens bellow. + break; + } + + if(!$cond) { + // If no match can be made, then this is a parse error. No + // characters are consumed, and nothing is returned. + $this->char = $start; + return false; + } + + // Return a character token for the character corresponding to the + // entity name (as given by the second column of the entities table). + return html_entity_decode('&'.$entity.';', ENT_QUOTES, 'UTF-8'); + } + + private function emitToken($token) { + $emit = $this->tree->emitToken($token); + + if(is_int($emit)) { + $this->content_model = $emit; + + } elseif($token['type'] === self::ENDTAG) { + $this->content_model = self::PCDATA; + } + } + + private function EOF() { + $this->state = null; + $this->tree->emitToken(array( + 'type' => self::EOF + )); + } +} + +class HTML5TreeConstructer { + public $stack = array(); + + private $phase; + private $mode; + private $dom; + private $foster_parent = null; + private $a_formatting = array(); + + private $head_pointer = null; + private $form_pointer = null; + + private $scoping = array('button','caption','html','marquee','object','table','td','th'); + private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u'); + private $special = array('address','area','base','basefont','bgsound', + 'blockquote','body','br','center','col','colgroup','dd','dir','div','dl', + 'dt','embed','fieldset','form','frame','frameset','h1','h2','h3','h4','h5', + 'h6','head','hr','iframe','image','img','input','isindex','li','link', + 'listing','menu','meta','noembed','noframes','noscript','ol','optgroup', + 'option','p','param','plaintext','pre','script','select','spacer','style', + 'tbody','textarea','tfoot','thead','title','tr','ul','wbr'); + + // The different phases. + const INIT_PHASE = 0; + const ROOT_PHASE = 1; + const MAIN_PHASE = 2; + const END_PHASE = 3; + + // The different insertion modes for the main phase. + const BEFOR_HEAD = 0; + const IN_HEAD = 1; + const AFTER_HEAD = 2; + const IN_BODY = 3; + const IN_TABLE = 4; + const IN_CAPTION = 5; + const IN_CGROUP = 6; + const IN_TBODY = 7; + const IN_ROW = 8; + const IN_CELL = 9; + const IN_SELECT = 10; + const AFTER_BODY = 11; + const IN_FRAME = 12; + const AFTR_FRAME = 13; + + // The different types of elements. + const SPECIAL = 0; + const SCOPING = 1; + const FORMATTING = 2; + const PHRASING = 3; + + const MARKER = 0; + + public function __construct() { + $this->phase = self::INIT_PHASE; + $this->mode = self::BEFOR_HEAD; + $this->dom = new DOMDocument; + + $this->dom->encoding = 'UTF-8'; + $this->dom->preserveWhiteSpace = true; + $this->dom->substituteEntities = true; + $this->dom->strictErrorChecking = false; + } + + // Process tag tokens + public function emitToken($token) { + switch($this->phase) { + case self::INIT_PHASE: return $this->initPhase($token); break; + case self::ROOT_PHASE: return $this->rootElementPhase($token); break; + case self::MAIN_PHASE: return $this->mainPhase($token); break; + case self::END_PHASE : return $this->trailingEndPhase($token); break; + } + } + + private function initPhase($token) { + /* Initially, the tree construction stage must handle each token + emitted from the tokenisation stage as follows: */ + + /* A DOCTYPE token that is marked as being in error + A comment token + A start tag token + An end tag token + A character token that is not one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE + An end-of-file token */ + if((isset($token['error']) && $token['error']) || + $token['type'] === HTML5::COMMENT || + $token['type'] === HTML5::STARTTAG || + $token['type'] === HTML5::ENDTAG || + $token['type'] === HTML5::EOF || + ($token['type'] === HTML5::CHARACTR && isset($token['data']) && + !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))) { + /* This specification does not define how to handle this case. In + particular, user agents may ignore the entirety of this specification + altogether for such documents, and instead invoke special parse modes + with a greater emphasis on backwards compatibility. */ + + $this->phase = self::ROOT_PHASE; + return $this->rootElementPhase($token); + + /* A DOCTYPE token marked as being correct */ + } elseif(isset($token['error']) && !$token['error']) { + /* Append a DocumentType node to the Document node, with the name + attribute set to the name given in the DOCTYPE token (which will be + "HTML"), and the other attributes specific to DocumentType objects + set to null, empty lists, or the empty string as appropriate. */ + $doctype = new DOMDocumentType(null, null, 'HTML'); + + /* Then, switch to the root element phase of the tree construction + stage. */ + $this->phase = self::ROOT_PHASE; + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif(isset($token['data']) && preg_match('/^[\t\n\x0b\x0c ]+$/', + $token['data'])) { + /* Append that character to the Document node. */ + $text = $this->dom->createTextNode($token['data']); + $this->dom->appendChild($text); + } + } + + private function rootElementPhase($token) { + /* After the initial phase, as each token is emitted from the tokenisation + stage, it must be processed as described in this section. */ + + /* A DOCTYPE token */ + if($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the Document object with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->dom->appendChild($comment); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append that character to the Document node. */ + $text = $this->dom->createTextNode($token['data']); + $this->dom->appendChild($text); + + /* A character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED + (FF), or U+0020 SPACE + A start tag token + An end tag token + An end-of-file token */ + } elseif(($token['type'] === HTML5::CHARACTR && + !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || + $token['type'] === HTML5::STARTTAG || + $token['type'] === HTML5::ENDTAG || + $token['type'] === HTML5::EOF) { + /* Create an HTMLElement node with the tag name html, in the HTML + namespace. Append it to the Document object. Switch to the main + phase and reprocess the current token. */ + $html = $this->dom->createElement('html'); + $this->dom->appendChild($html); + $this->stack[] = $html; + + $this->phase = self::MAIN_PHASE; + return $this->mainPhase($token); + } + } + + private function mainPhase($token) { + /* Tokens in the main phase must be handled as follows: */ + + /* A DOCTYPE token */ + if($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A start tag token with the tag name "html" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') { + /* If this start tag token was not the first start tag token, then + it is a parse error. */ + + /* For each attribute on the token, check to see if the attribute + is already present on the top element of the stack of open elements. + If it is not, add the attribute and its corresponding value to that + element. */ + foreach($token['attr'] as $attr) { + if(!$this->stack[0]->hasAttribute($attr['name'])) { + $this->stack[0]->setAttribute($attr['name'], $attr['value']); + } + } + + /* An end-of-file token */ + } elseif($token['type'] === HTML5::EOF) { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Anything else. */ + } else { + /* Depends on the insertion mode: */ + switch($this->mode) { + case self::BEFOR_HEAD: return $this->beforeHead($token); break; + case self::IN_HEAD: return $this->inHead($token); break; + case self::AFTER_HEAD: return $this->afterHead($token); break; + case self::IN_BODY: return $this->inBody($token); break; + case self::IN_TABLE: return $this->inTable($token); break; + case self::IN_CAPTION: return $this->inCaption($token); break; + case self::IN_CGROUP: return $this->inColumnGroup($token); break; + case self::IN_TBODY: return $this->inTableBody($token); break; + case self::IN_ROW: return $this->inRow($token); break; + case self::IN_CELL: return $this->inCell($token); break; + case self::IN_SELECT: return $this->inSelect($token); break; + case self::AFTER_BODY: return $this->afterBody($token); break; + case self::IN_FRAME: return $this->inFrameset($token); break; + case self::AFTR_FRAME: return $this->afterFrameset($token); break; + case self::END_PHASE: return $this->trailingEndPhase($token); break; + } + } + } + + private function beforeHead($token) { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token with the tag name "head" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') { + /* Create an element for the token, append the new element to the + current node and push it onto the stack of open elements. */ + $element = $this->insertElement($token); + + /* Set the head element pointer to this new element node. */ + $this->head_pointer = $element; + + /* Change the insertion mode to "in head". */ + $this->mode = self::IN_HEAD; + + /* A start tag token whose tag name is one of: "base", "link", "meta", + "script", "style", "title". Or an end tag with the tag name "html". + Or a character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. Or any other start tag token */ + } elseif($token['type'] === HTML5::STARTTAG || + ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') || + ($token['type'] === HTML5::CHARACTR && !preg_match('/^[\t\n\x0b\x0c ]$/', + $token['data']))) { + /* Act as if a start tag token with the tag name "head" and no + attributes had been seen, then reprocess the current token. */ + $this->beforeHead(array( + 'name' => 'head', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + return $this->inHead($token); + + /* Any other end tag */ + } elseif($token['type'] === HTML5::ENDTAG) { + /* Parse error. Ignore the token. */ + } + } + + private function inHead($token) { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. + + THIS DIFFERS FROM THE SPEC: If the current node is either a title, style + or script element, append the character to the current node regardless + of its content. */ + if(($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || ( + $token['type'] === HTML5::CHARACTR && in_array(end($this->stack)->nodeName, + array('title', 'style', 'script')))) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + } elseif($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('title', 'style', 'script'))) { + array_pop($this->stack); + return HTML5::PCDATA; + + /* A start tag with the tag name "title" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + } else { + $element = $this->insertElement($token); + } + + /* Switch the tokeniser's content model flag to the RCDATA state. */ + return HTML5::RCDATA; + + /* A start tag with the tag name "style" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + } else { + $this->insertElement($token); + } + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + + /* A start tag with the tag name "script" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') { + /* Create an element for the token. */ + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + + /* A start tag with the tag name "base", "link", or "meta" */ + } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('base', 'link', 'meta'))) { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + array_pop($this->stack); + + } else { + $this->insertElement($token); + } + + /* An end tag with the tag name "head" */ + } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') { + /* If the current node is a head element, pop the current node off + the stack of open elements. */ + if($this->head_pointer->isSameNode(end($this->stack))) { + array_pop($this->stack); + + /* Otherwise, this is a parse error. */ + } else { + // k + } + + /* Change the insertion mode to "after head". */ + $this->mode = self::AFTER_HEAD; + + /* A start tag with the tag name "head" or an end tag except "html". */ + } elseif(($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') || + ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* If the current node is a head element, act as if an end tag + token with the tag name "head" had been seen. */ + if($this->head_pointer->isSameNode(end($this->stack))) { + $this->inHead(array( + 'name' => 'head', + 'type' => HTML5::ENDTAG + )); + + /* Otherwise, change the insertion mode to "after head". */ + } else { + $this->mode = self::AFTER_HEAD; + } + + /* Then, reprocess the current token. */ + return $this->afterHead($token); + } + } + + private function afterHead($token) { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token with the tag name "body" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') { + /* Insert a body element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in body". */ + $this->mode = self::IN_BODY; + + /* A start tag token with the tag name "frameset" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') { + /* Insert a frameset element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in frameset". */ + $this->mode = self::IN_FRAME; + + /* A start tag token whose tag name is one of: "base", "link", "meta", + "script", "style", "title" */ + } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('base', 'link', 'meta', 'script', 'style', 'title'))) { + /* Parse error. Switch the insertion mode back to "in head" and + reprocess the token. */ + $this->mode = self::IN_HEAD; + return $this->inHead($token); + + /* Anything else */ + } else { + /* Act as if a start tag token with the tag name "body" and no + attributes had been seen, and then reprocess the current token. */ + $this->afterHead(array( + 'name' => 'body', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + return $this->inBody($token); + } + } + + private function inBody($token) { + /* Handle the token as follows: */ + + switch($token['type']) { + /* A character token */ + case HTML5::CHARACTR: + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Append the token's character to the current node. */ + $this->insertText($token['data']); + break; + + /* A comment token */ + case HTML5::COMMENT: + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + break; + + case HTML5::STARTTAG: + switch($token['name']) { + /* A start tag token whose tag name is one of: "script", + "style" */ + case 'script': case 'style': + /* Process the token as if the insertion mode had been "in + head". */ + return $this->inHead($token); + break; + + /* A start tag token whose tag name is one of: "base", "link", + "meta", "title" */ + case 'base': case 'link': case 'meta': case 'title': + /* Parse error. Process the token as if the insertion mode + had been "in head". */ + return $this->inHead($token); + break; + + /* A start tag token with the tag name "body" */ + case 'body': + /* Parse error. If the second element on the stack of open + elements is not a body element, or, if the stack of open + elements has only one node on it, then ignore the token. + (innerHTML case) */ + if(count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') { + // Ignore + + /* Otherwise, for each attribute on the token, check to see + if the attribute is already present on the body element (the + second element) on the stack of open elements. If it is not, + add the attribute and its corresponding value to that + element. */ + } else { + foreach($token['attr'] as $attr) { + if(!$this->stack[1]->hasAttribute($attr['name'])) { + $this->stack[1]->setAttribute($attr['name'], $attr['value']); + } + } + } + break; + + /* A start tag whose tag name is one of: "address", + "blockquote", "center", "dir", "div", "dl", "fieldset", + "listing", "menu", "ol", "p", "ul" */ + case 'address': case 'blockquote': case 'center': case 'dir': + case 'div': case 'dl': case 'fieldset': case 'listing': + case 'menu': case 'ol': case 'p': case 'ul': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + break; + + /* A start tag whose tag name is "form" */ + case 'form': + /* If the form element pointer is not null, ignore the + token with a parse error. */ + if($this->form_pointer !== null) { + // Ignore. + + /* Otherwise: */ + } else { + /* If the stack of open elements has a p element in + scope, then act as if an end tag with the tag name p + had been seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token, and set the + form element pointer to point to the element created. */ + $element = $this->insertElement($token); + $this->form_pointer = $element; + } + break; + + /* A start tag whose tag name is "li", "dd" or "dt" */ + case 'li': case 'dd': case 'dt': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + $stack_length = count($this->stack) - 1; + + for($n = $stack_length; 0 <= $n; $n--) { + /* 1. Initialise node to be the current node (the + bottommost node of the stack). */ + $stop = false; + $node = $this->stack[$n]; + $cat = $this->getElementCategory($node->tagName); + + /* 2. If node is an li, dd or dt element, then pop all + the nodes from the current node up to node, including + node, then stop this algorithm. */ + if($token['name'] === $node->tagName || ($token['name'] !== 'li' + && ($node->tagName === 'dd' || $node->tagName === 'dt'))) { + for($x = $stack_length; $x >= $n ; $x--) { + array_pop($this->stack); + } + + break; + } + + /* 3. If node is not in the formatting category, and is + not in the phrasing category, and is not an address or + div element, then stop this algorithm. */ + if($cat !== self::FORMATTING && $cat !== self::PHRASING && + $node->tagName !== 'address' && $node->tagName !== 'div') { + break; + } + } + + /* Finally, insert an HTML element with the same tag + name as the token's. */ + $this->insertElement($token); + break; + + /* A start tag token whose tag name is "plaintext" */ + case 'plaintext': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + return HTML5::PLAINTEXT; + break; + + /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", + "h5", "h6" */ + case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + /* If the stack of open elements has in scope an element whose + tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then + this is a parse error; pop elements from the stack until an + element with one of those tag names has been popped from the + stack. */ + while($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) { + array_pop($this->stack); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + break; + + /* A start tag whose tag name is "a" */ + case 'a': + /* If the list of active formatting elements contains + an element whose tag name is "a" between the end of the + list and the last marker on the list (or the start of + the list if there is no marker on the list), then this + is a parse error; act as if an end tag with the tag name + "a" had been seen, then remove that element from the list + of active formatting elements and the stack of open + elements if the end tag didn't already remove it (it + might not have if the element is not in table scope). */ + $leng = count($this->a_formatting); + + for($n = $leng - 1; $n >= 0; $n--) { + if($this->a_formatting[$n] === self::MARKER) { + break; + + } elseif($this->a_formatting[$n]->nodeName === 'a') { + $this->emitToken(array( + 'name' => 'a', + 'type' => HTML5::ENDTAG + )); + break; + } + } + + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $el = $this->insertElement($token); + + /* Add that element to the list of active formatting + elements. */ + $this->a_formatting[] = $el; + break; + + /* A start tag whose tag name is one of: "b", "big", "em", "font", + "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ + case 'b': case 'big': case 'em': case 'font': case 'i': + case 'nobr': case 's': case 'small': case 'strike': + case 'strong': case 'tt': case 'u': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $el = $this->insertElement($token); + + /* Add that element to the list of active formatting + elements. */ + $this->a_formatting[] = $el; + break; + + /* A start tag token whose tag name is "button" */ + case 'button': + /* If the stack of open elements has a button element in scope, + then this is a parse error; act as if an end tag with the tag + name "button" had been seen, then reprocess the token. (We don't + do that. Unnecessary.) */ + if($this->elementInScope('button')) { + $this->inBody(array( + 'name' => 'button', + 'type' => HTML5::ENDTAG + )); + } + + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + break; + + /* A start tag token whose tag name is one of: "marquee", "object" */ + case 'marquee': case 'object': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + break; + + /* A start tag token whose tag name is "xmp" */ + case 'xmp': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Switch the content model flag to the CDATA state. */ + return HTML5::CDATA; + break; + + /* A start tag whose tag name is "table" */ + case 'table': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in table". */ + $this->mode = self::IN_TABLE; + break; + + /* A start tag whose tag name is one of: "area", "basefont", + "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ + case 'area': case 'basefont': case 'bgsound': case 'br': + case 'embed': case 'img': case 'param': case 'spacer': + case 'wbr': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "hr" */ + case 'hr': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if($this->elementInScope('p')) { + $this->emitToken(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "image" */ + case 'image': + /* Parse error. Change the token's tag name to "img" and + reprocess it. (Don't ask.) */ + $token['name'] = 'img'; + return $this->inBody($token); + break; + + /* A start tag whose tag name is "input" */ + case 'input': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an input element for the token. */ + $element = $this->insertElement($token, false); + + /* If the form element pointer is not null, then associate the + input element with the form element pointed to by the form + element pointer. */ + $this->form_pointer !== null + ? $this->form_pointer->appendChild($element) + : end($this->stack)->appendChild($element); + + /* Pop that input element off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "isindex" */ + case 'isindex': + /* Parse error. */ + // w/e + + /* If the form element pointer is not null, + then ignore the token. */ + if($this->form_pointer === null) { + /* Act as if a start tag token with the tag name "form" had + been seen. */ + $this->inBody(array( + 'name' => 'body', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + /* Act as if a start tag token with the tag name "hr" had + been seen. */ + $this->inBody(array( + 'name' => 'hr', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + /* Act as if a start tag token with the tag name "p" had + been seen. */ + $this->inBody(array( + 'name' => 'p', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + /* Act as if a start tag token with the tag name "label" + had been seen. */ + $this->inBody(array( + 'name' => 'label', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + /* Act as if a stream of character tokens had been seen. */ + $this->insertText('This is a searchable index. '. + 'Insert your search keywords here: '); + + /* Act as if a start tag token with the tag name "input" + had been seen, with all the attributes from the "isindex" + token, except with the "name" attribute set to the value + "isindex" (ignoring any explicit "name" attribute). */ + $attr = $token['attr']; + $attr[] = array('name' => 'name', 'value' => 'isindex'); + + $this->inBody(array( + 'name' => 'input', + 'type' => HTML5::STARTTAG, + 'attr' => $attr + )); + + /* Act as if a stream of character tokens had been seen + (see below for what they should say). */ + $this->insertText('This is a searchable index. '. + 'Insert your search keywords here: '); + + /* Act as if an end tag token with the tag name "label" + had been seen. */ + $this->inBody(array( + 'name' => 'label', + 'type' => HTML5::ENDTAG + )); + + /* Act as if an end tag token with the tag name "p" had + been seen. */ + $this->inBody(array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + )); + + /* Act as if a start tag token with the tag name "hr" had + been seen. */ + $this->inBody(array( + 'name' => 'hr', + 'type' => HTML5::ENDTAG + )); + + /* Act as if an end tag token with the tag name "form" had + been seen. */ + $this->inBody(array( + 'name' => 'form', + 'type' => HTML5::ENDTAG + )); + } + break; + + /* A start tag whose tag name is "textarea" */ + case 'textarea': + $this->insertElement($token); + + /* Switch the tokeniser's content model flag to the + RCDATA state. */ + return HTML5::RCDATA; + break; + + /* A start tag whose tag name is one of: "iframe", "noembed", + "noframes" */ + case 'iframe': case 'noembed': case 'noframes': + $this->insertElement($token); + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + break; + + /* A start tag whose tag name is "select" */ + case 'select': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in select". */ + $this->mode = self::IN_SELECT; + break; + + /* A start or end tag whose tag name is one of: "caption", "col", + "colgroup", "frame", "frameset", "head", "option", "optgroup", + "tbody", "td", "tfoot", "th", "thead", "tr". */ + case 'caption': case 'col': case 'colgroup': case 'frame': + case 'frameset': case 'head': case 'option': case 'optgroup': + case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': + case 'tr': + // Parse error. Ignore the token. + break; + + /* A start or end tag whose tag name is one of: "event-source", + "section", "nav", "article", "aside", "header", "footer", + "datagrid", "command" */ + case 'event-source': case 'section': case 'nav': case 'article': + case 'aside': case 'header': case 'footer': case 'datagrid': + case 'command': + // Work in progress! + break; + + /* A start tag token not covered by the previous entries */ + default: + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + $this->insertElement($token, true, true); + break; + } + break; + + case HTML5::ENDTAG: + switch($token['name']) { + /* An end tag with the tag name "body" */ + case 'body': + /* If the second element in the stack of open elements is + not a body element, this is a parse error. Ignore the token. + (innerHTML case) */ + if(count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') { + // Ignore. + + /* If the current node is not the body element, then this + is a parse error. */ + } elseif(end($this->stack)->nodeName !== 'body') { + // Parse error. + } + + /* Change the insertion mode to "after body". */ + $this->mode = self::AFTER_BODY; + break; + + /* An end tag with the tag name "html" */ + case 'html': + /* Act as if an end tag with tag name "body" had been seen, + then, if that token wasn't ignored, reprocess the current + token. */ + $this->inBody(array( + 'name' => 'body', + 'type' => HTML5::ENDTAG + )); + + return $this->afterBody($token); + break; + + /* An end tag whose tag name is one of: "address", "blockquote", + "center", "dir", "div", "dl", "fieldset", "listing", "menu", + "ol", "pre", "ul" */ + case 'address': case 'blockquote': case 'center': case 'dir': + case 'div': case 'dl': case 'fieldset': case 'listing': + case 'menu': case 'ol': case 'pre': case 'ul': + /* If the stack of open elements has an element in scope + with the same tag name as that of the token, then generate + implied end tags. */ + if($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with + the same tag name as that of the token, then this + is a parse error. */ + // w/e + + /* If the stack of open elements has an element in + scope with the same tag name as that of the token, + then pop elements from this stack until an element + with that tag name has been popped from the stack. */ + for($n = count($this->stack) - 1; $n >= 0; $n--) { + if($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is "form" */ + case 'form': + /* If the stack of open elements has an element in scope + with the same tag name as that of the token, then generate + implied end tags. */ + if($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + } + + if(end($this->stack)->nodeName !== $token['name']) { + /* Now, if the current node is not an element with the + same tag name as that of the token, then this is a parse + error. */ + // w/e + + } else { + /* Otherwise, if the current node is an element with + the same tag name as that of the token pop that element + from the stack. */ + array_pop($this->stack); + } + + /* In any case, set the form element pointer to null. */ + $this->form_pointer = null; + break; + + /* An end tag whose tag name is "p" */ + case 'p': + /* If the stack of open elements has a p element in scope, + then generate implied end tags, except for p elements. */ + if($this->elementInScope('p')) { + $this->generateImpliedEndTags(array('p')); + + /* If the current node is not a p element, then this is + a parse error. */ + // k + + /* If the stack of open elements has a p element in + scope, then pop elements from this stack until the stack + no longer has a p element in scope. */ + for($n = count($this->stack) - 1; $n >= 0; $n--) { + if($this->elementInScope('p')) { + array_pop($this->stack); + + } else { + break; + } + } + } + break; + + /* An end tag whose tag name is "dd", "dt", or "li" */ + case 'dd': case 'dt': case 'li': + /* If the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then + generate implied end tags, except for elements with the + same tag name as the token. */ + if($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(array($token['name'])); + + /* If the current node is not an element with the same + tag name as the token, then this is a parse error. */ + // w/e + + /* If the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then + pop elements from this stack until an element with that + tag name has been popped from the stack. */ + for($n = count($this->stack) - 1; $n >= 0; $n--) { + if($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", + "h5", "h6" */ + case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': + $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); + + /* If the stack of open elements has in scope an element whose + tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then + generate implied end tags. */ + if($this->elementInScope($elements)) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with the same + tag name as that of the token, then this is a parse error. */ + // w/e + + /* If the stack of open elements has in scope an element + whose tag name is one of "h1", "h2", "h3", "h4", "h5", or + "h6", then pop elements from the stack until an element + with one of those tag names has been popped from the stack. */ + while($this->elementInScope($elements)) { + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is one of: "a", "b", "big", "em", + "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ + case 'a': case 'b': case 'big': case 'em': case 'font': + case 'i': case 'nobr': case 's': case 'small': case 'strike': + case 'strong': case 'tt': case 'u': + /* 1. Let the formatting element be the last element in + the list of active formatting elements that: + * is between the end of the list and the last scope + marker in the list, if any, or the start of the list + otherwise, and + * has the same tag name as the token. + */ + while(true) { + for($a = count($this->a_formatting) - 1; $a >= 0; $a--) { + if($this->a_formatting[$a] === self::MARKER) { + break; + + } elseif($this->a_formatting[$a]->tagName === $token['name']) { + $formatting_element = $this->a_formatting[$a]; + $in_stack = in_array($formatting_element, $this->stack, true); + $fe_af_pos = $a; + break; + } + } + + /* If there is no such node, or, if that node is + also in the stack of open elements but the element + is not in scope, then this is a parse error. Abort + these steps. The token is ignored. */ + if(!isset($formatting_element) || ($in_stack && + !$this->elementInScope($token['name']))) { + break; + + /* Otherwise, if there is such a node, but that node + is not in the stack of open elements, then this is a + parse error; remove the element from the list, and + abort these steps. */ + } elseif(isset($formatting_element) && !$in_stack) { + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + break; + } + + /* 2. Let the furthest block be the topmost node in the + stack of open elements that is lower in the stack + than the formatting element, and is not an element in + the phrasing or formatting categories. There might + not be one. */ + $fe_s_pos = array_search($formatting_element, $this->stack, true); + $length = count($this->stack); + + for($s = $fe_s_pos + 1; $s < $length; $s++) { + $category = $this->getElementCategory($this->stack[$s]->nodeName); + + if($category !== self::PHRASING && $category !== self::FORMATTING) { + $furthest_block = $this->stack[$s]; + } + } + + /* 3. If there is no furthest block, then the UA must + skip the subsequent steps and instead just pop all + the nodes from the bottom of the stack of open + elements, from the current node up to the formatting + element, and remove the formatting element from the + list of active formatting elements. */ + if(!isset($furthest_block)) { + for($n = $length - 1; $n >= $fe_s_pos; $n--) { + array_pop($this->stack); + } + + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + break; + } + + /* 4. Let the common ancestor be the element + immediately above the formatting element in the stack + of open elements. */ + $common_ancestor = $this->stack[$fe_s_pos - 1]; + + /* 5. If the furthest block has a parent node, then + remove the furthest block from its parent node. */ + if($furthest_block->parentNode !== null) { + $furthest_block->parentNode->removeChild($furthest_block); + } + + /* 6. Let a bookmark note the position of the + formatting element in the list of active formatting + elements relative to the elements on either side + of it in the list. */ + $bookmark = $fe_af_pos; + + /* 7. Let node and last node be the furthest block. + Follow these steps: */ + $node = $furthest_block; + $last_node = $furthest_block; + + while(true) { + for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { + /* 7.1 Let node be the element immediately + prior to node in the stack of open elements. */ + $node = $this->stack[$n]; + + /* 7.2 If node is not in the list of active + formatting elements, then remove node from + the stack of open elements and then go back + to step 1. */ + if(!in_array($node, $this->a_formatting, true)) { + unset($this->stack[$n]); + $this->stack = array_merge($this->stack); + + } else { + break; + } + } + + /* 7.3 Otherwise, if node is the formatting + element, then go to the next step in the overall + algorithm. */ + if($node === $formatting_element) { + break; + + /* 7.4 Otherwise, if last node is the furthest + block, then move the aforementioned bookmark to + be immediately after the node in the list of + active formatting elements. */ + } elseif($last_node === $furthest_block) { + $bookmark = array_search($node, $this->a_formatting, true) + 1; + } + + /* 7.5 If node has any children, perform a + shallow clone of node, replace the entry for + node in the list of active formatting elements + with an entry for the clone, replace the entry + for node in the stack of open elements with an + entry for the clone, and let node be the clone. */ + if($node->hasChildNodes()) { + $clone = $node->cloneNode(); + $s_pos = array_search($node, $this->stack, true); + $a_pos = array_search($node, $this->a_formatting, true); + + $this->stack[$s_pos] = $clone; + $this->a_formatting[$a_pos] = $clone; + $node = $clone; + } + + /* 7.6 Insert last node into node, first removing + it from its previous parent node if any. */ + if($last_node->parentNode !== null) { + $last_node->parentNode->removeChild($last_node); + } + + $node->appendChild($last_node); + + /* 7.7 Let last node be node. */ + $last_node = $node; + } + + /* 8. Insert whatever last node ended up being in + the previous step into the common ancestor node, + first removing it from its previous parent node if + any. */ + if($last_node->parentNode !== null) { + $last_node->parentNode->removeChild($last_node); + } + + $common_ancestor->appendChild($last_node); + + /* 9. Perform a shallow clone of the formatting + element. */ + $clone = $formatting_element->cloneNode(); + + /* 10. Take all of the child nodes of the furthest + block and append them to the clone created in the + last step. */ + while($furthest_block->hasChildNodes()) { + $child = $furthest_block->firstChild; + $furthest_block->removeChild($child); + $clone->appendChild($child); + } + + /* 11. Append that clone to the furthest block. */ + $furthest_block->appendChild($clone); + + /* 12. Remove the formatting element from the list + of active formatting elements, and insert the clone + into the list of active formatting elements at the + position of the aforementioned bookmark. */ + $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + + $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); + $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting)); + $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); + + /* 13. Remove the formatting element from the stack + of open elements, and insert the clone into the stack + of open elements immediately after (i.e. in a more + deeply nested position than) the position of the + furthest block in that stack. */ + $fe_s_pos = array_search($formatting_element, $this->stack, true); + $fb_s_pos = array_search($furthest_block, $this->stack, true); + unset($this->stack[$fe_s_pos]); + + $s_part1 = array_slice($this->stack, 0, $fb_s_pos); + $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack)); + $this->stack = array_merge($s_part1, array($clone), $s_part2); + + /* 14. Jump back to step 1 in this series of steps. */ + unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); + } + break; + + /* An end tag token whose tag name is one of: "button", + "marquee", "object" */ + case 'button': case 'marquee': case 'object': + /* If the stack of open elements has an element in scope whose + tag name matches the tag name of the token, then generate implied + tags. */ + if($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with the same + tag name as the token, then this is a parse error. */ + // k + + /* Now, if the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then pop + elements from the stack until that element has been popped from + the stack, and clear the list of active formatting elements up + to the last marker. */ + for($n = count($this->stack) - 1; $n >= 0; $n--) { + if($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + + $marker = end(array_keys($this->a_formatting, self::MARKER, true)); + + for($n = count($this->a_formatting) - 1; $n > $marker; $n--) { + array_pop($this->a_formatting); + } + } + break; + + /* Or an end tag whose tag name is one of: "area", "basefont", + "bgsound", "br", "embed", "hr", "iframe", "image", "img", + "input", "isindex", "noembed", "noframes", "param", "select", + "spacer", "table", "textarea", "wbr" */ + case 'area': case 'basefont': case 'bgsound': case 'br': + case 'embed': case 'hr': case 'iframe': case 'image': + case 'img': case 'input': case 'isindex': case 'noembed': + case 'noframes': case 'param': case 'select': case 'spacer': + case 'table': case 'textarea': case 'wbr': + // Parse error. Ignore the token. + break; + + /* An end tag token not covered by the previous entries */ + default: + for($n = count($this->stack) - 1; $n >= 0; $n--) { + /* Initialise node to be the current node (the bottommost + node of the stack). */ + $node = end($this->stack); + + /* If node has the same tag name as the end tag token, + then: */ + if($token['name'] === $node->nodeName) { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* If the tag name of the end tag token does not + match the tag name of the current node, this is a + parse error. */ + // k + + /* Pop all the nodes from the current node up to + node, including node, then stop this algorithm. */ + for($x = count($this->stack) - $n; $x >= $n; $x--) { + array_pop($this->stack); + } + + } else { + $category = $this->getElementCategory($node); + + if($category !== self::SPECIAL && $category !== self::SCOPING) { + /* Otherwise, if node is in neither the formatting + category nor the phrasing category, then this is a + parse error. Stop this algorithm. The end tag token + is ignored. */ + return false; + } + } + } + break; + } + break; + } + } + + private function inTable($token) { + $clear = array('html', 'table'); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append the character to the current node. */ + $text = $this->dom->createTextNode($token['data']); + end($this->stack)->appendChild($text); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + end($this->stack)->appendChild($comment); + + /* A start tag whose tag name is "caption" */ + } elseif($token['type'] === HTML5::STARTTAG && + $token['name'] === 'caption') { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + + /* Insert an HTML element for the token, then switch the + insertion mode to "in caption". */ + $this->insertElement($token); + $this->mode = self::IN_CAPTION; + + /* A start tag whose tag name is "colgroup" */ + } elseif($token['type'] === HTML5::STARTTAG && + $token['name'] === 'colgroup') { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the + insertion mode to "in column group". */ + $this->insertElement($token); + $this->mode = self::IN_CGROUP; + + /* A start tag whose tag name is "col" */ + } elseif($token['type'] === HTML5::STARTTAG && + $token['name'] === 'col') { + $this->inTable(array( + 'name' => 'colgroup', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + $this->inColumnGroup($token); + + /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('tbody', 'tfoot', 'thead'))) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the insertion + mode to "in table body". */ + $this->insertElement($token); + $this->mode = self::IN_TBODY; + + /* A start tag whose tag name is one of: "td", "th", "tr" */ + } elseif($token['type'] === HTML5::STARTTAG && + in_array($token['name'], array('td', 'th', 'tr'))) { + /* Act as if a start tag token with the tag name "tbody" had been + seen, then reprocess the current token. */ + $this->inTable(array( + 'name' => 'tbody', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + return $this->inTableBody($token); + + /* A start tag whose tag name is "table" */ + } elseif($token['type'] === HTML5::STARTTAG && + $token['name'] === 'table') { + /* Parse error. Act as if an end tag token with the tag name "table" + had been seen, then, if that token wasn't ignored, reprocess the + current token. */ + $this->inTable(array( + 'name' => 'table', + 'type' => HTML5::ENDTAG + )); + + return $this->mainPhase($token); + + /* An end tag whose tag name is "table" */ + } elseif($token['type'] === HTML5::ENDTAG && + $token['name'] === 'table') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if(!$this->elementInScope($token['name'], true)) { + return false; + + /* Otherwise: */ + } else { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Now, if the current node is not a table element, then this + is a parse error. */ + // w/e + + /* Pop elements from this stack until a table element has been + popped from the stack. */ + while(true) { + $current = end($this->stack)->nodeName; + array_pop($this->stack); + + if($current === 'table') { + break; + } + } + + /* Reset the insertion mode appropriately. */ + $this->resetInsertionMode(); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td', + 'tfoot', 'th', 'thead', 'tr'))) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* Parse error. Process the token as if the insertion mode was "in + body", with the following exception: */ + + /* If the current node is a table, tbody, tfoot, thead, or tr + element, then, whenever a node would be inserted into the current + node, it must instead be inserted into the foster parent element. */ + if(in_array(end($this->stack)->nodeName, + array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { + /* The foster parent element is the parent element of the last + table element in the stack of open elements, if there is a + table element and it has such a parent element. If there is no + table element in the stack of open elements (innerHTML case), + then the foster parent element is the first element in the + stack of open elements (the html element). Otherwise, if there + is a table element in the stack of open elements, but the last + table element in the stack of open elements has no parent, or + its parent node is not an element, then the foster parent + element is the element before the last table element in the + stack of open elements. */ + for($n = count($this->stack) - 1; $n >= 0; $n--) { + if($this->stack[$n]->nodeName === 'table') { + $table = $this->stack[$n]; + break; + } + } + + if(isset($table) && $table->parentNode !== null) { + $this->foster_parent = $table->parentNode; + + } elseif(!isset($table)) { + $this->foster_parent = $this->stack[0]; + + } elseif(isset($table) && ($table->parentNode === null || + $table->parentNode->nodeType !== XML_ELEMENT_NODE)) { + $this->foster_parent = $this->stack[$n - 1]; + } + } + + $this->inBody($token); + } + } + + private function inCaption($token) { + /* An end tag whose tag name is "caption" */ + if($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if(!$this->elementInScope($token['name'], true)) { + // Ignore + + /* Otherwise: */ + } else { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Now, if the current node is not a caption element, then this + is a parse error. */ + // w/e + + /* Pop elements from this stack until a caption element has + been popped from the stack. */ + while(true) { + $node = end($this->stack)->nodeName; + array_pop($this->stack); + + if($node === 'caption') { + break; + } + } + + /* Clear the list of active formatting elements up to the last + marker. */ + $this->clearTheActiveFormattingElementsUpToTheLastMarker(); + + /* Switch the insertion mode to "in table". */ + $this->mode = self::IN_TABLE; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag + name is "table" */ + } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', + 'thead', 'tr'))) || ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'table')) { + /* Parse error. Act as if an end tag with the tag name "caption" + had been seen, then, if that token wasn't ignored, reprocess the + current token. */ + $this->inCaption(array( + 'name' => 'caption', + 'type' => HTML5::ENDTAG + )); + + return $this->inTable($token); + + /* An end tag whose tag name is one of: "body", "col", "colgroup", + "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], + array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th', + 'thead', 'tr'))) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in body". */ + $this->inBody($token); + } + } + + private function inColumnGroup($token) { + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append the character to the current node. */ + $text = $this->dom->createTextNode($token['data']); + end($this->stack)->appendChild($text); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + end($this->stack)->appendChild($comment); + + /* A start tag whose tag name is "col" */ + } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') { + /* Insert a col element for the token. Immediately pop the current + node off the stack of open elements. */ + $this->insertElement($token); + array_pop($this->stack); + + /* An end tag whose tag name is "colgroup" */ + } elseif($token['type'] === HTML5::ENDTAG && + $token['name'] === 'colgroup') { + /* If the current node is the root html element, then this is a + parse error, ignore the token. (innerHTML case) */ + if(end($this->stack)->nodeName === 'html') { + // Ignore + + /* Otherwise, pop the current node (which will be a colgroup + element) from the stack of open elements. Switch the insertion + mode to "in table". */ + } else { + array_pop($this->stack); + $this->mode = self::IN_TABLE; + } + + /* An end tag whose tag name is "col" */ + } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Act as if an end tag with the tag name "colgroup" had been seen, + and then, if that token wasn't ignored, reprocess the current token. */ + $this->inColumnGroup(array( + 'name' => 'colgroup', + 'type' => HTML5::ENDTAG + )); + + return $this->inTable($token); + } + } + + private function inTableBody($token) { + $clear = array('tbody', 'tfoot', 'thead', 'html'); + + /* A start tag whose tag name is "tr" */ + if($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Insert a tr element for the token, then switch the insertion + mode to "in row". */ + $this->insertElement($token); + $this->mode = self::IN_ROW; + + /* A start tag whose tag name is one of: "th", "td" */ + } elseif($token['type'] === HTML5::STARTTAG && + ($token['name'] === 'th' || $token['name'] === 'td')) { + /* Parse error. Act as if a start tag with the tag name "tr" had + been seen, then reprocess the current token. */ + $this->inTableBody(array( + 'name' => 'tr', + 'type' => HTML5::STARTTAG, + 'attr' => array() + )); + + return $this->inRow($token); + + /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. */ + if(!$this->elementInScope($token['name'], true)) { + // Ignore + + /* Otherwise: */ + } else { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Pop the current node from the stack of open elements. Switch + the insertion mode to "in table". */ + array_pop($this->stack); + $this->mode = self::IN_TABLE; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ + } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead'))) || + ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')) { + /* If the stack of open elements does not have a tbody, thead, or + tfoot element in table scope, this is a parse error. Ignore the + token. (innerHTML case) */ + if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Act as if an end tag with the same tag name as the current + node ("tbody", "tfoot", or "thead") had been seen, then + reprocess the current token. */ + $this->inTableBody(array( + 'name' => end($this->stack)->nodeName, + 'type' => HTML5::ENDTAG + )); + + return $this->mainPhase($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "td", "th", "tr" */ + } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in table". */ + $this->inTable($token); + } + } + + private function inRow($token) { + $clear = array('tr', 'html'); + + /* A start tag whose tag name is one of: "th", "td" */ + if($token['type'] === HTML5::STARTTAG && + ($token['name'] === 'th' || $token['name'] === 'td')) { + /* Clear the stack back to a table row context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the insertion + mode to "in cell". */ + $this->insertElement($token); + $this->mode = self::IN_CELL; + + /* Insert a marker at the end of the list of active formatting + elements. */ + $this->a_formatting[] = self::MARKER; + + /* An end tag whose tag name is "tr" */ + } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if(!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Clear the stack back to a table row context. */ + $this->clearStackToTableContext($clear); + + /* Pop the current node (which will be a tr element) from the + stack of open elements. Switch the insertion mode to "in table + body". */ + array_pop($this->stack); + $this->mode = self::IN_TBODY; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ + } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) { + /* Act as if an end tag with the tag name "tr" had been seen, then, + if that token wasn't ignored, reprocess the current token. */ + $this->inRow(array( + 'name' => 'tr', + 'type' => HTML5::ENDTAG + )); + + return $this->inCell($token); + + /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. */ + if(!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Otherwise, act as if an end tag with the tag name "tr" had + been seen, then reprocess the current token. */ + $this->inRow(array( + 'name' => 'tr', + 'type' => HTML5::ENDTAG + )); + + return $this->inCell($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "td", "th" */ + } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in table". */ + $this->inTable($token); + } + } + + private function inCell($token) { + /* An end tag whose tag name is one of: "td", "th" */ + if($token['type'] === HTML5::ENDTAG && + ($token['name'] === 'td' || $token['name'] === 'th')) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as that of the token, then this is a + parse error and the token must be ignored. */ + if(!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Generate implied end tags, except for elements with the same + tag name as the token. */ + $this->generateImpliedEndTags(array($token['name'])); + + /* Now, if the current node is not an element with the same tag + name as the token, then this is a parse error. */ + // k + + /* Pop elements from this stack until an element with the same + tag name as the token has been popped from the stack. */ + while(true) { + $node = end($this->stack)->nodeName; + array_pop($this->stack); + + if($node === $token['name']) { + break; + } + } + + /* Clear the list of active formatting elements up to the last + marker. */ + $this->clearTheActiveFormattingElementsUpToTheLastMarker(); + + /* Switch the insertion mode to "in row". (The current node + will be a tr element at this point.) */ + $this->mode = self::IN_ROW; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', + 'thead', 'tr'))) { + /* If the stack of open elements does not have a td or th element + in table scope, then this is a parse error; ignore the token. + (innerHTML case) */ + if(!$this->elementInScope(array('td', 'th'), true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', + 'thead', 'tr'))) { + /* If the stack of open elements does not have a td or th element + in table scope, then this is a parse error; ignore the token. + (innerHTML case) */ + if(!$this->elementInScope(array('td', 'th'), true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html" */ + } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], + array('body', 'caption', 'col', 'colgroup', 'html'))) { + /* Parse error. Ignore the token. */ + + /* An end tag whose tag name is one of: "table", "tbody", "tfoot", + "thead", "tr" */ + } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], + array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as that of the token (which can only + happen for "tbody", "tfoot" and "thead", or, in the innerHTML case), + then this is a parse error and the token must be ignored. */ + if(!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in body". */ + $this->inBody($token); + } + } + + private function inSelect($token) { + /* Handle the token as follows: */ + + /* A character token */ + if($token['type'] === HTML5::CHARACTR) { + /* Append the token's character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token whose tag name is "option" */ + } elseif($token['type'] === HTML5::STARTTAG && + $token['name'] === 'option') { + /* If the current node is an option element, act as if an end tag + with the tag name "option" had been seen. */ + if(end($this->stack)->nodeName === 'option') { + $this->inSelect(array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* A start tag token whose tag name is "optgroup" */ + } elseif($token['type'] === HTML5::STARTTAG && + $token['name'] === 'optgroup') { + /* If the current node is an option element, act as if an end tag + with the tag name "option" had been seen. */ + if(end($this->stack)->nodeName === 'option') { + $this->inSelect(array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + )); + } + + /* If the current node is an optgroup element, act as if an end tag + with the tag name "optgroup" had been seen. */ + if(end($this->stack)->nodeName === 'optgroup') { + $this->inSelect(array( + 'name' => 'optgroup', + 'type' => HTML5::ENDTAG + )); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* An end tag token whose tag name is "optgroup" */ + } elseif($token['type'] === HTML5::ENDTAG && + $token['name'] === 'optgroup') { + /* First, if the current node is an option element, and the node + immediately before it in the stack of open elements is an optgroup + element, then act as if an end tag with the tag name "option" had + been seen. */ + $elements_in_stack = count($this->stack); + + if($this->stack[$elements_in_stack - 1]->nodeName === 'option' && + $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup') { + $this->inSelect(array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + )); + } + + /* If the current node is an optgroup element, then pop that node + from the stack of open elements. Otherwise, this is a parse error, + ignore the token. */ + if($this->stack[$elements_in_stack - 1] === 'optgroup') { + array_pop($this->stack); + } + + /* An end tag token whose tag name is "option" */ + } elseif($token['type'] === HTML5::ENDTAG && + $token['name'] === 'option') { + /* If the current node is an option element, then pop that node + from the stack of open elements. Otherwise, this is a parse error, + ignore the token. */ + if(end($this->stack)->nodeName === 'option') { + array_pop($this->stack); + } + + /* An end tag whose tag name is "select" */ + } elseif($token['type'] === HTML5::ENDTAG && + $token['name'] === 'select') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if(!$this->elementInScope($token['name'], true)) { + // w/e + + /* Otherwise: */ + } else { + /* Pop elements from the stack of open elements until a select + element has been popped from the stack. */ + while(true) { + $current = end($this->stack)->nodeName; + array_pop($this->stack); + + if($current === 'select') { + break; + } + } + + /* Reset the insertion mode appropriately. */ + $this->resetInsertionMode(); + } + + /* A start tag whose tag name is "select" */ + } elseif($token['name'] === 'select' && + $token['type'] === HTML5::STARTTAG) { + /* Parse error. Act as if the token had been an end tag with the + tag name "select" instead. */ + $this->inSelect(array( + 'name' => 'select', + 'type' => HTML5::ENDTAG + )); + + /* An end tag whose tag name is one of: "caption", "table", "tbody", + "tfoot", "thead", "tr", "td", "th" */ + } elseif(in_array($token['name'], array('caption', 'table', 'tbody', + 'tfoot', 'thead', 'tr', 'td', 'th')) && $token['type'] === HTML5::ENDTAG) { + /* Parse error. */ + // w/e + + /* If the stack of open elements has an element in table scope with + the same tag name as that of the token, then act as if an end tag + with the tag name "select" had been seen, and reprocess the token. + Otherwise, ignore the token. */ + if($this->elementInScope($token['name'], true)) { + $this->inSelect(array( + 'name' => 'select', + 'type' => HTML5::ENDTAG + )); + + $this->mainPhase($token); + } + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function afterBody($token) { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Process the token as it would be processed if the insertion mode + was "in body". */ + $this->inBody($token); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the first element in the stack of open + elements (the html element), with the data attribute set to the + data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->stack[0]->appendChild($comment); + + /* An end tag with the tag name "html" */ + } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') { + /* If the parser was originally created in order to handle the + setting of an element's innerHTML attribute, this is a parse error; + ignore the token. (The element will be an html element in this + case.) (innerHTML case) */ + + /* Otherwise, switch to the trailing end phase. */ + $this->phase = self::END_PHASE; + + /* Anything else */ + } else { + /* Parse error. Set the insertion mode to "in body" and reprocess + the token. */ + $this->mode = self::IN_BODY; + return $this->inBody($token); + } + } + + private function inFrameset($token) { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag with the tag name "frameset" */ + } elseif($token['name'] === 'frameset' && + $token['type'] === HTML5::STARTTAG) { + $this->insertElement($token); + + /* An end tag with the tag name "frameset" */ + } elseif($token['name'] === 'frameset' && + $token['type'] === HTML5::ENDTAG) { + /* If the current node is the root html element, then this is a + parse error; ignore the token. (innerHTML case) */ + if(end($this->stack)->nodeName === 'html') { + // Ignore + + } else { + /* Otherwise, pop the current node from the stack of open + elements. */ + array_pop($this->stack); + + /* If the parser was not originally created in order to handle + the setting of an element's innerHTML attribute (innerHTML case), + and the current node is no longer a frameset element, then change + the insertion mode to "after frameset". */ + $this->mode = self::AFTR_FRAME; + } + + /* A start tag with the tag name "frame" */ + } elseif($token['name'] === 'frame' && + $token['type'] === HTML5::STARTTAG) { + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + + /* A start tag with the tag name "noframes" */ + } elseif($token['name'] === 'noframes' && + $token['type'] === HTML5::STARTTAG) { + /* Process the token as if the insertion mode had been "in body". */ + $this->inBody($token); + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function afterFrameset($token) { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ + if($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* An end tag with the tag name "html" */ + } elseif($token['name'] === 'html' && + $token['type'] === HTML5::ENDTAG) { + /* Switch to the trailing end phase. */ + $this->phase = self::END_PHASE; + + /* A start tag with the tag name "noframes" */ + } elseif($token['name'] === 'noframes' && + $token['type'] === HTML5::STARTTAG) { + /* Process the token as if the insertion mode had been "in body". */ + $this->inBody($token); + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function trailingEndPhase($token) { + /* After the main phase, as each token is emitted from the tokenisation + stage, it must be processed as described in this section. */ + + /* A DOCTYPE token */ + if($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A comment token */ + } elseif($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the Document object with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->dom->appendChild($comment); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { + /* Process the token as it would be processed in the main phase. */ + $this->mainPhase($token); + + /* A character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. Or a start tag token. Or an end tag token. */ + } elseif(($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || + $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG) { + /* Parse error. Switch back to the main phase and reprocess the + token. */ + $this->phase = self::MAIN_PHASE; + return $this->mainPhase($token); + + /* An end-of-file token */ + } elseif($token['type'] === HTML5::EOF) { + /* OMG DONE!! */ + } + } + + private function insertElement($token, $append = true, $check = false) { + // Proprietary workaround for libxml2's limitations with tag names + if ($check) { + // Slightly modified HTML5 tag-name modification, + // removing anything that's not an ASCII letter, digit, or hyphen + $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']); + // Remove leading hyphens and numbers + $token['name'] = ltrim($token['name'], '-0..9'); + // In theory, this should ever be needed, but just in case + if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice + } + + $el = $this->dom->createElement($token['name']); + + foreach($token['attr'] as $attr) { + if(!$el->hasAttribute($attr['name'])) { + $el->setAttribute($attr['name'], $attr['value']); + } + } + + $this->appendToRealParent($el); + $this->stack[] = $el; + + return $el; + } + + private function insertText($data) { + $text = $this->dom->createTextNode($data); + $this->appendToRealParent($text); + } + + private function insertComment($data) { + $comment = $this->dom->createComment($data); + $this->appendToRealParent($comment); + } + + private function appendToRealParent($node) { + if($this->foster_parent === null) { + end($this->stack)->appendChild($node); + + } elseif($this->foster_parent !== null) { + /* If the foster parent element is the parent element of the + last table element in the stack of open elements, then the new + node must be inserted immediately before the last table element + in the stack of open elements in the foster parent element; + otherwise, the new node must be appended to the foster parent + element. */ + for($n = count($this->stack) - 1; $n >= 0; $n--) { + if($this->stack[$n]->nodeName === 'table' && + $this->stack[$n]->parentNode !== null) { + $table = $this->stack[$n]; + break; + } + } + + if(isset($table) && $this->foster_parent->isSameNode($table->parentNode)) + $this->foster_parent->insertBefore($node, $table); + else + $this->foster_parent->appendChild($node); + + $this->foster_parent = null; + } + } + + private function elementInScope($el, $table = false) { + if(is_array($el)) { + foreach($el as $element) { + if($this->elementInScope($element, $table)) { + return true; + } + } + + return false; + } + + $leng = count($this->stack); + + for($n = 0; $n < $leng; $n++) { + /* 1. Initialise node to be the current node (the bottommost node of + the stack). */ + $node = $this->stack[$leng - 1 - $n]; + + if($node->tagName === $el) { + /* 2. If node is the target node, terminate in a match state. */ + return true; + + } elseif($node->tagName === 'table') { + /* 3. Otherwise, if node is a table element, terminate in a failure + state. */ + return false; + + } elseif($table === true && in_array($node->tagName, array('caption', 'td', + 'th', 'button', 'marquee', 'object'))) { + /* 4. Otherwise, if the algorithm is the "has an element in scope" + variant (rather than the "has an element in table scope" variant), + and node is one of the following, terminate in a failure state. */ + return false; + + } elseif($node === $node->ownerDocument->documentElement) { + /* 5. Otherwise, if node is an html element (root element), terminate + in a failure state. (This can only happen if the node is the topmost + node of the stack of open elements, and prevents the next step from + being invoked if there are no more elements in the stack.) */ + return false; + } + + /* Otherwise, set node to the previous entry in the stack of open + elements and return to step 2. (This will never fail, since the loop + will always terminate in the previous step if the top of the stack + is reached.) */ + } + } + + private function reconstructActiveFormattingElements() { + /* 1. If there are no entries in the list of active formatting elements, + then there is nothing to reconstruct; stop this algorithm. */ + $formatting_elements = count($this->a_formatting); + + if($formatting_elements === 0) { + return false; + } + + /* 3. Let entry be the last (most recently added) element in the list + of active formatting elements. */ + $entry = end($this->a_formatting); + + /* 2. If the last (most recently added) entry in the list of active + formatting elements is a marker, or if it is an element that is in the + stack of open elements, then there is nothing to reconstruct; stop this + algorithm. */ + if($entry === self::MARKER || in_array($entry, $this->stack, true)) { + return false; + } + + for($a = $formatting_elements - 1; $a >= 0; true) { + /* 4. If there are no entries before entry in the list of active + formatting elements, then jump to step 8. */ + if($a === 0) { + $step_seven = false; + break; + } + + /* 5. Let entry be the entry one earlier than entry in the list of + active formatting elements. */ + $a--; + $entry = $this->a_formatting[$a]; + + /* 6. If entry is neither a marker nor an element that is also in + thetack of open elements, go to step 4. */ + if($entry === self::MARKER || in_array($entry, $this->stack, true)) { + break; + } + } + + while(true) { + /* 7. Let entry be the element one later than entry in the list of + active formatting elements. */ + if(isset($step_seven) && $step_seven === true) { + $a++; + $entry = $this->a_formatting[$a]; + } + + /* 8. Perform a shallow clone of the element entry to obtain clone. */ + $clone = $entry->cloneNode(); + + /* 9. Append clone to the current node and push it onto the stack + of open elements so that it is the new current node. */ + end($this->stack)->appendChild($clone); + $this->stack[] = $clone; + + /* 10. Replace the entry for entry in the list with an entry for + clone. */ + $this->a_formatting[$a] = $clone; + + /* 11. If the entry for clone in the list of active formatting + elements is not the last entry in the list, return to step 7. */ + if(end($this->a_formatting) !== $clone) { + $step_seven = true; + } else { + break; + } + } + } + + private function clearTheActiveFormattingElementsUpToTheLastMarker() { + /* When the steps below require the UA to clear the list of active + formatting elements up to the last marker, the UA must perform the + following steps: */ + + while(true) { + /* 1. Let entry be the last (most recently added) entry in the list + of active formatting elements. */ + $entry = end($this->a_formatting); + + /* 2. Remove entry from the list of active formatting elements. */ + array_pop($this->a_formatting); + + /* 3. If entry was a marker, then stop the algorithm at this point. + The list has been cleared up to the last marker. */ + if($entry === self::MARKER) { + break; + } + } + } + + private function generateImpliedEndTags($exclude = array()) { + /* When the steps below require the UA to generate implied end tags, + then, if the current node is a dd element, a dt element, an li element, + a p element, a td element, a th element, or a tr element, the UA must + act as if an end tag with the respective tag name had been seen and + then generate implied end tags again. */ + $node = end($this->stack); + $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); + + while(in_array(end($this->stack)->nodeName, $elements)) { + array_pop($this->stack); + } + } + + private function getElementCategory($node) { + $name = $node->tagName; + if(in_array($name, $this->special)) + return self::SPECIAL; + + elseif(in_array($name, $this->scoping)) + return self::SCOPING; + + elseif(in_array($name, $this->formatting)) + return self::FORMATTING; + + else + return self::PHRASING; + } + + private function clearStackToTableContext($elements) { + /* When the steps above require the UA to clear the stack back to a + table context, it means that the UA must, while the current node is not + a table element or an html element, pop elements from the stack of open + elements. If this causes any elements to be popped from the stack, then + this is a parse error. */ + while(true) { + $node = end($this->stack)->nodeName; + + if(in_array($node, $elements)) { + break; + } else { + array_pop($this->stack); + } + } + } + + private function resetInsertionMode() { + /* 1. Let last be false. */ + $last = false; + $leng = count($this->stack); + + for($n = $leng - 1; $n >= 0; $n--) { + /* 2. Let node be the last node in the stack of open elements. */ + $node = $this->stack[$n]; + + /* 3. If node is the first node in the stack of open elements, then + set last to true. If the element whose innerHTML attribute is being + set is neither a td element nor a th element, then set node to the + element whose innerHTML attribute is being set. (innerHTML case) */ + if($this->stack[0]->isSameNode($node)) { + $last = true; + } + + /* 4. If node is a select element, then switch the insertion mode to + "in select" and abort these steps. (innerHTML case) */ + if($node->nodeName === 'select') { + $this->mode = self::IN_SELECT; + break; + + /* 5. If node is a td or th element, then switch the insertion mode + to "in cell" and abort these steps. */ + } elseif($node->nodeName === 'td' || $node->nodeName === 'th') { + $this->mode = self::IN_CELL; + break; + + /* 6. If node is a tr element, then switch the insertion mode to + "in row" and abort these steps. */ + } elseif($node->nodeName === 'tr') { + $this->mode = self::IN_ROW; + break; + + /* 7. If node is a tbody, thead, or tfoot element, then switch the + insertion mode to "in table body" and abort these steps. */ + } elseif(in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) { + $this->mode = self::IN_TBODY; + break; + + /* 8. If node is a caption element, then switch the insertion mode + to "in caption" and abort these steps. */ + } elseif($node->nodeName === 'caption') { + $this->mode = self::IN_CAPTION; + break; + + /* 9. If node is a colgroup element, then switch the insertion mode + to "in column group" and abort these steps. (innerHTML case) */ + } elseif($node->nodeName === 'colgroup') { + $this->mode = self::IN_CGROUP; + break; + + /* 10. If node is a table element, then switch the insertion mode + to "in table" and abort these steps. */ + } elseif($node->nodeName === 'table') { + $this->mode = self::IN_TABLE; + break; + + /* 11. If node is a head element, then switch the insertion mode + to "in body" ("in body"! not "in head"!) and abort these steps. + (innerHTML case) */ + } elseif($node->nodeName === 'head') { + $this->mode = self::IN_BODY; + break; + + /* 12. If node is a body element, then switch the insertion mode to + "in body" and abort these steps. */ + } elseif($node->nodeName === 'body') { + $this->mode = self::IN_BODY; + break; + + /* 13. If node is a frameset element, then switch the insertion + mode to "in frameset" and abort these steps. (innerHTML case) */ + } elseif($node->nodeName === 'frameset') { + $this->mode = self::IN_FRAME; + break; + + /* 14. If node is an html element, then: if the head element + pointer is null, switch the insertion mode to "before head", + otherwise, switch the insertion mode to "after head". In either + case, abort these steps. (innerHTML case) */ + } elseif($node->nodeName === 'html') { + $this->mode = ($this->head_pointer === null) + ? self::BEFOR_HEAD + : self::AFTER_HEAD; + + break; + + /* 15. If last is true, then set the insertion mode to "in body" + and abort these steps. (innerHTML case) */ + } elseif($last) { + $this->mode = self::IN_BODY; + break; + } + } + } + + private function closeCell() { + /* If the stack of open elements has a td or th element in table scope, + then act as if an end tag token with that tag name had been seen. */ + foreach(array('td', 'th') as $cell) { + if($this->elementInScope($cell, true)) { + $this->inCell(array( + 'name' => $cell, + 'type' => HTML5::ENDTAG + )); + + break; + } + } + } + + public function save() { + return $this->dom; + } +} +?> diff --git a/lib/php/HTMLPurifier/PercentEncoder.php b/lib/php/HTMLPurifier/PercentEncoder.php new file mode 100644 index 0000000..a43c44f --- /dev/null +++ b/lib/php/HTMLPurifier/PercentEncoder.php @@ -0,0 +1,98 @@ +<?php + +/** + * Class that handles operations involving percent-encoding in URIs. + * + * @warning + * Be careful when reusing instances of PercentEncoder. The object + * you use for normalize() SHOULD NOT be used for encode(), or + * vice-versa. + */ +class HTMLPurifier_PercentEncoder +{ + + /** + * Reserved characters to preserve when using encode(). + */ + protected $preserve = array(); + + /** + * String of characters that should be preserved while using encode(). + */ + public function __construct($preserve = false) { + // unreserved letters, ought to const-ify + for ($i = 48; $i <= 57; $i++) $this->preserve[$i] = true; // digits + for ($i = 65; $i <= 90; $i++) $this->preserve[$i] = true; // upper-case + for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case + $this->preserve[45] = true; // Dash - + $this->preserve[46] = true; // Period . + $this->preserve[95] = true; // Underscore _ + $this->preserve[126]= true; // Tilde ~ + + // extra letters not to escape + if ($preserve !== false) { + for ($i = 0, $c = strlen($preserve); $i < $c; $i++) { + $this->preserve[ord($preserve[$i])] = true; + } + } + } + + /** + * Our replacement for urlencode, it encodes all non-reserved characters, + * as well as any extra characters that were instructed to be preserved. + * @note + * Assumes that the string has already been normalized, making any + * and all percent escape sequences valid. Percents will not be + * re-escaped, regardless of their status in $preserve + * @param $string String to be encoded + * @return Encoded string. + */ + public function encode($string) { + $ret = ''; + for ($i = 0, $c = strlen($string); $i < $c; $i++) { + if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) { + $ret .= '%' . sprintf('%02X', $int); + } else { + $ret .= $string[$i]; + } + } + return $ret; + } + + /** + * Fix up percent-encoding by decoding unreserved characters and normalizing. + * @warning This function is affected by $preserve, even though the + * usual desired behavior is for this not to preserve those + * characters. Be careful when reusing instances of PercentEncoder! + * @param $string String to normalize + */ + public function normalize($string) { + if ($string == '') return ''; + $parts = explode('%', $string); + $ret = array_shift($parts); + foreach ($parts as $part) { + $length = strlen($part); + if ($length < 2) { + $ret .= '%25' . $part; + continue; + } + $encoding = substr($part, 0, 2); + $text = substr($part, 2); + if (!ctype_xdigit($encoding)) { + $ret .= '%25' . $part; + continue; + } + $int = hexdec($encoding); + if (isset($this->preserve[$int])) { + $ret .= chr($int) . $text; + continue; + } + $encoding = strtoupper($encoding); + $ret .= '%' . $encoding . $text; + } + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Printer.php b/lib/php/HTMLPurifier/Printer.php new file mode 100644 index 0000000..e7eb82e --- /dev/null +++ b/lib/php/HTMLPurifier/Printer.php @@ -0,0 +1,176 @@ +<?php + +// OUT OF DATE, NEEDS UPDATING! +// USE XMLWRITER! + +class HTMLPurifier_Printer +{ + + /** + * Instance of HTMLPurifier_Generator for HTML generation convenience funcs + */ + protected $generator; + + /** + * Instance of HTMLPurifier_Config, for easy access + */ + protected $config; + + /** + * Initialize $generator. + */ + public function __construct() { + } + + /** + * Give generator necessary configuration if possible + */ + public function prepareGenerator($config) { + $all = $config->getAll(); + $context = new HTMLPurifier_Context(); + $this->generator = new HTMLPurifier_Generator($config, $context); + } + + /** + * Main function that renders object or aspect of that object + * @note Parameters vary depending on printer + */ + // function render() {} + + /** + * Returns a start tag + * @param $tag Tag name + * @param $attr Attribute array + */ + protected function start($tag, $attr = array()) { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) + ); + } + + /** + * Returns an end teg + * @param $tag Tag name + */ + protected function end($tag) { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_End($tag) + ); + } + + /** + * Prints a complete element with content inside + * @param $tag Tag name + * @param $contents Element contents + * @param $attr Tag attributes + * @param $escape Bool whether or not to escape contents + */ + protected function element($tag, $contents, $attr = array(), $escape = true) { + return $this->start($tag, $attr) . + ($escape ? $this->escape($contents) : $contents) . + $this->end($tag); + } + + protected function elementEmpty($tag, $attr = array()) { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Empty($tag, $attr) + ); + } + + protected function text($text) { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Text($text) + ); + } + + /** + * Prints a simple key/value row in a table. + * @param $name Key + * @param $value Value + */ + protected function row($name, $value) { + if (is_bool($value)) $value = $value ? 'On' : 'Off'; + return + $this->start('tr') . "\n" . + $this->element('th', $name) . "\n" . + $this->element('td', $value) . "\n" . + $this->end('tr') + ; + } + + /** + * Escapes a string for HTML output. + * @param $string String to escape + */ + protected function escape($string) { + $string = HTMLPurifier_Encoder::cleanUTF8($string); + $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); + return $string; + } + + /** + * Takes a list of strings and turns them into a single list + * @param $array List of strings + * @param $polite Bool whether or not to add an end before the last + */ + protected function listify($array, $polite = false) { + if (empty($array)) return 'None'; + $ret = ''; + $i = count($array); + foreach ($array as $value) { + $i--; + $ret .= $value; + if ($i > 0 && !($polite && $i == 1)) $ret .= ', '; + if ($polite && $i == 1) $ret .= 'and '; + } + return $ret; + } + + /** + * Retrieves the class of an object without prefixes, as well as metadata + * @param $obj Object to determine class of + * @param $prefix Further prefix to remove + */ + protected function getClass($obj, $sec_prefix = '') { + static $five = null; + if ($five === null) $five = version_compare(PHP_VERSION, '5', '>='); + $prefix = 'HTMLPurifier_' . $sec_prefix; + if (!$five) $prefix = strtolower($prefix); + $class = str_replace($prefix, '', get_class($obj)); + $lclass = strtolower($class); + $class .= '('; + switch ($lclass) { + case 'enum': + $values = array(); + foreach ($obj->valid_values as $value => $bool) { + $values[] = $value; + } + $class .= implode(', ', $values); + break; + case 'css_composite': + $values = array(); + foreach ($obj->defs as $def) { + $values[] = $this->getClass($def, $sec_prefix); + } + $class .= implode(', ', $values); + break; + case 'css_multiple': + $class .= $this->getClass($obj->single, $sec_prefix) . ', '; + $class .= $obj->max; + break; + case 'css_denyelementdecorator': + $class .= $this->getClass($obj->def, $sec_prefix) . ', '; + $class .= $obj->element; + break; + case 'css_importantdecorator': + $class .= $this->getClass($obj->def, $sec_prefix); + if ($obj->allow) $class .= ', !important'; + break; + } + $class .= ')'; + return $class; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Printer/CSSDefinition.php b/lib/php/HTMLPurifier/Printer/CSSDefinition.php new file mode 100644 index 0000000..81f9865 --- /dev/null +++ b/lib/php/HTMLPurifier/Printer/CSSDefinition.php @@ -0,0 +1,38 @@ +<?php + +class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer +{ + + protected $def; + + public function render($config) { + $this->def = $config->getCSSDefinition(); + $ret = ''; + + $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); + $ret .= $this->start('table'); + + $ret .= $this->element('caption', 'Properties ($info)'); + + $ret .= $this->start('thead'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Property', array('class' => 'heavy')); + $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;')); + $ret .= $this->end('tr'); + $ret .= $this->end('thead'); + + ksort($this->def->info); + foreach ($this->def->info as $property => $obj) { + $name = $this->getClass($obj, 'AttrDef_'); + $ret .= $this->row($property, $name); + } + + $ret .= $this->end('table'); + $ret .= $this->end('div'); + + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Printer/ConfigForm.css b/lib/php/HTMLPurifier/Printer/ConfigForm.css new file mode 100644 index 0000000..3ff1a88 --- /dev/null +++ b/lib/php/HTMLPurifier/Printer/ConfigForm.css @@ -0,0 +1,10 @@ + +.hp-config {} + +.hp-config tbody th {text-align:right; padding-right:0.5em;} +.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;} +.hp-config .namespace th {text-align:center;} +.hp-config .verbose {display:none;} +.hp-config .controls {text-align:center;} + +/* vim: et sw=4 sts=4 */ diff --git a/lib/php/HTMLPurifier/Printer/ConfigForm.js b/lib/php/HTMLPurifier/Printer/ConfigForm.js new file mode 100644 index 0000000..cba00c9 --- /dev/null +++ b/lib/php/HTMLPurifier/Printer/ConfigForm.js @@ -0,0 +1,5 @@ +function toggleWriteability(id_of_patient, checked) { + document.getElementById(id_of_patient).disabled = checked; +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Printer/ConfigForm.php b/lib/php/HTMLPurifier/Printer/ConfigForm.php new file mode 100644 index 0000000..02aa656 --- /dev/null +++ b/lib/php/HTMLPurifier/Printer/ConfigForm.php @@ -0,0 +1,368 @@ +<?php + +/** + * @todo Rewrite to use Interchange objects + */ +class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer +{ + + /** + * Printers for specific fields + */ + protected $fields = array(); + + /** + * Documentation URL, can have fragment tagged on end + */ + protected $docURL; + + /** + * Name of form element to stuff config in + */ + protected $name; + + /** + * Whether or not to compress directive names, clipping them off + * after a certain amount of letters. False to disable or integer letters + * before clipping. + */ + protected $compress = false; + + /** + * @param $name Form element name for directives to be stuffed into + * @param $doc_url String documentation URL, will have fragment tagged on + * @param $compress Integer max length before compressing a directive name, set to false to turn off + */ + public function __construct( + $name, $doc_url = null, $compress = false + ) { + parent::__construct(); + $this->docURL = $doc_url; + $this->name = $name; + $this->compress = $compress; + // initialize sub-printers + $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); + $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); + } + + /** + * Sets default column and row size for textareas in sub-printers + * @param $cols Integer columns of textarea, null to use default + * @param $rows Integer rows of textarea, null to use default + */ + public function setTextareaDimensions($cols = null, $rows = null) { + if ($cols) $this->fields['default']->cols = $cols; + if ($rows) $this->fields['default']->rows = $rows; + } + + /** + * Retrieves styling, in case it is not accessible by webserver + */ + public static function getCSS() { + return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); + } + + /** + * Retrieves JavaScript, in case it is not accessible by webserver + */ + public static function getJavaScript() { + return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); + } + + /** + * Returns HTML output for a configuration form + * @param $config Configuration object of current form state, or an array + * where [0] has an HTML namespace and [1] is being rendered. + * @param $allowed Optional namespace(s) and directives to restrict form to. + */ + public function render($config, $allowed = true, $render_controls = true) { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + + $this->config = $config; + $this->genConfig = $gen_config; + $this->prepareGenerator($gen_config); + + $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def); + $all = array(); + foreach ($allowed as $key) { + list($ns, $directive) = $key; + $all[$ns][$directive] = $config->get($ns .'.'. $directive); + } + + $ret = ''; + $ret .= $this->start('table', array('class' => 'hp-config')); + $ret .= $this->start('thead'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); + $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); + $ret .= $this->end('tr'); + $ret .= $this->end('thead'); + foreach ($all as $ns => $directives) { + $ret .= $this->renderNamespace($ns, $directives); + } + if ($render_controls) { + $ret .= $this->start('tbody'); + $ret .= $this->start('tr'); + $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); + $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); + $ret .= '[<a href="?">Reset</a>]'; + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); + } + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders a single namespace + * @param $ns String namespace name + * @param $directive Associative array of directives to values + */ + protected function renderNamespace($ns, $directives) { + $ret = ''; + $ret .= $this->start('tbody', array('class' => 'namespace')); + $ret .= $this->start('tr'); + $ret .= $this->element('th', $ns, array('colspan' => 2)); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); + $ret .= $this->start('tbody'); + foreach ($directives as $directive => $value) { + $ret .= $this->start('tr'); + $ret .= $this->start('th'); + if ($this->docURL) { + $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); + $ret .= $this->start('a', array('href' => $url)); + } + $attr = array('for' => "{$this->name}:$ns.$directive"); + + // crop directive name if it's too long + if (!$this->compress || (strlen($directive) < $this->compress)) { + $directive_disp = $directive; + } else { + $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; + $attr['title'] = $directive; + } + + $ret .= $this->element( + 'label', + $directive_disp, + // component printers must create an element with this id + $attr + ); + if ($this->docURL) $ret .= $this->end('a'); + $ret .= $this->end('th'); + + $ret .= $this->start('td'); + $def = $this->config->def->info["$ns.$directive"]; + if (is_int($def)) { + $allow_null = $def < 0; + $type = abs($def); + } else { + $type = $def->type; + $allow_null = isset($def->allow_null); + } + if (!isset($this->fields[$type])) $type = 0; // default + $type_obj = $this->fields[$type]; + if ($allow_null) { + $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); + } + $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + } + $ret .= $this->end('tbody'); + return $ret; + } + +} + +/** + * Printer decorator for directives that accept null + */ +class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer { + /** + * Printer being decorated + */ + protected $obj; + /** + * @param $obj Printer to decorate + */ + public function __construct($obj) { + parent::__construct(); + $this->obj = $obj; + } + public function render($ns, $directive, $value, $name, $config) { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + $this->prepareGenerator($gen_config); + + $ret = ''; + $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' Null/Disabled'); + $ret .= $this->end('label'); + $attr = array( + 'type' => 'checkbox', + 'value' => '1', + 'class' => 'null-toggle', + 'name' => "$name"."[Null_$ns.$directive]", + 'id' => "$name:Null_$ns.$directive", + 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! + ); + if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { + // modify inline javascript slightly + $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)"; + } + if ($value === null) $attr['checked'] = 'checked'; + $ret .= $this->elementEmpty('input', $attr); + $ret .= $this->text(' or '); + $ret .= $this->elementEmpty('br'); + $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config)); + return $ret; + } +} + +/** + * Swiss-army knife configuration form field printer + */ +class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { + public $cols = 18; + public $rows = 5; + public function render($ns, $directive, $value, $name, $config) { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + $this->prepareGenerator($gen_config); + // this should probably be split up a little + $ret = ''; + $def = $config->def->info["$ns.$directive"]; + if (is_int($def)) { + $type = abs($def); + } else { + $type = $def->type; + } + if (is_array($value)) { + switch ($type) { + case HTMLPurifier_VarParser::LOOKUP: + $array = $value; + $value = array(); + foreach ($array as $val => $b) { + $value[] = $val; + } + case HTMLPurifier_VarParser::ALIST: + $value = implode(PHP_EOL, $value); + break; + case HTMLPurifier_VarParser::HASH: + $nvalue = ''; + foreach ($value as $i => $v) { + $nvalue .= "$i:$v" . PHP_EOL; + } + $value = $nvalue; + break; + default: + $value = ''; + } + } + if ($type === HTMLPurifier_VarParser::MIXED) { + return 'Not supported'; + $value = serialize($value); + } + $attr = array( + 'name' => "$name"."[$ns.$directive]", + 'id' => "$name:$ns.$directive" + ); + if ($value === null) $attr['disabled'] = 'disabled'; + if (isset($def->allowed)) { + $ret .= $this->start('select', $attr); + foreach ($def->allowed as $val => $b) { + $attr = array(); + if ($value == $val) $attr['selected'] = 'selected'; + $ret .= $this->element('option', $val, $attr); + } + $ret .= $this->end('select'); + } elseif ( + $type === HTMLPurifier_VarParser::TEXT || + $type === HTMLPurifier_VarParser::ITEXT || + $type === HTMLPurifier_VarParser::ALIST || + $type === HTMLPurifier_VarParser::HASH || + $type === HTMLPurifier_VarParser::LOOKUP + ) { + $attr['cols'] = $this->cols; + $attr['rows'] = $this->rows; + $ret .= $this->start('textarea', $attr); + $ret .= $this->text($value); + $ret .= $this->end('textarea'); + } else { + $attr['value'] = $value; + $attr['type'] = 'text'; + $ret .= $this->elementEmpty('input', $attr); + } + return $ret; + } +} + +/** + * Bool form field printer + */ +class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { + public function render($ns, $directive, $value, $name, $config) { + if (is_array($config) && isset($config[0])) { + $gen_config = $config[0]; + $config = $config[1]; + } else { + $gen_config = $config; + } + $this->prepareGenerator($gen_config); + $ret = ''; + $ret .= $this->start('div', array('id' => "$name:$ns.$directive")); + + $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' Yes'); + $ret .= $this->end('label'); + + $attr = array( + 'type' => 'radio', + 'name' => "$name"."[$ns.$directive]", + 'id' => "$name:Yes_$ns.$directive", + 'value' => '1' + ); + if ($value === true) $attr['checked'] = 'checked'; + if ($value === null) $attr['disabled'] = 'disabled'; + $ret .= $this->elementEmpty('input', $attr); + + $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' No'); + $ret .= $this->end('label'); + + $attr = array( + 'type' => 'radio', + 'name' => "$name"."[$ns.$directive]", + 'id' => "$name:No_$ns.$directive", + 'value' => '0' + ); + if ($value === false) $attr['checked'] = 'checked'; + if ($value === null) $attr['disabled'] = 'disabled'; + $ret .= $this->elementEmpty('input', $attr); + + $ret .= $this->end('div'); + + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Printer/HTMLDefinition.php b/lib/php/HTMLPurifier/Printer/HTMLDefinition.php new file mode 100644 index 0000000..8a8f126 --- /dev/null +++ b/lib/php/HTMLPurifier/Printer/HTMLDefinition.php @@ -0,0 +1,272 @@ +<?php + +class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer +{ + + /** + * Instance of HTMLPurifier_HTMLDefinition, for easy access + */ + protected $def; + + public function render($config) { + $ret = ''; + $this->config =& $config; + + $this->def = $config->getHTMLDefinition(); + + $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); + + $ret .= $this->renderDoctype(); + $ret .= $this->renderEnvironment(); + $ret .= $this->renderContentSets(); + $ret .= $this->renderInfo(); + + $ret .= $this->end('div'); + + return $ret; + } + + /** + * Renders the Doctype table + */ + protected function renderDoctype() { + $doctype = $this->def->doctype; + $ret = ''; + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Doctype'); + $ret .= $this->row('Name', $doctype->name); + $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No'); + $ret .= $this->row('Default Modules', implode($doctype->modules, ', ')); + $ret .= $this->row('Default Tidy Modules', implode($doctype->tidyModules, ', ')); + $ret .= $this->end('table'); + return $ret; + } + + + /** + * Renders environment table, which is miscellaneous info + */ + protected function renderEnvironment() { + $def = $this->def; + + $ret = ''; + + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Environment'); + + $ret .= $this->row('Parent of fragment', $def->info_parent); + $ret .= $this->renderChildren($def->info_parent_def->child); + $ret .= $this->row('Block wrap name', $def->info_block_wrapper); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Global attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0); + $ret .= $this->end('tr'); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Tag transforms'); + $list = array(); + foreach ($def->info_tag_transform as $old => $new) { + $new = $this->getClass($new, 'TagTransform_'); + $list[] = "<$old> with $new"; + } + $ret .= $this->element('td', $this->listify($list)); + $ret .= $this->end('tr'); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); + $ret .= $this->end('tr'); + + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); + $ret .= $this->end('tr'); + + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders the Content Sets table + */ + protected function renderContentSets() { + $ret = ''; + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Content Sets'); + foreach ($this->def->info_content_sets as $name => $lookup) { + $ret .= $this->heavyHeader($name); + $ret .= $this->start('tr'); + $ret .= $this->element('td', $this->listifyTagLookup($lookup)); + $ret .= $this->end('tr'); + } + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders the Elements ($info) table + */ + protected function renderInfo() { + $ret = ''; + $ret .= $this->start('table'); + $ret .= $this->element('caption', 'Elements ($info)'); + ksort($this->def->info); + $ret .= $this->heavyHeader('Allowed tags', 2); + $ret .= $this->start('tr'); + $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2)); + $ret .= $this->end('tr'); + foreach ($this->def->info as $name => $def) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2)); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Inline content'); + $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); + $ret .= $this->end('tr'); + if (!empty($def->excludes)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Excludes'); + $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); + $ret .= $this->end('tr'); + } + if (!empty($def->attr_transform_pre)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); + $ret .= $this->end('tr'); + } + if (!empty($def->attr_transform_post)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); + $ret .= $this->end('tr'); + } + if (!empty($def->auto_close)) { + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Auto closed by'); + $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); + $ret .= $this->end('tr'); + } + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Allowed attributes'); + $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0); + $ret .= $this->end('tr'); + + if (!empty($def->required_attr)) { + $ret .= $this->row('Required attributes', $this->listify($def->required_attr)); + } + + $ret .= $this->renderChildren($def->child); + } + $ret .= $this->end('table'); + return $ret; + } + + /** + * Renders a row describing the allowed children of an element + * @param $def HTMLPurifier_ChildDef of pertinent element + */ + protected function renderChildren($def) { + $context = new HTMLPurifier_Context(); + $ret = ''; + $ret .= $this->start('tr'); + $elements = array(); + $attr = array(); + if (isset($def->elements)) { + if ($def->type == 'strictblockquote') { + $def->validateChildren(array(), $this->config, $context); + } + $elements = $def->elements; + } + if ($def->type == 'chameleon') { + $attr['rowspan'] = 2; + } elseif ($def->type == 'empty') { + $elements = array(); + } elseif ($def->type == 'table') { + $elements = array_flip(array('col', 'caption', 'colgroup', 'thead', + 'tfoot', 'tbody', 'tr')); + } + $ret .= $this->element('th', 'Allowed children', $attr); + + if ($def->type == 'chameleon') { + + $ret .= $this->element('td', + '<em>Block</em>: ' . + $this->escape($this->listifyTagLookup($def->block->elements)),0,0); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element('td', + '<em>Inline</em>: ' . + $this->escape($this->listifyTagLookup($def->inline->elements)),0,0); + + } elseif ($def->type == 'custom') { + + $ret .= $this->element('td', '<em>'.ucfirst($def->type).'</em>: ' . + $def->dtd_regex); + + } else { + $ret .= $this->element('td', + '<em>'.ucfirst($def->type).'</em>: ' . + $this->escape($this->listifyTagLookup($elements)),0,0); + } + $ret .= $this->end('tr'); + return $ret; + } + + /** + * Listifies a tag lookup table. + * @param $array Tag lookup array in form of array('tagname' => true) + */ + protected function listifyTagLookup($array) { + ksort($array); + $list = array(); + foreach ($array as $name => $discard) { + if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue; + $list[] = $name; + } + return $this->listify($list); + } + + /** + * Listifies a list of objects by retrieving class names and internal state + * @param $array List of objects + * @todo Also add information about internal state + */ + protected function listifyObjectList($array) { + ksort($array); + $list = array(); + foreach ($array as $discard => $obj) { + $list[] = $this->getClass($obj, 'AttrTransform_'); + } + return $this->listify($list); + } + + /** + * Listifies a hash of attributes to AttrDef classes + * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) + */ + protected function listifyAttr($array) { + ksort($array); + $list = array(); + foreach ($array as $name => $obj) { + if ($obj === false) continue; + $list[] = "$name = <i>" . $this->getClass($obj, 'AttrDef_') . '</i>'; + } + return $this->listify($list); + } + + /** + * Creates a heavy header row + */ + protected function heavyHeader($text, $num = 1) { + $ret = ''; + $ret .= $this->start('tr'); + $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy')); + $ret .= $this->end('tr'); + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/PropertyList.php b/lib/php/HTMLPurifier/PropertyList.php new file mode 100644 index 0000000..2b99fb7 --- /dev/null +++ b/lib/php/HTMLPurifier/PropertyList.php @@ -0,0 +1,86 @@ +<?php + +/** + * Generic property list implementation + */ +class HTMLPurifier_PropertyList +{ + /** + * Internal data-structure for properties + */ + protected $data = array(); + + /** + * Parent plist + */ + protected $parent; + + protected $cache; + + public function __construct($parent = null) { + $this->parent = $parent; + } + + /** + * Recursively retrieves the value for a key + */ + public function get($name) { + if ($this->has($name)) return $this->data[$name]; + // possible performance bottleneck, convert to iterative if necessary + if ($this->parent) return $this->parent->get($name); + throw new HTMLPurifier_Exception("Key '$name' not found"); + } + + /** + * Sets the value of a key, for this plist + */ + public function set($name, $value) { + $this->data[$name] = $value; + } + + /** + * Returns true if a given key exists + */ + public function has($name) { + return array_key_exists($name, $this->data); + } + + /** + * Resets a value to the value of it's parent, usually the default. If + * no value is specified, the entire plist is reset. + */ + public function reset($name = null) { + if ($name == null) $this->data = array(); + else unset($this->data[$name]); + } + + /** + * Squashes this property list and all of its property lists into a single + * array, and returns the array. This value is cached by default. + * @param $force If true, ignores the cache and regenerates the array. + */ + public function squash($force = false) { + if ($this->cache !== null && !$force) return $this->cache; + if ($this->parent) { + return $this->cache = array_merge($this->parent->squash($force), $this->data); + } else { + return $this->cache = $this->data; + } + } + + /** + * Returns the parent plist. + */ + public function getParent() { + return $this->parent; + } + + /** + * Sets the parent plist. + */ + public function setParent($plist) { + $this->parent = $plist; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/PropertyListIterator.php b/lib/php/HTMLPurifier/PropertyListIterator.php new file mode 100644 index 0000000..8f25044 --- /dev/null +++ b/lib/php/HTMLPurifier/PropertyListIterator.php @@ -0,0 +1,32 @@ +<?php + +/** + * Property list iterator. Do not instantiate this class directly. + */ +class HTMLPurifier_PropertyListIterator extends FilterIterator +{ + + protected $l; + protected $filter; + + /** + * @param $data Array of data to iterate over + * @param $filter Optional prefix to only allow values of + */ + public function __construct(Iterator $iterator, $filter = null) { + parent::__construct($iterator); + $this->l = strlen($filter); + $this->filter = $filter; + } + + public function accept() { + $key = $this->getInnerIterator()->key(); + if( strncmp($key, $this->filter, $this->l) !== 0 ) { + return false; + } + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy.php b/lib/php/HTMLPurifier/Strategy.php new file mode 100644 index 0000000..2462865 --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy.php @@ -0,0 +1,26 @@ +<?php + +/** + * Supertype for classes that define a strategy for modifying/purifying tokens. + * + * While HTMLPurifier's core purpose is fixing HTML into something proper, + * strategies provide plug points for extra configuration or even extra + * features, such as custom tags, custom parsing of text, etc. + */ + + +abstract class HTMLPurifier_Strategy +{ + + /** + * Executes the strategy on the tokens. + * + * @param $tokens Array of HTMLPurifier_Token objects to be operated on. + * @param $config Configuration options + * @returns Processed array of token objects. + */ + abstract public function execute($tokens, $config, $context); + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy/Composite.php b/lib/php/HTMLPurifier/Strategy/Composite.php new file mode 100644 index 0000000..816490b --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy/Composite.php @@ -0,0 +1,25 @@ +<?php + +/** + * Composite strategy that runs multiple strategies on tokens. + */ +abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy +{ + + /** + * List of strategies to run tokens through. + */ + protected $strategies = array(); + + abstract public function __construct(); + + public function execute($tokens, $config, $context) { + foreach ($this->strategies as $strategy) { + $tokens = $strategy->execute($tokens, $config, $context); + } + return $tokens; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy/Core.php b/lib/php/HTMLPurifier/Strategy/Core.php new file mode 100644 index 0000000..d90e158 --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy/Core.php @@ -0,0 +1,18 @@ +<?php + +/** + * Core strategy composed of the big four strategies. + */ +class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite +{ + + public function __construct() { + $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements(); + $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed(); + $this->strategies[] = new HTMLPurifier_Strategy_FixNesting(); + $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes(); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy/FixNesting.php b/lib/php/HTMLPurifier/Strategy/FixNesting.php new file mode 100644 index 0000000..f818023 --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy/FixNesting.php @@ -0,0 +1,328 @@ +<?php + +/** + * Takes a well formed list of tokens and fixes their nesting. + * + * HTML elements dictate which elements are allowed to be their children, + * for example, you can't have a p tag in a span tag. Other elements have + * much more rigorous definitions: tables, for instance, require a specific + * order for their elements. There are also constraints not expressible by + * document type definitions, such as the chameleon nature of ins/del + * tags and global child exclusions. + * + * The first major objective of this strategy is to iterate through all the + * nodes (not tokens) of the list of tokens and determine whether or not + * their children conform to the element's definition. If they do not, the + * child definition may optionally supply an amended list of elements that + * is valid or require that the entire node be deleted (and the previous + * node rescanned). + * + * The second objective is to ensure that explicitly excluded elements of + * an element do not appear in its children. Code that accomplishes this + * task is pervasive through the strategy, though the two are distinct tasks + * and could, theoretically, be seperated (although it's not recommended). + * + * @note Whether or not unrecognized children are silently dropped or + * translated into text depends on the child definitions. + * + * @todo Enable nodes to be bubbled out of the structure. + */ + +class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy +{ + + public function execute($tokens, $config, $context) { + //####################################################################// + // Pre-processing + + // get a copy of the HTML definition + $definition = $config->getHTMLDefinition(); + + // insert implicit "parent" node, will be removed at end. + // DEFINITION CALL + $parent_name = $definition->info_parent; + array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name)); + $tokens[] = new HTMLPurifier_Token_End($parent_name); + + // setup the context variable 'IsInline', for chameleon processing + // is 'false' when we are not inline, 'true' when it must always + // be inline, and an integer when it is inline for a certain + // branch of the document tree + $is_inline = $definition->info_parent_def->descendants_are_inline; + $context->register('IsInline', $is_inline); + + // setup error collector + $e =& $context->get('ErrorCollector', true); + + //####################################################################// + // Loop initialization + + // stack that contains the indexes of all parents, + // $stack[count($stack)-1] being the current parent + $stack = array(); + + // stack that contains all elements that are excluded + // it is organized by parent elements, similar to $stack, + // but it is only populated when an element with exclusions is + // processed, i.e. there won't be empty exclusions. + $exclude_stack = array(); + + // variable that contains the start token while we are processing + // nodes. This enables error reporting to do its job + $start_token = false; + $context->register('CurrentToken', $start_token); + + //####################################################################// + // Loop + + // iterate through all start nodes. Determining the start node + // is complicated so it has been omitted from the loop construct + for ($i = 0, $size = count($tokens) ; $i < $size; ) { + + //################################################################// + // Gather information on children + + // child token accumulator + $child_tokens = array(); + + // scroll to the end of this node, report number, and collect + // all children + for ($j = $i, $depth = 0; ; $j++) { + if ($tokens[$j] instanceof HTMLPurifier_Token_Start) { + $depth++; + // skip token assignment on first iteration, this is the + // token we currently are on + if ($depth == 1) continue; + } elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) { + $depth--; + // skip token assignment on last iteration, this is the + // end token of the token we're currently on + if ($depth == 0) break; + } + $child_tokens[] = $tokens[$j]; + } + + // $i is index of start token + // $j is index of end token + + $start_token = $tokens[$i]; // to make token available via CurrentToken + + //################################################################// + // Gather information on parent + + // calculate parent information + if ($count = count($stack)) { + $parent_index = $stack[$count-1]; + $parent_name = $tokens[$parent_index]->name; + if ($parent_index == 0) { + $parent_def = $definition->info_parent_def; + } else { + $parent_def = $definition->info[$parent_name]; + } + } else { + // processing as if the parent were the "root" node + // unknown info, it won't be used anyway, in the future, + // we may want to enforce one element only (this is + // necessary for HTML Purifier to clean entire documents + $parent_index = $parent_name = $parent_def = null; + } + + // calculate context + if ($is_inline === false) { + // check if conditions make it inline + if (!empty($parent_def) && $parent_def->descendants_are_inline) { + $is_inline = $count - 1; + } + } else { + // check if we're out of inline + if ($count === $is_inline) { + $is_inline = false; + } + } + + //################################################################// + // Determine whether element is explicitly excluded SGML-style + + // determine whether or not element is excluded by checking all + // parent exclusions. The array should not be very large, two + // elements at most. + $excluded = false; + if (!empty($exclude_stack)) { + foreach ($exclude_stack as $lookup) { + if (isset($lookup[$tokens[$i]->name])) { + $excluded = true; + // no need to continue processing + break; + } + } + } + + //################################################################// + // Perform child validation + + if ($excluded) { + // there is an exclusion, remove the entire node + $result = false; + $excludes = array(); // not used, but good to initialize anyway + } else { + // DEFINITION CALL + if ($i === 0) { + // special processing for the first node + $def = $definition->info_parent_def; + } else { + $def = $definition->info[$tokens[$i]->name]; + + } + + if (!empty($def->child)) { + // have DTD child def validate children + $result = $def->child->validateChildren( + $child_tokens, $config, $context); + } else { + // weird, no child definition, get rid of everything + $result = false; + } + + // determine whether or not this element has any exclusions + $excludes = $def->excludes; + } + + // $result is now a bool or array + + //################################################################// + // Process result by interpreting $result + + if ($result === true || $child_tokens === $result) { + // leave the node as is + + // register start token as a parental node start + $stack[] = $i; + + // register exclusions if there are any + if (!empty($excludes)) $exclude_stack[] = $excludes; + + // move cursor to next possible start node + $i++; + + } elseif($result === false) { + // remove entire node + + if ($e) { + if ($excluded) { + $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); + } else { + $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); + } + } + + // calculate length of inner tokens and current tokens + $length = $j - $i + 1; + + // perform removal + array_splice($tokens, $i, $length); + + // update size + $size -= $length; + + // there is no start token to register, + // current node is now the next possible start node + // unless it turns out that we need to do a double-check + + // this is a rought heuristic that covers 100% of HTML's + // cases and 99% of all other cases. A child definition + // that would be tricked by this would be something like: + // ( | a b c) where it's all or nothing. Fortunately, + // our current implementation claims that that case would + // not allow empty, even if it did + if (!$parent_def->child->allow_empty) { + // we need to do a double-check + $i = $parent_index; + array_pop($stack); + } + + // PROJECTED OPTIMIZATION: Process all children elements before + // reprocessing parent node. + + } else { + // replace node with $result + + // calculate length of inner tokens + $length = $j - $i - 1; + + if ($e) { + if (empty($result) && $length) { + $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); + } else { + $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); + } + } + + // perform replacement + array_splice($tokens, $i + 1, $length, $result); + + // update size + $size -= $length; + $size += count($result); + + // register start token as a parental node start + $stack[] = $i; + + // register exclusions if there are any + if (!empty($excludes)) $exclude_stack[] = $excludes; + + // move cursor to next possible start node + $i++; + + } + + //################################################################// + // Scroll to next start node + + // We assume, at this point, that $i is the index of the token + // that is the first possible new start point for a node. + + // Test if the token indeed is a start tag, if not, move forward + // and test again. + $size = count($tokens); + while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) { + if ($tokens[$i] instanceof HTMLPurifier_Token_End) { + // pop a token index off the stack if we ended a node + array_pop($stack); + // pop an exclusion lookup off exclusion stack if + // we ended node and that node had exclusions + if ($i == 0 || $i == $size - 1) { + // use specialized var if it's the super-parent + $s_excludes = $definition->info_parent_def->excludes; + } else { + $s_excludes = $definition->info[$tokens[$i]->name]->excludes; + } + if ($s_excludes) { + array_pop($exclude_stack); + } + } + $i++; + } + + } + + //####################################################################// + // Post-processing + + // remove implicit parent tokens at the beginning and end + array_shift($tokens); + array_pop($tokens); + + // remove context variables + $context->destroy('IsInline'); + $context->destroy('CurrentToken'); + + //####################################################################// + // Return + + return $tokens; + + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy/MakeWellFormed.php b/lib/php/HTMLPurifier/Strategy/MakeWellFormed.php new file mode 100644 index 0000000..feb0c32 --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy/MakeWellFormed.php @@ -0,0 +1,457 @@ +<?php + +/** + * Takes tokens makes them well-formed (balance end tags, etc.) + */ +class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy +{ + + /** + * Array stream of tokens being processed. + */ + protected $tokens; + + /** + * Current index in $tokens. + */ + protected $t; + + /** + * Current nesting of elements. + */ + protected $stack; + + /** + * Injectors active in this stream processing. + */ + protected $injectors; + + /** + * Current instance of HTMLPurifier_Config. + */ + protected $config; + + /** + * Current instance of HTMLPurifier_Context. + */ + protected $context; + + public function execute($tokens, $config, $context) { + + $definition = $config->getHTMLDefinition(); + + // local variables + $generator = new HTMLPurifier_Generator($config, $context); + $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); + $e = $context->get('ErrorCollector', true); + $t = false; // token index + $i = false; // injector index + $token = false; // the current token + $reprocess = false; // whether or not to reprocess the same token + $stack = array(); + + // member variables + $this->stack =& $stack; + $this->t =& $t; + $this->tokens =& $tokens; + $this->config = $config; + $this->context = $context; + + // context variables + $context->register('CurrentNesting', $stack); + $context->register('InputIndex', $t); + $context->register('InputTokens', $tokens); + $context->register('CurrentToken', $token); + + // -- begin INJECTOR -- + + $this->injectors = array(); + + $injectors = $config->getBatch('AutoFormat'); + $def_injectors = $definition->info_injector; + $custom_injectors = $injectors['Custom']; + unset($injectors['Custom']); // special case + foreach ($injectors as $injector => $b) { + // XXX: Fix with a legitimate lookup table of enabled filters + if (strpos($injector, '.') !== false) continue; + $injector = "HTMLPurifier_Injector_$injector"; + if (!$b) continue; + $this->injectors[] = new $injector; + } + foreach ($def_injectors as $injector) { + // assumed to be objects + $this->injectors[] = $injector; + } + foreach ($custom_injectors as $injector) { + if (is_string($injector)) { + $injector = "HTMLPurifier_Injector_$injector"; + $injector = new $injector; + } + $this->injectors[] = $injector; + } + + // give the injectors references to the definition and context + // variables for performance reasons + foreach ($this->injectors as $ix => $injector) { + $error = $injector->prepare($config, $context); + if (!$error) continue; + array_splice($this->injectors, $ix, 1); // rm the injector + trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); + } + + // -- end INJECTOR -- + + // a note on punting: + // In order to reduce code duplication, whenever some code needs + // to make HTML changes in order to make things "correct", the + // new HTML gets sent through the purifier, regardless of its + // status. This means that if we add a start token, because it + // was totally necessary, we don't have to update nesting; we just + // punt ($reprocess = true; continue;) and it does that for us. + + // isset is in loop because $tokens size changes during loop exec + for ( + $t = 0; + $t == 0 || isset($tokens[$t - 1]); + // only increment if we don't need to reprocess + $reprocess ? $reprocess = false : $t++ + ) { + + // check for a rewind + if (is_int($i) && $i >= 0) { + // possibility: disable rewinding if the current token has a + // rewind set on it already. This would offer protection from + // infinite loop, but might hinder some advanced rewinding. + $rewind_to = $this->injectors[$i]->getRewind(); + if (is_int($rewind_to) && $rewind_to < $t) { + if ($rewind_to < 0) $rewind_to = 0; + while ($t > $rewind_to) { + $t--; + $prev = $tokens[$t]; + // indicate that other injectors should not process this token, + // but we need to reprocess it + unset($prev->skip[$i]); + $prev->rewind = $i; + if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack); + elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start; + } + } + $i = false; + } + + // handle case of document end + if (!isset($tokens[$t])) { + // kill processing if stack is empty + if (empty($this->stack)) break; + + // peek + $top_nesting = array_pop($this->stack); + $this->stack[] = $top_nesting; + + // send error + if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); + } + + // append, don't splice, since this is the end + $tokens[] = new HTMLPurifier_Token_End($top_nesting->name); + + // punt! + $reprocess = true; + continue; + } + + $token = $tokens[$t]; + + //echo '<br>'; printTokens($tokens, $t); printTokens($this->stack); + + // quick-check: if it's not a tag, no need to process + if (empty($token->is_tag)) { + if ($token instanceof HTMLPurifier_Token_Text) { + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) continue; + if ($token->rewind !== null && $token->rewind !== $i) continue; + $injector->handleText($token); + $this->processToken($token, $i); + $reprocess = true; + break; + } + } + // another possibility is a comment + continue; + } + + if (isset($definition->info[$token->name])) { + $type = $definition->info[$token->name]->child->type; + } else { + $type = false; // Type is unknown, treat accordingly + } + + // quick tag checks: anything that's *not* an end tag + $ok = false; + if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { + // claims to be a start tag but is empty + $token = new HTMLPurifier_Token_Empty($token->name, $token->attr); + $ok = true; + } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { + // claims to be empty but really is a start tag + $this->swap(new HTMLPurifier_Token_End($token->name)); + $this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr)); + // punt (since we had to modify the input stream in a non-trivial way) + $reprocess = true; + continue; + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + // real empty token + $ok = true; + } elseif ($token instanceof HTMLPurifier_Token_Start) { + // start tag + + // ...unless they also have to close their parent + if (!empty($this->stack)) { + + $parent = array_pop($this->stack); + $this->stack[] = $parent; + + if (isset($definition->info[$parent->name])) { + $elements = $definition->info[$parent->name]->child->getAllowedElements($config); + $autoclose = !isset($elements[$token->name]); + } else { + $autoclose = false; + } + + $carryover = false; + if ($autoclose && $definition->info[$parent->name]->formatting) { + $carryover = true; + } + + if ($autoclose) { + // errors need to be updated + $new_token = new HTMLPurifier_Token_End($parent->name); + $new_token->start = $parent; + if ($carryover) { + $element = clone $parent; + $element->armor['MakeWellFormed_TagClosedError'] = true; + $element->carryover = true; + $this->processToken(array($new_token, $token, $element)); + } else { + $this->insertBefore($new_token); + } + if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { + if (!$carryover) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); + } else { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); + } + } + $reprocess = true; + continue; + } + + } + $ok = true; + } + + if ($ok) { + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) continue; + if ($token->rewind !== null && $token->rewind !== $i) continue; + $injector->handleElement($token); + $this->processToken($token, $i); + $reprocess = true; + break; + } + if (!$reprocess) { + // ah, nothing interesting happened; do normal processing + $this->swap($token); + if ($token instanceof HTMLPurifier_Token_Start) { + $this->stack[] = $token; + } elseif ($token instanceof HTMLPurifier_Token_End) { + throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed'); + } + } + continue; + } + + // sanity check: we should be dealing with a closing tag + if (!$token instanceof HTMLPurifier_Token_End) { + throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); + } + + // make sure that we have something open + if (empty($this->stack)) { + if ($escape_invalid_tags) { + if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); + $this->swap(new HTMLPurifier_Token_Text( + $generator->generateFromToken($token) + )); + } else { + $this->remove(); + if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); + } + $reprocess = true; + continue; + } + + // first, check for the simplest case: everything closes neatly. + // Eventually, everything passes through here; if there are problems + // we modify the input stream accordingly and then punt, so that + // the tokens get processed again. + $current_parent = array_pop($this->stack); + if ($current_parent->name == $token->name) { + $token->start = $current_parent; + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) continue; + if ($token->rewind !== null && $token->rewind !== $i) continue; + $injector->handleEnd($token); + $this->processToken($token, $i); + $this->stack[] = $current_parent; + $reprocess = true; + break; + } + continue; + } + + // okay, so we're trying to close the wrong tag + + // undo the pop previous pop + $this->stack[] = $current_parent; + + // scroll back the entire nest, trying to find our tag. + // (feature could be to specify how far you'd like to go) + $size = count($this->stack); + // -2 because -1 is the last element, but we already checked that + $skipped_tags = false; + for ($j = $size - 2; $j >= 0; $j--) { + if ($this->stack[$j]->name == $token->name) { + $skipped_tags = array_slice($this->stack, $j); + break; + } + } + + // we didn't find the tag, so remove + if ($skipped_tags === false) { + if ($escape_invalid_tags) { + $this->swap(new HTMLPurifier_Token_Text( + $generator->generateFromToken($token) + )); + if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); + } else { + $this->remove(); + if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); + } + $reprocess = true; + continue; + } + + // do errors, in REVERSE $j order: a,b,c with </a></b></c> + $c = count($skipped_tags); + if ($e) { + for ($j = $c - 1; $j > 0; $j--) { + // notice we exclude $j == 0, i.e. the current ending tag, from + // the errors... + if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); + } + } + } + + // insert tags, in FORWARD $j order: c,b,a with </a></b></c> + $replace = array($token); + for ($j = 1; $j < $c; $j++) { + // ...as well as from the insertions + $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); + $new_token->start = $skipped_tags[$j]; + array_unshift($replace, $new_token); + if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { + $element = clone $skipped_tags[$j]; + $element->carryover = true; + $element->armor['MakeWellFormed_TagClosedError'] = true; + $replace[] = $element; + } + } + $this->processToken($replace); + $reprocess = true; + continue; + } + + $context->destroy('CurrentNesting'); + $context->destroy('InputTokens'); + $context->destroy('InputIndex'); + $context->destroy('CurrentToken'); + + unset($this->injectors, $this->stack, $this->tokens, $this->t); + return $tokens; + } + + /** + * Processes arbitrary token values for complicated substitution patterns. + * In general: + * + * If $token is an array, it is a list of tokens to substitute for the + * current token. These tokens then get individually processed. If there + * is a leading integer in the list, that integer determines how many + * tokens from the stream should be removed. + * + * If $token is a regular token, it is swapped with the current token. + * + * If $token is false, the current token is deleted. + * + * If $token is an integer, that number of tokens (with the first token + * being the current one) will be deleted. + * + * @param $token Token substitution value + * @param $injector Injector that performed the substitution; default is if + * this is not an injector related operation. + */ + protected function processToken($token, $injector = -1) { + + // normalize forms of token + if (is_object($token)) $token = array(1, $token); + if (is_int($token)) $token = array($token); + if ($token === false) $token = array(1); + if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector'); + if (!is_int($token[0])) array_unshift($token, 1); + if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); + + // $token is now an array with the following form: + // array(number nodes to delete, new node 1, new node 2, ...) + + $delete = array_shift($token); + $old = array_splice($this->tokens, $this->t, $delete, $token); + + if ($injector > -1) { + // determine appropriate skips + $oldskip = isset($old[0]) ? $old[0]->skip : array(); + foreach ($token as $object) { + $object->skip = $oldskip; + $object->skip[$injector] = true; + } + } + + } + + /** + * Inserts a token before the current token. Cursor now points to this token + */ + private function insertBefore($token) { + array_splice($this->tokens, $this->t, 0, array($token)); + } + + /** + * Removes current token. Cursor now points to new token occupying previously + * occupied space. + */ + private function remove() { + array_splice($this->tokens, $this->t, 1); + } + + /** + * Swap current token with new token. Cursor points to new token (no change). + */ + private function swap($token) { + $this->tokens[$this->t] = $token; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy/RemoveForeignElements.php b/lib/php/HTMLPurifier/Strategy/RemoveForeignElements.php new file mode 100644 index 0000000..cf3a33e --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy/RemoveForeignElements.php @@ -0,0 +1,171 @@ +<?php + +/** + * Removes all unrecognized tags from the list of tokens. + * + * This strategy iterates through all the tokens and removes unrecognized + * tokens. If a token is not recognized but a TagTransform is defined for + * that element, the element will be transformed accordingly. + */ + +class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy +{ + + public function execute($tokens, $config, $context) { + $definition = $config->getHTMLDefinition(); + $generator = new HTMLPurifier_Generator($config, $context); + $result = array(); + + $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); + $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); + + // currently only used to determine if comments should be kept + $trusted = $config->get('HTML.Trusted'); + + $remove_script_contents = $config->get('Core.RemoveScriptContents'); + $hidden_elements = $config->get('Core.HiddenElements'); + + // remove script contents compatibility + if ($remove_script_contents === true) { + $hidden_elements['script'] = true; + } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) { + unset($hidden_elements['script']); + } + + $attr_validator = new HTMLPurifier_AttrValidator(); + + // removes tokens until it reaches a closing tag with its value + $remove_until = false; + + // converts comments into text tokens when this is equal to a tag name + $textify_comments = false; + + $token = false; + $context->register('CurrentToken', $token); + + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + + foreach($tokens as $token) { + if ($remove_until) { + if (empty($token->is_tag) || $token->name !== $remove_until) { + continue; + } + } + if (!empty( $token->is_tag )) { + // DEFINITION CALL + + // before any processing, try to transform the element + if ( + isset($definition->info_tag_transform[$token->name]) + ) { + $original_name = $token->name; + // there is a transformation for this tag + // DEFINITION CALL + $token = $definition-> + info_tag_transform[$token->name]-> + transform($token, $config, $context); + if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); + } + + if (isset($definition->info[$token->name])) { + + // mostly everything's good, but + // we need to make sure required attributes are in order + if ( + ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && + $definition->info[$token->name]->required_attr && + ($token->name != 'img' || $remove_invalid_img) // ensure config option still works + ) { + $attr_validator->validateToken($token, $config, $context); + $ok = true; + foreach ($definition->info[$token->name]->required_attr as $name) { + if (!isset($token->attr[$name])) { + $ok = false; + break; + } + } + if (!$ok) { + if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name); + continue; + } + $token->armor['ValidateAttributes'] = true; + } + + if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) { + $textify_comments = $token->name; + } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) { + $textify_comments = false; + } + + } elseif ($escape_invalid_tags) { + // invalid tag, generate HTML representation and insert in + if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); + $token = new HTMLPurifier_Token_Text( + $generator->generateFromToken($token) + ); + } else { + // check if we need to destroy all of the tag's children + // CAN BE GENERICIZED + if (isset($hidden_elements[$token->name])) { + if ($token instanceof HTMLPurifier_Token_Start) { + $remove_until = $token->name; + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + // do nothing: we're still looking + } else { + $remove_until = false; + } + if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); + } else { + if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); + } + continue; + } + } elseif ($token instanceof HTMLPurifier_Token_Comment) { + // textify comments in script tags when they are allowed + if ($textify_comments !== false) { + $data = $token->data; + $token = new HTMLPurifier_Token_Text($data); + } elseif ($trusted) { + // keep, but perform comment cleaning + if ($e) { + // perform check whether or not there's a trailing hyphen + if (substr($token->data, -1) == '-') { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'); + } + } + $token->data = rtrim($token->data, '-'); + $found_double_hyphen = false; + while (strpos($token->data, '--') !== false) { + if ($e && !$found_double_hyphen) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); + } + $found_double_hyphen = true; // prevent double-erroring + $token->data = str_replace('--', '-', $token->data); + } + } else { + // strip comments + if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + continue; + } + } elseif ($token instanceof HTMLPurifier_Token_Text) { + } else { + continue; + } + $result[] = $token; + } + if ($remove_until && $e) { + // we removed tokens until the end, throw error + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until); + } + + $context->destroy('CurrentToken'); + + return $result; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Strategy/ValidateAttributes.php b/lib/php/HTMLPurifier/Strategy/ValidateAttributes.php new file mode 100644 index 0000000..c3328a9 --- /dev/null +++ b/lib/php/HTMLPurifier/Strategy/ValidateAttributes.php @@ -0,0 +1,39 @@ +<?php + +/** + * Validate all attributes in the tokens. + */ + +class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy +{ + + public function execute($tokens, $config, $context) { + + // setup validator + $validator = new HTMLPurifier_AttrValidator(); + + $token = false; + $context->register('CurrentToken', $token); + + foreach ($tokens as $key => $token) { + + // only process tokens that have attributes, + // namely start and empty tags + if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue; + + // skip tokens that are armored + if (!empty($token->armor['ValidateAttributes'])) continue; + + // note that we have no facilities here for removing tokens + $validator->validateToken($token, $config, $context); + + $tokens[$key] = $token; // for PHP 4 + } + $context->destroy('CurrentToken'); + + return $tokens; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/StringHash.php b/lib/php/HTMLPurifier/StringHash.php new file mode 100644 index 0000000..62085c5 --- /dev/null +++ b/lib/php/HTMLPurifier/StringHash.php @@ -0,0 +1,39 @@ +<?php + +/** + * This is in almost every respect equivalent to an array except + * that it keeps track of which keys were accessed. + * + * @warning For the sake of backwards compatibility with early versions + * of PHP 5, you must not use the $hash[$key] syntax; if you do + * our version of offsetGet is never called. + */ +class HTMLPurifier_StringHash extends ArrayObject +{ + protected $accessed = array(); + + /** + * Retrieves a value, and logs the access. + */ + public function offsetGet($index) { + $this->accessed[$index] = true; + return parent::offsetGet($index); + } + + /** + * Returns a lookup array of all array indexes that have been accessed. + * @return Array in form array($index => true). + */ + public function getAccessed() { + return $this->accessed; + } + + /** + * Resets the access array. + */ + public function resetAccessed() { + $this->accessed = array(); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/StringHashParser.php b/lib/php/HTMLPurifier/StringHashParser.php new file mode 100644 index 0000000..f3e70c7 --- /dev/null +++ b/lib/php/HTMLPurifier/StringHashParser.php @@ -0,0 +1,110 @@ +<?php + +/** + * Parses string hash files. File format is as such: + * + * DefaultKeyValue + * KEY: Value + * KEY2: Value2 + * --MULTILINE-KEY-- + * Multiline + * value. + * + * Which would output something similar to: + * + * array( + * 'ID' => 'DefaultKeyValue', + * 'KEY' => 'Value', + * 'KEY2' => 'Value2', + * 'MULTILINE-KEY' => "Multiline\nvalue.\n", + * ) + * + * We use this as an easy to use file-format for configuration schema + * files, but the class itself is usage agnostic. + * + * You can use ---- to forcibly terminate parsing of a single string-hash; + * this marker is used in multi string-hashes to delimit boundaries. + */ +class HTMLPurifier_StringHashParser +{ + + public $default = 'ID'; + + /** + * Parses a file that contains a single string-hash. + */ + public function parseFile($file) { + if (!file_exists($file)) return false; + $fh = fopen($file, 'r'); + if (!$fh) return false; + $ret = $this->parseHandle($fh); + fclose($fh); + return $ret; + } + + /** + * Parses a file that contains multiple string-hashes delimited by '----' + */ + public function parseMultiFile($file) { + if (!file_exists($file)) return false; + $ret = array(); + $fh = fopen($file, 'r'); + if (!$fh) return false; + while (!feof($fh)) { + $ret[] = $this->parseHandle($fh); + } + fclose($fh); + return $ret; + } + + /** + * Internal parser that acepts a file handle. + * @note While it's possible to simulate in-memory parsing by using + * custom stream wrappers, if such a use-case arises we should + * factor out the file handle into its own class. + * @param $fh File handle with pointer at start of valid string-hash + * block. + */ + protected function parseHandle($fh) { + $state = false; + $single = false; + $ret = array(); + do { + $line = fgets($fh); + if ($line === false) break; + $line = rtrim($line, "\n\r"); + if (!$state && $line === '') continue; + if ($line === '----') break; + if (strncmp('--#', $line, 3) === 0) { + // Comment + continue; + } elseif (strncmp('--', $line, 2) === 0) { + // Multiline declaration + $state = trim($line, '- '); + if (!isset($ret[$state])) $ret[$state] = ''; + continue; + } elseif (!$state) { + $single = true; + if (strpos($line, ':') !== false) { + // Single-line declaration + list($state, $line) = explode(':', $line, 2); + $line = trim($line); + } else { + // Use default declaration + $state = $this->default; + } + } + if ($single) { + $ret[$state] = $line; + $single = false; + $state = false; + } else { + $ret[$state] .= "$line\n"; + } + } while (!feof($fh)); + return $ret; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/TagTransform.php b/lib/php/HTMLPurifier/TagTransform.php new file mode 100644 index 0000000..210a447 --- /dev/null +++ b/lib/php/HTMLPurifier/TagTransform.php @@ -0,0 +1,36 @@ +<?php + +/** + * Defines a mutation of an obsolete tag into a valid tag. + */ +abstract class HTMLPurifier_TagTransform +{ + + /** + * Tag name to transform the tag to. + */ + public $transform_to; + + /** + * Transforms the obsolete tag into the valid tag. + * @param $tag Tag to be transformed. + * @param $config Mandatory HTMLPurifier_Config object + * @param $context Mandatory HTMLPurifier_Context object + */ + abstract public function transform($tag, $config, $context); + + /** + * Prepends CSS properties to the style attribute, creating the + * attribute if it doesn't exist. + * @warning Copied over from AttrTransform, be sure to keep in sync + * @param $attr Attribute array to process (passed by reference) + * @param $css CSS to prepend + */ + protected function prependCSS(&$attr, $css) { + $attr['style'] = isset($attr['style']) ? $attr['style'] : ''; + $attr['style'] = $css . $attr['style']; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/TagTransform/Font.php b/lib/php/HTMLPurifier/TagTransform/Font.php new file mode 100644 index 0000000..ed24637 --- /dev/null +++ b/lib/php/HTMLPurifier/TagTransform/Font.php @@ -0,0 +1,96 @@ +<?php + +/** + * Transforms FONT tags to the proper form (SPAN with CSS styling) + * + * This transformation takes the three proprietary attributes of FONT and + * transforms them into their corresponding CSS attributes. These are color, + * face, and size. + * + * @note Size is an interesting case because it doesn't map cleanly to CSS. + * Thanks to + * http://style.cleverchimp.com/font_size_intervals/altintervals.html + * for reasonable mappings. + * @warning This doesn't work completely correctly; specifically, this + * TagTransform operates before well-formedness is enforced, so + * the "active formatting elements" algorithm doesn't get applied. + */ +class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform +{ + + public $transform_to = 'span'; + + protected $_size_lookup = array( + '0' => 'xx-small', + '1' => 'xx-small', + '2' => 'small', + '3' => 'medium', + '4' => 'large', + '5' => 'x-large', + '6' => 'xx-large', + '7' => '300%', + '-1' => 'smaller', + '-2' => '60%', + '+1' => 'larger', + '+2' => '150%', + '+3' => '200%', + '+4' => '300%' + ); + + public function transform($tag, $config, $context) { + + if ($tag instanceof HTMLPurifier_Token_End) { + $new_tag = clone $tag; + $new_tag->name = $this->transform_to; + return $new_tag; + } + + $attr = $tag->attr; + $prepend_style = ''; + + // handle color transform + if (isset($attr['color'])) { + $prepend_style .= 'color:' . $attr['color'] . ';'; + unset($attr['color']); + } + + // handle face transform + if (isset($attr['face'])) { + $prepend_style .= 'font-family:' . $attr['face'] . ';'; + unset($attr['face']); + } + + // handle size transform + if (isset($attr['size'])) { + // normalize large numbers + if ($attr['size']{0} == '+' || $attr['size']{0} == '-') { + $size = (int) $attr['size']; + if ($size < -2) $attr['size'] = '-2'; + if ($size > 4) $attr['size'] = '+4'; + } else { + $size = (int) $attr['size']; + if ($size > 7) $attr['size'] = '7'; + } + if (isset($this->_size_lookup[$attr['size']])) { + $prepend_style .= 'font-size:' . + $this->_size_lookup[$attr['size']] . ';'; + } + unset($attr['size']); + } + + if ($prepend_style) { + $attr['style'] = isset($attr['style']) ? + $prepend_style . $attr['style'] : + $prepend_style; + } + + $new_tag = clone $tag; + $new_tag->name = $this->transform_to; + $new_tag->attr = $attr; + + return $new_tag; + + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/TagTransform/Simple.php b/lib/php/HTMLPurifier/TagTransform/Simple.php new file mode 100644 index 0000000..0e36130 --- /dev/null +++ b/lib/php/HTMLPurifier/TagTransform/Simple.php @@ -0,0 +1,35 @@ +<?php + +/** + * Simple transformation, just change tag name to something else, + * and possibly add some styling. This will cover most of the deprecated + * tag cases. + */ +class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform +{ + + protected $style; + + /** + * @param $transform_to Tag name to transform to. + * @param $style CSS style to add to the tag + */ + public function __construct($transform_to, $style = null) { + $this->transform_to = $transform_to; + $this->style = $style; + } + + public function transform($tag, $config, $context) { + $new_tag = clone $tag; + $new_tag->name = $this->transform_to; + if (!is_null($this->style) && + ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty) + ) { + $this->prependCSS($new_tag->attr, $this->style); + } + return $new_tag; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token.php b/lib/php/HTMLPurifier/Token.php new file mode 100644 index 0000000..7900e6c --- /dev/null +++ b/lib/php/HTMLPurifier/Token.php @@ -0,0 +1,57 @@ +<?php + +/** + * Abstract base token class that all others inherit from. + */ +class HTMLPurifier_Token { + public $line; /**< Line number node was on in source document. Null if unknown. */ + public $col; /**< Column of line node was on in source document. Null if unknown. */ + + /** + * Lookup array of processing that this token is exempt from. + * Currently, valid values are "ValidateAttributes" and + * "MakeWellFormed_TagClosedError" + */ + public $armor = array(); + + /** + * Used during MakeWellFormed. + */ + public $skip; + public $rewind; + public $carryover; + + public function __get($n) { + if ($n === 'type') { + trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE); + switch (get_class($this)) { + case 'HTMLPurifier_Token_Start': return 'start'; + case 'HTMLPurifier_Token_Empty': return 'empty'; + case 'HTMLPurifier_Token_End': return 'end'; + case 'HTMLPurifier_Token_Text': return 'text'; + case 'HTMLPurifier_Token_Comment': return 'comment'; + default: return null; + } + } + } + + /** + * Sets the position of the token in the source document. + */ + public function position($l = null, $c = null) { + $this->line = $l; + $this->col = $c; + } + + /** + * Convenience function for DirectLex settings line/col position. + */ + public function rawPosition($l, $c) { + if ($c === -1) $l++; + $this->line = $l; + $this->col = $c; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token/Comment.php b/lib/php/HTMLPurifier/Token/Comment.php new file mode 100644 index 0000000..dc6bdca --- /dev/null +++ b/lib/php/HTMLPurifier/Token/Comment.php @@ -0,0 +1,22 @@ +<?php + +/** + * Concrete comment token class. Generally will be ignored. + */ +class HTMLPurifier_Token_Comment extends HTMLPurifier_Token +{ + public $data; /**< Character data within comment. */ + public $is_whitespace = true; + /** + * Transparent constructor. + * + * @param $data String comment data. + */ + public function __construct($data, $line = null, $col = null) { + $this->data = $data; + $this->line = $line; + $this->col = $col; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token/Empty.php b/lib/php/HTMLPurifier/Token/Empty.php new file mode 100644 index 0000000..2a82b47 --- /dev/null +++ b/lib/php/HTMLPurifier/Token/Empty.php @@ -0,0 +1,11 @@ +<?php + +/** + * Concrete empty token class. + */ +class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag +{ + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token/End.php b/lib/php/HTMLPurifier/Token/End.php new file mode 100644 index 0000000..353e79d --- /dev/null +++ b/lib/php/HTMLPurifier/Token/End.php @@ -0,0 +1,19 @@ +<?php + +/** + * Concrete end token class. + * + * @warning This class accepts attributes even though end tags cannot. This + * is for optimization reasons, as under normal circumstances, the Lexers + * do not pass attributes. + */ +class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag +{ + /** + * Token that started this node. Added by MakeWellFormed. Please + * do not edit this! + */ + public $start; +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token/Start.php b/lib/php/HTMLPurifier/Token/Start.php new file mode 100644 index 0000000..e0e14fc --- /dev/null +++ b/lib/php/HTMLPurifier/Token/Start.php @@ -0,0 +1,11 @@ +<?php + +/** + * Concrete start token class. + */ +class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag +{ + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token/Tag.php b/lib/php/HTMLPurifier/Token/Tag.php new file mode 100644 index 0000000..798be02 --- /dev/null +++ b/lib/php/HTMLPurifier/Token/Tag.php @@ -0,0 +1,56 @@ +<?php + +/** + * Abstract class of a tag token (start, end or empty), and its behavior. + */ +class HTMLPurifier_Token_Tag extends HTMLPurifier_Token +{ + /** + * Static bool marker that indicates the class is a tag. + * + * This allows us to check objects with <tt>!empty($obj->is_tag)</tt> + * without having to use a function call <tt>is_a()</tt>. + */ + public $is_tag = true; + + /** + * The lower-case name of the tag, like 'a', 'b' or 'blockquote'. + * + * @note Strictly speaking, XML tags are case sensitive, so we shouldn't + * be lower-casing them, but these tokens cater to HTML tags, which are + * insensitive. + */ + public $name; + + /** + * Associative array of the tag's attributes. + */ + public $attr = array(); + + /** + * Non-overloaded constructor, which lower-cases passed tag name. + * + * @param $name String name. + * @param $attr Associative array of attributes. + */ + public function __construct($name, $attr = array(), $line = null, $col = null) { + $this->name = ctype_lower($name) ? $name : strtolower($name); + foreach ($attr as $key => $value) { + // normalization only necessary when key is not lowercase + if (!ctype_lower($key)) { + $new_key = strtolower($key); + if (!isset($attr[$new_key])) { + $attr[$new_key] = $attr[$key]; + } + if ($new_key !== $key) { + unset($attr[$key]); + } + } + } + $this->attr = $attr; + $this->line = $line; + $this->col = $col; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/Token/Text.php b/lib/php/HTMLPurifier/Token/Text.php new file mode 100644 index 0000000..82efd82 --- /dev/null +++ b/lib/php/HTMLPurifier/Token/Text.php @@ -0,0 +1,33 @@ +<?php + +/** + * Concrete text token class. + * + * Text tokens comprise of regular parsed character data (PCDATA) and raw + * character data (from the CDATA sections). Internally, their + * data is parsed with all entities expanded. Surprisingly, the text token + * does have a "tag name" called #PCDATA, which is how the DTD represents it + * in permissible child nodes. + */ +class HTMLPurifier_Token_Text extends HTMLPurifier_Token +{ + + public $name = '#PCDATA'; /**< PCDATA tag name compatible with DTD. */ + public $data; /**< Parsed character data of text. */ + public $is_whitespace; /**< Bool indicating if node is whitespace. */ + + /** + * Constructor, accepts data and determines if it is whitespace. + * + * @param $data String parsed character data. + */ + public function __construct($data, $line = null, $col = null) { + $this->data = $data; + $this->is_whitespace = ctype_space($data); + $this->line = $line; + $this->col = $col; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/TokenFactory.php b/lib/php/HTMLPurifier/TokenFactory.php new file mode 100644 index 0000000..7cf48fb --- /dev/null +++ b/lib/php/HTMLPurifier/TokenFactory.php @@ -0,0 +1,94 @@ +<?php + +/** + * Factory for token generation. + * + * @note Doing some benchmarking indicates that the new operator is much + * slower than the clone operator (even discounting the cost of the + * constructor). This class is for that optimization. + * Other then that, there's not much point as we don't + * maintain parallel HTMLPurifier_Token hierarchies (the main reason why + * you'd want to use an abstract factory). + * @todo Port DirectLex to use this + */ +class HTMLPurifier_TokenFactory +{ + + /** + * Prototypes that will be cloned. + * @private + */ + // p stands for prototype + private $p_start, $p_end, $p_empty, $p_text, $p_comment; + + /** + * Generates blank prototypes for cloning. + */ + public function __construct() { + $this->p_start = new HTMLPurifier_Token_Start('', array()); + $this->p_end = new HTMLPurifier_Token_End(''); + $this->p_empty = new HTMLPurifier_Token_Empty('', array()); + $this->p_text = new HTMLPurifier_Token_Text(''); + $this->p_comment= new HTMLPurifier_Token_Comment(''); + } + + /** + * Creates a HTMLPurifier_Token_Start. + * @param $name Tag name + * @param $attr Associative array of attributes + * @return Generated HTMLPurifier_Token_Start + */ + public function createStart($name, $attr = array()) { + $p = clone $this->p_start; + $p->__construct($name, $attr); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_End. + * @param $name Tag name + * @return Generated HTMLPurifier_Token_End + */ + public function createEnd($name) { + $p = clone $this->p_end; + $p->__construct($name); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Empty. + * @param $name Tag name + * @param $attr Associative array of attributes + * @return Generated HTMLPurifier_Token_Empty + */ + public function createEmpty($name, $attr = array()) { + $p = clone $this->p_empty; + $p->__construct($name, $attr); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Text. + * @param $data Data of text token + * @return Generated HTMLPurifier_Token_Text + */ + public function createText($data) { + $p = clone $this->p_text; + $p->__construct($data); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Comment. + * @param $data Data of comment token + * @return Generated HTMLPurifier_Token_Comment + */ + public function createComment($data) { + $p = clone $this->p_comment; + $p->__construct($data); + return $p; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URI.php b/lib/php/HTMLPurifier/URI.php new file mode 100644 index 0000000..8b50d0d --- /dev/null +++ b/lib/php/HTMLPurifier/URI.php @@ -0,0 +1,173 @@ +<?php + +/** + * HTML Purifier's internal representation of a URI. + * @note + * Internal data-structures are completely escaped. If the data needs + * to be used in a non-URI context (which is very unlikely), be sure + * to decode it first. The URI may not necessarily be well-formed until + * validate() is called. + */ +class HTMLPurifier_URI +{ + + public $scheme, $userinfo, $host, $port, $path, $query, $fragment; + + /** + * @note Automatically normalizes scheme and port + */ + public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment) { + $this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme); + $this->userinfo = $userinfo; + $this->host = $host; + $this->port = is_null($port) ? $port : (int) $port; + $this->path = $path; + $this->query = $query; + $this->fragment = $fragment; + } + + /** + * Retrieves a scheme object corresponding to the URI's scheme/default + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return Scheme object appropriate for validating this URI + */ + public function getSchemeObj($config, $context) { + $registry = HTMLPurifier_URISchemeRegistry::instance(); + if ($this->scheme !== null) { + $scheme_obj = $registry->getScheme($this->scheme, $config, $context); + if (!$scheme_obj) return false; // invalid scheme, clean it out + } else { + // no scheme: retrieve the default one + $def = $config->getDefinition('URI'); + $scheme_obj = $registry->getScheme($def->defaultScheme, $config, $context); + if (!$scheme_obj) { + // something funky happened to the default scheme object + trigger_error( + 'Default scheme object "' . $def->defaultScheme . '" was not readable', + E_USER_WARNING + ); + return false; + } + } + return $scheme_obj; + } + + /** + * Generic validation method applicable for all schemes. May modify + * this URI in order to get it into a compliant form. + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return True if validation/filtering succeeds, false if failure + */ + public function validate($config, $context) { + + // ABNF definitions from RFC 3986 + $chars_sub_delims = '!$&\'()*+,;='; + $chars_gen_delims = ':/?#[]@'; + $chars_pchar = $chars_sub_delims . ':@'; + + // validate scheme (MUST BE FIRST!) + if (!is_null($this->scheme) && is_null($this->host)) { + $def = $config->getDefinition('URI'); + if ($def->defaultScheme === $this->scheme) { + $this->scheme = null; + } + } + + // validate host + if (!is_null($this->host)) { + $host_def = new HTMLPurifier_AttrDef_URI_Host(); + $this->host = $host_def->validate($this->host, $config, $context); + if ($this->host === false) $this->host = null; + } + + // validate username + if (!is_null($this->userinfo)) { + $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':'); + $this->userinfo = $encoder->encode($this->userinfo); + } + + // validate port + if (!is_null($this->port)) { + if ($this->port < 1 || $this->port > 65535) $this->port = null; + } + + // validate path + $path_parts = array(); + $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/'); + if (!is_null($this->host)) { + // path-abempty (hier and relative) + $this->path = $segments_encoder->encode($this->path); + } elseif ($this->path !== '' && $this->path[0] === '/') { + // path-absolute (hier and relative) + if (strlen($this->path) >= 2 && $this->path[1] === '/') { + // This shouldn't ever happen! + $this->path = ''; + } else { + $this->path = $segments_encoder->encode($this->path); + } + } elseif (!is_null($this->scheme) && $this->path !== '') { + // path-rootless (hier) + // Short circuit evaluation means we don't need to check nz + $this->path = $segments_encoder->encode($this->path); + } elseif (is_null($this->scheme) && $this->path !== '') { + // path-noscheme (relative) + // (once again, not checking nz) + $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@'); + $c = strpos($this->path, '/'); + if ($c !== false) { + $this->path = + $segment_nc_encoder->encode(substr($this->path, 0, $c)) . + $segments_encoder->encode(substr($this->path, $c)); + } else { + $this->path = $segment_nc_encoder->encode($this->path); + } + } else { + // path-empty (hier and relative) + $this->path = ''; // just to be safe + } + + // qf = query and fragment + $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?'); + + if (!is_null($this->query)) { + $this->query = $qf_encoder->encode($this->query); + } + + if (!is_null($this->fragment)) { + $this->fragment = $qf_encoder->encode($this->fragment); + } + + return true; + + } + + /** + * Convert URI back to string + * @return String URI appropriate for output + */ + public function toString() { + // reconstruct authority + $authority = null; + if (!is_null($this->host)) { + $authority = ''; + if(!is_null($this->userinfo)) $authority .= $this->userinfo . '@'; + $authority .= $this->host; + if(!is_null($this->port)) $authority .= ':' . $this->port; + } + + // reconstruct the result + $result = ''; + if (!is_null($this->scheme)) $result .= $this->scheme . ':'; + if (!is_null($authority)) $result .= '//' . $authority; + $result .= $this->path; + if (!is_null($this->query)) $result .= '?' . $this->query; + if (!is_null($this->fragment)) $result .= '#' . $this->fragment; + + return $result; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIDefinition.php b/lib/php/HTMLPurifier/URIDefinition.php new file mode 100644 index 0000000..ea2b8fe --- /dev/null +++ b/lib/php/HTMLPurifier/URIDefinition.php @@ -0,0 +1,93 @@ +<?php + +class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition +{ + + public $type = 'URI'; + protected $filters = array(); + protected $postFilters = array(); + protected $registeredFilters = array(); + + /** + * HTMLPurifier_URI object of the base specified at %URI.Base + */ + public $base; + + /** + * String host to consider "home" base, derived off of $base + */ + public $host; + + /** + * Name of default scheme based on %URI.DefaultScheme and %URI.Base + */ + public $defaultScheme; + + public function __construct() { + $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal()); + $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources()); + $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist()); + $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute()); + $this->registerFilter(new HTMLPurifier_URIFilter_Munge()); + } + + public function registerFilter($filter) { + $this->registeredFilters[$filter->name] = $filter; + } + + public function addFilter($filter, $config) { + $r = $filter->prepare($config); + if ($r === false) return; // null is ok, for backwards compat + if ($filter->post) { + $this->postFilters[$filter->name] = $filter; + } else { + $this->filters[$filter->name] = $filter; + } + } + + protected function doSetup($config) { + $this->setupMemberVariables($config); + $this->setupFilters($config); + } + + protected function setupFilters($config) { + foreach ($this->registeredFilters as $name => $filter) { + $conf = $config->get('URI.' . $name); + if ($conf !== false && $conf !== null) { + $this->addFilter($filter, $config); + } + } + unset($this->registeredFilters); + } + + protected function setupMemberVariables($config) { + $this->host = $config->get('URI.Host'); + $base_uri = $config->get('URI.Base'); + if (!is_null($base_uri)) { + $parser = new HTMLPurifier_URIParser(); + $this->base = $parser->parse($base_uri); + $this->defaultScheme = $this->base->scheme; + if (is_null($this->host)) $this->host = $this->base->host; + } + if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme'); + } + + public function filter(&$uri, $config, $context) { + foreach ($this->filters as $name => $f) { + $result = $f->filter($uri, $config, $context); + if (!$result) return false; + } + return true; + } + + public function postFilter(&$uri, $config, $context) { + foreach ($this->postFilters as $name => $f) { + $result = $f->filter($uri, $config, $context); + if (!$result) return false; + } + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIFilter.php b/lib/php/HTMLPurifier/URIFilter.php new file mode 100644 index 0000000..c116f93 --- /dev/null +++ b/lib/php/HTMLPurifier/URIFilter.php @@ -0,0 +1,45 @@ +<?php + +/** + * Chainable filters for custom URI processing. + * + * These filters can perform custom actions on a URI filter object, + * including transformation or blacklisting. + * + * @warning This filter is called before scheme object validation occurs. + * Make sure, if you require a specific scheme object, you + * you check that it exists. This allows filters to convert + * proprietary URI schemes into regular ones. + */ +abstract class HTMLPurifier_URIFilter +{ + + /** + * Unique identifier of filter + */ + public $name; + + /** + * True if this filter should be run after scheme validation. + */ + public $post = false; + + /** + * Performs initialization for the filter + */ + public function prepare($config) {return true;} + + /** + * Filter a URI object + * @param $uri Reference to URI object variable + * @param $config Instance of HTMLPurifier_Config + * @param $context Instance of HTMLPurifier_Context + * @return bool Whether or not to continue processing: false indicates + * URL is no good, true indicates continue processing. Note that + * all changes are committed directly on the URI object + */ + abstract public function filter(&$uri, $config, $context); + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIFilter/DisableExternal.php b/lib/php/HTMLPurifier/URIFilter/DisableExternal.php new file mode 100644 index 0000000..d8a39a5 --- /dev/null +++ b/lib/php/HTMLPurifier/URIFilter/DisableExternal.php @@ -0,0 +1,23 @@ +<?php + +class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter +{ + public $name = 'DisableExternal'; + protected $ourHostParts = false; + public function prepare($config) { + $our_host = $config->getDefinition('URI')->host; + if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host)); + } + public function filter(&$uri, $config, $context) { + if (is_null($uri->host)) return true; + if ($this->ourHostParts === false) return false; + $host_parts = array_reverse(explode('.', $uri->host)); + foreach ($this->ourHostParts as $i => $x) { + if (!isset($host_parts[$i])) return false; + if ($host_parts[$i] != $this->ourHostParts[$i]) return false; + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIFilter/DisableExternalResources.php b/lib/php/HTMLPurifier/URIFilter/DisableExternalResources.php new file mode 100644 index 0000000..881abc4 --- /dev/null +++ b/lib/php/HTMLPurifier/URIFilter/DisableExternalResources.php @@ -0,0 +1,12 @@ +<?php + +class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal +{ + public $name = 'DisableExternalResources'; + public function filter(&$uri, $config, $context) { + if (!$context->get('EmbeddedURI', true)) return true; + return parent::filter($uri, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIFilter/HostBlacklist.php b/lib/php/HTMLPurifier/URIFilter/HostBlacklist.php new file mode 100644 index 0000000..045aa09 --- /dev/null +++ b/lib/php/HTMLPurifier/URIFilter/HostBlacklist.php @@ -0,0 +1,21 @@ +<?php + +class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter +{ + public $name = 'HostBlacklist'; + protected $blacklist = array(); + public function prepare($config) { + $this->blacklist = $config->get('URI.HostBlacklist'); + return true; + } + public function filter(&$uri, $config, $context) { + foreach($this->blacklist as $blacklisted_host_fragment) { + if (strpos($uri->host, $blacklisted_host_fragment) !== false) { + return false; + } + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIFilter/MakeAbsolute.php b/lib/php/HTMLPurifier/URIFilter/MakeAbsolute.php new file mode 100644 index 0000000..f46ab26 --- /dev/null +++ b/lib/php/HTMLPurifier/URIFilter/MakeAbsolute.php @@ -0,0 +1,114 @@ +<?php + +// does not support network paths + +class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter +{ + public $name = 'MakeAbsolute'; + protected $base; + protected $basePathStack = array(); + public function prepare($config) { + $def = $config->getDefinition('URI'); + $this->base = $def->base; + if (is_null($this->base)) { + trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING); + return false; + } + $this->base->fragment = null; // fragment is invalid for base URI + $stack = explode('/', $this->base->path); + array_pop($stack); // discard last segment + $stack = $this->_collapseStack($stack); // do pre-parsing + $this->basePathStack = $stack; + return true; + } + public function filter(&$uri, $config, $context) { + if (is_null($this->base)) return true; // abort early + if ( + $uri->path === '' && is_null($uri->scheme) && + is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment) + ) { + // reference to current document + $uri = clone $this->base; + return true; + } + if (!is_null($uri->scheme)) { + // absolute URI already: don't change + if (!is_null($uri->host)) return true; + $scheme_obj = $uri->getSchemeObj($config, $context); + if (!$scheme_obj) { + // scheme not recognized + return false; + } + if (!$scheme_obj->hierarchical) { + // non-hierarchal URI with explicit scheme, don't change + return true; + } + // special case: had a scheme but always is hierarchical and had no authority + } + if (!is_null($uri->host)) { + // network path, don't bother + return true; + } + if ($uri->path === '') { + $uri->path = $this->base->path; + } elseif ($uri->path[0] !== '/') { + // relative path, needs more complicated processing + $stack = explode('/', $uri->path); + $new_stack = array_merge($this->basePathStack, $stack); + if ($new_stack[0] !== '' && !is_null($this->base->host)) { + array_unshift($new_stack, ''); + } + $new_stack = $this->_collapseStack($new_stack); + $uri->path = implode('/', $new_stack); + } else { + // absolute path, but still we should collapse + $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path))); + } + // re-combine + $uri->scheme = $this->base->scheme; + if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo; + if (is_null($uri->host)) $uri->host = $this->base->host; + if (is_null($uri->port)) $uri->port = $this->base->port; + return true; + } + + /** + * Resolve dots and double-dots in a path stack + */ + private function _collapseStack($stack) { + $result = array(); + $is_folder = false; + for ($i = 0; isset($stack[$i]); $i++) { + $is_folder = false; + // absorb an internally duplicated slash + if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue; + if ($stack[$i] == '..') { + if (!empty($result)) { + $segment = array_pop($result); + if ($segment === '' && empty($result)) { + // error case: attempted to back out too far: + // restore the leading slash + $result[] = ''; + } elseif ($segment === '..') { + $result[] = '..'; // cannot remove .. with .. + } + } else { + // relative path, preserve the double-dots + $result[] = '..'; + } + $is_folder = true; + continue; + } + if ($stack[$i] == '.') { + // silently absorb + $is_folder = true; + continue; + } + $result[] = $stack[$i]; + } + if ($is_folder) $result[] = ''; + return $result; + } +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIFilter/Munge.php b/lib/php/HTMLPurifier/URIFilter/Munge.php new file mode 100644 index 0000000..efa10a6 --- /dev/null +++ b/lib/php/HTMLPurifier/URIFilter/Munge.php @@ -0,0 +1,58 @@ +<?php + +class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter +{ + public $name = 'Munge'; + public $post = true; + private $target, $parser, $doEmbed, $secretKey; + + protected $replace = array(); + + public function prepare($config) { + $this->target = $config->get('URI.' . $this->name); + $this->parser = new HTMLPurifier_URIParser(); + $this->doEmbed = $config->get('URI.MungeResources'); + $this->secretKey = $config->get('URI.MungeSecretKey'); + return true; + } + public function filter(&$uri, $config, $context) { + if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true; + + $scheme_obj = $uri->getSchemeObj($config, $context); + if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it + if (is_null($uri->host) || empty($scheme_obj->browsable)) { + return true; + } + // don't redirect if target host is our host + if ($uri->host === $config->getDefinition('URI')->host) { + return true; + } + + $this->makeReplace($uri, $config, $context); + $this->replace = array_map('rawurlencode', $this->replace); + + $new_uri = strtr($this->target, $this->replace); + $new_uri = $this->parser->parse($new_uri); + // don't redirect if the target host is the same as the + // starting host + if ($uri->host === $new_uri->host) return true; + $uri = $new_uri; // overwrite + return true; + } + + protected function makeReplace($uri, $config, $context) { + $string = $uri->toString(); + // always available + $this->replace['%s'] = $string; + $this->replace['%r'] = $context->get('EmbeddedURI', true); + $token = $context->get('CurrentToken', true); + $this->replace['%n'] = $token ? $token->name : null; + $this->replace['%m'] = $context->get('CurrentAttr', true); + $this->replace['%p'] = $context->get('CurrentCSSProperty', true); + // not always available + if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIParser.php b/lib/php/HTMLPurifier/URIParser.php new file mode 100644 index 0000000..7179e4a --- /dev/null +++ b/lib/php/HTMLPurifier/URIParser.php @@ -0,0 +1,70 @@ +<?php + +/** + * Parses a URI into the components and fragment identifier as specified + * by RFC 3986. + */ +class HTMLPurifier_URIParser +{ + + /** + * Instance of HTMLPurifier_PercentEncoder to do normalization with. + */ + protected $percentEncoder; + + public function __construct() { + $this->percentEncoder = new HTMLPurifier_PercentEncoder(); + } + + /** + * Parses a URI. + * @param $uri string URI to parse + * @return HTMLPurifier_URI representation of URI. This representation has + * not been validated yet and may not conform to RFC. + */ + public function parse($uri) { + + $uri = $this->percentEncoder->normalize($uri); + + // Regexp is as per Appendix B. + // Note that ["<>] are an addition to the RFC's recommended + // characters, because they represent external delimeters. + $r_URI = '!'. + '(([^:/?#"<>]+):)?'. // 2. Scheme + '(//([^/?#"<>]*))?'. // 4. Authority + '([^?#"<>]*)'. // 5. Path + '(\?([^#"<>]*))?'. // 7. Query + '(#([^"<>]*))?'. // 8. Fragment + '!'; + + $matches = array(); + $result = preg_match($r_URI, $uri, $matches); + + if (!$result) return false; // *really* invalid URI + + // seperate out parts + $scheme = !empty($matches[1]) ? $matches[2] : null; + $authority = !empty($matches[3]) ? $matches[4] : null; + $path = $matches[5]; // always present, can be empty + $query = !empty($matches[6]) ? $matches[7] : null; + $fragment = !empty($matches[8]) ? $matches[9] : null; + + // further parse authority + if ($authority !== null) { + $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/"; + $matches = array(); + preg_match($r_authority, $authority, $matches); + $userinfo = !empty($matches[1]) ? $matches[2] : null; + $host = !empty($matches[3]) ? $matches[3] : ''; + $port = !empty($matches[4]) ? (int) $matches[5] : null; + } else { + $port = $host = $userinfo = null; + } + + return new HTMLPurifier_URI( + $scheme, $userinfo, $host, $port, $path, $query, $fragment); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme.php b/lib/php/HTMLPurifier/URIScheme.php new file mode 100644 index 0000000..039710f --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme.php @@ -0,0 +1,42 @@ +<?php + +/** + * Validator for the components of a URI for a specific scheme + */ +class HTMLPurifier_URIScheme +{ + + /** + * Scheme's default port (integer) + */ + public $default_port = null; + + /** + * Whether or not URIs of this schem are locatable by a browser + * http and ftp are accessible, while mailto and news are not. + */ + public $browsable = false; + + /** + * Whether or not the URI always uses <hier_part>, resolves edge cases + * with making relative URIs absolute + */ + public $hierarchical = false; + + /** + * Validates the components of a URI + * @note This implementation should be called by children if they define + * a default port, as it does port processing. + * @param $uri Instance of HTMLPurifier_URI + * @param $config HTMLPurifier_Config object + * @param $context HTMLPurifier_Context object + * @return Bool success or failure + */ + public function validate(&$uri, $config, $context) { + if ($this->default_port == $uri->port) $uri->port = null; + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme/ftp.php b/lib/php/HTMLPurifier/URIScheme/ftp.php new file mode 100644 index 0000000..5849bf7 --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme/ftp.php @@ -0,0 +1,43 @@ +<?php + +/** + * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738. + */ +class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme { + + public $default_port = 21; + public $browsable = true; // usually + public $hierarchical = true; + + public function validate(&$uri, $config, $context) { + parent::validate($uri, $config, $context); + $uri->query = null; + + // typecode check + $semicolon_pos = strrpos($uri->path, ';'); // reverse + if ($semicolon_pos !== false) { + $type = substr($uri->path, $semicolon_pos + 1); // no semicolon + $uri->path = substr($uri->path, 0, $semicolon_pos); + $type_ret = ''; + if (strpos($type, '=') !== false) { + // figure out whether or not the declaration is correct + list($key, $typecode) = explode('=', $type, 2); + if ($key !== 'type') { + // invalid key, tack it back on encoded + $uri->path .= '%3B' . $type; + } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') { + $type_ret = ";type=$typecode"; + } + } else { + $uri->path .= '%3B' . $type; + } + $uri->path = str_replace(';', '%3B', $uri->path); + $uri->path .= $type_ret; + } + + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme/http.php b/lib/php/HTMLPurifier/URIScheme/http.php new file mode 100644 index 0000000..b097a31 --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme/http.php @@ -0,0 +1,20 @@ +<?php + +/** + * Validates http (HyperText Transfer Protocol) as defined by RFC 2616 + */ +class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme { + + public $default_port = 80; + public $browsable = true; + public $hierarchical = true; + + public function validate(&$uri, $config, $context) { + parent::validate($uri, $config, $context); + $uri->userinfo = null; + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme/https.php b/lib/php/HTMLPurifier/URIScheme/https.php new file mode 100644 index 0000000..29e3809 --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme/https.php @@ -0,0 +1,12 @@ +<?php + +/** + * Validates https (Secure HTTP) according to http scheme. + */ +class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http { + + public $default_port = 443; + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme/mailto.php b/lib/php/HTMLPurifier/URIScheme/mailto.php new file mode 100644 index 0000000..c1e2cd5 --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme/mailto.php @@ -0,0 +1,27 @@ +<?php + +// VERY RELAXED! Shouldn't cause problems, not even Firefox checks if the +// email is valid, but be careful! + +/** + * Validates mailto (for E-mail) according to RFC 2368 + * @todo Validate the email address + * @todo Filter allowed query parameters + */ + +class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme { + + public $browsable = false; + + public function validate(&$uri, $config, $context) { + parent::validate($uri, $config, $context); + $uri->userinfo = null; + $uri->host = null; + $uri->port = null; + // we need to validate path against RFC 2368's addr-spec + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme/news.php b/lib/php/HTMLPurifier/URIScheme/news.php new file mode 100644 index 0000000..f5f54f4 --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme/news.php @@ -0,0 +1,22 @@ +<?php + +/** + * Validates news (Usenet) as defined by generic RFC 1738 + */ +class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme { + + public $browsable = false; + + public function validate(&$uri, $config, $context) { + parent::validate($uri, $config, $context); + $uri->userinfo = null; + $uri->host = null; + $uri->port = null; + $uri->query = null; + // typecode check needed on path + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URIScheme/nntp.php b/lib/php/HTMLPurifier/URIScheme/nntp.php new file mode 100644 index 0000000..5bf93ea --- /dev/null +++ b/lib/php/HTMLPurifier/URIScheme/nntp.php @@ -0,0 +1,20 @@ +<?php + +/** + * Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738 + */ +class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme { + + public $default_port = 119; + public $browsable = false; + + public function validate(&$uri, $config, $context) { + parent::validate($uri, $config, $context); + $uri->userinfo = null; + $uri->query = null; + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/URISchemeRegistry.php b/lib/php/HTMLPurifier/URISchemeRegistry.php new file mode 100644 index 0000000..576bf7b --- /dev/null +++ b/lib/php/HTMLPurifier/URISchemeRegistry.php @@ -0,0 +1,68 @@ +<?php + +/** + * Registry for retrieving specific URI scheme validator objects. + */ +class HTMLPurifier_URISchemeRegistry +{ + + /** + * Retrieve sole instance of the registry. + * @param $prototype Optional prototype to overload sole instance with, + * or bool true to reset to default registry. + * @note Pass a registry object $prototype with a compatible interface and + * the function will copy it and return it all further times. + */ + public static function instance($prototype = null) { + static $instance = null; + if ($prototype !== null) { + $instance = $prototype; + } elseif ($instance === null || $prototype == true) { + $instance = new HTMLPurifier_URISchemeRegistry(); + } + return $instance; + } + + /** + * Cache of retrieved schemes. + */ + protected $schemes = array(); + + /** + * Retrieves a scheme validator object + * @param $scheme String scheme name like http or mailto + * @param $config HTMLPurifier_Config object + * @param $config HTMLPurifier_Context object + */ + public function getScheme($scheme, $config, $context) { + if (!$config) $config = HTMLPurifier_Config::createDefault(); + + // important, otherwise attacker could include arbitrary file + $allowed_schemes = $config->get('URI.AllowedSchemes'); + if (!$config->get('URI.OverrideAllowedSchemes') && + !isset($allowed_schemes[$scheme]) + ) { + return; + } + + if (isset($this->schemes[$scheme])) return $this->schemes[$scheme]; + if (!isset($allowed_schemes[$scheme])) return; + + $class = 'HTMLPurifier_URIScheme_' . $scheme; + if (!class_exists($class)) return; + $this->schemes[$scheme] = new $class(); + return $this->schemes[$scheme]; + } + + /** + * Registers a custom scheme to the cache, bypassing reflection. + * @param $scheme Scheme name + * @param $scheme_obj HTMLPurifier_URIScheme object + */ + public function register($scheme, $scheme_obj) { + $this->schemes[$scheme] = $scheme_obj; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/UnitConverter.php b/lib/php/HTMLPurifier/UnitConverter.php new file mode 100644 index 0000000..545d426 --- /dev/null +++ b/lib/php/HTMLPurifier/UnitConverter.php @@ -0,0 +1,254 @@ +<?php + +/** + * Class for converting between different unit-lengths as specified by + * CSS. + */ +class HTMLPurifier_UnitConverter +{ + + const ENGLISH = 1; + const METRIC = 2; + const DIGITAL = 3; + + /** + * Units information array. Units are grouped into measuring systems + * (English, Metric), and are assigned an integer representing + * the conversion factor between that unit and the smallest unit in + * the system. Numeric indexes are actually magical constants that + * encode conversion data from one system to the next, with a O(n^2) + * constraint on memory (this is generally not a problem, since + * the number of measuring systems is small.) + */ + protected static $units = array( + self::ENGLISH => array( + 'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary + 'pt' => 4, + 'pc' => 48, + 'in' => 288, + self::METRIC => array('pt', '0.352777778', 'mm'), + ), + self::METRIC => array( + 'mm' => 1, + 'cm' => 10, + self::ENGLISH => array('mm', '2.83464567', 'pt'), + ), + ); + + /** + * Minimum bcmath precision for output. + */ + protected $outputPrecision; + + /** + * Bcmath precision for internal calculations. + */ + protected $internalPrecision; + + /** + * Whether or not BCMath is available + */ + private $bcmath; + + public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) { + $this->outputPrecision = $output_precision; + $this->internalPrecision = $internal_precision; + $this->bcmath = !$force_no_bcmath && function_exists('bcmul'); + } + + /** + * Converts a length object of one unit into another unit. + * @param HTMLPurifier_Length $length + * Instance of HTMLPurifier_Length to convert. You must validate() + * it before passing it here! + * @param string $to_unit + * Unit to convert to. + * @note + * About precision: This conversion function pays very special + * attention to the incoming precision of values and attempts + * to maintain a number of significant figure. Results are + * fairly accurate up to nine digits. Some caveats: + * - If a number is zero-padded as a result of this significant + * figure tracking, the zeroes will be eliminated. + * - If a number contains less than four sigfigs ($outputPrecision) + * and this causes some decimals to be excluded, those + * decimals will be added on. + */ + public function convert($length, $to_unit) { + + if (!$length->isValid()) return false; + + $n = $length->getN(); + $unit = $length->getUnit(); + + if ($n === '0' || $unit === false) { + return new HTMLPurifier_Length('0', false); + } + + $state = $dest_state = false; + foreach (self::$units as $k => $x) { + if (isset($x[$unit])) $state = $k; + if (isset($x[$to_unit])) $dest_state = $k; + } + if (!$state || !$dest_state) return false; + + // Some calculations about the initial precision of the number; + // this will be useful when we need to do final rounding. + $sigfigs = $this->getSigFigs($n); + if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision; + + // BCMath's internal precision deals only with decimals. Use + // our default if the initial number has no decimals, or increase + // it by how ever many decimals, thus, the number of guard digits + // will always be greater than or equal to internalPrecision. + $log = (int) floor(log(abs($n), 10)); + $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision + + for ($i = 0; $i < 2; $i++) { + + // Determine what unit IN THIS SYSTEM we need to convert to + if ($dest_state === $state) { + // Simple conversion + $dest_unit = $to_unit; + } else { + // Convert to the smallest unit, pending a system shift + $dest_unit = self::$units[$state][$dest_state][0]; + } + + // Do the conversion if necessary + if ($dest_unit !== $unit) { + $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp); + $n = $this->mul($n, $factor, $cp); + $unit = $dest_unit; + } + + // Output was zero, so bail out early. Shouldn't ever happen. + if ($n === '') { + $n = '0'; + $unit = $to_unit; + break; + } + + // It was a simple conversion, so bail out + if ($dest_state === $state) { + break; + } + + if ($i !== 0) { + // Conversion failed! Apparently, the system we forwarded + // to didn't have this unit. This should never happen! + return false; + } + + // Pre-condition: $i == 0 + + // Perform conversion to next system of units + $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp); + $unit = self::$units[$state][$dest_state][2]; + $state = $dest_state; + + // One more loop around to convert the unit in the new system. + + } + + // Post-condition: $unit == $to_unit + if ($unit !== $to_unit) return false; + + // Useful for debugging: + //echo "<pre>n"; + //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n"; + + $n = $this->round($n, $sigfigs); + if (strpos($n, '.') !== false) $n = rtrim($n, '0'); + $n = rtrim($n, '.'); + + return new HTMLPurifier_Length($n, $unit); + } + + /** + * Returns the number of significant figures in a string number. + * @param string $n Decimal number + * @return int number of sigfigs + */ + public function getSigFigs($n) { + $n = ltrim($n, '0+-'); + $dp = strpos($n, '.'); // decimal position + if ($dp === false) { + $sigfigs = strlen(rtrim($n, '0')); + } else { + $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character + if ($dp !== 0) $sigfigs--; + } + return $sigfigs; + } + + /** + * Adds two numbers, using arbitrary precision when available. + */ + private function add($s1, $s2, $scale) { + if ($this->bcmath) return bcadd($s1, $s2, $scale); + else return $this->scale($s1 + $s2, $scale); + } + + /** + * Multiples two numbers, using arbitrary precision when available. + */ + private function mul($s1, $s2, $scale) { + if ($this->bcmath) return bcmul($s1, $s2, $scale); + else return $this->scale($s1 * $s2, $scale); + } + + /** + * Divides two numbers, using arbitrary precision when available. + */ + private function div($s1, $s2, $scale) { + if ($this->bcmath) return bcdiv($s1, $s2, $scale); + else return $this->scale($s1 / $s2, $scale); + } + + /** + * Rounds a number according to the number of sigfigs it should have, + * using arbitrary precision when available. + */ + private function round($n, $sigfigs) { + $new_log = (int) floor(log(abs($n), 10)); // Number of digits left of decimal - 1 + $rp = $sigfigs - $new_log - 1; // Number of decimal places needed + $neg = $n < 0 ? '-' : ''; // Negative sign + if ($this->bcmath) { + if ($rp >= 0) { + $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); + $n = bcdiv($n, '1', $rp); + } else { + // This algorithm partially depends on the standardized + // form of numbers that comes out of bcmath. + $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0); + $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1); + } + return $n; + } else { + return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1); + } + } + + /** + * Scales a float to $scale digits right of decimal point, like BCMath. + */ + private function scale($r, $scale) { + if ($scale < 0) { + // The f sprintf type doesn't support negative numbers, so we + // need to cludge things manually. First get the string. + $r = sprintf('%.0f', (float) $r); + // Due to floating point precision loss, $r will more than likely + // look something like 4652999999999.9234. We grab one more digit + // than we need to precise from $r and then use that to round + // appropriately. + $precise = (string) round(substr($r, 0, strlen($r) + $scale), -1); + // Now we return it, truncating the zero that was rounded off. + return substr($precise, 0, -1) . str_repeat('0', -$scale + 1); + } + return sprintf('%.' . $scale . 'f', (float) $r); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/VarParser.php b/lib/php/HTMLPurifier/VarParser.php new file mode 100644 index 0000000..68e72ae --- /dev/null +++ b/lib/php/HTMLPurifier/VarParser.php @@ -0,0 +1,154 @@ +<?php + +/** + * Parses string representations into their corresponding native PHP + * variable type. The base implementation does a simple type-check. + */ +class HTMLPurifier_VarParser +{ + + const STRING = 1; + const ISTRING = 2; + const TEXT = 3; + const ITEXT = 4; + const INT = 5; + const FLOAT = 6; + const BOOL = 7; + const LOOKUP = 8; + const ALIST = 9; + const HASH = 10; + const MIXED = 11; + + /** + * Lookup table of allowed types. Mainly for backwards compatibility, but + * also convenient for transforming string type names to the integer constants. + */ + static public $types = array( + 'string' => self::STRING, + 'istring' => self::ISTRING, + 'text' => self::TEXT, + 'itext' => self::ITEXT, + 'int' => self::INT, + 'float' => self::FLOAT, + 'bool' => self::BOOL, + 'lookup' => self::LOOKUP, + 'list' => self::ALIST, + 'hash' => self::HASH, + 'mixed' => self::MIXED + ); + + /** + * Lookup table of types that are string, and can have aliases or + * allowed value lists. + */ + static public $stringTypes = array( + self::STRING => true, + self::ISTRING => true, + self::TEXT => true, + self::ITEXT => true, + ); + + /** + * Validate a variable according to type. Throws + * HTMLPurifier_VarParserException if invalid. + * It may return NULL as a valid type if $allow_null is true. + * + * @param $var Variable to validate + * @param $type Type of variable, see HTMLPurifier_VarParser->types + * @param $allow_null Whether or not to permit null as a value + * @return Validated and type-coerced variable + */ + final public function parse($var, $type, $allow_null = false) { + if (is_string($type)) { + if (!isset(HTMLPurifier_VarParser::$types[$type])) { + throw new HTMLPurifier_VarParserException("Invalid type '$type'"); + } else { + $type = HTMLPurifier_VarParser::$types[$type]; + } + } + $var = $this->parseImplementation($var, $type, $allow_null); + if ($allow_null && $var === null) return null; + // These are basic checks, to make sure nothing horribly wrong + // happened in our implementations. + switch ($type) { + case (self::STRING): + case (self::ISTRING): + case (self::TEXT): + case (self::ITEXT): + if (!is_string($var)) break; + if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var); + return $var; + case (self::INT): + if (!is_int($var)) break; + return $var; + case (self::FLOAT): + if (!is_float($var)) break; + return $var; + case (self::BOOL): + if (!is_bool($var)) break; + return $var; + case (self::LOOKUP): + case (self::ALIST): + case (self::HASH): + if (!is_array($var)) break; + if ($type === self::LOOKUP) { + foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true'); + } elseif ($type === self::ALIST) { + $keys = array_keys($var); + if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform'); + } + return $var; + case (self::MIXED): + return $var; + default: + $this->errorInconsistent(get_class($this), $type); + } + $this->errorGeneric($var, $type); + } + + /** + * Actually implements the parsing. Base implementation is to not + * do anything to $var. Subclasses should overload this! + */ + protected function parseImplementation($var, $type, $allow_null) { + return $var; + } + + /** + * Throws an exception. + */ + protected function error($msg) { + throw new HTMLPurifier_VarParserException($msg); + } + + /** + * Throws an inconsistency exception. + * @note This should not ever be called. It would be called if we + * extend the allowed values of HTMLPurifier_VarParser without + * updating subclasses. + */ + protected function errorInconsistent($class, $type) { + throw new HTMLPurifier_Exception("Inconsistency in $class: ".HTMLPurifier_VarParser::getTypeName($type)." not implemented"); + } + + /** + * Generic error for if a type didn't work. + */ + protected function errorGeneric($var, $type) { + $vtype = gettype($var); + $this->error("Expected type ".HTMLPurifier_VarParser::getTypeName($type).", got $vtype"); + } + + static public function getTypeName($type) { + static $lookup; + if (!$lookup) { + // Lazy load the alternative lookup table + $lookup = array_flip(HTMLPurifier_VarParser::$types); + } + if (!isset($lookup[$type])) return 'unknown'; + return $lookup[$type]; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/VarParser/Flexible.php b/lib/php/HTMLPurifier/VarParser/Flexible.php new file mode 100644 index 0000000..c954250 --- /dev/null +++ b/lib/php/HTMLPurifier/VarParser/Flexible.php @@ -0,0 +1,96 @@ +<?php + +/** + * Performs safe variable parsing based on types which can be used by + * users. This may not be able to represent all possible data inputs, + * however. + */ +class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser +{ + + protected function parseImplementation($var, $type, $allow_null) { + if ($allow_null && $var === null) return null; + switch ($type) { + // Note: if code "breaks" from the switch, it triggers a generic + // exception to be thrown. Specific errors can be specifically + // done here. + case self::MIXED : + case self::ISTRING : + case self::STRING : + case self::TEXT : + case self::ITEXT : + return $var; + case self::INT : + if (is_string($var) && ctype_digit($var)) $var = (int) $var; + return $var; + case self::FLOAT : + if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var; + return $var; + case self::BOOL : + if (is_int($var) && ($var === 0 || $var === 1)) { + $var = (bool) $var; + } elseif (is_string($var)) { + if ($var == 'on' || $var == 'true' || $var == '1') { + $var = true; + } elseif ($var == 'off' || $var == 'false' || $var == '0') { + $var = false; + } else { + throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type"); + } + } + return $var; + case self::ALIST : + case self::HASH : + case self::LOOKUP : + if (is_string($var)) { + // special case: technically, this is an array with + // a single empty string item, but having an empty + // array is more intuitive + if ($var == '') return array(); + if (strpos($var, "\n") === false && strpos($var, "\r") === false) { + // simplistic string to array method that only works + // for simple lists of tag names or alphanumeric characters + $var = explode(',',$var); + } else { + $var = preg_split('/(,|[\n\r]+)/', $var); + } + // remove spaces + foreach ($var as $i => $j) $var[$i] = trim($j); + if ($type === self::HASH) { + // key:value,key2:value2 + $nvar = array(); + foreach ($var as $keypair) { + $c = explode(':', $keypair, 2); + if (!isset($c[1])) continue; + $nvar[$c[0]] = $c[1]; + } + $var = $nvar; + } + } + if (!is_array($var)) break; + $keys = array_keys($var); + if ($keys === array_keys($keys)) { + if ($type == self::ALIST) return $var; + elseif ($type == self::LOOKUP) { + $new = array(); + foreach ($var as $key) { + $new[$key] = true; + } + return $new; + } else break; + } + if ($type === self::LOOKUP) { + foreach ($var as $key => $value) { + $var[$key] = true; + } + } + return $var; + default: + $this->errorInconsistent(__CLASS__, $type); + } + $this->errorGeneric($var, $type); + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/VarParser/Native.php b/lib/php/HTMLPurifier/VarParser/Native.php new file mode 100644 index 0000000..b02a6de --- /dev/null +++ b/lib/php/HTMLPurifier/VarParser/Native.php @@ -0,0 +1,26 @@ +<?php + +/** + * This variable parser uses PHP's internal code engine. Because it does + * this, it can represent all inputs; however, it is dangerous and cannot + * be used by users. + */ +class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser +{ + + protected function parseImplementation($var, $type, $allow_null) { + return $this->evalExpression($var); + } + + protected function evalExpression($expr) { + $var = null; + $result = eval("\$var = $expr;"); + if ($result === false) { + throw new HTMLPurifier_VarParserException("Fatal error in evaluated code"); + } + return $var; + } + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/VarParserException.php b/lib/php/HTMLPurifier/VarParserException.php new file mode 100644 index 0000000..5df3414 --- /dev/null +++ b/lib/php/HTMLPurifier/VarParserException.php @@ -0,0 +1,11 @@ +<?php + +/** + * Exception type for HTMLPurifier_VarParser + */ +class HTMLPurifier_VarParserException extends HTMLPurifier_Exception +{ + +} + +// vim: et sw=4 sts=4 diff --git a/lib/php/HTMLPurifier/tags b/lib/php/HTMLPurifier/tags new file mode 100644 index 0000000..189f18b --- /dev/null +++ b/lib/php/HTMLPurifier/tags @@ -0,0 +1,2972 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.7 // +CDATACallback Lexer.php /^ protected static function CDATACallback($matches) {$/;" f +EOF Lexer/PH5P.php /^ private function EOF() {$/;" f +HTML5 Lexer/PH5P.php /^class HTML5 {$/;" c +HTML5TreeConstructer Lexer/PH5P.php /^class HTML5TreeConstructer {$/;" c +HTMLPURIFIER_PREFIX Bootstrap.php /^ define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '\/..'));$/;" d +HTMLPurifier_AttrCollections AttrCollections.php /^class HTMLPurifier_AttrCollections$/;" c +HTMLPurifier_AttrDef AttrDef.php /^abstract class HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS AttrDef/CSS.php /^class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_AlphaValue AttrDef/CSS/AlphaValue.php /^class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number$/;" c +HTMLPurifier_AttrDef_CSS_Background AttrDef/CSS/Background.php /^class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_BackgroundPosition AttrDef/CSS/BackgroundPosition.php /^class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Border AttrDef/CSS/Border.php /^class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Color AttrDef/CSS/Color.php /^class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Composite AttrDef/CSS/Composite.php /^class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_DenyElementDecorator AttrDef/CSS/DenyElementDecorator.php /^class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Filter AttrDef/CSS/Filter.php /^class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Font AttrDef/CSS/Font.php /^class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_FontFamily AttrDef/CSS/FontFamily.php /^class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_ImportantDecorator AttrDef/CSS/ImportantDecorator.php /^class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Length AttrDef/CSS/Length.php /^class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_ListStyle AttrDef/CSS/ListStyle.php /^class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Multiple AttrDef/CSS/Multiple.php /^class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Number AttrDef/CSS/Number.php /^class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_Percentage AttrDef/CSS/Percentage.php /^class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_TextDecoration AttrDef/CSS/TextDecoration.php /^class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_CSS_URI AttrDef/CSS/URI.php /^class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI$/;" c +HTMLPurifier_AttrDef_Enum AttrDef/Enum.php /^class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_HTML_Bool AttrDef/HTML/Bool.php /^class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_HTML_Class AttrDef/HTML/Class.php /^class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens$/;" c +HTMLPurifier_AttrDef_HTML_Color AttrDef/HTML/Color.php /^class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_HTML_FrameTarget AttrDef/HTML/FrameTarget.php /^class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum$/;" c +HTMLPurifier_AttrDef_HTML_ID AttrDef/HTML/ID.php /^class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_HTML_Length AttrDef/HTML/Length.php /^class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels$/;" c +HTMLPurifier_AttrDef_HTML_LinkTypes AttrDef/HTML/LinkTypes.php /^class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_HTML_MultiLength AttrDef/HTML/MultiLength.php /^class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length$/;" c +HTMLPurifier_AttrDef_HTML_Nmtokens AttrDef/HTML/Nmtokens.php /^class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_HTML_Pixels AttrDef/HTML/Pixels.php /^class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_Integer AttrDef/Integer.php /^class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_Lang AttrDef/Lang.php /^class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_Switch AttrDef/Switch.php /^class HTMLPurifier_AttrDef_Switch$/;" c +HTMLPurifier_AttrDef_Text AttrDef/Text.php /^class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_URI AttrDef/URI.php /^class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_URI_Email AttrDef/URI/Email.php /^abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_URI_Email_SimpleCheck AttrDef/URI/Email/SimpleCheck.php /^class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email$/;" c +HTMLPurifier_AttrDef_URI_Host AttrDef/URI/Host.php /^class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_URI_IPv4 AttrDef/URI/IPv4.php /^class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef$/;" c +HTMLPurifier_AttrDef_URI_IPv6 AttrDef/URI/IPv6.php /^class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4$/;" c +HTMLPurifier_AttrTransform AttrTransform.php /^abstract class HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_Background AttrTransform/Background.php /^class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_BdoDir AttrTransform/BdoDir.php /^class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_BgColor AttrTransform/BgColor.php /^class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_BoolToCSS AttrTransform/BoolToCSS.php /^class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_Border AttrTransform/Border.php /^class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_EnumToCSS AttrTransform/EnumToCSS.php /^class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_ImgRequired AttrTransform/ImgRequired.php /^class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_ImgSpace AttrTransform/ImgSpace.php /^class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_Input AttrTransform/Input.php /^class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform {$/;" c +HTMLPurifier_AttrTransform_Lang AttrTransform/Lang.php /^class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_Length AttrTransform/Length.php /^class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_Name AttrTransform/Name.php /^class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_NameSync AttrTransform/NameSync.php /^class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_SafeEmbed AttrTransform/SafeEmbed.php /^class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_SafeObject AttrTransform/SafeObject.php /^class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_SafeParam AttrTransform/SafeParam.php /^class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_ScriptRequired AttrTransform/ScriptRequired.php /^class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTransform_Textarea AttrTransform/Textarea.php /^class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform$/;" c +HTMLPurifier_AttrTypes AttrTypes.php /^class HTMLPurifier_AttrTypes$/;" c +HTMLPurifier_AttrValidator AttrValidator.php /^class HTMLPurifier_AttrValidator$/;" c +HTMLPurifier_Bootstrap Bootstrap.php /^class HTMLPurifier_Bootstrap$/;" c +HTMLPurifier_CSSDefinition CSSDefinition.php /^class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition$/;" c +HTMLPurifier_ChildDef ChildDef.php /^abstract class HTMLPurifier_ChildDef$/;" c +HTMLPurifier_ChildDef_Chameleon ChildDef/Chameleon.php /^class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef$/;" c +HTMLPurifier_ChildDef_Custom ChildDef/Custom.php /^class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef$/;" c +HTMLPurifier_ChildDef_Empty ChildDef/Empty.php /^class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef$/;" c +HTMLPurifier_ChildDef_Optional ChildDef/Optional.php /^class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required$/;" c +HTMLPurifier_ChildDef_Required ChildDef/Required.php /^class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef$/;" c +HTMLPurifier_ChildDef_StrictBlockquote ChildDef/StrictBlockquote.php /^class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required$/;" c +HTMLPurifier_ChildDef_Table ChildDef/Table.php /^class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef$/;" c +HTMLPurifier_Config Config.php /^class HTMLPurifier_Config$/;" c +HTMLPurifier_ConfigSchema ConfigSchema.php /^class HTMLPurifier_ConfigSchema {$/;" c +HTMLPurifier_ConfigSchema_Builder_ConfigSchema ConfigSchema/Builder/ConfigSchema.php /^class HTMLPurifier_ConfigSchema_Builder_ConfigSchema$/;" c +HTMLPurifier_ConfigSchema_Builder_Xml ConfigSchema/Builder/Xml.php /^class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter$/;" c +HTMLPurifier_ConfigSchema_Exception ConfigSchema/Exception.php /^class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception$/;" c +HTMLPurifier_ConfigSchema_Interchange ConfigSchema/Interchange.php /^class HTMLPurifier_ConfigSchema_Interchange$/;" c +HTMLPurifier_ConfigSchema_InterchangeBuilder ConfigSchema/InterchangeBuilder.php /^class HTMLPurifier_ConfigSchema_InterchangeBuilder$/;" c +HTMLPurifier_ConfigSchema_Interchange_Directive ConfigSchema/Interchange/Directive.php /^class HTMLPurifier_ConfigSchema_Interchange_Directive$/;" c +HTMLPurifier_ConfigSchema_Interchange_Id ConfigSchema/Interchange/Id.php /^class HTMLPurifier_ConfigSchema_Interchange_Id$/;" c +HTMLPurifier_ConfigSchema_Validator ConfigSchema/Validator.php /^class HTMLPurifier_ConfigSchema_Validator$/;" c +HTMLPurifier_ConfigSchema_ValidatorAtom ConfigSchema/ValidatorAtom.php /^class HTMLPurifier_ConfigSchema_ValidatorAtom$/;" c +HTMLPurifier_ContentSets ContentSets.php /^class HTMLPurifier_ContentSets$/;" c +HTMLPurifier_Context Context.php /^class HTMLPurifier_Context$/;" c +HTMLPurifier_Definition Definition.php /^abstract class HTMLPurifier_Definition$/;" c +HTMLPurifier_DefinitionCache DefinitionCache.php /^abstract class HTMLPurifier_DefinitionCache$/;" c +HTMLPurifier_DefinitionCacheFactory DefinitionCacheFactory.php /^class HTMLPurifier_DefinitionCacheFactory$/;" c +HTMLPurifier_DefinitionCache_Decorator DefinitionCache/Decorator.php /^class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache$/;" c +HTMLPurifier_DefinitionCache_Decorator_Cleanup DefinitionCache/Decorator/Cleanup.php /^class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends$/;" c +HTMLPurifier_DefinitionCache_Decorator_Memory DefinitionCache/Decorator/Memory.php /^class HTMLPurifier_DefinitionCache_Decorator_Memory extends$/;" c +HTMLPurifier_DefinitionCache_Null DefinitionCache/Null.php /^class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache$/;" c +HTMLPurifier_DefinitionCache_Serializer DefinitionCache/Serializer.php /^class HTMLPurifier_DefinitionCache_Serializer extends$/;" c +HTMLPurifier_Doctype Doctype.php /^class HTMLPurifier_Doctype$/;" c +HTMLPurifier_DoctypeRegistry DoctypeRegistry.php /^class HTMLPurifier_DoctypeRegistry$/;" c +HTMLPurifier_ElementDef ElementDef.php /^class HTMLPurifier_ElementDef$/;" c +HTMLPurifier_Encoder Encoder.php /^class HTMLPurifier_Encoder$/;" c +HTMLPurifier_EntityLookup EntityLookup.php /^class HTMLPurifier_EntityLookup {$/;" c +HTMLPurifier_EntityParser EntityParser.php /^class HTMLPurifier_EntityParser$/;" c +HTMLPurifier_ErrorCollector ErrorCollector.php /^class HTMLPurifier_ErrorCollector$/;" c +HTMLPurifier_ErrorStruct ErrorStruct.php /^class HTMLPurifier_ErrorStruct$/;" c +HTMLPurifier_Exception Exception.php /^class HTMLPurifier_Exception extends Exception$/;" c +HTMLPurifier_Filter Filter.php /^class HTMLPurifier_Filter$/;" c +HTMLPurifier_Filter_ExtractStyleBlocks Filter/ExtractStyleBlocks.php /^class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter$/;" c +HTMLPurifier_Filter_YouTube Filter/YouTube.php /^class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter$/;" c +HTMLPurifier_Generator Generator.php /^class HTMLPurifier_Generator$/;" c +HTMLPurifier_HTMLDefinition HTMLDefinition.php /^class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition$/;" c +HTMLPurifier_HTMLModule HTMLModule.php /^class HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModuleManager HTMLModuleManager.php /^class HTMLPurifier_HTMLModuleManager$/;" c +HTMLPurifier_HTMLModule_Bdo HTMLModule/Bdo.php /^class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_CommonAttributes HTMLModule/CommonAttributes.php /^class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Edit HTMLModule/Edit.php /^class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Forms HTMLModule/Forms.php /^class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Hypertext HTMLModule/Hypertext.php /^class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Image HTMLModule/Image.php /^class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Legacy HTMLModule/Legacy.php /^class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_List HTMLModule/List.php /^class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Name HTMLModule/Name.php /^class HTMLPurifier_HTMLModule_Name extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_NonXMLCommonAttributes HTMLModule/NonXMLCommonAttributes.php /^class HTMLPurifier_HTMLModule_NonXMLCommonAttributes extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Object HTMLModule/Object.php /^class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Presentation HTMLModule/Presentation.php /^class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Proprietary HTMLModule/Proprietary.php /^class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Ruby HTMLModule/Ruby.php /^class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_SafeEmbed HTMLModule/SafeEmbed.php /^class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_SafeObject HTMLModule/SafeObject.php /^class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Scripting HTMLModule/Scripting.php /^class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_StyleAttribute HTMLModule/StyleAttribute.php /^class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Tables HTMLModule/Tables.php /^class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Target HTMLModule/Target.php /^class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Text HTMLModule/Text.php /^class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Tidy HTMLModule/Tidy.php /^class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_HTMLModule_Tidy_Name HTMLModule/Tidy/Name.php /^class HTMLPurifier_HTMLModule_Tidy_Name extends HTMLPurifier_HTMLModule_Tidy$/;" c +HTMLPurifier_HTMLModule_Tidy_Proprietary HTMLModule/Tidy/Proprietary.php /^class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_Tidy$/;" c +HTMLPurifier_HTMLModule_Tidy_Strict HTMLModule/Tidy/Strict.php /^class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4$/;" c +HTMLPurifier_HTMLModule_Tidy_Transitional HTMLModule/Tidy/Transitional.php /^class HTMLPurifier_HTMLModule_Tidy_Transitional extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4$/;" c +HTMLPurifier_HTMLModule_Tidy_XHTML HTMLModule/Tidy/XHTML.php /^class HTMLPurifier_HTMLModule_Tidy_XHTML extends HTMLPurifier_HTMLModule_Tidy$/;" c +HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 HTMLModule/Tidy/XHTMLAndHTML4.php /^class HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 extends HTMLPurifier_HTMLModule_Tidy$/;" c +HTMLPurifier_HTMLModule_XMLCommonAttributes HTMLModule/XMLCommonAttributes.php /^class HTMLPurifier_HTMLModule_XMLCommonAttributes extends HTMLPurifier_HTMLModule$/;" c +HTMLPurifier_IDAccumulator IDAccumulator.php /^class HTMLPurifier_IDAccumulator$/;" c +HTMLPurifier_Injector Injector.php /^abstract class HTMLPurifier_Injector$/;" c +HTMLPurifier_Injector_AutoParagraph Injector/AutoParagraph.php /^class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector$/;" c +HTMLPurifier_Injector_DisplayLinkURI Injector/DisplayLinkURI.php /^class HTMLPurifier_Injector_DisplayLinkURI extends HTMLPurifier_Injector$/;" c +HTMLPurifier_Injector_Linkify Injector/Linkify.php /^class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector$/;" c +HTMLPurifier_Injector_PurifierLinkify Injector/PurifierLinkify.php /^class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector$/;" c +HTMLPurifier_Injector_RemoveEmpty Injector/RemoveEmpty.php /^class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector$/;" c +HTMLPurifier_Injector_SafeObject Injector/SafeObject.php /^class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector$/;" c +HTMLPurifier_Language Language.php /^class HTMLPurifier_Language$/;" c +HTMLPurifier_LanguageFactory LanguageFactory.php /^class HTMLPurifier_LanguageFactory$/;" c +HTMLPurifier_Language_en_x_test Language/classes/en-x-test.php /^class HTMLPurifier_Language_en_x_test extends HTMLPurifier_Language$/;" c +HTMLPurifier_Length Length.php /^class HTMLPurifier_Length$/;" c +HTMLPurifier_Lexer Lexer.php /^class HTMLPurifier_Lexer$/;" c +HTMLPurifier_Lexer_DOMLex Lexer/DOMLex.php /^class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer$/;" c +HTMLPurifier_Lexer_DirectLex Lexer/DirectLex.php /^class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer$/;" c +HTMLPurifier_Lexer_PEARSax3 Lexer/PEARSax3.php /^class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer$/;" c +HTMLPurifier_Lexer_PH5P Lexer/PH5P.php /^class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex {$/;" c +HTMLPurifier_PercentEncoder PercentEncoder.php /^class HTMLPurifier_PercentEncoder$/;" c +HTMLPurifier_Printer Printer.php /^class HTMLPurifier_Printer$/;" c +HTMLPurifier_Printer_CSSDefinition Printer/CSSDefinition.php /^class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer$/;" c +HTMLPurifier_Printer_ConfigForm Printer/ConfigForm.php /^class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer$/;" c +HTMLPurifier_Printer_ConfigForm_NullDecorator Printer/ConfigForm.php /^class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer {$/;" c +HTMLPurifier_Printer_ConfigForm_bool Printer/ConfigForm.php /^class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer {$/;" c +HTMLPurifier_Printer_ConfigForm_default Printer/ConfigForm.php /^class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {$/;" c +HTMLPurifier_Printer_HTMLDefinition Printer/HTMLDefinition.php /^class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer$/;" c +HTMLPurifier_PropertyList PropertyList.php /^class HTMLPurifier_PropertyList$/;" c +HTMLPurifier_PropertyListIterator PropertyListIterator.php /^class HTMLPurifier_PropertyListIterator extends FilterIterator$/;" c +HTMLPurifier_Strategy Strategy.php /^abstract class HTMLPurifier_Strategy$/;" c +HTMLPurifier_Strategy_Composite Strategy/Composite.php /^abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy$/;" c +HTMLPurifier_Strategy_Core Strategy/Core.php /^class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite$/;" c +HTMLPurifier_Strategy_FixNesting Strategy/FixNesting.php /^class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy$/;" c +HTMLPurifier_Strategy_MakeWellFormed Strategy/MakeWellFormed.php /^class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy$/;" c +HTMLPurifier_Strategy_RemoveForeignElements Strategy/RemoveForeignElements.php /^class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy$/;" c +HTMLPurifier_Strategy_ValidateAttributes Strategy/ValidateAttributes.php /^class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy$/;" c +HTMLPurifier_StringHash StringHash.php /^class HTMLPurifier_StringHash extends ArrayObject$/;" c +HTMLPurifier_StringHashParser StringHashParser.php /^class HTMLPurifier_StringHashParser$/;" c +HTMLPurifier_TagTransform TagTransform.php /^abstract class HTMLPurifier_TagTransform$/;" c +HTMLPurifier_TagTransform_Font TagTransform/Font.php /^class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform$/;" c +HTMLPurifier_TagTransform_Simple TagTransform/Simple.php /^class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform$/;" c +HTMLPurifier_Token Token.php /^class HTMLPurifier_Token {$/;" c +HTMLPurifier_TokenFactory TokenFactory.php /^class HTMLPurifier_TokenFactory$/;" c +HTMLPurifier_Token_Comment Token/Comment.php /^class HTMLPurifier_Token_Comment extends HTMLPurifier_Token$/;" c +HTMLPurifier_Token_Empty Token/Empty.php /^class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag$/;" c +HTMLPurifier_Token_End Token/End.php /^class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag$/;" c +HTMLPurifier_Token_Start Token/Start.php /^class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag$/;" c +HTMLPurifier_Token_Tag Token/Tag.php /^class HTMLPurifier_Token_Tag extends HTMLPurifier_Token$/;" c +HTMLPurifier_Token_Text Token/Text.php /^class HTMLPurifier_Token_Text extends HTMLPurifier_Token$/;" c +HTMLPurifier_URI URI.php /^class HTMLPurifier_URI$/;" c +HTMLPurifier_URIDefinition URIDefinition.php /^class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition$/;" c +HTMLPurifier_URIFilter URIFilter.php /^abstract class HTMLPurifier_URIFilter$/;" c +HTMLPurifier_URIFilter_DisableExternal URIFilter/DisableExternal.php /^class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter$/;" c +HTMLPurifier_URIFilter_DisableExternalResources URIFilter/DisableExternalResources.php /^class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal$/;" c +HTMLPurifier_URIFilter_HostBlacklist URIFilter/HostBlacklist.php /^class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter$/;" c +HTMLPurifier_URIFilter_MakeAbsolute URIFilter/MakeAbsolute.php /^class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter$/;" c +HTMLPurifier_URIFilter_Munge URIFilter/Munge.php /^class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter$/;" c +HTMLPurifier_URIParser URIParser.php /^class HTMLPurifier_URIParser$/;" c +HTMLPurifier_URIScheme URIScheme.php /^class HTMLPurifier_URIScheme$/;" c +HTMLPurifier_URISchemeRegistry URISchemeRegistry.php /^class HTMLPurifier_URISchemeRegistry$/;" c +HTMLPurifier_URIScheme_ftp URIScheme/ftp.php /^class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme {$/;" c +HTMLPurifier_URIScheme_http URIScheme/http.php /^class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme {$/;" c +HTMLPurifier_URIScheme_https URIScheme/https.php /^class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http {$/;" c +HTMLPurifier_URIScheme_mailto URIScheme/mailto.php /^class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme {$/;" c +HTMLPurifier_URIScheme_news URIScheme/news.php /^class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme {$/;" c +HTMLPurifier_URIScheme_nntp URIScheme/nntp.php /^class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme {$/;" c +HTMLPurifier_UnitConverter UnitConverter.php /^class HTMLPurifier_UnitConverter$/;" c +HTMLPurifier_VarParser VarParser.php /^class HTMLPurifier_VarParser$/;" c +HTMLPurifier_VarParserException VarParserException.php /^class HTMLPurifier_VarParserException extends HTMLPurifier_Exception$/;" c +HTMLPurifier_VarParser_Flexible VarParser/Flexible.php /^class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser$/;" c +HTMLPurifier_VarParser_Native VarParser/Native.php /^class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser$/;" c +PHP_EOL Bootstrap.php /^ define('PHP_EOL', "\\n");$/;" d +PHP_EOL Bootstrap.php /^ define('PHP_EOL', "\\r");$/;" d +PHP_EOL Bootstrap.php /^ define('PHP_EOL', "\\r\\n");$/;" d +__construct AttrCollections.php /^ public function __construct($attr_types, $modules) {$/;" f +__construct AttrDef/CSS/AlphaValue.php /^ public function __construct() {$/;" f +__construct AttrDef/CSS/Background.php /^ public function __construct($config) {$/;" f +__construct AttrDef/CSS/BackgroundPosition.php /^ public function __construct() {$/;" f +__construct AttrDef/CSS/Border.php /^ public function __construct($config) {$/;" f +__construct AttrDef/CSS/Composite.php /^ public function __construct($defs) {$/;" f +__construct AttrDef/CSS/DenyElementDecorator.php /^ public function __construct($def, $element) {$/;" f +__construct AttrDef/CSS/Filter.php /^ public function __construct() {$/;" f +__construct AttrDef/CSS/Font.php /^ public function __construct($config) {$/;" f +__construct AttrDef/CSS/ImportantDecorator.php /^ public function __construct($def, $allow = false) {$/;" f +__construct AttrDef/CSS/Length.php /^ public function __construct($min = null, $max = null) {$/;" f +__construct AttrDef/CSS/ListStyle.php /^ public function __construct($config) {$/;" f +__construct AttrDef/CSS/Multiple.php /^ public function __construct($single, $max = 4) {$/;" f +__construct AttrDef/CSS/Number.php /^ public function __construct($non_negative = false) {$/;" f +__construct AttrDef/CSS/Percentage.php /^ public function __construct($non_negative = false) {$/;" f +__construct AttrDef/CSS/URI.php /^ public function __construct() {$/;" f +__construct AttrDef/Enum.php /^ public function __construct($/;" f +__construct AttrDef/HTML/Bool.php /^ public function __construct($name = false) {$this->name = $name;}$/;" f +__construct AttrDef/HTML/FrameTarget.php /^ public function __construct() {}$/;" f +__construct AttrDef/HTML/LinkTypes.php /^ public function __construct($name) {$/;" f +__construct AttrDef/HTML/Pixels.php /^ public function __construct($max = null) {$/;" f +__construct AttrDef/Integer.php /^ public function __construct($/;" f +__construct AttrDef/Switch.php /^ public function __construct($tag, $with_tag, $without_tag) {$/;" f +__construct AttrDef/URI.php /^ public function __construct($embeds_resource = false) {$/;" f +__construct AttrDef/URI/Host.php /^ public function __construct() {$/;" f +__construct AttrTransform/BoolToCSS.php /^ public function __construct($attr, $css) {$/;" f +__construct AttrTransform/EnumToCSS.php /^ public function __construct($attr, $enum_to_css, $case_sensitive = false) {$/;" f +__construct AttrTransform/ImgSpace.php /^ public function __construct($attr) {$/;" f +__construct AttrTransform/Input.php /^ public function __construct() {$/;" f +__construct AttrTransform/Length.php /^ public function __construct($name, $css_name = null) {$/;" f +__construct AttrTransform/NameSync.php /^ public function __construct() {$/;" f +__construct AttrTransform/SafeParam.php /^ public function __construct() {$/;" f +__construct AttrTypes.php /^ public function __construct() {$/;" f +__construct ChildDef/Chameleon.php /^ public function __construct($inline, $block) {$/;" f +__construct ChildDef/Custom.php /^ public function __construct($dtd_regex) {$/;" f +__construct ChildDef/Empty.php /^ public function __construct() {}$/;" f +__construct ChildDef/Required.php /^ public function __construct($elements) {$/;" f +__construct ChildDef/Table.php /^ public function __construct() {}$/;" f +__construct Config.php /^ public function __construct($definition, $parent = null) {$/;" f +__construct ConfigSchema.php /^ public function __construct() {$/;" f +__construct ConfigSchema/Interchange/Id.php /^ public function __construct($key) {$/;" f +__construct ConfigSchema/InterchangeBuilder.php /^ public function __construct($varParser = null) {$/;" f +__construct ConfigSchema/Validator.php /^ public function __construct() {$/;" f +__construct ConfigSchema/ValidatorAtom.php /^ public function __construct($context, $obj, $member) {$/;" f +__construct ContentSets.php /^ public function __construct($modules) {$/;" f +__construct DefinitionCache.php /^ public function __construct($type) {$/;" f +__construct DefinitionCache/Decorator.php /^ public function __construct() {}$/;" f +__construct Doctype.php /^ public function __construct($name = null, $xml = true, $modules = array(),$/;" f +__construct Encoder.php /^ private function __construct() {$/;" f +__construct ErrorCollector.php /^ public function __construct($context) {$/;" f +__construct Filter/ExtractStyleBlocks.php /^ public function __construct() {$/;" f +__construct Generator.php /^ public function __construct($config, $context) {$/;" f +__construct HTMLDefinition.php /^ public function __construct() {$/;" f +__construct HTMLModuleManager.php /^ public function __construct() {$/;" f +__construct Language.php /^ public function __construct($config, $context) {$/;" f +__construct Length.php /^ public function __construct($n = '0', $u = false) {$/;" f +__construct Lexer.php /^ public function __construct() {$/;" f +__construct Lexer/DOMLex.php /^ public function __construct() {$/;" f +__construct Lexer/PH5P.php /^ public function __construct($data) {$/;" f +__construct Lexer/PH5P.php /^ public function __construct() {$/;" f +__construct PercentEncoder.php /^ public function __construct($preserve = false) {$/;" f +__construct Printer.php /^ public function __construct() {$/;" f +__construct Printer/ConfigForm.php /^ public function __construct($/;" f +__construct Printer/ConfigForm.php /^ public function __construct($obj) {$/;" f +__construct PropertyList.php /^ public function __construct($parent = null) {$/;" f +__construct PropertyListIterator.php /^ public function __construct(Iterator $iterator, $filter = null) {$/;" f +__construct Strategy/Composite.php /^ abstract public function __construct();$/;" f +__construct Strategy/Core.php /^ public function __construct() {$/;" f +__construct TagTransform/Simple.php /^ public function __construct($transform_to, $style = null) {$/;" f +__construct Token/Comment.php /^ public function __construct($data, $line = null, $col = null) {$/;" f +__construct Token/Tag.php /^ public function __construct($name, $attr = array(), $line = null, $col = null) {$/;" f +__construct Token/Text.php /^ public function __construct($data, $line = null, $col = null) {$/;" f +__construct TokenFactory.php /^ public function __construct() {$/;" f +__construct URI.php /^ public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment) {$/;" f +__construct URIDefinition.php /^ public function __construct() {$/;" f +__construct URIParser.php /^ public function __construct() {$/;" f +__construct UnitConverter.php /^ public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) {$/;" f +__get Token.php /^ public function __get($n) {$/;" f +_checkNeedsP Injector/AutoParagraph.php /^ private function _checkNeedsP($current) {$/;" f +_collapseStack URIFilter/MakeAbsolute.php /^ private function _collapseStack($stack) {$/;" f +_compileRegex ChildDef/Custom.php /^ protected function _compileRegex() {$/;" f +_findUnused ConfigSchema/InterchangeBuilder.php /^ protected function _findUnused($hash) {$/;" f +_isInline Injector/AutoParagraph.php /^ private function _isInline($token) {$/;" f +_listify Config.php /^ private function _listify($lookup) {$/;" f +_loadRegex AttrDef/URI/IPv4.php /^ protected function _loadRegex() {$/;" f +_loaded Language.php /^ public $_loaded = false;$/;" v +_mergeAssocArray ElementDef.php /^ private function _mergeAssocArray(&$a1, $a2) {$/;" f +_pLookAhead Injector/AutoParagraph.php /^ private function _pLookAhead() {$/;" f +_pStart Injector/AutoParagraph.php /^ private function _pStart() {$/;" f +_prepareDir DefinitionCache/Serializer.php /^ private function _prepareDir($config) {$/;" f +_renderStruct ErrorCollector.php /^ private function _renderStruct(&$ret, $struct, $line = null, $col = null) {$/;" f +_scriptFix Generator.php /^ private $_scriptFix = false;$/;" v +_size_lookup TagTransform/Font.php /^ protected $_size_lookup = array($/;" v +_special_dec2str EntityParser.php /^ protected $_special_dec2str =$/;" v +_special_ent2dec EntityParser.php /^ protected $_special_ent2dec =$/;" v +_special_entity2str Lexer.php /^ protected $_special_entity2str =$/;" v +_splitText Injector/AutoParagraph.php /^ private function _splitText($data, &$result) {$/;" f +_stacks ErrorCollector.php /^ protected $_stacks = array(array());$/;" v +_storage Context.php /^ private $_storage = array();$/;" v +_styleMatches Filter/ExtractStyleBlocks.php /^ private $_styleMatches = array();$/;" v +_substituteEntitiesRegex EntityParser.php /^ protected $_substituteEntitiesRegex =$/;" v +_testPermissions DefinitionCache/Serializer.php /^ private function _testPermissions($dir) {$/;" f +_whitespace Lexer/DirectLex.php /^ protected $_whitespace = "\\x20\\x09\\x0D\\x0A";$/;" v +_write DefinitionCache/Serializer.php /^ private function _write($file, $data) {$/;" f +_xhtml Generator.php /^ private $_xhtml = true;$/;" v +a AttrDef/URI/Host.php /^ $a = '[a-z]'; \/\/ alpha$/;" v +a Config.php /^ public function get($key, $a = null) {$/;" v +a Config.php /^ public function set($key, $value, $a = null) {$/;" v +a HTMLModule/Hypertext.php /^ $a = $this->addElement($/;" v +aIP AttrDef/URI/IPv6.php /^ $aIP = substr($aIP, 0, 0-strlen($find[0]));$/;" v +aIP AttrDef/URI/IPv6.php /^ $aIP = $first;$/;" v +aIP AttrDef/URI/IPv6.php /^ $aIP = explode(':', $aIP[0]);$/;" v +aIP AttrDef/URI/IPv6.php /^ $aIP = substr($aIP, 0, 0-strlen($find[0]));$/;" v +aIP AttrDef/URI/IPv6.php /^ $aIP = explode('::', $aIP);$/;" v +a_formatting Lexer/PH5P.php /^ private $a_formatting = array();$/;" v +a_pos Lexer/PH5P.php /^ $a_pos = array_search($node, $this->a_formatting, true);$/;" v +accept PropertyListIterator.php /^ public function accept() {$/;" f +accepts Token/End.php /^ * @warning This class accepts attributes even though end tags cannot. This$/;" c +accessed ConfigSchema/InterchangeBuilder.php /^ $accessed = $hash->getAccessed();$/;" v +accessed StringHash.php /^ protected $accessed = array();$/;" v +activated_levels HTMLModule/Tidy.php /^ $activated_levels = array();$/;" v +add ConfigSchema.php /^ public function add($key, $default, $type, $allow_null) {$/;" f +add ContentSets.php /^ $add = array();$/;" v +add DefinitionCache.php /^ abstract public function add($def, $config);$/;" f +add DefinitionCache/Decorator.php /^ public function add($def, $config) {$/;" f +add DefinitionCache/Decorator/Cleanup.php /^ public function add($def, $config) {$/;" f +add DefinitionCache/Decorator/Memory.php /^ public function add($def, $config) {$/;" f +add DefinitionCache/Null.php /^ public function add($def, $config) {$/;" f +add DefinitionCache/Serializer.php /^ public function add($def, $config) {$/;" f +add IDAccumulator.php /^ public function add($id) {$/;" f +add UnitConverter.php /^ private function add($s1, $s2, $scale) {$/;" f +addAlias ConfigSchema.php /^ public function addAlias($key, $new_key) {$/;" f +addAllowedValues ConfigSchema.php /^ public function addAllowedValues($key, $allowed) {$/;" f +addAttribute HTMLDefinition.php /^ public function addAttribute($element_name, $attr_name, $def) {$/;" f +addBlankElement HTMLDefinition.php /^ public function addBlankElement($element_name) {$/;" f +addBlankElement HTMLModule.php /^ public function addBlankElement($element) {$/;" f +addDecorator DefinitionCacheFactory.php /^ public function addDecorator($decorator) {$/;" f +addDirective ConfigSchema/Interchange.php /^ public function addDirective($directive) {$/;" f +addElement HTMLDefinition.php /^ public function addElement($element_name, $type, $contents, $attr_collections, $attributes) {$/;" f +addElement HTMLModule.php /^ public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) {$/;" f +addElementToContentSet HTMLModule.php /^ public function addElementToContentSet($element, $type) {$/;" f +addError ErrorStruct.php /^ public function addError($severity, $message) {$/;" f +addFilter URIDefinition.php /^ public function addFilter($filter, $config) {$/;" f +addModule HTMLModuleManager.php /^ public function addModule($module) {$/;" f +addParam Injector/SafeObject.php /^ protected $addParam = array($/;" v +addPrefix HTMLModuleManager.php /^ public function addPrefix($prefix) {$/;" f +addValueAliases ConfigSchema.php /^ public function addValueAliases($key, $aliases) {$/;" f +add_fixes HTMLModule/Tidy.php /^ $add_fixes = $config->get('HTML.TidyAdd');$/;" v +address HTMLModule/Legacy.php /^ $address = $this->addBlankElement('address');$/;" v +af_part1 Lexer/PH5P.php /^ $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);$/;" v +af_part2 Lexer/PH5P.php /^ $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));$/;" v +afterAttributeNameState Lexer/PH5P.php /^ private function afterAttributeNameState() {$/;" f +afterBody Lexer/PH5P.php /^ private function afterBody($token) {$/;" f +afterDoctypeNameState Lexer/PH5P.php /^ private function afterDoctypeNameState() {$/;" f +afterFrameset Lexer/PH5P.php /^ private function afterFrameset($token) {$/;" f +afterHead Lexer/PH5P.php /^ private function afterHead($token) {$/;" f +alias ConfigSchema.php /^ foreach ($aliases as $alias => $real) {$/;" v +alias ConfigSchema/Validator.php /^ foreach ($d->valueAliases as $alias => $real) {$/;" v +alias ConfigSchema/Validator.php /^ foreach ($d->valueAliases as $alias => $real) {$/;" v +aliases ConfigSchema/Interchange/Directive.php /^ public $aliases = array();$/;" v +aliases ConfigSchema/InterchangeBuilder.php /^ $aliases = preg_split('\/\\s*,\\s*\/', $raw_aliases);$/;" v +aliases Doctype.php /^ public $aliases = array();$/;" v +aliases DoctypeRegistry.php /^ if (!is_array($aliases)) $aliases = array($aliases);$/;" v +align HTMLModule/Legacy.php /^ $align = 'Enum#left,right,center,justify';$/;" v +align_lookup HTMLModule/Tidy/XHTMLAndHTML4.php /^ $align_lookup = array();$/;" v +align_values HTMLModule/Tidy/XHTMLAndHTML4.php /^ $align_values = array('left', 'right', 'center', 'justify');$/;" v +all Printer.php /^ $all = $config->getAll();$/;" v +all Printer/ConfigForm.php /^ $all = array();$/;" v +all_whitespace ChildDef/Required.php /^ $all_whitespace = false; \/\/ phew, we're not talking about whitespace$/;" v +all_whitespace ChildDef/Required.php /^ $all_whitespace = true;$/;" v +allow AttrDef/CSS/ImportantDecorator.php /^ public function __construct($def, $allow = false) {$/;" v +allow_empty ChildDef/Custom.php /^ public $allow_empty = false;$/;" v +allow_empty ChildDef/Empty.php /^ public $allow_empty = true;$/;" v +allow_empty ChildDef/Optional.php /^ public $allow_empty = true;$/;" v +allow_empty ChildDef/Required.php /^ public $allow_empty = false;$/;" v +allow_empty ChildDef/StrictBlockquote.php /^ public $allow_empty = true;$/;" v +allow_empty ChildDef/Table.php /^ public $allow_empty = false;$/;" v +allow_important CSSDefinition.php /^ $allow_important = $config->get('CSS.AllowImportant');$/;" v +allow_null Config.php /^ $allow_null = isset($def->allow_null);$/;" v +allow_null Config.php /^ $allow_null = true;$/;" v +allow_null Printer/ConfigForm.php /^ $allow_null = $def < 0;$/;" v +allow_null Printer/ConfigForm.php /^ $allow_null = isset($def->allow_null);$/;" v +allow_null VarParser.php /^ final public function parse($var, $type, $allow_null = false) {$/;" v +allowed AttrDef/HTML/Class.php /^ $allowed = $config->get('Attr.AllowedClasses');$/;" v +allowed AttrDef/HTML/LinkTypes.php /^ $allowed = $config->get('Attr.' . $this->name);$/;" v +allowed Config.php /^ if (is_string($allowed)) $allowed = array($allowed);$/;" v +allowed Config.php /^ $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);$/;" v +allowed HTMLDefinition.php /^ $allowed = $config->get('HTML.Allowed');$/;" v +allowed Printer/ConfigForm.php /^ $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);$/;" v +allowed Printer/ConfigForm.php /^ public function render($config, $allowed = true, $render_controls = true) {$/;" v +allowedParam Injector/SafeObject.php /^ protected $allowedParam = array($/;" v +allowedUnits Length.php /^ protected static $allowedUnits = array($/;" v +allowed_attributes CSSDefinition.php /^ $allowed_attributes = $config->get('CSS.AllowedProperties');$/;" v +allowed_attributes HTMLDefinition.php /^ $allowed_attributes = $config->get('HTML.AllowedAttributes'); \/\/ retrieve early$/;" v +allowed_attributes_mutable HTMLDefinition.php /^ $allowed_attributes_mutable = $allowed_attributes; \/\/ by copy!$/;" v +allowed_directives Config.php /^ $allowed_directives = array();$/;" v +allowed_elements HTMLDefinition.php /^ $allowed_elements = $config->get('HTML.AllowedElements');$/;" v +allowed_ns Config.php /^ $allowed_ns = array();$/;" v +allowed_schemes URISchemeRegistry.php /^ $allowed_schemes = $config->get('URI.AllowedSchemes');$/;" v +allowed_values AttrDef/CSS/TextDecoration.php /^ static $allowed_values = array($/;" v +allowsElement Injector.php /^ public function allowsElement($name) {$/;" f +also Injector.php /^ * the injector. This function also checks if the HTML environment$/;" f +alt AttrTransform/ImgRequired.php /^ $alt = $config->get('Attr.DefaultImageAlt');$/;" v +an AttrDef/URI/Host.php /^ $an = '[a-z0-9]'; \/\/ alphanum$/;" v +and AttrDef/URI/Host.php /^ $and = '[a-z0-9-]'; \/\/ alphanum | "-"$/;" v +and URISchemeRegistry.php /^ * @note Pass a registry object $prototype with a compatible interface and$/;" i +anon DoctypeRegistry.php /^ $anon = new HTMLPurifier_Doctype($doctype);$/;" v +append Lexer/PH5P.php /^ private function insertElement($token, $append = true, $check = false) {$/;" v +appendToRealParent Lexer/PH5P.php /^ private function appendToRealParent($node) {$/;" f +args ErrorCollector.php /^ $args = func_get_args();$/;" v +args ErrorCollector.php /^ $args = array();$/;" v +args Language.php /^ public function formatMessage($key, $args = array()) {$/;" v +armor Token.php /^ public $armor = array();$/;" v +armorUrl Filter/YouTube.php /^ protected function armorUrl($url) {$/;" f +array Config.php /^ $array = parse_ini_file($filename, true);$/;" v +array Config.php /^ if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();$/;" v +array ContentSets.php /^ $array = explode('|', str_replace(' ', '', $string));$/;" v +array Lexer/DOMLex.php /^ $array = array();$/;" v +array Lexer/DirectLex.php /^ $array = array(); \/\/ return assoc array of attributes$/;" v +array Lexer/DirectLex.php /^ $array = array(); \/\/ result array$/;" v +array Printer/ConfigForm.php /^ $array = $value;$/;" v +as Lexer.php /^ * You should be able to treat the output of this function as$/;" f +ascii_fix Encoder.php /^ $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding);$/;" v +assertAlnum ConfigSchema/ValidatorAtom.php /^ public function assertAlnum() {$/;" f +assertIsArray ConfigSchema/ValidatorAtom.php /^ public function assertIsArray() {$/;" f +assertIsBool ConfigSchema/ValidatorAtom.php /^ public function assertIsBool() {$/;" f +assertIsLookup ConfigSchema/ValidatorAtom.php /^ public function assertIsLookup() {$/;" f +assertIsString ConfigSchema/ValidatorAtom.php /^ public function assertIsString() {$/;" f +assertNotEmpty ConfigSchema/ValidatorAtom.php /^ public function assertNotEmpty() {$/;" f +assertNotNull ConfigSchema/ValidatorAtom.php /^ public function assertNotNull() {$/;" f +associated Language/messages/en-x-testmini.php /^\/\/ this language file has no class associated with it$/;" c +at Encoder.php /^ * @note Based on Feyd's function at$/;" f +attr AttrValidator.php /^ $attr = $transform->transform($o = $attr, $config, $context);$/;" v +attr AttrValidator.php /^ $attr = $token->attr;$/;" v +attr ElementDef.php /^ public $attr = array();$/;" v +attr ErrorCollector.php /^ $attr = $this->context->get('CurrentAttr', true);$/;" v +attr Generator.php /^ $attr = $this->generateAttributes($token->attr, $token->name);$/;" v +attr HTMLDefinition.php /^ $attr = false;$/;" v +attr HTMLDefinition.php /^ foreach ($info->attr as $attr => $x) {$/;" v +attr HTMLDefinition.php /^ $attr = explode('|', $attr);$/;" v +attr HTMLDefinition.php /^ $attr = substr($attr, 0, strlen($attr) - 1); \/\/ remove trailing ]$/;" v +attr HTMLDefinition.php /^ foreach ($info->attr as $attr => $x) {$/;" v +attr HTMLDefinition.php /^ foreach ($this->info_global_attr as $attr => $x) {$/;" v +attr HTMLModule/Edit.php /^ $attr = array($/;" v +attr HTMLModule/Tidy.php /^ $attr = $params['attr'];$/;" v +attr Lexer/DOMLex.php /^ $attr = $node->hasAttributes() ?$/;" v +attr Lexer/DirectLex.php /^ $attr = $this->parseAttributeString($/;" v +attr Lexer/DirectLex.php /^ $attr = array();$/;" v +attr Lexer/PH5P.php /^ $attr = $token['attr'];$/;" v +attr Printer.php /^ protected function element($tag, $contents, $attr = array(), $escape = true) {$/;" v +attr Printer.php /^ protected function elementEmpty($tag, $attr = array()) {$/;" v +attr Printer.php /^ protected function start($tag, $attr = array()) {$/;" v +attr Printer/ConfigForm.php /^ $attr = array('for' => "{$this->name}:$ns.$directive");$/;" v +attr Printer/ConfigForm.php /^ $attr = array();$/;" v +attr Printer/ConfigForm.php /^ $attr = array($/;" v +attr Printer/HTMLDefinition.php /^ $attr = array();$/;" v +attr TagTransform/Font.php /^ $attr = $tag->attr;$/;" v +attr Token/Tag.php /^ public $attr = array();$/;" v +attr Token/Tag.php /^ public function __construct($name, $attr = array(), $line = null, $col = null) {$/;" v +attr TokenFactory.php /^ public function createEmpty($name, $attr = array()) {$/;" v +attr TokenFactory.php /^ public function createStart($name, $attr = array()) {$/;" v +attr_collections HTMLModule.php /^ public $attr_collections = array();$/;" v +attr_collections HTMLModule/Bdo.php /^ public $attr_collections = array($/;" v +attr_collections HTMLModule/CommonAttributes.php /^ public $attr_collections = array($/;" v +attr_collections HTMLModule/NonXMLCommonAttributes.php /^ public $attr_collections = array($/;" v +attr_collections HTMLModule/StyleAttribute.php /^ public $attr_collections = array($/;" v +attr_collections HTMLModule/XMLCommonAttributes.php /^ public $attr_collections = array($/;" v +attr_i AttrCollections.php /^ foreach ($coll as $attr_i => $attr) {$/;" v +attr_includes HTMLModule.php /^ else $attr_includes = array($attr_includes);$/;" v +attr_includes HTMLModule.php /^ if (empty($attr_includes)) $attr_includes = array();$/;" v +attr_includes HTMLModule.php /^ public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) {$/;" v +attr_key AttrValidator.php /^ $attr_key = false;$/;" v +attr_key AttrValidator.php /^ foreach ($attr as $attr_key => $value) {$/;" v +attr_name HTMLModuleManager.php /^ foreach ($def->attr as $attr_name => $attr_def) {$/;" v +attr_transform_post ElementDef.php /^ public $attr_transform_post = array();$/;" v +attr_transform_pre ElementDef.php /^ public $attr_transform_pre = array();$/;" v +attr_validator Strategy/RemoveForeignElements.php /^ $attr_validator = new HTMLPurifier_AttrValidator();$/;" v +attribute AttrDef/HTML/Class.php /^ * Implements special behavior for class attribute (normally NMTOKENS)$/;" c +attribute HTMLDefinition.php /^ $attribute = htmlspecialchars($bits[1]);$/;" v +attribute HTMLDefinition.php /^ $attribute = htmlspecialchars($bits[0]);$/;" v +attributeNameState Lexer/PH5P.php /^ private function attributeNameState() {$/;" f +attributeValueDoubleQuotedState Lexer/PH5P.php /^ private function attributeValueDoubleQuotedState() {$/;" f +attributeValueSingleQuotedState Lexer/PH5P.php /^ private function attributeValueSingleQuotedState() {$/;" f +attributeValueUnquotedState Lexer/PH5P.php /^ private function attributeValueUnquotedState() {$/;" f +attribute_string Lexer/DirectLex.php /^ $attribute_string =$/;" v +attributes HTMLDefinition.php /^ $attributes = array();$/;" v +authority URI.php /^ $authority = '';$/;" v +authority URI.php /^ $authority = null;$/;" v +authority URIParser.php /^ $authority = !empty($matches[3]) ? $matches[4] : null;$/;" v +autoFinalize Config.php /^ public $autoFinalize = true;$/;" v +autoFinalize Config.php /^ public function autoFinalize() {$/;" f +autoclose ElementDef.php /^ public $autoclose = array();$/;" v +autoclose Strategy/MakeWellFormed.php /^ $autoclose = !isset($elements[$token->name]);$/;" v +autoclose Strategy/MakeWellFormed.php /^ $autoclose = false;$/;" v +autoload Bootstrap.php /^ $autoload = array('HTMLPurifier_Bootstrap', 'autoload');$/;" v +autoload Bootstrap.php /^ public static function autoload($class) {$/;" f +b HTMLModule/Presentation.php /^ $b = $this->addElement('b', 'Inline', 'Inline', 'Common');$/;" v +background AttrTransform/Background.php /^ $background = $this->confiscateAttr($attr, 'background');$/;" v +backtrace Config.php /^ $backtrace = debug_backtrace();$/;" v +backward Injector.php /^ protected function backward(&$i, &$current) {$/;" f +base DefinitionCache/Serializer.php /^ $base = $this->generateBaseDirectoryPath($config);$/;" v +base DefinitionCache/Serializer.php /^ $base = $config->get('Cache.SerializerPath');$/;" v +base DefinitionCache/Serializer.php /^ $base = $this->generateBaseDirectoryPath($config);$/;" v +base DefinitionCache/Serializer.php /^ $base = is_null($base) ? HTMLPURIFIER_PREFIX . '\/HTMLPurifier\/DefinitionCache\/Serializer' : $base;$/;" v +basePathStack URIFilter/MakeAbsolute.php /^ protected $basePathStack = array();$/;" v +base_uri URIDefinition.php /^ $base_uri = $config->get('URI.Base');$/;" v +based AttrDef/URI/Email/SimpleCheck.php /^ * Primitive email validation class based on the regexp found at$/;" c +batch Config.php /^ $batch = $this->getBatch($namespace);$/;" v +bdo HTMLModule/Bdo.php /^ $bdo = $this->addElement($/;" v +because EntityParser.php /^ * @notice We try to avoid calling this function because otherwise, it$/;" f +beforeAttributeNameState Lexer/PH5P.php /^ private function beforeAttributeNameState() {$/;" f +beforeAttributeValueState Lexer/PH5P.php /^ private function beforeAttributeValueState() {$/;" f +beforeDoctypeNameState Lexer/PH5P.php /^ private function beforeDoctypeNameState() {$/;" f +beforeHead Lexer/PH5P.php /^ private function beforeHead($token) {$/;" f +bgcolor AttrTransform/BgColor.php /^ $bgcolor = $this->confiscateAttr($attr, 'bgcolor');$/;" v +big HTMLModule/Presentation.php /^ $big = $this->addElement('big', 'Inline', 'Inline', 'Common');$/;" v +bits AttrDef/CSS/Background.php /^ $bits = explode(' ', strtolower($string)); \/\/ bits to process$/;" v +bits AttrDef/CSS/BackgroundPosition.php /^ $bits = explode(' ', $string);$/;" v +bits AttrDef/CSS/Border.php /^ $bits = explode(' ', $string);$/;" v +bits AttrDef/CSS/Font.php /^ $bits = explode(' ', $string); \/\/ bits to process$/;" v +bits AttrDef/CSS/ListStyle.php /^ $bits = explode(' ', strtolower($string)); \/\/ bits to process$/;" v +bits HTMLDefinition.php /^ $bits = preg_split('\/[.@]\/', $elattr, 2);$/;" v +bits Injector/Linkify.php /^ $bits = preg_split('#((?:https?|ftp):\/\/[^\\s\\'"<>()]+)#S', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);$/;" v +bits Injector/PurifierLinkify.php /^ $bits = preg_split('#%([a-z0-9]+\\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);$/;" v +blacklist URIFilter/HostBlacklist.php /^ protected $blacklist = array();$/;" v +blacklisted_directives Config.php /^ $blacklisted_directives = array();$/;" v +blk AttrDef/URI/IPv6.php /^ $blk = '(?:' . $hex . '{1,4})';$/;" v +block_wrap_end ChildDef/StrictBlockquote.php /^ $block_wrap_end = new HTMLPurifier_Token_End( $def->info_block_wrapper);$/;" v +block_wrap_start ChildDef/StrictBlockquote.php /^ $block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper);$/;" v +block_wrapper HTMLDefinition.php /^ $block_wrapper = $config->get('HTML.BlockWrapper');$/;" v +blockquote HTMLModule/Legacy.php /^ $blockquote = $this->addBlankElement('blockquote');$/;" v +bogusCommentState Lexer/PH5P.php /^ private function bogusCommentState() {$/;" f +bogusDoctypeState Lexer/PH5P.php /^ private function bogusDoctypeState() {$/;" f +bookmark Lexer/PH5P.php /^ $bookmark = array_search($node, $this->a_formatting, true) + 1;$/;" v +bookmark Lexer/PH5P.php /^ $bookmark = $fe_af_pos;$/;" v +border_color CSSDefinition.php /^ $border_color =$/;" v +border_style CSSDefinition.php /^ $border_style =$/;" v +border_width AttrTransform/Border.php /^ $border_width = $this->confiscateAttr($attr, 'border');$/;" v +border_width CSSDefinition.php /^ $border_width =$/;" v +br HTMLModule/Legacy.php /^ $br = $this->addBlankElement('br');$/;" v +browsable URIScheme.php /^ public $browsable = false;$/;" v +browsable URIScheme/ftp.php /^ public $browsable = true; \/\/ usually$/;" v +browsable URIScheme/http.php /^ public $browsable = true;$/;" v +browsable URIScheme/mailto.php /^ public $browsable = false;$/;" v +browsable URIScheme/news.php /^ public $browsable = false;$/;" v +browsable URIScheme/nntp.php /^ public $browsable = false;$/;" v +build ConfigSchema/Builder/ConfigSchema.php /^ public function build($interchange) {$/;" f +build ConfigSchema/Builder/Xml.php /^ public function build($interchange) {$/;" f +build ConfigSchema/InterchangeBuilder.php /^ public function build($interchange, $hash) {$/;" f +build IDAccumulator.php /^ public static function build($config, $context) {$/;" f +buildDirective ConfigSchema/Builder/Xml.php /^ public function buildDirective($directive) {$/;" f +buildDirective ConfigSchema/InterchangeBuilder.php /^ public function buildDirective($interchange, $hash) {$/;" f +buildFile ConfigSchema/InterchangeBuilder.php /^ public function buildFile($interchange, $file) {$/;" f +buildFromDirectory ConfigSchema/InterchangeBuilder.php /^ public static function buildFromDirectory($dir = null) {$/;" f +builder ConfigSchema/InterchangeBuilder.php /^ $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();$/;" v +button HTMLModule/Forms.php /^ $button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array($/;" v +bypass Encoder.php /^ public static function testEncodingSupportsASCII($encoding, $bypass = false) {$/;" v +bytesleft Encoder.php /^ $bytesleft = 0;$/;" v +bytesleft Encoder.php /^ $bytesleft = 1;$/;" v +bytesleft Encoder.php /^ $bytesleft = 2;$/;" v +bytesleft Encoder.php /^ $bytesleft = 3;$/;" v +bytesleft Encoder.php /^ $bytesleft = 0;$/;" v +bytevalue Encoder.php /^ $bytevalue = ord( $str[$i] );$/;" v +c AttrDef/CSS/FontFamily.php /^ for ($i = 0, $c = strlen($font); $i < $c; $i++) {$/;" v +c AttrDef/CSS/Length.php /^ $c = $length->compareTo($this->max);$/;" v +c AttrDef/CSS/Length.php /^ $c = $length->compareTo($this->min);$/;" v +c AttrDef/URI/IPv6.php /^ $c = count($aIP);$/;" v +c Encoder.php /^ $c = chr($i); \/\/ UTF-8 char$/;" v +c HTMLDefinition.php /^ $c = count($bits);$/;" v +c HTMLModule/Tidy.php /^ for ($i = 1, $c = count($this->levels); $i < $c; $i++) {$/;" v +c Injector/AutoParagraph.php /^ $c = count($raw_paragraphs);$/;" v +c Injector/Linkify.php /^ \/\/ $c = count$/;" v +c Injector/Linkify.php /^ for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {$/;" v +c Injector/PurifierLinkify.php /^ \/\/ $c = count$/;" v +c Injector/PurifierLinkify.php /^ for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {$/;" v +c Injector/RemoveEmpty.php /^ for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) {$/;" v +c Language.php /^ for ($i = 0, $c = count($array); $i < $c; $i++) {$/;" v +c PercentEncoder.php /^ for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {$/;" v +c PercentEncoder.php /^ for ($i = 0, $c = strlen($string); $i < $c; $i++) {$/;" v +c Strategy/MakeWellFormed.php /^ $c = count($skipped_tags);$/;" v +c Token.php /^ public function position($l = null, $c = null) {$/;" v +c URI.php /^ $c = strpos($this->path, '\/');$/;" v +c VarParser/Flexible.php /^ $c = explode(':', $keypair, 2);$/;" v +cache Config.php /^ $cache = $factory->create($type, $this);$/;" v +cache DefinitionCacheFactory.php /^ $cache = $new_cache;$/;" v +cache DefinitionCacheFactory.php /^ $cache = new $class($type);$/;" v +cache DefinitionCacheFactory.php /^ $cache = new HTMLPurifier_DefinitionCache_Serializer($type);$/;" v +cache LanguageFactory.php /^ $cache = array();$/;" v +cache LanguageFactory.php /^ $cache = compact($this->keys);$/;" v +caches DefinitionCacheFactory.php /^ protected $caches = array('Serializer' => array());$/;" v +call Token/Tag.php /^ * without having to use a function call <tt>is_a()<\/tt>.$/;" f +callbackArmorCommentEntities Lexer/DOMLex.php /^ public function callbackArmorCommentEntities($matches) {$/;" f +callbackUndoCommentSubst Lexer/DOMLex.php /^ public function callbackUndoCommentSubst($matches) {$/;" f +calls DefinitionCache.php /^ return $config->version . ',' . \/\/ possibly replace with function calls$/;" f +can AttrDef/CSS/Multiple.php /^ * lengths to be specified. This class can take a vanilla border-width$/;" c +caption ChildDef/Table.php /^ $caption = $collection;$/;" v +caption ChildDef/Table.php /^ $caption = false;$/;" v +caption HTMLModule/Legacy.php /^ $caption = $this->addBlankElement('caption');$/;" v +carryover Strategy/MakeWellFormed.php /^ $carryover = true;$/;" v +carryover Strategy/MakeWellFormed.php /^ $carryover = false;$/;" v +caseSensitive AttrTransform/EnumToCSS.php /^ protected $caseSensitive = false;$/;" v +case_sensitive AttrDef/Enum.php /^ protected $case_sensitive = false; \/\/ values according to W3C spec$/;" v +case_sensitive AttrDef/HTML/FrameTarget.php /^ protected $case_sensitive = false;$/;" v +case_sensitive AttrTransform/EnumToCSS.php /^ public function __construct($attr, $enum_to_css, $case_sensitive = false) {$/;" v +cat Lexer/PH5P.php /^ $cat = $this->getElementCategory($node->tagName);$/;" v +category Lexer/PH5P.php /^ $category = $this->getElementCategory($node);$/;" v +category Lexer/PH5P.php /^ $category = $this->getElementCategory($this->stack[$s]->nodeName);$/;" v +caught AttrDef/CSS/Background.php /^ $caught = array();$/;" v +caught AttrDef/CSS/Font.php /^ $caught = array(); \/\/ which stage 0 properties have we caught?$/;" v +caught AttrDef/CSS/ListStyle.php /^ $caught = array();$/;" v +cell_align HTMLModule/Tables.php /^ $cell_align = array($/;" v +cell_col HTMLModule/Tables.php /^ $cell_col = array_merge($/;" v +cell_t HTMLModule/Tables.php /^ $cell_t = array_merge($/;" v +char AttrDef/CSS/FontFamily.php /^ $char = HTMLPurifier_Encoder::unichr(hexdec($code));$/;" v +char Encoder.php /^ $char = '';$/;" v +char Encoder.php /^ $char = '';$/;" v +char Encoder.php /^ $char ='';$/;" v +char Encoder.php /^ $char = '';$/;" v +char Lexer/DOMLex.php /^ $char = '[^a-z!\\\/]';$/;" v +char Lexer/DirectLex.php /^ $char = @$string[$cursor];$/;" v +char Lexer/DirectLex.php /^ if ($char == '"' || $char == "'") {$/;" v +char Lexer/PH5P.php /^ $char = 0;$/;" v +char Lexer/PH5P.php /^ $char = 1;$/;" v +char Lexer/PH5P.php /^ $char = $this->char();$/;" v +char Lexer/PH5P.php /^ $char = $this->char();$/;" v +char Lexer/PH5P.php /^ $char = substr($this->data, $this->char, $len);$/;" v +char Lexer/PH5P.php /^ $char = $this->char();$/;" v +char Lexer/PH5P.php /^ $char = $this->character($this->char);$/;" v +char Lexer/PH5P.php /^ $char = (!$entity) ? '&' : $entity;$/;" v +char Lexer/PH5P.php /^ $char = (!$entity)$/;" v +char Lexer/PH5P.php /^ private function char() {$/;" f +char_class Lexer/PH5P.php /^ $char_class = '0-9';$/;" v +char_class Lexer/PH5P.php /^ $char_class = '0-9A-Fa-f';$/;" v +character Lexer/PH5P.php /^ private function character($s, $l = 0) {$/;" f +characters Lexer/PH5P.php /^ private function characters($char_class, $start) {$/;" f +chars_gen_delims URI.php /^ $chars_gen_delims = ':\/?#[]@';$/;" v +chars_pchar URI.php /^ $chars_pchar = $chars_sub_delims . ':@';$/;" v +chars_sub_delims URI.php /^ $chars_sub_delims = '!$&\\'()*+,;=';$/;" v +chatty Config.php /^ public $chatty = true;$/;" v +checkDefType DefinitionCache.php /^ public function checkDefType($def) {$/;" f +checkNeeded Injector.php /^ public function checkNeeded($config) {$/;" f +checks Injector.php /^ * This function checks if the HTML environment$/;" f +child Lexer/PH5P.php /^ $child = $furthest_block->firstChild;$/;" v +child_tokens Strategy/FixNesting.php /^ $child_tokens = array();$/;" v +child_tokens Strategy/FixNesting.php /^ if ($result === true || $child_tokens === $result) {$/;" v +children ErrorStruct.php /^ public $children = array();$/;" v +chmod DefinitionCache/Serializer.php /^ $chmod = '775';$/;" v +chmod DefinitionCache/Serializer.php /^ $chmod = '777';$/;" v +chunks HTMLDefinition.php /^ $chunks = preg_split('\/(,|[\\n\\r]+)\/', $list);$/;" v +class AttrDef/HTML/Pixels.php /^ $class = get_class($this);$/;" v +class DefinitionCacheFactory.php /^ $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";$/;" v +class HTMLModuleManager.php /^ $class = "HTMLPurifier_Injector_$injector";$/;" v +class LanguageFactory.php /^ $class = 'HTMLPurifier_Language_' . $pcode;$/;" v +class Printer.php /^ $class = str_replace($prefix, '', get_class($obj));$/;" v +class URISchemeRegistry.php /^ $class = 'HTMLPurifier_URIScheme_' . $scheme;$/;" v +cleanCSS Filter/ExtractStyleBlocks.php /^ public function cleanCSS($css, $config, $context) {$/;" f +cleanUTF8 Encoder.php /^ public static function cleanUTF8($str, $force_php = false) {$/;" f +cleanup DefinitionCache.php /^ abstract public function cleanup($config);$/;" f +cleanup DefinitionCache/Decorator.php /^ public function cleanup($config) {$/;" f +cleanup DefinitionCache/Null.php /^ public function cleanup($config) {$/;" f +cleanup DefinitionCache/Serializer.php /^ public function cleanup($config) {$/;" f +clear Lexer/PH5P.php /^ $clear = array('html', 'table');$/;" v +clear Lexer/PH5P.php /^ $clear = array('tbody', 'tfoot', 'thead', 'html');$/;" v +clear Lexer/PH5P.php /^ $clear = array('tr', 'html');$/;" v +clearStackToTableContext Lexer/PH5P.php /^ private function clearStackToTableContext($elements) {$/;" f +clearTheActiveFormattingElementsUpToTheLastMarker Lexer/PH5P.php /^ private function clearTheActiveFormattingElementsUpToTheLastMarker() {$/;" f +clear_fix Encoder.php /^ $clear_fix = array();$/;" v +clone Lexer/PH5P.php /^ $clone = $node->cloneNode();$/;" v +clone Lexer/PH5P.php /^ $clone = $formatting_element->cloneNode();$/;" v +clone Lexer/PH5P.php /^ $clone = $entry->cloneNode();$/;" v +closeCell Lexer/PH5P.php /^ private function closeCell() {$/;" f +closeHandler Lexer/PEARSax3.php /^ public function closeHandler(&$parser, $name) {$/;" f +closeTagOpenState Lexer/PH5P.php /^ private function closeTagOpenState() {$/;" f +code AttrDef/CSS/FontFamily.php /^ $code = $font[$i];$/;" v +code Bootstrap.php /^ $code = str_replace('_', '-', substr($class, 22));$/;" v +code EntityParser.php /^ $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];$/;" v +code HTMLModule/Text.php /^ $code = $this->addElement('code', 'Inline', 'Inline', 'Common');$/;" v +code Language.php /^ public $code = 'en';$/;" v +code LanguageFactory.php /^ $code = $this->validator->validate($/;" v +code LanguageFactory.php /^ $code = $this->validator->validate($code, $config, $context);$/;" v +code LanguageFactory.php /^ if ($code === false) $code = 'en'; \/\/ malformed code becomes English$/;" v +code LanguageFactory.php /^ public function create($config, $context, $code = false) {$/;" v +col ErrorCollector.php /^ foreach ($col_array as $col => $struct) {$/;" v +col ErrorCollector.php /^ $col = $token ? $token->col : $this->context->get('CurrentCol', true);$/;" v +coll_i AttrCollections.php /^ foreach ($module->attr_collections as $coll_i => $coll) {$/;" v +collect Lexer/DOMLex.php /^ protected function tokenizeDOM($node, &$tokens, $collect = false) {$/;" v +collection ChildDef/Table.php /^ $collection = array();$/;" v +collection ChildDef/Table.php /^ $collection = array(); \/\/ collected nodes$/;" v +color AttrDef/CSS/Color.php /^ $color = '#' . $color;$/;" v +color AttrDef/CSS/Color.php /^ $color = "rgb($new_triad)";$/;" v +color AttrDef/CSS/Color.php /^ $color = trim($color);$/;" v +colors AttrDef/CSS/Color.php /^ if ($colors === null) $colors = $config->get('Core.ColorKeywords');$/;" v +colors AttrDef/CSS/Color.php /^ static $colors = null;$/;" v +colors AttrDef/HTML/Color.php /^ if ($colors === null) $colors = $config->get('Core.ColorKeywords');$/;" v +colors AttrDef/HTML/Color.php /^ static $colors = null;$/;" v +cols ChildDef/Table.php /^ $cols = array();$/;" v +cols Printer/ConfigForm.php /^ public $cols = 18;$/;" v +comment Lexer/DOMLex.php /^ $comment = "\/<!--(.*?)(-->|\\z)\/is";$/;" v +comment Lexer/PH5P.php /^ $comment = $this->dom->createComment($token['data']);$/;" v +comment Lexer/PH5P.php /^ $comment = $this->dom->createComment($data);$/;" v +commentDashState Lexer/PH5P.php /^ private function commentDashState() {$/;" f +commentEndState Lexer/PH5P.php /^ private function commentEndState() {$/;" f +commentState Lexer/PH5P.php /^ private function commentState() {$/;" f +common HTMLModuleManager.php /^ $common = array($/;" v +common_ancestor Lexer/PH5P.php /^ $common_ancestor = $this->stack[$fe_s_pos - 1];$/;" v +compare DefinitionCache.php /^ $compare = version_compare($version, $config->version);$/;" v +compareTo Length.php /^ public function compareTo($l) {$/;" f +compat Bootstrap.php /^ $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&$/;" v +compress Printer/ConfigForm.php /^ protected $compress = false;$/;" v +cond Lexer/PH5P.php /^ $cond = isset($entity);$/;" v +cond Lexer/PH5P.php /^ $cond = strlen($e_name) > 0;$/;" v +conf URIDefinition.php /^ $conf = $config->get('URI.' . $name);$/;" v +config Config.php /^ $config = HTMLPurifier_Config::create($ret, $schema);$/;" v +config Config.php /^ $config = new HTMLPurifier_Config($definition);$/;" v +config Printer/ConfigForm.php /^ $config = $config[1];$/;" v +config URISchemeRegistry.php /^ if (!$config) $config = HTMLPurifier_Config::createDefault();$/;" v +configLookup AttrDef/HTML/LinkTypes.php /^ $configLookup = array($/;" v +confiscateAttr AttrTransform.php /^ public function confiscateAttr(&$attr, $key) {$/;" f +content ChildDef/Table.php /^ $content = array();$/;" v +content_model ContentSets.php /^ $content_model = $def->content_model;$/;" v +content_model HTMLModule.php /^ $content_model = trim($content_model);$/;" v +content_model_type HTMLModule.php /^ $content_model_type = strtolower(trim($content_model_type));$/;" v +content_sets HTMLModule.php /^ public $content_sets = array();$/;" v +content_sets HTMLModule/Forms.php /^ public $content_sets = array($/;" v +content_sets HTMLModule/List.php /^ public $content_sets = array('Flow' => 'List');$/;" v +content_sets HTMLModule/Scripting.php /^ public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');$/;" v +content_sets HTMLModule/Text.php /^ public $content_sets = array($/;" v +contents HTMLModule/Edit.php /^ $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';$/;" v +context ConfigSchema/Validator.php /^ protected $context = array();$/;" v +context ErrorCollector.php /^ $context = array_pop($context_stack);$/;" v +context Printer.php /^ $context = new HTMLPurifier_Context();$/;" v +context Printer/HTMLDefinition.php /^ $context = new HTMLPurifier_Context();$/;" v +context_stack ErrorCollector.php /^ $context_stack = array(array());$/;" v +convert UnitConverter.php /^ public function convert($length, $to_unit) {$/;" f +convertFromUTF8 Encoder.php /^ public static function convertFromUTF8($str, $config, $context) {$/;" f +convertToASCIIDumbLossless Encoder.php /^ public static function convertToASCIIDumbLossless($str) {$/;" f +convertToLookup ContentSets.php /^ protected function convertToLookup($string) {$/;" f +convertToUTF8 Encoder.php /^ public static function convertToUTF8($str, $config, $context) {$/;" f +converter Length.php /^ $converter = new HTMLPurifier_UnitConverter();$/;" v +copy DefinitionCache/Decorator.php /^ public function copy() {$/;" f +copy DefinitionCache/Decorator/Cleanup.php /^ public function copy() {$/;" f +copy DefinitionCache/Decorator/Memory.php /^ public function copy() {$/;" f +could AttrDef/CSS/TextDecoration.php /^ * @note This class could be generalized into a version that acts sort of$/;" c +cp UnitConverter.php /^ $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; \/\/ internal precision$/;" v +create Config.php /^ public static function create($config, $schema = null) {$/;" f +create DefinitionCacheFactory.php /^ public function create($type, $config) {$/;" f +create ElementDef.php /^ public static function create($content_model, $content_model_type, $attr) {$/;" f +create LanguageFactory.php /^ public function create($config, $context, $code = false) {$/;" f +create Lexer.php /^ public static function create($config) {$/;" f +createComment TokenFactory.php /^ public function createComment($data) {$/;" f +createDefault Config.php /^ public static function createDefault() {$/;" f +createEmpty TokenFactory.php /^ public function createEmpty($name, $attr = array()) {$/;" f +createEnd TokenFactory.php /^ public function createEnd($name) {$/;" f +createStart TokenFactory.php /^ public function createStart($name, $attr = array()) {$/;" f +createText TokenFactory.php /^ public function createText($data) {$/;" f +css AttrDef/CSS.php /^ $css = $this->parseCDATA($css);$/;" v +css AttrTransform/ImgSpace.php /^ protected $css = array($/;" v +css Filter/ExtractStyleBlocks.php /^ $css = str_replace($/;" v +css Filter/ExtractStyleBlocks.php /^ $css = substr($css, 0, -3);$/;" v +css Filter/ExtractStyleBlocks.php /^ $css = substr($css, 4);$/;" v +css Filter/ExtractStyleBlocks.php /^ $css = $this->_tidy->print->plain();$/;" v +css Filter/ExtractStyleBlocks.php /^ $css = trim($css);$/;" v +css_definition Filter/ExtractStyleBlocks.php /^ $css_definition = $config->getDefinition('CSS');$/;" v +css_name AttrTransform/Length.php /^ public function __construct($name, $css_name = null) {$/;" v +current Injector.php /^ $current = $this->inputTokens[$i];$/;" v +current Injector.php /^ protected function current(&$i, &$current) {$/;" f +current Lexer/PH5P.php /^ $current = end($this->stack)->nodeName;$/;" v +current_col Lexer/DirectLex.php /^ $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1);$/;" v +current_col Lexer/DirectLex.php /^ $current_col = 0;$/;" v +current_col Lexer/DirectLex.php /^ $current_col = false;$/;" v +current_line Lexer/DirectLex.php /^ $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor);$/;" v +current_line Lexer/DirectLex.php /^ $current_line = 1;$/;" v +current_line Lexer/DirectLex.php /^ $current_line = false;$/;" v +current_parent Strategy/MakeWellFormed.php /^ $current_parent = array_pop($this->stack);$/;" v +current_token AttrValidator.php /^ $current_token =& $context->get('CurrentToken', true);$/;" v +cursor AttrDef/CSS/Filter.php /^ $cursor = $function_length + 1;$/;" v +cursor Lexer/DirectLex.php /^ $cursor = $end ? $position_comment_end : $position_comment_end + 3;$/;" v +cursor Lexer/DirectLex.php /^ $cursor = $position_next_gt + 1;$/;" v +cursor Lexer/DirectLex.php /^ $cursor = $size;$/;" v +cursor Lexer/DirectLex.php /^ $cursor = strpos($string, $char, $cursor);$/;" v +cursor Lexer/DirectLex.php /^ $cursor = $position_next_lt + 1;$/;" v +cursor Lexer/DirectLex.php /^ $cursor = $position_next_gt + 1;$/;" v +cursor Lexer/DirectLex.php /^ $cursor = 0; \/\/ current position in string (moves forward)$/;" v +cursor Lexer/DirectLex.php /^ $cursor = 0; \/\/ our location in the text$/;" v +custom_injectors Strategy/MakeWellFormed.php /^ $custom_injectors = $injectors['Custom'];$/;" v +d Config.php /^ $d = $this->def->info[$key];$/;" v +d_defs AttrValidator.php /^ $d_defs = $definition->info_global_attr;$/;" v +d_int ConfigSchema/Validator.php /^ $d_int = HTMLPurifier_VarParser::$types[$d->type];$/;" v +data Generator.php /^ $data = preg_replace('#\/\/\\s*$#', '', $token->data);$/;" v +data Lexer/DOMLex.php /^ $data = substr($data, 0, -3);$/;" v +data Lexer/DOMLex.php /^ $data = substr($new_data, 4);$/;" v +data Lexer/DOMLex.php /^ $data = $node->data;$/;" v +data Lexer/PH5P.php /^ $data = $this->characters('^>', $this->char);$/;" v +data Lexer/PH5P.php /^ $data = str_replace("\\r", null, $data);$/;" v +data Lexer/PH5P.php /^ $data = str_replace("\\r\\n", "\\n", $data);$/;" v +data PropertyList.php /^ protected $data = array();$/;" v +data Strategy/RemoveForeignElements.php /^ $data = $token->data;$/;" v +dataHandler Lexer/PEARSax3.php /^ public function dataHandler(&$parser, $data) {$/;" f +dataState Lexer/PH5P.php /^ private function dataState() {$/;" f +declarations AttrDef/CSS.php /^ $declarations = explode(';', $css);$/;" v +decorate DefinitionCache/Decorator.php /^ public function decorate(&$cache) {$/;" f +decorator DefinitionCache/Decorator.php /^ $decorator = $this->copy();$/;" v +decorator DefinitionCacheFactory.php /^ $decorator = new $class;$/;" v +decorators DefinitionCacheFactory.php /^ protected $decorators = array();$/;" v +def AttrDef/CSS/Background.php /^ $def = $config->getCSSDefinition();$/;" v +def AttrDef/CSS/Border.php /^ $def = $config->getCSSDefinition();$/;" v +def AttrDef/CSS/Font.php /^ $def = $config->getCSSDefinition();$/;" v +def AttrDef/CSS/ListStyle.php /^ $def = $config->getCSSDefinition();$/;" v +def ChildDef/StrictBlockquote.php /^ $def = $config->getHTMLDefinition();$/;" v +def ChildDef/StrictBlockquote.php /^ $def = $config->getHTMLDefinition();$/;" v +def Config.php /^ $def = $this->def->info[$key];$/;" v +def ElementDef.php /^ $def = new HTMLPurifier_ElementDef();$/;" v +def Filter/ExtractStyleBlocks.php /^ $def = $css_definition->info[$name];$/;" v +def HTMLDefinition.php /^ $def = $this->manager->getElement($parent, true);$/;" v +def HTMLModuleManager.php /^ $def = $new_def;$/;" v +def HTMLModuleManager.php /^ $def = false;$/;" v +def Injector.php /^ $def = $config->getHTMLDefinition();$/;" v +def Length.php /^ $def = new HTMLPurifier_AttrDef_CSS_Number();$/;" v +def Lexer/DOMLex.php /^ $def = $config->getDefinition('HTML');$/;" v +def Printer/ConfigForm.php /^ $def = $this->config->def->info["$ns.$directive"];$/;" v +def Printer/ConfigForm.php /^ $def = $config->def->info["$ns.$directive"];$/;" v +def Printer/HTMLDefinition.php /^ $def = $this->def;$/;" v +def Strategy/FixNesting.php /^ $def = $definition->info[$tokens[$i]->name];$/;" v +def Strategy/FixNesting.php /^ $def = $definition->info_parent_def;$/;" v +def URI.php /^ $def = $config->getDefinition('URI');$/;" v +def URIFilter/MakeAbsolute.php /^ $def = $config->getDefinition('URI');$/;" v +def_i AttrCollections.php /^ $def_i = trim($def_i, '*');$/;" v +def_i AttrCollections.php /^ foreach ($attr as $def_i => $def) {$/;" v +def_injectors Strategy/MakeWellFormed.php /^ $def_injectors = $definition->info_injector;$/;" v +default StringHashParser.php /^ public $default = 'ID';$/;" v +defaultLevel HTMLModule/Tidy.php /^ public $defaultLevel = null;$/;" v +defaultLevel HTMLModule/Tidy/Name.php /^ public $defaultLevel = 'heavy';$/;" v +defaultLevel HTMLModule/Tidy/Proprietary.php /^ public $defaultLevel = 'light';$/;" v +defaultLevel HTMLModule/Tidy/Strict.php /^ public $defaultLevel = 'light';$/;" v +defaultLevel HTMLModule/Tidy/Transitional.php /^ public $defaultLevel = 'heavy';$/;" v +defaultLevel HTMLModule/Tidy/XHTML.php /^ public $defaultLevel = 'medium';$/;" v +default_port URIScheme.php /^ public $default_port = null;$/;" v +default_port URIScheme/ftp.php /^ public $default_port = 21;$/;" v +default_port URIScheme/http.php /^ public $default_port = 80;$/;" v +default_port URIScheme/https.php /^ public $default_port = 443;$/;" v +default_port URIScheme/nntp.php /^ public $default_port = 119;$/;" v +defaults ConfigSchema.php /^ public $defaults = array();$/;" v +defines_child_def HTMLModule.php /^ public $defines_child_def = false;$/;" v +defines_child_def HTMLModule/Edit.php /^ public $defines_child_def = true;$/;" v +defines_child_def HTMLModule/Tidy/Strict.php /^ public $defines_child_def = true;$/;" v +definition AttrDef/CSS.php /^ $definition = $config->getCSSDefinition();$/;" v +definition AttrValidator.php /^ $definition = $config->getHTMLDefinition();$/;" v +definition Config.php /^ $definition = HTMLPurifier_ConfigSchema::instance();$/;" v +definition Strategy/FixNesting.php /^ $definition = $config->getHTMLDefinition();$/;" v +definition Strategy/MakeWellFormed.php /^ $definition = $config->getHTMLDefinition();$/;" v +definition Strategy/RemoveForeignElements.php /^ $definition = $config->getHTMLDefinition();$/;" v +defs AttrValidator.php /^ $defs = $definition->info[$token->name]->attr;$/;" v +delete HTMLDefinition.php /^ $delete = false;$/;" v +delete HTMLDefinition.php /^ $delete = false;$/;" v +delete HTMLDefinition.php /^ $delete = true;$/;" v +delete HTMLDefinition.php /^ $delete = true;$/;" v +delete Strategy/MakeWellFormed.php /^ $delete = array_shift($token);$/;" v +depth ChildDef/StrictBlockquote.php /^ $depth = 0;$/;" v +depth LanguageFactory.php /^ static $depth = 0; \/\/ recursion protection$/;" v +depth Strategy/FixNesting.php /^ for ($j = $i, $depth = 0; ; $j++) {$/;" v +descendants_are_inline ElementDef.php /^ public $descendants_are_inline = false;$/;" v +describing ConfigSchema/Interchange/Directive.php /^ * Interchange component class describing configuration directives.$/;" c +dest_state UnitConverter.php /^ if (isset($x[$to_unit])) $dest_state = $k;$/;" v +dest_unit UnitConverter.php /^ $dest_unit = $to_unit;$/;" v +dest_unit UnitConverter.php /^ $dest_unit = self::$units[$state][$dest_state][0];$/;" v +destroy Context.php /^ public function destroy($name) {$/;" f +dh ConfigSchema/InterchangeBuilder.php /^ $dh = opendir($dir);$/;" v +dh DefinitionCache/Serializer.php /^ $dh = opendir($dir);$/;" v +digits AttrDef/Integer.php /^ $digits = $integer = substr($integer, 1); \/\/ rm unnecessary plus$/;" v +digits AttrDef/Integer.php /^ $digits = $integer;$/;" v +digits AttrDef/Integer.php /^ $digits = substr($integer, 1);$/;" v +dir ConfigSchema/InterchangeBuilder.php /^ if (!$dir) $dir = HTMLPURIFIER_PREFIX . '\/HTMLPurifier\/ConfigSchema\/schema\/';$/;" v +dir DefinitionCache/Serializer.php /^ $dir = $this->generateDirectoryPath($config);$/;" v +directive Config.php /^ foreach ($namespace_values as $directive => $value) {$/;" v +directive Config.php /^ $directive = $value;$/;" v +directive Config.php /^ if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;$/;" v +directive ConfigSchema/InterchangeBuilder.php /^ $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();$/;" v +directive Printer/ConfigForm.php /^ foreach ($directives as $directive => $value) {$/;" v +directive_disp Printer/ConfigForm.php /^ $directive_disp = $directive;$/;" v +directive_disp Printer/ConfigForm.php /^ $directive_disp = substr($directive, 0, $this->compress - 2) . '...';$/;" v +directives ConfigSchema/Interchange.php /^ public $directives = array();$/;" v +directly PropertyListIterator.php /^ * Property list iterator. Do not instantiate this class directly.$/;" c +directory DefinitionCache/Serializer.php /^ $directory = $this->generateDirectoryPath($config);$/;" v +discard Printer/HTMLDefinition.php /^ foreach ($array as $discard => $obj) {$/;" v +div HTMLModule/Legacy.php /^ $div = $this->addBlankElement('div');$/;" v +div UnitConverter.php /^ private function div($s1, $s2, $scale) {$/;" f +dl HTMLModule/Legacy.php /^ $dl = $this->addBlankElement('dl');$/;" v +doSetup CSSDefinition.php /^ protected function doSetup($config) {$/;" f +doSetup Definition.php /^ abstract protected function doSetup($config);$/;" f +doSetup HTMLDefinition.php /^ protected function doSetup($config) {$/;" f +doSetup URIDefinition.php /^ protected function doSetup($config) {$/;" f +doSetupProprietary CSSDefinition.php /^ protected function doSetupProprietary($config) {$/;" f +doSetupTricky CSSDefinition.php /^ protected function doSetupTricky($config) {$/;" f +doc Lexer/DOMLex.php /^ $doc = new DOMDocument();$/;" v +doc Lexer/PH5P.php /^ $doc = $parser->save();$/;" v +doc_url Printer/ConfigForm.php /^ $name, $doc_url = null, $compress = false$/;" v +doctype DoctypeRegistry.php /^ $doctype = 'HTML 4.01';$/;" v +doctype DoctypeRegistry.php /^ $doctype = 'XHTML 1.0';$/;" v +doctype DoctypeRegistry.php /^ $doctype = new HTMLPurifier_Doctype($/;" v +doctype DoctypeRegistry.php /^ $doctype = $config->get('HTML.CustomDoctype');$/;" v +doctype DoctypeRegistry.php /^ $doctype = $config->get('HTML.Doctype');$/;" v +doctype DoctypeRegistry.php /^ if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype];$/;" v +doctype Lexer/PH5P.php /^ $doctype = new DOMDocumentType(null, null, 'HTML');$/;" v +doctype Printer/HTMLDefinition.php /^ $doctype = $this->def->doctype;$/;" v +doctypeNameState Lexer/PH5P.php /^ private function doctypeNameState() {$/;" f +doctypeState Lexer/PH5P.php /^ private function doctypeState() {$/;" f +does AttrDef/CSS/Multiple.php /^ * shorthand declaration. Thus, this class does not allow inherit.$/;" c +doesn IDAccumulator.php /^ * @note This function doesn't care about duplicates$/;" f +domainlabel AttrDef/URI/Host.php /^ $domainlabel = "$an($and*$an)?";$/;" v +done AttrDef/CSS/Border.php /^ $done = array(); \/\/ segments we've finished$/;" v +dp UnitConverter.php /^ $dp = strpos($n, '.'); \/\/ decimal position$/;" v +e AttrValidator.php /^ $e =& $context->get('ErrorCollector', true);$/;" v +e HTMLModule/Target.php /^ $e = $this->addBlankElement($name);$/;" v +e HTMLModule/Tidy.php /^ $e = $this->addBlankElement($element);$/;" v +e HTMLModule/Tidy.php /^ $e = $this->info[$element];$/;" v +e HTMLModule/Tidy.php /^ $e = $this->addBlankElement($element);$/;" v +e HTMLModule/Tidy.php /^ $e = $this->info[$element];$/;" v +e HTMLModule/Tidy.php /^ $e = $this;$/;" v +e Lexer/DirectLex.php /^ $e =& $context->get('ErrorCollector');$/;" v +e Lexer/DirectLex.php /^ $e = false;$/;" v +e Strategy/FixNesting.php /^ $e =& $context->get('ErrorCollector', true);$/;" v +e Strategy/MakeWellFormed.php /^ $e = $context->get('ErrorCollector', true);$/;" v +e Strategy/RemoveForeignElements.php /^ $e =& $context->get('ErrorCollector');$/;" v +e Strategy/RemoveForeignElements.php /^ $e = false;$/;" v +e_name Lexer/PH5P.php /^ $e_name = $this->characters($char_class, $this->char + $char + 1);$/;" v +e_name Lexer/PH5P.php /^ $e_name = $this->characters('0-9A-Za-z;', $this->char + 1);$/;" v +el ChildDef/Custom.php /^ $el = '[#a-zA-Z0-9_.-]+';$/;" v +el Lexer/PH5P.php /^ $el = $this->insertElement($token);$/;" v +el Lexer/PH5P.php /^ $el = $this->dom->createElement($token['name']);$/;" v +elattr HTMLDefinition.php /^ foreach ($allowed_attributes_mutable as $elattr => $d) {$/;" v +element ContentSets.php /^ foreach ($set as $element => $x) {$/;" v +element Generator.php /^ public function generateAttributes($assoc_array_of_attributes, $element = false) {$/;" v +element HTMLDefinition.php /^ $element = htmlspecialchars($bits[0]);$/;" v +element HTMLDefinition.php /^ $element = $chunk;$/;" v +element HTMLDefinition.php /^ $element = htmlspecialchars($element); \/\/ PHP doesn't escape errors, be careful!$/;" v +element HTMLDefinition.php /^ $element = $module->addBlankElement($element_name);$/;" v +element HTMLDefinition.php /^ $element = $module->info[$element_name];$/;" v +element HTMLDefinition.php /^ foreach ($allowed_elements as $element => $d) {$/;" v +element HTMLDefinition.php /^ $element = $module->addBlankElement($element_name);$/;" v +element HTMLDefinition.php /^ $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);$/;" v +element HTMLModule/Name.php /^ $element = $this->addBlankElement($name);$/;" v +element HTMLModule/Tidy.php /^ $element = $params['element'];$/;" v +element HTMLModule/Tidy.php /^ $element = $params['element'];$/;" v +element Injector.php /^ if (is_int($element)) $element = $attributes;$/;" v +element Injector.php /^ foreach ($this->needed as $element => $attributes) {$/;" v +element Lexer/PH5P.php /^ $element = $this->insertElement($token);$/;" v +element Lexer/PH5P.php /^ $element = $this->insertElement($token, false);$/;" v +element Lexer/PH5P.php /^ $element = $this->insertElement($token);$/;" v +element Lexer/PH5P.php /^ $element = $this->insertElement($token, false);$/;" v +element Lexer/PH5P.php /^ $element = $this->insertElement($token);$/;" v +element Lexer/PH5P.php /^ $element = $this->insertElement($token, false);$/;" v +element Printer.php /^ protected function element($tag, $contents, $attr = array(), $escape = true) {$/;" f +element Strategy/MakeWellFormed.php /^ $element = clone $parent;$/;" v +element Strategy/MakeWellFormed.php /^ $element = clone $skipped_tags[$j];$/;" v +elementEmpty Printer.php /^ protected function elementEmpty($tag, $attr = array()) {$/;" f +elementInScope Lexer/PH5P.php /^ private function elementInScope($el, $table = false) {$/;" f +elementLookup HTMLModuleManager.php /^ public $elementLookup = array();$/;" v +elements ChildDef.php /^ public $elements = array();$/;" v +elements ChildDef/Required.php /^ $elements = array_flip($elements);$/;" v +elements ChildDef/Required.php /^ $elements = explode('|', $elements);$/;" v +elements ChildDef/Required.php /^ $elements = str_replace(' ', '', $elements);$/;" v +elements ChildDef/Required.php /^ public $elements = array();$/;" v +elements ChildDef/Table.php /^ public $elements = array('tr' => true, 'tbody' => true, 'thead' => true,$/;" v +elements HTMLDefinition.php /^ $elements = array();$/;" v +elements HTMLModule.php /^ public $elements = array();$/;" v +elements HTMLModule/Name.php /^ $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');$/;" v +elements HTMLModule/Scripting.php /^ public $elements = array('script', 'noscript');$/;" v +elements HTMLModule/Target.php /^ $elements = array('a');$/;" v +elements HTMLModuleManager.php /^ $elements = array();$/;" v +elements Lexer/PH5P.php /^ $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');$/;" v +elements Lexer/PH5P.php /^ $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);$/;" v +elements Printer/HTMLDefinition.php /^ $elements = $def->elements;$/;" v +elements Printer/HTMLDefinition.php /^ $elements = array();$/;" v +elements Printer/HTMLDefinition.php /^ $elements = array_flip(array('col', 'caption', 'colgroup', 'thead',$/;" v +elements Printer/HTMLDefinition.php /^ $elements = array();$/;" v +elements Strategy/MakeWellFormed.php /^ $elements = $definition->info[$parent->name]->child->getAllowedElements($config);$/;" v +elements_in_stack Lexer/PH5P.php /^ $elements_in_stack = count($this->stack);$/;" v +em HTMLModule/Text.php /^ $em = $this->addElement('em', 'Inline', 'Inline', 'Common');$/;" v +embed HTMLModule/SafeEmbed.php /^ $embed = $this->addElement($/;" v +embeds AttrDef/URI.php /^ $embeds = (bool) $string;$/;" v +emit Lexer/PH5P.php /^ $emit = $this->tree->emitToken($token);$/;" v +emitToken Lexer/PH5P.php /^ private function emitToken($token) {$/;" f +emitToken Lexer/PH5P.php /^ public function emitToken($token) {$/;" f +encode PercentEncoder.php /^ public function encode($string) {$/;" f +encoder URI.php /^ $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');$/;" v +encoding Encoder.php /^ $encoding = $config->get('Core.Encoding');$/;" v +encoding PercentEncoder.php /^ $encoding = strtoupper($encoding);$/;" v +encoding PercentEncoder.php /^ $encoding = substr($part, 0, 2);$/;" v +encodings Encoder.php /^ static $encodings = array();$/;" v +end Lexer/DirectLex.php /^ $end = false;$/;" v +end Lexer/DirectLex.php /^ $end = true;$/;" v +end Printer.php /^ protected function end($tag) {$/;" f +entities Lexer/PH5P.php /^ private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute',$/;" v +entity EntityParser.php /^ $entity = $matches[0];$/;" v +entity Lexer/PH5P.php /^ $entity = $id;$/;" v +entity Lexer/PH5P.php /^ $entity = $this->character($start, $this->char);$/;" v +entity Lexer/PH5P.php /^ $entity = $this->entity();$/;" v +entity Lexer/PH5P.php /^ private function entity() {$/;" f +entityDataState Lexer/PH5P.php /^ private function entityDataState() {$/;" f +entityInAttributeValueState Lexer/PH5P.php /^ private function entityInAttributeValueState() {$/;" f +entry Lexer/PH5P.php /^ $entry = $this->a_formatting[$a];$/;" v +entry Lexer/PH5P.php /^ $entry = $this->a_formatting[$a];$/;" v +entry Lexer/PH5P.php /^ $entry = end($this->a_formatting);$/;" v +entry Lexer/PH5P.php /^ $entry = end($this->a_formatting);$/;" v +enumToCSS AttrTransform/EnumToCSS.php /^ protected $enumToCSS = array();$/;" v +error ConfigSchema/Validator.php /^ protected function error($target, $msg) {$/;" f +error ConfigSchema/ValidatorAtom.php /^ protected function error($msg) {$/;" f +error ErrorCollector.php /^ $error = $this->locale->getErrorName($severity);$/;" v +error ErrorCollector.php /^ $error = array($/;" v +error Language.php /^ public $error = false;$/;" v +error Strategy/MakeWellFormed.php /^ $error = $injector->prepare($config, $context);$/;" v +error VarParser.php /^ protected function error($msg) {$/;" f +errorGeneric VarParser.php /^ protected function errorGeneric($var, $type) {$/;" f +errorInconsistent VarParser.php /^ protected function errorInconsistent($class, $type) {$/;" f +errorNames Language.php /^ public $errorNames = array();$/;" v +errorNames Language/messages/en.php /^$errorNames = array($/;" v +errors ErrorCollector.php /^ if ($errors === null) $errors = $this->errors;$/;" v +errors ErrorCollector.php /^ public function getHTMLFormatted($config, $errors = null) {$/;" v +errors ErrorStruct.php /^ public $errors = array();$/;" v +escape Generator.php /^ public function escape($string, $quote = ENT_COMPAT) {$/;" f +escape Lexer/PH5P.php /^ private $escape = false;$/;" v +escape Printer.php /^ protected function escape($string) {$/;" f +escapeCDATA Lexer.php /^ protected static function escapeCDATA($string) {$/;" f +escapeCommentedCDATA Lexer.php /^ protected static function escapeCommentedCDATA($string) {$/;" f +escapeHandler Lexer/PEARSax3.php /^ public function escapeHandler(&$parser, $data) {$/;" f +escape_invalid_children ChildDef/Required.php /^ $escape_invalid_children = $config->get('Core.EscapeInvalidChildren');$/;" v +escape_invalid_tags Strategy/MakeWellFormed.php /^ $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');$/;" v +escape_invalid_tags Strategy/RemoveForeignElements.php /^ $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');$/;" v +evalArray ConfigSchema/InterchangeBuilder.php /^ protected function evalArray($contents) {$/;" f +evalExpression VarParser/Native.php /^ protected function evalExpression($expr) {$/;" f +exclude_stack Strategy/FixNesting.php /^ $exclude_stack = array();$/;" v +excluded Strategy/FixNesting.php /^ $excluded = true;$/;" v +excluded Strategy/FixNesting.php /^ $excluded = false;$/;" v +excludes ElementDef.php /^ public $excludes = array();$/;" v +excludes Strategy/FixNesting.php /^ $excludes = $def->excludes;$/;" v +excludes Strategy/FixNesting.php /^ $excludes = array(); \/\/ not used, but good to initialize anyway$/;" v +execute Strategy.php /^ abstract public function execute($tokens, $config, $context);$/;" f +execute Strategy/Composite.php /^ public function execute($tokens, $config, $context) {$/;" f +execute Strategy/FixNesting.php /^ public function execute($tokens, $config, $context) {$/;" f +execute Strategy/MakeWellFormed.php /^ public function execute($tokens, $config, $context) {$/;" f +execute Strategy/RemoveForeignElements.php /^ public function execute($tokens, $config, $context) {$/;" f +execute Strategy/ValidateAttributes.php /^ public function execute($tokens, $config, $context) {$/;" f +exists Context.php /^ public function exists($name) {$/;" f +expandIdentifiers AttrCollections.php /^ public function expandIdentifiers(&$attr, $attr_types) {$/;" f +expects AttrTransform/SafeParam.php /^ * This class expects an injector to add the necessary parameters tags.$/;" c +export ConfigSchema/Builder/Xml.php /^ protected function export($var) {$/;" f +external ConfigSchema/Interchange/Directive.php /^ public $external = array();$/;" v +extra Config.php /^ $extra = " on line {$frame['line']} in file {$frame['file']}";$/;" v +extra Config.php /^ $extra = '';$/;" v +extractBody Lexer.php /^ public function extractBody($html) {$/;" f +f HTMLModule/Tidy.php /^ $f =& $e->$type;$/;" v +factor UnitConverter.php /^ $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);$/;" v +factory Config.php /^ $factory = HTMLPurifier_DefinitionCacheFactory::instance();$/;" v +factory Language.php /^ $factory = HTMLPurifier_LanguageFactory::instance();$/;" v +fallback Language.php /^ public $fallback = false;$/;" v +fallback Language/messages/en-x-test.php /^$fallback = 'en';$/;" v +fallback Language/messages/en-x-testmini.php /^$fallback = 'en';$/;" v +fallback Language/messages/en.php /^$fallback = false;$/;" v +fallback LanguageFactory.php /^ $fallback = $raw_fallback ? $raw_fallback : 'en';$/;" v +fallback LanguageFactory.php /^ $fallback = 'en';$/;" v +fallback LanguageFactory.php /^ $fallback = ($code != 'en') ? 'en' : false;$/;" v +fallback_cache LanguageFactory.php /^ $fallback_cache = $this->cache[$fallback];$/;" v +fallbacks LanguageFactory.php /^ * Creates a language object, handles class fallbacks$/;" c +fb_s_pos Lexer/PH5P.php /^ $fb_s_pos = array_search($furthest_block, $this->stack, true);$/;" v +fe_af_pos Lexer/PH5P.php /^ $fe_af_pos = $a;$/;" v +fe_af_pos Lexer/PH5P.php /^ $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);$/;" v +fe_s_pos Lexer/PH5P.php /^ $fe_s_pos = array_search($formatting_element, $this->stack, true);$/;" v +fh StringHashParser.php /^ $fh = fopen($file, 'r');$/;" v +fields Printer/ConfigForm.php /^ protected $fields = array();$/;" v +file Bootstrap.php /^ $file = 'HTMLPurifier\/Language\/classes\/' . $code . '.php';$/;" v +file Bootstrap.php /^ $file = str_replace('_', '\/', $class) . '.php';$/;" v +file Bootstrap.php /^ $file = HTMLPurifier_Bootstrap::getPath($class);$/;" v +file DefinitionCache/Serializer.php /^ $file = $this->generateFilePath($config);$/;" v +file EntityLookup.php /^ $file = HTMLPURIFIER_PREFIX . '\/HTMLPurifier\/EntityLookup\/entities.ser';$/;" v +file LanguageFactory.php /^ $file = $this->dir . '\/Language\/classes\/' . $code . '.php';$/;" v +filename LanguageFactory.php /^ $filename = $this->dir . '\/Language\/messages\/en.php';$/;" v +filename LanguageFactory.php /^ $filename = $this->dir . '\/Language\/messages\/' . $code . '.php';$/;" v +files ConfigSchema/InterchangeBuilder.php /^ $files = array();$/;" v +filter AttrDef/HTML/Class.php /^ protected function filter($tokens, $config, $context) {$/;" f +filter AttrDef/HTML/Nmtokens.php /^ protected function filter($tokens, $config, $context) {$/;" f +filter PropertyListIterator.php /^ public function __construct(Iterator $iterator, $filter = null) {$/;" v +filter URIDefinition.php /^ public function filter(&$uri, $config, $context) {$/;" f +filter URIFilter.php /^ abstract public function filter(&$uri, $config, $context);$/;" f +filter URIFilter/DisableExternal.php /^ public function filter(&$uri, $config, $context) {$/;" f +filter URIFilter/DisableExternalResources.php /^ public function filter(&$uri, $config, $context) {$/;" f +filter URIFilter/HostBlacklist.php /^ public function filter(&$uri, $config, $context) {$/;" f +filter URIFilter/MakeAbsolute.php /^ public function filter(&$uri, $config, $context) {$/;" f +filter URIFilter/Munge.php /^ public function filter(&$uri, $config, $context) {$/;" f +filters URIDefinition.php /^ protected $filters = array();$/;" v +final AttrDef/CSS/Font.php /^ $final = ''; \/\/ output$/;" v +final AttrDef/CSS/FontFamily.php /^ $final = '';$/;" v +final AttrDef/CSS/FontFamily.php /^ $final = rtrim($final, ', ');$/;" v +final AttrDef/CSS/Multiple.php /^ $final = '';$/;" v +final AttrDef/CSS/TextDecoration.php /^ $final = '';$/;" v +final AttrDef/CSS/TextDecoration.php /^ $final = rtrim($final);$/;" v +finalize Config.php /^ public function finalize() {$/;" f +finalized Config.php /^ protected $finalized = false;$/;" v +first AttrDef/URI/IPv6.php /^ $first = explode(':', $first);$/;" v +first_char Lexer/DirectLex.php /^ $first_char = @$quoted_value[0];$/;" v +first_char Lexer/DirectLex.php /^ $first_char = @$string[$cursor];$/;" v +five Printer.php /^ if ($five === null) $five = version_compare(PHP_VERSION, '5', '>=');$/;" v +five Printer.php /^ static $five = null;$/;" v +fixes HTMLModule/Tidy.php /^ $fixes = $this->makeFixes();$/;" v +fixesForLevel HTMLModule/Tidy.php /^ public $fixesForLevel = array($/;" v +fixes_lookup HTMLModule/Tidy.php /^ $fixes_lookup = $this->getFixesForLevel($level);$/;" v +float AttrDef/CSS/AlphaValue.php /^ $float = (float) $result;$/;" v +flush DefinitionCache.php /^ abstract public function flush($config);$/;" f +flush DefinitionCache/Decorator.php /^ public function flush($config) {$/;" f +flush DefinitionCache/Null.php /^ public function flush($config) {$/;" f +flush DefinitionCache/Serializer.php /^ public function flush($config) {$/;" f +font AttrDef/CSS/FontFamily.php /^ $font = $new_font;$/;" v +font AttrDef/CSS/FontFamily.php /^ $font = substr($font, 1, $length - 2);$/;" v +font AttrDef/CSS/FontFamily.php /^ $font = str_replace("'", "\\\\'", $font);$/;" v +font AttrDef/CSS/FontFamily.php /^ $font = str_replace("\\\\", "\\\\\\\\", $font);$/;" v +font AttrDef/CSS/FontFamily.php /^ $font = trim($font);$/;" v +font_family AttrDef/CSS/Font.php /^ $font_family =$/;" v +font_size AttrDef/CSS/Font.php /^ $font_size = $bits[$i];$/;" v +fonts AttrDef/CSS/FontFamily.php /^ $fonts = explode(',', $string);$/;" v +for AttrDef.php /^ * Base class for all validating attribute definitions.$/;" c +for AttrDef/CSS/Multiple.php /^ * Framework class for strings that involve multiple values.$/;" c +for Bootstrap.php /^ * Autoload function for HTML Purifier$/;" f +for Config.php /^ * Convenience function for error reporting$/;" f +for ConfigSchema/Validator.php /^ * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom$/;" f +for ConfigSchema/ValidatorAtom.php /^ * Fluent interface for validating the contents of member variables.$/;" i +for DefinitionCache/Serializer.php /^ * Convenience wrapper function for file_put_contents$/;" f +for EntityParser.php /^ * Callback function for substituteNonSpecialEntities() that does the work.$/;" f +for EntityParser.php /^ * Callback function for substituteSpecialEntities() that does the work.$/;" f +for Exception.php /^ * Global exception class for HTML Purifier; any exceptions we throw$/;" c +for HTMLModule/Tidy.php /^ * Abstract class for a set of proprietary modules that clean up (tidy)$/;" c +for Language/classes/en-x-test.php /^\/\/ private class for unit testing$/;" c +for Lexer.php /^ * Callback function for escapeCDATA() that does the work.$/;" f +for Lexer.php /^ * @note The unit tests will instantiate this class for testing purposes, as$/;" c +for Lexer/DOMLex.php /^ * Callback function for undoing escaping of stray angled brackets$/;" f +for Lexer/DirectLex.php /^ * Callback function for script CDATA fudge$/;" f +for Token.php /^ * Convenience function for DirectLex settings line\/col position.$/;" f +forbidden AttrDef/HTML/Class.php /^ $forbidden = $config->get('Attr.ForbiddenClasses');$/;" v +forbidden_attributes HTMLDefinition.php /^ $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');$/;" v +forbidden_elements HTMLDefinition.php /^ $forbidden_elements = $config->get('HTML.ForbiddenElements');$/;" v +force_php Encoder.php /^ public static function cleanUTF8($str, $force_php = false) {$/;" v +form HTMLModule/Forms.php /^ $form = $this->addElement('form', 'Form',$/;" v +form_pointer Lexer/PH5P.php /^ private $form_pointer = null;$/;" v +formatMessage Language.php /^ public function formatMessage($key, $args = array()) {$/;" f +formatting Lexer/PH5P.php /^ private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u');$/;" v +formatting_element Lexer/PH5P.php /^ $formatting_element = $this->a_formatting[$a];$/;" v +formatting_elements Lexer/PH5P.php /^ $formatting_elements = count($this->a_formatting);$/;" v +forward Injector.php /^ protected function forward(&$i, &$current) {$/;" f +forwardUntilEndToken Injector.php /^ protected function forwardUntilEndToken(&$i, &$current, &$nesting) {$/;" f +foster_parent Lexer/PH5P.php /^ private $foster_parent = null;$/;" v +found_double_hyphen Strategy/RemoveForeignElements.php /^ $found_double_hyphen = true; \/\/ prevent double-erroring$/;" v +found_double_hyphen Strategy/RemoveForeignElements.php /^ $found_double_hyphen = false;$/;" v +found_slash AttrDef/CSS/Font.php /^ $found_slash = true;$/;" v +found_slash AttrDef/CSS/Font.php /^ $found_slash = true;$/;" v +found_slash AttrDef/CSS/Font.php /^ $found_slash = false;$/;" v +fragment URIParser.php /^ $fragment = !empty($matches[8]) ? $matches[9] : null;$/;" v +frame Config.php /^ $frame = $backtrace[1];$/;" v +from AttrDef.php /^ * Factory method for creating this class from a string.$/;" c +full Config.php /^ $full = $this->getAll();$/;" v +func Bootstrap.php /^ if ($compat) $func = implode('::', $func);$/;" v +function AttrDef/CSS/Filter.php /^ $function = trim(substr($value, 0, $function_length));$/;" v +function_length AttrDef/CSS/Filter.php /^ $function_length = strcspn($value, '(');$/;" v +furthest_block Lexer/PH5P.php /^ $furthest_block = $this->stack[$s];$/;" v +gen ChildDef/Required.php /^ $gen = new HTMLPurifier_Generator($config, $context);$/;" v +gen_config Printer/ConfigForm.php /^ $gen_config = $config;$/;" v +gen_config Printer/ConfigForm.php /^ $gen_config = $config[0];$/;" v +generateAttributes Generator.php /^ public function generateAttributes($assoc_array_of_attributes, $element = false) {$/;" f +generateBaseDirectoryPath DefinitionCache/Serializer.php /^ public function generateBaseDirectoryPath($config) {$/;" f +generateChildDef ContentSets.php /^ public function generateChildDef(&$def, $module) {$/;" f +generateChildDefCallback ContentSets.php /^ public function generateChildDefCallback($matches) {$/;" f +generateDirectoryPath DefinitionCache/Serializer.php /^ public function generateDirectoryPath($config) {$/;" f +generateFilePath DefinitionCache/Serializer.php /^ public function generateFilePath($config) {$/;" f +generateFromToken Generator.php /^ public function generateFromToken($token) {$/;" f +generateFromTokens Generator.php /^ public function generateFromTokens($tokens) {$/;" f +generateImpliedEndTags Lexer/PH5P.php /^ private function generateImpliedEndTags($exclude = array()) {$/;" f +generateKey DefinitionCache.php /^ public function generateKey($config) {$/;" f +generateScriptFromToken Generator.php /^ public function generateScriptFromToken($token) {$/;" f +generator Language.php /^ if (!$generator) $generator = $this->context->get('Generator');$/;" v +generator Language.php /^ $generator = false;$/;" v +generator Strategy/MakeWellFormed.php /^ $generator = new HTMLPurifier_Generator($config, $context);$/;" v +generator Strategy/RemoveForeignElements.php /^ $generator = new HTMLPurifier_Generator($config, $context);$/;" v +generic_names AttrDef/CSS/FontFamily.php /^ static $generic_names = array($/;" v +get AttrTypes.php /^ public function get($type) {$/;" f +get Config.php /^ public function get($key, $a = null) {$/;" f +get Context.php /^ public function &get($name, $ignore_error = false) {$/;" f +get DefinitionCache.php /^ abstract public function get($config);$/;" f +get DefinitionCache/Decorator.php /^ public function get($config) {$/;" f +get DefinitionCache/Decorator/Cleanup.php /^ public function get($config) {$/;" f +get DefinitionCache/Decorator/Memory.php /^ public function get($config) {$/;" f +get DefinitionCache/Null.php /^ public function get($config) {$/;" f +get DefinitionCache/Serializer.php /^ public function get($config) {$/;" f +get DoctypeRegistry.php /^ public function get($doctype) {$/;" f +get PropertyList.php /^ public function get($name) {$/;" f +getAccessed StringHash.php /^ public function getAccessed() {$/;" f +getAll Config.php /^ public function getAll() {$/;" f +getAllowedDirectivesForForm Config.php /^ public static function getAllowedDirectivesForForm($allowed, $schema = null) {$/;" f +getAllowedElements ChildDef.php /^ public function getAllowedElements($config) {$/;" f +getAllowedElements ChildDef/StrictBlockquote.php /^ public function getAllowedElements($config) {$/;" f +getAnonymousModule HTMLDefinition.php /^ public function getAnonymousModule() {$/;" f +getBatch Config.php /^ public function getBatch($namespace) {$/;" f +getBatchSerial Config.php /^ public function getBatchSerial($namespace) {$/;" f +getCSS Printer/ConfigForm.php /^ public static function getCSS() {$/;" f +getCSSDefinition Config.php /^ public function getCSSDefinition($raw = false) {$/;" f +getChild ErrorStruct.php /^ public function getChild($type, $id) {$/;" f +getChildDef ContentSets.php /^ public function getChildDef($def, $module) {$/;" f +getChildDef HTMLModule.php /^ public function getChildDef($def) {return false;}$/;" f +getChildDef HTMLModule/Edit.php /^ public function getChildDef($def) {$/;" f +getChildDef HTMLModule/Tidy/Strict.php /^ public function getChildDef($def) {$/;" f +getClass Printer.php /^ protected function getClass($obj, $sec_prefix = '') {$/;" f +getDefinition Config.php /^ public function getDefinition($type, $raw = false) {$/;" f +getDirective ConfigSchema/Interchange/Id.php /^ public function getDirective() {$/;" f +getDoctypeFromConfig DoctypeRegistry.php /^ public function getDoctypeFromConfig($config) {$/;" f +getElement HTMLModuleManager.php /^ public function getElement($name, $trusted = null) {$/;" f +getElementCategory Lexer/PH5P.php /^ private function getElementCategory($node) {$/;" f +getElements HTMLModuleManager.php /^ public function getElements() {$/;" f +getErrorName Language.php /^ public function getErrorName($int) {$/;" f +getFallbackFor LanguageFactory.php /^ public function getFallbackFor($code) {$/;" f +getFixType HTMLModule/Tidy.php /^ public function getFixType($name) {$/;" f +getFixesForLevel HTMLModule/Tidy.php /^ public function getFixesForLevel($level) {$/;" f +getFormattedContext ConfigSchema/Validator.php /^ protected function getFormattedContext() {$/;" f +getHTMLDefinition Config.php /^ public function getHTMLDefinition($raw = false) {$/;" f +getHTMLFormatted ErrorCollector.php /^ public function getHTMLFormatted($config, $errors = null) {$/;" f +getJavaScript Printer/ConfigForm.php /^ public static function getJavaScript() {$/;" f +getMessage Language.php /^ public function getMessage($key) {$/;" f +getN Length.php /^ public function getN() {return $this->n;}$/;" f +getParent PropertyList.php /^ public function getParent() {$/;" f +getPath Bootstrap.php /^ public static function getPath($class) {$/;" f +getRaw ErrorCollector.php /^ public function getRaw() {$/;" f +getRewind Injector.php /^ public function getRewind() {$/;" f +getRootNamespace ConfigSchema/Interchange/Id.php /^ public function getRootNamespace() {$/;" f +getScheme URISchemeRegistry.php /^ public function getScheme($scheme, $config, $context) {$/;" f +getSchemeObj URI.php /^ public function getSchemeObj($config, $context) {$/;" f +getSerial Config.php /^ public function getSerial() {$/;" f +getSigFigs UnitConverter.php /^ public function getSigFigs($n) {$/;" f +getTypeName VarParser.php /^ static public function getTypeName($type) {$/;" f +getUnit Length.php /^ public function getUnit() {return $this->unit;}$/;" f +h HTMLModule/Legacy.php /^ $h = $this->addBlankElement("h$i");$/;" v +handleElement Injector.php /^ public function handleElement(&$token) {}$/;" f +handleElement Injector/AutoParagraph.php /^ public function handleElement(&$token) {$/;" f +handleElement Injector/DisplayLinkURI.php /^ public function handleElement(&$token) {$/;" f +handleElement Injector/RemoveEmpty.php /^ public function handleElement(&$token) {$/;" f +handleElement Injector/SafeObject.php /^ public function handleElement(&$token) {$/;" f +handleEnd Injector.php /^ public function handleEnd(&$token) {$/;" f +handleEnd Injector/DisplayLinkURI.php /^ public function handleEnd(&$token) {$/;" f +handleEnd Injector/SafeObject.php /^ public function handleEnd(&$token) {$/;" f +handleText Injector.php /^ public function handleText(&$token) {}$/;" f +handleText Injector/AutoParagraph.php /^ public function handleText(&$token) {$/;" f +handleText Injector/Linkify.php /^ public function handleText(&$token) {$/;" f +handleText Injector/PurifierLinkify.php /^ public function handleText(&$token) {$/;" f +has Lexer.php /^ * @note The behavior of this class has changed, rather than accepting$/;" c +has PropertyList.php /^ public function has($name) {$/;" f +has_space Lexer/DirectLex.php /^ $has_space = strpos($string, ' ');$/;" v +hash ConfigSchema/InterchangeBuilder.php /^ $hash = new HTMLPurifier_StringHash($hash);$/;" v +hash DefinitionCache.php /^ $hash == $config->getBatchSerial($this->type) &&$/;" v +haystack Lexer/DirectLex.php /^ $haystack = substr($haystack, $offset, $length);$/;" v +head_pointer Lexer/PH5P.php /^ private $head_pointer = null;$/;" v +heavyHeader Printer/HTMLDefinition.php /^ protected function heavyHeader($text, $num = 1) {$/;" f +hex AttrDef/CSS/Color.php /^ $hex = $color;$/;" v +hex AttrDef/CSS/Color.php /^ $hex = substr($color, 1);$/;" v +hex AttrDef/HTML/Color.php /^ else $hex = $string;$/;" v +hex AttrDef/HTML/Color.php /^ if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];$/;" v +hex AttrDef/HTML/Color.php /^ if ($string[0] === '#') $hex = substr($string, 1);$/;" v +hex AttrDef/URI/IPv6.php /^ $hex = '[0-9a-fA-F]';$/;" v +hidden_elements Strategy/RemoveForeignElements.php /^ $hidden_elements = $config->get('Core.HiddenElements');$/;" v +hierarchical URIScheme.php /^ public $hierarchical = false;$/;" v +hierarchical URIScheme/ftp.php /^ public $hierarchical = true;$/;" v +hierarchical URIScheme/http.php /^ public $hierarchical = true;$/;" v +host URIParser.php /^ $host = !empty($matches[3]) ? $matches[3] : '';$/;" v +host_def URI.php /^ $host_def = new HTMLPurifier_AttrDef_URI_Host();$/;" v +host_parts URIFilter/DisableExternal.php /^ $host_parts = array_reverse(explode('.', $uri->host));$/;" v +hr HTMLModule/Legacy.php /^ $hr = $this->addBlankElement('hr');$/;" v +html ConfigSchema/Builder/Xml.php /^ $html = $purifier->purify($html);$/;" v +html Filter/ExtractStyleBlocks.php /^ $html = preg_replace_callback('#<style(?:\\s.*)?>(.+)<\/style>#isU', array($this, 'styleCallback'), $html);$/;" v +html Generator.php /^ $html = (string) $tidy; \/\/ explicit cast necessary$/;" v +html Generator.php /^ $html = '';$/;" v +html Generator.php /^ if ($nl !== "\\n") $html = str_replace("\\n", $nl, $html);$/;" v +html Lexer.php /^ $html = $this->escapeCommentedCDATA($html);$/;" v +html Lexer.php /^ $html = $this->extractBody($html);$/;" v +html Lexer.php /^ $html = $this->_entity_parser->substituteNonSpecialEntities($html);$/;" v +html Lexer.php /^ $html = $this->escapeCDATA($html);$/;" v +html Lexer.php /^ $html = HTMLPurifier_Encoder::cleanUTF8($html);$/;" v +html Lexer.php /^ $html = str_replace("\\r", "\\n", $html);$/;" v +html Lexer.php /^ $html = str_replace("\\r\\n", "\\n", $html);$/;" v +html Lexer/DOMLex.php /^ $html = preg_replace("\/<($char)\/i", '<\\\\1', $html);$/;" v +html Lexer/DOMLex.php /^ $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);$/;" v +html Lexer/DOMLex.php /^ $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); \/\/ fix comments$/;" v +html Lexer/DOMLex.php /^ $html = $this->normalize($html, $config, $context);$/;" v +html Lexer/DOMLex.php /^ $html = $this->wrapHTML($html, $config, $context);$/;" v +html Lexer/DirectLex.php /^ $html = preg_replace_callback('#(<script[^>]*>)(\\s*[^<].+?)(<\/script>)#si',$/;" v +html Lexer/DirectLex.php /^ $html = $this->normalize($html, $config, $context);$/;" v +html Lexer/PH5P.php /^ $html = $this->dom->createElement('html');$/;" v +i AttrDef/CSS/Background.php /^ $i = 0; \/\/ number of catches$/;" v +i AttrDef/CSS/BackgroundPosition.php /^ $i = 0;$/;" v +i AttrDef/CSS/Composite.php /^ foreach ($this->defs as $i => $def) {$/;" v +i AttrDef/CSS/Font.php /^ $i = $j;$/;" v +i AttrDef/CSS/ListStyle.php /^ $i = 0; \/\/ number of catches$/;" v +i ChildDef/Required.php /^ foreach ($elements as $i => $x) {$/;" v +i ChildDef/StrictBlockquote.php /^ foreach ($result as $i => $token) {$/;" v +i ConfigSchema/Validator.php /^ foreach ($interchange->directives as $i => $directive) {$/;" v +i ContentSets.php /^ foreach ($this->lookup as $i => $set) {$/;" v +i ContentSets.php /^ foreach ($array as $i => $k) {$/;" v +i Encoder.php /^ for( $i = 0; $i < $len; $i++ ) {$/;" v +i HTMLDefinition.php /^ foreach ($this->info_injector as $i => $injector) {$/;" v +i HTMLModule/Presentation.php /^ $i = $this->addElement('i', 'Inline', 'Inline', 'Common');$/;" v +i HTMLModuleManager.php /^ foreach ($module->info_injector as $i => $injector) {$/;" v +i Injector.php /^ if ($i === null) $i = $this->inputIndex + 1;$/;" v +i Injector.php /^ if ($i === null) $i = $this->inputIndex - 1;$/;" v +i Injector.php /^ if ($i === null) $i = $this->inputIndex;$/;" v +i Injector.php /^ * functions by setting $i = null beforehand!$/;" v +i Injector/AutoParagraph.php /^ $i = null;$/;" v +i Injector/AutoParagraph.php /^ $i = $nesting = null;$/;" v +i Injector/AutoParagraph.php /^ $i = null;$/;" v +i Injector/Linkify.php /^ \/\/ $i = index$/;" v +i Injector/PurifierLinkify.php /^ \/\/ $i = index$/;" v +i Injector/SafeObject.php /^ $i = count($this->objectStack) - 1;$/;" v +i Language.php /^ foreach ($args as $i => $value) {$/;" v +i Printer.php /^ if ($i > 0 && !($polite && $i == 1)) $ret .= ', ';$/;" v +i Printer.php /^ if ($polite && $i == 1) $ret .= 'and ';$/;" v +i Printer.php /^ $i = count($array);$/;" v +i Printer/ConfigForm.php /^ foreach ($value as $i => $v) {$/;" v +i Strategy/FixNesting.php /^ $i = $parent_index;$/;" v +i Strategy/FixNesting.php /^ if ($i == 0 || $i == $size - 1) {$/;" v +i Strategy/MakeWellFormed.php /^ foreach ($this->injectors as $i => $injector) {$/;" v +i Strategy/MakeWellFormed.php /^ $i = false;$/;" v +i Strategy/MakeWellFormed.php /^ foreach ($this->injectors as $i => $injector) {$/;" v +i Strategy/MakeWellFormed.php /^ $i = false; \/\/ injector index$/;" v +i URIFilter/DisableExternal.php /^ foreach ($this->ourHostParts as $i => $x) {$/;" v +i UnitConverter.php /^ \/\/ Pre-condition: $i == 0$/;" v +i VarParser/Flexible.php /^ foreach ($var as $i => $j) $var[$i] = trim($j);$/;" v +iconv Encoder.php /^ if ($iconv === null) $iconv = function_exists('iconv');$/;" v +iconv Encoder.php /^ static $iconv = null;$/;" v +id AttrDef/HTML/ID.php /^ if (strpos($id, $prefix) !== 0) $id = $prefix . $id;$/;" v +id AttrDef/HTML/ID.php /^ $id = trim($id); \/\/ trim it first$/;" v +id AttrTransform/Name.php /^ $id = $this->confiscateAttr($attr, 'name');$/;" v +id ConfigSchema/InterchangeBuilder.php /^ $id = $directive->id->toString(); \/\/ convenience$/;" v +id ConfigSchema/InterchangeBuilder.php /^ protected function id($id) {$/;" f +id ConfigSchema/Validator.php /^ $id = $directive->id->toString();$/;" v +id ConfigSchema/Validator.php /^ $id = $d->id->toString();$/;" v +id Lexer/PH5P.php /^ $id = substr($e_name, 0, $c);$/;" v +id_accumulator AttrDef/HTML/ID.php /^ $id_accumulator =& $context->get('IDAccumulator');$/;" v +id_accumulator AttrValidator.php /^ $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);$/;" v +id_accumulator IDAccumulator.php /^ $id_accumulator = new HTMLPurifier_IDAccumulator();$/;" v +id_string ConfigSchema/Validator.php /^ $id_string = $id->toString();$/;" v +ids IDAccumulator.php /^ public $ids = array();$/;" v +if HTMLDefinition.php /^ * update that class if things here change.$/;" c +ignore_error Context.php /^ public function &get($name, $ignore_error = false) {$/;" v +img HTMLModule/Image.php /^ $img = $this->addElement($/;" v +img HTMLModule/Legacy.php /^ $img = $this->addBlankElement('img');$/;" v +implementations DefinitionCacheFactory.php /^ protected $implementations = array();$/;" v +in Encoder.php /^ !($in == 9 || $in == 13 || $in == 10) \/\/ save \\r\\t\\n$/;" v +in Encoder.php /^ if (($in <= 31 || $in == 127) &&$/;" v +in Encoder.php /^ $in = ord($str{$i});$/;" v +in Encoder.php /^ * @note This is very similar to the unichr function in$/;" f +inBody Lexer/PH5P.php /^ private function inBody($token) {$/;" f +inCaption Lexer/PH5P.php /^ private function inCaption($token) {$/;" f +inCell Lexer/PH5P.php /^ private function inCell($token) {$/;" f +inColumnGroup Lexer/PH5P.php /^ private function inColumnGroup($token) {$/;" f +inFrameset Lexer/PH5P.php /^ private function inFrameset($token) {$/;" f +inHead Lexer/PH5P.php /^ private function inHead($token) {$/;" f +inRow Lexer/PH5P.php /^ private function inRow($token) {$/;" f +inSelect Lexer/PH5P.php /^ private function inSelect($token) {$/;" f +inTable Lexer/PH5P.php /^ private function inTable($token) {$/;" f +inTableBody Lexer/PH5P.php /^ private function inTableBody($token) {$/;" f +in_stack Lexer/PH5P.php /^ $in_stack = in_array($formatting_element, $this->stack, true);$/;" v +index Config.php /^ public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {$/;" v +index Config.php /^ public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {$/;" v +index Config.php /^ public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {$/;" v +info AttrCollections.php /^ public $info = array();$/;" v +info AttrDef/CSS/Border.php /^ protected $info = array();$/;" v +info AttrDef/CSS/Font.php /^ protected $info = array();$/;" v +info AttrTypes.php /^ protected $info = array();$/;" v +info CSSDefinition.php /^ public $info = array();$/;" v +info ConfigSchema.php /^ public $info = array();$/;" v +info ConfigSchema/InterchangeBuilder.php /^ $info = parse_ini_file($dir . 'info.ini');$/;" v +info ContentSets.php /^ public $info = array();$/;" v +info HTMLDefinition.php /^ public $info = array();$/;" v +info HTMLModule.php /^ public $info = array();$/;" v +info_attr_transform_post HTMLDefinition.php /^ public $info_attr_transform_post = array();$/;" v +info_attr_transform_post HTMLModule.php /^ public $info_attr_transform_post = array();$/;" v +info_attr_transform_pre HTMLDefinition.php /^ public $info_attr_transform_pre = array();$/;" v +info_attr_transform_pre HTMLModule.php /^ public $info_attr_transform_pre = array();$/;" v +info_block_wrapper HTMLDefinition.php /^ public $info_block_wrapper = 'p';$/;" v +info_content_sets HTMLDefinition.php /^ public $info_content_sets = array();$/;" v +info_global_attr HTMLDefinition.php /^ public $info_global_attr = array();$/;" v +info_injector HTMLDefinition.php /^ public $info_injector = array();$/;" v +info_injector HTMLModule.php /^ public $info_injector = array();$/;" v +info_parent HTMLDefinition.php /^ public $info_parent = 'div';$/;" v +info_tag_transform HTMLDefinition.php /^ public $info_tag_transform = array();$/;" v +info_tag_transform HTMLModule.php /^ public $info_tag_transform = array();$/;" v +inherit Config.php /^ public static function inherit(HTMLPurifier_Config $config) {$/;" f +init ChildDef/StrictBlockquote.php /^ private function init($config) {$/;" f +init ChildDef/StrictBlockquote.php /^ protected $init = false;$/;" v +initPhase Lexer/PH5P.php /^ private function initPhase($token) {$/;" f +injector HTMLModuleManager.php /^ $injector = new $class;$/;" v +injector Strategy/MakeWellFormed.php /^ $injector = "HTMLPurifier_Injector_$injector";$/;" v +injector Strategy/MakeWellFormed.php /^ $injector = new $injector;$/;" v +injector Strategy/MakeWellFormed.php /^ $injector = "HTMLPurifier_Injector_$injector";$/;" v +injector Strategy/MakeWellFormed.php /^ foreach ($injectors as $injector => $b) {$/;" v +injector Strategy/MakeWellFormed.php /^ protected function processToken($token, $injector = -1) {$/;" v +injectors Strategy/MakeWellFormed.php /^ $injectors = $config->getBatch('AutoFormat');$/;" v +input HTMLModule/Forms.php /^ $input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array($/;" v +insertBefore Strategy/MakeWellFormed.php /^ private function insertBefore($token) {$/;" f +insertComment Lexer/PH5P.php /^ private function insertComment($data) {$/;" f +insertElement Lexer/PH5P.php /^ private function insertElement($token, $append = true, $check = false) {$/;" f +insertText Lexer/PH5P.php /^ private function insertText($data) {$/;" f +inside_tag Lexer/DirectLex.php /^ $inside_tag = false;$/;" v +inside_tag Lexer/DirectLex.php /^ $inside_tag = false;$/;" v +inside_tag Lexer/DirectLex.php /^ $inside_tag = true;$/;" v +inside_tag Lexer/DirectLex.php /^ $inside_tag = false; \/\/ whether or not we're parsing the inside of a tag$/;" v +inst Lexer.php /^ $inst = new HTMLPurifier_Lexer_DOMLex();$/;" v +inst Lexer.php /^ $inst = new HTMLPurifier_Lexer_DirectLex();$/;" v +inst Lexer.php /^ $inst = new HTMLPurifier_Lexer_PH5P();$/;" v +inst Lexer.php /^ $inst = $lexer;$/;" v +inst Lexer.php /^ $inst = null;$/;" v +instance ConfigSchema.php /^ public static function instance($prototype = null) {$/;" f +instance DefinitionCacheFactory.php /^ $instance = $prototype;$/;" v +instance DefinitionCacheFactory.php /^ $instance = new HTMLPurifier_DefinitionCacheFactory();$/;" v +instance DefinitionCacheFactory.php /^ public static function instance($prototype = null) {$/;" f +instance EntityLookup.php /^ $instance = $prototype;$/;" v +instance EntityLookup.php /^ $instance = new HTMLPurifier_EntityLookup();$/;" v +instance EntityLookup.php /^ static $instance = null;$/;" v +instance EntityLookup.php /^ public static function instance($prototype = false) {$/;" f +instance LanguageFactory.php /^ $instance = $prototype;$/;" v +instance LanguageFactory.php /^ $instance = new HTMLPurifier_LanguageFactory();$/;" v +instance LanguageFactory.php /^ static $instance = null;$/;" v +instance LanguageFactory.php /^ public static function instance($prototype = null) {$/;" f +instance URISchemeRegistry.php /^ $instance = $prototype;$/;" v +instance URISchemeRegistry.php /^ $instance = new HTMLPurifier_URISchemeRegistry();$/;" v +instance URISchemeRegistry.php /^ static $instance = null;$/;" v +instance URISchemeRegistry.php /^ public static function instance($prototype = null) {$/;" f +int AttrDef/CSS/Filter.php /^ $int = (int) $value;$/;" v +int AttrDef/HTML/MultiLength.php /^ $int = (int) $int;$/;" v +int AttrDef/HTML/MultiLength.php /^ $int = substr($string, 0, $length - 1);$/;" v +int AttrDef/HTML/Pixels.php /^ $int = (int) $string;$/;" v +int EntityParser.php /^ $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];$/;" v +int PercentEncoder.php /^ $int = hexdec($encoding);$/;" v +integer AttrDef/Integer.php /^ if ($digits === '0') $integer = '0'; \/\/ rm minus sign for zero$/;" v +integer AttrDef/Integer.php /^ $integer = $this->parseCDATA($integer);$/;" v +integer AttrDef/Integer.php /^ if (!$this->zero && $integer == 0) return false;$/;" v +interchange ConfigSchema/InterchangeBuilder.php /^ $interchange = new HTMLPurifier_ConfigSchema_Interchange();$/;" v +internal_precision UnitConverter.php /^ public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) {$/;" v +into ChildDef/StrictBlockquote.php /^ \/\/ trick the parent class into thinking it allows more$/;" c +ip AttrDef/URI/Host.php /^ $ip = substr($string, 1, $length - 2);$/;" v +ip AttrDef/URI/IPv6.php /^ $ip = array_map('dechex', $ip);$/;" v +ip AttrDef/URI/IPv6.php /^ $ip = explode('.', $find[0]);$/;" v +ipv4 AttrDef/URI/Host.php /^ $ipv4 = $this->ipv4->validate($string, $config, $context);$/;" v +is ChildDef/Custom.php /^ * @warning Currently this class is an all or nothing proposition, that is,$/;" c +is ChildDef/Empty.php /^ * @warning validateChildren() in this class is actually never called, because$/;" c +is Config.php /^ * @warning This class is strongly defined: that means that the class$/;" c +is ConfigSchema.php /^ * This class is friendly with HTMLPurifier_Config. If you need introspection$/;" c +is Doctype.php /^ * @note This class is inspected by Printer_HTMLDefinition->renderDoctype.$/;" c +is ElementDef.php /^ * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.$/;" c +is HTMLDefinition.php /^ * @note This class is inspected by Printer_HTMLDefinition; please$/;" c +is Lexer.php /^ * This means that, even though this class is not runnable, it will$/;" c +is Lexer/PEARSax3.php /^ * Close tag event handler, interface is defined by PEAR package.$/;" i +is Lexer/PEARSax3.php /^ * Data event handler, interface is defined by PEAR package.$/;" i +is Lexer/PEARSax3.php /^ * Escaped text handler, interface is defined by PEAR package.$/;" i +is Lexer/PEARSax3.php /^ * Open tag event handler, interface is defined by PEAR package.$/;" i +is PercentEncoder.php /^ * @warning This function is affected by $preserve, even though the$/;" f +is Token/Tag.php /^ * Static bool marker that indicates the class is a tag.$/;" c +is TokenFactory.php /^ * constructor). This class is for that optimization.$/;" c +isFinalized Config.php /^ public function isFinalized($error = false) {$/;" f +isOld DefinitionCache.php /^ public function isOld($key, $config) {$/;" f +isValid Length.php /^ public function isValid() {$/;" f +isWsOrNbsp Injector/RemoveEmpty.php /^ $isWsOrNbsp = $plain === '' || ctype_space($plain);$/;" v +is_child ChildDef/Custom.php /^ $is_child = ($nesting == 0); \/\/ direct$/;" v +is_child ChildDef/Required.php /^ $is_child = ($nesting == 0);$/;" v +is_child ChildDef/Table.php /^ $is_child = ($nesting == 0);$/;" v +is_collecting ChildDef/Table.php /^ $is_collecting = true;$/;" v +is_collecting ChildDef/Table.php /^ $is_collecting = false;$/;" v +is_collecting ChildDef/Table.php /^ $is_collecting = false; \/\/ are we globbing together tokens to package$/;" v +is_collecting ChildDef/Table.php /^ if (!empty($collection) && $is_collecting == false){$/;" v +is_deleting ChildDef/Required.php /^ $is_deleting = true;$/;" v +is_deleting ChildDef/Required.php /^ $is_deleting = false;$/;" v +is_deleting ChildDef/Required.php /^ $is_deleting = false;$/;" v +is_end_tag Lexer/DirectLex.php /^ $is_end_tag = (strpos($segment,'\/') === 0);$/;" v +is_folder URIFilter/MakeAbsolute.php /^ $is_folder = true;$/;" v +is_folder URIFilter/MakeAbsolute.php /^ $is_folder = false;$/;" v +is_folder URIFilter/MakeAbsolute.php /^ $is_folder = false;$/;" v +is_hex EntityParser.php /^ $is_hex = (@$entity[2] === 'x');$/;" v +is_important AttrDef/CSS/ImportantDecorator.php /^ $is_important = true;$/;" v +is_important AttrDef/CSS/ImportantDecorator.php /^ $is_important = false;$/;" v +is_inline ChildDef/StrictBlockquote.php /^ $is_inline = false;$/;" v +is_inline ChildDef/StrictBlockquote.php /^ $is_inline = true;$/;" v +is_inline ChildDef/StrictBlockquote.php /^ $is_inline = false;$/;" v +is_inline Strategy/FixNesting.php /^ $is_inline = $count - 1;$/;" v +is_inline Strategy/FixNesting.php /^ $is_inline = false;$/;" v +is_inline Strategy/FixNesting.php /^ $is_inline = $definition->info_parent_def->descendants_are_inline;$/;" v +is_num EntityParser.php /^ $is_num = (@$matches[0][1] === '#');$/;" v +is_self_closing Lexer/DirectLex.php /^ $is_self_closing = (strrpos($segment,'\/') === $strlen_segment-1);$/;" v +is_tag Token/Tag.php /^ public $is_tag = true;$/;" v +is_whitespace Token/Comment.php /^ public $is_whitespace = true;$/;" v +itself StringHashParser.php /^ * files, but the class itself is usage agnostic.$/;" c +ix Strategy/MakeWellFormed.php /^ foreach ($this->injectors as $ix => $injector) {$/;" v +j AttrDef/CSS/Font.php /^ $j = $i;$/;" v +j Strategy/MakeWellFormed.php /^ \/\/ notice we exclude $j == 0, i.e. the current ending tag, from$/;" v +k CSSDefinition.php /^ foreach ($this->info as $k => $v) {$/;" v +k ConfigSchema/InterchangeBuilder.php /^ foreach ($hash as $k => $v) {$/;" v +k ElementDef.php /^ foreach ($a2 as $k => $v) {$/;" v +k ElementDef.php /^ foreach($def->attr as $k => $v) {$/;" v +k Filter/ExtractStyleBlocks.php /^ foreach ($this->_tidy->css as $k => $decls) {$/;" v +k HTMLDefinition.php /^ foreach ($module->info_injector as $k => $v) {$/;" v +k HTMLDefinition.php /^ foreach($module->info_attr_transform_post as $k => $v) {$/;" v +k HTMLDefinition.php /^ foreach($module->info_attr_transform_pre as $k => $v) {$/;" v +k HTMLDefinition.php /^ foreach($module->info_tag_transform as $k => $v) {$/;" v +k HTMLDefinition.php /^ foreach ($this->info as $k => $v) {$/;" v +k HTMLModuleManager.php /^ foreach ($modules as $k => $m) {$/;" v +k UnitConverter.php /^ foreach (self::$units as $k => $x) {$/;" v +key AttrCollections.php /^ foreach ($this->info[$merge[$i]] as $key => $value) {$/;" v +key AttrDef/CSS/Background.php /^ foreach ($caught as $key => $status) {$/;" v +key AttrDef/CSS/Filter.php /^ $key = trim($key);$/;" v +key AttrDef/CSS/ListStyle.php /^ foreach ($caught as $key => $status) {$/;" v +key Config.php /^ $key = "$key.$a";$/;" v +key Config.php /^ $key = "$key.$directive";$/;" v +key Config.php /^ $key = str_replace('_', '.', $key);$/;" v +key Config.php /^ foreach ($config_array as $key => $value) {$/;" v +key Config.php /^ foreach ($schema->info as $key => $def) {$/;" v +key ConfigSchema.php /^ foreach ($this->info as $key => $v) {$/;" v +key ContentSets.php /^ foreach ($module->content_sets as $key => $value) {$/;" v +key ContentSets.php /^ foreach ($this->lookup as $key => $lookup) {$/;" v +key Context.php /^ foreach ($context_array as $key => $discard) {$/;" v +key DefinitionCache/Decorator/Memory.php /^ $key = $this->generateKey($config);$/;" v +key DefinitionCache/Serializer.php /^ $key = substr($filename, 0, strlen($filename) - 4);$/;" v +key DefinitionCache/Serializer.php /^ $key = $this->generateKey($config);$/;" v +key Generator.php /^ foreach ($assoc_array_of_attributes as $key => $value) {$/;" v +key HTMLDefinition.php /^ foreach ($forbidden_attributes as $key => $v) {$/;" v +key Lexer/DirectLex.php /^ $key = substr($string, $key_begin, $key_end - $key_begin);$/;" v +key Lexer/PEARSax3.php /^ foreach ($attrs as $key => $attr) {$/;" v +key PropertyListIterator.php /^ $key = $this->getInnerIterator()->key();$/;" v +key Strategy/ValidateAttributes.php /^ foreach ($tokens as $key => $token) {$/;" v +key Token/Tag.php /^ foreach ($attr as $key => $value) {$/;" v +key VarParser/Flexible.php /^ foreach ($var as $key => $value) {$/;" v +key_begin Lexer/DirectLex.php /^ $key_begin = $cursor; \/\/we're currently at the start of the key$/;" v +key_end Lexer/DirectLex.php /^ $key_end = $cursor; \/\/ now at the end of the key$/;" v +keys AttrDef/CSS/URI.php /^ $keys = array( '(', ')', ',', ' ', '"', "'");$/;" v +keys ChildDef/Required.php /^ $keys = array_keys($elements);$/;" v +keys ContentSets.php /^ protected $keys = array();$/;" v +keys HTMLDefinition.php /^ $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");$/;" v +keys HTMLDefinition.php /^ $keys = array($attr, "*@$attr", "*.$attr");$/;" v +keys Language.php /^ $keys = array_keys($value);$/;" v +keys LanguageFactory.php /^ public $keys = array('fallback', 'messages', 'errorNames');$/;" v +keys VarParser.php /^ $keys = array_keys($var);$/;" v +keys VarParser/Flexible.php /^ $keys = array_keys($var);$/;" v +keywords AttrDef/CSS/BackgroundPosition.php /^ $keywords = array();$/;" v +l Injector/Linkify.php /^ \/\/ $l = is link$/;" v +l Injector/PurifierLinkify.php /^ \/\/ $l = is link$/;" v +l Length.php /^ $l = $converter->convert($l, $this->unit);$/;" v +l Lexer/PH5P.php /^ private function character($s, $l = 0) {$/;" v +label HTMLModule/Forms.php /^ $label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array($/;" v +lang AttrTransform/Lang.php /^ $lang = isset($attr['lang']) ? $attr['lang'] : false;$/;" v +lang LanguageFactory.php /^ $lang = $this->create($config, $context, $fallback);$/;" v +lang LanguageFactory.php /^ $lang = new $class($config, $context);$/;" v +lang LanguageFactory.php /^ $lang = new HTMLPurifier_Language($config, $context);$/;" v +languages_seen LanguageFactory.php /^ static $languages_seen = array(); \/\/ recursion guard$/;" v +last Lexer/DOMLex.php /^ $last = end($tokens);$/;" v +last Lexer/PH5P.php /^ $last = true;$/;" v +last Lexer/PH5P.php /^ $last = count($this->token['attr']) - 1;$/;" v +last Lexer/PH5P.php /^ $last = count($this->token['attr']) - 1;$/;" v +last Lexer/PH5P.php /^ $last = false;$/;" v +last_char AttrDef/HTML/Length.php /^ $last_char = $string[$length - 1];$/;" v +last_char AttrDef/HTML/MultiLength.php /^ $last_char = $string[$length - 1];$/;" v +last_char Lexer/DirectLex.php /^ $last_char = @$quoted_value[strlen($quoted_value)-1];$/;" v +last_node Lexer/PH5P.php /^ $last_node = $node;$/;" v +last_node Lexer/PH5P.php /^ $last_node = $furthest_block;$/;" v +lbit AttrDef/CSS/BackgroundPosition.php /^ $lbit = ctype_lower($bit) ? $bit : strtolower($bit);$/;" v +lclass Printer.php /^ $lclass = strtolower($class);$/;" v +left AttrDef/CSS/Number.php /^ $left = ltrim($left, '0');$/;" v +len Encoder.php /^ $len = strlen($str);$/;" v +len Lexer/PH5P.php /^ $len = strlen($e_name);$/;" v +len Lexer/PH5P.php /^ $len = strcspn($this->data, '<&', $this->char);$/;" v +lenc Encoder.php /^ $lenc = strtolower($encoding);$/;" v +leng Lexer/PH5P.php /^ $leng = count($this->a_formatting);$/;" v +leng Lexer/PH5P.php /^ $leng = count($this->stack);$/;" v +length AttrDef/CSS/Color.php /^ $length = strlen($part);$/;" v +length AttrDef/CSS/Color.php /^ $length = strlen($color);$/;" v +length AttrDef/CSS/Color.php /^ $length = strlen($hex);$/;" v +length AttrDef/CSS/FontFamily.php /^ $length = strlen($font);$/;" v +length AttrDef/CSS/Length.php /^ $length = HTMLPurifier_Length::make($string);$/;" v +length AttrDef/CSS/Multiple.php /^ $length = count($parts);$/;" v +length AttrDef/CSS/Percentage.php /^ $length = strlen($string);$/;" v +length AttrDef/HTML/Color.php /^ $length = strlen($hex);$/;" v +length AttrDef/HTML/Length.php /^ $length = strlen($string);$/;" v +length AttrDef/HTML/MultiLength.php /^ $length = strlen($string);$/;" v +length AttrDef/HTML/Pixels.php /^ $length = strlen($string);$/;" v +length AttrDef/Lang.php /^ $length = strlen($subtags[$i]);$/;" v +length AttrDef/Lang.php /^ $length = strlen($subtags[0]);$/;" v +length AttrDef/Lang.php /^ $length = strlen($subtags[1]);$/;" v +length AttrDef/URI/Host.php /^ $length = strlen($string);$/;" v +length AttrTransform/Length.php /^ $length = $this->confiscateAttr($attr, $this->name);$/;" v +length Lexer/DirectLex.php /^ $length = false;$/;" v +length Lexer/DirectLex.php /^ $length = strlen($html);$/;" v +length Lexer/PH5P.php /^ $length = count($this->stack);$/;" v +length PercentEncoder.php /^ $length = strlen($part);$/;" v +length Strategy/FixNesting.php /^ $length = $j - $i + 1;$/;" v +length Strategy/FixNesting.php /^ $length = $j - $i - 1;$/;" v +level HTMLModule/Tidy.php /^ $level = $config->get('HTML.TidyLevel');$/;" v +levels HTMLModule/Tidy.php /^ public $levels = array(0 => 'none', 'light', 'medium', 'heavy');$/;" v +lexer Lexer.php /^ $lexer = 'DOMLex';$/;" v +lexer Lexer.php /^ $lexer = 'DirectLex';$/;" v +lexer Lexer.php /^ $lexer = $config->get('Core.LexerImpl');$/;" v +lexer Lexer.php /^ $lexer = $config;$/;" v +lexer Lexer/PH5P.php /^ $lexer = new HTMLPurifier_Lexer_DirectLex();$/;" v +li HTMLModule/Legacy.php /^ $li = $this->addBlankElement('li');$/;" v +li_types HTMLModule/Tidy/XHTMLAndHTML4.php /^ $li_types = $ul_types + $ol_types;$/;" v +line ErrorCollector.php /^ $line = $token ? $token->line : $this->context->get('CurrentLine', true);$/;" v +line ErrorCollector.php /^ foreach ($this->lines as $line => $col_array) {$/;" v +line ErrorCollector.php /^ private function _renderStruct(&$ret, $struct, $line = null, $col = null) {$/;" v +line StringHashParser.php /^ $line = trim($line);$/;" v +line StringHashParser.php /^ $line = fgets($fh);$/;" v +line StringHashParser.php /^ $line = rtrim($line, "\\n\\r");$/;" v +line StringHashParser.php /^ if (!$state && $line === '') continue;$/;" v +line Token/Comment.php /^ public function __construct($data, $line = null, $col = null) {$/;" v +line Token/Text.php /^ public function __construct($data, $line = null, $col = null) {$/;" v +line_height AttrDef/CSS/Font.php /^ $line_height = $bits[$j];$/;" v +line_height AttrDef/CSS/Font.php /^ $line_height = false;$/;" v +line_height AttrDef/CSS/Font.php /^ $line_height = false;$/;" v +lines ErrorCollector.php /^ protected $lines = array();$/;" v +list Config.php /^ $list = array();$/;" v +list HTMLDefinition.php /^ $list = str_replace(array(' ', "\\t"), '', $list);$/;" v +list HTMLModule.php /^ if (is_string($list)) $list = func_get_args();$/;" v +list Printer/HTMLDefinition.php /^ $list = array();$/;" v +list Printer/HTMLDefinition.php /^ $list = array();$/;" v +list_of_children ChildDef/Custom.php /^ $list_of_children = '';$/;" v +list_of_children ChildDef/Custom.php /^ $list_of_children = ',' . rtrim($list_of_children, ',');$/;" v +listify Language.php /^ public function listify($array) {$/;" f +listify Printer.php /^ protected function listify($array, $polite = false) {$/;" f +listifyAttr Printer/HTMLDefinition.php /^ protected function listifyAttr($array) {$/;" f +listifyObjectList Printer/HTMLDefinition.php /^ protected function listifyObjectList($array) {$/;" f +listifyTagLookup Printer/HTMLDefinition.php /^ protected function listifyTagLookup($array) {$/;" f +load IDAccumulator.php /^ public function load($array_of_ids) {$/;" f +load Language.php /^ public function load() {$/;" f +loadArray Config.php /^ public function loadArray($config_array) {$/;" f +loadArray Context.php /^ public function loadArray($context_array) {$/;" f +loadArrayFromForm Config.php /^ public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {$/;" f +loadIni Config.php /^ public function loadIni($filename) {$/;" f +loadLanguage LanguageFactory.php /^ public function loadLanguage($code) {$/;" f +lock Config.php /^ $lock = $this->lock;$/;" v +log UnitConverter.php /^ $log = (int) floor(log(abs($n), 10));$/;" v +lookup AttrDef/CSS/BackgroundPosition.php /^ $lookup = array($/;" v +lookup AttrDef/CSS/Filter.php /^ $lookup = array();$/;" v +lookup ConfigSchema/InterchangeBuilder.php /^ protected function lookup($array) {$/;" f +lookup ContentSets.php /^ public $lookup = array();$/;" v +lookup HTMLModuleManager.php /^ $lookup = $config->get('HTML.AllowedModules');$/;" v +lookup VarParser.php /^ $lookup = array_flip(HTMLPurifier_VarParser::$types);$/;" v +loops Lexer/DirectLex.php /^ $loops = 0;$/;" v +lower AttrDef/CSS/Color.php /^ $lower = strtolower($color);$/;" v +lowercase_string AttrDef/CSS/Font.php /^ $lowercase_string = strtolower($string);$/;" v +mBytes Encoder.php /^ $mBytes = 1;$/;" v +mBytes Encoder.php /^ $mBytes = 1;$/;" v +mBytes Encoder.php /^ $mBytes = 2;$/;" v +mBytes Encoder.php /^ $mBytes = 3;$/;" v +mBytes Encoder.php /^ $mBytes = 4;$/;" v +mBytes Encoder.php /^ $mBytes = 5;$/;" v +mBytes Encoder.php /^ $mBytes = 6;$/;" v +mBytes Encoder.php /^ $mBytes = 1; \/\/ cached expected number of octets in the current sequence$/;" v +mState Encoder.php /^ $mState = 0;$/;" v +mState Encoder.php /^ $mState = 0;$/;" v +mState Encoder.php /^ $mState = 1;$/;" v +mState Encoder.php /^ $mState = 2;$/;" v +mState Encoder.php /^ $mState = 3;$/;" v +mState Encoder.php /^ $mState = 4;$/;" v +mState Encoder.php /^ $mState = 5;$/;" v +mState Encoder.php /^ $mState = 0; \/\/ cached expected number of octets after the current octet$/;" v +mUcs4 Encoder.php /^ $mUcs4 = 0;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = 0;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = ($in);$/;" v +mUcs4 Encoder.php /^ $mUcs4 = ($mUcs4 & 0x03) << 24;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = ($mUcs4 & 0x07) << 18;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = ($mUcs4 & 0x0F) << 12;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = ($mUcs4 & 0x1F) << 6;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = ($mUcs4 & 1) << 30;$/;" v +mUcs4 Encoder.php /^ $mUcs4 = 0; \/\/ cached Unicode character$/;" v +mainPhase Lexer/PH5P.php /^ private function mainPhase($token) {$/;" f +maintain_line_numbers Lexer/DirectLex.php /^ $maintain_line_numbers = $config->get('Core.CollectErrors');$/;" v +maintain_line_numbers Lexer/DirectLex.php /^ $maintain_line_numbers = $config->get('Core.MaintainLineNumbers');$/;" v +make AttrDef.php /^ public function make($string) {$/;" f +make AttrDef/Enum.php /^ public function make($string) {$/;" f +make AttrDef/HTML/Bool.php /^ public function make($string) {$/;" f +make AttrDef/HTML/Pixels.php /^ public function make($string) {$/;" f +make AttrDef/URI.php /^ public function make($string) {$/;" f +make ConfigSchema/Interchange/Id.php /^ public static function make($id) {$/;" f +make DoctypeRegistry.php /^ public function make($config) {$/;" f +make Length.php /^ static public function make($s) {$/;" f +makeFixes HTMLModule/Tidy.php /^ public function makeFixes() {}$/;" f +makeFixes HTMLModule/Tidy/Name.php /^ public function makeFixes() {$/;" f +makeFixes HTMLModule/Tidy/Proprietary.php /^ public function makeFixes() {$/;" f +makeFixes HTMLModule/Tidy/Strict.php /^ public function makeFixes() {$/;" f +makeFixes HTMLModule/Tidy/XHTML.php /^ public function makeFixes() {$/;" f +makeFixes HTMLModule/Tidy/XHTMLAndHTML4.php /^ public function makeFixes() {$/;" f +makeFixesForLevel HTMLModule/Tidy.php /^ public function makeFixesForLevel($fixes) {$/;" f +makeFromSerial ConfigSchema.php /^ public static function makeFromSerial() {$/;" f +makeLookup HTMLModule.php /^ public function makeLookup($list) {$/;" f +makeReplace URIFilter/Munge.php /^ protected function makeReplace($uri, $config, $context) {$/;" f +margin CSSDefinition.php /^ $margin =$/;" v +marker Lexer/PH5P.php /^ $marker = end(array_keys($this->a_formatting, self::MARKER, true));$/;" v +markupDeclarationOpenState Lexer/PH5P.php /^ private function markupDeclarationOpenState() {$/;" f +match AttrDef/URI/Host.php /^ $match = preg_match("\/^($domainlabel\\.)*$toplabel\\.?$\/i", $string);$/;" v +matches Lexer.php /^ $matches = array();$/;" v +matches URIParser.php /^ $matches = array();$/;" v +matches URIParser.php /^ $matches = array();$/;" v +max AttrDef/CSS/Length.php /^ public function __construct($min = null, $max = null) {$/;" v +max AttrDef/CSS/Multiple.php /^ public function __construct($single, $max = 4) {$/;" v +max AttrDef/HTML/Pixels.php /^ else $max = (int) $string;$/;" v +max AttrDef/HTML/Pixels.php /^ if ($string === '') $max = null;$/;" v +max CSSDefinition.php /^ $max === null ?$/;" v +max CSSDefinition.php /^ $max = $config->get('CSS.MaxImgLength');$/;" v +max HTMLModule/Image.php /^ $max = $config->get('HTML.MaxImgLength');$/;" v +max HTMLModule/SafeEmbed.php /^ $max = $config->get('HTML.MaxImgLength');$/;" v +max HTMLModule/SafeObject.php /^ $max = $config->get('HTML.MaxImgLength');$/;" v +may Bootstrap.php /^ * This class may be used without any other files from HTML Purifier.$/;" c +measures AttrDef/CSS/BackgroundPosition.php /^ $measures = array();$/;" v +merge AttrCollections.php /^ $merge = array_merge($merge, $this->info[$merge[$i]][0]);$/;" v +merge AttrCollections.php /^ $merge = $attr[0];$/;" v +mergeArrayFromForm Config.php /^ public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {$/;" f +mergeIn ElementDef.php /^ public function mergeIn($def) {$/;" f +mergeInAttrIncludes HTMLModule.php /^ public function mergeInAttrIncludes(&$attr, $attr_includes) {$/;" f +mergeable_keys_list LanguageFactory.php /^ protected $mergeable_keys_list = array();$/;" v +mergeable_keys_map LanguageFactory.php /^ protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);$/;" v +messages Language.php /^ public $messages = array();$/;" v +messages Language/messages/en-x-test.php /^$messages = array($/;" v +messages Language/messages/en-x-testmini.php /^$messages = array($/;" v +messages Language/messages/en.php /^$messages = array($/;" v +method DefinitionCacheFactory.php /^ $method = $config->get('Cache.DefinitionImpl');$/;" v +minimized AttrDef.php /^ public $minimized = false;$/;" v +minimized AttrDef/HTML/Bool.php /^ public $minimized = true;$/;" v +module HTMLDefinition.php /^ $module = $this->getAnonymousModule();$/;" v +module HTMLDefinition.php /^ $module = $this->getAnonymousModule();$/;" v +module HTMLModuleManager.php /^ $module = $original_module;$/;" v +module HTMLModuleManager.php /^ $module = $prefix . $original_module;$/;" v +module HTMLModuleManager.php /^ $module = $this->modules[$module_name];$/;" v +module HTMLModuleManager.php /^ $module = new $module();$/;" v +module HTMLModuleManager.php /^ if (is_object($module)) $module = $module->name;$/;" v +module_i ContentSets.php /^ foreach ($modules as $module_i => $module) {$/;" v +modules ContentSets.php /^ if (!is_array($modules)) $modules = array($modules);$/;" v +modules Doctype.php /^ public $modules = array();$/;" v +modules DoctypeRegistry.php /^ if (!is_array($modules)) $modules = array($modules);$/;" v +modules HTMLModuleManager.php /^ $modules = $this->doctype->modules;$/;" v +modules HTMLModuleManager.php /^ $modules = array_merge($modules, $this->userModules);$/;" v +modules HTMLModuleManager.php /^ public $modules = array();$/;" v +mq Config.php /^ $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();$/;" v +msg ErrorCollector.php /^ $msg = $this->locale->formatMessage($msg, $args);$/;" v +msg ErrorCollector.php /^ $msg = $this->locale->getMessage($msg);$/;" v +msg ErrorCollector.php /^ if (!empty($subst)) $msg = strtr($msg, $subst);$/;" v +mul UnitConverter.php /^ private function mul($s1, $s2, $scale) {$/;" f +mungeRgb AttrDef.php /^ protected function mungeRgb($string) {$/;" f +muteErrorHandler Encoder.php /^ public static function muteErrorHandler() {}$/;" f +muteErrorHandler Lexer/DOMLex.php /^ public function muteErrorHandler($errno, $errstr) {}$/;" f +n HTMLModuleManager.php /^ $n = array();$/;" v +n HTMLModuleManager.php /^ foreach ($elements as $n => $v) {$/;" v +n Injector/SafeObject.php /^ $n = $token->attr['name'];$/;" v +n Length.php /^ $n = substr($s, 0, $n_length);$/;" v +n Length.php /^ * String unit. False is permitted if $n = 0.$/;" v +n Lexer/PH5P.php /^ $n = -1;$/;" v +n UnitConverter.php /^ $n = $this->mul($n, $factor, $cp);$/;" v +n UnitConverter.php /^ $n = '0';$/;" v +n UnitConverter.php /^ $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1);$/;" v +n UnitConverter.php /^ $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0);$/;" v +n UnitConverter.php /^ $n = bcdiv($n, '1', $rp);$/;" v +n UnitConverter.php /^ $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1);$/;" v +n UnitConverter.php /^ $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp);$/;" v +n UnitConverter.php /^ $n = $length->getN();$/;" v +n UnitConverter.php /^ $n = $this->round($n, $sigfigs);$/;" v +n UnitConverter.php /^ $n = ltrim($n, '0+-');$/;" v +n UnitConverter.php /^ $n = rtrim($n, '.');$/;" v +n UnitConverter.php /^ if (strpos($n, '.') !== false) $n = rtrim($n, '0');$/;" v +n_length Length.php /^ $n_length = strspn($s, '1234567890.+-');$/;" v +name AttrCollections.php /^ foreach ($this->info as $name => $attr) {$/;" v +name AttrDef/HTML/Class.php /^ $name = $config->getDefinition('HTML')->doctype->name;$/;" v +name AttrDef/HTML/Class.php /^ if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {$/;" v +name AttrTransform/NameSync.php /^ $name = $attr['name'];$/;" v +name AttrTransform/SafeEmbed.php /^ public $name = "SafeEmbed";$/;" v +name AttrTransform/SafeObject.php /^ public $name = "SafeObject";$/;" v +name AttrTransform/SafeParam.php /^ public $name = "SafeParam";$/;" v +name CSSDefinition.php /^ $name = htmlspecialchars($name);$/;" v +name CSSDefinition.php /^ foreach ($allowed_attributes as $name => $d) {$/;" v +name CSSDefinition.php /^ foreach ($this->info as $name => $d) {$/;" v +name ChildDef.php /^ * Type of child definition, usually right-most part of class name lowercase.$/;" c +name Config.php /^ foreach ($lookup as $name => $b) $list[] = $name;$/;" v +name Config.php /^ foreach ($this->plist->squash() as $name => $value) {$/;" v +name DefinitionCache/Decorator/Cleanup.php /^ public $name = 'Cleanup';$/;" v +name DefinitionCache/Decorator/Memory.php /^ public $name = 'Memory';$/;" v +name DefinitionCacheFactory.php /^ * @param $long Full class name of cache object, for construction$/;" c +name DoctypeRegistry.php /^ $name = $doctype->name;$/;" v +name Filter/ExtractStyleBlocks.php /^ foreach ($style as $name => $value) {$/;" v +name Filter/ExtractStyleBlocks.php /^ public $name = 'ExtractStyleBlocks';$/;" v +name Filter/YouTube.php /^ public $name = 'YouTube';$/;" v +name HTMLDefinition.php /^ foreach ($this->info as $name => $d) {$/;" v +name HTMLModule/Bdo.php /^ public $name = 'Bdo';$/;" v +name HTMLModule/CommonAttributes.php /^ public $name = 'CommonAttributes';$/;" v +name HTMLModule/Edit.php /^ public $name = 'Edit';$/;" v +name HTMLModule/Forms.php /^ public $name = 'Forms';$/;" v +name HTMLModule/Hypertext.php /^ public $name = 'Hypertext';$/;" v +name HTMLModule/Image.php /^ public $name = 'Image';$/;" v +name HTMLModule/Legacy.php /^ public $name = 'Legacy';$/;" v +name HTMLModule/List.php /^ public $name = 'List';$/;" v +name HTMLModule/Name.php /^ public $name = 'Name';$/;" v +name HTMLModule/NonXMLCommonAttributes.php /^ public $name = 'NonXMLCommonAttributes';$/;" v +name HTMLModule/Object.php /^ public $name = 'Object';$/;" v +name HTMLModule/Presentation.php /^ public $name = 'Presentation';$/;" v +name HTMLModule/Proprietary.php /^ public $name = 'Proprietary';$/;" v +name HTMLModule/Ruby.php /^ public $name = 'Ruby';$/;" v +name HTMLModule/SafeEmbed.php /^ public $name = 'SafeEmbed';$/;" v +name HTMLModule/SafeObject.php /^ public $name = 'SafeObject';$/;" v +name HTMLModule/Scripting.php /^ public $name = 'Scripting';$/;" v +name HTMLModule/StyleAttribute.php /^ public $name = 'StyleAttribute';$/;" v +name HTMLModule/Tables.php /^ public $name = 'Tables';$/;" v +name HTMLModule/Target.php /^ public $name = 'Target';$/;" v +name HTMLModule/Text.php /^ public $name = 'Text';$/;" v +name HTMLModule/Tidy.php /^ foreach ($fixes as $name => $fix) {$/;" v +name HTMLModule/Tidy/Name.php /^ public $name = 'Tidy_Name';$/;" v +name HTMLModule/Tidy/Proprietary.php /^ public $name = 'Tidy_Proprietary';$/;" v +name HTMLModule/Tidy/Strict.php /^ public $name = 'Tidy_Strict';$/;" v +name HTMLModule/Tidy/Transitional.php /^ public $name = 'Tidy_Transitional';$/;" v +name HTMLModule/Tidy/XHTML.php /^ public $name = 'Tidy_XHTML';$/;" v +name HTMLModule/XMLCommonAttributes.php /^ public $name = 'XMLCommonAttributes';$/;" v +name HTMLModuleManager.php /^ foreach ($module->info as $name => $def) {$/;" v +name HTMLModuleManager.php /^ foreach ($module->info as $name => $v) {$/;" v +name HTMLModuleManager.php /^ * module's class name. This array is usually lazy loaded, but a$/;" c +name Injector/AutoParagraph.php /^ public $name = 'AutoParagraph';$/;" v +name Injector/DisplayLinkURI.php /^ public $name = 'DisplayLinkURI';$/;" v +name Injector/Linkify.php /^ public $name = 'Linkify';$/;" v +name Injector/PurifierLinkify.php /^ public $name = 'PurifierLinkify';$/;" v +name Injector/SafeObject.php /^ foreach ($this->addParam as $name => $value) {$/;" v +name Injector/SafeObject.php /^ public $name = 'SafeObject';$/;" v +name Lexer/PH5P.php /^ $name = $node->tagName;$/;" v +name Printer/CSSDefinition.php /^ $name = $this->getClass($obj, 'AttrDef_');$/;" v +name Printer/HTMLDefinition.php /^ foreach ($array as $name => $discard) {$/;" v +name Printer/HTMLDefinition.php /^ foreach ($array as $name => $obj) {$/;" v +name Printer/HTMLDefinition.php /^ foreach ($this->def->info as $name => $def) {$/;" v +name Printer/HTMLDefinition.php /^ foreach ($this->def->info_content_sets as $name => $lookup) {$/;" v +name Token/Text.php /^ public $name = '#PCDATA'; \/**< PCDATA tag name compatible with DTD. *\/$/;" v +name URIDefinition.php /^ foreach ($this->filters as $name => $f) {$/;" v +name URIDefinition.php /^ foreach ($this->postFilters as $name => $f) {$/;" v +name URIDefinition.php /^ foreach ($this->registeredFilters as $name => $filter) {$/;" v +name URIFilter/DisableExternal.php /^ public $name = 'DisableExternal';$/;" v +name URIFilter/DisableExternalResources.php /^ public $name = 'DisableExternalResources';$/;" v +name URIFilter/HostBlacklist.php /^ public $name = 'HostBlacklist';$/;" v +name URIFilter/MakeAbsolute.php /^ public $name = 'MakeAbsolute';$/;" v +name URIFilter/Munge.php /^ public $name = 'Munge';$/;" v +names Printer/HTMLDefinition.php /^ * Listifies a list of objects by retrieving class names and internal state$/;" c +namespace Config.php /^ $namespace = $key;$/;" v +namespace Config.php /^ $namespace = $key;$/;" v +namespace Config.php /^ if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {$/;" v +namespace_values Config.php /^ $namespace_values = $value;$/;" v +needed Injector.php /^ public $needed = array();$/;" v +needed Injector/AutoParagraph.php /^ public $needed = array('p');$/;" v +needed Injector/DisplayLinkURI.php /^ public $needed = array('a');$/;" v +needed Injector/Linkify.php /^ public $needed = array('a' => array('href'));$/;" v +needed Injector/PurifierLinkify.php /^ public $needed = array('a' => array('href'));$/;" v +needed Injector/SafeObject.php /^ public $needed = array('object', 'param');$/;" v +needs_end Injector/AutoParagraph.php /^ $needs_end = true;$/;" v +needs_end Injector/AutoParagraph.php /^ $needs_end = false;$/;" v +needs_start Injector/AutoParagraph.php /^ $needs_start = true;$/;" v +needs_start Injector/AutoParagraph.php /^ $needs_start = false;$/;" v +needs_tracking Lexer.php /^ $needs_tracking =$/;" v +neg UnitConverter.php /^ $neg = $n < 0 ? '-' : ''; \/\/ Negative sign$/;" v +negative AttrDef/Integer.php /^ $negative = true, $zero = true, $positive = true$/;" v +negative AttrDef/Integer.php /^ protected $negative = true;$/;" v +nest Injector/SafeObject.php /^ $nest = count($this->currentNesting) - 1;$/;" v +nesting ChildDef/Custom.php /^ $nesting = 0; \/\/ depth into the nest$/;" v +nesting ChildDef/Required.php /^ $nesting = 0;$/;" v +nesting ChildDef/Table.php /^ $nesting = 0; \/\/ current depth so we can determine nodes$/;" v +nesting Injector.php /^ if ($nesting === null) $nesting = 0;$/;" v +nesting Injector/AutoParagraph.php /^ else $nesting = 0;$/;" v +nesting Injector/AutoParagraph.php /^ if ($current instanceof HTMLPurifier_Token_Start) $nesting = 1;$/;" v +new Injector/SafeObject.php /^ $new = array($token);$/;" v +new Printer/HTMLDefinition.php /^ $new = $this->getClass($new, 'TagTransform_');$/;" v +new VarParser/Flexible.php /^ $new = array();$/;" v +new_cache DefinitionCacheFactory.php /^ $new_cache = $decorator->decorate($cache);$/;" v +new_data Lexer/DOMLex.php /^ $new_data = trim($data);$/;" v +new_declarations AttrDef/CSS.php /^ $new_declarations = '';$/;" v +new_decls Filter/ExtractStyleBlocks.php /^ $new_decls = array();$/;" v +new_def HTMLModuleManager.php /^ $new_def = clone $module->info[$name];$/;" v +new_font AttrDef/CSS/FontFamily.php /^ $new_font = '';$/;" v +new_html Lexer/PH5P.php /^ $new_html = $this->normalize($html, $config, $context);$/;" v +new_html Lexer/PH5P.php /^ $new_html = $this->wrapHTML($new_html, $config, $context);$/;" v +new_key Token/Tag.php /^ $new_key = strtolower($key);$/;" v +new_length AttrDef/CSS/URI.php /^ $new_length = strlen($uri) - 1;$/;" v +new_length AttrDef/CSS/URI.php /^ $new_length = strlen($uri_string) - 1;$/;" v +new_log UnitConverter.php /^ $new_log = (int) floor(log(abs($n), 10)); \/\/ Number of digits left of decimal - 1$/;" v +new_parts AttrDef/CSS/Color.php /^ $new_parts = array();$/;" v +new_selector Filter/ExtractStyleBlocks.php /^ $new_selector = array(); \/\/ because multiple ones are possible$/;" v +new_stack URIFilter/MakeAbsolute.php /^ $new_stack = $this->_collapseStack($new_stack);$/;" v +new_stack URIFilter/MakeAbsolute.php /^ $new_stack = array_merge($this->basePathStack, $stack);$/;" v +new_string AttrDef/Lang.php /^ $new_string = $subtags[0];$/;" v +new_struct ErrorCollector.php /^ $new_struct = new HTMLPurifier_ErrorStruct();$/;" v +new_tag TagTransform/Font.php /^ $new_tag = clone $tag;$/;" v +new_tag TagTransform/Font.php /^ $new_tag = clone $tag;$/;" v +new_tag TagTransform/Simple.php /^ $new_tag = clone $tag;$/;" v +new_token Strategy/MakeWellFormed.php /^ $new_token = new HTMLPurifier_Token_End($parent->name);$/;" v +new_token Strategy/MakeWellFormed.php /^ $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name);$/;" v +new_triad AttrDef/CSS/Color.php /^ $new_triad = implode(',', $new_parts);$/;" v +new_uri URIFilter/Munge.php /^ $new_uri = $this->parser->parse($new_uri);$/;" v +new_uri URIFilter/Munge.php /^ $new_uri = strtr($this->target, $this->replace);$/;" v +next Injector/RemoveEmpty.php /^ $next = $this->inputTokens[$i];$/;" v +next Injector/RemoveEmpty.php /^ $next = false;$/;" v +next_node Lexer/PH5P.php /^ $next_node = strtolower($this->characters('A-Za-z', $this->char + 1));$/;" v +nl Generator.php /^ $nl = $this->config->get('Output.Newline');$/;" v +nl Generator.php /^ if ($nl === null) $nl = PHP_EOL;$/;" v +nl Lexer/DirectLex.php /^ $nl = "\\n";$/;" v +nl_pos Lexer/DirectLex.php /^ $nl_pos = strrpos($html, $nl, $rcursor - $length);$/;" v +node Lexer/PH5P.php /^ $node = $clone;$/;" v +node Lexer/PH5P.php /^ $node = $this->stack[$n];$/;" v +node Lexer/PH5P.php /^ $node = $furthest_block;$/;" v +node Lexer/PH5P.php /^ $node = $this->stack[$n];$/;" v +node Lexer/PH5P.php /^ $node = end($this->stack);$/;" v +node Lexer/PH5P.php /^ $node = end($this->stack)->nodeName;$/;" v +node Lexer/PH5P.php /^ $node = $this->stack[$leng - 1 - $n];$/;" v +node Lexer/PH5P.php /^ $node = $this->stack[$n];$/;" v +node Lexer/PH5P.php /^ $node = end($this->stack)->nodeName;$/;" v +node Lexer/PH5P.php /^ $node = end($this->stack);$/;" v +nonSpecialEntityCallback EntityParser.php /^ protected function nonSpecialEntityCallback($matches) {$/;" f +non_negative AttrDef/CSS/Number.php /^ protected $non_negative = false;$/;" v +non_xml HTMLModuleManager.php /^ $non_xml = array('NonXMLCommonAttributes');$/;" v +none AttrDef/CSS/Background.php /^ $none = false;$/;" v +none AttrDef/CSS/ListStyle.php /^ else $none = true;$/;" v +none AttrDef/CSS/ListStyle.php /^ $none = false;$/;" v +normalize Lexer.php /^ public function normalize($html, $config, $context) {$/;" f +normalize PercentEncoder.php /^ public function normalize($string) {$/;" f +notifyEnd Injector.php /^ public function notifyEnd($token) {}$/;" f +ns Printer/ConfigForm.php /^ foreach ($all as $ns => $directives) {$/;" v +num AttrDef/CSS/Color.php /^ $num = (float) substr($part, 0, $length - 1);$/;" v +num AttrDef/CSS/Color.php /^ $num = (int) $part;$/;" v +num AttrDef/CSS/Color.php /^ if ($num < 0) $num = 0;$/;" v +num AttrDef/CSS/Color.php /^ if ($num > 100) $num = 100;$/;" v +num AttrDef/CSS/Color.php /^ if ($num > 255) $num = 255;$/;" v +num AttrDef/CSS/Multiple.php /^ for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {$/;" v +num Printer/HTMLDefinition.php /^ protected function heavyHeader($text, $num = 1) {$/;" v +num_amp Lexer.php /^ $num_amp = substr_count($string, '&') - substr_count($string, '& ') -$/;" v +num_amp_2 Lexer.php /^ $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -$/;" v +num_equal Lexer/DirectLex.php /^ $num_equal = substr_count($string, '=');$/;" v +num_esc_amp Lexer.php /^ $num_esc_amp = substr_count($string, '&');$/;" v +num_subtags AttrDef/Lang.php /^ $num_subtags = count($subtags);$/;" v +number AttrDef/CSS/Number.php /^ $number = substr($number, 1);$/;" v +number AttrDef/CSS/Number.php /^ $number = ltrim($number, '0');$/;" v +number AttrDef/CSS/Number.php /^ $number = $this->parseCDATA($number);$/;" v +number AttrDef/CSS/Percentage.php /^ $number = $this->number_def->validate($number, $config, $context);$/;" v +number AttrDef/CSS/Percentage.php /^ $number = substr($string, 0, $length - 1);$/;" v +nvalue Printer/ConfigForm.php /^ $nvalue = '';$/;" v +nvar VarParser/Flexible.php /^ $nvar = array();$/;" v +obj ConfigSchema.php /^ $obj = new stdclass();$/;" v +obj ConfigSchema.php /^ $obj = new stdclass;$/;" v +object HTMLModule/SafeObject.php /^ $object = $this->addElement($/;" v +objectStack Injector/SafeObject.php /^ protected $objectStack = array();$/;" v +oct AttrDef/URI/IPv4.php /^ $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; \/\/ 0-255$/;" v +of Printer.php /^ * @param $obj Object to determine class of$/;" c +of Printer.php /^ * Retrieves the class of an object without prefixes, as well as metadata$/;" c +of Token/Tag.php /^ * Abstract class of a tag token (start, end or empty), and its behavior.$/;" c +offsetGet StringHash.php /^ public function offsetGet($index) {$/;" f +ok AttrDef/CSS.php /^ $ok = true;$/;" v +ok AttrDef/CSS.php /^ $ok = false;$/;" v +ok AttrDef/URI.php /^ $ok = true;$/;" v +ok AttrDef/URI.php /^ $ok = false;$/;" v +ok AttrValidator.php /^ $ok =& $context->get('IDAccumulator', true);$/;" v +ok HTMLModuleManager.php /^ $ok = true;$/;" v +ok HTMLModuleManager.php /^ $ok = false;$/;" v +ok Injector/AutoParagraph.php /^ $ok = $result;$/;" v +ok Injector/AutoParagraph.php /^ $ok = false;$/;" v +ok Strategy/MakeWellFormed.php /^ $ok = true;$/;" v +ok Strategy/MakeWellFormed.php /^ $ok = false;$/;" v +ok Strategy/RemoveForeignElements.php /^ $ok = false;$/;" v +ok Strategy/RemoveForeignElements.php /^ $ok = true;$/;" v +okay ChildDef/Custom.php /^ $okay =$/;" v +ol HTMLModule/Legacy.php /^ $ol = $this->addBlankElement('ol');$/;" v +ol_types HTMLModule/Tidy/XHTMLAndHTML4.php /^ $ol_types = array($/;" v +old DefinitionCache/Serializer.php /^ $old = umask(0022); \/\/ disable group and world writes$/;" v +old Lexer/DOMLex.php /^ $old = $html;$/;" v +old Printer/HTMLDefinition.php /^ foreach ($def->info_tag_transform as $old => $new) {$/;" v +old Strategy/MakeWellFormed.php /^ $old = array_splice($this->tokens, $this->t, $delete, $token);$/;" v +oldVersion Lexer/DirectLex.php /^ $oldVersion = version_compare(PHP_VERSION, '5.1', '<');$/;" v +old_lookup ContentSets.php /^ $old_lookup = $this->lookup;$/;" v +old_lookup ContentSets.php /^ $old_lookup = false;$/;" v +oldskip Strategy/MakeWellFormed.php /^ $oldskip = isset($old[0]) ? $old[0]->skip : array();$/;" v +only AttrTransform/SafeParam.php /^ * This class only supports Flash. In the future, Quicktime support$/;" c +openHandler Lexer/PEARSax3.php /^ public function openHandler(&$parser, $name, $attrs, $closed) {$/;" f +open_quote Lexer/DirectLex.php /^ $open_quote = ($first_char == '"' || $first_char == "'");$/;" v +original AttrDef/URI/IPv6.php /^ $original = $aIP;$/;" v +original_module HTMLModuleManager.php /^ $original_module = $module;$/;" v +original_name Strategy/RemoveForeignElements.php /^ $original_name = $token->name;$/;" v +other_directive ConfigSchema/Validator.php /^ $other_directive = $this->aliases[$s];$/;" v +ourHostParts URIFilter/DisableExternal.php /^ protected $ourHostParts = false;$/;" v +our_host URIFilter/DisableExternal.php /^ $our_host = $config->getDefinition('URI')->host;$/;" v +out Encoder.php /^ $out = '';$/;" v +overload HTMLModuleManager.php /^ public function registerModule($module, $overload = false) {$/;" v +p HTMLModule/Legacy.php /^ $p = $this->addBlankElement('p');$/;" v +p HTMLModule/Text.php /^ $p = $this->addElement('p', 'Block', 'Inline', 'Common');$/;" v +p TokenFactory.php /^ $p = clone $this->p_comment;$/;" v +p TokenFactory.php /^ $p = clone $this->p_empty;$/;" v +p TokenFactory.php /^ $p = clone $this->p_end;$/;" v +p TokenFactory.php /^ $p = clone $this->p_start;$/;" v +p TokenFactory.php /^ $p = clone $this->p_text;$/;" v +padding CSSDefinition.php /^ $padding =$/;" v +par Injector/AutoParagraph.php /^ $par = $raw_paragraphs[$i];$/;" v +par Injector/AutoParagraph.php /^ $par = new HTMLPurifier_Token_Start('p');$/;" v +paragraphs Injector/AutoParagraph.php /^ $paragraphs = array(); \/\/ without empty paragraphs$/;" v +param HTMLModule/SafeObject.php /^ $param = $this->addElement('param', false, 'Empty', false,$/;" v +paramStack Injector/SafeObject.php /^ protected $paramStack = array();$/;" v +parameters AttrDef/CSS/Filter.php /^ $parameters = substr($value, $cursor, $parameters_length);$/;" v +parameters_length AttrDef/CSS/Filter.php /^ $parameters_length = strcspn($value, ')', $cursor);$/;" v +params AttrDef/CSS/Filter.php /^ $params = explode(',', $parameters);$/;" v +params HTMLModule/Tidy.php /^ $params = array();$/;" v +parent Config.php /^ $parent = $parent ? $parent : $definition->defaultPlist;$/;" v +parent Config.php /^ public function __construct($definition, $parent = null) {$/;" v +parent HTMLDefinition.php /^ $parent = $config->get('HTML.Parent');$/;" v +parent Injector.php /^ $parent = $this->htmlDefinition->info[$parent_token->name];$/;" v +parent Injector.php /^ $parent = $this->htmlDefinition->info_parent_def;$/;" v +parent Strategy/MakeWellFormed.php /^ $parent = array_pop($this->stack);$/;" v +parent_def Strategy/FixNesting.php /^ $parent_def = $definition->info[$parent_name];$/;" v +parent_def Strategy/FixNesting.php /^ $parent_def = $definition->info_parent_def;$/;" v +parent_index Strategy/FixNesting.php /^ $parent_index = $parent_name = $parent_def = null;$/;" v +parent_index Strategy/FixNesting.php /^ $parent_index = $stack[$count-1];$/;" v +parent_name Strategy/FixNesting.php /^ $parent_name = $tokens[$parent_index]->name;$/;" v +parent_name Strategy/FixNesting.php /^ $parent_name = $definition->info_parent;$/;" v +parent_result AttrDef/HTML/Length.php /^ $parent_result = parent::validate($string, $config, $context);$/;" v +parent_result AttrDef/HTML/MultiLength.php /^ $parent_result = parent::validate($string, $config, $context);$/;" v +parent_token Injector.php /^ $parent_token = array_pop($this->currentNesting);$/;" v +parse URIParser.php /^ public function parse($uri) {$/;" f +parse VarParser.php /^ final public function parse($var, $type, $allow_null = false) {$/;" f +parseAttributeString Lexer/DirectLex.php /^ public function parseAttributeString($string, $config, $context) {$/;" f +parseCDATA AttrDef.php /^ public function parseCDATA($string) {$/;" f +parseContents HTMLModule.php /^ public function parseContents($contents) {$/;" f +parseData Lexer.php /^ public function parseData($string) {$/;" f +parseFile StringHashParser.php /^ public function parseFile($file) {$/;" f +parseHandle StringHashParser.php /^ protected function parseHandle($fh) {$/;" f +parseImplementation VarParser.php /^ protected function parseImplementation($var, $type, $allow_null) {$/;" f +parseImplementation VarParser/Flexible.php /^ protected function parseImplementation($var, $type, $allow_null) {$/;" f +parseImplementation VarParser/Native.php /^ protected function parseImplementation($var, $type, $allow_null) {$/;" f +parseMultiFile StringHashParser.php /^ public function parseMultiFile($file) {$/;" f +parseTinyMCEAllowedList HTMLDefinition.php /^ public function parseTinyMCEAllowedList($list) {$/;" f +parser ConfigSchema/InterchangeBuilder.php /^ $parser = new HTMLPurifier_StringHashParser();$/;" v +parser Lexer/PEARSax3.php /^ $parser = new XML_HTMLSax3();$/;" v +parser Lexer/PH5P.php /^ $parser = new HTML5($new_html);$/;" v +parser URIDefinition.php /^ $parser = new HTMLPurifier_URIParser();$/;" v +part AttrDef/CSS/Color.php /^ $part = trim($part);$/;" v +part AttrDef/HTML/LinkTypes.php /^ $part = strtolower(trim($part));$/;" v +parts AttrDef/CSS/Color.php /^ $parts = explode(',', $triad);$/;" v +parts AttrDef/CSS/Multiple.php /^ $parts = explode(' ', $string); \/\/ parseCDATA replaced \\r, \\t and \\n$/;" v +parts AttrDef/CSS/TextDecoration.php /^ $parts = explode(' ', $string);$/;" v +parts AttrDef/HTML/LinkTypes.php /^ $parts = explode(' ', $string);$/;" v +parts PercentEncoder.php /^ $parts = explode('%', $string);$/;" v +path URIParser.php /^ $path = $matches[5]; \/\/ always present, can be empty$/;" v +path_parts URI.php /^ $path_parts = array();$/;" v +pattern AttrDef/HTML/Nmtokens.php /^ $pattern = '\/(?:(?<=\\s)|\\A)'. \/\/ look behind for space or string start$/;" v +pays UnitConverter.php /^ * About precision: This conversion function pays very special$/;" f +pcdata_allowed ChildDef/Required.php /^ $pcdata_allowed = isset($this->elements['#PCDATA']);$/;" v +pcode LanguageFactory.php /^ $pcode = str_replace('-', '_', $code); \/\/ make valid PHP classname$/;" v +performInclusions AttrCollections.php /^ public function performInclusions(&$attr) {$/;" f +performs AttrValidator.php /^ * because the operation this class performs on the token are$/;" c +plain Injector/RemoveEmpty.php /^ $plain = str_replace("\\xC2\\xA0", "", $next->data);$/;" v +points AttrDef/HTML/Length.php /^ $points = (int) $points;$/;" v +points AttrDef/HTML/Length.php /^ $points = substr($string, 0, $length - 1);$/;" v +polite Printer.php /^ protected function listify($array, $polite = false) {$/;" v +populate HTMLModule/Tidy.php /^ public function populate($fixes) {$/;" f +port URIParser.php /^ $port = !empty($matches[4]) ? (int) $matches[5] : null;$/;" v +port URIParser.php /^ $port = $host = $userinfo = null;$/;" v +position Token.php /^ public function position($l = null, $c = null) {$/;" f +position_comment_end Lexer/DirectLex.php /^ $position_comment_end = strlen($html);$/;" v +position_comment_end Lexer/DirectLex.php /^ $position_comment_end = strpos($html, '-->', $cursor);$/;" v +position_first_space Lexer/DirectLex.php /^ $position_first_space = strcspn($segment, $this->_whitespace);$/;" v +position_next_gt Lexer/DirectLex.php /^ $position_next_gt = strpos($html, '>', $cursor);$/;" v +position_next_lt Lexer/DirectLex.php /^ $position_next_lt = strpos($html, '<', $cursor);$/;" v +positive AttrDef/Integer.php /^ protected $positive = true;$/;" v +post URIFilter.php /^ public $post = false;$/;" v +post URIFilter/Munge.php /^ public $post = true;$/;" v +postFilter Filter.php /^ public function postFilter($html, $config, $context) {$/;" f +postFilter Filter/YouTube.php /^ public function postFilter($html, $config, $context) {$/;" f +postFilter URIDefinition.php /^ public function postFilter(&$uri, $config, $context) {$/;" f +postFilterCallback Filter/YouTube.php /^ protected function postFilterCallback($matches) {$/;" f +postFilters URIDefinition.php /^ protected $postFilters = array();$/;" v +postProcess ConfigSchema.php /^ public function postProcess() {$/;" f +post_regex Filter/YouTube.php /^ $post_regex = '#<span class="youtube-embed">([A-Za-z0-9\\-_]+)<\/span>#';$/;" v +pre AttrDef/URI/IPv6.php /^ $pre = '(?:\/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; \/\/ \/0 - \/128$/;" v +pre HTMLModule/Legacy.php /^ $pre = $this->addBlankElement('pre');$/;" v +pre HTMLModule/Text.php /^ $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');$/;" v +preFilter Filter.php /^ public function preFilter($html, $config, $context) {$/;" f +preFilter Filter/ExtractStyleBlocks.php /^ public function preFilter($html, $config, $context) {$/;" f +preFilter Filter/YouTube.php /^ public function preFilter($html, $config, $context) {$/;" f +pre_regex Filter/YouTube.php /^ $pre_regex = '#<object[^>]+>.+?'.$/;" v +pre_replace Filter/YouTube.php /^ $pre_replace = '<span class="youtube-embed">\\1<\/span>';$/;" v +precise UnitConverter.php /^ $precise = (string) round(substr($r, 0, strlen($r) + $scale), -1);$/;" v +prefix AttrDef/HTML/ID.php /^ $prefix = $config->get('Attr.IDPrefix');$/;" v +prefix ConfigSchema/Validator.php /^ else $prefix = ucfirst($this->getFormattedContext());$/;" v +prefix ConfigSchema/Validator.php /^ if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();$/;" v +prefix HTMLModuleManager.php /^ * Adds a class prefix that registerModule() will use to resolve a$/;" c +prefix Printer.php /^ $prefix = 'HTMLPurifier_' . $sec_prefix;$/;" v +prefix Printer.php /^ if (!$five) $prefix = strtolower($prefix);$/;" v +prefixes HTMLModuleManager.php /^ public $prefixes = array('HTMLPurifier_HTMLModule_');$/;" v +prepare Injector.php /^ public function prepare($config, $context) {$/;" f +prepare Injector/PurifierLinkify.php /^ public function prepare($config, $context) {$/;" f +prepare Injector/RemoveEmpty.php /^ public function prepare($config, $context) {$/;" f +prepare Injector/SafeObject.php /^ public function prepare($config, $context) {$/;" f +prepare URIFilter.php /^ public function prepare($config) {return true;}$/;" f +prepare URIFilter/DisableExternal.php /^ public function prepare($config) {$/;" f +prepare URIFilter/HostBlacklist.php /^ public function prepare($config) {$/;" f +prepare URIFilter/MakeAbsolute.php /^ public function prepare($config) {$/;" f +prepare URIFilter/Munge.php /^ public function prepare($config) {$/;" f +prepareArrayFromForm Config.php /^ public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {$/;" f +prepareGenerator Printer.php /^ public function prepareGenerator($config) {$/;" f +prependCSS AttrTransform.php /^ public function prependCSS(&$attr, $css) {$/;" f +prependCSS TagTransform.php /^ protected function prependCSS(&$attr, $css) {$/;" f +prepend_style TagTransform/Font.php /^ $prepend_style = '';$/;" v +preserve PercentEncoder.php /^ protected $preserve = array();$/;" v +prev Injector/RemoveEmpty.php /^ $prev = $this->inputTokens[$b];$/;" v +prev Strategy/MakeWellFormed.php /^ $prev = $tokens[$t];$/;" v +processModule HTMLModuleManager.php /^ public function processModule($module) {$/;" f +processModules HTMLDefinition.php /^ protected function processModules($config) {$/;" f +processToken Strategy/MakeWellFormed.php /^ protected function processToken($token, $injector = -1) {$/;" f +processed AttrCollections.php /^ $processed = array();$/;" v +prop AttrDef/CSS.php /^ foreach ($propvalues as $prop => $value) {$/;" v +property AttrDef/CSS.php /^ $property = strtolower($property);$/;" v +property AttrDef/CSS.php /^ $property = trim($property);$/;" v +property AttrDef/CSS.php /^ $property = false;$/;" v +property AttrTransform/ImgSpace.php /^ $property = "margin-$suffix";$/;" v +property HTMLModule/Tidy.php /^ if (is_null($property)) $property = 'pre';$/;" v +property HTMLModule/Tidy.php /^ $property = $attr = null;$/;" v +property Printer/CSSDefinition.php /^ foreach ($this->def->info as $property => $obj) {$/;" v +propname AttrDef/CSS/Border.php /^ foreach ($this->info as $propname => $validator) {$/;" v +propvalues AttrDef/CSS.php /^ $propvalues = array();$/;" v +prototype ConfigSchema.php /^ } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {$/;" v +prototype DefinitionCacheFactory.php /^ } elseif ($instance === null || $prototype === true) {$/;" v +prototype LanguageFactory.php /^ } elseif ($instance === null || $prototype == true) {$/;" v +prototype URISchemeRegistry.php /^ } elseif ($instance === null || $prototype == true) {$/;" v +purifier ConfigSchema/Builder/Xml.php /^ $purifier = HTMLPurifier::getInstance();$/;" v +qf_encoder URI.php /^ $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '\/?');$/;" v +query URIParser.php /^ $query = !empty($matches[6]) ? $matches[7] : null;$/;" v +quote AttrDef/CSS/FontFamily.php /^ $quote = $font[0];$/;" v +quote AttrDef/CSS/URI.php /^ $quote = $uri[0];$/;" v +quote Generator.php /^ public function escape($string, $quote = ENT_COMPAT) {$/;" v +quoted_value Lexer/DirectLex.php /^ $quoted_value = trim($quoted_value);$/;" v +r AttrDef/CSS/Background.php /^ $r = $bit;$/;" v +r AttrDef/CSS/Background.php /^ $r = $this->info['background-' . $key]->validate($bit, $config, $context);$/;" v +r AttrDef/CSS/BackgroundPosition.php /^ $r = $this->length->validate($bit, $config, $context);$/;" v +r AttrDef/CSS/BackgroundPosition.php /^ $r = $this->percentage->validate($bit, $config, $context);$/;" v +r AttrDef/CSS/Border.php /^ $r = $validator->validate($bit, $config, $context);$/;" v +r AttrDef/CSS/Font.php /^ $r = $this->info['line-height']->validate($/;" v +r AttrDef/CSS/Font.php /^ $r = $this->info[$validator_name]->validate($/;" v +r AttrDef/CSS/Font.php /^ $r = $this->info['font-family']->validate($/;" v +r AttrDef/CSS/Font.php /^ $r = $this->info['font-size']->validate($/;" v +r AttrDef/CSS/ListStyle.php /^ $r = $this->info['list-style-' . $key]->validate($bit, $config, $context);$/;" v +r Encoder.php /^ $r === '' ||$/;" v +r Encoder.php /^ $r = iconv('UTF-8', "$encoding\/\/IGNORE", $c); \/\/ initial conversion$/;" v +r HTMLModule/Tidy/Name.php /^ $r = array();$/;" v +r HTMLModule/Tidy/Proprietary.php /^ $r = array();$/;" v +r HTMLModule/Tidy/Strict.php /^ $r = parent::makeFixes();$/;" v +r HTMLModule/Tidy/XHTML.php /^ $r = array();$/;" v +r HTMLModule/Tidy/XHTMLAndHTML4.php /^ $r = array();$/;" v +r Injector.php /^ $r = $this->rewind;$/;" v +r URIDefinition.php /^ $r = $filter->prepare($config);$/;" v +r UnitConverter.php /^ $r = sprintf('%.0f', (float) $r);$/;" v +r_URI URIParser.php /^ $r_URI = '!'.$/;" v +r_authority URIParser.php /^ $r_authority = "\/^((.+?)@)?(\\[[^\\]]+\\]|[^:]*)(:(\\d*))?\/";$/;" v +raw ChildDef/Custom.php /^ $raw = "($raw)";$/;" v +raw ChildDef/Custom.php /^ $raw = str_replace(' ', '', $this->dtd_regex);$/;" v +raw Config.php /^ public function getDefinition($type, $raw = false) {$/;" v +raw Language.php /^ $raw = $this->messages[$key];$/;" v +rawPosition Token.php /^ public function rawPosition($l, $c) {$/;" f +raw_aliases ConfigSchema/InterchangeBuilder.php /^ $raw_aliases = trim($hash->offsetGet('ALIASES'));$/;" v +raw_fallback LanguageFactory.php /^ $raw_fallback = $this->getFallbackFor($code);$/;" v +raw_paragraphs Injector/AutoParagraph.php /^ $raw_paragraphs = explode("\\n\\n", $data);$/;" v +rb HTMLModule/Ruby.php /^ $rb = $this->addElement('rb', false, 'Inline', 'Common');$/;" v +rcursor Lexer/DirectLex.php /^ $rcursor = $cursor - (int) $inside_tag;$/;" v +reconstructActiveFormattingElements Lexer/PH5P.php /^ private function reconstructActiveFormattingElements() {$/;" f +reflector Bootstrap.php /^ $reflector = new ReflectionMethod($func[0], $func[1]);$/;" v +reg ChildDef/Custom.php /^ $reg = $raw;$/;" v +reg ChildDef/Custom.php /^ $reg = preg_replace("\/$el\/", '(,\\\\0)', $reg);$/;" v +reg ChildDef/Custom.php /^ $reg = preg_replace("\/([^,(|]\\(+),\/", '\\\\1', $reg);$/;" v +reg ChildDef/Custom.php /^ $reg = preg_replace("\/,\\(\/", '(', $reg);$/;" v +regexp AttrDef/HTML/ID.php /^ $regexp = $config->get('Attr.IDBlacklistRegexp');$/;" v +register Context.php /^ public function register($name, &$ref) {$/;" f +register DefinitionCacheFactory.php /^ public function register($short, $long) {$/;" f +register DoctypeRegistry.php /^ public function register($doctype, $xml = true, $modules = array(),$/;" f +register URISchemeRegistry.php /^ public function register($scheme, $scheme_obj) {$/;" f +registerAutoload Bootstrap.php /^ public static function registerAutoload() {$/;" f +registerFilter URIDefinition.php /^ public function registerFilter($filter) {$/;" f +registerModule HTMLModuleManager.php /^ public function registerModule($module, $overload = false) {$/;" f +registeredFilters URIDefinition.php /^ protected $registeredFilters = array();$/;" v +registeredModules HTMLModuleManager.php /^ public $registeredModules = array();$/;" v +registry URI.php /^ $registry = HTMLPurifier_URISchemeRegistry::instance();$/;" v +remove DefinitionCache.php /^ abstract public function remove($config);$/;" f +remove DefinitionCache/Decorator.php /^ public function remove($config) {$/;" f +remove DefinitionCache/Null.php /^ public function remove($config) {$/;" f +remove DefinitionCache/Serializer.php /^ public function remove($config) {$/;" f +remove Strategy/MakeWellFormed.php /^ private function remove() {$/;" f +remove_fixes HTMLModule/Tidy.php /^ $remove_fixes = $config->get('HTML.TidyRemove');$/;" v +remove_invalid_img Strategy/RemoveForeignElements.php /^ $remove_invalid_img = $config->get('Core.RemoveInvalidImg');$/;" v +remove_script_contents Strategy/RemoveForeignElements.php /^ $remove_script_contents = $config->get('Core.RemoveScriptContents');$/;" v +remove_until Strategy/RemoveForeignElements.php /^ $remove_until = $token->name;$/;" v +remove_until Strategy/RemoveForeignElements.php /^ $remove_until = false;$/;" v +remove_until Strategy/RemoveForeignElements.php /^ $remove_until = false;$/;" v +render Printer.php /^ \/\/ function render() {}$/;" f +render Printer/CSSDefinition.php /^ public function render($config) {$/;" f +render Printer/ConfigForm.php /^ public function render($config, $allowed = true, $render_controls = true) {$/;" f +render Printer/ConfigForm.php /^ public function render($ns, $directive, $value, $name, $config) {$/;" f +render Printer/HTMLDefinition.php /^ public function render($config) {$/;" f +renderChildren Printer/HTMLDefinition.php /^ protected function renderChildren($def) {$/;" f +renderContentSets Printer/HTMLDefinition.php /^ protected function renderContentSets() {$/;" f +renderDoctype Printer/HTMLDefinition.php /^ protected function renderDoctype() {$/;" f +renderEnvironment Printer/HTMLDefinition.php /^ protected function renderEnvironment() {$/;" f +renderInfo Printer/HTMLDefinition.php /^ protected function renderInfo() {$/;" f +renderNamespace Printer/ConfigForm.php /^ protected function renderNamespace($ns, $directives) {$/;" f +replace DefinitionCache.php /^ abstract public function replace($def, $config);$/;" f +replace DefinitionCache/Decorator.php /^ public function replace($def, $config) {$/;" f +replace DefinitionCache/Decorator/Cleanup.php /^ public function replace($def, $config) {$/;" f +replace DefinitionCache/Decorator/Memory.php /^ public function replace($def, $config) {$/;" f +replace DefinitionCache/Null.php /^ public function replace($def, $config) {$/;" f +replace DefinitionCache/Serializer.php /^ public function replace($def, $config) {$/;" f +replace Strategy/MakeWellFormed.php /^ $replace = array($token);$/;" v +replace URIFilter/Munge.php /^ protected $replace = array();$/;" v +representing DefinitionCache.php /^ * Abstract class representing Definition cache managers that implements$/;" c +reprocess Strategy/MakeWellFormed.php /^ $reprocess = true;$/;" v +reprocess Strategy/MakeWellFormed.php /^ $reprocess = true;$/;" v +reprocess Strategy/MakeWellFormed.php /^ $reprocess = true;$/;" v +reprocess Strategy/MakeWellFormed.php /^ $reprocess = true;$/;" v +reprocess Strategy/MakeWellFormed.php /^ $reprocess ? $reprocess = false : $t++$/;" v +reprocess Strategy/MakeWellFormed.php /^ $reprocess = false; \/\/ whether or not to reprocess the same token$/;" v +required AttrDef.php /^ public $required = false;$/;" v +required_attr ElementDef.php /^ public $required_attr = array();$/;" v +requires AttrDef/URI/IPv6.php /^ * @note This function requires brackets to have been removed from address$/;" f +reset PropertyList.php /^ public function reset($name = null) {$/;" f +resetAccessed StringHash.php /^ public function resetAccessed() {$/;" f +resetInsertionMode Lexer/PH5P.php /^ private function resetInsertionMode() {$/;" f +resolves DoctypeRegistry.php /^ * @note This function resolves aliases$/;" f +result AttrDef/CSS.php /^ $result = $definition->info[$property]->validate($/;" v +result AttrDef/CSS.php /^ $result = 'inherit';$/;" v +result AttrDef/CSS/AlphaValue.php /^ $result = parent::validate($number, $config, $context);$/;" v +result AttrDef/CSS/AlphaValue.php /^ if ($float < 0.0) $result = '0';$/;" v +result AttrDef/CSS/AlphaValue.php /^ if ($float > 1.0) $result = '1';$/;" v +result AttrDef/CSS/Composite.php /^ $result = $this->defs[$i]->validate($string, $config, $context);$/;" v +result AttrDef/CSS/Multiple.php /^ $result = $this->single->validate($parts[$i], $config, $context);$/;" v +result AttrDef/CSS/URI.php /^ $result = parent::validate($uri, $config, $context);$/;" v +result AttrDef/CSS/URI.php /^ $result = str_replace($keys, $values, $result);$/;" v +result AttrDef/Enum.php /^ $result = isset($this->valid_values[$string]);$/;" v +result AttrDef/HTML/ID.php /^ $result = ($trim === '');$/;" v +result AttrDef/HTML/ID.php /^ $result = true;$/;" v +result AttrDef/URI.php /^ $result = $scheme_obj->validate($uri, $config, $context);$/;" v +result AttrDef/URI.php /^ $result = $uri->validate($config, $context);$/;" v +result AttrDef/URI.php /^ $result = $uri_def->filter($uri, $config, $context);$/;" v +result AttrDef/URI.php /^ $result = $uri_def->postFilter($uri, $config, $context);$/;" v +result AttrDef/URI/Email/SimpleCheck.php /^ $result = preg_match('\/^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$\/i', $string);$/;" v +result AttrTransform/Input.php /^ $result = $this->pixels->validate($attr['size'], $config, $context);$/;" v +result AttrTransform/NameSync.php /^ $result = $this->idDef->validate($name, $config, $context);$/;" v +result AttrValidator.php /^ $result = $defs[$attr_key]->validate($/;" v +result AttrValidator.php /^ $result = false;$/;" v +result AttrValidator.php /^ $result = $d_defs[$attr_key]->validate($/;" v +result AttrValidator.php /^ $result = false;$/;" v +result AttrValidator.php /^ if ($result === false || $result === null) {$/;" v +result ChildDef/Optional.php /^ $result = parent::validateChildren($tokens_of_children, $config, $context);$/;" v +result ChildDef/Required.php /^ $result = array();$/;" v +result ChildDef/StrictBlockquote.php /^ $result = parent::validateChildren($tokens_of_children, $config, $context);$/;" v +result ChildDef/StrictBlockquote.php /^ if ($result === true) $result = $tokens_of_children;$/;" v +result Encoder.php /^ $result = '';$/;" v +result Injector.php /^ $result = $this->checkNeeded($config);$/;" v +result Injector.php /^ $result = $this->forward($i, $current);$/;" v +result Injector/AutoParagraph.php /^ $result = $this->_checkNeedsP($current);$/;" v +result Length.php /^ $result = $def->validate($this->n, false, false);$/;" v +result Lexer.php /^ $result = preg_match('!<body[^>]*>(.+?)<\/body>!is', $html, $matches);$/;" v +result Strategy/FixNesting.php /^ $result = $def->child->validateChildren($/;" v +result Strategy/FixNesting.php /^ $result = false;$/;" v +result Strategy/FixNesting.php /^ $result = false;$/;" v +result Strategy/RemoveForeignElements.php /^ $result = array();$/;" v +result URI.php /^ $result = '';$/;" v +result URIDefinition.php /^ $result = $f->filter($uri, $config, $context);$/;" v +result URIFilter/MakeAbsolute.php /^ $result = array();$/;" v +result URIParser.php /^ $result = preg_match($r_URI, $uri, $matches);$/;" v +result VarParser/Native.php /^ $result = eval("\\$var = $expr;");$/;" v +ret AttrDef/CSS/Background.php /^ $ret = array();$/;" v +ret AttrDef/CSS/BackgroundPosition.php /^ $ret = array();$/;" v +ret AttrDef/CSS/Border.php /^ $ret = ''; \/\/ return value$/;" v +ret AttrDef/CSS/ListStyle.php /^ $ret = array();$/;" v +ret AttrDef/HTML/Class.php /^ $ret = array();$/;" v +ret ChildDef/StrictBlockquote.php /^ $ret = array();$/;" v +ret ChildDef/Table.php /^ $ret = array_merge($ret, $collection);$/;" v +ret ChildDef/Table.php /^ $ret = array();$/;" v +ret ChildDef/Table.php /^ foreach ($content as $token_array) $ret = array_merge($ret, $token_array);$/;" v +ret ChildDef/Table.php /^ if ($caption !== false) $ret = array_merge($ret, $caption);$/;" v +ret ChildDef/Table.php /^ if ($cols !== false) foreach ($cols as $token_array) $ret = array_merge($ret, $token_array);$/;" v +ret ChildDef/Table.php /^ if ($tfoot !== false) $ret = array_merge($ret, $tfoot);$/;" v +ret ChildDef/Table.php /^ if ($thead !== false) $ret = array_merge($ret, $thead);$/;" v +ret Config.php /^ $ret = HTMLPurifier_Config::createDefault();$/;" v +ret Config.php /^ $ret = new HTMLPurifier_Config($schema);$/;" v +ret Config.php /^ $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);$/;" v +ret Config.php /^ $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);$/;" v +ret Config.php /^ $ret = array();$/;" v +ret ConfigSchema/InterchangeBuilder.php /^ $ret = array();$/;" v +ret ContentSets.php /^ $ret = array();$/;" v +ret DefinitionCache/Decorator/Cleanup.php /^ $ret = parent::get($config);$/;" v +ret Encoder.php /^ $ret = '';$/;" v +ret Encoder.php /^ $ret = array();$/;" v +ret ErrorCollector.php /^ $ret = array();$/;" v +ret Filter/ExtractStyleBlocks.php /^ $ret = $def->validate($value, $config, $context);$/;" v +ret HTMLModule.php /^ $ret = array();$/;" v +ret HTMLModule/Tidy.php /^ $ret = array();$/;" v +ret Language.php /^ $ret = '';$/;" v +ret Lexer/DOMLex.php /^ $ret = '';$/;" v +ret PercentEncoder.php /^ $ret = '';$/;" v +ret PercentEncoder.php /^ $ret = array_shift($parts);$/;" v +ret Printer.php /^ $ret = '';$/;" v +ret Printer/CSSDefinition.php /^ $ret = '';$/;" v +ret Printer/ConfigForm.php /^ $ret = '';$/;" v +ret Printer/HTMLDefinition.php /^ $ret = '';$/;" v +ret StringHashParser.php /^ $ret = array();$/;" v +ret StringHashParser.php /^ $ret = $this->parseHandle($fh);$/;" v +ret StringHashParser.php /^ $ret = array();$/;" v +ret_function AttrDef/CSS/Filter.php /^ $ret_function = "$function($ret_parameters)";$/;" v +ret_lookup AttrDef/HTML/LinkTypes.php /^ $ret_lookup = array();$/;" v +ret_parameters AttrDef/CSS/Filter.php /^ $ret_parameters = implode(',', $ret_params);$/;" v +ret_params AttrDef/CSS/Filter.php /^ $ret_params = array();$/;" v +return ContentSets.php /^ $return = $module->getChildDef($def);$/;" v +return ContentSets.php /^ $return = false;$/;" v +rewind Injector.php /^ protected $rewind = false;$/;" v +rewind Injector.php /^ public function rewind($index) {$/;" f +rewind_to Strategy/MakeWellFormed.php /^ if ($rewind_to < 0) $rewind_to = 0;$/;" v +rewind_to Strategy/MakeWellFormed.php /^ $rewind_to = $this->injectors[$i]->getRewind();$/;" v +right AttrDef/CSS/Number.php /^ $right = rtrim($right, '0');$/;" v +right AttrDef/CSS/Number.php /^ if ($left === '' && $right === '') return false;$/;" v +rootElementPhase Lexer/PH5P.php /^ private function rootElementPhase($token) {$/;" f +round UnitConverter.php /^ private function round($n, $sigfigs) {$/;" f +row Printer.php /^ protected function row($name, $value) {$/;" f +rows Printer/ConfigForm.php /^ public $rows = 5;$/;" v +rows Printer/ConfigForm.php /^ public function setTextareaDimensions($cols = null, $rows = null) {$/;" v +rp UnitConverter.php /^ $rp = $sigfigs - $new_log - 1; \/\/ Number of decimal places needed$/;" v +rt HTMLModule/Ruby.php /^ $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));$/;" v +rtype Config.php /^ $rtype = is_int($def) ? $def : $def->type;$/;" v +s ConfigSchema/Validator.php /^ $s = $alias->toString();$/;" v +s HTMLModule/Legacy.php /^ $s = $this->addElement('s', 'Inline', 'Inline', 'Common');$/;" v +s_excludes Strategy/FixNesting.php /^ $s_excludes = $definition->info[$tokens[$i]->name]->excludes;$/;" v +s_excludes Strategy/FixNesting.php /^ $s_excludes = $definition->info_parent_def->excludes;$/;" v +s_part1 Lexer/PH5P.php /^ $s_part1 = array_slice($this->stack, 0, $fb_s_pos);$/;" v +s_part2 Lexer/PH5P.php /^ $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));$/;" v +s_pos Lexer/PH5P.php /^ $s_pos = array_search($node, $this->stack, true);$/;" v +safe HTMLModule.php /^ public $safe = true;$/;" v +safe HTMLModule/Forms.php /^ public $safe = false;$/;" v +safe HTMLModule/Object.php /^ public $safe = false;$/;" v +safe HTMLModule/Scripting.php /^ public $safe = false;$/;" v +same_quote Lexer/DirectLex.php /^ $same_quote = ($first_char == $last_char);$/;" v +save Lexer/PH5P.php /^ public function save() {$/;" f +scale UnitConverter.php /^ private function scale($r, $scale) {$/;" f +schema Config.php /^ $schema = HTMLPurifier_ConfigSchema::instance();$/;" v +schema Config.php /^ public static function create($config, $schema = null) {$/;" v +schema Config.php /^ public static function getAllowedDirectivesForForm($allowed, $schema = null) {$/;" v +schema ConfigSchema/Builder/ConfigSchema.php /^ $schema = new HTMLPurifier_ConfigSchema();$/;" v +scheme URIParser.php /^ $scheme = !empty($matches[1]) ? $matches[2] : null;$/;" v +scheme_obj AttrDef/URI.php /^ $scheme_obj = $uri->getSchemeObj($config, $context);$/;" v +scheme_obj URI.php /^ $scheme_obj = $registry->getScheme($def->defaultScheme, $config, $context);$/;" v +scheme_obj URI.php /^ $scheme_obj = $registry->getScheme($this->scheme, $config, $context);$/;" v +scheme_obj URIFilter/MakeAbsolute.php /^ $scheme_obj = $uri->getSchemeObj($config, $context);$/;" v +scheme_obj URIFilter/Munge.php /^ $scheme_obj = $uri->getSchemeObj($config, $context);$/;" v +schemes URISchemeRegistry.php /^ protected $schemes = array();$/;" v +scope Filter/ExtractStyleBlocks.php /^ $scope = $config->get('Filter.ExtractStyleBlocks.Scope');$/;" v +scopes Filter/ExtractStyleBlocks.php /^ $scopes = array();$/;" v +scopes Filter/ExtractStyleBlocks.php /^ $scopes = array_map('trim', explode(',', $scope));$/;" v +scoping Lexer/PH5P.php /^ private $scoping = array('button','caption','html','marquee','object','table','td','th');$/;" v +scriptCallback Lexer/DirectLex.php /^ protected function scriptCallback($matches) {$/;" f +sec_prefix Printer.php /^ protected function getClass($obj, $sec_prefix = '') {$/;" v +second AttrDef/URI/IPv6.php /^ $second = explode(':', $second);$/;" v +seen AttrCollections.php /^ $seen = array(); \/\/ recursion guard$/;" v +segment Lexer/DirectLex.php /^ $segment = substr($html, $cursor, $strlen_segment);$/;" v +segment Lexer/DirectLex.php /^ $segment = substr($segment, 0, $strlen_segment);$/;" v +segment Lexer/DirectLex.php /^ $segment = substr($html, $cursor, $strlen_segment);$/;" v +segment URIFilter/MakeAbsolute.php /^ $segment = array_pop($result);$/;" v +segment_nc_encoder URI.php /^ $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');$/;" v +segments_encoder URI.php /^ $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '\/');$/;" v +selector Filter/ExtractStyleBlocks.php /^ $selector = implode(', ', $new_selector); \/\/ now it's a string$/;" v +selector Filter/ExtractStyleBlocks.php /^ $selector = trim($selector);$/;" v +selector Filter/ExtractStyleBlocks.php /^ foreach ($decls as $selector => $style) {$/;" v +selectors Filter/ExtractStyleBlocks.php /^ $selectors = array_map('trim', explode(',', $selector));$/;" v +semicolon_pos URIScheme/ftp.php /^ $semicolon_pos = strrpos($uri->path, ';'); \/\/ reverse$/;" v +send ErrorCollector.php /^ public function send($severity, $msg) {$/;" f +sensitive AttrDef/Enum.php /^ $sensitive = false;$/;" v +sensitive AttrDef/Enum.php /^ $sensitive = true;$/;" v +sep Language.php /^ $sep = $this->getMessage('Item separator');$/;" v +sep_last Language.php /^ $sep_last = $this->getMessage('Item separator last');$/;" v +serials Config.php /^ protected $serials = array();$/;" v +set AttrTypes.php /^ public function set($type, $impl) {$/;" f +set Config.php /^ public function set($key, $value, $a = null) {$/;" f +set DefinitionCache.php /^ abstract public function set($def, $config);$/;" f +set DefinitionCache/Decorator.php /^ public function set($def, $config) {$/;" f +set DefinitionCache/Decorator/Cleanup.php /^ public function set($def, $config) {$/;" f +set DefinitionCache/Decorator/Memory.php /^ public function set($def, $config) {$/;" f +set DefinitionCache/Null.php /^ public function set($def, $config) {$/;" f +set DefinitionCache/Serializer.php /^ public function set($def, $config) {$/;" f +set PropertyList.php /^ public function set($name, $value) {$/;" f +setParent PropertyList.php /^ public function setParent($plist) {$/;" f +setTextareaDimensions Printer/ConfigForm.php /^ public function setTextareaDimensions($cols = null, $rows = null) {$/;" f +setup Definition.php /^ public $setup = false;$/;" v +setup Definition.php /^ public function setup($config) {$/;" f +setup DefinitionCacheFactory.php /^ public function setup() {$/;" f +setup EntityLookup.php /^ public function setup($file = false) {$/;" f +setup HTMLModule.php /^ public function setup($config) {}$/;" f +setup HTMLModule/Bdo.php /^ public function setup($config) {$/;" f +setup HTMLModule/Edit.php /^ public function setup($config) {$/;" f +setup HTMLModule/Forms.php /^ public function setup($config) {$/;" f +setup HTMLModule/Hypertext.php /^ public function setup($config) {$/;" f +setup HTMLModule/Image.php /^ public function setup($config) {$/;" f +setup HTMLModule/Legacy.php /^ public function setup($config) {$/;" f +setup HTMLModule/List.php /^ public function setup($config) {$/;" f +setup HTMLModule/Name.php /^ public function setup($config) {$/;" f +setup HTMLModule/Object.php /^ public function setup($config) {$/;" f +setup HTMLModule/Presentation.php /^ public function setup($config) {$/;" f +setup HTMLModule/Proprietary.php /^ public function setup($config) {$/;" f +setup HTMLModule/Ruby.php /^ public function setup($config) {$/;" f +setup HTMLModule/SafeEmbed.php /^ public function setup($config) {$/;" f +setup HTMLModule/SafeObject.php /^ public function setup($config) {$/;" f +setup HTMLModule/Scripting.php /^ public function setup($config) {$/;" f +setup HTMLModule/StyleAttribute.php /^ public function setup($config) {$/;" f +setup HTMLModule/Tables.php /^ public function setup($config) {$/;" f +setup HTMLModule/Target.php /^ public function setup($config) {$/;" f +setup HTMLModule/Text.php /^ public function setup($config) {$/;" f +setup HTMLModule/Tidy.php /^ public function setup($config) {$/;" f +setup HTMLModuleManager.php /^ public function setup($config) {$/;" f +setup LanguageFactory.php /^ public function setup() {$/;" f +setupConfigStuff CSSDefinition.php /^ protected function setupConfigStuff($config) {$/;" f +setupConfigStuff HTMLDefinition.php /^ protected function setupConfigStuff($config) {$/;" f +setupFilters URIDefinition.php /^ protected function setupFilters($config) {$/;" f +setupMemberVariables URIDefinition.php /^ protected function setupMemberVariables($config) {$/;" f +shift Encoder.php /^ $shift = ($mState - 1) * 6;$/;" v +should Encoder.php /^ * @note All functions in this class should be static.$/;" c +should Lexer.php /^ * This class should not be directly instantiated, but you may use create() to$/;" c +sigfigs UnitConverter.php /^ $sigfigs = strlen(ltrim($n, '0.')); \/\/ eliminate extra decimal character$/;" v +sigfigs UnitConverter.php /^ $sigfigs = strlen(rtrim($n, '0'));$/;" v +sigfigs UnitConverter.php /^ $sigfigs = $this->getSigFigs($n);$/;" v +sigfigs UnitConverter.php /^ if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision;$/;" v +sign AttrDef/CSS/Number.php /^ $sign = '-';$/;" v +sign AttrDef/CSS/Number.php /^ $sign = '';$/;" v +single StringHashParser.php /^ $single = false;$/;" v +single StringHashParser.php /^ $single = true;$/;" v +single StringHashParser.php /^ $single = false;$/;" v +size AttrDef/CSS/Font.php /^ for ($i = 0, $size = count($bits); $i < $size; $i++) {$/;" v +size Generator.php /^ for ($i = 0, $size = count($tokens); $i < $size; $i++) {$/;" v +size Lexer/DirectLex.php /^ $size = strlen($string); \/\/ size of the string (stays the same)$/;" v +size Strategy/FixNesting.php /^ $size = count($tokens);$/;" v +size Strategy/FixNesting.php /^ for ($i = 0, $size = count($tokens) ; $i < $size; ) {$/;" v +size Strategy/MakeWellFormed.php /^ $size = count($this->stack);$/;" v +size TagTransform/Font.php /^ $size = (int) $attr['size'];$/;" v +skey Config.php /^ $skey = "$ns.$directive";$/;" v +skipped_tags Strategy/MakeWellFormed.php /^ $skipped_tags = array_slice($this->stack, $j);$/;" v +skipped_tags Strategy/MakeWellFormed.php /^ $skipped_tags = false;$/;" v +small HTMLModule/Presentation.php /^ $small = $this->addElement('small', 'Inline', 'Inline', 'Common');$/;" v +so Generator.php /^ * @todo Refactor interface so that configuration\/context is determined$/;" i +special Lexer/PH5P.php /^ private $special = array('address','area','base','basefont','bgsound',$/;" v +specialEntityCallback EntityParser.php /^ protected function specialEntityCallback($matches) {$/;" f +special_cases HTMLModuleManager.php /^ $special_cases = $config->get('HTML.CoreModules');$/;" v +split AttrDef/HTML/Class.php /^ protected function split($string, $config, $context) {$/;" f +split AttrDef/HTML/Nmtokens.php /^ protected function split($string, $config, $context) {$/;" f +squash PropertyList.php /^ public function squash($force = false) {$/;" f +src AttrTransform/ImgRequired.php /^ $src = false;$/;" v +src AttrTransform/ImgRequired.php /^ $src = true;$/;" v +stack ErrorCollector.php /^ $stack = array_merge($stack, array_reverse($array, true));$/;" v +stack ErrorCollector.php /^ $stack = array($struct);$/;" v +stack Lexer/PH5P.php /^ public $stack = array();$/;" v +stack Strategy/FixNesting.php /^ $stack = array();$/;" v +stack Strategy/MakeWellFormed.php /^ $stack = array();$/;" v +stack URIFilter/MakeAbsolute.php /^ $stack = explode('\/', $uri->path);$/;" v +stack URIFilter/MakeAbsolute.php /^ $stack = $this->_collapseStack($stack); \/\/ do pre-parsing$/;" v +stack URIFilter/MakeAbsolute.php /^ $stack = explode('\/', $this->base->path);$/;" v +stack_length Lexer/PH5P.php /^ $stack_length = count($this->stack) - 1;$/;" v +stage AttrDef/CSS/Font.php /^ $stage = 2;$/;" v +stage AttrDef/CSS/Font.php /^ if (count($caught) >= 3) $stage = 1;$/;" v +stage AttrDef/CSS/Font.php /^ $stage = 0; \/\/ this indicates what we're looking for$/;" v +stage_1 AttrDef/CSS/Font.php /^ $stage_1 = array('font-style', 'font-variant', 'font-weight');$/;" v +standalone ElementDef.php /^ public $standalone = true;$/;" v +start Lexer/PH5P.php /^ $start = $this->char;$/;" v +start Printer.php /^ protected function start($tag, $attr = array()) {$/;" f +start_token Strategy/FixNesting.php /^ $start_token = $tokens[$i]; \/\/ to make token available via CurrentToken$/;" v +start_token Strategy/FixNesting.php /^ $start_token = false;$/;" v +state StringHashParser.php /^ $state = $this->default;$/;" v +state StringHashParser.php /^ $state = false;$/;" v +state StringHashParser.php /^ $state = trim($line, '- ');$/;" v +state StringHashParser.php /^ $state = false;$/;" v +state UnitConverter.php /^ $state = $dest_state;$/;" v +state UnitConverter.php /^ if (isset($x[$unit])) $state = $k;$/;" v +state UnitConverter.php /^ $state = $dest_state = false;$/;" v +status AttrDef/CSS/BackgroundPosition.php /^ $status = $lookup[$lbit];$/;" v +status DefinitionCache/Decorator/Cleanup.php /^ $status = parent::add($def, $config);$/;" v +status DefinitionCache/Decorator/Cleanup.php /^ $status = parent::replace($def, $config);$/;" v +status DefinitionCache/Decorator/Cleanup.php /^ $status = parent::set($def, $config);$/;" v +status DefinitionCache/Decorator/Memory.php /^ $status = parent::add($def, $config);$/;" v +status DefinitionCache/Decorator/Memory.php /^ $status = parent::replace($def, $config);$/;" v +status DefinitionCache/Decorator/Memory.php /^ $status = parent::set($def, $config);$/;" v +step_seven Lexer/PH5P.php /^ $step_seven = false;$/;" v +step_seven Lexer/PH5P.php /^ $step_seven = true;$/;" v +step_seven Lexer/PH5P.php /^ if(isset($step_seven) && $step_seven === true) {$/;" v +stop Lexer/PH5P.php /^ $stop = false;$/;" v +str Encoder.php /^ $str = strtr($str, $clear_fix);$/;" v +str Encoder.php /^ $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);$/;" v +str Encoder.php /^ $str = iconv($encoding, 'utf-8\/\/IGNORE', $str);$/;" v +str Encoder.php /^ $str = iconv('utf-8', $encoding . '\/\/IGNORE', $str);$/;" v +str Encoder.php /^ $str = strtr($str, HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding));$/;" v +str Encoder.php /^ $str = strtr($str, array_flip($ascii_fix));$/;" v +str Encoder.php /^ $str = utf8_decode($str);$/;" v +str Encoder.php /^ $str = utf8_encode($str);$/;" v +strategies Strategy/Composite.php /^ protected $strategies = array();$/;" v +strike HTMLModule/Legacy.php /^ $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common');$/;" v +string AttrDef.php /^ $string = str_replace(array("\\n", "\\t", "\\r"), ' ', $string);$/;" v +string AttrDef.php /^ $string = trim($string);$/;" v +string AttrDef/CSS/Background.php /^ $string = $this->mungeRgb($string);$/;" v +string AttrDef/CSS/Background.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/BackgroundPosition.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/Border.php /^ $string = $this->mungeRgb($string);$/;" v +string AttrDef/CSS/Border.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/Font.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/ImportantDecorator.php /^ $string = rtrim(substr($temp, 0, -1));$/;" v +string AttrDef/CSS/ImportantDecorator.php /^ $string = $this->def->validate($string, $config, $context);$/;" v +string AttrDef/CSS/ImportantDecorator.php /^ $string = trim($string);$/;" v +string AttrDef/CSS/Length.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/ListStyle.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/Multiple.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/Percentage.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/CSS/TextDecoration.php /^ $string = strtolower($this->parseCDATA($string));$/;" v +string AttrDef/Enum.php /^ $string = ctype_lower($string) ? $string : strtolower($string);$/;" v +string AttrDef/Enum.php /^ $string = substr($string, 2);$/;" v +string AttrDef/Enum.php /^ $string = trim($string);$/;" v +string AttrDef/HTML/Color.php /^ $string = trim($string);$/;" v +string AttrDef/HTML/Length.php /^ $string = trim($string);$/;" v +string AttrDef/HTML/LinkTypes.php /^ $string = $this->parseCDATA($string);$/;" v +string AttrDef/HTML/LinkTypes.php /^ $string = implode(' ', array_keys($ret_lookup));$/;" v +string AttrDef/HTML/MultiLength.php /^ $string = trim($string);$/;" v +string AttrDef/HTML/Nmtokens.php /^ $string = trim($string);$/;" v +string AttrDef/HTML/Pixels.php /^ $string = substr($string, 0, $length - 2);$/;" v +string AttrDef/HTML/Pixels.php /^ $string = trim($string);$/;" v +string AttrDef/Lang.php /^ $string = trim($string);$/;" v +string AttrDef/URI/Email/SimpleCheck.php /^ $string = trim($string);$/;" v +string AttrTypes.php /^ else $string = '';$/;" v +string ErrorCollector.php /^ $string = '';$/;" v +string Lexer.php /^ $string = $this->_entity_parser->substituteSpecialEntities($string);$/;" v +string Lexer.php /^ $string = strtr($string, $this->_special_entity2str);$/;" v +string Lexer/DirectLex.php /^ $string = (string) $string; \/\/ quick typecast$/;" v +string Lexer/PEARSax3.php /^ $string = $this->normalize($string, $config, $context);$/;" v +string Printer.php /^ $string = HTMLPurifier_Encoder::cleanUTF8($string);$/;" v +string Printer.php /^ $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');$/;" v +string URIFilter/Munge.php /^ $string = $uri->toString();$/;" v +stringTypes VarParser.php /^ static public $stringTypes = array($/;" v +stripped_token Language.php /^ $stripped_token = clone $value;$/;" v +strlen_segment Lexer/DirectLex.php /^ $strlen_segment = $position_comment_end - $cursor;$/;" v +strlen_segment Lexer/DirectLex.php /^ $strlen_segment = $position_next_gt - $cursor;$/;" v +strong HTMLModule/Text.php /^ $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common');$/;" v +struct ErrorCollector.php /^ $struct = $this->lines[$line][$col] = $new_struct;$/;" v +struct ErrorCollector.php /^ $struct = $this->lines[$line][$col];$/;" v +struct ErrorCollector.php /^ $struct = $this->lines[-1] = $new_struct;$/;" v +struct ErrorCollector.php /^ $struct = $this->lines[-1];$/;" v +struct ErrorCollector.php /^ $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);$/;" v +struct ErrorCollector.php /^ $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);$/;" v +struct ErrorCollector.php /^ $struct = null;$/;" v +style AttrTransform/ImgSpace.php /^ $style = '';$/;" v +style Filter/ExtractStyleBlocks.php /^ $style = $this->cleanCSS($style, $config, $context);$/;" v +style TagTransform/Simple.php /^ public function __construct($transform_to, $style = null) {$/;" v +styleCallback Filter/ExtractStyleBlocks.php /^ protected function styleCallback($matches) {$/;" f +style_blocks Filter/ExtractStyleBlocks.php /^ $style_blocks = $this->_styleMatches;$/;" v +subst ErrorCollector.php /^ $subst = array();$/;" v +subst Language.php /^ $subst = array();$/;" v +substituteNonSpecialEntities EntityParser.php /^ public function substituteNonSpecialEntities($string) {$/;" f +substituteSpecialEntities EntityParser.php /^ public function substituteSpecialEntities($string) {$/;" f +substrCount Lexer/DirectLex.php /^ protected function substrCount($haystack, $needle, $offset, $length) {$/;" f +subtags AttrDef/Lang.php /^ $subtags = explode('-', $string);$/;" v +support CSSDefinition.php /^ $support = "(for information on implementing this, see the ".$/;" v +support HTMLDefinition.php /^ $support = "(for information on implementing this, see the ".$/;" v +swap Strategy/MakeWellFormed.php /^ private function swap($token) {$/;" f +synchronize_interval Lexer/DirectLex.php /^ $loops % $synchronize_interval === 0 \/\/ time to synchronize!$/;" v +synchronize_interval Lexer/DirectLex.php /^ $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval');$/;" v +system_fonts AttrDef/CSS/Font.php /^ static $system_fonts = array($/;" v +t AttrTransform/Input.php /^ else $t = strtolower($attr['type']);$/;" v +t AttrTransform/Input.php /^ if (!isset($attr['type'])) $t = 'text';$/;" v +t AttrTransform/Input.php /^ if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {$/;" v +t Strategy/MakeWellFormed.php /^ $t = 0;$/;" v +t Strategy/MakeWellFormed.php /^ $t == 0 || isset($tokens[$t - 1]);$/;" v +t Strategy/MakeWellFormed.php /^ $t = false; \/\/ token index$/;" v +table HTMLModule/Legacy.php /^ $table = $this->addBlankElement('table');$/;" v +table Lexer/PH5P.php /^ $table = $this->stack[$n];$/;" v +table Lexer/PH5P.php /^ $table = $this->stack[$n];$/;" v +table Lexer/PH5P.php /^ private function elementInScope($el, $table = false) {$/;" v +tag HTMLDefinition.php /^ foreach ($this->info as $tag => $info) {$/;" v +tag HTMLDefinition.php /^ foreach ($this->info as $tag => $info) {$/;" v +tagNameState Lexer/PH5P.php /^ private function tagNameState() {$/;" f +tagOpenState Lexer/PH5P.php /^ private function tagOpenState() {$/;" f +tag_index ChildDef/Table.php /^ $tag_index = 0;$/;" v +tag_index ChildDef/Table.php /^ $tag_index = 0; \/\/ the first node might be whitespace,$/;" v +tag_name Lexer/DOMLex.php /^ $tag_name = $node->tagName, \/\/ somehow, it get's dropped$/;" v +td HTMLModule/Legacy.php /^ $td = $this->addBlankElement('td');$/;" v +temp AttrDef/CSS/ImportantDecorator.php /^ $temp = rtrim(substr($string, 0, -9));$/;" v +temp ContentSets.php /^ $temp = $this->convertToLookup($value);$/;" v +testEncodingSupportsASCII Encoder.php /^ public static function testEncodingSupportsASCII($encoding, $bypass = false) {$/;" f +tests Encoder.php /^ * This expensive function tests whether or not a given character$/;" f +text Injector/AutoParagraph.php /^ $text = $token->data;$/;" v +text Lexer/PH5P.php /^ $text = $this->dom->createTextNode($token['data']);$/;" v +text Lexer/PH5P.php /^ $text = $this->dom->createTextNode($data);$/;" v +text PercentEncoder.php /^ $text = substr($part, 2);$/;" v +text Printer.php /^ protected function text($text) {$/;" f +textarea HTMLModule/Forms.php /^ $textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array($/;" v +textify_comments Strategy/RemoveForeignElements.php /^ $textify_comments = $token->name;$/;" v +textify_comments Strategy/RemoveForeignElements.php /^ $textify_comments = false;$/;" v +textify_comments Strategy/RemoveForeignElements.php /^ $textify_comments = false;$/;" v +tfoot ChildDef/Table.php /^ $tfoot = false;$/;" v +th HTMLModule/Legacy.php /^ $th = $this->addBlankElement('th');$/;" v +that Bootstrap.php /^ * Bootstrap class that contains meta-functionality for HTML Purifier such as$/;" c +that ConfigSchema/InterchangeBuilder.php /^ * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id$/;" f +that Definition.php /^ * Setup function that aborts if already setup$/;" f +that DefinitionCache/Decorator/Cleanup.php /^ * Definition cache decorator class that cleans up the cache$/;" c +that DefinitionCache/Decorator/Memory.php /^ * Definition cache decorator class that saves all cache retrievals$/;" c +that Encoder.php /^ * function that needs to be able to understand UTF-8 characters.$/;" f +that ErrorCollector.php /^ * Error collection class that enables HTML Purifier to report HTML$/;" c +that HTMLModule.php /^ * Convenience function that creates a totally blank, non-standalone$/;" f +that HTMLModule.php /^ * Convenience function that generates a lookup table with boolean$/;" f +that HTMLModule.php /^ * Convenience function that merges a list of attribute includes into$/;" f +that HTMLModule.php /^ * Convenience function that registers an element to a content set$/;" f +that HTMLModule.php /^ * Convenience function that sets up a new element$/;" f +that HTMLModule.php /^ * Convenience function that transforms single-string contents$/;" f +that Lexer/DOMLex.php /^ * Callback function that entity-izes ampersands in comments so that$/;" f +that Lexer/DOMLex.php /^ * Recursive function that tokenizes a node, putting it into an accumulator.$/;" f +that Printer.php /^ * Main function that renders object or aspect of that object$/;" f +that Token.php /^ * Abstract base token class that all others inherit from.$/;" c +the_same Lexer/PH5P.php /^ $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName;$/;" v +thead ChildDef/Table.php /^ $thead = false;$/;" v +tidy Filter/ExtractStyleBlocks.php /^ $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');$/;" v +tidy Generator.php /^ $tidy = new Tidy;$/;" v +tidyModules Doctype.php /^ $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null$/;" v +tidyModules Doctype.php /^ public $tidyModules = array();$/;" v +tidy_modules DoctypeRegistry.php /^ $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null$/;" v +tidy_modules DoctypeRegistry.php /^ if (!is_array($tidy_modules)) $tidy_modules = array($tidy_modules);$/;" v +tmp Encoder.php /^ $tmp = $in;$/;" v +tmp Encoder.php /^ $tmp = ($tmp & 0x0000003F) << $shift;$/;" v +to AttrDef/URI/IPv4.php /^ * Lazy load function to prevent regex from being stuffed in$/;" f +to ConfigSchema/Interchange.php /^ * Convenience function to perform standard validation. Throws exception$/;" f +to ContentSets.php /^ 'Could not determine which ChildDef class to instantiate',$/;" c +to DoctypeRegistry.php /^ * @note Use this function to get a copy of doctype that config$/;" f +to Lexer.php /^ * many of the utility functions require a class to be instantiated.$/;" c +to Lexer/DOMLex.php /^ * our own function to do that.$/;" f +toString ConfigSchema/Interchange/Id.php /^ public function toString() {$/;" f +toString Length.php /^ public function toString() {$/;" f +toString URI.php /^ public function toString() {$/;" f +toggleWriteability Printer/ConfigForm.js /^function toggleWriteability(id_of_patient, checked) {$/;" f +token AttrDef/CSS/DenyElementDecorator.php /^ $token = $context->get('CurrentToken', true);$/;" v +token AttrDef/Switch.php /^ $token = $context->get('CurrentToken', true);$/;" v +token ChildDef/StrictBlockquote.php /^ $token = $result[$i];$/;" v +token ErrorCollector.php /^ $token = $this->context->get('CurrentToken', true);$/;" v +token Injector/AutoParagraph.php /^ $token = array($this->_pStart(), $token);$/;" v +token Injector/AutoParagraph.php /^ if (!is_array($token)) $token = array($token);$/;" v +token Injector/AutoParagraph.php /^ $token = array($this->_pStart());$/;" v +token Injector/AutoParagraph.php /^ $token = array($this->_pStart(), $token);$/;" v +token Injector/AutoParagraph.php /^ $token = array();$/;" v +token Injector/DisplayLinkURI.php /^ $token = array($token, new HTMLPurifier_Token_Text(" ($url)"));$/;" v +token Injector/Linkify.php /^ $token = array();$/;" v +token Injector/PurifierLinkify.php /^ $token = array();$/;" v +token Injector/RemoveEmpty.php /^ $token = $i - $this->inputIndex + 1;$/;" v +token Injector/SafeObject.php /^ $token = false;$/;" v +token Injector/SafeObject.php /^ $token = false;$/;" v +token Injector/SafeObject.php /^ $token = $new;$/;" v +token Lexer/DirectLex.php /^ $token = new HTMLPurifier_Token_Empty($segment);$/;" v +token Lexer/DirectLex.php /^ $token = new HTMLPurifier_Token_Start($segment);$/;" v +token Lexer/DirectLex.php /^ $token = new HTMLPurifier_Token_Empty($type, $attr);$/;" v +token Lexer/DirectLex.php /^ $token = new HTMLPurifier_Token_End($type);$/;" v +token Lexer/DirectLex.php /^ $token = new HTMLPurifier_Token_Start($type, $attr);$/;" v +token Lexer/DirectLex.php /^ $token = new HTMLPurifier_Token_Text('<');$/;" v +token Lexer/DirectLex.php /^ $token = new$/;" v +token Lexer/DirectLex.php /^ $token = new$/;" v +token Strategy/MakeWellFormed.php /^ $token = new HTMLPurifier_Token_Empty($token->name, $token->attr);$/;" v +token Strategy/MakeWellFormed.php /^ $token = $tokens[$t];$/;" v +token Strategy/MakeWellFormed.php /^ $token = false; \/\/ the current token$/;" v +token Strategy/MakeWellFormed.php /^ if ($token === false) $token = array(1);$/;" v +token Strategy/MakeWellFormed.php /^ if (is_int($token)) $token = array($token);$/;" v +token Strategy/MakeWellFormed.php /^ if (is_object($token)) $token = array(1, $token);$/;" v +token Strategy/RemoveForeignElements.php /^ $token = $definition->$/;" v +token Strategy/RemoveForeignElements.php /^ $token = new HTMLPurifier_Token_Text($/;" v +token Strategy/RemoveForeignElements.php /^ $token = new HTMLPurifier_Token_Text($data);$/;" v +token Strategy/RemoveForeignElements.php /^ $token = false;$/;" v +token Strategy/ValidateAttributes.php /^ $token = false;$/;" v +token URIFilter/Munge.php /^ $token = $context->get('CurrentToken', true);$/;" v +tokenizeDOM Lexer/DOMLex.php /^ protected function tokenizeDOM($node, &$tokens, $collect = false) {$/;" f +tokenizeHTML Lexer.php /^ public function tokenizeHTML($string, $config, $context) {$/;" f +tokenizeHTML Lexer/DOMLex.php /^ public function tokenizeHTML($html, $config, $context) {$/;" f +tokenizeHTML Lexer/DirectLex.php /^ public function tokenizeHTML($html, $config, $context) {$/;" f +tokenizeHTML Lexer/PEARSax3.php /^ public function tokenizeHTML($string, $config, $context) {$/;" f +tokenizeHTML Lexer/PH5P.php /^ public function tokenizeHTML($html, $config, $context) {$/;" f +tokens AttrDef/HTML/Nmtokens.php /^ $tokens = $this->filter($tokens, $config, $context);$/;" v +tokens AttrDef/HTML/Nmtokens.php /^ $tokens = $this->split($string, $config, $context);$/;" v +tokens Lexer/DOMLex.php /^ $tokens = array();$/;" v +tokens Lexer/PEARSax3.php /^ protected $tokens = array();$/;" v +tokens Lexer/PH5P.php /^ $tokens = array();$/;" v +tokens Strategy/Composite.php /^ $tokens = $strategy->execute($tokens, $config, $context);$/;" v +too ElementDef.php /^ * Please update that class too.$/;" c +top_nesting Strategy/MakeWellFormed.php /^ $top_nesting = array_pop($this->stack);$/;" v +toplabel AttrDef/URI/Host.php /^ $toplabel = "$a($and*$an)?";$/;" v +tr HTMLModule/Legacy.php /^ $tr = $this->addBlankElement('tr');$/;" v +tracksLineNumbers Lexer.php /^ public $tracksLineNumbers = false;$/;" v +tracksLineNumbers Lexer/DirectLex.php /^ public $tracksLineNumbers = true;$/;" v +trailingEndPhase Lexer/PH5P.php /^ private function trailingEndPhase($token) {$/;" f +transform AttrTransform.php /^ abstract public function transform($attr, $config, $context);$/;" f +transform AttrTransform/Background.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/BdoDir.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/BgColor.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/BoolToCSS.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/Border.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/EnumToCSS.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/ImgRequired.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/ImgSpace.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/Input.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/Lang.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/Length.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/Name.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/NameSync.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/SafeEmbed.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/SafeObject.php /^ function transform($attr, $config, $context) {$/;" f +transform AttrTransform/SafeParam.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/ScriptRequired.php /^ public function transform($attr, $config, $context) {$/;" f +transform AttrTransform/Textarea.php /^ public function transform($attr, $config, $context) {$/;" f +transform TagTransform.php /^ abstract public function transform($tag, $config, $context);$/;" f +transform TagTransform/Font.php /^ public function transform($tag, $config, $context) {$/;" f +transform TagTransform/Simple.php /^ public function transform($tag, $config, $context) {$/;" f +transformAttrToAssoc Lexer/DOMLex.php /^ protected function transformAttrToAssoc($node_map) {$/;" f +transform_to TagTransform/Font.php /^ public $transform_to = 'span';$/;" v +transitional HTMLModuleManager.php /^ $transitional = array('Legacy', 'Target');$/;" v +triad AttrDef/CSS/Color.php /^ $triad = substr($color, 4, $length - 4 - 1);$/;" v +triggerError Config.php /^ protected function triggerError($msg, $no) {$/;" f +trim AttrDef/HTML/ID.php /^ $trim = trim( \/\/ primitive style of regexps, I suppose$/;" v +trusted HTMLModuleManager.php /^ if ($trusted === null) $trusted = $this->trusted;$/;" v +trusted HTMLModuleManager.php /^ public $trusted = false;$/;" v +trusted HTMLModuleManager.php /^ public function getElement($name, $trusted = null) {$/;" v +trusted Strategy/RemoveForeignElements.php /^ $trusted = $config->get('HTML.Trusted');$/;" v +trusted_wh CSSDefinition.php /^ $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array($/;" v +tt HTMLModule/Presentation.php /^ $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common');$/;" v +type AttrDef/CSS/Color.php /^ $type = 'integer';$/;" v +type AttrDef/CSS/Color.php /^ $type = 'percentage';$/;" v +type AttrDef/CSS/Color.php /^ $type = false; \/\/ to ensure that they're all the same type$/;" v +type CSSDefinition.php /^ public $type = 'CSS';$/;" v +type ChildDef/Chameleon.php /^ public $type = 'chameleon';$/;" v +type ChildDef/Custom.php /^ public $type = 'custom';$/;" v +type ChildDef/Empty.php /^ public $type = 'empty';$/;" v +type ChildDef/Optional.php /^ public $type = 'optional';$/;" v +type ChildDef/Required.php /^ public $type = 'required';$/;" v +type ChildDef/StrictBlockquote.php /^ public $type = 'strictblockquote';$/;" v +type ChildDef/Table.php /^ public $type = 'table';$/;" v +type Config.php /^ $type = $rtype;$/;" v +type Config.php /^ $type = -$rtype;$/;" v +type ConfigSchema/InterchangeBuilder.php /^ $type = explode('\/', $hash->offsetGet('TYPE'));$/;" v +type ErrorCollector.php /^ foreach ($current->children as $type => $array) {$/;" v +type HTMLDefinition.php /^ public $type = 'HTML';$/;" v +type HTMLModule/Tidy.php /^ $type = "info_$type";$/;" v +type HTMLModule/Tidy.php /^ $type = 'attr_transform_' . $property;$/;" v +type Lexer/DirectLex.php /^ $type = substr($segment, 1);$/;" v +type Lexer/DirectLex.php /^ $type = substr($segment, 0, $position_first_space);$/;" v +type Printer/ConfigForm.php /^ $type = $def->type;$/;" v +type Printer/ConfigForm.php /^ $type = abs($def);$/;" v +type Printer/ConfigForm.php /^ if (!isset($this->fields[$type])) $type = 0; \/\/ default$/;" v +type Printer/ConfigForm.php /^ $type = $def->type;$/;" v +type Printer/ConfigForm.php /^ $type = abs($def);$/;" v +type Printer/ConfigForm.php /^ $type === HTMLPurifier_VarParser::ALIST ||$/;" v +type Printer/ConfigForm.php /^ $type === HTMLPurifier_VarParser::HASH ||$/;" v +type Printer/ConfigForm.php /^ $type === HTMLPurifier_VarParser::ITEXT ||$/;" v +type Printer/ConfigForm.php /^ $type === HTMLPurifier_VarParser::LOOKUP$/;" v +type Printer/ConfigForm.php /^ $type === HTMLPurifier_VarParser::TEXT ||$/;" v +type Strategy/MakeWellFormed.php /^ $type = $definition->info[$token->name]->child->type;$/;" v +type Strategy/MakeWellFormed.php /^ $type = false; \/\/ Type is unknown, treat accordingly$/;" v +type URIDefinition.php /^ public $type = 'URI';$/;" v +type URIScheme/ftp.php /^ $type = substr($uri->path, $semicolon_pos + 1); \/\/ no semicolon$/;" v +type VarParser.php /^ $type = HTMLPurifier_VarParser::$types[$type];$/;" v +type VarParser.php /^ if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var);$/;" v +typeAllowsNull ConfigSchema/Interchange/Directive.php /^ public $typeAllowsNull = false;$/;" v +type_obj Printer/ConfigForm.php /^ $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);$/;" v +type_obj Printer/ConfigForm.php /^ $type_obj = $this->fields[$type];$/;" v +type_ret URIScheme/ftp.php /^ $type_ret = ";type=$typecode";$/;" v +type_ret URIScheme/ftp.php /^ $type_ret = '';$/;" v +typecode URIScheme/ftp.php /^ } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') {$/;" v +types VarParser.php /^ static public $types = array($/;" v +u HTMLModule/Legacy.php /^ $u = $this->addElement('u', 'Inline', 'Inline', 'Common');$/;" v +u Length.php /^ public function __construct($n = '0', $u = false) {$/;" v +ul HTMLModule/Legacy.php /^ $ul = $this->addBlankElement('ul');$/;" v +ul_types HTMLModule/Tidy/XHTMLAndHTML4.php /^ $ul_types = array($/;" v +unichr Encoder.php /^ public static function unichr($code) {$/;" f +unit Length.php /^ $unit = substr($s, $n_length);$/;" v +unit Length.php /^ if ($unit === '') $unit = false;$/;" v +unit UnitConverter.php /^ $unit = $dest_unit;$/;" v +unit UnitConverter.php /^ $unit = $to_unit;$/;" v +unit UnitConverter.php /^ $unit = self::$units[$state][$dest_state][2];$/;" v +unit UnitConverter.php /^ $unit = $length->getUnit();$/;" v +unit UnitConverter.php /^ \/\/ Post-condition: $unit == $to_unit$/;" v +unit UnitConverter.php /^ if ($n === '0' || $unit === false) {$/;" v +units UnitConverter.php /^ protected static $units = array($/;" v +unpack AttrDef/URI/Email.php /^ function unpack($string) {$/;" f +uri AttrDef/CSS/URI.php /^ $uri = substr($uri, 1, $new_length - 1);$/;" v +uri AttrDef/CSS/URI.php /^ $uri = str_replace($values, $keys, $uri);$/;" v +uri AttrDef/CSS/URI.php /^ $uri = trim(substr($uri_string, 0, $new_length));$/;" v +uri AttrDef/URI.php /^ $uri = $this->parseCDATA($uri);$/;" v +uri AttrDef/URI.php /^ $uri = $this->parser->parse($uri);$/;" v +uri URIFilter/MakeAbsolute.php /^ $uri = clone $this->base;$/;" v +uri URIFilter/Munge.php /^ $uri = $new_uri; \/\/ overwrite$/;" v +uri URIParser.php /^ $uri = $this->percentEncoder->normalize($uri);$/;" v +uri_def AttrDef/URI.php /^ $uri_def = $config->getDefinition('URI');$/;" v +uri_or_none CSSDefinition.php /^ $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite($/;" v +uri_string AttrDef/CSS/URI.php /^ $uri_string = $this->parseCDATA($uri_string);$/;" v +uri_string AttrDef/CSS/URI.php /^ $uri_string = substr($uri_string, 4);$/;" v +url Filter/YouTube.php /^ $url = $this->armorUrl($matches[1]);$/;" v +url Injector/DisplayLinkURI.php /^ $url = $token->start->attr['href'];$/;" v +url Printer/ConfigForm.php /^ $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);$/;" v +userModules HTMLModuleManager.php /^ public $userModules = array();$/;" v +userinfo URIParser.php /^ $userinfo = !empty($matches[1]) ? $matches[2] : null;$/;" v +uses AttrDef/Enum.php /^ * @warning The case-insensitive compare of this function uses PHP's$/;" f +utf8 Encoder.php /^ foreach ($ascii_fix as $utf8 => $native) $clear_fix[$utf8] = '';$/;" v +val ConfigSchema/Validator.php /^ foreach ($d->allowed as $val => $x) {$/;" v +val Printer/ConfigForm.php /^ foreach ($array as $val => $b) {$/;" v +val Printer/ConfigForm.php /^ foreach ($def->allowed as $val => $b) {$/;" v +valid AttrDef/URI/Host.php /^ $valid = $this->ipv6->validate($ip, $config, $context);$/;" v +valid_values AttrDef/Enum.php /^ $valid_values = array(), $case_sensitive = false$/;" v +valid_values AttrDef/Enum.php /^ public $valid_values = array();$/;" v +valid_values AttrDef/HTML/FrameTarget.php /^ public $valid_values = false; \/\/ uninitialized value$/;" v +validate AttrDef.php /^ abstract public function validate($string, $config, $context);$/;" f +validate AttrDef/CSS.php /^ public function validate($css, $config, $context) {$/;" f +validate AttrDef/CSS/AlphaValue.php /^ public function validate($number, $config, $context) {$/;" f +validate AttrDef/CSS/Background.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/BackgroundPosition.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/Border.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/Color.php /^ public function validate($color, $config, $context) {$/;" f +validate AttrDef/CSS/Composite.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/DenyElementDecorator.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/Filter.php /^ public function validate($value, $config, $context) {$/;" f +validate AttrDef/CSS/Font.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/FontFamily.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/ImportantDecorator.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/Length.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/ListStyle.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/Multiple.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/Number.php /^ public function validate($number, $config, $context) {$/;" f +validate AttrDef/CSS/Percentage.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/TextDecoration.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/CSS/URI.php /^ public function validate($uri_string, $config, $context) {$/;" f +validate AttrDef/Enum.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/Bool.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/Color.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/FrameTarget.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/ID.php /^ public function validate($id, $config, $context) {$/;" f +validate AttrDef/HTML/Length.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/LinkTypes.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/MultiLength.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/Nmtokens.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/HTML/Pixels.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/Integer.php /^ public function validate($integer, $config, $context) {$/;" f +validate AttrDef/Lang.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/Switch.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/Text.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/URI.php /^ public function validate($uri, $config, $context) {$/;" f +validate AttrDef/URI/Email/SimpleCheck.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/URI/Host.php /^ public function validate($string, $config, $context) {$/;" f +validate AttrDef/URI/IPv4.php /^ public function validate($aIP, $config, $context) {$/;" f +validate AttrDef/URI/IPv6.php /^ public function validate($aIP, $config, $context) {$/;" f +validate ConfigSchema/Interchange.php /^ public function validate() {$/;" f +validate ConfigSchema/Validator.php /^ public function validate($interchange) {$/;" f +validate Length.php /^ protected function validate() {$/;" f +validate URI.php /^ public function validate($config, $context) {$/;" f +validate URIScheme.php /^ public function validate(&$uri, $config, $context) {$/;" f +validate URIScheme/ftp.php /^ public function validate(&$uri, $config, $context) {$/;" f +validate URIScheme/http.php /^ public function validate(&$uri, $config, $context) {$/;" f +validate URIScheme/mailto.php /^ public function validate(&$uri, $config, $context) {$/;" f +validate URIScheme/news.php /^ public function validate(&$uri, $config, $context) {$/;" f +validate URIScheme/nntp.php /^ public function validate(&$uri, $config, $context) {$/;" f +validateChildren ChildDef.php /^ abstract public function validateChildren($tokens_of_children, $config, $context);$/;" f +validateChildren ChildDef/Chameleon.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateChildren ChildDef/Custom.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateChildren ChildDef/Empty.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateChildren ChildDef/Optional.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateChildren ChildDef/Required.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateChildren ChildDef/StrictBlockquote.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateChildren ChildDef/Table.php /^ public function validateChildren($tokens_of_children, $config, $context) {$/;" f +validateDirective ConfigSchema/Validator.php /^ public function validateDirective($d) {$/;" f +validateDirectiveAliases ConfigSchema/Validator.php /^ public function validateDirectiveAliases($d) {$/;" f +validateDirectiveAllowed ConfigSchema/Validator.php /^ public function validateDirectiveAllowed($d) {$/;" f +validateDirectiveValueAliases ConfigSchema/Validator.php /^ public function validateDirectiveValueAliases($d) {$/;" f +validateId ConfigSchema/Validator.php /^ public function validateId($id) {$/;" f +validateToken AttrValidator.php /^ public function validateToken(&$token, &$config, $context) {$/;" f +validator ConfigSchema/Interchange.php /^ $validator = new HTMLPurifier_ConfigSchema_Validator();$/;" v +validator Strategy/ValidateAttributes.php /^ $validator = new HTMLPurifier_AttrValidator();$/;" v +value AttrDef/CSS.php /^ $value = trim($value);$/;" v +value AttrDef/CSS/Filter.php /^ $value = $this->intValidator->validate($value, $config, $context);$/;" v +value AttrDef/CSS/Filter.php /^ $value = trim($value);$/;" v +value AttrDef/CSS/Filter.php /^ if ($int < 0) $value = '0';$/;" v +value AttrDef/CSS/Filter.php /^ if ($int > 100) $value = '100';$/;" v +value AttrDef/CSS/Filter.php /^ $value = $this->parseCDATA($value);$/;" v +value AttrTransform.php /^ $value = $attr[$key];$/;" v +value AttrTransform/EnumToCSS.php /^ $value = trim($attr[$this->attr]);$/;" v +value AttrTransform/EnumToCSS.php /^ if (!$this->caseSensitive) $value = strtolower($value);$/;" v +value Config.php /^ $value = $def->aliases[$value];$/;" v +value Config.php /^ $value = $a;$/;" v +value Config.php /^ $value = $mq ? stripslashes($array[$skey]) : $array[$skey];$/;" v +value Config.php /^ $value = $this->parser->parse($value, $type, $allow_null);$/;" v +value ConfigSchema/Builder/Xml.php /^ foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value);$/;" v +value ContentSets.php /^ $value = $def->content_model;$/;" v +value HTMLModule/Edit.php /^ $value = explode('!', $def->content_model);$/;" v +value Lexer/DirectLex.php /^ $value = $quoted_value;$/;" v +value Lexer/DirectLex.php /^ $value = substr($quoted_value, 1);$/;" v +value Lexer/DirectLex.php /^ $value = substr($quoted_value, 1, strlen($quoted_value) - 2);$/;" v +value Lexer/DirectLex.php /^ $value = substr($string, $value_begin, $value_end - $value_begin);$/;" v +value Lexer/DirectLex.php /^ if ($value === false) $value = '';$/;" v +value Lexer/DirectLex.php /^ if ($value === false) $value = '';$/;" v +value Printer.php /^ foreach ($obj->valid_values as $value => $bool) {$/;" v +value Printer.php /^ if (is_bool($value)) $value = $value ? 'On' : 'Off';$/;" v +value Printer/ConfigForm.php /^ $value = $nvalue;$/;" v +value Printer/ConfigForm.php /^ $value = '';$/;" v +value Printer/ConfigForm.php /^ $value = array();$/;" v +value Printer/ConfigForm.php /^ $value = implode(PHP_EOL, $value);$/;" v +value Printer/ConfigForm.php /^ $value = serialize($value);$/;" v +value_begin Lexer/DirectLex.php /^ $value_begin = $cursor;$/;" v +value_end Lexer/DirectLex.php /^ $value_end = $cursor;$/;" v +values AttrDef/CSS/URI.php /^ $values = array('\\\\(', '\\\\)', '\\\\,', '\\\\ ', '\\\\"', "\\\\'");$/;" v +values AttrDef/Enum.php /^ $values = explode(',', $string);$/;" v +values ContentSets.php /^ protected $values = array();$/;" v +values Printer.php /^ $values = array();$/;" v +var ChildDef/Table.php /^ $var = $collection[$tag_index]->name;$/;" v +var Context.php /^ $var = null; \/\/ so we can return by reference$/;" v +var VarParser.php /^ $var = $this->parseImplementation($var, $type, $allow_null);$/;" v +var VarParser.php /^ if ($allow_null && $var === null) return null;$/;" v +var VarParser/Flexible.php /^ $var = $nvar;$/;" v +var VarParser/Flexible.php /^ $var = explode(',',$var);$/;" v +var VarParser/Flexible.php /^ $var = false;$/;" v +var VarParser/Flexible.php /^ $var = preg_split('\/(,|[\\n\\r]+)\/', $var);$/;" v +var VarParser/Flexible.php /^ $var = true;$/;" v +var VarParser/Flexible.php /^ $var = (bool) $var;$/;" v +var VarParser/Flexible.php /^ if ($var == 'on' || $var == 'true' || $var == '1') {$/;" v +var VarParser/Flexible.php /^ } elseif ($var == 'off' || $var == 'false' || $var == '0') {$/;" v +var VarParser/Flexible.php /^ if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var;$/;" v +var VarParser/Flexible.php /^ if (is_int($var) && ($var === 0 || $var === 1)) {$/;" v +var VarParser/Flexible.php /^ if (is_string($var) && ctype_digit($var)) $var = (int) $var;$/;" v +var VarParser/Flexible.php /^ if ($allow_null && $var === null) return null;$/;" v +var VarParser/Native.php /^ $var = null;$/;" v +version Config.php /^ public $version = '3.3.0';$/;" v +vtype VarParser.php /^ $vtype = gettype($var);$/;" v +w Encoder.php /^ $w = (($code >> 18) & 7) | 240;$/;" v +was AttrDef/Integer.php /^ * @note While this class was modeled off the CSS definition, no currently$/;" c +was Encoder.php /^ * a string would be somewhat expensive, so the function was modded to$/;" f +whitespace ChildDef/Required.php /^ protected $whitespace = false;$/;" v +width AttrTransform/ImgSpace.php /^ $width = $this->confiscateAttr($attr, $this->attr);$/;" v +will HTMLModuleManager.php /^ * @note This function will not call autoload, you must instantiate$/;" f +will URISchemeRegistry.php /^ * the function will copy it and return it all further times.$/;" f +with ConfigSchema/Validator.php /^ protected function with($obj, $member) {$/;" f +working Encoder.php /^ $working = $bytevalue & 0x07;$/;" v +working Encoder.php /^ $working = $bytevalue & 0x0F;$/;" v +working Encoder.php /^ $working = $bytevalue & 0x1F;$/;" v +working Encoder.php /^ $working = $working << 6;$/;" v +working Encoder.php /^ $working = 0;$/;" v +would ConfigSchema/Validator.php /^ * design decision in that class would prevent this validation from$/;" c +wrapHTML Lexer/DOMLex.php /^ protected function wrapHTML($html, $config, $context) {$/;" f +writeHTMLDiv ConfigSchema/Builder/Xml.php /^ protected function writeHTMLDiv($html) {$/;" f +x Encoder.php /^ $x = $code;$/;" v +x Encoder.php /^ $x = ($code & 63) | 128;$/;" v +x Encoder.php /^ $x = $y = $z = $w = 0;$/;" v +xml Doctype.php /^ public $xml = true;$/;" v +xml Doctype.php /^ public function __construct($name = null, $xml = true, $modules = array(),$/;" v +xml DoctypeRegistry.php /^ public function register($doctype, $xml = true, $modules = array(),$/;" v +xml HTMLModuleManager.php /^ $xml = array('XMLCommonAttributes');$/;" v +xml_lang AttrTransform/Lang.php /^ $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;$/;" v +xml_lang AttrTransform/Lang.php /^ if ($lang !== false && $xml_lang === false) {$/;" v +y Encoder.php /^ $y = (($code & 2047) >> 6) | 192;$/;" v +y Encoder.php /^ $y = (($code & 4032) >> 6) | 128;$/;" v +z Encoder.php /^ $z = (($code >> 12) & 15) | 224;$/;" v +z Encoder.php /^ $z = (($code >> 12) & 63) | 128;$/;" v +zero AttrDef/Integer.php /^ protected $zero = true;$/;" v diff --git a/lib/php/UNL/Autoload.php b/lib/php/UNL/Autoload.php new file mode 100644 index 0000000..b1bd7a6 --- /dev/null +++ b/lib/php/UNL/Autoload.php @@ -0,0 +1,62 @@ +<?php +function UNL_Autoload($class) +{ + if (substr($class, 0, 4) !== 'UNL_') { + return false; + } + $fp = @fopen(str_replace('_', '/', $class) . '.php', 'r', true); + if ($fp) { + fclose($fp); + require str_replace('_', '/', $class) . '.php'; + if (!class_exists($class, false) && !interface_exists($class, false)) { + die(new Exception('Class ' . $class . ' was not present in ' . + str_replace('_', '/', $class) . '.php (include_path="' . get_include_path() . + '") [UNL_Autoload version 1.0]')); + } + return true; + } + $e = new Exception('Class ' . $class . ' could not be loaded from ' . + str_replace('_', '/', $class) . '.php, file does not exist (include_path="' . get_include_path() . + '") [UNL_Autoload version 1.0]'); + $trace = $e->getTrace(); + if (isset($trace[2]) && isset($trace[2]['function']) && + in_array($trace[2]['function'], array('class_exists', 'interface_exists'))) { + return false; + } + if (isset($trace[1]) && isset($trace[1]['function']) && + in_array($trace[1]['function'], array('class_exists', 'interface_exists'))) { + return false; + } + die ((string) $e); +} + +// set up __autoload +if (function_exists('spl_autoload_register')) { + if (!($_____t = spl_autoload_functions()) || !in_array('UNL_Autoload', spl_autoload_functions())) { + spl_autoload_register('UNL_Autoload'); + if (function_exists('__autoload') && ($_____t === false)) { + // __autoload() was being used, but now would be ignored, add + // it to the autoload stack + spl_autoload_register('__autoload'); + } + } + unset($_____t); +} elseif (!function_exists('__autoload')) { + function __autoload($class) { return UNL_Autoload($class); } +} + +// set up include_path if it doesn't register our current location +$____paths = explode(PATH_SEPARATOR, get_include_path()); +$____found = false; +foreach ($____paths as $____path) { + if ($____path == dirname(dirname(__FILE__))) { + $____found = true; + break; + } +} +if (!$____found) { + set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(__FILE__))); +} +unset($____paths); +unset($____path); +unset($____found); -- GitLab