From bf34b9d018b8f5013166e0ecb1c35dd736467cc1 Mon Sep 17 00:00:00 2001 From: YuCheng Hu Date: Fri, 18 Jun 2010 04:17:35 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=B0=20trunk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +YUCHENG HU+ git-svn-id: https://svn.code.sf.net/p/hawebs/svn@358 a2543c7e-f6e9-4f8a-8bff-1ffc34733512 --- .../trunk/include/HTTP_Session/Session.php | 804 ++++ .../HTTP_Session/Session/Container.php | 280 ++ .../HTTP_Session/Session/Container/DB.php | 364 ++ .../HTTP_Session/Session/Container/MDB.php | 364 ++ .../HTTP_Session/Session/Container/MDB2.php | 364 ++ .../Session/Container/Memcache.php | 202 + .../trunk/include/ListView/ListView.php | 288 ++ .../include/ListView/ListViewPagging.php | 78 + .../include/ListView/ListViewSession.php | 199 + .../trunk/include/fpdf/font/courier.php | 7 + .../trunk/include/fpdf/font/helvetica.php | 15 + .../trunk/include/fpdf/font/helveticab.php | 15 + .../trunk/include/fpdf/font/helveticabi.php | 15 + .../trunk/include/fpdf/font/helveticai.php | 15 + oss/vtiger/trunk/include/fpdf/font/symbol.php | 15 + oss/vtiger/trunk/include/fpdf/font/times.php | 15 + oss/vtiger/trunk/include/fpdf/font/timesb.php | 15 + .../trunk/include/fpdf/font/timesbi.php | 15 + oss/vtiger/trunk/include/fpdf/font/timesi.php | 15 + .../trunk/include/fpdf/font/zapfdingbats.php | 15 + oss/vtiger/trunk/include/fpdf/fpdf.css | 11 + oss/vtiger/trunk/include/fpdf/fpdf.php | 1660 +++++++ oss/vtiger/trunk/include/fpdf/pdf.php | 413 ++ oss/vtiger/trunk/include/fpdf/pdfconfig.php | 41 + .../trunk/include/fpdf/templates/body.php | 238 + .../trunk/include/freetag/freetag.class.php | 1150 +++++ oss/vtiger/trunk/include/freetag/license.txt | 479 ++ oss/vtiger/trunk/include/htmlpurifier/CREDITS | 9 + oss/vtiger/trunk/include/htmlpurifier/INSTALL | 373 ++ oss/vtiger/trunk/include/htmlpurifier/LICENSE | 504 +++ oss/vtiger/trunk/include/htmlpurifier/NEWS | 842 ++++ .../library/HTMLPurifier.auto.php | 11 + .../library/HTMLPurifier.autoload.php | 21 + .../library/HTMLPurifier.func.php | 23 + .../library/HTMLPurifier.includes.php | 206 + .../library/HTMLPurifier.kses.php | 30 + .../library/HTMLPurifier.path.php | 11 + .../htmlpurifier/library/HTMLPurifier.php | 236 + .../library/HTMLPurifier.safe-includes.php | 200 + .../library/HTMLPurifier/AttrCollections.php | 128 + .../library/HTMLPurifier/AttrDef.php | 87 + .../library/HTMLPurifier/AttrDef/CSS.php | 87 + .../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 21 + .../HTMLPurifier/AttrDef/CSS/Background.php | 87 + .../AttrDef/CSS/BackgroundPosition.php | 126 + .../HTMLPurifier/AttrDef/CSS/Border.php | 43 + .../HTMLPurifier/AttrDef/CSS/Color.php | 78 + .../HTMLPurifier/AttrDef/CSS/Composite.php | 38 + .../AttrDef/CSS/DenyElementDecorator.php | 28 + .../HTMLPurifier/AttrDef/CSS/Filter.php | 54 + .../library/HTMLPurifier/AttrDef/CSS/Font.php | 149 + .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 90 + .../AttrDef/CSS/ImportantDecorator.php | 40 + .../HTMLPurifier/AttrDef/CSS/Length.php | 47 + .../HTMLPurifier/AttrDef/CSS/ListStyle.php | 78 + .../HTMLPurifier/AttrDef/CSS/Multiple.php | 58 + .../HTMLPurifier/AttrDef/CSS/Number.php | 69 + .../HTMLPurifier/AttrDef/CSS/Percentage.php | 40 + .../AttrDef/CSS/TextDecoration.php | 38 + .../library/HTMLPurifier/AttrDef/CSS/URI.php | 56 + .../library/HTMLPurifier/AttrDef/Enum.php | 65 + .../HTMLPurifier/AttrDef/HTML/Bool.php | 28 + .../HTMLPurifier/AttrDef/HTML/Color.php | 32 + .../HTMLPurifier/AttrDef/HTML/FrameTarget.php | 21 + .../library/HTMLPurifier/AttrDef/HTML/ID.php | 70 + .../HTMLPurifier/AttrDef/HTML/Length.php | 41 + .../HTMLPurifier/AttrDef/HTML/LinkTypes.php | 53 + .../HTMLPurifier/AttrDef/HTML/MultiLength.php | 41 + .../HTMLPurifier/AttrDef/HTML/Nmtokens.php | 48 + .../HTMLPurifier/AttrDef/HTML/Pixels.php | 48 + .../library/HTMLPurifier/AttrDef/Integer.php | 73 + .../library/HTMLPurifier/AttrDef/Lang.php | 73 + .../library/HTMLPurifier/AttrDef/Switch.php | 34 + .../library/HTMLPurifier/AttrDef/Text.php | 15 + .../library/HTMLPurifier/AttrDef/URI.php | 77 + .../HTMLPurifier/AttrDef/URI/Email.php | 17 + .../AttrDef/URI/Email/SimpleCheck.php | 21 + .../library/HTMLPurifier/AttrDef/URI/Host.php | 62 + .../library/HTMLPurifier/AttrDef/URI/IPv4.php | 39 + .../library/HTMLPurifier/AttrDef/URI/IPv6.php | 99 + .../library/HTMLPurifier/AttrTransform.php | 56 + .../HTMLPurifier/AttrTransform/Background.php | 23 + .../HTMLPurifier/AttrTransform/BdoDir.php | 19 + .../HTMLPurifier/AttrTransform/BgColor.php | 23 + .../HTMLPurifier/AttrTransform/BoolToCSS.php | 36 + .../HTMLPurifier/AttrTransform/Border.php | 18 + .../HTMLPurifier/AttrTransform/EnumToCSS.php | 58 + .../AttrTransform/ImgRequired.php | 42 + .../HTMLPurifier/AttrTransform/ImgSpace.php | 44 + .../HTMLPurifier/AttrTransform/Input.php | 40 + .../HTMLPurifier/AttrTransform/Lang.php | 28 + .../HTMLPurifier/AttrTransform/Length.php | 27 + .../HTMLPurifier/AttrTransform/Name.php | 19 + .../HTMLPurifier/AttrTransform/SafeEmbed.php | 15 + .../HTMLPurifier/AttrTransform/SafeObject.php | 16 + .../HTMLPurifier/AttrTransform/SafeParam.php | 50 + .../AttrTransform/ScriptRequired.php | 16 + .../HTMLPurifier/AttrTransform/Textarea.php | 18 + .../library/HTMLPurifier/AttrTypes.php | 74 + .../library/HTMLPurifier/AttrValidator.php | 162 + .../library/HTMLPurifier/Bootstrap.php | 98 + .../library/HTMLPurifier/CSSDefinition.php | 292 ++ .../library/HTMLPurifier/ChildDef.php | 48 + .../HTMLPurifier/ChildDef/Chameleon.php | 48 + .../library/HTMLPurifier/ChildDef/Custom.php | 90 + .../library/HTMLPurifier/ChildDef/Empty.php | 20 + .../HTMLPurifier/ChildDef/Optional.php | 26 + .../HTMLPurifier/ChildDef/Required.php | 117 + .../ChildDef/StrictBlockquote.php | 88 + .../library/HTMLPurifier/ChildDef/Table.php | 142 + .../library/HTMLPurifier/Config.php | 497 +++ .../library/HTMLPurifier/ConfigSchema.php | 231 + .../ConfigSchema/Builder/ConfigSchema.php | 52 + .../HTMLPurifier/ConfigSchema/Builder/Xml.php | 108 + .../HTMLPurifier/ConfigSchema/Exception.php | 11 + .../HTMLPurifier/ConfigSchema/Interchange.php | 57 + .../ConfigSchema/Interchange/Directive.php | 77 + .../ConfigSchema/Interchange/Id.php | 31 + .../ConfigSchema/Interchange/Namespace.php | 21 + .../ConfigSchema/InterchangeBuilder.php | 176 + .../HTMLPurifier/ConfigSchema/Validator.php | 225 + .../ConfigSchema/ValidatorAtom.php | 66 + .../HTMLPurifier/ConfigSchema/schema.ser | Bin 0 -> 11324 bytes .../schema/Attr.AllowedFrameTargets.txt | 12 + .../ConfigSchema/schema/Attr.AllowedRel.txt | 9 + .../ConfigSchema/schema/Attr.AllowedRev.txt | 9 + .../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 + .../ConfigSchema/schema/Attr.IDBlacklist.txt | 5 + .../schema/Attr.IDBlacklistRegexp.txt | 9 + .../ConfigSchema/schema/Attr.IDPrefix.txt | 12 + .../schema/Attr.IDPrefixLocal.txt | 14 + .../HTMLPurifier/ConfigSchema/schema/Attr.txt | 3 + .../schema/AutoFormat.AutoParagraph.txt | 31 + .../ConfigSchema/schema/AutoFormat.Custom.txt | 12 + .../schema/AutoFormat.DisplayLinkURI.txt | 11 + .../schema/AutoFormat.Linkify.txt | 12 + .../schema/AutoFormat.PurifierLinkify.txt | 12 + .../schema/AutoFormat.RemoveEmpty.txt | 45 + .../ConfigSchema/schema/AutoFormat.txt | 3 + .../AutoFormatParam.PurifierLinkifyDocURL.txt | 12 + .../ConfigSchema/schema/AutoFormatParam.txt | 3 + .../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 + .../HTMLPurifier/ConfigSchema/schema/CSS.txt | 3 + .../schema/Cache.DefinitionImpl.txt | 14 + .../schema/Cache.SerializerPath.txt | 13 + .../ConfigSchema/schema/Cache.txt | 3 + .../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 + .../HTMLPurifier/ConfigSchema/schema/Core.txt | 3 + .../ConfigSchema/schema/Filter.Custom.txt | 11 + .../schema/Filter.ExtractStyleBlocks.txt | 74 + .../ConfigSchema/schema/Filter.YouTube.txt | 11 + .../ConfigSchema/schema/Filter.txt | 3 + ...FilterParam.ExtractStyleBlocksEscaping.txt | 14 + .../FilterParam.ExtractStyleBlocksScope.txt | 29 + ...FilterParam.ExtractStyleBlocksTidyImpl.txt | 15 + .../ConfigSchema/schema/FilterParam.txt | 3 + .../ConfigSchema/schema/HTML.Allowed.txt | 22 + .../schema/HTML.AllowedAttributes.txt | 19 + .../schema/HTML.AllowedElements.txt | 18 + .../schema/HTML.AllowedModules.txt | 20 + .../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 + .../HTMLPurifier/ConfigSchema/schema/HTML.txt | 3 + .../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/Output.txt | 3 + .../ConfigSchema/schema/Test.ForceNoIconv.txt | 7 + .../HTMLPurifier/ConfigSchema/schema/Test.txt | 3 + .../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/URI.txt | 3 + .../HTMLPurifier/ConfigSchema/schema/info.ini | 3 + .../library/HTMLPurifier/ContentSets.php | 155 + .../library/HTMLPurifier/Context.php | 82 + .../library/HTMLPurifier/Definition.php | 39 + .../library/HTMLPurifier/DefinitionCache.php | 108 + .../DefinitionCache/Decorator.php | 62 + .../DefinitionCache/Decorator/Cleanup.php | 43 + .../DefinitionCache/Decorator/Memory.php | 46 + .../DefinitionCache/Decorator/Template.php.in | 47 + .../HTMLPurifier/DefinitionCache/Null.php | 39 + .../DefinitionCache/Serializer.php | 172 + .../DefinitionCache/Serializer/README | 3 + .../HTMLPurifier/DefinitionCacheFactory.php | 91 + .../library/HTMLPurifier/Doctype.php | 60 + .../library/HTMLPurifier/DoctypeRegistry.php | 103 + .../library/HTMLPurifier/ElementDef.php | 175 + .../library/HTMLPurifier/Encoder.php | 426 ++ .../library/HTMLPurifier/EntityLookup.php | 44 + .../HTMLPurifier/EntityLookup/entities.ser | 1 + .../library/HTMLPurifier/EntityParser.php | 144 + .../library/HTMLPurifier/ErrorCollector.php | 209 + .../library/HTMLPurifier/ErrorStruct.php | 60 + .../library/HTMLPurifier/Exception.php | 12 + .../library/HTMLPurifier/Filter.php | 46 + .../Filter/ExtractStyleBlocks.php | 135 + .../library/HTMLPurifier/Filter/YouTube.php | 39 + .../library/HTMLPurifier/Generator.php | 183 + .../library/HTMLPurifier/HTMLDefinition.php | 420 ++ .../library/HTMLPurifier/HTMLModule.php | 244 + .../library/HTMLPurifier/HTMLModule/Bdo.php | 31 + .../HTMLModule/CommonAttributes.php | 25 + .../library/HTMLPurifier/HTMLModule/Edit.php | 38 + .../library/HTMLPurifier/HTMLModule/Forms.php | 118 + .../HTMLPurifier/HTMLModule/Hypertext.php | 31 + .../library/HTMLPurifier/HTMLModule/Image.php | 40 + .../HTMLPurifier/HTMLModule/Legacy.php | 143 + .../library/HTMLPurifier/HTMLModule/List.php | 35 + .../library/HTMLPurifier/HTMLModule/Name.php | 18 + .../HTMLModule/NonXMLCommonAttributes.php | 14 + .../HTMLPurifier/HTMLModule/Object.php | 47 + .../HTMLPurifier/HTMLModule/Presentation.php | 36 + .../HTMLPurifier/HTMLModule/Proprietary.php | 33 + .../library/HTMLPurifier/HTMLModule/Ruby.php | 27 + .../HTMLPurifier/HTMLModule/SafeEmbed.php | 33 + .../HTMLPurifier/HTMLModule/SafeObject.php | 50 + .../HTMLPurifier/HTMLModule/Scripting.php | 54 + .../HTMLModule/StyleAttribute.php | 24 + .../HTMLPurifier/HTMLModule/Tables.php | 66 + .../HTMLPurifier/HTMLModule/Target.php | 23 + .../library/HTMLPurifier/HTMLModule/Text.php | 71 + .../library/HTMLPurifier/HTMLModule/Tidy.php | 207 + .../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 + .../HTMLPurifier/HTMLModuleManager.php | 403 ++ .../library/HTMLPurifier/IDAccumulator.php | 53 + .../library/HTMLPurifier/Injector.php | 233 + .../HTMLPurifier/Injector/AutoParagraph.php | 340 ++ .../HTMLPurifier/Injector/DisplayLinkURI.php | 26 + .../library/HTMLPurifier/Injector/Linkify.php | 46 + .../HTMLPurifier/Injector/PurifierLinkify.php | 45 + .../HTMLPurifier/Injector/RemoveEmpty.php | 42 + .../HTMLPurifier/Injector/SafeObject.php | 87 + .../library/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 + .../HTMLPurifier/Language/messages/en.php | 62 + .../library/HTMLPurifier/LanguageFactory.php | 198 + .../library/HTMLPurifier/Length.php | 115 + .../library/HTMLPurifier/Lexer.php | 298 ++ .../library/HTMLPurifier/Lexer/DOMLex.php | 213 + .../library/HTMLPurifier/Lexer/DirectLex.php | 490 +++ .../library/HTMLPurifier/Lexer/PEARSax3.php | 106 + .../library/HTMLPurifier/Lexer/PH5P.php | 3906 +++++++++++++++++ .../library/HTMLPurifier/PercentEncoder.php | 98 + .../library/HTMLPurifier/Printer.php | 176 + .../HTMLPurifier/Printer/CSSDefinition.php | 38 + .../HTMLPurifier/Printer/ConfigForm.css | 10 + .../HTMLPurifier/Printer/ConfigForm.js | 5 + .../HTMLPurifier/Printer/ConfigForm.php | 368 ++ .../HTMLPurifier/Printer/HTMLDefinition.php | 272 ++ .../library/HTMLPurifier/PropertyList.php | 86 + .../HTMLPurifier/PropertyListIterator.php | 32 + .../library/HTMLPurifier/Strategy.php | 26 + .../HTMLPurifier/Strategy/Composite.php | 25 + .../library/HTMLPurifier/Strategy/Core.php | 18 + .../HTMLPurifier/Strategy/FixNesting.php | 328 ++ .../HTMLPurifier/Strategy/MakeWellFormed.php | 455 ++ .../Strategy/RemoveForeignElements.php | 171 + .../Strategy/ValidateAttributes.php | 39 + .../library/HTMLPurifier/StringHash.php | 39 + .../library/HTMLPurifier/StringHashParser.php | 110 + .../library/HTMLPurifier/TagTransform.php | 36 + .../HTMLPurifier/TagTransform/Font.php | 96 + .../HTMLPurifier/TagTransform/Simple.php | 35 + .../library/HTMLPurifier/Token.php | 57 + .../library/HTMLPurifier/Token/Comment.php | 22 + .../library/HTMLPurifier/Token/Empty.php | 11 + .../library/HTMLPurifier/Token/End.php | 19 + .../library/HTMLPurifier/Token/Start.php | 11 + .../library/HTMLPurifier/Token/Tag.php | 56 + .../library/HTMLPurifier/Token/Text.php | 33 + .../library/HTMLPurifier/TokenFactory.php | 94 + .../htmlpurifier/library/HTMLPurifier/URI.php | 173 + .../library/HTMLPurifier/URIDefinition.php | 93 + .../library/HTMLPurifier/URIFilter.php | 45 + .../URIFilter/DisableExternal.php | 23 + .../URIFilter/DisableExternalResources.php | 12 + .../HTMLPurifier/URIFilter/HostBlacklist.php | 21 + .../HTMLPurifier/URIFilter/MakeAbsolute.php | 114 + .../library/HTMLPurifier/URIFilter/Munge.php | 54 + .../library/HTMLPurifier/URIParser.php | 70 + .../library/HTMLPurifier/URIScheme.php | 42 + .../library/HTMLPurifier/URIScheme/ftp.php | 43 + .../library/HTMLPurifier/URIScheme/http.php | 20 + .../library/HTMLPurifier/URIScheme/https.php | 12 + .../library/HTMLPurifier/URIScheme/mailto.php | 27 + .../library/HTMLPurifier/URIScheme/news.php | 22 + .../library/HTMLPurifier/URIScheme/nntp.php | 20 + .../HTMLPurifier/URISchemeRegistry.php | 69 + .../library/HTMLPurifier/UnitConverter.php | 254 ++ .../library/HTMLPurifier/VarParser.php | 154 + .../HTMLPurifier/VarParser/Flexible.php | 96 + .../library/HTMLPurifier/VarParser/Native.php | 26 + .../HTMLPurifier/VarParserException.php | 11 + oss/vtiger/trunk/include/images/announ.gif | Bin 0 -> 2748 bytes oss/vtiger/trunk/include/images/bgcolor.gif | Bin 0 -> 178 bytes oss/vtiger/trunk/include/images/blank.gif | Bin 0 -> 43 bytes .../trunk/include/images/loginAlpha.gif | Bin 0 -> 761 bytes oss/vtiger/trunk/include/images/loginBg.gif | Bin 0 -> 1072 bytes .../trunk/include/images/loginBottomBg.gif | Bin 0 -> 108 bytes .../trunk/include/images/loginBottomURL.gif | Bin 0 -> 444 bytes .../trunk/include/images/loginBtnSignin.gif | Bin 0 -> 1778 bytes .../include/images/loginSIBottomLeft.gif | Bin 0 -> 99 bytes .../include/images/loginSIBottomRight.gif | Bin 0 -> 98 bytes .../trunk/include/images/loginSITopLeft.gif | Bin 0 -> 99 bytes .../trunk/include/images/loginSITopRight.gif | Bin 0 -> 98 bytes .../trunk/include/images/loginTopBg.gif | Bin 0 -> 1068 bytes .../trunk/include/images/loginTopShade.gif | Bin 0 -> 121 bytes .../trunk/include/images/loginVtigerCRM.gif | Bin 0 -> 2708 bytes oss/vtiger/trunk/include/images/noimage.gif | Bin 0 -> 3859 bytes oss/vtiger/trunk/include/images/right_arc.gif | Bin 0 -> 49 bytes oss/vtiger/trunk/include/images/spacer.gif | Bin 0 -> 49 bytes oss/vtiger/trunk/include/images/topBg.gif | Bin 0 -> 1252 bytes .../trunk/include/images/vtigercrm_icon.ico | Bin 0 -> 1406 bytes 377 files changed, 32428 insertions(+) create mode 100644 oss/vtiger/trunk/include/HTTP_Session/Session.php create mode 100644 oss/vtiger/trunk/include/HTTP_Session/Session/Container.php create mode 100644 oss/vtiger/trunk/include/HTTP_Session/Session/Container/DB.php create mode 100644 oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB.php create mode 100644 oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB2.php create mode 100644 oss/vtiger/trunk/include/HTTP_Session/Session/Container/Memcache.php create mode 100644 oss/vtiger/trunk/include/ListView/ListView.php create mode 100644 oss/vtiger/trunk/include/ListView/ListViewPagging.php create mode 100644 oss/vtiger/trunk/include/ListView/ListViewSession.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/courier.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/helvetica.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/helveticab.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/helveticabi.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/helveticai.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/symbol.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/times.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/timesb.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/timesbi.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/timesi.php create mode 100644 oss/vtiger/trunk/include/fpdf/font/zapfdingbats.php create mode 100644 oss/vtiger/trunk/include/fpdf/fpdf.css create mode 100644 oss/vtiger/trunk/include/fpdf/fpdf.php create mode 100644 oss/vtiger/trunk/include/fpdf/pdf.php create mode 100644 oss/vtiger/trunk/include/fpdf/pdfconfig.php create mode 100644 oss/vtiger/trunk/include/fpdf/templates/body.php create mode 100644 oss/vtiger/trunk/include/freetag/freetag.class.php create mode 100644 oss/vtiger/trunk/include/freetag/license.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/CREDITS create mode 100644 oss/vtiger/trunk/include/htmlpurifier/INSTALL create mode 100644 oss/vtiger/trunk/include/htmlpurifier/LICENSE create mode 100644 oss/vtiger/trunk/include/htmlpurifier/NEWS create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.auto.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.autoload.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.func.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.includes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.kses.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.path.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier.safe-includes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrCollections.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrTypes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/AttrValidator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Bootstrap.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/CSSDefinition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Config.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Namespace.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.PurifierLinkifyDocURL.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormatParam.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.txt create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ContentSets.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Context.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Definition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Doctype.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ElementDef.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Encoder.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityParser.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorCollector.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorStruct.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Exception.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Generator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/IDAccumulator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-test.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/LanguageFactory.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Length.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PEARSax3.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PercentEncoder.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyList.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/Core.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHash.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHashParser.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Comment.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Empty.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/End.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Start.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Tag.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Text.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TokenFactory.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URI.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIDefinition.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIParser.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/http.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/https.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/news.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/UnitConverter.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Native.php create mode 100644 oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParserException.php create mode 100644 oss/vtiger/trunk/include/images/announ.gif create mode 100644 oss/vtiger/trunk/include/images/bgcolor.gif create mode 100644 oss/vtiger/trunk/include/images/blank.gif create mode 100644 oss/vtiger/trunk/include/images/loginAlpha.gif create mode 100644 oss/vtiger/trunk/include/images/loginBg.gif create mode 100644 oss/vtiger/trunk/include/images/loginBottomBg.gif create mode 100644 oss/vtiger/trunk/include/images/loginBottomURL.gif create mode 100644 oss/vtiger/trunk/include/images/loginBtnSignin.gif create mode 100644 oss/vtiger/trunk/include/images/loginSIBottomLeft.gif create mode 100644 oss/vtiger/trunk/include/images/loginSIBottomRight.gif create mode 100644 oss/vtiger/trunk/include/images/loginSITopLeft.gif create mode 100644 oss/vtiger/trunk/include/images/loginSITopRight.gif create mode 100644 oss/vtiger/trunk/include/images/loginTopBg.gif create mode 100644 oss/vtiger/trunk/include/images/loginTopShade.gif create mode 100644 oss/vtiger/trunk/include/images/loginVtigerCRM.gif create mode 100644 oss/vtiger/trunk/include/images/noimage.gif create mode 100644 oss/vtiger/trunk/include/images/right_arc.gif create mode 100644 oss/vtiger/trunk/include/images/spacer.gif create mode 100644 oss/vtiger/trunk/include/images/topBg.gif create mode 100644 oss/vtiger/trunk/include/images/vtigercrm_icon.ico diff --git a/oss/vtiger/trunk/include/HTTP_Session/Session.php b/oss/vtiger/trunk/include/HTTP_Session/Session.php new file mode 100644 index 00000000..c811c0ba --- /dev/null +++ b/oss/vtiger/trunk/include/HTTP_Session/Session.php @@ -0,0 +1,804 @@ + + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Session.php,v 1.15 2007/07/14 12:11:54 troehr Exp $ + * @link http://pear.php.net/package/HTTP_Session + * @since File available since Release 0.4.0 + */ + +// @const HTTP_SESSION_STARTED - The session was started with the current request +define("HTTP_SESSION_STARTED", 1); +// @const HTTP_SESSION_STARTED - No new session was started with the current request +define("HTTP_SESSION_CONTINUED", 2); + +/** + * Class for managing HTTP sessions + * + * Provides access to session-state values as well as session-level + * settings and lifetime management methods. + * Based on the standart PHP session handling mechanism + * it provides for you more advanced features such as + * database container, idle and expire timeouts, etc. + * + * Example 1: + * + * + * // Setting some options and detecting of a new session + * HTTP_Session::setCookieless(false); + * HTTP_Session::start('MySessionID'); + * HTTP_Session::set('variable', 'Tet string'); + * if (HTTP_Session::isNew()) { + * echo('new session was created with the current request'); + * $visitors++; // Increase visitors count + * } + * + * //HTTP_Session::regenerateId(); + * + * + * Example 2: + * + * + * // Using database container + * HTTP_Session::setContainer('DB'); + * HTTP_Session::start(); + * + * + * Example 3: + * + * + * // Setting timeouts + * HTTP_Session::start(); + * HTTP_Session::setExpire(time() + 60 * 60); // expires in one hour + * HTTP_Session::setIdle(time()+ 10 * 60); // idles in ten minutes + * if (HTTP_Session::isExpired()) { + * // expired + * echo('Your session is expired!'); + * HTTP_Session::destroy(); + * } + * if (HTTP_Session::isIdle()) { + * // idle + * echo('You've been idle for too long!'); + * HTTP_Session::destroy(); + * } + * HTTP_Session::updateIdle(); + * + * + * @category HTTP + * @package HTTP_Session + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/HTTP_Session + * @since Class available since Release 0.4.0 + */ +class HTTP_Session +{ + /** + * Sets user-defined session storage functions + * + * Sets the user-defined session storage functions which are used + * for storing and retrieving data associated with a session. + * This is most useful when a storage method other than + * those supplied by PHP sessions is preferred. + * i.e. Storing the session data in a local database. + * + * @param string $container Container name + * @param array $container_options Container options + * + * @static + * @access public + * @return void + * @see session_set_save_handler() + */ + function setContainer($container, $container_options = null) + { + $container_class = 'HTTP_Session_Container_' . $container; + $container_classfile = 'HTTP/Session/Container/' . $container . '.php'; + + include_once $container_classfile; + $container = new $container_class($container_options); + + $container->set(); + } + + /** + * Initializes session data + * + * Creates a session (or resumes the current one + * based on the session id being passed + * via a GET variable or a cookie). + * You can provide your own name and/or id for a session. + * + * @param string $name string Name of a session, default is 'SessionID' + * @param string $id string Id of a session which will be used + * only when the session is new + * + * @static + * @access public + * @return void + * @see session_name() + * @see session_id() + * @see session_start() + */ + function start($name = 'SessionID', $id = null) + { + HTTP_Session::name($name); + if ($id) { + HTTP_Session::id($id); + } elseif (is_null(HTTP_Session::detectID())) { + HTTP_Session::id($id ? $id : uniqid(dechex(rand()))); + } + session_start(); + if (!isset($_SESSION['__HTTP_Session_Info'])) { + $_SESSION['__HTTP_Session_Info'] = HTTP_SESSION_STARTED; + } else { + $_SESSION['__HTTP_Session_Info'] = HTTP_SESSION_CONTINUED; + } + } + + /** + * Writes session data and ends session + * + * Session data is usually stored after your script + * terminated without the need to call HTTP_Session::stop(), + * but as session data is locked to prevent concurrent + * writes only one script may operate on a session at any time. + * When using framesets together with sessions you will + * experience the frames loading one by one due to this + * locking. You can reduce the time needed to load all the + * frames by ending the session as soon as all changes + * to session variables are done. + * + * @static + * @access public + * @return void + * @see session_write_close() + */ + function pause() + { + session_write_close(); + } + + /** + * Frees all session variables and destroys all data + * registered to a session + * + * This method resets the $_SESSION variable and + * destroys all of the data associated + * with the current session in its storage (file or DB). + * It forces new session to be started after this method + * is called. It does not unset the session cookie. + * + * @static + * @access public + * @return void + * @see session_unset() + * @see session_destroy() + */ + function destroy() + { + session_unset(); + session_destroy(); + + // set session handlers again to avoid fatal error in case + // HTTP_Session::start() will be called afterwards + if (isset($GLOBALS['HTTP_Session_Container']) && + is_a($GLOBALS['HTTP_Session_Container'], 'HTTP_Session_Container')) { + $GLOBALS['HTTP_Session_Container']->set(); + } + } + + /** + * Calls session_regenerate_id() if available + * + * @param bool $deleteOldSessionData Whether to delete data of old session + * + * @static + * @access public + * @return bool + */ + function regenerateId($deleteOldSessionData = false) + { + if (function_exists('session_regenerate_id')) { + return session_regenerate_id($deleteOldSessionData); + + // emulate session_regenerate_id() + } else { + + do { + $newId = uniqid(dechex(rand())); + } while ($newId === session_id()); + + if ($deleteOldSessionData) { + session_unset(); + } + + session_id($newId); + + return true; + } + } + + /** + * This function copies session data of specified id to specified table + * + * @param string $targetTable Table to replicate data to + * @param string $id ID of the session + * + * @static + * @access public + * @return bool + */ + function replicate($targetTable, $id = null) + { + return $GLOBALS['HTTP_Session_Container']->replicate($targetTable, $id); + } + + /** + * Free all session variables + * + * @todo TODO Save expire and idle timestamps? + * @static + * @access public + * @return void + */ + function clear() + { + $info = $_SESSION['__HTTP_Session_Info']; + session_unset(); + $_SESSION['__HTTP_Session_Info'] = $info; + } + + /** + * Tries to find any session id in $_GET, $_POST or $_COOKIE + * + * @static + * @access private + * @return string Session ID (if exists) or null + */ + function detectID() + { + if (HTTP_Session::useCookies()) { + if (isset($_COOKIE[HTTP_Session::name()])) { + return $_COOKIE[HTTP_Session::name()]; + } + } else { + if (isset($_GET[HTTP_Session::name()])) { + return $_GET[HTTP_Session::name()]; + } + if (isset($_POST[HTTP_Session::name()])) { + return $_POST[HTTP_Session::name()]; + } + } + return null; + } + + /** + * Sets new name of a session + * + * @param string $name New name of a session + * + * @static + * @access public + * @return string Previous name of a session + * @see session_name() + */ + function name($name = null) + { + return isset($name) ? session_name($name) : session_name(); + } + + /** + * Sets new ID of a session + * + * @param string $id New ID of a session + * + * @static + * @access public + * @return string Previous ID of a session + * @see session_id() + */ + function id($id = null) + { + return isset($id) ? session_id($id) : session_id(); + } + + /** + * Sets the maximum expire time + * + * @param integer $time Time in seconds + * @param bool $add Add time to current expire time or not + * + * @static + * @access public + * @return void + */ + function setExpire($time, $add = false) + { + if ($add) { + if (!isset($_SESSION['__HTTP_Session_Expire_TS'])) { + $_SESSION['__HTTP_Session_Expire_TS'] = time() + $time; + } + + // update session.gc_maxlifetime + $currentGcMaxLifetime = HTTP_Session::setGcMaxLifetime(null); + HTTP_Session::setGcMaxLifetime($currentGcMaxLifetime + $time); + + } elseif (!isset($_SESSION['__HTTP_Session_Expire_TS'])) { + $_SESSION['__HTTP_Session_Expire_TS'] = $time; + } + } + + /** + * Sets the maximum idle time + * + * Sets the time-out period allowed + * between requests before the session-state + * provider terminates the session. + * + * @param int $time Time in seconds + * @param bool $add Add time to current maximum idle time or not + * + * @static + * @access public + * @return void + */ + function setIdle($time, $add = false) + { + if ($add) { + $_SESSION['__HTTP_Session_Idle'] = $time; + } else { + // substract time again because it doesn't make any sense to provide + // the idle time as a timestamp + // keep $add functionality to provide BC + $_SESSION['__HTTP_Session_Idle'] = $time - time(); + } + } + + /** + * Returns the time up to the session is valid + * + * @static + * @access public + * @return integer Time when the session idles + */ + function sessionValidThru() + { + if (!isset($_SESSION['__HTTP_Session_Idle_TS']) || + !isset($_SESSION['__HTTP_Session_Idle'])) { + return 0; + } else { + return $_SESSION['__HTTP_Session_Idle_TS'] + + $_SESSION['__HTTP_Session_Idle']; + } + } + + /** + * Check if session is expired + * + * @static + * @access public + * @return bool + */ + function isExpired() + { + if (isset($_SESSION['__HTTP_Session_Expire_TS']) && + $_SESSION['__HTTP_Session_Expire_TS'] < time()) { + return true; + } else { + return false; + } + } + + /** + * Check if session is idle + * + * @static + * @access public + * @return bool + */ + function isIdle() + { + if (isset($_SESSION['__HTTP_Session_Idle_TS']) && + (($_SESSION['__HTTP_Session_Idle_TS'] + + $_SESSION['__HTTP_Session_Idle']) < time())) { + return true; + } else { + return false; + } + } + + /** + * Updates the idletime + * + * @static + * @access public + * @return void + */ + function updateIdle() + { + $_SESSION['__HTTP_Session_Idle_TS'] = time(); + } + + /** + * If optional parameter is specified it indicates + * whether the module will use cookies to store + * the session id on the client side + * + * It returns the previous value of this property + * + * @param bool $useCookies If specified it will replace the previous value + * of this property + * + * @static + * @access public + * + * @return bool The previous value of the property + */ + function useCookies($useCookies = null) + { + $return = ini_get('session.use_cookies') ? true : false; + if (isset($useCookies)) { + ini_set('session.use_cookies', $useCookies ? 1 : 0); + } + return $return; + } + + /** + * Gets a value indicating whether the session + * was created with the current request + * + * You MUST call this method only after you have started + * the session with the HTTP_Session::start() method. + * + * @static + * @access public + * @return bool True if the session was created + * with the current request, false otherwise + */ + function isNew() + { + // The best way to check if a session is new is to check + // for existence of a session data storage + // with the current session id, but this is impossible + // with the default PHP module wich is 'files'. + // So we need to emulate it. + return !isset($_SESSION['__HTTP_Session_Info']) || + $_SESSION['__HTTP_Session_Info'] == HTTP_SESSION_STARTED; + } + + /** + * Register variable with the current session + * + * @param string $name Name of a global variable + * + * @deprecated Use set()/setRef() instead + * + * @static + * @access public + * @return bool + * @see session_register() + */ + function register($name) + { + return session_register($name); + } + + /** + * Unregister a variable from the current session + * + * @param string $name Name of a global variable + * + * @deprecated Use get()/getRef() instead + * + * @static + * @access public + * @return bool + * @see session_unregister() + */ + function unregister($name) + { + return session_unregister($name); + } + + /** + * Checks if a session variable is registered + * + * @param string $name Variable name + * + * @deprecated Use is_set() instead + * + * @static + * @access public + * @return bool + */ + function registered($name) + { + return session_is_registered($name); + } + + /** + * Returns session variable + * + * @param string $name Name of a variable + * @param mixed $default Default value of a variable if not set + * + * @static + * @access public + * @return mixed Value of a variable + */ + function get($name, $default = null) + { + if (!isset($_SESSION[$name]) && isset($default)) { + $_SESSION[$name] = $default; + } + $return = (isset($_SESSION[$name])) ? $_SESSION[$name] : null; + return $return; + } + + /** + * Returns session variable by reference + * + * @param string $name Name of a variable + * + * @static + * @access public + * @return mixed Value of a variable + */ + function &getRef($name) + { + if (isset($_SESSION[$name])) { + $return =& $_SESSION[$name]; + } else { + $return = null; + } + + return $return; + } + + /** + * Sets session variable + * + * @param string $name Name of a variable + * @param mixed $value Value of a variable + * + * @static + * @access public + * @return mixed Old value of a variable + */ + function set($name, $value) + { + $return = (isset($_SESSION[$name])) ? $_SESSION[$name] : null; + if (null === $value) { + unset($_SESSION[$name]); + } else { + $_SESSION[$name] = $value; + } + return $return; + } + + /** + * Sets session variable by reference + * + * @param string $name Name of a variable + * @param mixed $value Value of a variable + * + * @static + * @access public + * @return mixed Old value of a variable + */ + function setRef($name, &$value) + { + $return = (isset($_SESSION[$name])) ? $_SESSION[$name] : null; + + $_SESSION[$name] =& $value; + + return $return; + } + + /** + * Checks if a session variable is set + * + * @param string $name Variable name + * + * @static + * @access public + * @return bool + */ + function is_set($name) + { + return isset($_SESSION[$name]); + } + + /** + * Returns local variable of a script + * + * Two scripts can have local variables with the same names + * + * @param string $name Name of a variable + * @param mixed $default Default value of a variable if not set + * + * @static + * @access public + * @return mixed Value of a local variable + */ + function &getLocal($name, $default = null) + { + $local = md5(HTTP_Session::localName()); + if (!isset($_SESSION[$local]) || !is_array($_SESSION[$local])) { + $_SESSION[$local] = array(); + } + if (!isset($_SESSION[$local][$name]) && isset($default)) { + $_SESSION[$local][$name] = $default; + } + return $_SESSION[$local][$name]; + } + + /** + * Sets local variable of a script. + * Two scripts can have local variables with the same names. + * + * @param string $name Name of a local variable + * @param mixed $value Value of a local variable + * + * @static + * @access public + * @return mixed Old value of a local variable + */ + function setLocal($name, $value) + { + $local = md5(HTTP_Session::localName()); + if (!isset($_SESSION[$local]) || !is_array($_SESSION[$local])) { + $_SESSION[$local] = array(); + } + $return = (isset($_SESSION[$local][$name])) ? $_SESSION[$local][$name] + : null; + + if (null === $value) { + unset($_SESSION[$local][$name]); + } else { + $_SESSION[$local][$name] = $value; + } + return $return; + } + + /** + * Sets new local name + * + * @param string $name New local name + * + * @static + * @access public + * @return string Previous local name + */ + function localName($name = null) + { + $return = (isset($GLOBALS['__HTTP_Session_Localname'])) ? $GLOBALS['__HTTP_Session_Localname'] + : null; + + if (!empty($name)) { + $GLOBALS['__HTTP_Session_Localname'] = $name; + } + return $return; + } + + /** + * Initialize + * + * @static + * @access private + * @return void + */ + function _init() + { + // Disable auto-start of a sesion + ini_set('session.auto_start', 0); + + // Set local name equal to the current script name + HTTP_Session::localName($_SERVER['PHP_SELF']); + } + + /** + * If optional parameter is specified it indicates + * whether the session id will automatically be appended to + * all links + * + * It returns the previous value of this property + * + * @param bool $useTransSID If specified it will replace the previous value + * of this property + * + * @static + * @access public + * @return bool The previous value of the property + */ + function useTransSID($useTransSID = null) + { + $return = ini_get('session.use_trans_sid') ? true : false; + if (isset($useTransSID)) { + ini_set('session.use_trans_sid', $useTransSID ? 1 : 0); + } + return $return; + } + + /** + * If optional parameter is specified it determines the number of seconds + * after which session data will be seen as 'garbage' and cleaned up + * + * It returns the previous value of this property + * + * @param bool $gcMaxLifetime If specified it will replace the previous value + * of this property + * + * @static + * @access public + * @return bool The previous value of the property + */ + function setGcMaxLifetime($gcMaxLifetime = null) + { + $return = ini_get('session.gc_maxlifetime'); + if (isset($gcMaxLifetime) && is_int($gcMaxLifetime) && $gcMaxLifetime >= 1) { + ini_set('session.gc_maxlifetime', $gcMaxLifetime); + } + return $return; + } + + /** + * If optional parameter is specified it determines the + * probability that the gc (garbage collection) routine is started + * and session data is cleaned up + * + * It returns the previous value of this property + * + * @param bool $gcProbability If specified it will replace the previous value + * of this property + * + * @static + * @access public + * @return bool The previous value of the property + */ + function setGcProbability($gcProbability = null) + { + $return = ini_get('session.gc_probability'); + if (isset($gcProbability) && + is_int($gcProbability) && + $gcProbability >= 1 && + $gcProbability <= 100) { + ini_set('session.gc_probability', $gcProbability); + } + return $return; + } +} + +HTTP_Session::_init(); +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/HTTP_Session/Session/Container.php b/oss/vtiger/trunk/include/HTTP_Session/Session/Container.php new file mode 100644 index 00000000..ba85fa2a --- /dev/null +++ b/oss/vtiger/trunk/include/HTTP_Session/Session/Container.php @@ -0,0 +1,280 @@ + + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Container.php,v 1.8 2007/07/14 12:11:54 troehr Exp $ + * @link http://pear.php.net/package/HTTP_Session + * @since File available since Release 0.4.0 + */ + +/** + * Container class for storing session data + * + * @category HTTP + * @package HTTP_Session + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/HTTP_Session + * @since Class available since Release 0.4.0 + */ +class HTTP_Session_Container +{ + /** + * Additional options for the container object + * + * @var array + * @access private + */ + var $options = array(); + + /** + * Constrtuctor method + * + * @param array $options Additional options for the container object + * + * @access public + * @return object + */ + function HTTP_Session_Container($options = null) + { + $this->_setDefaults(); + if (is_array($options)) { + $this->_parseOptions(); + } + } + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + } + + /** + * Parse options passed to the container class + * + * @param array $options Options + * + * @access private + * @return void + */ + function _parseOptions($options) + { + foreach ($options as $option => $value) { + if (in_array($option, array_keys($this->options))) { + $this->options[$option] = $value; + } + } + } + + /** + * This function is called by the session handler to initialize things + * + * @param string $save_path Save path + * @param string $session_name Session name + * + * @access public + * @return bool + */ + function open($save_path, $session_name) + { + return true; + } + + /** + * This function is called when the page is finished + * executing and the session handler needs to close things off + * + * Has to be overwritten by each container class + * + * @access public + * @return bool + */ + function close() + { + return true; + } + + /** + * This function is called by the session handler + * to read the data associated with a given session ID. + * This function must retrieve and return the session data + * for the session identified by $id. + * + * Has to be overwritten by each container class + * + * @param string $id ID of the session + * + * @access public + * @return string + */ + function read($id) + { + return ''; + } + + /** + * This function is called when the session handler + * has session data to save, which usually happens + * at the end of your script + * + * Has to be overwritten by each container class + * + * @param string $id ID of the session + * @param mixed $data The data associated with a given session ID + * + * @access public + * @return bool + */ + function write($id, $data) + { + return true; + } + + /** + * This function is called when a session is destroyed. + * It is responsible for deleting the session and cleaning things up. + * + * Has to be overwritten by each container class + * + * @param string $id ID of the session + * + * @access public + * @return bool + */ + function destroy($id) + { + return true; + } + + /** + * This function copies session data of specified id to specified table + * + * Has to be overwritten by each container class + * + * @param string $targetTable Table to replicate data to + * @param string $id ID of the session + * + * @access public + * @return bool + */ + function replicate($targetTable, $id = null) + { + return true; + } + + /** + * This function is responsible for garbage collection. + * In the case of session handling, it is responsible + * for deleting old, stale sessions that are hanging around. + * The session handler will call this every now and then. + * + * Has to be overwritten by each container class + * + * @param integer $maxlifetime Maximum lifetime + * + * @access public + * @return bool + */ + function gc($maxlifetime) + { + return true; + } + + /** + * Set session save handler + * + * @access public + * @return void + */ + function set() + { + $GLOBALS['HTTP_Session_Container'] =& $this; + session_module_name('user'); + session_set_save_handler('HTTP_Session_Open', + 'HTTP_Session_Close', + 'HTTP_Session_Read', + 'HTTP_Session_Write', + 'HTTP_Session_Destroy', + 'HTTP_Session_GC'); + } + + /** + * Destructor for compatibility with PHP >= 5.0.5 + * + * @access private + * @return void + */ + function __destruct() + { + $GLOBALS['HTTP_Session_Container'] =& $this; + session_write_close(); + } +} + +// Delegate function calls to the object's methods +/** @ignore */ +function HTTP_Session_Open($save_path, $session_name) +{ + return (isset($GLOBALS['HTTP_Session_Container'])) ? $GLOBALS['HTTP_Session_Container']->open($save_path, $session_name) + : false; +} +/** @ignore */ +function HTTP_Session_Close() +{ + return (isset($GLOBALS['HTTP_Session_Container'])) ? $GLOBALS['HTTP_Session_Container']->close() + : false; +} +/** @ignore */ +function HTTP_Session_Read($id) +{ + return (isset($GLOBALS['HTTP_Session_Container'])) ? $GLOBALS['HTTP_Session_Container']->read($id) + : false; +} +/** @ignore */ +function HTTP_Session_Write($id, $data) +{ + return (isset($GLOBALS['HTTP_Session_Container'])) ? $GLOBALS['HTTP_Session_Container']->write($id, $data) + : false; +} +/** @ignore */ +function HTTP_Session_Destroy($id) +{ + return (isset($GLOBALS['HTTP_Session_Container'])) ? $GLOBALS['HTTP_Session_Container']->destroy($id) + : false; +} +/** @ignore */ +function HTTP_Session_GC($maxlifetime) +{ + return (isset($GLOBALS['HTTP_Session_Container'])) ? $GLOBALS['HTTP_Session_Container']->gc($maxlifetime) + : false; +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/HTTP_Session/Session/Container/DB.php b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/DB.php new file mode 100644 index 00000000..3b600731 --- /dev/null +++ b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/DB.php @@ -0,0 +1,364 @@ + + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: DB.php,v 1.7 2007/07/14 12:11:54 troehr Exp $ + * @link http://pear.php.net/package/HTTP_Session + * @since File available since Release 0.4.0 + */ + +require_once 'HTTP/Session/Container.php'; +require_once 'DB.php'; + +/** + * Database container for session data + * + * Create the following table to store session data + * + * CREATE TABLE `sessiondata` ( + * `id` CHAR(32) NOT NULL, + * `expiry` INT UNSIGNED NOT NULL DEFAULT 0, + * `data` TEXT NOT NULL, + * PRIMARY KEY (`id`) + * ); + * + * + * @category HTTP + * @package HTTP_Session + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/HTTP_Session + * @since Class available since Release 0.4.0 + */ +class HTTP_Session_Container_DB extends HTTP_Session_Container +{ + /** + * DB connection object + * + * @var object DB + * @access private + */ + var $db = null; + + /** + * Session data cache id + * + * @var mixed + * @access private + */ + var $crc = false; + + /** + * Constrtuctor method + * + * $options is an array with the options.
+ * The options are: + *
    + *
  • 'dsn' - The DSN string
  • + *
  • 'table' - Table with session data, default is 'sessiondata'
  • + *
  • 'autooptimize' - Boolean, 'true' to optimize + * the table on garbage collection, default is 'false'.
  • + *
+ * + * @param array $options Options + * + * @access public + * @return object + */ + function HTTP_Session_Container_DB($options) + { + $this->_setDefaults(); + if (is_array($options)) { + $this->_parseOptions($options); + } else { + $this->options['dsn'] = $options; + } + } + + /** + * Connect to database by using the given DSN string + * + * @param string $dsn DSN string + * + * @access private + * @return mixed Object on error, otherwise bool + */ + function _connect($dsn) + { + if (is_string($dsn) || is_array($dsn)) { + $this->db = DB::connect($dsn); + } else if (is_object($dsn) && is_a($dsn, "db_common")) { + $this->db = $dsn; + } else if (is_object($dsn) && DB::isError($dsn)) { + return new DB_Error($dsn->code, PEAR_ERROR_DIE); + } else { + return new PEAR_Error("The given dsn was not valid in file " . __FILE__ + . " at line " . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + + } + + if (DB::isError($this->db)) { + return new DB_Error($this->db->code, PEAR_ERROR_DIE); + } + + return true; + } + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['dsn'] = null; + $this->options['table'] = 'sessiondata'; + $this->options['autooptimize'] = false; + } + + /** + * Establish connection to a database + * + * @param string $save_path Save path + * @param string $session_name Session name + * + * @return bool + */ + function open($save_path, $session_name) + { + if (DB::isError($this->_connect($this->options['dsn']))) { + return false; + } else { + return true; + } + } + + /** + * Free resources + * + * @return void + */ + function close() + { + return true; + } + + /** + * Read session data + * + * @param string $id Session id + * + * @return void + */ + function read($id) + { + $query = sprintf("SELECT data FROM %s WHERE id = %s AND expiry >= %d", + $this->options['table'], + $this->db->quoteSmart(md5($id)), + time()); + $result = $this->db->getOne($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + $this->crc = strlen($result) . crc32($result); + return $result; + } + + /** + * Write session data + * + * @param string $id Session id + * @param mixed $data Data + * + * @return bool + */ + function write($id, $data) + { + if ((false !== $this->crc) && + ($this->crc === strlen($data) . crc32($data))) { + // $_SESSION hasn't been touched, no need to update the blob column + $query = sprintf("UPDATE %s SET expiry = %d WHERE id = %s", + $this->options['table'], + time() + ini_get('session.gc_maxlifetime'), + $this->db->quoteSmart(md5($id))); + } else { + // Check if table row already exists + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", + $this->options['table'], + $this->db->quoteSmart(md5($id))); + $result = $this->db->getOne($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + if (0 == intval($result)) { + // Insert new row into table + $query = sprintf("INSERT INTO %s (id, expiry, data) VALUES (%s, %d, %s)", + $this->options['table'], + $this->db->quoteSmart(md5($id)), + time() + ini_get('session.gc_maxlifetime'), + $this->db->quoteSmart($data)); + } else { + // Update existing row + $query = sprintf("UPDATE %s SET expiry = %d, data = %s WHERE id = %s", + $this->options['table'], + time() + ini_get('session.gc_maxlifetime'), + $this->db->quoteSmart($data), + $this->db->quoteSmart(md5($id))); + } + } + $result = $this->db->query($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Destroy session data + * + * @param string $id Session id + * + * @return void + */ + function destroy($id) + { + $query = sprintf("DELETE FROM %s WHERE id = %s", + $this->options['table'], + $this->db->quoteSmart(md5($id))); + $result = $this->db->query($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Replicate session data to table specified in option 'replicateBeforeDestroy' + * + * @param string $targetTable Table to replicate to + * @param string $id Id of record to replicate + * + * @access private + * @return bool + */ + function replicate($targetTable, $id = null) + { + if (is_null($id)) { + $id = HTTP_Session::id(); + } + + // Check if table row already exists + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", + $targetTable, + $this->db->quoteSmart(md5($id))); + $result = $this->db->getOne($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + // Insert new row into dest table + if (0 == intval($result)) { + $query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = %s", + $targetTable, + $this->options['table'], + $this->db->quoteSmart(md5($id))); + + } else { + // Update existing row + $query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id AND src.id = %s", + $targetTable, + $this->options['table'], + $this->db->quoteSmart(md5($id))); + } + + $result = $this->db->query($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Garbage collection + * + * @param int $maxlifetime Maximum lifetime + * + * @return bool + */ + function gc($maxlifetime) + { + $query = sprintf("DELETE FROM %s WHERE expiry < %d", + $this->options['table'], + time()); + $result = $this->db->query($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + if ($this->options['autooptimize']) { + switch($this->db->phptype) { + case 'mysql': + $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); + break; + case 'pgsql': + $query = sprintf("VACUUM %s", $this->options['table']); + break; + default: + $query = null; + break; + } + if (isset($query)) { + $result = $this->db->query($query); + if (DB::isError($result)) { + new DB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + } + } + + return true; + } +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB.php b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB.php new file mode 100644 index 00000000..2a8f54c7 --- /dev/null +++ b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB.php @@ -0,0 +1,364 @@ + + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: MDB.php,v 1.5 2007/07/14 12:11:55 troehr Exp $ + * @link http://pear.php.net/package/HTTP_Session + * @since File available since Release 0.5.0 + */ + +require_once 'HTTP/Session/Container.php'; +require_once 'MDB.php'; + +/** + * Database container for session data + * + * Create the following table to store session data + * + * CREATE TABLE `sessiondata` ( + * `id` CHAR(32) NOT NULL, + * `expiry` INT UNSIGNED NOT NULL DEFAULT 0, + * `data` TEXT NOT NULL, + * PRIMARY KEY (`id`) + * ); + * + * + * @category HTTP + * @package HTTP_Session + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/HTTP_Session + * @since Class available since Release 0.4.0 + */ +class HTTP_Session_Container_MDB extends HTTP_Session_Container +{ + + /** + * MDB connection object + * + * @var object MDB + * @access private + */ + var $db = null; + + /** + * Session data cache id + * + * @var mixed + * @access private + */ + var $crc = false; + + /** + * Constructor method + * + * $options is an array with the options.
+ * The options are: + *
    + *
  • 'dsn' - The DSN string
  • + *
  • 'table' - Table with session data, default is 'sessiondata'
  • + *
  • 'autooptimize' - Boolean, 'true' to optimize + * the table on garbage collection, default is 'false'.
  • + *
+ * + * @param array $options Options + * + * @access public + * @return object + */ + function HTTP_Session_Container_MDB($options) + { + $this->_setDefaults(); + if (is_array($options)) { + $this->_parseOptions($options); + } else { + $this->options['dsn'] = $options; + } + } + + /** + * Connect to database by using the given DSN string + * + * @param string $dsn DSN string + * + * @access private + * @return mixed Object on error, otherwise bool + */ + function _connect($dsn) + { + if (is_string($dsn) || is_array($dsn)) { + $this->db = MDB::connect($dsn); + } else if (is_object($dsn) && is_a($dsn, 'mdb_common')) { + $this->db = $dsn; + } else if (is_object($dsn) && MDB::isError($dsn)) { + return new MDB_Error($dsn->code, PEAR_ERROR_DIE); + } else { + return new PEAR_Error("The given dsn was not valid in file " . __FILE__ + . " at line " . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + + } + + if (MDB::isError($this->db)) { + return new MDB_Error($this->db->code, PEAR_ERROR_DIE); + } + + return true; + } + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['dsn'] = null; + $this->options['table'] = 'sessiondata'; + $this->options['autooptimize'] = false; + } + + /** + * Establish connection to a database + * + * @param string $save_path Save path + * @param string $session_name Session name + * + * @return bool + */ + function open($save_path, $session_name) + { + if (MDB::isError($this->_connect($this->options['dsn']))) { + return false; + } else { + return true; + } + } + + /** + * Free resources + * + * @return bool + */ + function close() + { + return true; + } + + /** + * Read session data + * + * @param string $id Session id + * + * @return mixed + */ + function read($id) + { + $query = sprintf("SELECT data FROM %s WHERE id = %s AND expiry >= %d", + $this->options['table'], + $this->db->getTextValue(md5($id)), + time()); + $result = $this->db->getOne($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + $this->crc = strlen($result) . crc32($result); + return $result; + } + + /** + * Write session data + * + * @param string $id Session id + * @param mixed $data Data + * + * @return bool + */ + function write($id, $data) + { + if ((false !== $this->crc) && + ($this->crc === strlen($data) . crc32($data))) { + // $_SESSION hasn't been touched, no need to update the blob column + $query = sprintf("UPDATE %s SET expiry = %d WHERE id = %s", + $this->options['table'], + time() + ini_get('session.gc_maxlifetime'), + $this->db->getTextValue(md5($id))); + } else { + // Check if table row already exists + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", + $this->options['table'], + $this->db->getTextValue(md5($id))); + $result = $this->db->getOne($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + if (0 == intval($result)) { + // Insert new row into table + $query = sprintf("INSERT INTO %s (id, expiry, data) VALUES (%s, %d, %s)", + $this->options['table'], + $this->db->getTextValue(md5($id)), + time() + ini_get('session.gc_maxlifetime'), + $this->db->getTextValue($data)); + } else { + // Update existing row + $query = sprintf("UPDATE %s SET expiry = %d, data = %s WHERE id = %s", + $this->options['table'], + time() + ini_get('session.gc_maxlifetime'), + $this->db->getTextValue($data), + $this->db->getTextValue(md5($id))); + } + } + $result = $this->db->query($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Destroy session data + * + * @param string $id Session id + * + * @return bool + */ + function destroy($id) + { + $query = sprintf("DELETE FROM %s WHERE id = %s", + $this->options['table'], + $this->db->getTextValue(md5($id))); + $result = $this->db->query($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Replicate session data to table specified in option 'replicateBeforeDestroy' + * + * @param string $targetTable Table to replicate to + * @param string $id Id of record to replicate + * + * @access private + * @return bool + */ + function replicate($targetTable, $id = null) + { + if (is_null($id)) { + $id = HTTP_Session::id(); + } + + // Check if table row already exists + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", + $targetTable, + $this->db->getTextValue(md5($id))); + $result = $this->db->getOne($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + // Insert new row into dest table + if (0 == intval($result)) { + $query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = %s", + $targetTable, + $this->options['table'], + $this->db->getTextValue(md5($id))); + } else { + // Update existing row + $query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id AND src.id = %s", + $targetTable, + $this->options['table'], + $this->db->getTextValue(md5($id))); + } + + $result = $this->db->query($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Garbage collection + * + * @param int $maxlifetime Maximum lifetime + * + * @return bool + */ + function gc($maxlifetime) + { + $query = sprintf("DELETE FROM %s WHERE expiry < %d", + $this->options['table'], + time()); + $result = $this->db->query($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + if ($this->options['autooptimize']) { + switch($this->db->phptype) { + case 'mysql': + $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); + break; + case 'pgsql': + $query = sprintf("VACUUM %s", $this->options['table']); + break; + default: + $query = null; + break; + } + if (isset($query)) { + $result = $this->db->query($query); + if (MDB::isError($result)) { + new MDB_Error($result->code, PEAR_ERROR_DIE); + return false; + } + } + } + + return true; + } +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB2.php b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB2.php new file mode 100644 index 00000000..e36616ba --- /dev/null +++ b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/MDB2.php @@ -0,0 +1,364 @@ + + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: MDB2.php,v 1.5 2007/07/14 12:11:55 troehr Exp $ + * @link http://pear.php.net/package/HTTP_Session + * @since File available since Release 0.5.0 + */ + +require_once 'HTTP/Session/Container.php'; +require_once 'MDB2.php'; + +/** + * Database container for session data + * + * Create the following table to store session data + * + * CREATE TABLE `sessiondata` ( + * `id` CHAR(32) NOT NULL, + * `expiry` INT UNSIGNED NOT NULL DEFAULT 0, + * `data` TEXT NOT NULL, + * PRIMARY KEY (`id`) + * ); + * + * + * @category HTTP + * @package HTTP_Session + * @author David Costa + * @author Michael Metz + * @author Stefan Neufeind + * @author Torsten Roehr + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/HTTP_Session + * @since Class available since Release 0.5.0 + */ +class HTTP_Session_Container_MDB2 extends HTTP_Session_Container +{ + /** + * MDB2 connection object + * + * @var object MDB2 + * @access private + */ + var $db = null; + + /** + * Session data cache id + * + * @var mixed + * @access private + */ + var $crc = false; + + /** + * Constructor method + * + * $options is an array with the options.
+ * The options are: + *
    + *
  • 'dsn' - The DSN string
  • + *
  • 'table' - Table with session data, default is 'sessiondata'
  • + *
  • 'autooptimize' - Boolean, 'true' to optimize + * the table on garbage collection, default is 'false'.
  • + *
+ * + * @param array $options Options + * + * @access public + * @return void + */ + function HTTP_Session_Container_MDB2($options) + { + $this->_setDefaults(); + if (is_array($options)) { + $this->_parseOptions($options); + } else { + $this->options['dsn'] = $options; + } + } + + /** + * Connect to database by using the given DSN string + * + * @param string $dsn DSN string + * + * @access private + * @return mixed Object on error, otherwise bool + */ + function _connect($dsn) + { + if (is_string($dsn) || is_array($dsn)) { + $this->db = MDB2::connect($dsn); + } else if (is_object($dsn) && is_a($dsn, 'MDB2_Driver_Common')) { + $this->db = $dsn; + } else if (is_object($dsn) && MDB2::isError($dsn)) { + return $dsn; + } else { + return new PEAR_Error("The given dsn was not valid in file " . __FILE__ + . " at line " . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + + } + + if (MDB2::isError($this->db)) { + return new MDB2_Error($this->db->code, PEAR_ERROR_DIE); + } + + return true; + } + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['dsn'] = null; + $this->options['table'] = 'sessiondata'; + $this->options['autooptimize'] = false; + } + + /** + * Establish connection to a database + * + * @param string $save_path Save path + * @param string $session_name Session name + * + * @return bool + */ + function open($save_path, $session_name) + { + if (MDB2::isError($this->_connect($this->options['dsn']))) { + return false; + } else { + return true; + } + } + + /** + * Free resources + * + * @return bool + */ + function close() + { + return true; + } + + /** + * Read session data + * + * @param string $id Session id + * + * @return mixed + */ + function read($id) + { + $query = sprintf("SELECT data FROM %s WHERE id = %s AND expiry >= %d", + $this->options['table'], + $this->db->quote(md5($id), 'text'), + time()); + $result = $this->db->queryOne($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + $this->crc = strlen($result) . crc32($result); + return $result; + } + + /** + * Write session data + * + * @param string $id Session id + * @param mixed $data Data + * + * @return bool + */ + function write($id, $data) + { + if ((false !== $this->crc) && + ($this->crc === strlen($data) . crc32($data))) { + // $_SESSION hasn't been touched, no need to update the blob column + $query = sprintf("UPDATE %s SET expiry = %d WHERE id = %s", + $this->options['table'], + time() + ini_get('session.gc_maxlifetime'), + $this->db->quote(md5($id), 'text')); + } else { + // Check if table row already exists + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", + $this->options['table'], + $this->db->quote(md5($id), 'text')); + $result = $this->db->queryOne($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + if (0 == intval($result)) { + // Insert new row into table + $query = sprintf("INSERT INTO %s (id, expiry, data) VALUES (%s, %d, %s)", + $this->options['table'], + $this->db->quote(md5($id), 'text'), + time() + ini_get('session.gc_maxlifetime'), + $this->db->quote($data, 'text')); + } else { + // Update existing row + $query = sprintf("UPDATE %s SET expiry = %d, data = %s WHERE id = %s", + $this->options['table'], + time() + ini_get('session.gc_maxlifetime'), + $this->db->quote($data, 'text'), + $this->db->quote(md5($id), 'text')); + } + } + $result = $this->db->query($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Destroy session data + * + * @param string $id Session id + * + * @return bool + */ + function destroy($id) + { + $query = sprintf("DELETE FROM %s WHERE id = %s", + $this->options['table'], + $this->db->quote(md5($id), 'text')); + $result = $this->db->query($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Replicate session data to table specified in option 'replicateBeforeDestroy' + * + * @param string $targetTable Table to replicate to + * @param string $id Id of record to replicate + * + * @access private + * @return bool + */ + function replicate($targetTable, $id = null) + { + if (is_null($id)) { + $id = HTTP_Session::id(); + } + + // Check if table row already exists + $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = %s", + $targetTable, + $this->db->quote(md5($id), 'text')); + $result = $this->db->queryOne($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + + // Insert new row into dest table + if (0 == intval($result)) { + $query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = %s", + $targetTable, + $this->options['table'], + $this->db->quote(md5($id), 'text')); + + } else { + // Update existing row + $query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id AND src.id = %s", + $targetTable, + $this->options['table'], + $this->db->quote(md5($id), 'text')); + } + + $result = $this->db->query($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + + return true; + } + + /** + * Garbage collection + * + * @param int $maxlifetime Maximum lifetime + * + * @return bool + */ + function gc($maxlifetime) + { + $query = sprintf("DELETE FROM %s WHERE expiry < %d", + $this->options['table'], + time()); + $result = $this->db->query($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + if ($this->options['autooptimize']) { + switch($this->db->phptype) { + case 'mysql': + $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); + break; + case 'pgsql': + $query = sprintf("VACUUM %s", $this->options['table']); + break; + default: + $query = null; + break; + } + if (isset($query)) { + $result = $this->db->query($query); + if (MDB2::isError($result)) { + $this->db->raiseError($result->code, PEAR_ERROR_DIE); + return false; + } + } + } + + return true; + } +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/HTTP_Session/Session/Container/Memcache.php b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/Memcache.php new file mode 100644 index 00000000..d2889ffc --- /dev/null +++ b/oss/vtiger/trunk/include/HTTP_Session/Session/Container/Memcache.php @@ -0,0 +1,202 @@ + + * @author Torsten Roehr + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Memcache.php,v 1.3 2007/07/14 12:11:55 troehr Exp $ + * @link http://pear.php.net/package/HTTP_Session + * @since File available since Release 0.5.6 + */ + +require_once 'HTTP/Session/Container.php'; + +/** + * Database container for session data + * + * @category HTTP + * @package HTTP_Session + * @author Chad Wagner + * @author Torsten Roehr + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/HTTP_Session + * @since Class available since Release 0.5.6 + */ +class HTTP_Session_Container_Memcache extends HTTP_Session_Container +{ + /** + * Memcache connection object + * + * @var object Memcache + * @access private + */ + var $mc; + + /** + * Constructor method + * + * $options is an array with the options.
+ * The options are: + *
    + *
  • 'memcache' - Memcache object + *
  • 'prefix' - Key prefix, default is 'sessiondata:'
  • + *
+ * + * @param array $options Options + * + * @access public + * @return object + */ + function HTTP_Session_Container_Memcache($options) + { + $this->_setDefaults(); + + if (is_array($options)) { + $this->_parseOptions($options); + } + } + + /** + * Connect to database by using the given DSN string + * + * @param string $mc Memcache object + * + * @access private + * @return mixed Object on error, otherwise bool + */ + function _connect($mc) + { + if (is_object($mc) && is_a($mc, 'Memcache')) { + $this->mc = $mc; + + } else { + + return new PEAR_Error('The given memcache object was not valid in file ' + . __FILE__ . ' at line ' . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + } + + return true; + } + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['prefix'] = 'sessiondata:'; + $this->options['memcache'] = null; + } + + /** + * Establish connection to a database + * + * @param string $save_path Save path + * @param string $session_name Session name + * + * @access public + * @return mixed Object on error, otherwise bool + */ + function open($save_path, $session_name) + { + return $this->_connect($this->options['memcache']); + } + + /** + * Free resources + * + * @access public + * @return bool + */ + function close() + { + return true; + } + + /** + * Read session data + * + * @param string $id Session id + * + * @access public + * @return mixed + */ + function read($id) + { + $result = $this->mc->get($this->options['prefix'] . $id); + return $result; + } + + /** + * Write session data + * + * @param string $id Session id + * @param mixed $data Session data + * + * @access public + * @return bool + */ + function write($id, $data) + { + $this->mc->set($this->options['prefix'] . $id, + $data, + MEMCACHE_COMPRESSED, + time() + ini_get('session.gc_maxlifetime')); + + return true; + } + + /** + * Destroy session data + * + * @param string $id Session id + * + * @access public + * @return bool + */ + function destroy($id) + { + $this->mc->delete($this->options['prefix'] . $id); + return true; + } + + /** + * Garbage collection + * + * @param int $maxlifetime Maximum lifetime + * + * @access public + * @return bool + */ + function gc($maxlifetime) + { + return true; + } +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/ListView/ListView.php b/oss/vtiger/trunk/include/ListView/ListView.php new file mode 100644 index 00000000..25bc7ae8 --- /dev/null +++ b/oss/vtiger/trunk/include/ListView/ListView.php @@ -0,0 +1,288 @@ +debug("Entering ListView() method ..."); + + + if(!$this->initialized){ + global $list_max_entries_per_page; + $this->records_per_page = $list_max_entries_per_page + 0; + $this->initialized = true; + global $theme, $app_strings, $image_path, $currentModule; + $this->local_theme = $theme; + $this->local_app_strings = &$app_strings; + $this->local_image_path = $image_path; + $this->local_current_module = $currentModule; + + if(empty($this->local_image_path)){ + $this->local_image_path = 'themes/'.$theme.'/images'; + } + $this->log = LoggerManager::getLogger('listView_'.$this->local_current_module); + $log->debug("Exiting ListView method ..."); + } +} +/**sets the header title */ + function setHeaderTitle($value){ + global $log; + $log->debug("Entering setHeaderTitle(".$value.") method ..."); + $this->header_title = $value; + $log->debug("Exiting setHeaderTitle method ..."); +} +/**sets the header text this is text thats appended to the header vtiger_table and is usually used for the creation of buttons + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setHeaderText($value){ + global $log; + $log->debug("Entering setHeaderText(".$value.") method ..."); + $this->header_text = $value; + $log->debug("Exiting setHeaderText method ..."); +} +/**sets the parameters dealing with the db + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setQuery($where, $limit, $orderBy, $varName, $allowOrderByOveride= true){ + global $log; + $log->debug("Entering setQuery(".$where.",". $limit.",". $orderBy.",". $varName.",". $allowOrderByOveride.") method ..."); + $this->query_where = $where; + if($this->getSessionVariable("query", "where") != $where){ + $this->querey_where_has_changed = true; + $this->setSessionVariable("query", "where", $where); + } + + $this->query_limit = $limit; + if(!$allowOrderByOveride){ + $this->query_orderby = $orderBy; + $log->debug("Exiting setQuery method ..."); + return; + } + $sortBy = $this->getSessionVariable($varName, "ORDER_BY") ; + + if(empty($sortBy)){ + $this->setUserVariable($varName, "ORDER_BY", $orderBy); + $sortBy = $orderBy; + }else{ + $this->setUserVariable($varName, "ORDER_BY", $sortBy); + } + if($sortBy == 'amount'){ + $sortBy = 'amount*1'; + } + + $desc = false; + $desc = $this->getSessionVariable($varName, $sortBy."_desc"); + + if(empty($desc)) + $desc = false; + if(isset($_REQUEST[$this->getSessionVariableName($varName, "ORDER_BY")])) + $last = $this->getSessionVariable($varName, "ORDER_BY_LAST"); + if(!empty($last) && $last == $sortBy){ + $desc = !$desc; + }else { + $this->setSessionVariable($varName, "ORDER_BY_LAST", $sortBy); + } + $this->setSessionVariable($varName, $sortBy."_desc", $desc); + if(!empty($sortBy)){ + if(substr_count(strtolower($sortBy), ' desc') == 0 && substr_count(strtolower($sortBy), ' asc') == 0){ + if($desc){ + $this->query_orderby = $sortBy.' desc'; + }else{ + $this->query_orderby = $sortBy.' asc'; + } + } + else{ + $this->query_orderby = $sortBy; + } + }else { + $this->query_orderby = ""; + } + $log->debug("Exiting setQuery method ..."); + + + + +} + +/**sets the theme used only use if it is different from the global + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setTheme($theme){ + global $log; + $log->debug("Entering setTheme(".$theme.") method ..."); + $this->local_theme = $theme; + if(isset($this->xTemplate))$this->xTemplate->assign("THEME", $this->local_theme ); + $log->debug("Exiting setTheme method ..."); +} + +/**sets the AppStrings used only use if it is different from the global + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setAppStrings(&$app_strings){ + global $log; + $log->debug("Entering setAppStrings(".$app_strings.") method ..."); + unset($this->local_app_strings); + $this->local_app_strings = $app_strings; + if(isset($this->xTemplate))$this->xTemplate->assign("APP", $this->local_app_strings ); + $log->debug("Exiting setAppStrings method ..."); +} + +/**sets the ModStrings used + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setModStrings(&$mod_strings){ + global $log; + $log->debug("Entering setModStrings(".$mod_strings.") method ..."); + unset($this->local_module_strings); + $this->local_mod_strings = $mod_strings; + if(isset($this->xTemplate))$this->xTemplate->assign("MOD", $this->local_mod_strings ); + $log->debug("Exiting setModStrings method ..."); +} + +/**sets the ImagePath used + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setImagePath($image_path){ + global $log; + $log->debug("Entering setImagePath(".$image_path.") method ..."); + $this->local_image_path = $image_path; + if(empty($this->local_image_path)){ + $this->local_image_path = 'themes/'.$this->local_theme.'/images'; + } + if(isset($this->xTemplate))$this->xTemplate->assign("IMAGE_PATH", $this->local_image_path ); + $log->debug("Exiting setImagePath method ..."); +} + +/**sets the currentModule only use if this is different from the global + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setCurrentModule($currentModule){ + global $log; + $log->debug("Entering setCurrentModule(".$currentModule.") method ..."); + unset($this->local_current_module); + $this->local_current_module = $currentModule; + $this->log = LoggerManager::getLogger('listView_'.$this->local_current_module); + if(isset($this->xTemplate))$this->xTemplate->assign("MODULE_NAME", $this->local_current_module ); + $log->debug("Exiting setCurrentModule method ..."); + +} + + +/**INTERNAL FUNCTION sets a session variable + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function setSessionVariable($localVarName,$varName, $value){ + global $log; + $log->debug("Entering setSessionVariable(".$localVarName.",".$varName.",". $value.") method ..."); + $_SESSION[$this->local_current_module."_".$localVarName."_".$varName] = $value; + $log->debug("Exiting setSessionVariable method ..."); +} + +function setUserVariable($localVarName,$varName, $value){ + global $log; + $log->debug("Entering setUserVariable(".$localVarName.",".$varName.",". $value.") method ..."); + global $current_user; + $current_user->setPreference($this->local_current_module."_".$localVarName."_".$varName, $value); + $log->debug("Exiting setUserVariable method ..."); +} + +/**INTERNAL FUNCTION returns a session variable first checking the querey for it then checking the session + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________. +*/ + function getSessionVariable($localVarName,$varName){ + global $log; + $log->debug("Entering getSessionVariable(".$localVarName.",".$varName.") method ..."); + if(isset($_REQUEST[$this->getSessionVariableName($localVarName, $varName)])){ + $this->setSessionVariable($localVarName,$varName,vtlib_purify($_REQUEST[$this->getSessionVariableName($localVarName, $varName)])); + } + if(isset($_SESSION[$this->getSessionVariableName($localVarName, $varName)])){ + $log->debug("Exiting getSessionVariable method ..."); + return $_SESSION[$this->getSessionVariableName($localVarName, $varName)]; + } + $log->debug("Exiting getSessionVariable method ..."); + return ""; +} + +/** + + * @return void + * @param unknown $localVarName + * @param unknown $varName + * @desc INTERNAL FUNCTION returns the session/query variable name + * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. + * All Rights Reserved. + * Contributor(s): ______________________________________.. + */ +function getSessionVariableName($localVarName,$varName){ + global $log; + $log->debug("Entering getSessionVariableName(".$localVarName.",".$varName.") method ..."); + $log->debug("Exiting getSessionVariableName method ..."); + return $this->local_current_module."_".$localVarName."_".$varName; +} + + +} + +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/ListView/ListViewPagging.php b/oss/vtiger/trunk/include/ListView/ListViewPagging.php new file mode 100644 index 00000000..3582b369 --- /dev/null +++ b/oss/vtiger/trunk/include/ListView/ListViewPagging.php @@ -0,0 +1,78 @@ +getViewId($currentModule); +$customview_html = $customView->getCustomViewCombo($viewid); +$viewinfo = $customView->getCustomViewByCvid($viewid); + +if($viewid != "0"&& $viewid != 0){ + $listquery = getListQuery($currentModule); + $list_query= $customView->getModifiedCvListQuery($viewid, $listquery, $currentModule); +}else{ + $list_query = getListQuery($currentModule); +} + +// Enabling Module Search +$url_string = ''; +if($_REQUEST['query'] == 'true') { + if(!empty($_REQUEST['globalSearch'])){ + $searchValue = vtlib_purify($_REQUEST['globalSearchText']); + $where = '(' . getUnifiedWhere($list_query,$currentModule,$searchValue) . ')'; + $url_string .= '&query=true&globalSearch=true&globalSearchText='.$searchValue; + }else{ + list($where, $ustring) = split('#@@#', getWhereCondition($currentModule)); + $url_string .= "&query=true$ustring"; + } +} +//print_r($where); +if($where != '') { + $list_query = "$list_query AND $where"; + $_SESSION['export_where'] = $where; +}else{ + unset($_SESSION['export_where']); +} +// Sorting +if($order_by) { + if($order_by == 'smownerid'){ + if( $adb->dbType == "pgsql"){ + $list_query .= ' GROUP BY user_name'; + } + $list_query .= ' ORDER BY user_name '.$sorder; + }else { + $tablename = getTableNameForField($currentModule, $order_by); + $tablename = ($tablename != '')? ($tablename . '.') : ''; + if( $adb->dbType == "pgsql"){ + $list_query .= ' GROUP BY '. $tablename . $order_by; + } + $list_query .= ' ORDER BY ' . $tablename . $order_by . ' ' . $sorder; + } +} + +$count_result = $adb->query(mkCountQuery($list_query)); +$noofrows = $adb->query_result($count_result,0,"count"); + +$pageNumber = ceil($noofrows/$list_max_entries_per_page); +if($pageNumber == 0){ + $pageNumber = 1; +} +echo $app_strings['LBL_LIST_OF'].' '.$pageNumber; +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/ListView/ListViewSession.php b/oss/vtiger/trunk/include/ListView/ListViewSession.php new file mode 100644 index 00000000..1efe1fd3 --- /dev/null +++ b/oss/vtiger/trunk/include/ListView/ListViewSession.php @@ -0,0 +1,199 @@ +debug("Entering ListViewSession() method ..."); + + $this->module = $currentModule; + $this->sortby = 'ASC'; + $this->start =1; + } + + function getCurrentPage($currentModule,$viewId){ + if(!empty($_SESSION['lvs'][$currentModule][$viewId]['start'])){ + return $_SESSION['lvs'][$currentModule][$viewId]['start']; + } + return 1; + } + + function getRequestStartPage(){ + $start = $_REQUEST['start']; + if(!is_numeric($start)){ + $start = 1; + } + if($start < 1){ + $start = 1; + } + $start = ceil($start); + return $start; + } + + function getListViewNavigation($currentRecordId){ + global $currentModule,$current_user,$adb,$log,$list_max_entries_per_page; + Zend_Json::$useBuiltinEncoderDecoder = true; + $reUseData = false; + $displayBufferRecordCount = 10; + $bufferRecordCount = 15; + if($currentModule == 'Documents'){ + $sql = "select folderid from vtiger_notes where notesid=?"; + $params = array($currentRecordId); + $result = $adb->pquery($sql,$params); + $folderId = $adb->query_result($result,0,'folderid'); + } + $cv = new CustomView(); + $viewId = $cv->getViewId($currentModule); + if(!empty($_SESSION[$currentModule.'_DetailView_Navigation'.$viewId])){ + $recordNavigationInfo = Zend_Json::decode($_SESSION[$currentModule.'_DetailView_Navigation'.$viewId]); + $pageNumber =0; + if(count($recordNavigationInfo) == 1){ + foreach ($recordNavigationInfo as $recordIdList) { + if(in_array($currentRecordId,$recordIdList)){ + $reUseData = true; + } + } + }else{ + $recordList = array(); + $recordPageMapping = array(); + foreach ($recordNavigationInfo as $start=>$recordIdList){ + foreach ($recordIdList as $index=>$recordId) { + $recordList[] = $recordId; + $recordPageMapping[$recordId] = $start; + if($recordId == $currentRecordId){ + $searchKey = count($recordList)-1; + } + } + } + if($searchKey > $displayBufferRecordCount -1 && $searchKey < count($recordList)-$displayBufferRecordCount){ + $reUseData= true; + } + } + } + + if($reUseData === false){ + $recordNavigationInfo = array(); + if(!empty($_REQUEST['start'])){ + $start = ListViewSession::getRequestStartPage(); + }else{ + $start = ListViewSession::getCurrentPage($currentModule,$viewId); + } + $startRecord = (($start - 1) * $list_max_entries_per_page) - $bufferRecordCount; + if($startRecord < 0){ + $startRecord = 0; + } + + $list_query = $_SESSION[$currentModule.'_listquery']; + if($currentModule=='Documents' && !empty($folderId)){ + $list_query = preg_replace("/[\n\r\s]+/"," ",$list_query); + $findOrderByPosition = stripos($list_query,' ORDER BY '); + if ($findOrderByPosition > 0) { + $orderByClause = substr($list_query, $findOrderByPosition, strlen($list_query)); + $list_query = substr($list_query, 0, $findOrderByPosition). " AND vtiger_notes.folderid=$folderId " . $orderByClause; + } else { + $list_query .= " AND vtiger_notes.folderid=$folderId"; + } + } + if($start !=1){ + $recordCount = ($list_max_entries_per_page+2 * $bufferRecordCount); + }else{ + $recordCount = ($list_max_entries_per_page+ $bufferRecordCount); + } + if( $adb->dbType == "pgsql"){ + $list_query .= " OFFSET $startRecord LIMIT $recordCount"; + }else{ + $list_query .= " LIMIT $startRecord, $recordCount"; + } + + $resultAllCRMIDlist_query=$adb->pquery($list_query,array()); + $navigationRecordList = array(); + while($forAllCRMID = $adb->fetch_array($resultAllCRMIDlist_query)) { + $navigationRecordList[] = $forAllCRMID['crmid']; + } + + $pageCount = 0; + $current = $start; + if($start ==1){ + $firstPageRecordCount = $list_max_entries_per_page; + }else{ + $firstPageRecordCount = $bufferRecordCount; + $current -=1; + } + + $searchKey = array_search($currentRecordId,$navigationRecordList); + $recordNavigationInfo = array(); + if($searchKey !== false){ + foreach ($navigationRecordList as $index => $recordId) { + if(!is_array($recordNavigationInfo[$current])){ + $recordNavigationInfo[$current] = array(); + } + if($index == $firstPageRecordCount || $index == ($firstPageRecordCount+$pageCount * $list_max_entries_per_page)){ + $current++; + $pageCount++; + } + $recordNavigationInfo[$current][] = $recordId; + } + } + $_SESSION[$currentModule.'_DetailView_Navigation'.$viewId] = + Zend_Json::encode($recordNavigationInfo); + } + return $recordNavigationInfo; + } + + function getRequestCurrentPage($currentModule, $query, $viewid, $queryMode = false) { + global $list_max_entries_per_page, $adb; + $start = 1; + if(isset($_REQUEST['query']) && $_REQUEST['query'] == 'true'){ + return ListViewSession::getRequestStartPage(); + } + if(!empty($_REQUEST['start'])){ + $start = $_REQUEST['start']; + if($start == 'last'){ + $count_result = $adb->query( mkCountQuery( $query)); + $noofrows = $adb->query_result($count_result,0,"count"); + if($noofrows > 0){ + $start = ceil($noofrows/$list_max_entries_per_page); + } + } + if(!is_numeric($start)){ + $start = 1; + }elseif($start < 1){ + $start = 1; + } + $start = ceil($start); + }else if(!empty($_SESSION['lvs'][$currentModule][$viewid]['start'])){ + $start = $_SESSION['lvs'][$currentModule][$viewid]['start']; + } + if(!$queryMode) { + $_SESSION['lvs'][$currentModule][$viewid]['start'] = intval($start); + } + return $start; + } + +} +?> \ No newline at end of file diff --git a/oss/vtiger/trunk/include/fpdf/font/courier.php b/oss/vtiger/trunk/include/fpdf/font/courier.php new file mode 100644 index 00000000..4c009f39 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/courier.php @@ -0,0 +1,7 @@ + diff --git a/oss/vtiger/trunk/include/fpdf/font/helvetica.php b/oss/vtiger/trunk/include/fpdf/font/helvetica.php new file mode 100644 index 00000000..8fa7683e --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/helvetica.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, + 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, + chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, + chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/helveticab.php b/oss/vtiger/trunk/include/fpdf/font/helveticab.php new file mode 100644 index 00000000..a8473c94 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/helveticab.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, + 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, + 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, + chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, + chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/helveticabi.php b/oss/vtiger/trunk/include/fpdf/font/helveticabi.php new file mode 100644 index 00000000..41379537 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/helveticabi.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, + 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, + 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, + chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, + chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/helveticai.php b/oss/vtiger/trunk/include/fpdf/font/helveticai.php new file mode 100644 index 00000000..d5bb6e09 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/helveticai.php @@ -0,0 +1,15 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, + 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, + chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, + chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/symbol.php b/oss/vtiger/trunk/include/fpdf/font/symbol.php new file mode 100644 index 00000000..b556ed84 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/symbol.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>713,'#'=>500,'$'=>549,'%'=>833,'&'=>778,'\''=>439,'('=>333,')'=>333,'*'=>500,'+'=>549, + ','=>250,'-'=>549,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>549,'='=>549,'>'=>549,'?'=>444,'@'=>549,'A'=>722, + 'B'=>667,'C'=>722,'D'=>612,'E'=>611,'F'=>763,'G'=>603,'H'=>722,'I'=>333,'J'=>631,'K'=>722,'L'=>686,'M'=>889,'N'=>722,'O'=>722,'P'=>768,'Q'=>741,'R'=>556,'S'=>592,'T'=>611,'U'=>690,'V'=>439,'W'=>768, + 'X'=>645,'Y'=>795,'Z'=>611,'['=>333,'\\'=>863,']'=>333,'^'=>658,'_'=>500,'`'=>500,'a'=>631,'b'=>549,'c'=>549,'d'=>494,'e'=>439,'f'=>521,'g'=>411,'h'=>603,'i'=>329,'j'=>603,'k'=>549,'l'=>549,'m'=>576, + 'n'=>521,'o'=>549,'p'=>549,'q'=>521,'r'=>549,'s'=>603,'t'=>439,'u'=>576,'v'=>713,'w'=>686,'x'=>493,'y'=>686,'z'=>494,'{'=>480,'|'=>200,'}'=>480,'~'=>549,chr(127)=>0,chr(128)=>0,chr(129)=>0,chr(130)=>0,chr(131)=>0, + chr(132)=>0,chr(133)=>0,chr(134)=>0,chr(135)=>0,chr(136)=>0,chr(137)=>0,chr(138)=>0,chr(139)=>0,chr(140)=>0,chr(141)=>0,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, + chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>750,chr(161)=>620,chr(162)=>247,chr(163)=>549,chr(164)=>167,chr(165)=>713,chr(166)=>500,chr(167)=>753,chr(168)=>753,chr(169)=>753,chr(170)=>753,chr(171)=>1042,chr(172)=>987,chr(173)=>603,chr(174)=>987,chr(175)=>603, + chr(176)=>400,chr(177)=>549,chr(178)=>411,chr(179)=>549,chr(180)=>549,chr(181)=>713,chr(182)=>494,chr(183)=>460,chr(184)=>549,chr(185)=>549,chr(186)=>549,chr(187)=>549,chr(188)=>1000,chr(189)=>603,chr(190)=>1000,chr(191)=>658,chr(192)=>823,chr(193)=>686,chr(194)=>795,chr(195)=>987,chr(196)=>768,chr(197)=>768, + chr(198)=>823,chr(199)=>768,chr(200)=>768,chr(201)=>713,chr(202)=>713,chr(203)=>713,chr(204)=>713,chr(205)=>713,chr(206)=>713,chr(207)=>713,chr(208)=>768,chr(209)=>713,chr(210)=>790,chr(211)=>790,chr(212)=>890,chr(213)=>823,chr(214)=>549,chr(215)=>250,chr(216)=>713,chr(217)=>603,chr(218)=>603,chr(219)=>1042, + chr(220)=>987,chr(221)=>603,chr(222)=>987,chr(223)=>603,chr(224)=>494,chr(225)=>329,chr(226)=>790,chr(227)=>790,chr(228)=>786,chr(229)=>713,chr(230)=>384,chr(231)=>384,chr(232)=>384,chr(233)=>384,chr(234)=>384,chr(235)=>384,chr(236)=>494,chr(237)=>494,chr(238)=>494,chr(239)=>494,chr(240)=>0,chr(241)=>329, + chr(242)=>274,chr(243)=>686,chr(244)=>686,chr(245)=>686,chr(246)=>384,chr(247)=>384,chr(248)=>384,chr(249)=>384,chr(250)=>384,chr(251)=>384,chr(252)=>494,chr(253)=>494,chr(254)=>494,chr(255)=>0); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/times.php b/oss/vtiger/trunk/include/fpdf/font/times.php new file mode 100644 index 00000000..b9be1b21 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/times.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>408,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>180,'('=>333,')'=>333,'*'=>500,'+'=>564, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>564,'='=>564,'>'=>564,'?'=>444,'@'=>921,'A'=>722, + 'B'=>667,'C'=>667,'D'=>722,'E'=>611,'F'=>556,'G'=>722,'H'=>722,'I'=>333,'J'=>389,'K'=>722,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>556,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>722,'W'=>944, + 'X'=>722,'Y'=>722,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>469,'_'=>500,'`'=>333,'a'=>444,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, + 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>333,'s'=>389,'t'=>278,'u'=>500,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>480,'|'=>200,'}'=>480,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>444,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>889,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>444,chr(148)=>444,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>980, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>200,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>564,chr(173)=>333,chr(174)=>760,chr(175)=>333, + chr(176)=>400,chr(177)=>564,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>453,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>444,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>564,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>722,chr(222)=>556,chr(223)=>500,chr(224)=>444,chr(225)=>444,chr(226)=>444,chr(227)=>444,chr(228)=>444,chr(229)=>444,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>564,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>500,chr(254)=>500,chr(255)=>500); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/timesb.php b/oss/vtiger/trunk/include/fpdf/font/timesb.php new file mode 100644 index 00000000..c3eb9fa7 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/timesb.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>555,'#'=>500,'$'=>500,'%'=>1000,'&'=>833,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>930,'A'=>722, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>778,'I'=>389,'J'=>500,'K'=>778,'L'=>667,'M'=>944,'N'=>722,'O'=>778,'P'=>611,'Q'=>778,'R'=>722,'S'=>556,'T'=>667,'U'=>722,'V'=>722,'W'=>1000, + 'X'=>722,'Y'=>722,'Z'=>667,'['=>333,'\\'=>278,']'=>333,'^'=>581,'_'=>500,'`'=>333,'a'=>500,'b'=>556,'c'=>444,'d'=>556,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>333,'k'=>556,'l'=>278,'m'=>833, + 'n'=>556,'o'=>500,'p'=>556,'q'=>556,'r'=>444,'s'=>389,'t'=>333,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>394,'|'=>220,'}'=>394,'~'=>520,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>667,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>300,chr(171)=>500,chr(172)=>570,chr(173)=>333,chr(174)=>747,chr(175)=>333, + chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>556,chr(182)=>540,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>330,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>570,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>722,chr(222)=>611,chr(223)=>556,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/timesbi.php b/oss/vtiger/trunk/include/fpdf/font/timesbi.php new file mode 100644 index 00000000..161f6302 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/timesbi.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>389,'"'=>555,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>832,'A'=>667, + 'B'=>667,'C'=>667,'D'=>722,'E'=>667,'F'=>667,'G'=>722,'H'=>778,'I'=>389,'J'=>500,'K'=>667,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>611,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>667,'W'=>889, + 'X'=>667,'Y'=>611,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>570,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, + 'n'=>556,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>556,'v'=>444,'w'=>667,'x'=>500,'y'=>444,'z'=>389,'{'=>348,'|'=>220,'}'=>348,'~'=>570,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>389,chr(159)=>611,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>266,chr(171)=>500,chr(172)=>606,chr(173)=>333,chr(174)=>747,chr(175)=>333, + chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>576,chr(182)=>500,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>300,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>944,chr(199)=>667,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>570,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>611,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>444,chr(254)=>500,chr(255)=>444); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/timesi.php b/oss/vtiger/trunk/include/fpdf/font/timesi.php new file mode 100644 index 00000000..de171fd1 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/timesi.php @@ -0,0 +1,15 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>420,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>214,'('=>333,')'=>333,'*'=>500,'+'=>675, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>675,'='=>675,'>'=>675,'?'=>500,'@'=>920,'A'=>611, + 'B'=>611,'C'=>667,'D'=>722,'E'=>611,'F'=>611,'G'=>722,'H'=>722,'I'=>333,'J'=>444,'K'=>667,'L'=>556,'M'=>833,'N'=>667,'O'=>722,'P'=>611,'Q'=>722,'R'=>611,'S'=>500,'T'=>556,'U'=>722,'V'=>611,'W'=>833, + 'X'=>611,'Y'=>556,'Z'=>556,'['=>389,'\\'=>278,']'=>389,'^'=>422,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>278,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>444,'l'=>278,'m'=>722, + 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>500,'v'=>444,'w'=>667,'x'=>444,'y'=>444,'z'=>389,'{'=>400,'|'=>275,'}'=>400,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>556,chr(133)=>889,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>500,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>556,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>556,chr(148)=>556,chr(149)=>350,chr(150)=>500,chr(151)=>889,chr(152)=>333,chr(153)=>980, + chr(154)=>389,chr(155)=>333,chr(156)=>667,chr(157)=>350,chr(158)=>389,chr(159)=>556,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>275,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>675,chr(173)=>333,chr(174)=>760,chr(175)=>333, + chr(176)=>400,chr(177)=>675,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>523,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>611,chr(193)=>611,chr(194)=>611,chr(195)=>611,chr(196)=>611,chr(197)=>611, + chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>667,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>675,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>556,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>675,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>444,chr(254)=>500,chr(255)=>444); +?> diff --git a/oss/vtiger/trunk/include/fpdf/font/zapfdingbats.php b/oss/vtiger/trunk/include/fpdf/font/zapfdingbats.php new file mode 100644 index 00000000..f2bdfd5c --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/font/zapfdingbats.php @@ -0,0 +1,15 @@ +0,chr(1)=>0,chr(2)=>0,chr(3)=>0,chr(4)=>0,chr(5)=>0,chr(6)=>0,chr(7)=>0,chr(8)=>0,chr(9)=>0,chr(10)=>0,chr(11)=>0,chr(12)=>0,chr(13)=>0,chr(14)=>0,chr(15)=>0,chr(16)=>0,chr(17)=>0,chr(18)=>0,chr(19)=>0,chr(20)=>0,chr(21)=>0, + chr(22)=>0,chr(23)=>0,chr(24)=>0,chr(25)=>0,chr(26)=>0,chr(27)=>0,chr(28)=>0,chr(29)=>0,chr(30)=>0,chr(31)=>0,' '=>278,'!'=>974,'"'=>961,'#'=>974,'$'=>980,'%'=>719,'&'=>789,'\''=>790,'('=>791,')'=>690,'*'=>960,'+'=>939, + ','=>549,'-'=>855,'.'=>911,'/'=>933,'0'=>911,'1'=>945,'2'=>974,'3'=>755,'4'=>846,'5'=>762,'6'=>761,'7'=>571,'8'=>677,'9'=>763,':'=>760,';'=>759,'<'=>754,'='=>494,'>'=>552,'?'=>537,'@'=>577,'A'=>692, + 'B'=>786,'C'=>788,'D'=>788,'E'=>790,'F'=>793,'G'=>794,'H'=>816,'I'=>823,'J'=>789,'K'=>841,'L'=>823,'M'=>833,'N'=>816,'O'=>831,'P'=>923,'Q'=>744,'R'=>723,'S'=>749,'T'=>790,'U'=>792,'V'=>695,'W'=>776, + 'X'=>768,'Y'=>792,'Z'=>759,'['=>707,'\\'=>708,']'=>682,'^'=>701,'_'=>826,'`'=>815,'a'=>789,'b'=>789,'c'=>707,'d'=>687,'e'=>696,'f'=>689,'g'=>786,'h'=>787,'i'=>713,'j'=>791,'k'=>785,'l'=>791,'m'=>873, + 'n'=>761,'o'=>762,'p'=>762,'q'=>759,'r'=>759,'s'=>892,'t'=>892,'u'=>788,'v'=>784,'w'=>438,'x'=>138,'y'=>277,'z'=>415,'{'=>392,'|'=>392,'}'=>668,'~'=>668,chr(127)=>0,chr(128)=>390,chr(129)=>390,chr(130)=>317,chr(131)=>317, + chr(132)=>276,chr(133)=>276,chr(134)=>509,chr(135)=>509,chr(136)=>410,chr(137)=>410,chr(138)=>234,chr(139)=>234,chr(140)=>334,chr(141)=>334,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, + chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>0,chr(161)=>732,chr(162)=>544,chr(163)=>544,chr(164)=>910,chr(165)=>667,chr(166)=>760,chr(167)=>760,chr(168)=>776,chr(169)=>595,chr(170)=>694,chr(171)=>626,chr(172)=>788,chr(173)=>788,chr(174)=>788,chr(175)=>788, + chr(176)=>788,chr(177)=>788,chr(178)=>788,chr(179)=>788,chr(180)=>788,chr(181)=>788,chr(182)=>788,chr(183)=>788,chr(184)=>788,chr(185)=>788,chr(186)=>788,chr(187)=>788,chr(188)=>788,chr(189)=>788,chr(190)=>788,chr(191)=>788,chr(192)=>788,chr(193)=>788,chr(194)=>788,chr(195)=>788,chr(196)=>788,chr(197)=>788, + chr(198)=>788,chr(199)=>788,chr(200)=>788,chr(201)=>788,chr(202)=>788,chr(203)=>788,chr(204)=>788,chr(205)=>788,chr(206)=>788,chr(207)=>788,chr(208)=>788,chr(209)=>788,chr(210)=>788,chr(211)=>788,chr(212)=>894,chr(213)=>838,chr(214)=>1016,chr(215)=>458,chr(216)=>748,chr(217)=>924,chr(218)=>748,chr(219)=>918, + chr(220)=>927,chr(221)=>928,chr(222)=>928,chr(223)=>834,chr(224)=>873,chr(225)=>828,chr(226)=>924,chr(227)=>924,chr(228)=>917,chr(229)=>930,chr(230)=>931,chr(231)=>463,chr(232)=>883,chr(233)=>836,chr(234)=>836,chr(235)=>867,chr(236)=>867,chr(237)=>696,chr(238)=>696,chr(239)=>874,chr(240)=>0,chr(241)=>874, + chr(242)=>760,chr(243)=>946,chr(244)=>771,chr(245)=>865,chr(246)=>771,chr(247)=>888,chr(248)=>967,chr(249)=>888,chr(250)=>831,chr(251)=>873,chr(252)=>927,chr(253)=>970,chr(254)=>918,chr(255)=>0); +?> diff --git a/oss/vtiger/trunk/include/fpdf/fpdf.css b/oss/vtiger/trunk/include/fpdf/fpdf.css new file mode 100644 index 00000000..d979baec --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/fpdf.css @@ -0,0 +1,11 @@ +BODY {font-family:times new roman,serif} +H2 {color:#4000A0} +P.demo {text-align:center; margin-top:-10px} +A.demo {text-decoration:none; font-weight:bold; color:#0000CC} +A.demo:link {text-decoration:none; font-weight:bold; color:#0000CC} +A.demo:hover {text-decoration:none; font-weight:bold; color:#0000FF} +A.demo:active {text-decoration:none; font-weight:bold; color:#0000FF} +.st {font-weight:bold; color:#900000} +.kw {color:#000080; font-weight:bold} +.str {color:#CC0000} +.cmt {color:#008000} diff --git a/oss/vtiger/trunk/include/fpdf/fpdf.php b/oss/vtiger/trunk/include/fpdf/fpdf.php new file mode 100644 index 00000000..4b87a626 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/fpdf.php @@ -0,0 +1,1660 @@ +_dochecks(); + //Initialization of properties + $this->page=0; + $this->n=2; + $this->buffer=''; + $this->pages=array(); + $this->OrientationChanges=array(); + $this->state=0; + $this->fonts=array(); + $this->FontFiles=array(); + $this->diffs=array(); + $this->images=array(); + $this->links=array(); + $this->InFooter=false; + $this->lasth=0; + $this->FontFamily=''; + $this->FontStyle=''; + $this->FontSizePt=12; + $this->underline=false; + $this->DrawColor='0 G'; + $this->FillColor='0 g'; + $this->TextColor='0 g'; + $this->ColorFlag=false; + $this->ws=0; + //Standard fonts + $this->CoreFonts=array('courier'=>'Courier','courierB'=>'Courier-Bold','courierI'=>'Courier-Oblique','courierBI'=>'Courier-BoldOblique', + 'helvetica'=>'Helvetica','helveticaB'=>'Helvetica-Bold','helveticaI'=>'Helvetica-Oblique','helveticaBI'=>'Helvetica-BoldOblique', + 'times'=>'Times-Roman','timesB'=>'Times-Bold','timesI'=>'Times-Italic','timesBI'=>'Times-BoldItalic', + 'symbol'=>'Symbol','zapfdingbats'=>'ZapfDingbats'); + //Scale factor + if($unit=='pt') + $this->k=1; + elseif($unit=='mm') + $this->k=72/25.4; + elseif($unit=='cm') + $this->k=72/2.54; + elseif($unit=='in') + $this->k=72; + else + $this->Error('Incorrect unit: '.$unit); + //Page format + if(is_string($format)) + { + $format=strtolower($format); + if($format=='a1') + $format=array(2500,3000); + elseif($format=='a3') + $format=array(841.89,1190.55); + elseif($format=='a4') + $format=array(595.28,841.89); + elseif($format=='a5') + $format=array(420.94,595.28); + elseif($format=='a6') + $format=array(4000,5000); + elseif($format=='a2') + $format=array(8000,10100); + elseif($format=='letter') + $format=array(612,792); + elseif($format=='legal') + $format=array(612,1008); + else + $this->Error('Unknown page format: '.$format); + $this->fwPt=$format[0]; + $this->fhPt=$format[1]; + } + else + { + $this->fwPt=$format[0]*$this->k; + $this->fhPt=$format[1]*$this->k; + } + $this->fw=$this->fwPt/$this->k; + $this->fh=$this->fhPt/$this->k; + //Page orientation + $orientation=strtolower($orientation); + if($orientation=='p' || $orientation=='portrait') + { + $this->DefOrientation='P'; + $this->wPt=$this->fwPt; + $this->hPt=$this->fhPt; + } + elseif($orientation=='l' || $orientation=='landscape') + { + $this->DefOrientation='L'; + $this->wPt=$this->fhPt; + $this->hPt=$this->fwPt; + } + else + $this->Error('Incorrect orientation: '.$orientation); + $this->CurOrientation=$this->DefOrientation; + $this->w=$this->wPt/$this->k; + $this->h=$this->hPt/$this->k; + //Page margins (1 cm) + $margin=28.35/$this->k; + $this->SetMargins($margin,$margin); + //Interior cell margin (1 mm) + $this->cMargin=$margin/10; + //Line width (0.2 mm) + $this->LineWidth=.567/$this->k; + //Automatic page break + $this->SetAutoPageBreak(true,2*$margin); + //Full width display mode + $this->SetDisplayMode('fullwidth'); + //Enable compression + $this->SetCompression(true); + //Set default PDF version number + $this->PDFVersion='1.3'; +} + +function SetMargins($left,$top,$right=-1) +{ + //Set left, top and right margins + $this->lMargin=$left; + $this->tMargin=$top; + if($right==-1) + $right=$left; + $this->rMargin=$right; +} + +function SetLeftMargin($margin) +{ + //Set left margin + $this->lMargin=$margin; + if($this->page>0 && $this->x<$margin) + $this->x=$margin; +} + +function SetTopMargin($margin) +{ + //Set top margin + $this->tMargin=$margin; +} + +function SetRightMargin($margin) +{ + //Set right margin + $this->rMargin=$margin; +} + +function SetAutoPageBreak($auto,$margin=0) +{ + //Set auto page break mode and triggering margin + $this->AutoPageBreak=$auto; + $this->bMargin=$margin; + $this->PageBreakTrigger=$this->h-$margin; +} + +function SetDisplayMode($zoom,$layout='continuous') +{ + //Set display mode in viewer + if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) + $this->ZoomMode=$zoom; + else + $this->Error('Incorrect zoom display mode: '.$zoom); + if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') + $this->LayoutMode=$layout; + else + $this->Error('Incorrect layout display mode: '.$layout); +} + +function SetCompression($compress) +{ + //Set page compression + if(function_exists('gzcompress')) + $this->compress=$compress; + else + $this->compress=false; +} + +function SetTitle($title) +{ + //Title of document + $this->title=$title; +} + +function SetSubject($subject) +{ + //Subject of document + $this->subject=$subject; +} + +function SetAuthor($author) +{ + //Author of document + $this->author=$author; +} + +function SetKeywords($keywords) +{ + //Keywords of document + $this->keywords=$keywords; +} + +function SetCreator($creator) +{ + //Creator of document + $this->creator=$creator; +} + +function AliasNbPages($alias='{nb}') +{ + //Define an alias for total number of pages + $this->AliasNbPages=$alias; +} + +function Error($msg) +{ + //Fatal error + die('FPDF error: '.$msg); +} + +function Open() +{ + //Begin document + $this->state=1; +} + +function Close() +{ + //Terminate document + if($this->state==3) + return; + if($this->page==0) + $this->AddPage(); + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + //Close document + $this->_enddoc(); +} + +function AddPage($orientation='') +{ + //Start a new page + if($this->state==0) + $this->Open(); + $family=$this->FontFamily; + $style=$this->FontStyle.($this->underline ? 'U' : ''); + $size=$this->FontSizePt; + $lw=$this->LineWidth; + $dc=$this->DrawColor; + $fc=$this->FillColor; + $tc=$this->TextColor; + $cf=$this->ColorFlag; + if($this->page>0) + { + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + } + //Start new page + $this->_beginpage($orientation); + //Set line cap style to square + $this->_out('2 J'); + //Set line width + $this->LineWidth=$lw; + $this->_out(sprintf('%.2f w',$lw*$this->k)); + //Set font + if($family) + $this->SetFont($family,$style,$size); + //Set colors + $this->DrawColor=$dc; + if($dc!='0 G') + $this->_out($dc); + $this->FillColor=$fc; + if($fc!='0 g') + $this->_out($fc); + $this->TextColor=$tc; + $this->ColorFlag=$cf; + //Page header + $this->Header(); + //Restore line width + if($this->LineWidth!=$lw) + { + $this->LineWidth=$lw; + $this->_out(sprintf('%.2f w',$lw*$this->k)); + } + //Restore font + if($family) + $this->SetFont($family,$style,$size); + //Restore colors + if($this->DrawColor!=$dc) + { + $this->DrawColor=$dc; + $this->_out($dc); + } + if($this->FillColor!=$fc) + { + $this->FillColor=$fc; + $this->_out($fc); + } + $this->TextColor=$tc; + $this->ColorFlag=$cf; +} + +function Header() +{ + //To be implemented in your own inherited class +} + +function Footer() +{ + //To be implemented in your own inherited class +} + +function PageNo() +{ + //Get current page number + return $this->page; +} + +function SetDrawColor($r,$g=-1,$b=-1) +{ + //Set color for all stroking operations + if(($r==0 && $g==0 && $b==0) || $g==-1) + $this->DrawColor=sprintf('%.3f G',$r/255); + else + $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255); + if($this->page>0) + $this->_out($this->DrawColor); +} + +function SetFillColor($r,$g=-1,$b=-1) +{ + //Set color for all filling operations + if(($r==0 && $g==0 && $b==0) || $g==-1) + $this->FillColor=sprintf('%.3f g',$r/255); + else + $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); + $this->ColorFlag=($this->FillColor!=$this->TextColor); + if($this->page>0) + $this->_out($this->FillColor); +} + +function SetTextColor($r,$g=-1,$b=-1) +{ + //Set color for text + if(($r==0 && $g==0 && $b==0) || $g==-1) + $this->TextColor=sprintf('%.3f g',$r/255); + else + $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); + $this->ColorFlag=($this->FillColor!=$this->TextColor); +} + +function GetStringWidth($s) +{ + //Get width of a string in the current font + $s=(string)$s; + $cw=&$this->CurrentFont['cw']; + $w=0; + $l=strlen($s); + for($i=0;$i<$l;$i++) + $w+=$cw[$s{$i}]; + return $w*$this->FontSize/1000; +} + +function SetLineWidth($width) +{ + //Set line width + $this->LineWidth=$width; + if($this->page>0) + $this->_out(sprintf('%.2f w',$width*$this->k)); +} + +function Line($x1,$y1,$x2,$y2) +{ + //Draw a line + $this->_out(sprintf('%.2f %.2f m %.2f %.2f l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k)); +} + +function Rect($x,$y,$w,$h,$style='') +{ + //Draw a rectangle + if($style=='F') + $op='f'; + elseif($style=='FD' || $style=='DF') + $op='B'; + else + $op='S'; + $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); +} + +function AddFont($family,$style='',$file='') +{ + //Add a TrueType or Type1 font + $family=strtolower($family); + if($file=='') + $file=str_replace(' ','',$family).strtolower($style).'.php'; + if($family=='arial') + $family='msyh'; + $style=strtoupper($style); + if($style=='IB') + $style='BI'; + $fontkey=$family.$style; + if(isset($this->fonts[$fontkey])) + $this->Error('Font already added: '.$family.' '.$style); + include($this->_getfontpath().$file); + if(!isset($name)) + $this->Error('Could not include font definition file'); + $i=count($this->fonts)+1; + $this->fonts[$fontkey]=array('i'=>$i,'type'=>$type,'name'=>$name,'desc'=>$desc,'up'=>$up,'ut'=>$ut,'cw'=>$cw,'enc'=>$enc,'file'=>$file); + if($diff) + { + //Search existing encodings + $d=0; + $nb=count($this->diffs); + for($i=1;$i<=$nb;$i++) + { + if($this->diffs[$i]==$diff) + { + $d=$i; + break; + } + } + if($d==0) + { + $d=$nb+1; + $this->diffs[$d]=$diff; + } + $this->fonts[$fontkey]['diff']=$d; + } + if($file) + { + if($type=='TrueType') + $this->FontFiles[$file]=array('length1'=>$originalsize); + else + $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); + } +} + +function SetFont($family,$style='',$size=0) +{ + //Select a font; size given in points + global $fpdf_charwidths; + + $family=strtolower($family); + if($family=='') + $family=$this->FontFamily; + if($family=='arial') + $family='stsongstdlight'; + elseif($family=='symbol' || $family=='zapfdingbats') + $style=''; + $style=strtoupper($style); + if(strpos($style,'U')!==false) + { + $this->underline=true; + $style=str_replace('U','',$style); + } + else + $this->underline=false; + if($style=='IB') + $style='BI'; + if($size==0) + $size=$this->FontSizePt; + //Test if font is already selected + if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size) + return; + //Test if used for the first time + $fontkey=$family.$style; + if(!isset($this->fonts[$fontkey])) + { + //Check if one of the standard fonts + if(isset($this->CoreFonts[$fontkey])) + { + if(!isset($fpdf_charwidths[$fontkey])) + { + //Load metric file + $file=$family; + if($family=='times' || $family=='stsongstdlight') + $file.=strtolower($style); + include($this->_getfontpath().$file.'.php'); + if(!isset($fpdf_charwidths[$fontkey])) + $this->Error('Could not include font metric file'); + } + $i=count($this->fonts)+1; + $this->fonts[$fontkey]=array('i'=>$i,'type'=>'core','name'=>$this->CoreFonts[$fontkey],'up'=>-100,'ut'=>50,'cw'=>$fpdf_charwidths[$fontkey]); + } + else + $this->Error('Undefined font: '.$family.' '.$style); + } + //Select it + $this->FontFamily=$family; + $this->FontStyle=$style; + $this->FontSizePt=$size; + $this->FontSize=$size/$this->k; + $this->CurrentFont=&$this->fonts[$fontkey]; + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function SetFontSize($size) +{ + //Set font size in points + if($this->FontSizePt==$size) + return; + $this->FontSizePt=$size; + $this->FontSize=$size/$this->k; + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function AddLink() +{ + //Create a new internal link + $n=count($this->links)+1; + $this->links[$n]=array(0,0); + return $n; +} + +function SetLink($link,$y=0,$page=-1) +{ + //Set destination of internal link + if($y==-1) + $y=$this->y; + if($page==-1) + $page=$this->page; + $this->links[$link]=array($page,$y); +} + +function Link($x,$y,$w,$h,$link) +{ + //Put a link on the page + $this->PageLinks[$this->page][]=array($x*$this->k,$this->hPt-$y*$this->k,$w*$this->k,$h*$this->k,$link); +} + +function Text($x,$y,$txt) +{ + //Output a string + $s=sprintf('BT %.2f %.2f Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt)); + if($this->underline && $txt!='') + $s.=' '.$this->_dounderline($x,$y,$txt); + if($this->ColorFlag) + $s='q '.$this->TextColor.' '.$s.' Q'; + $this->_out($s); +} + +function AcceptPageBreak() +{ + //Accept automatic page break or not + return $this->AutoPageBreak; +} + +function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='') +{ + //Output a cell + $k=$this->k; + if($this->y+$h>$this->PageBreakTrigger && !$this->InFooter && $this->AcceptPageBreak()) + { + //Automatic page break + $x=$this->x; + $ws=$this->ws; + if($ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation); + $this->x=$x; + if($ws>0) + { + $this->ws=$ws; + $this->_out(sprintf('%.3f Tw',$ws*$k)); + } + } + if($w==0) + $w=$this->w-$this->rMargin-$this->x; + $s=''; + if($fill==1 || $border==1) + { + if($fill==1) + $op=($border==1) ? 'B' : 'f'; + else + $op='S'; + $s=sprintf('%.2f %.2f %.2f %.2f re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); + } + if(is_string($border)) + { + $x=$this->x; + $y=$this->y; + if(strpos($border,'L')!==false) + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); + if(strpos($border,'T')!==false) + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); + if(strpos($border,'R')!==false) + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + if(strpos($border,'B')!==false) + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + } + if($txt!=='') + { + if($align=='R') + $dx=$w-$this->cMargin-$this->GetStringWidth($txt); + elseif($align=='C') + $dx=($w-$this->GetStringWidth($txt))/2; + else + $dx=$this->cMargin; + if($this->ColorFlag) + $s.='q '.$this->TextColor.' '; + $txt2=str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt))); + $s.=sprintf('BT %.2f %.2f Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2); + if($this->underline) + $s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt); + if($this->ColorFlag) + $s.=' Q'; + if($link) + $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link); + } + if($s) + $this->_out($s); + $this->lasth=$h; + if($ln>0) + { + //Go to next line + $this->y+=$h; + if($ln==1) + $this->x=$this->lMargin; + } + else + $this->x+=$w; +} + +function MultiCell($w,$h,$txt,$border=0,$align='J',$fill=0) +{ + //Output text with automatic or explicit line breaks + $cw=&$this->CurrentFont['cw']; + if($w==0) + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin)*1000/$this->FontSize; + $s=str_replace("\r",'',$txt); + $nb=strlen($s); + if($nb>0 && $s[$nb-1]=="\n") + $nb--; + $b=0; + if($border) + { + if($border==1) + { + $border='LTRB'; + $b='LRT'; + $b2='LR'; + } + else + { + $b2=''; + if(strpos($border,'L')!==false) + $b2.='L'; + if(strpos($border,'R')!==false) + $b2.='R'; + $b=(strpos($border,'T')!==false) ? $b2.'T' : $b2; + } + } + $sep=-1; + $i=0; + $j=0; + $l=0; + $ns=0; + $nl=1; + while($i<$nb) + { + //Get next character + $c=$s{$i}; + if($c=="\n") + { + //Explicit line break + if($this->ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + $i++; + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + if($border && $nl==2) + $b=$b2; + continue; + } + if($c==' ') + { + $sep=$i; + $ls=$l; + $ns++; + } + $l+=$cw[$c]; + if($l>$wmax) + { + //Automatic line break + if($sep==-1) + { + if($i==$j) + $i++; + if($this->ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + else + { + if($align=='J') + { + $this->ws=($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; + $this->_out(sprintf('%.3f Tw',$this->ws*$this->k)); + } + $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill); + $i=$sep+1; + } + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + if($border && $nl==2) + $b=$b2; + } + else + $i++; + } + //Last chunk + if($this->ws>0) + { + $this->ws=0; + $this->_out('0 Tw'); + } + if($border && strpos($border,'B')!==false) + $b.='B'; + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + $this->x=$this->lMargin; +} + +function Write($h,$txt,$link='') +{ + //Output text in flowing mode + $cw=&$this->CurrentFont['cw']; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin)*1000/$this->FontSize; + $s=str_replace("\r",'',$txt); + $nb=strlen($s); + $sep=-1; + $i=0; + $j=0; + $l=0; + $nl=1; + while($i<$nb) + { + //Get next character + $c=$s{$i}; + if($c=="\n") + { + //Explicit line break + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); + $i++; + $sep=-1; + $j=$i; + $l=0; + if($nl==1) + { + $this->x=$this->lMargin; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin)*1000/$this->FontSize; + } + $nl++; + continue; + } + if($c==' ') + $sep=$i; + $l+=$cw[$c]; + if($l>$wmax) + { + //Automatic line break + if($sep==-1) + { + if($this->x>$this->lMargin) + { + //Move to next line + $this->x=$this->lMargin; + $this->y+=$h; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin)*1000/$this->FontSize; + $i++; + $nl++; + continue; + } + if($i==$j) + $i++; + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); + } + else + { + $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link); + $i=$sep+1; + } + $sep=-1; + $j=$i; + $l=0; + if($nl==1) + { + $this->x=$this->lMargin; + $w=$this->w-$this->rMargin-$this->x; + $wmax=($w-2*$this->cMargin)*1000/$this->FontSize; + } + $nl++; + } + else + $i++; + } + //Last chunk + if($i!=$j) + $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',0,$link); +} + +function Image($file,$x,$y,$w=0,$h=0,$type='',$link='') +{ + //Put an image on the page + if(!isset($this->images[$file])) + { + //First use of image, get info + if($type=='') + { + $pos=strrpos($file,'.'); + if(!$pos) + $this->Error('Image file has no extension and no type was specified: '.$file); + $type=substr($file,$pos+1); + } + $type=strtolower($type); + $mqr=get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + if($type=='jpg' || $type=='jpeg') + $info=$this->_parsejpg($file); + elseif($type=='png') + $info=$this->_parsepng($file); + else + { + //Allow for additional formats + $mtd='_parse'.$type; + if(!method_exists($this,$mtd)) + $this->Error('Unsupported image type: '.$type); + $info=$this->$mtd($file); + } + set_magic_quotes_runtime($mqr); + $info['i']=count($this->images)+1; + $this->images[$file]=$info; + } + else + $info=$this->images[$file]; + //Automatic width and height calculation if needed + if($w==0 && $h==0) + { + //Put image at 72 dpi + $w=$info['w']/$this->k; + $h=$info['h']/$this->k; + } + if($w==0) + $w=$h*$info['w']/$info['h']; + if($h==0) + $h=$w*$info['h']/$info['w']; + $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i'])); + if($link) + $this->Link($x,$y,$w,$h,$link); +} + +function Ln($h='') +{ + //Line feed; default value is last cell height + $this->x=$this->lMargin; + if(is_string($h)) + $this->y+=$this->lasth; + else + $this->y+=$h; +} + +function GetX() +{ + //Get x position + return $this->x; +} + +function SetX($x) +{ + //Set x position + if($x>=0) + $this->x=$x; + else + $this->x=$this->w+$x; +} + +function GetY() +{ + //Get y position + return $this->y; +} + +function SetY($y) +{ + //Set y position and reset x + $this->x=$this->lMargin; + if($y>=0) + $this->y=$y; + else + $this->y=$this->h+$y; +} + +function SetXY($x,$y) +{ + //Set x and y positions + $this->SetY($y); + $this->SetX($x); +} + +function Output($name='',$dest='') +{ + //Output PDF to some destination + //Finish document if necessary + if($this->state<3) + $this->Close(); + //Normalize parameters + if(is_bool($dest)) + $dest=$dest ? 'D' : 'F'; + $dest=strtoupper($dest); + if($dest=='') + { + if($name=='') + { + $name='doc.pdf'; + $dest='I'; + } + else + $dest='F'; + } + switch($dest) + { + case 'I': + //Send to standard output + if(ob_get_contents()) + $this->Error('Some data has already been output, can\'t send PDF file'); + if(php_sapi_name()!='cli') + { + //We send to a browser + header('Content-Type: application/pdf'); + if(headers_sent()) + $this->Error('Some data has already been output to browser, can\'t send PDF file'); + header('Content-Length: '.strlen($this->buffer)); + header('Content-disposition: inline; filename="'.$name.'"'); + } + echo $this->buffer; + break; + case 'D': + //Download file + if(ob_get_contents()) + $this->Error('Some data has already been output, can\'t send PDF file'); + if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) + { + //Bug fixed by vtiger + header("Pragma: public"); + header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); + header('Content-Type: application/force-download'); + } + else + { + header('Content-Type: application/octet-stream'); + } + if(headers_sent()) + $this->Error('Some data has already been output to browser, can\'t send PDF file'); + header('Content-Length: '.strlen($this->buffer)); + header('Content-disposition: attachment; filename="'.$name.'"'); + echo $this->buffer; + break; + case 'F': + //Save to local file + $f=fopen($name,'wb'); + if(!$f) + $this->Error('Unable to create output file: '.$name); + fwrite($f,$this->buffer,strlen($this->buffer)); + fclose($f); + break; + case 'S': + //Return as a string + return $this->buffer; + default: + $this->Error('Incorrect output destination: '.$dest); + } + return ''; +} + +/******************************************************************************* +* * +* Protected methods * +* * +*******************************************************************************/ +function _dochecks() +{ + //Check for locale-related bug + if(1.1==1) + $this->Error('Don\'t alter the locale before including class file'); + //Check for decimal separator + if(sprintf('%.1f',1.0)!='1.0') + setlocale(LC_NUMERIC,'C'); +} + +function _getfontpath() +{ + if(!defined('FPDF_FONTPATH') && is_dir(dirname(__FILE__).'/font')) + define('FPDF_FONTPATH',dirname(__FILE__).'/font/'); + return defined('FPDF_FONTPATH') ? FPDF_FONTPATH : ''; +} + +function _putpages() +{ + $nb=$this->page; + if(!empty($this->AliasNbPages)) + { + //Replace number of pages + for($n=1;$n<=$nb;$n++) + $this->pages[$n]=str_replace($this->AliasNbPages,$nb,$this->pages[$n]); + } + if($this->DefOrientation=='P') + { + $wPt=$this->fwPt; + $hPt=$this->fhPt; + } + else + { + $wPt=$this->fhPt; + $hPt=$this->fwPt; + } + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + for($n=1;$n<=$nb;$n++) + { + //Page + $this->_newobj(); + $this->_out('<_out('/Parent 1 0 R'); + if(isset($this->OrientationChanges[$n])) + $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt)); + $this->_out('/Resources 2 0 R'); + if(isset($this->PageLinks[$n])) + { + //Links + $annots='/Annots ['; + foreach($this->PageLinks[$n] as $pl) + { + $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); + $annots.='<_textstring($pl[4]).'>>>>'; + else + { + $l=$this->links[$pl[4]]; + $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt; + $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k); + } + } + $this->_out($annots.']'); + } + $this->_out('/Contents '.($this->n+1).' 0 R>>'); + $this->_out('endobj'); + //Page content + $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; + $this->_newobj(); + $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); + $this->_putstream($p); + $this->_out('endobj'); + } + //Pages root + $this->offsets[1]=strlen($this->buffer); + $this->_out('1 0 obj'); + $this->_out('<_out($kids.']'); + $this->_out('/Count '.$nb); + $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt)); + $this->_out('>>'); + $this->_out('endobj'); +} + +function _putfonts() +{ + $nf=$this->n; + foreach($this->diffs as $diff) + { + //Encodings + $this->_newobj(); + $this->_out('<>'); + $this->_out('endobj'); + } + $mqr=get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + foreach($this->FontFiles as $file=>$info) + { + //Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n']=$this->n; + $font=''; + $f=fopen($this->_getfontpath().$file,'rb',1); + if(!$f) + $this->Error('Font file not found'); + while(!feof($f)) + $font.=fread($f,8192); + fclose($f); + $compressed=(substr($file,-2)=='.z'); + if(!$compressed && isset($info['length2'])) + { + $header=(ord($font{0})==128); + if($header) + { + //Strip first binary header + $font=substr($font,6); + } + if($header && ord($font{$info['length1']})==128) + { + //Strip second binary header + $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); + } + } + $this->_out('<_out('/Filter /FlateDecode'); + $this->_out('/Length1 '.$info['length1']); + if(isset($info['length2'])) + $this->_out('/Length2 '.$info['length2'].' /Length3 0'); + $this->_out('>>'); + $this->_putstream($font); + $this->_out('endobj'); + } + set_magic_quotes_runtime($mqr); + foreach($this->fonts as $k=>$font) + { + //Font objects + $this->fonts[$k]['n']=$this->n+1; + $type=$font['type']; + $name=$font['name']; + if($type=='core') + { + //Standard font + $this->_newobj(); + $this->_out('<_out('/BaseFont /'.$name); + $this->_out('/Subtype /Type1'); + if($name!='Symbol' && $name!='ZapfDingbats') + $this->_out('/Encoding /WinAnsiEncoding'); + $this->_out('>>'); + $this->_out('endobj'); + } + elseif($type=='Type1' || $type=='TrueType') + { + //Additional Type1 or TrueType font + $this->_newobj(); + $this->_out('<_out('/BaseFont /'.$name); + $this->_out('/Subtype /'.$type); + $this->_out('/FirstChar 32 /LastChar 255'); + $this->_out('/Widths '.($this->n+1).' 0 R'); + $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); + if($font['enc']) + { + if(isset($font['diff'])) + $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); + else + $this->_out('/Encoding /WinAnsiEncoding'); + } + $this->_out('>>'); + $this->_out('endobj'); + //Widths + $this->_newobj(); + $cw=&$font['cw']; + $s='['; + for($i=32;$i<=255;$i++) + $s.=$cw[chr($i)].' '; + $this->_out($s.']'); + $this->_out('endobj'); + //Descriptor + $this->_newobj(); + $s='<$v) + $s.=' /'.$k.' '.$v; + $file=$font['file']; + if($file) + $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; + $this->_out($s.'>>'); + $this->_out('endobj'); + } + else + { + //Allow for additional types + $mtd='_put'.strtolower($type); + if(!method_exists($this,$mtd)) + $this->Error('Unsupported font type: '.$type); + $this->$mtd($font); + } + } +} + +function _putimages() +{ + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + reset($this->images); + while(list($file,$info)=each($this->images)) + { + $this->_newobj(); + $this->images[$file]['n']=$this->n; + $this->_out('<_out('/Subtype /Image'); + $this->_out('/Width '.$info['w']); + $this->_out('/Height '.$info['h']); + if($info['cs']=='Indexed') + $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + else + { + $this->_out('/ColorSpace /'.$info['cs']); + if($info['cs']=='DeviceCMYK') + $this->_out('/Decode [1 0 1 0 1 0 1 0]'); + } + $this->_out('/BitsPerComponent '.$info['bpc']); + if(isset($info['f'])) + $this->_out('/Filter /'.$info['f']); + if(isset($info['parms'])) + $this->_out($info['parms']); + if(isset($info['trns']) && is_array($info['trns'])) + { + $trns=''; + for($i=0;$i_out('/Mask ['.$trns.']'); + } + $this->_out('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + unset($this->images[$file]['data']); + $this->_out('endobj'); + //Palette + if($info['cs']=='Indexed') + { + $this->_newobj(); + $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; + $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); + $this->_putstream($pal); + $this->_out('endobj'); + } + } +} + +function _putxobjectdict() +{ + foreach($this->images as $image) + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); +} + +function _putresourcedict() +{ + $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_out('/Font <<'); + foreach($this->fonts as $font) + $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); + $this->_out('>>'); + $this->_out('/XObject <<'); + $this->_putxobjectdict(); + $this->_out('>>'); +} + +function _putresources() +{ + $this->_putfonts(); + $this->_putimages(); + //Resource dictionary + $this->offsets[2]=strlen($this->buffer); + $this->_out('2 0 obj'); + $this->_out('<<'); + $this->_putresourcedict(); + $this->_out('>>'); + $this->_out('endobj'); +} + +function _putinfo() +{ + $this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION)); + if(!empty($this->title)) + $this->_out('/Title '.$this->_textstring($this->title)); + if(!empty($this->subject)) + $this->_out('/Subject '.$this->_textstring($this->subject)); + if(!empty($this->author)) + $this->_out('/Author '.$this->_textstring($this->author)); + if(!empty($this->keywords)) + $this->_out('/Keywords '.$this->_textstring($this->keywords)); + if(!empty($this->creator)) + $this->_out('/Creator '.$this->_textstring($this->creator)); + $this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis'))); +} + +function _putcatalog() +{ + $this->_out('/Type /Catalog'); + $this->_out('/Pages 1 0 R'); + if($this->ZoomMode=='fullpage') + $this->_out('/OpenAction [3 0 R /Fit]'); + elseif($this->ZoomMode=='fullwidth') + $this->_out('/OpenAction [3 0 R /FitH null]'); + elseif($this->ZoomMode=='real') + $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); + elseif(!is_string($this->ZoomMode)) + $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']'); + if($this->LayoutMode=='single') + $this->_out('/PageLayout /SinglePage'); + elseif($this->LayoutMode=='continuous') + $this->_out('/PageLayout /OneColumn'); + elseif($this->LayoutMode=='two') + $this->_out('/PageLayout /TwoColumnLeft'); +} + +function _putheader() +{ + $this->_out('%PDF-'.$this->PDFVersion); +} + +function _puttrailer() +{ + $this->_out('/Size '.($this->n+1)); + $this->_out('/Root '.$this->n.' 0 R'); + $this->_out('/Info '.($this->n-1).' 0 R'); +} + +function _enddoc() +{ + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + //Info + $this->_newobj(); + $this->_out('<<'); + $this->_putinfo(); + $this->_out('>>'); + $this->_out('endobj'); + //Catalog + $this->_newobj(); + $this->_out('<<'); + $this->_putcatalog(); + $this->_out('>>'); + $this->_out('endobj'); + //Cross-ref + $o=strlen($this->buffer); + $this->_out('xref'); + $this->_out('0 '.($this->n+1)); + $this->_out('0000000000 65535 f '); + for($i=1;$i<=$this->n;$i++) + $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); + //Trailer + $this->_out('trailer'); + $this->_out('<<'); + $this->_puttrailer(); + $this->_out('>>'); + $this->_out('startxref'); + $this->_out($o); + $this->_out('%%EOF'); + $this->state=3; +} + +function _beginpage($orientation) +{ + $this->page++; + $this->pages[$this->page]=''; + $this->state=2; + $this->x=$this->lMargin; + $this->y=$this->tMargin; + $this->FontFamily=''; + //Page orientation + if(!$orientation) + $orientation=$this->DefOrientation; + else + { + $orientation=strtoupper($orientation{0}); + if($orientation!=$this->DefOrientation) + $this->OrientationChanges[$this->page]=true; + } + if($orientation!=$this->CurOrientation) + { + //Change orientation + if($orientation=='P') + { + $this->wPt=$this->fwPt; + $this->hPt=$this->fhPt; + $this->w=$this->fw; + $this->h=$this->fh; + } + else + { + $this->wPt=$this->fhPt; + $this->hPt=$this->fwPt; + $this->w=$this->fh; + $this->h=$this->fw; + } + $this->PageBreakTrigger=$this->h-$this->bMargin; + $this->CurOrientation=$orientation; + } +} + +function _endpage() +{ + //End of page contents + $this->state=1; +} + +function _newobj() +{ + //Begin a new object + $this->n++; + $this->offsets[$this->n]=strlen($this->buffer); + $this->_out($this->n.' 0 obj'); +} + +function _dounderline($x,$y,$txt) +{ + //Underline text + $up=$this->CurrentFont['up']; + $ut=$this->CurrentFont['ut']; + $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); + return sprintf('%.2f %.2f %.2f %.2f re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt); +} + +function _parsejpg($file) +{ + //Extract info from a JPEG file + $a=GetImageSize($file); + if(!$a) + $this->Error('Missing or incorrect image file: '.$file); + if($a[2]!=2) + $this->Error('Not a JPEG file: '.$file); + if(!isset($a['channels']) || $a['channels']==3) + $colspace='DeviceRGB'; + elseif($a['channels']==4) + $colspace='DeviceCMYK'; + else + $colspace='DeviceGray'; + $bpc=isset($a['bits']) ? $a['bits'] : 8; + //Read whole file + $f=fopen($file,'rb'); + $data=''; + while(!feof($f)) + $data.=fread($f,4096); + fclose($f); + return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); +} + +function _parsepng($file) +{ + //Extract info from a PNG file + $f=fopen($file,'rb'); + if(!$f) + $this->Error('Can\'t open image file: '.$file); + //Check signature + if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) + $this->Error('Not a PNG file: '.$file); + //Read header chunk + fread($f,4); + if(fread($f,4)!='IHDR') + $this->Error('Incorrect PNG file: '.$file); + $w=$this->_freadint($f); + $h=$this->_freadint($f); + $bpc=ord(fread($f,1)); + if($bpc>8) + $this->Error('16-bit depth not supported: '.$file); + $ct=ord(fread($f,1)); + if($ct==0) + $colspace='DeviceGray'; + elseif($ct==2) + $colspace='DeviceRGB'; + elseif($ct==3) + $colspace='Indexed'; + else + $this->Error('Alpha channel not supported: '.$file); + if(ord(fread($f,1))!=0) + $this->Error('Unknown compression method: '.$file); + if(ord(fread($f,1))!=0) + $this->Error('Unknown filter method: '.$file); + if(ord(fread($f,1))!=0) + $this->Error('Interlacing not supported: '.$file); + fread($f,4); + $parms='/DecodeParms <>'; + //Scan chunks looking for palette, transparency and image data + $pal=''; + $trns=''; + $data=''; + do + { + $n=$this->_freadint($f); + $type=fread($f,4); + if($type=='PLTE') + { + //Read palette + $pal=fread($f,$n); + fread($f,4); + } + elseif($type=='tRNS') + { + //Read transparency info + $t=fread($f,$n); + if($ct==0) + $trns=array(ord(substr($t,1,1))); + elseif($ct==2) + $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1))); + else + { + $pos=strpos($t,chr(0)); + if($pos!==false) + $trns=array($pos); + } + fread($f,4); + } + elseif($type=='IDAT') + { + //Read image data block + $data.=fread($f,$n); + fread($f,4); + } + elseif($type=='IEND') + break; + else + fread($f,$n+4); + } + while($n); + if($colspace=='Indexed' && empty($pal)) + $this->Error('Missing palette in '.$file); + fclose($f); + return array('w'=>$w,'h'=>$h,'cs'=>$colspace,'bpc'=>$bpc,'f'=>'FlateDecode','parms'=>$parms,'pal'=>$pal,'trns'=>$trns,'data'=>$data); +} + +function _freadint($f) +{ + //Read a 4-byte integer from file + $a=unpack('Ni',fread($f,4)); + return $a['i']; +} + +function _textstring($s) +{ + //Format a text string + return '('.$this->_escape($s).')'; +} + +function _escape($s) +{ + //Add \ before \, ( and ) + return str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$s))); +} + +function _putstream($s) +{ + $this->_out('stream'); + $this->_out($s); + $this->_out('endstream'); +} + +function _out($s) +{ + //Add a line to the document + if($this->state==2) + $this->pages[$this->page].=$s."\n"; + else + $this->buffer.=$s."\n"; +} +//End of class +} + +//Handle special IE contype request +if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype') +{ + header('Content-Type: application/pdf'); + exit; +} + +} +?> diff --git a/oss/vtiger/trunk/include/fpdf/pdf.php b/oss/vtiger/trunk/include/fpdf/pdf.php new file mode 100644 index 00000000..4780c26d --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/pdf.php @@ -0,0 +1,413 @@ +k; + $hp = $this->h; + if($style=='F') + $op='f'; + elseif($style=='FD' or $style=='DF') + $op='B'; + else + $op='S'; + $MyArc = 4/3 * (sqrt(2) - 1); + $this->_out(sprintf('%.2f %.2f m',($x+$r)*$k,($hp-$y)*$k )); + $xc = $x+$w-$r ; + $yc = $y+$r; + $this->_out(sprintf('%.2f %.2f l', $xc*$k,($hp-$y)*$k )); + + $this->_Arc($xc + $r*$MyArc, $yc - $r, $xc + $r, $yc - $r*$MyArc, $xc + $r, $yc); + $xc = $x+$w-$r ; + $yc = $y+$h-$r; + $this->_out(sprintf('%.2f %.2f l',($x+$w)*$k,($hp-$yc)*$k)); + $this->_Arc($xc + $r, $yc + $r*$MyArc, $xc + $r*$MyArc, $yc + $r, $xc, $yc + $r); + $xc = $x+$r ; + $yc = $y+$h-$r; + $this->_out(sprintf('%.2f %.2f l',$xc*$k,($hp-($y+$h))*$k)); + $this->_Arc($xc - $r*$MyArc, $yc + $r, $xc - $r, $yc + $r*$MyArc, $xc - $r, $yc); + $xc = $x+$r ; + $yc = $y+$r; + $this->_out(sprintf('%.2f %.2f l',($x)*$k,($hp-$yc)*$k )); + $this->_Arc($xc - $r, $yc - $r*$MyArc, $xc - $r*$MyArc, $yc - $r, $xc, $yc - $r); + $this->_out($op); +} + +function _Arc($x1, $y1, $x2, $y2, $x3, $y3) +{ + $h = $this->h; + $this->_out(sprintf('%.2f %.2f %.2f %.2f %.2f %.2f c ', $x1*$this->k, ($h-$y1)*$this->k, + $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k)); +} + +function Rotate($angle,$x=-1,$y=-1) +{ + if($x==-1) + $x=$this->x; + if($y==-1) + $y=$this->y; + if($this->angle!=0) + $this->_out('Q'); + $this->angle=$angle; + if($angle!=0) + { + $angle*=M_PI/180; + $c=cos($angle); + $s=sin($angle); + $cx=$x*$this->k; + $cy=($this->h-$y)*$this->k; + $this->_out(sprintf('q %.5f %.5f %.5f %.5f %.2f %.2f cm 1 0 0 1 %.2f %.2f cm',$c,$s,-$s,$c,$cx,$cy,-$cx,-$cy)); + } +} + +function _endpage() +{ + if($this->angle!=0) + { + $this->angle=0; + $this->_out('Q'); + } + parent::_endpage(); +} + +// public functions +function sizeOfText( $text, $largeur ) +{ + $index = 0; + $nb_lines = 0; + $loop = TRUE; + while ( $loop ) + { + $pos = strpos($text, "\n"); + if (!$pos) + { + $loop = FALSE; + $line = $text; + } + else + { + $line = substr( $text, $index, $pos); + $text = substr( $text, $pos+1 ); + } + $length = floor( $this->GetStringWidth( $line ) ); + $res = 1 + floor( $length / $largeur) ; + $nb_lines += $res; + } + return $nb_lines; +} + +// addImage +// Default will place vtiger in the top left corner +function addImage( $logo_name, $location=array('10','10','0','0') ) { + if($logo_name)//error checking just in case, by OpenCRM + { + $x1 = $location[0]; + $y1 = $location[1]; + $stretchx = $location[2]; + $stretchy = $location[3]; + $this->Image('test/logo/'.$logo_name,$x1,$y1,$stretchx,$stretchy); + } +} + +// Company +function addCompany( $nom, $address, $location='' ) +{ + $x1 = $location[0]; + $y1 = $location[1]; + //Positionnement en bas + $this->SetXY( $x1, $y1 ); + $this->SetFont('stsongstdlight','B',12); + $length = $this->GetStringWidth( $nom ); + $this->Cell( $length, 2, $nom); + $this->SetXY( $x1, $y1 + 4 ); + $this->SetFont('stsongstdlight','',10); + $length = $this->GetStringWidth( $address ); + //Coordonnées de la société + $lines = $this->sizeOfText( $address, $length) ; + $this->MultiCell($length, 4, $address); +} + +// bubble blocks +function title ($label, $total, $position) +{ + $r1 = $position[0]; + $r2 = $r1 + 19 + $position[2] ; + $y1 = $position[1]; + $y2 = $y1; + $mid = $y1 + ($y2 / 2); + $width=10; + $this->SetFillColor(192); + $this->RoundedRect($r1-16, $y1-1, 52, $y2+1, 2.5, 'DF'); + $this->SetXY( $r1 + 4, $y1+1 ); + $this->SetFont( "stsongstdlight", "B", 15); + $this->Cell($width,5, $label." ".$total, 0, 0, "C"); +} + +// text block, non-wrapped +function addTextBlock( $title,$text,$positions ) +{ + $r1 = $positions[0]; + $y1 = $positions[1]; + $this->SetXY( $r1, $y1); + $this->SetFont( "stsongstdlight", "B", 10); + $this->Cell( $positions[2], 4,$title); + $this->SetXY( $r1, $y1+4); + $this->SetFont( "stsongstdlight", "", 10); + $this->MultiCell( $positions[2], 4, $text); +} + +function tableWrapper($position) +{ + $r1 = $position[0]; + $r2 = $r1 + 19 + $position[2] ; + $y1 = $position[1]; + if($position[3]) + $y2 = $position[3]; + else + $y2 = 17; + + $mid = $y1 + (13 / 2); + $width=10; + $this->RoundedRect($r1, $y1, ($r2 - $r1), $y2, 4.5, 'D'); + $this->Line( $r1, $mid, $r2, $mid); + $this->SetXY( $r1 + ($r2-$r1)/2 - 3, $y1+3 ); + $this->SetXY( $r1 + ($r2-$r1)/2 - 5, $y1 + 9 ); +} + +function addBubble($page,$title,$position) +{ + $r1 = $position[0]; + $r2 = $r1 + 19 + $position[2] ; + $y1 = $position[1]; + if($position[3]) + $y2 = 17*$position[3]; + else + $y2 = 17; + + $mid = $y1 + (19 / 2); + $width=10; + $this->RoundedRect($r1, $y1, ($r2 - $r1), $y2, 4.5, 'D'); + $this->Line( $r1, $mid, $r2, $mid); + $this->SetXY( $r1 + ($r2-$r1)/2 - 3, $y1+3 ); + $this->SetFont( "stsongstdlight", "B", 10); + $this->Cell($width,5, $title, 0, 0, "C"); + $this->SetXY( $r1 + ($r2-$r1)/2 - 5, $y1 + 9 ); + $this->SetFont( "stsongstdlight", "", 10); + $this->MultiCell($width,5,$page, 0,0, "C"); +} + +// bubble blocks +function addBubbleBlock ($page, $title, $position) +{ + $r1 = $position[0]; + $r2 = $r1 + 19 + $position[2] ; + $y1 = $position[1]; + $y2 = 17; + + $mid = $y1 + ($y2 / 2); + $width=10; + $this->RoundedRect($r1, $y1, ($r2 - $r1), $y2, 4.5, 'D'); + $this->Line( $r1, $mid, $r2, $mid); + $this->SetXY( $r1 + ($r2-$r1)/2 - 5, $y1+3 ); + $this->SetFont( "stsongstdlight", "B", 10); + $this->Cell($width,5, $title, 0, 0, "C"); + $this->SetXY( $r1 + ($r2-$r1)/2 - 5, $y1 + 9 ); + $this->SetFont( "stsongstdlight", "", 10); + $this->Cell($width,5,$page, 0,0, "C"); +} + +// record blocks +function addRecBlock( $data, $title, $postion ) +{ + $lengthtitle = strlen($title); + $lengthdata = strlen($data); + $length=$lengthtitle; + $r1 = $postion[0]; + $r2 = $r1 + 40 + $length; + $y1 = $postion[1]; + $y2 = $y1+10; + $mid = $y1 + (($y2-$y1) / 2); + + $this->RoundedRect($r1, $y1, ($r2 - $r1), ($y2-$y1), 2.5, 'D'); + $this->Line( $r1, $mid, $r2, $mid); + $this->SetXY( $r1 + ($r2-$r1)/2 -5 , $y1+1 ); + $this->SetFont( "stsongstdlight", "B", 10); + $this->Cell(10,4, $title, 0, 0, "C"); + $this->SetXY( $r1 + ($r2-$r1)/2 -5 , $y1 + 5 ); + $this->SetFont( "stsongstdlight", "", 10); + $this->Cell(10,4,$data, 0, 0, "C"); +} + +// description blocks +function addDescBlock( $data, $title, $position ) +{ + $lengthtitle = strlen($title); + $lengthdata= $position[3]; + + $length=$position[2]; + $r1 = $position[0]; + $r2 = $r1 + 40 + $length; + $y1 = $position[1]; + $y2 = $y1+10; + $mid = $y1 + (($y2-$y1) / 2); + + $this->RoundedRect($r1,$y1, ($length + 40), ($lengthdata/140*30), 2.5, 'D'); + $this->Line( $r1, $mid, $r2, $mid); + + $this->SetXY( $position[0]+2 , $y1 + 1 ); + $this->SetFont( "stsongstdlight", "B", 10); + $this->Cell(10,4, $title); + + $this->SetXY( $position[0]+2 , $y1 + 6 ); + $this->SetFont( "stsongstdlight", "", 10); + $this->MultiCell(($length+36),4,$data); +} + +function drawLine($positions) +{ + $x=$positions[0]; + $y=$positions[1]; + $width=$positions[2]; + $this->Line( $x, $y, $x+$width, $y); +} + +// add columns to table +function addCols( $tab ,$positions ,$bottom, $taxtype = 'group') +{ + global $columns; + + $r1 = 10; + $r2 = $this->w - ($r1 * 2) ; + $y1 = 80; + $x1 = $positions[1]; + $y2 = $bottom; + $this->SetXY( $r1, $y1 ); + $this->SetFont( "stsongstdlight", "", 10); + + $colX = $r1; + $columns = $tab; + while ( list( $lib, $pos ) = each ($tab) ) + { + $this->SetXY( $colX, $y1+3 ); + $this->Cell( $pos, 1, $lib, 0, 0, "C"); + $colX += $pos; + switch($lib) { + case 'Total': + break; + case 'Qty': + case 'Price': + if($taxtype == "individual") + $this->Line( $colX, $y1, $colX, (($y1+$y2)-37)); + else + $this->Line( $colX, $y1, $colX, (($y1+$y2)-43)); + break; + default: + if($taxtype == "individual" && $lib == 'Discount') + $this->Line( $colX, $y1, $colX, (($y1+$y2)-37)); + else + $this->Line( $colX, $y1, $colX, ($y1+$y2)); + break; + } + } +} + +function addLineFormat( $tab ) +{ + global $format, $columns; + + while ( list( $lib, $pos ) = each ($columns) ) + { + if ( isset( $tab["$lib"] ) ) + $format[ $lib ] = $tab["$lib"]; + } +} + +function addProductLine( $line, $tab, $totals='' ) +{ + global $columns, $format; + + $ordonnee = 10; + $maxSize = $line; + + reset( $columns ); + while ( list( $lib, $pos ) = each ($columns) ) + { + $longCell = $pos -2; + $text = $tab[ $lib ]; + $length = $this->GetStringWidth( $text ); + $formText = $format[ $lib ]; + $this->SetXY( $ordonnee, $line); + $this->MultiCell( $longCell, 3 , $text, 3, $formText); + if ( $maxSize < ($this->GetY() ) ) + $maxSize = $this->GetY() ; + $ordonnee += $pos; + } + return ( $maxSize - $line ); +} + +function addTotalsRec($names, $totals, $positions) +{ + $this->SetFont( "stsongstdlight", "B", 8); + $r1 = $positions[0]; + $r2 = $r1 + 90; + $y1 = $positions[1]; + $y2 = $y1+10; + $this->RoundedRect($r1, $y1, ($r2 - $r1), ($y2-$y1), 2.5, 'D'); + $this->Line( $r1, $y1+4, $r2, $y1+4); + $this->Line( $r1+27, $y1, $r1+27, $y2); // avant Subtotal + $this->Line( $r1+43, $y1, $r1+43, $y2); // avant Tax + $this->Line( $r1+66, $y1, $r1+66, $y2); // avant Adjustment + + $this->SetXY( $r1+2, $y1); + $this->Cell(10,4, $names[0]); + $this->SetX( $r1+29,$y1 ); + $this->Cell(10,4, $names[1]); + $this->SetX( $r1+45 ); + $this->Cell(10,4, $names[2]); + $this->SetX( $r1+66 ); + $this->Cell(10,4, $names[3]); + + + $this->SetXY( $r1+2, $y1+5 ); + $this->Cell( 10,4, $totals[0] ); + $this->SetXY( $r1+29, $y1+5 ); + $this->Cell( 10,4, $totals[1] ); + $this->SetXY( $r1+44, $y1+5 ); + $this->Cell( 10,4, $totals[2] ); + $this->SetXY( $r1+66, $y1+5 ); + $this->Cell( 10,4, $totals[3] ); + + $this->SetFont( "stsongstdlight", "B", 6); + $this->SetXY( $r1+90, $y2 - 8 ); + $this->SetFont( "stsongstdlight", "", 10); +} + +// add a watermark (temporary estimate, DUPLICATA...) +// call this method first +function watermark( $text, $positions, $rotate = array('45','50','180') ) +{ + $this->SetFont('stsongstdlight','B',50); + $this->SetTextColor(230,230,230); + $this->Rotate($rotate[0],$rotate[1],$rotate[2]); + $this->Text($positions[0],$positions[1],$text); + $this->Rotate(0); + $this->SetTextColor(0,0,0); +} + +} +function StripLastZero($string) +{ + $count=strlen($string); + $ret=substr($string,0,($count-1)); + return $ret; +} +?> diff --git a/oss/vtiger/trunk/include/fpdf/pdfconfig.php b/oss/vtiger/trunk/include/fpdf/pdfconfig.php new file mode 100644 index 00000000..aad6f75a --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/pdfconfig.php @@ -0,0 +1,41 @@ + 170 then last lines will be displayed in next page +$bottom="130";//"130" + +//how may products per page +$products_per_page="6";//used in modules/{PO/SO/Quotes/Invoice}/CreatePDF.php + + +//where the top line starts +$body_top="80"; + +//This is the y position where the nettotal starts, next fields will be displayed in bottom+n(6) pixels +$nettotal_y = $bottom+38;//"168"; +$next_y = "6"; + + +//Following variables will be used in modules/{PO/SO/Quotes/Invoice}/pdf_templates/footer.php +$top = $nettotal_y+48;//"216"; + + +//if true then Description and Terms & Conditions will be displayed in all pages, false - displayed only in the last page +$display_desc_tc = 'true'; + + +?> diff --git a/oss/vtiger/trunk/include/fpdf/templates/body.php b/oss/vtiger/trunk/include/fpdf/templates/body.php new file mode 100644 index 00000000..9c435d20 --- /dev/null +++ b/oss/vtiger/trunk/include/fpdf/templates/body.php @@ -0,0 +1,238 @@ +watermark( $status, $waterMarkPositions, $waterMarkRotate ); + +include("include/fpdf/pdfconfig.php"); + +// blow a bubble around the table +$Bubble=array("10",$body_top,"170","$bottom"); +$pdf->tableWrapper($Bubble); + +/* ************ Begin Table Setup ********************** */ +// Each of these arrays needs to have matching keys +// "key" => "Length" +// total of columns needs to be 190 in order to fit the table +// correctly +$prodTable=array("10","60"); +//added for value field allignment +//contains the x angle starting point of the value field +$space=array("4"=>"191","5"=>"189","6"=>"187","7"=>"186","8"=>"184","9"=>"182","10"=>"180","11"=>"179","12"=>"177","13"=>"175"); +//if taxtype is individual +if($focus->column_fields["hdnTaxType"] == "individual") +{ + $colsAlign["Product Name"] = "L"; + $colsAlign["Description"] = "L"; + $colsAlign["Qty"] = "R"; + $colsAlign["Price"] = "R"; + $colsAlign["Discount"] = "R"; + $colsAlign["Tax"] = "R"; + $colsAlign["Total"] = "R"; + $cols["Product Code"] = "30"; + $cols["Product Name"] = "65"; + $cols["Qty"] = "13"; + $cols["Price"] = "22"; + $cols["Discount"] = "15"; + $cols["Tax"] = "20"; + $cols["Total"] = "25"; +} +else +{ + //if taxtype is group + $colsAlign["Product Name"] = "L"; + $colsAlign["Description"] = "L"; + $colsAlign["Qty"] = "R"; + $colsAlign["Price"] = "R"; + $colsAlign["Discount"] = "R"; + $colsAlign["Total"] = "R"; + $cols["Product Code"] = "30"; + $cols["Product Name"] = "65"; + $cols["Qty"] = "15"; + $cols["Price"] = "30"; + $cols["Discount"] = "20"; + $cols["Total"] = "30"; +} + + +$pdf->addCols( $cols,$prodTable,$bottom, $focus->column_fields["hdnTaxType"]); +$pdf->addLineFormat( $colsAlign); + +/* ************** End Table Setup *********************** */ + + + +/* ************* Begin Product Population *************** */ +$ppad=3; +$y = $body_top+10; + +for($i=0;$iaddProductLine( $y, $line[$i] ); + $y += $size+$ppad; +} + +/* ******************* End product population ********* */ + + +/* ************* Begin Totals ************************** */ +$t=$bottom+56; +$pad=6; +for($i=0;$iaddProductLine( $t, $total[$i], $total[$i] ); + $t += $pad; +} + +//Set the x and y positions to place the NetTotal, Discount, S&H charge +//if taxtype is not individual ie., group tax +if($focus->column_fields["hdnTaxType"] != "individual") +{ + $lineData=array("105",$bottom+37,"94"); + $pdf->drawLine($lineData); + $data= $app_strings['LBL_NET_TOTAL'].":";// ".$price_subtotal.""; + $pdf->SetXY( 105 , ($nettotal_y+(0*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); + +//Added for value field alignment + $pdf->SetXY( $space[strlen($price_subtotal)] , ($nettotal_y+(0*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $price_subtotal); + + + $lineData=array("105",$bottom+43,"94"); + $pdf->drawLine($lineData); + + //For alignment + if($final_price_discount_percent != '') + $data= $app_strings['LBL_DISCOUNT'].": $final_price_discount_percent";// ".$price_discount.""; + else + $data= $app_strings['LBL_DISCOUNT'].":";// ".$price_discount.""; + $pdf->SetXY( 105 , ($nettotal_y+(1*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); + +//Added for value field alignment + $pdf->SetXY( $space[strlen($price_discount)] , ($nettotal_y+(1*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $price_discount); + + $lineData=array("105",$bottom+49,"94"); + $pdf->drawLine($lineData); + $data= $app_strings['LBL_TAX'].": ($group_total_tax_percent %)";// ".$price_salestax.""; + $pdf->SetXY( 105 , ($nettotal_y+(2*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); + + //Added for value field alignment + $pdf->SetXY( $space[strlen($price_salestax)] , ($nettotal_y+(2*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $price_salestax); + + $lineData=array("105",$bottom+55,"94"); + $pdf->drawLine($lineData); + $data = $app_strings['LBL_SHIPPING_AND_HANDLING_CHARGES'].":";// ".$price_shipping; + $pdf->SetXY( 105 , ($nettotal_y+(3*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); + +//Added for value field alignment + $pdf->SetXY( $space[strlen($price_shipping)] , ($nettotal_y+(3*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $price_shipping); +} +else +{ + //if taxtype is individual + $lineData=array("105",$bottom+43,"94"); + $pdf->drawLine($lineData); + $data= $app_strings['LBL_NET_TOTAL'].":";// ".$price_subtotal.""; + $pdf->SetXY( 105 , ($nettotal_y+(1*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); + +// added for value field allignment + + $pdf->SetXY( $space[strlen($price_subtotal)] , ($nettotal_y+(1*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4,$price_subtotal); + + + $lineData=array("105",$bottom+49,"94"); + $pdf->drawLine($lineData); + + //For alignment + if($final_price_discount_percent != '') + $data= $app_strings['LBL_DISCOUNT'].": $final_price_discount_percent";// ".$price_discount.""; + else + $data= $app_strings['LBL_DISCOUNT'].":";// ".$price_discount.""; + + $pdf->SetXY( 105 , ($nettotal_y+(2*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); +//Added for value field alignment + $pdf->SetXY( $space[strlen($price_discount)] , ($nettotal_y+(2*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $price_discount); + + + $lineData=array("105",$bottom+55,"94"); + $pdf->drawLine($lineData); + $data = $app_strings['LBL_SHIPPING_AND_HANDLING_CHARGES'].":";// ".$price_shipping; + $pdf->SetXY( 105 , ($nettotal_y+(3*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $data); + +//Added for value field alignment + + $pdf->SetXY( $space[strlen($price_shipping)] , ($nettotal_y+(3*$next_y)) ); + $pdf->SetFont( "stsongstdlight", "", 10); + $pdf->MultiCell(110, 4, $price_shipping); + +} + +//Set the x and y positions to place the S&H Tax, Adjustment and Grand Total +$lineData=array("105",$bottom+61,"94"); +$pdf->drawLine($lineData); +$data = $app_strings['LBL_TAX_FOR_SHIPPING_AND_HANDLING'].": ($sh_tax_percent %)";// ".$price_shipping_tax; +$pdf->SetXY( 105 , ($nettotal_y+(4*$next_y)) ); +$pdf->SetFont( "stsongstdlight", "", 10); +$pdf->MultiCell(110, 4, $data); + +//Added for value field alignment +$pdf->SetXY( $space[strlen($price_shipping_tax)] , ($nettotal_y+(4*$next_y)) ); +$pdf->SetFont( "stsongstdlight", "", 10); +$pdf->MultiCell(110, 4, $price_shipping_tax); + +$lineData=array("105",$bottom+67,"94"); +$pdf->drawLine($lineData); +$data = $app_strings['LBL_ADJUSTMENT'].":";// ".$price_adjustment; +$pdf->SetXY( 105 , ($nettotal_y+(5*$next_y)) ); +$pdf->SetFont( "stsongstdlight", "", 10); +$pdf->MultiCell(110, 4, $data); + +//Added for value field alignment +$pdf->SetXY( $space[strlen($price_adjustment)] , ($nettotal_y+(5*$next_y)) ); +$pdf->SetFont( "stsongstdlight", "", 10); +$pdf->MultiCell(110, 4, $price_adjustment); + + +$lineData=array("105",$bottom+73,"94"); +$pdf->drawLine($lineData); +$data = $app_strings['LBL_GRAND_TOTAL'].":(in $currency_symbol)";// ".$price_total; +$pdf->SetXY( 105 , ($nettotal_y+(6*$next_y)) ); +$pdf->SetFont( "stsongstdlight", "", 10); +$pdf->MultiCell(110, 4, $data); + +//Added for value field alignment +$pdf->SetXY( $space[strlen($price_total)] , ($nettotal_y+(6*$next_y)) ); +$pdf->SetFont( "stsongstdlight", "", 10); +$pdf->MultiCell(110, 4, $price_total); + +/* ************** End Totals *********************** */ + + +?> diff --git a/oss/vtiger/trunk/include/freetag/freetag.class.php b/oss/vtiger/trunk/include/freetag/freetag.class.php new file mode 100644 index 00000000..c3b52e89 --- /dev/null +++ b/oss/vtiger/trunk/include/freetag/freetag.class.php @@ -0,0 +1,1150 @@ + + * + * Released under both BSD license and Lesser GPL library license. Whenever + * there is any discrepancy between the two licenses, the BSD license will + * take precedence. See License.txt. + * + */ +/** + * Freetag API Implementation + * + * Freetag is a generic PHP class that can hook-in to existing database + * schemas and allows tagging of content within a social website. It's fun, + * fast, and easy! Try it today and see what all the folksonomy fuss is + * about. + * + * Contributions and/or donations are welcome. + * + * Author: Gordon Luk + * http://www.getluky.net + * + * Version: 0.240 + * Last Updated: 12/26/2005 + * + */ + +class freetag { + + /**#@+ + * @access private + * @param string + */ + /**#@-*/ + + /** + * @access private + * @param ADOConnection The ADODB Database connection instance. + */ + //var $_db; + /** + * @access private + * @param bool Prints out limited debugging information if true, not fully implemented yet. + */ + var $_debug = FALSE; + /** + * @access private + * @param string The prefix of freetag database vtiger_tables. + */ + var $_table_prefix = 'vtiger_'; + /** + * @access private + * @param string The regex-style set of characters that are valid for normalized tags. + */ + var $_normalized_valid_chars = 'a-zA-Z0-9'; + /** + * @access private + * @param string Whether to normalize tags at all. + * value 0 saves the tag in case insensitive mode + * value 1 save the tag in lower case + */ + var $_normalize_tags = 0; + /** + * @access private + * @param string Whether to prevent multiple vtiger_users from tagging the same object. By default, set to block (ala Upcoming.org) + */ + var $_block_multiuser_tag_on_object =0; + /** + * @access private + * @param bool Whether to use persistent ADODB connections. False by default. + */ + //var $_PCONNECT = FALSE; + /** + * @access private + * @param int The maximum length of a tag. + */ + var $_MAX_TAG_LENGTH = 30; + /** + * @access private + * @param string The file path to the installation of ADOdb used. + */ + //var $_ADODB_DIR = 'adodb/'; + + /** + * freetag + * + * Constructor for the freetag class. + * + * @param array An associative array of options to pass to the instance of Freetag. + * The following options are valid: + * - debug: Set to TRUE for debugging information. [default:FALSE] + * - db: If you've already got an ADODB ADOConnection, you can pass it directly and Freetag will use that. [default:NULL] + * - db_user: Database username + * - db_pass: Database password + * - db_host: Database hostname [default: localhost] + * - db_name: Database name + * - vtiger_table_prefix: If you wish to create multiple Freetag databases on the same database, you can put a prefix in front of the vtiger_table names and pass separate prefixes to the constructor. [default: ''] + * - normalize_tags: Whether to normalize (lowercase and filter for valid characters) on tags at all. [default: 1] + * - normalized_valid_chars: Pass a regex-style set of valid characters that you want your tags normalized against. [default: 'a-zA-Z0-9' for alphanumeric] + * - block_multiuser_tag_on_object: Set to 0 in order to allow individual vtiger_users to all tag the same object with the same tag. Default is 1 to only allow one occurence of a tag per object. [default: 1] + * - MAX_TAG_LENGTH: maximum length of normalized tags in chars. [default: 30] + * - ADODB_DIR: directory in which adodb is installed. Change if you don't want to use the bundled version. [default: adodb/] + * - PCONNECT: Whether to use ADODB persistent connections. [default: FALSE] + * + */ + function freetag($options = NULL) { +/* + $available_options = array('debug', 'db', 'db_user', 'db_pass', 'db_host', 'db_name', 'table_prefix', 'normalize_tags', 'normalized_valid_chars', 'block_multiuser_tag_on_object', 'MAX_TAG_LENGTH', 'ADODB_DIR', 'PCONNECT'); + if (is_array($options)) { + foreach ($options as $key => $value) { + $this->debug_text("Option: $key"); + + if (in_array($key, $available_options) ) { + $this->debug_text("Valid Config options: $key"); + $property = '_'.$key; + $this->$property = $value; + $this->debug_text("Setting $property to $value"); + } else { + $this->debug_text("ERROR: Config option: $key is not a valid option"); + } + } + }*/ +/* + require_once($this->_ADODB_DIR . "/adodb.inc.php"); + if (is_object($this->_db)) { + $this->db = &$this->_db; + $this->debug_text("DB Instance already exists, using this one."); + } else { + $this->db = ADONewConnection("mysql"); + $this->debug_text("Connecting to db with:" . $this->_db_host . " " . $this->_db_user . " " . $this->_db_pass . " " . $this->_db_name); + if ($this->_PCONNECT) { + $this->db->PConnect($this->_db_host, $this->_db_user, $this->_db_pass, $this->_db_name); + } else { + $this->db->Connect($this->_db_host, $this->_db_user, $this->_db_pass, $this->_db_name); + } + } + $this->db->debug = $this->_debug; + // Freetag uses ASSOC for ease of maintenance and compatibility with people who choose to modify the schema. + // Feel free to convert to NUM if performance is the highest concern. + $this->db->SetFetchMode(ADODB_FETCH_ASSOC);*/ + } + + /** + * get_objects_with_tag + * + * Use this function to build a page of results that have been tagged with the same tag. + * Pass along a tagger_id to collect only a certain user's tagged objects, and pass along + * none in order to get back all user-tagged objects. Most of the get_*_tag* functions + * operate on the normalized form of tags, because most interfaces for navigating tags + * should use normal form. + * + * @param string - Pass the normalized tag form along to the function. + * @param int (Optional) - The numerical offset to begin display at. Defaults to 0. + * @param int (Optional) - The number of results per page to show. Defaults to 100. + * @param int (Optional) - The unique ID of the 'user' who tagged the object. + * + * @return An array of Object ID numbers that reference your original objects. + */ + function get_objects_with_tag($tag, $offset = 0, $limit = 100, $tagger_id = NULL) { + if(!isset($tag)) { + return false; + } + global $adb; + + $where = "tag = ? "; + $params = array($tag); + + if(isset($tagger_id) && ($tagger_id > 0)) { + $where .= "AND tagger_id = ? "; + array_push($params, $tagger_id); + } + + $prefix = $this->_table_prefix; + + $sql = "SELECT DISTINCT object_id + FROM ${prefix}freetagged_objects INNER JOIN ${prefix}freetags ON (tag_id = id) + WHERE $where + ORDER BY object_id ASC + LIMIT $offset, $limit"; + echo $sql; + $rs = $adb->pquery($sql, $params) or die("Error: $sql"); + $retarr = array(); + while(!$rs->EOF) { + $retarr[] = $rs->fields['object_id']; + $rs->MoveNext(); + } + return $retarr; + } + + /** + * get_objects_with_tag_all + * + * Use this function to build a page of results that have been tagged with the same tag. + * This function acts the same as get_objects_with_tag, except that it returns an unlimited + * number of results. Therefore, it's more useful for internal displays, not for API's. + * Pass along a tagger_id to collect only a certain user's tagged objects, and pass along + * none in order to get back all user-tagged objects. Most of the get_*_tag* functions + * operate on the normalized form of tags, because most interfaces for navigating tags + * should use normal form. + * + * @param string - Pass the normalized tag form along to the function. + * @param int (Optional) - The unique ID of the 'user' who tagged the object. + * + * @return An array of Object ID numbers that reference your original objects. + */ + function get_objects_with_tag_all($tag, $tagger_id = NULL) { + if(!isset($tag)) { + return false; + } + global $adb; + + $where = "tag = ? "; + $params = array($tag); + + if(isset($tagger_id) && ($tagger_id > 0)) { + $where .= "AND tagger_id = ? "; + array_push($params, $tagger_id); + } + $prefix = $this->_table_prefix; + + $sql = "SELECT DISTINCT object_id + FROM ${prefix}freetagged_objects INNER JOIN ${prefix}freetags ON (tag_id = id) + WHERE $where + ORDER BY object_id ASC + "; + //echo $sql; + $rs = $adb->pquery($sql, $params) or die("Error: $sql"); + $retarr = array(); + while(!$rs->EOF) { + $retarr[] = $rs->fields['object_id']; + $rs->MoveNext(); + } + return $retarr; + } + + /** + * get_objects_with_tag_combo + * + * Returns an array of object ID's that have all the tags passed in the + * tagArray parameter. Use this to provide tag combo services to your vtiger_users. + * + * @param array - Pass an array of normalized form tags along to the function. + * @param int (Optional) - The numerical offset to begin display at. Defaults to 0. + * @param int (Optional) - The number of results per page to show. Defaults to 100. + * @param int (Optional) - Restrict the result to objects tagged by a particular user. + * + * @return An array of Object ID numbers that reference your original objects. + */ + function get_objects_with_tag_combo($tagArray, $offset = 0, $limit = 100, $tagger_id = NULL) { + if (!isset($tagArray) || !is_array($tagArray)) { + return false; + } + global $adb; + //$db = &$this->db; + $retarr = array(); + if (count($tagArray) == 0) { + return $retarr; + } + $params = array($tagArray); + if(isset($tagger_id) && ($tagger_id > 0)) { + $tagger_sql = "AND tagger_id = ?"; + array_push($params, $tagger_id); + } else { + $tagger_sql = ""; + } + + foreach ($tagArray as $key => $value) { + $tagArray[$key] = $adb->qstr($value, get_magic_quotes_gpc()); + } + + $tagArray = array_unique($tagArray); + $numTags = count($tagArray); + $prefix = $this->_table_prefix; + + // We must adjust for duplicate normalized tags appearing multiple times in the join by + // counting only the distinct tags. It should also work for an individual user. + + $sql = "SELECT ${prefix}freetagged_objects.object_id, tag, COUNT(DISTINCT tag) AS uniques + FROM ${prefix}freetagged_objects + INNER JOIN ${prefix}freetags ON (${prefix}freetagged_objects.tag_id = ${prefix}freetags.id) + WHERE ${prefix}freetags.tag IN (". generateQuestionMarks($tagArray) .") + $tagger_sql + GROUP BY ${prefix}freetagged_objects.object_id + HAVING uniques = $numTags + LIMIT $offset, $limit"; + $this->debug_text("Tag combo: " . join("+", $tagArray) . " SQL: $sql"); + $rs = $adb->pquery($sql, $params) or die("Error: $sql"); + while(!$rs->EOF) { + $retarr[] = $rs->fields['object_id']; + $rs->MoveNext(); + } + return $retarr; + } + + /** + * get_objects_with_tag_id + * + * Use this function to build a page of results that have been tagged with the same tag. + * This function acts the same as get_objects_with_tag, except that it accepts a numerical + * tag_id instead of a text tag. + * Pass along a tagger_id to collect only a certain user's tagged objects, and pass along + * none in order to get back all user-tagged objects. + * + * @param int - Pass the ID number of the tag. + * @param int (Optional) - The numerical offset to begin display at. Defaults to 0. + * @param int (Optional) - The number of results per page to show. Defaults to 100. + * @param int (Optional) - The unique ID of the 'user' who tagged the object. + * + * @return An array of Object ID numbers that reference your original objects. + */ + function get_objects_with_tag_id($tag_id, $offset = 0, $limit = 100, $tagger_id = NULL) { + if(!isset($tag_id)) { + return false; + } + global $adb; + + $where = "id = ? "; + $params = array($tag_id); + + if(isset($tagger_id) && ($tagger_id > 0)) { + $where .= "AND tagger_id = ?"; + array_push($params, $tagger_id); + } + + $prefix = $this->_table_prefix; + + $sql = "SELECT DISTINCT object_id + FROM ${prefix}freetagged_objects INNER JOIN ${prefix}freetags ON (tag_id = id) + WHERE $where + ORDER BY object_id ASC + LIMIT $offset, $limit "; + $rs = $adb->pquery($sql, $params) or die("Error: $sql"); + $retarr = array(); + while(!$rs->EOF) { + $retarr[] = $rs->fields['object_id']; + $rs->MoveNext(); + } + return $retarr; + } + + + /** + * get_tags_on_object + * + * You can use this function to show the tags on an object. Since it supports both user-specific + * and general modes with the $tagger_id parameter, you can use it twice on a page to make it work + * similar to upcoming.org and flickr, where the page displays your own tags differently than + * other vtiger_users' tags. + * + * @param int The unique ID of the object in question. + * @param int The offset of tags to return. + * @param int The size of the tagset to return. Use a zero size to get all tags. + * @param int The unique ID of the person who tagged the object, if user-level tags only are preferred. + * + * @return array Returns a PHP array with object elements ordered by object ID. Each element is an associative + * array with the following elements: + * - 'tag' => Normalized-form tag + * - 'raw_tag' => The raw-form tag + * - 'tagger_id' => The unique ID of the person who tagged the object with this tag. + */ + function get_tags_on_object($object_id, $offset = 0, $limit = 10, $tagger_id = NULL) { + if(!isset($object_id)) { + return false; + } + + $where = "object_id = ? "; + $params = array($object_id); + + if(isset($tagger_id) && ($tagger_id > 0)) { + $where .= "AND tagger_id = ? "; + array_push($params, $tagger_id); + } + + if($limit <= 0) { + $limit_sql = ""; + } else { + $limit_sql = "LIMIT $offset, $limit"; + } + $prefix = $this->_table_prefix; + + global $adb; + + $sql = "SELECT DISTINCT tag, raw_tag, tagger_id, id + FROM ${prefix}freetagged_objects INNER JOIN ${prefix}freetags ON (tag_id = id) + WHERE $where + ORDER BY id ASC + $limit_sql + "; + //echo '

get_tags_on_object sql is ' .$sql; + $rs = $adb->pquery($sql, $params) or die("Error: $sql"); + $retarr = array(); + while(!$rs->EOF) { + $retarr[] = array( + 'tag' => $rs->fields['tag'], + 'raw_tag' => $rs->fields['raw_tag'], + 'tagger_id' => $rs->fields['tagger_id'] + ); + $rs->MoveNext(); + } + return $retarr; + } + + /** + * safe_tag + * + * Pass individual tag phrases along with object and person ID's in order to + * set a tag on an object. If the tag in its raw form does not yet exist, + * this function will create it. + * Fails transparently on duplicates, and checks for dupes based on the + * block_multiuser_tag_on_object constructor param. + * + * @param int The unique ID of the person who tagged the object with this tag. + * @param int The unique ID of the object in question. + * @param string A raw string from a web form containing tags. + * + * @return boolean Returns true if successful, false otherwise. Does not operate as a transaction. + */ + + function safe_tag($tagger_id, $object_id, $tag, $module) { + if(!isset($tagger_id)||!isset($object_id)||!isset($tag)) { + die("safe_tag argument missing"); + return false; + } + global $adb; + + $normalized_tag = $this->normalize_tag($tag); + $prefix = $this->_table_prefix; + $params = array(); + // First, check for duplicate of the normalized form of the tag on this object. + // Dynamically switch between allowing duplication between vtiger_users on the constructor param 'block_multiuser_tag_on_object'. + // If it's set not to block multiuser tags, then modify the existence + // check to look for a tag by this particular user. Otherwise, the following + // query will reveal whether that tag exists on that object for ANY user. + if ($this->_block_multiuser_tag_on_object == 0) { + $tagger_sql = " AND tagger_id = ? "; + array_push($params, $tagger_id); + } else $tagger_sql = ""; + $sql = "SELECT COUNT(*) as count + FROM ${prefix}freetagged_objects INNER JOIN ${prefix}freetags ON (tag_id = id) + WHERE 1=1 + $tagger_sql + AND object_id = ? + AND tag = ? "; + + array_push($params, $object_id, $normalized_tag); + $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql"); + if($rs->fields['count'] > 0) { + return true; + } + // Then see if a raw tag in this form exists. + $sql = "SELECT id + FROM ${prefix}freetags + WHERE raw_tag = ? "; + $rs = $adb->pquery($sql, array($tag)) or die("Syntax Error: $sql"); + if(!$rs->EOF) { + $tag_id = $rs->fields['id']; + } else { + // Add new tag! + $tag_id = $adb->getUniqueId('vtiger_freetags'); + $sql = "INSERT INTO ${prefix}freetags (id, tag, raw_tag) VALUES (?,?,?)"; + $params = array($tag_id, $normalized_tag, $tag); + $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql"); + + } + if(!($tag_id > 0)) { + return false; + } + $sql = "INSERT INTO ${prefix}freetagged_objects + (tag_id, tagger_id, object_id, tagged_on, module) VALUES (?,?,?, NOW(),?)"; + $params = array($tag_id, $tagger_id, $object_id, $module); + $rs = $adb->pquery($sql, $params) or die("Syntax error: $sql"); + + return true; + } + + /** + * normalize_tag + * + * This is a utility function used to take a raw tag and convert it to normalized form. + * Normalized form is essentially lowercased alphanumeric characters only, + * with no spaces or special characters. + * + * Customize the normalized valid chars with your own set of special characters + * in regex format within the option 'normalized_valid_chars'. It acts as a filter + * to let a customized set of characters through. + * + * After the filter is applied, the function also lowercases the characters using strtolower + * in the current locale. + * + * The default for normalized_valid_chars is a-zA-Z0-9, or english alphanumeric. + * + * @param string An individual tag in raw form that should be normalized. + * + * @return string Returns the tag in normalized form. + */ + function normalize_tag($tag) { + if ($this->_normalize_tags) { + $normalized_valid_chars = $this->_normalized_valid_chars; + $normalized_tag = preg_replace("/[^$normalized_valid_chars]/", "", $tag); + return strtolower($normalized_tag); + } else { + return $tag; + } + + } + + /** + * delete_object_tag + * + * Removes a tag from an object. This does not delete the tag itself from + * the database. Since most applications will only allow a user to delete + * their own tags, it supports raw-form tags as its tag parameter, because + * that's what is usually shown to a user for their own tags. + * + * @param int The unique ID of the person who tagged the object with this tag. + * @param int The ID of the object in question. + * @param string The raw string form of the tag to delete. See above for vtiger_notes. + * + * @return string Returns the tag in normalized form. + */ + function delete_object_tag($tagger_id, $object_id, $tag) { + if(!isset($tagger_id)||!isset($object_id)||!isset($tag)) { + die("delete_object_tag argument missing"); + return false; + } + global $adb; + $tag_id = $this->get_raw_tag_id($tag); + $prefix = $this->_table_prefix; + if($tag_id > 0) { + + $sql = "DELETE FROM ${prefix}freetagged_objects + WHERE tagger_id = ? AND object_id = ? AND tag_id = ? LIMIT 1"; + $params = array($tagger_id, $object_id, $tag_id); + $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql"); + return true; + } else { + return false; + } + } + + /** + * delete_all_object_tags + * + * Removes all tag from an object. This does not + * delete the tag itself from the database. This is most useful for + * cleanup, where an item is deleted and all its tags should be wiped out + * as well. + * + * @param int The ID of the object in question. + * + * @return boolean Returns true if successful, false otherwise. It will return true if the tagged object does not exist. + */ + function delete_all_object_tags($object_id) { + global $adb; + $prefix = $this->_table_prefix; + if($object_id > 0) { + $sql = "DELETE FROM ${prefix}freetagged_objects + WHERE object_id = ? "; + $rs = $adb->pquery($sql, array($object_id)) or die("Syntax Error: $sql"); + return true; + } else { + return false; + } + } + + + /** + * delete_all_object_tags_for_user + * + * Removes all tag from an object for a particular user. This does not + * delete the tag itself from the database. This is most useful for + * implementations similar to del.icio.us, where a user is allowed to retag + * an object from a text box. That way, it becomes a two step operation of + * deleting all the tags, then retagging with whatever's left in the input. + * + * @param int The unique ID of the person who tagged the object with this tag. + * @param int The ID of the object in question. + * + * @return boolean Returns true if successful, false otherwise. It will return true if the tagged object does not exist. + */ + + function delete_all_object_tags_for_user($tagger_id, $object_id) { + if(!isset($tagger_id)||!isset($object_id)) { + die("delete_all_object_tags_for_user argument missing"); + return false; + } + global $adb; + $prefix = $this->_table_prefix; + if($object_id > 0) { + + $sql = "DELETE FROM ${prefix}freetagged_objects + WHERE tagger_id = ? AND object_id = ?"; + $rs = $adb->pquery($sql, array($tagger_id, $object_id)) or die("Syntax Error: $sql"); + return true; + } else { + return false; + } + } + + /** + * get_tag_id + * + * Retrieves the unique ID number of a tag based upon its normal form. Actually, + * using this function is dangerous, because multiple tags can exist with the same + * normal form, so be careful, because this will only return one, assuming that + * if you're going by normal form, then the individual tags are interchangeable. + * + * @param string The normal form of the tag to fetch. + * + * @return string Returns the tag in normalized form. + */ + function get_tag_id($tag) { + if(!isset($tag)) { + die("get_tag_id argument missing"); + return false; + } + global $adb; + + $prefix = $this->_table_prefix; + + $sql = "SELECT id FROM ${prefix}freetags + WHERE tag = ? LIMIT 1 "; + $rs = $adb->pquery($sql, array($tag)) or die("Syntax Error: $sql"); + return $rs->fields['id']; + + } + + /** + * get_raw_tag_id + * + * Retrieves the unique ID number of a tag based upon its raw form. If a single + * unique record is needed, then use this function instead of get_tag_id, + * because raw_tags are unique. + * + * @param string The raw string form of the tag to fetch. + * + * @return string Returns the tag in normalized form. + */ + + function get_raw_tag_id($tag) { + if(!isset($tag)) { + die("get_tag_id argument missing"); + return false; + } + global $adb; + $prefix = $this->_table_prefix; + + $sql = "SELECT id FROM ${prefix}freetags + WHERE raw_tag = ? LIMIT 1 "; + $rs = $adb->pquery($sql, array($tag)) or die("Syntax Error: $sql"); + return $rs->fields['id']; + + } + + /** + * tag_object + * + * This function allows you to pass in a string directly from a form, which is then + * parsed for quoted phrases and special characters, normalized and converted into tags. + * The tag phrases are then individually sent through the safe_tag() method for processing + * and the object referenced is set with that tag. + * + * This method has been refactored to automatically look for existing tags and run + * adds/updates/deletes as appropriate. + * + * @param int The unique ID of the person who tagged the object with this tag. + * @param int The ID of the object in question. + * @param string The raw string form of the tag to delete. See above for vtiger_notes. + * @param int Whether to skip the update portion for objects that haven't been tagged. (Default: 1) + * + * @return string Returns the tag in normalized form. + */ + function tag_object($tagger_id, $object_id, $tag_string, $module, $skip_updates = 1) { + if($tag_string == '') { + // If an empty string was passed, just return true, don't die. + // die("Empty tag string passed"); + return true; + } + $tagArray = $this->_parse_tags($tag_string); + + $oldTags = $this->get_tags_on_object($object_id, 0, 0, $tagger_id); + + $preserveTags = array(); + + if (($skip_updates == 0) && (count($oldTags) > 0)) { + foreach ($oldTags as $tagItem) { + if (!in_array($tagItem['raw_tag'], $tagArray)) { + // We need to delete old tags that don't appear in the new parsed string. + $this->delete_object_tag($tagger_id, $object_id, $tagItem['raw_tag']); + } else { + // We need to preserve old tags that appear (to save timestamps) + $preserveTags[] = $tagItem['raw_tag']; + } + } + } + $newTags = array_diff($tagArray, $preserveTags); + + $this->_tag_object_array($tagger_id, $object_id, $newTags, $module); + + return true; + } + + /** + * _tag_object_array + * + * Private method to add tags to an object from an array. + * + * @param int Unique ID of tagger + * @param int Unique ID of object + * @param array Array of tags to add. + * + * @return boolean True if successful, false otherwise. + */ + function _tag_object_array($tagger_id, $object_id, $tagArray, $module) { + foreach($tagArray as $tag) { + $tag = trim($tag); + if(($tag != '') && (strlen($tag) <= $this->_MAX_TAG_LENGTH)) { + if(get_magic_quotes_gpc()) { + $tag = addslashes($tag); + } + $this->safe_tag($tagger_id, $object_id, $tag, $module); + } + } + return true; + } + + /** + * _parse_tags + * + * Private method to parse tags out of a string and into an array. + * + * @param string String to parse. + * + * @return array Returns an array of the raw "tags" parsed according to the freetag settings. + */ + + function _parse_tags($tag_string) { + $newwords = array(); + if ($tag_string == '') { + // If the tag string is empty, return the empty set. + return $newwords; + } + # Perform tag parsing + if(get_magic_quotes_gpc()) { + $query = stripslashes(trim($tag_string)); + } else { + $query = trim($tag_string); + } + $words = preg_split('/(")/', $query,-1,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE); + $delim = 0; + foreach ($words as $key => $word) + { + if ($word == '"') { + $delim++; + continue; + } + if (($delim % 2 == 1) && $words[$key - 1] == '"') { + $newwords[] = $word; + } else { + $newwords = array_merge($newwords, preg_split('/\s+/', $word, -1, PREG_SPLIT_NO_EMPTY)); + } + } + return $newwords; + } + + /** + * update_tags + * + * This method supports a user updating their set of all tags on an object + * in a streamlined manner. Very useful for interfaces where all tags on an + * object from a user may be edited through a single text box. + */ + + /** + * get_most_popular_tags + * + * This function returns the most popular tags in the freetag system, with + * offset and limit support for pagination. It also supports restricting to + * an individual user. Call it with no parameters for a list of 25 most popular + * tags. + * + * @param int The unique ID of the person to restrict results to. + * @param int The offset of the tag to start at. + * @param int The number of tags to return in the result set. + * + * @return array Returns a PHP array with tags ordered by popularity descending. + * Each element is an associative array with the following elements: + * - 'tag' => Normalized-form tag + * - 'count' => The number of objects tagged with this tag. + */ + + function get_most_popular_tags($tagger_id = NULL, $offset = 0, $limit = 25) { + global $adb; + $params = array(); + if(isset($tagger_id) && ($tagger_id > 0)) { + $tagger_sql = "AND tagger_id = ?"; + array_push($params, $tagger_id); + } else { + $tagger_sql = ""; + } + $prefix = $this->_table_prefix; + + $sql = "SELECT tag, COUNT(*) as count + FROM ${prefix}freetags INNER JOIN ${prefix}freetagged_objects ON (id = tag_id) + WHERE 1 + $tagger_sql + GROUP BY tag + ORDER BY count DESC, tag ASC + LIMIT $offset, $limit"; + + $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql"); + $retarr = array(); + while(!$rs->EOF) { + $retarr[] = array( + 'tag' => $rs->fields['tag'], + 'count' => $rs->fields['count'] + ); + $rs->MoveNext(); + } + + return $retarr; + + } + + /** + * count_tags + * + * Returns the total number of tag->object links in the system. + * It might be useful for pagination at times, but i'm not sure if I actually use + * this anywhere. Restrict to a person's tagging by using the $tagger_id parameter. + * + * @param int The unique ID of the person to restrict results to. + * + * @return int Returns the count + */ + function count_tags($tagger_id = NULL) { + global $adb; + $params = array(); + if(isset($tagger_id) && ($tagger_id > 0)) { + $tagger_sql = "AND tagger_id = ?"; + array_push($params, $tagger_id); + } else { + $tagger_sql = ""; + } + $prefix = $this->_table_prefix; + + $sql = "SELECT COUNT(*) as count + FROM ${prefix}freetags INNER JOIN ${prefix}freetagged_objects ON (id = tag_id) + WHERE 1 + $tagger_sql + "; + + $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql"); + if(!$rs->EOF) { + return $rs->fields['count']; + } + return false; + + } + + /** + * get_tag_cloud_html + * + * This is a pretty straightforward, flexible method that automatically + * generates some html that can be dropped in as a tag cloud. + * It uses explicit font sizes inside of the style attribute of SPAN + * elements to accomplish the differently sized objects. + * + * It will also link every tag to $tag_page_url, appended with the + * normalized form of the tag. You should adapt this value to your own + * tag detail page's URL. + * + * @param int The maximum number of tags to return. (default: 100) + * @param int The minimum font size in the cloud. (default: 10) + * @param int The maximum number of tags to return. (default: 20) + * @param string The "units" for the font size (i.e. 'px', 'pt', 'em') (default: px) + * @param string The class to use for all spans in the cloud. (default: cloud_tag) + * @param string The tag page URL (default: /tag/) + * + * @return string Returns an HTML snippet that can be used directly as a tag cloud. + */ + + function get_tag_cloud_html($module="",$tagger_id = NULL,$obj_id= NULL,$num_tags = 100, $min_font_size = 10, $max_font_size = 20, $font_units = 'px', $span_class = '', $tag_page_url = '/tag/') { + global $theme; + $theme_path="themes/".$theme."/"; + $image_path=$theme_path."images/"; + $tag_list = $this->get_tag_cloud_tags($num_tags, $tagger_id,$module,$obj_id); + if(!$tag_list[0]) return; + // Get the maximum qty of tagged objects in the set + $max_qty = max(array_values($tag_list[0])); + // Get the min qty of tagged objects in the set + $min_qty = min(array_values($tag_list[0])); + + // For ever additional tagged object from min to max, we add + // $step to the font size. + $spread = $max_qty - $min_qty; + if (0 == $spread) { // Divide by zero + $spread = 1; + } + $step = ($max_font_size - $min_font_size)/($spread); + + // Since the original tag_list is alphabetically ordered, + // we can now create the tag cloud by just putting a span + // on each element, multiplying the diff between min and qty + // by $step. + $cloud_html = ''; + $cloud_spans = array(); + if($module =='') + $module = 'All'; + if($module != 'All') + { + foreach($tag_list[0] as $tag => $qty) { + $size = $min_font_size + ($qty - $min_qty) * 3; + $cloud_span[] = '' . htmlspecialchars(stripslashes($tag)) . ''; + + } + }else + { + foreach($tag_list[0] as $tag => $qty) { + $size = $min_font_size + ($qty - $min_qty) * 3; + $cloud_span[] = '' . htmlspecialchars(stripslashes($tag)) . ''; + + } + + } + $cloud_html = join("\n ", $cloud_span); + + return $cloud_html; + + } + + /* + * get_tag_cloud_tags + * + * This is a function built explicitly to set up a page with most popular tags + * that contains an alphabetically sorted list of tags, which can then be sized + * or colored by popularity. + * + * Also known more popularly as Tag Clouds! + * + * Here's the example case: http://upcoming.org/tag/ + * + * @param int The maximum number of tags to return. + * + * @return array Returns an array where the keys are normalized tags, and the + * values are numeric quantity of objects tagged with that tag. + */ + + function get_tag_cloud_tags($max = 100, $tagger_id = NULL,$module = "",$obj_id = NULL) { + global $adb; + $params = array(); + if(isset($tagger_id) && ($tagger_id > 0)) { + $tagger_sql = " AND tagger_id = ?"; + array_push($params, $tagger_id); + } else { + $tagger_sql = ""; + } + + if($module != "") { + $tagger_sql .= " AND module = ?"; + array_push($params, $module); + } else { + $tagger_sql .= ""; + } + + if(isset($obj_id) && $obj_id > 0) { + $tagger_sql .= " AND object_id = ?"; + array_push($params, $obj_id); + } else { + $tagger_sql .= ""; + } + + $prefix = $this->_table_prefix; + $sql = "SELECT tag,tag_id,COUNT(object_id) AS quantity + FROM ${prefix}freetags INNER JOIN ${prefix}freetagged_objects + ON (${prefix}freetags.id = tag_id) + WHERE 1=1 + $tagger_sql + GROUP BY tag,tag_id + ORDER BY quantity DESC LIMIT 0, $max"; + //echo $sql; + $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql"); + $retarr = array(); + while(!$rs->EOF) { + $retarr[$rs->fields['tag']] = $rs->fields['quantity']; + $retarr1[$rs->fields['tag']] = $rs->fields['tag_id']; + $rs->MoveNext(); + } + if($retarr) ksort($retarr); + if($retarr1) ksort($retarr1); + $return_value[]=$retarr; + $return_value[]=$retarr1; + return $return_value; + + } + + /** + * similar_tags + * + * Finds tags that are "similar" or related to the given tag. + * It does this by looking at the other tags on objects tagged with the tag specified. + * Confusing? Think of it like e-commerce's "Other vtiger_users who bought this also bought," + * as that's exactly how this works. + * + * Returns an empty array if no tag is passed, or if no related tags are found. + * Hint: You can detect related tags returned with count($retarr > 0) + * + * It's important to note that the quantity passed back along with each tag + * is a measure of the *strength of the relation* between the original tag + * and the related tag. It measures the number of objects tagged with both + * the original tag and its related tag. + * + * Thanks to Myles Grant for contributing this function! + * + * @param string The raw normalized form of the tag to fetch. + * @param int The maximum number of tags to return. + * + * @return array Returns an array where the keys are normalized tags, and the + * values are numeric quantity of objects tagged with BOTH tags, sorted by + * number of occurences of that tag (high to low). + */ + + function similar_tags($tag, $max = 100) { + $retarr = array(); + if(!isset($tag)) { + return $retarr; + } + global $adb; + + // This query was written using a double join for PHP. If you're trying to eke + // additional performance and are running MySQL 4.X, you might want to try a subselect + // and compare perf numbers. + $prefix = $this->_table_prefix; + + $sql = "SELECT t1.tag, COUNT( o1.object_id ) AS quantity + FROM ${prefix}freetagged_objects o1 + INNER JOIN ${prefix}freetags t1 ON ( t1.id = o1.tag_id ) + INNER JOIN ${prefix}freetagged_objects o2 ON ( o1.object_id = o2.object_id ) + INNER JOIN ${prefix}freetags t2 ON ( t2.id = o2.tag_id ) + WHERE t2.tag = ? AND t1.tag != ? + GROUP BY o1.tag_id + ORDER BY quantity DESC + LIMIT 0, ?"; + + $rs = $adb->pquery($sql, array($tag, $tag, $max)) or die("Syntax Error: $sql"); + while(!$rs->EOF) { + $retarr[$rs->fields['tag']] = $rs->fields['quantity']; + $rs->MoveNext(); + } + + return $retarr; + } + + /** + * similar_objects + * + * This method implements a simple ability to find some objects in the database + * that might be similar to an existing object. It determines this by trying + * to match other objects that share the same tags. + * + * The user of the method has to use a threshold (by default, 1) which specifies + * how many tags other objects must have in common to match. If the original object + * has no tags, then it won't match anything. Matched objects are returned in order + * of most similar to least similar. + * + * The more tags set on a database, the better this method works. Since this + * is such an expensive operation, it requires a limit to be set via max_objects. + * + * @param int The unique ID of the object to find similar objects for. + * @param int The Threshold of tags that must be found in common (default: 1) + * @param int The maximum number of similar objects to return (default: 5). + * @param int Optionally pass a tagger id to restrict similarity to a tagger's view. + * + * @return array Returns a PHP array with matched objects ordered by strength of match descending. + * Each element is an associative array with the following elements: + * - 'strength' => A floating-point strength of match from 0-1.0 + * - 'object_id' => Unique ID of the matched object + * + */ + function similar_objects($object_id, $threshold = 1, $max_objects = 5, $tagger_id = NULL) { + global $adb; + $retarr = array(); + + $object_id = intval($object_id); + $threshold = intval($threshold); + $max_objects = intval($max_objects); + if (!isset($object_id) || !($object_id > 0)) { + return $retarr; + } + if ($threshold <= 0) { + return $retarr; + } + if ($max_objects <= 0) { + return $retarr; + } + + // Pass in a zero-limit to get all tags. + $tagItems = $this->get_tags_on_object($object_id, 0, 0); + + $tagArray = array(); + foreach ($tagItems as $tagItem) { + $tagArray[] = $tagItem['tag']; + } + $tagArray = array_unique($tagArray); + + $numTags = count($tagArray); + if ($numTags == 0) { + return $retarr; // Return empty set of matches + } + + $prefix = $this->_table_prefix; + + $sql = "SELECT matches.object_id, COUNT( matches.object_id ) AS num_common_tags + FROM ${prefix}freetagged_objects as matches + INNER JOIN ${prefix}freetags as tags ON ( tags.id = matches.tag_id ) + WHERE tags.tag IN (". generateQuestionMarks($tagArray) .") + GROUP BY matches.object_id + HAVING num_common_tags >= ? + ORDER BY num_common_tags DESC + LIMIT 0, ? "; + + $rs = $adb->pquery($sql, array($tagArray, $threshold, $max_objects)) or die("Syntax Error: $sql, Error: " . $adb->ErrorMsg()); + while(!$rs->EOF) { + $retarr[] = array ( + 'object_id' => $rs->fields['object_id'], + 'strength' => ($rs->fields['num_common_tags'] / $numTags) + ); + $rs->MoveNext(); + } + + return $retarr; + } + + + /* + * Prints debug text if debug is enabled. + * + * @param string The text to output + * @return boolean Always returns true + */ + function debug_text($text) { + if ($this->_debug) { + echo "$text
\n"; + } + return true; + } + +} + diff --git a/oss/vtiger/trunk/include/freetag/license.txt b/oss/vtiger/trunk/include/freetag/license.txt new file mode 100644 index 00000000..632ed008 --- /dev/null +++ b/oss/vtiger/trunk/include/freetag/license.txt @@ -0,0 +1,479 @@ +Freetag is dual licensed using BSD and LGPL. + +In plain English, you do not need to distribute your application in source code form, nor do you need to distribute Freetag or ADOdb source code, provided you follow the rest of terms of the BSD license. + +For more info about Freetag, visit http://www.getluky.net. + +========================================================== +BSD-Style License + +Copyright (c) 2004, 2005 Gordon D. Luk +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Freetag nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +========================================================== + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. diff --git a/oss/vtiger/trunk/include/htmlpurifier/CREDITS b/oss/vtiger/trunk/include/htmlpurifier/CREDITS new file mode 100644 index 00000000..d0cc45af --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/CREDITS @@ -0,0 +1,9 @@ + +CREDITS + +Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks +to the DevNetwork Community for their help (see docs/ref-devnetwork.html for +more details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake +for letting me package his fantastic XSS cheatsheet for a smoketest. + + vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/INSTALL b/oss/vtiger/trunk/include/htmlpurifier/INSTALL new file mode 100644 index 00000000..e08b44d9 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/INSTALL @@ -0,0 +1,373 @@ + +Install + How to install HTML Purifier + +HTML Purifier is designed to run out of the box, so actually using the +library is extremely easy. (Although... if you were looking for a +step-by-step installation GUI, you've downloaded the wrong software!) + +While the impatient can get going immediately with some of the sample +code at the bottom of this library, it's well worth reading this entire +document--most of the other documentation assumes that you are familiar +with these contents. + + +--------------------------------------------------------------------------- +1. Compatibility + +HTML Purifier is PHP 5 only, and is actively tested from PHP 5.0.5 and +up. It has no core dependencies with other libraries. PHP +4 support was deprecated on December 31, 2007 with HTML Purifier 3.0.0. + +These optional extensions can enhance the capabilities of HTML Purifier: + + * iconv : Converts text to and from non-UTF-8 encodings + * bcmath : Used for unit conversion and imagecrash protection + * tidy : Used for pretty-printing HTML + + +--------------------------------------------------------------------------- +2. Reconnaissance + +A big plus of HTML Purifier is its inerrant support of standards, so +your web-pages should be standards-compliant. (They should also use +semantic markup, but that's another issue altogether, one HTML Purifier +cannot fix without reading your mind.) + +HTML Purifier can process these doctypes: + +* XHTML 1.0 Transitional (default) +* XHTML 1.0 Strict +* HTML 4.01 Transitional +* HTML 4.01 Strict +* XHTML 1.1 + +...and these character encodings: + +* UTF-8 (default) +* Any encoding iconv supports (with crippled internationalization support) + +These defaults reflect what my choices would be if I were authoring an +HTML document, however, what you choose depends on the nature of your +codebase. If you don't know what doctype you are using, you can determine +the doctype from this identifier at the top of your source code: + + + +...and the character encoding from this code: + + + +If the character encoding declaration is missing, STOP NOW, and +read 'docs/enduser-utf8.html' (web accessible at +http://htmlpurifier.org/docs/enduser-utf8.html). In fact, even if it is +present, read this document anyway, as many websites specify their +document's character encoding incorrectly. + + +--------------------------------------------------------------------------- +3. Including the library + +The procedure is quite simple: + + require_once '/path/to/library/HTMLPurifier.auto.php'; + +This will setup an autoloader, so the library's files are only included +when you use them. + +Only the contents in the library/ folder are necessary, so you can remove +everything else when using HTML Purifier in a production environment. + +If you installed HTML Purifier via PEAR, all you need to do is: + + require_once 'HTMLPurifier.auto.php'; + +Please note that the usual PEAR practice of including just the classes you +want will not work with HTML Purifier's autoloading scheme. + +Advanced users, read on; other users can skip to section 4. + +Autoload compatibility +---------------------- + + HTML Purifier attempts to be as smart as possible when registering an + autoloader, but there are some cases where you will need to change + your own code to accomodate HTML Purifier. These are those cases: + + PHP VERSION IS LESS THAN 5.1.2, AND YOU'VE DEFINED __autoload + Because spl_autoload_register() doesn't exist in early versions + of PHP 5, HTML Purifier has no way of adding itself to the autoload + stack. Modify your __autoload function to test + HTMLPurifier_Bootstrap::autoload($class) + + For example, suppose your autoload function looks like this: + + function __autoload($class) { + require str_replace('_', '/', $class) . '.php'; + return true; + } + + A modified version with HTML Purifier would look like this: + + function __autoload($class) { + if (HTMLPurifier_Bootstrap::autoload($class)) return true; + require str_replace('_', '/', $class) . '.php'; + return true; + } + + Note that there *is* some custom behavior in our autoloader; the + original autoloader in our example would work for 99% of the time, + but would fail when including language files. + + AN __autoload FUNCTION IS DECLARED AFTER OUR AUTOLOADER IS REGISTERED + spl_autoload_register() has the curious behavior of disabling + the existing __autoload() handler. Users need to explicitly + spl_autoload_register('__autoload'). Because we use SPL when it + is available, __autoload() will ALWAYS be disabled. If __autoload() + is declared before HTML Purifier is loaded, this is not a problem: + HTML Purifier will register the function for you. But if it is + declared afterwards, it will mysteriously not work. This + snippet of code (after your autoloader is defined) will fix it: + + spl_autoload_register('__autoload') + + Users should also be on guard if they use a version of PHP previous + to 5.1.2 without an autoloader--HTML Purifier will define __autoload() + for you, which can collide with an autoloader that was added by *you* + later. + + +For better performance +---------------------- + + Opcode caches, which greatly speed up PHP initialization for scripts + with large amounts of code (HTML Purifier included), don't like + autoloaders. We offer an include file that includes all of HTML Purifier's + files in one go in an opcode cache friendly manner: + + // If /path/to/library isn't already in your include path, uncomment + // the below line: + // require '/path/to/library/HTMLPurifier.path.php'; + + require 'HTMLPurifier.includes.php'; + + Optional components still need to be included--you'll know if you try to + use a feature and you get a class doesn't exists error! The autoloader + can be used in conjunction with this approach to catch classes that are + missing. Simply add this afterwards: + + require 'HTMLPurifier.autoload.php'; + +Standalone version +------------------ + + HTML Purifier has a standalone distribution; you can also generate + a standalone file from the full version by running the script + maintenance/generate-standalone.php . The standalone version has the + benefit of having most of its code in one file, so parsing is much + faster and the library is easier to manage. + + If HTMLPurifier.standalone.php exists in the library directory, you + can use it like this: + + require '/path/to/HTMLPurifier.standalone.php'; + + This is equivalent to including HTMLPurifier.includes.php, except that + the contents of standalone/ will be added to your path. To override this + behavior, specify a new HTMLPURIFIER_PREFIX where standalone files can + be found (usually, this will be one directory up, the "true" library + directory in full distributions). Don't forget to set your path too! + + The autoloader can be added to the end to ensure the classes are + loaded when necessary; otherwise you can manually include them. + To use the autoloader, use this: + + require 'HTMLPurifier.autoload.php'; + +For advanced users +------------------ + + HTMLPurifier.auto.php performs a number of operations that can be done + individually. These are: + + HTMLPurifier.path.php + Puts /path/to/library in the include path. For high performance, + this should be done in php.ini. + + HTMLPurifier.autoload.php + Registers our autoload handler HTMLPurifier_Bootstrap::autoload($class). + + You can do these operations by yourself--in fact, you must modify your own + autoload handler if you are using a version of PHP earlier than PHP 5.1.2 + (See "Autoload compatibility" above). + + +--------------------------------------------------------------------------- +4. Configuration + +HTML Purifier is designed to run out-of-the-box, but occasionally HTML +Purifier needs to be told what to do. If you answer no to any of these +questions, read on; otherwise, you can skip to the next section (or, if you're +into configuring things just for the heck of it, skip to 4.3). + +* Am I using UTF-8? +* Am I using XHTML 1.0 Transitional? + +If you answered no to any of these questions, instantiate a configuration +object and read on: + + $config = HTMLPurifier_Config::createDefault(); + + +4.1. Setting a different character encoding + +You really shouldn't use any other encoding except UTF-8, especially if you +plan to support multilingual websites (read section three for more details). +However, switching to UTF-8 is not always immediately feasible, so we can +adapt. + +HTML Purifier uses iconv to support other character encodings, as such, +any encoding that iconv supports +HTML Purifier supports with this code: + + $config->set('Core', 'Encoding', /* put your encoding here */); + +An example usage for Latin-1 websites (the most common encoding for English +websites): + + $config->set('Core', 'Encoding', 'ISO-8859-1'); + +Note that HTML Purifier's support for non-Unicode encodings is crippled by the +fact that any character not supported by that encoding will be silently +dropped, EVEN if it is ampersand escaped. If you want to work around +this, you are welcome to read docs/enduser-utf8.html for a fix, +but please be cognizant of the issues the "solution" creates (for this +reason, I do not include the solution in this document). + + +4.2. Setting a different doctype + +For those of you using HTML 4.01 Transitional, you can disable +XHTML output like this: + + $config->set('HTML', 'Doctype', 'HTML 4.01 Transitional'); + +Other supported doctypes include: + + * HTML 4.01 Strict + * HTML 4.01 Transitional + * XHTML 1.0 Strict + * XHTML 1.0 Transitional + * XHTML 1.1 + + +4.3. Other settings + +There are more configuration directives which can be read about +here: They're a bit boring, +but they can help out for those of you who like to exert maximum control over +your code. Some of the more interesting ones are configurable at the +demo and are well worth looking into +for your own system. + +For example, you can fine tune allowed elements and attributes, convert +relative URLs to absolute ones, and even autoparagraph input text! These +are, respectively, %HTML.Allowed, %URI.MakeAbsolute and %URI.Base, and +%AutoFormat.AutoParagraph. The %Namespace.Directive naming convention +translates to: + + $config->set('Namespace', 'Directive', $value); + +E.g. + + $config->set('HTML', 'Allowed', 'p,b,a[href],i'); + $config->set('URI', 'Base', 'http://www.example.com'); + $config->set('URI', 'MakeAbsolute', true); + $config->set('AutoFormat', 'AutoParagraph', true); + + +--------------------------------------------------------------------------- +5. Caching + +HTML Purifier generates some cache files (generally one or two) to speed up +its execution. For maximum performance, make sure that +library/HTMLPurifier/DefinitionCache/Serializer is writeable by the webserver. + +If you are in the library/ folder of HTML Purifier, you can set the +appropriate permissions using: + + chmod -R 0755 HTMLPurifier/DefinitionCache/Serializer + +If the above command doesn't work, you may need to assign write permissions +to all. This may be necessary if your webserver runs as nobody, but is +not recommended since it means any other user can write files in the +directory. Use: + + chmod -R 0777 HTMLPurifier/DefinitionCache/Serializer + +You can also chmod files via your FTP client; this option +is usually accessible by right clicking the corresponding directory and +then selecting "chmod" or "file permissions". + +Starting with 2.0.1, HTML Purifier will generate friendly error messages +that will tell you exactly what you have to chmod the directory to, if in doubt, +follow its advice. + +If you are unable or unwilling to give write permissions to the cache +directory, you can either disable the cache (and suffer a performance +hit): + + $config->set('Core', 'DefinitionCache', null); + +Or move the cache directory somewhere else (no trailing slash): + + $config->set('Cache', 'SerializerPath', '/home/user/absolute/path'); + + +--------------------------------------------------------------------------- +6. Using the code + +The interface is mind-numbingly simple: + + $purifier = new HTMLPurifier(); + $clean_html = $purifier->purify( $dirty_html ); + +...or, if you're using the configuration object: + + $purifier = new HTMLPurifier($config); + $clean_html = $purifier->purify( $dirty_html ); + +That's it! For more examples, check out docs/examples/ (they aren't very +different though). Also, docs/enduser-slow.html gives advice on what to +do if HTML Purifier is slowing down your application. + + +--------------------------------------------------------------------------- +7. Quick install + +First, make sure library/HTMLPurifier/DefinitionCache/Serializer is +writable by the webserver (see Section 5: Caching above for details). +If your website is in UTF-8 and XHTML Transitional, use this code: + +purify($dirty_html); +?> + +If your website is in a different encoding or doctype, use this code: + +set('Core', 'Encoding', 'ISO-8859-1'); // replace with your encoding + $config->set('HTML', 'Doctype', 'HTML 4.01 Transitional'); // replace with your doctype + $purifier = new HTMLPurifier($config); + + $clean_html = $purifier->purify($dirty_html); +?> + + vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/LICENSE b/oss/vtiger/trunk/include/htmlpurifier/LICENSE new file mode 100644 index 00000000..21ee8faa --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/NEWS b/oss/vtiger/trunk/include/htmlpurifier/NEWS new file mode 100644 index 00000000..5509f417 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/NEWS @@ -0,0 +1,842 @@ +NEWS ( CHANGELOG and HISTORY ) HTMLPurifier +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + += KEY ==================== + # Breaks back-compat + ! Feature + - Bugfix + + Sub-comment + . Internal change +========================== + +3.3.0, released 2009-02-16 +! Implement CSS property 'overflow' when %CSS.AllowTricky is true. +! Implement generic property list classess +- Fix bug with testEncodingSupportsASCII() algorithm when iconv() implementation + does not do the "right thing" with characters not supported in the output + set. +- Spellcheck UTF-8: The Secret To Character Encoding +- Fix improper removal of the contents of elements with only whitespace. Thanks + Eric Wald for reporting. +- Fix broken test suite in versions of PHP without spl_autoload_register() +- Fix degenerate case with YouTube filter involving double hyphens. + Thanks Pierre Attar for reporting. +- Fix YouTube rendering problem on certain versions of Firefox. +- Fix CSSDefinition Printer problems with decorators +- Add text parameter to unit tests, forces text output +. Add verbose mode to command line test runner, use (--verbose) +. Turn on unit tests for UnitConverter +. Fix missing version number in configuration %Attr.DefaultImageAlt (added 3.2.0) +. Fix newline errors that caused spurious failures when CRLF HTML Purifier was + tested on Linux. +. Removed trailing whitespace from all text files, see + remote-trailing-whitespace.php maintenance script. +. Convert configuration to use property list backend. + +3.2.0, released 2008-10-31 +# Using %Core.CollectErrors forces line number/column tracking on, whereas + previously you could theoretically turn it off. +# HTMLPurifier_Injector->notifyEnd() is formally deprecated. Please + use handleEnd() instead. +! %Output.AttrSort for when you need your attributes in alphabetical order to + deal with a bug in FCKEditor. Requested by frank farmer. +! Enable HTML comments when %HTML.Trusted is on. Requested by Waldo Jaquith. +! Proper support for name attribute. It is now allowed and equivalent to the id + attribute in a and img tags, and is only converted to id when %HTML.TidyLevel + is heavy (for all doctypes). +! %AutoFormat.RemoveEmpty to remove some empty tags from documents. Please don't + use on hand-written HTML. +! Add error-cases for unsupported elements in MakeWellFormed. This enables + the strategy to be used, standalone, on untrusted input. +! %Core.AggressivelyFixLt is on by default. This causes more sensible + processing of left angled brackets in smileys and other whatnot. +! Test scripts now have a 'type' parameter, which lets you say 'htmlpurifier', + 'phpt', 'vtest', etc. in order to only execute those tests. This supercedes + the --only-phpt parameter, although for backwards-compatibility the flag + will still work. +! AutoParagraph auto-formatter will now preserve double-newlines upon output. + Users who are not performing inbound filtering, this may seem a little + useless, but as a bonus, the test suite and handling of edge cases is also + improved. +! Experimental implementation of forms for %HTML.Trusted +! Track column numbers when maintain line numbers is on +! Proprietary 'background' attribute on table-related elements converted into + corresponding CSS. Thanks Fusemail for sponsoring this feature! +! Add forward(), forwardUntilEndToken(), backward() and current() to Injector + supertype. +! HTMLPurifier_Injector->handleEnd() permits modification to end tokens. The + time of operation varies slightly from notifyEnd() as *all* end tokens are + processed by the injector before they are subject to the well-formedness rules. +! %Attr.DefaultImageAlt allows overriding default behavior of setting alt to + basename of image when not present. +! %AutoFormat.DisplayLinkURI neuters tags into plain text URLs. +- Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs, + the other involving an undefined $is_folder error. +- Throw error when %Core.Encoding is set to a spurious value. Previously, + this errored silently and returned false. +- Redirected stderr to stdout for flush error output. +- %URI.DisableExternal will now use the host in %URI.Base if %URI.Host is not + available. +- Do not re-munge URL if the output URL has the same host as the input URL. + Requested by Chris. +- Fix error in documentation regarding %Filter.ExtractStyleBlocks +- Prevent ]]> from triggering %Core.ConvertDocumentToFragment +- Fix bug with inline elements in blockquotes conflicting with strict doctype +- Detect if HTML support is disabled for DOM by checking for loadHTML() method. +- Fix bug where dots and double-dots in absolute URLs without hostname were + not collapsed by URIFilter_MakeAbsolute. +- Fix bug with anonymous modules operating on SafeEmbed or SafeObject elements + by reordering their addition. +- Will now throw exception on many error conditions during lexer creation; also + throw an exception when MaintainLineNumbers is true, but a non-tracksLineNumbers + is being used. +- Detect if domxml extension is loaded, and use DirectLEx accordingly. +- Improve handling of big numbers with floating point arithmetic in UnitConverter. + Reported by David Morton. +. Strategy_MakeWellFormed now operates in-place, saving memory and allowing + for more interesting filter-backtracking +. New HTMLPurifier_Injector->rewind() functionality, allows injectors to rewind + index to reprocess tokens. +. StringHashParser now allows for multiline sections with "empty" content; + previously the section would remain undefined. +. Added --quick option to multitest.php, which tests only the most recent + release for each series. +. Added --distro option to multitest.php, which accepts either 'normal' or + 'standalone'. This supercedes --exclude-normal and --exclude-standalone + +3.1.1, released 2008-06-19 +# %URI.Munge now, by default, does not munge resources (for example, ) + In order to enable this again, please set %URI.MungeResources to true. +! More robust imagecrash protection with height/width CSS with %CSS.MaxImgLength, + and height/width HTML with %HTML.MaxImgLength. +! %URI.MungeSecretKey for secure URI munging. Thanks Chris + for sponsoring this feature. Check out the corresponding documentation + for details. (Att Nightly testers: The API for this feature changed before + the general release. Namely, rename your directives %URI.SecureMungeSecretKey => + %URI.MungeSecretKey and and %URI.SecureMunge => %URI.Munge) +! Implemented post URI filtering. Set member variable $post to true to set + a URIFilter as such. +! Allow modules to define injectors via $info_injector. Injectors are + automatically disabled if injector's needed elements are not found. +! Support for "safe" objects added, use %HTML.SafeObject and %HTML.SafeEmbed. + Thanks Chris for sponsoring. If you've been using ad hoc code from the + forums, PLEASE use this instead. +! Added substitutions for %e, %n, %a and %p in %URI.Munge (in order, + embedded, tag name, attribute name, CSS property name). See %URI.Munge + for more details. Requested by Jochem Blok. +- Disable percent height/width attributes for img. +- AttrValidator operations are now atomic; updates to attributes are not + manifest in token until end of operations. This prevents naughty internal + code from directly modifying CurrentToken when they're not supposed to. + This semantics change was requested by frank farmer. +- Percent encoding checks enabled for URI query and fragment +- Fix stray backslashes in font-family; CSS Unicode character escapes are + now properly resolved (although *only* in font-family). Thanks Takeshi Terada + for reporting. +- Improve parseCDATA algorithm to take into account newline normalization +- Account for browser confusion between Yen character and backslash in + Shift_JIS encoding. This fix generalizes to any other encoding which is not + a strict superset of printable ASCII. Thanks Takeshi Terada for reporting. +- Fix missing configuration parameter in Generator calls. Thanks vs for the + partial patch. +- Improved adherence to Unicode by checking for non-character codepoints. + Thanks Geoffrey Sneddon for reporting. This may result in degraded + performance for extremely large inputs. +- Allow CSS property-value pair ''text-decoration: none''. Thanks Jochem Blok + for reporting. +. Added HTMLPurifier_UnitConverter and HTMLPurifier_Length for convenient + handling of CSS-style lengths. HTMLPurifier_AttrDef_CSS_Length now uses + this class. +. API of HTMLPurifier_AttrDef_CSS_Length changed from __construct($disable_negative) + to __construct($min, $max). __construct(true) is equivalent to + __construct('0'). +. Added HTMLPurifier_AttrDef_Switch class +. Rename HTMLPurifier_HTMLModule_Tidy->construct() to setup() and bubble method + up inheritance hierarchy to HTMLPurifier_HTMLModule. All HTMLModules + get this called with the configuration object. All modules now + use this rather than __construct(), although legacy code using constructors + will still work--the new format, however, lets modules access the + configuration object for HTML namespace dependant tweaks. +. AttrDef_HTML_Pixels now takes a single construction parameter, pixels. +. ConfigSchema data-structure heavily optimized; on average it uses a third + the memory it did previously. The interface has changed accordingly, + consult changes to HTMLPurifier_Config for details. +. Variable parsing types now are magic integers instead of strings +. Added benchmark for ConfigSchema +. HTMLPurifier_Generator requires $config and $context parameters. If you + don't know what they should be, use HTMLPurifier_Config::createDefault() + and new HTMLPurifier_Context(). +. Printers now properly distinguish between output configuration, and + target configuration. This is not applicable to scripts using + the Printers for HTML Purifier related tasks. +. HTML/CSS Printers must be primed with prepareGenerator($gen_config), otherwise + fatal errors will ensue. +. URIFilter->prepare can return false in order to abort loading of the filter +. Factory for AttrDef_URI implemented, URI#embedded to indicate URI that embeds + an external resource. +. %URI.Munge functionality factored out into a post-filter class. +. Added CurrentCSSProperty context variable during CSS validation + +3.1.0, released 2008-05-18 +# Unnecessary references to objects (vestiges of PHP4) removed from method + signatures. The following methods do not need references when assigning from + them and will result in E_STRICT errors if you try: + + HTMLPurifier_Config->get*Definition() [* = HTML, CSS] + + HTMLPurifier_ConfigSchema::instance() + + HTMLPurifier_DefinitionCacheFactory::instance() + + HTMLPurifier_DefinitionCacheFactory->create() + + HTMLPurifier_DoctypeRegistry->register() + + HTMLPurifier_DoctypeRegistry->get() + + HTMLPurifier_HTMLModule->addElement() + + HTMLPurifier_HTMLModule->addBlankElement() + + HTMLPurifier_LanguageFactory::instance() +# Printer_ConfigForm's get*() functions were static-ified +# %HTML.ForbiddenAttributes requires attribute declarations to be in the + form of tag@attr, NOT tag.attr (which will throw an error and won't do + anything). This is for forwards compatibility with XML; you'd do best + to migrate an %HTML.AllowedAttributes directives to this syntax too. +! Allow index to be false for config from form creation +! Added HTMLPurifier::VERSION constant +! Commas, not dashes, used for serializer IDs. This change is forwards-compatible + and allows for version numbers like "3.1.0-dev". +! %HTML.Allowed deals gracefully with whitespace anywhere, anytime! +! HTML Purifier's URI handling is a lot more robust, with much stricter + validation checks and better percent encoding handling. Thanks Gareth Heyes + for indicating security vulnerabilities from lax percent encoding. +! Bootstrap autoloader deals more robustly with classes that don't exist, + preventing class_exists($class, true) from barfing. +- InterchangeBuilder now alphabetizes its lists +- Validation error in configdoc output fixed +- Iconv and other encoding errors muted even with custom error handlers that + do not honor error_reporting +- Add protection against imagecrash attack with CSS height/width +- HTMLPurifier::instance() created for consistency, is equivalent to getInstance() +- Fixed and revamped broken ConfigForm smoketest +- Bug with bool/null fields in Printer_ConfigForm fixed +- Bug with global forbidden attributes fixed +- Improved error messages for allowed and forbidden HTML elements and attributes +- Missing (or null) in configdoc documentation restored +- If DOM throws and exception during parsing with PH5P (occurs in newer versions + of DOM), HTML Purifier punts to DirectLex +- Fatal error with unserialization of ScriptRequired +- Created directories are now chmod'ed properly +- Fixed bug with fallback languages in LanguageFactory +- Standalone testing setup properly with autoload +. Out-of-date documentation revised +. UTF-8 encoding check optimization as suggested by Diego +. HTMLPurifier_Error removed in favor of exceptions +. More copy() function removed; should use clone instead +. More extensive unit tests for HTMLDefinition +. assertPurification moved to central harness +. HTMLPurifier_Generator accepts $config and $context parameters during + instantiation, not runtime +. Double-quotes outside of attribute values are now unescaped + +3.1.0rc1, released 2008-04-22 +# Autoload support added. Internal require_once's removed in favor of an + explicit require list or autoloading. To use HTML Purifier, + you must now either use HTMLPurifier.auto.php + or HTMLPurifier.includes.php; setting the include path and including + HTMLPurifier.php is insufficient--in such cases include HTMLPurifier.autoload.php + as well to register our autoload handler (or modify your autoload function + to check HTMLPurifier_Bootstrap::getPath($class)). You can also use + HTMLPurifier.safe-includes.php for a less performance friendly but more + user-friendly library load. +# HTMLPurifier_ConfigSchema static functions are officially deprecated. Schema + information is stored in the ConfigSchema directory, and the + maintenance/generate-schema-cache.php generates the schema.ser file, which + is now instantiated. Support for userland schema changes coming soon! +# HTMLPurifier_Config will now throw E_USER_NOTICE when you use a directive + alias; to get rid of these errors just modify your configuration to use + the new directive name. +# HTMLPurifier->addFilter is deprecated; built-in filters can now be + enabled using %Filter.$filter_name or by setting your own filters using + %Filter.Custom +# Directive-level safety properties superceded in favor of module-level + safety. Internal method HTMLModule->addElement() has changed, although + the externally visible HTMLDefinition->addElement has *not* changed. +! Extra utility classes for testing and non-library operations can + be found in extras/. Specifically, these are FSTools and ConfigDoc. + You may find a use for these in your own project, but right now they + are highly experimental and volatile. +! Integration with PHPT allows for automated smoketests +! Limited support for proprietary HTML elements, namely , sponsored + by Chris. You can enable them with %HTML.Proprietary if your client + demands them. +! Support for !important CSS cascade modifier. By default, this will be stripped + from CSS, but you can enable it using %CSS.AllowImportant +! Support for display and visibility CSS properties added, set %CSS.AllowTricky + to true to use them. +! HTML Purifier now has its own Exception hierarchy under HTMLPurifier_Exception. + Developer error (not enduser error) can cause these to be triggered. +! Experimental kses() wrapper introduced with HTMLPurifier.kses.php +! Finally %CSS.AllowedProperties for tweaking allowed CSS properties without + mucking around with HTMLPurifier_CSSDefinition +! ConfigDoc output has been enhanced with version and deprecation info. +! %HTML.ForbiddenAttributes and %HTML.ForbiddenElements implemented. +- Autoclose now operates iteratively, i.e.
now has + both span tags closed. +- Various HTMLPurifier_Config convenience functions now accept another parameter + $schema which defines what HTMLPurifier_ConfigSchema to use besides the + global default. +- Fix bug with trusted script handling in libxml versions later than 2.6.28. +- Fix bug in ExtractStyleBlocks with comments in style tags +- Fix bug in comment parsing for DirectLex +- Flush output now displayed when in command line mode for unit tester +- Fix bug with rgb(0, 1, 2) color syntax with spaces inside shorthand syntax +- HTMLPurifier_HTMLDefinition->addAttribute can now be called multiple times + on the same element without emitting errors. +- Fixed fatal error in PH5P lexer with invalid tag names +. Plugins now get their own changelogs according to project conventions. +. Convert tokens to use instanceof, reducing memory footprint and + improving comparison speed. +. Dry runs now supported in SimpleTest; testing facilities improved +. Bootstrap class added for handling autoloading functionality +. Implemented recursive glob at FSTools->globr +. ConfigSchema now has instance methods for all corresponding define* + static methods. +. A couple of new historical maintenance scripts were added. +. HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php split into two files +. tests/index.php can now be run from any directory. +. HTMLPurifier_Token subclasses split into seperate files +. HTMLPURIFIER_PREFIX now is defined in Bootstrap.php, NOT HTMLPurifier.php +. HTMLPURIFIER_PREFIX can now be defined outside of HTML Purifier +. New --php=php flag added, allows PHP executable to be specified (command + line only!) +. htmlpurifier_add_test() preferred method to translate test files in to + classes, because it handles PHPT files too. +. Debugger class is deprecated and will be removed soon. +. Command line argument parsing for testing scripts revamped, now --opt value + format is supported. +. Smoketests now cleanup after magic quotes +. Generator now can output comments (however, comments are still stripped + from HTML Purifier output) +. HTMLPurifier_ConfigSchema->validate() deprecated in favor of + HTMLPurifier_VarParser->parse() +. Integers auto-cast into float type by VarParser. +. HTMLPURIFIER_STRICT removed; no validation is performed on runtime, only + during cache generation +. Reordered script calls in maintenance/flush.php +. Command line scripts now honor exit codes +. When --flush fails in unit testers, abort tests and print message +. Improved documentation in docs/dev-flush.html about the maintenance scripts +. copy() methods removed in favor of clone keyword + +3.0.0, released 2008-01-06 +# HTML Purifier is PHP 5 only! The 2.1.x branch will be maintained + until PHP 4 is completely deprecated, but no new features will be added + to it. + + Visibility declarations added + + Constructor methods renamed to __construct() + + PHP4 reference cruft removed (in progress) +! CSS properties are now case-insensitive +! DefinitionCacheFactory now can register new implementations +! New HTMLPurifier_Filter_ExtractStyleBlocks for extracting 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 ''; + } +?> + + +
+ +
+ + +]]> +

+ Warning: 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt new file mode 100644 index 00000000..7fa6536b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -0,0 +1,11 @@ +Filter.YouTube +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ This directive enables YouTube video embedding in HTML Purifier. Check + this document + on embedding videos for more information on what this filter does. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.txt new file mode 100644 index 00000000..f2d25a1b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.txt @@ -0,0 +1,3 @@ +Filter +DESCRIPTION: Directives for turning filters on and off, or specifying custom filters. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt new file mode 100644 index 00000000..d436ed01 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksEscaping.txt @@ -0,0 +1,14 @@ +FilterParam.ExtractStyleBlocksEscaping +TYPE: bool +VERSION: 3.0.0 +DEFAULT: true +ALIASES: Filter.ExtractStyleBlocksEscaping +--DESCRIPTION-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt new file mode 100644 index 00000000..3943529c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksScope.txt @@ -0,0 +1,29 @@ +FilterParam.ExtractStyleBlocksScope +TYPE: string/null +VERSION: 3.0.0 +DEFAULT: NULL +ALIASES: Filter.ExtractStyleBlocksScope +--DESCRIPTION-- + +

+ 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 #user-content and a user uses the + selector a:hover, the final selector will be + #user-content a:hover. +

+

+ The comma shorthand may be used; consider the above example, with + #user-content, #user-content2, the final selector will + be #user-content a:hover, #user-content2 a:hover. +

+

+ Warning: 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt new file mode 100644 index 00000000..cafccf8b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.ExtractStyleBlocksTidyImpl.txt @@ -0,0 +1,15 @@ +FilterParam.ExtractStyleBlocksTidyImpl +TYPE: mixed/null +VERSION: 3.1.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ If left NULL, HTML Purifier will attempt to instantiate a csstidy + class to use for internal cleaning. This will usually be good enough. +

+

+ However, for trusted user input, you can set this to false 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.txt new file mode 100644 index 00000000..dff9784b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/FilterParam.txt @@ -0,0 +1,3 @@ +FilterParam +DESCRIPTION: Configuration for filters. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt new file mode 100644 index 00000000..3e231d2d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -0,0 +1,22 @@ +HTML.Allowed +TYPE: itext/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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: + element1[attr1|attr2],element2.... You can also use + newlines instead of commas to separate elements. +

+

+ Warning: + All of the constraints on the component directives are still enforced. + The syntax is a subset of TinyMCE's valid_elements + 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt new file mode 100644 index 00000000..fcf093f1 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt @@ -0,0 +1,19 @@ +HTML.AllowedAttributes +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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). +

+

+ Warning: If another directive conflicts with the + elements here, that 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt new file mode 100644 index 00000000..888d5581 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -0,0 +1,18 @@ +HTML.AllowedElements +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ 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. +

+

+ Warning: If another directive conflicts with the + elements here, that directive will win and override. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt new file mode 100644 index 00000000..5a59a55c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt @@ -0,0 +1,20 @@ +HTML.AllowedModules +TYPE: lookup/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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. +

+

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt new file mode 100644 index 00000000..45ae469e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt @@ -0,0 +1,18 @@ +HTML.BlockWrapper +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'p' +--DESCRIPTION-- + +

+ 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. +

+

+ Example: by default value, + <blockquote>Foo</blockquote> would become + <blockquote><p>Foo</p></blockquote>. + The <p> tags can be replaced with whatever you desire, + as long as it is a block level element. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt new file mode 100644 index 00000000..52461887 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt new file mode 100644 index 00000000..a64e3d7c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt new file mode 100644 index 00000000..103db754 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt @@ -0,0 +1,33 @@ +HTML.DefinitionID +TYPE: string/null +DEFAULT: NULL +VERSION: 2.0.0 +--DESCRIPTION-- + +

+ 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: +

+
+$config = HTMLPurifier_Config::createDefault();
+$config->set('HTML', 'DefinitionID', '1');
+$def = $config->getHTMLDefinition();
+$def->addAttribute('a', 'tabindex', 'Number');
+
+

+ 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. +

+

+ You must specify a value to this directive to use the + advanced API features. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt new file mode 100644 index 00000000..229ae026 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt @@ -0,0 +1,16 @@ +HTML.DefinitionRev +TYPE: int +VERSION: 2.0.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt new file mode 100644 index 00000000..9dab497f --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt new file mode 100644 index 00000000..57358f9b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt @@ -0,0 +1,21 @@ +HTML.ForbiddenAttributes +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ While this directive is similar to %HTML.AllowedAttributes, for + forwards-compatibility with XML, this attribute has a different syntax. Instead of + tag.attr, use tag@attr. To disallow href + attributes in a tags, set this directive to + a@href. You can also disallow an attribute globally with + attr or *@attr (either syntax is fine; the latter + is provided for consistency with %HTML.AllowedAttributes). +

+

+ Warning: This directive complements %HTML.ForbiddenElements, + accordingly, check + out that directive for a discussion of why you + should think twice before using this directive. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt new file mode 100644 index 00000000..93a53e14 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt @@ -0,0 +1,20 @@ +HTML.ForbiddenElements +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ 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. +

+

+ 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 img + with the expectation of preventing images on your site, you'll be in for + a nasty surprise when people start using the background-image + CSS property. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt new file mode 100644 index 00000000..e424c386 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt @@ -0,0 +1,14 @@ +HTML.MaxImgLength +TYPE: int/null +DEFAULT: 1200 +VERSION: 3.1.1 +--DESCRIPTION-- +

+ This directive controls the maximum number of pixels in the width and + height attributes in img 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). +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt new file mode 100644 index 00000000..62e8e160 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt @@ -0,0 +1,12 @@ +HTML.Parent +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'div' +--DESCRIPTION-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt new file mode 100644 index 00000000..dfb72049 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt @@ -0,0 +1,12 @@ +HTML.Proprietary +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to allow proprietary elements and attributes in your + documents, as per HTMLPurifier_HTMLModule_Proprietary. + Warning: This can cause your documents to stop + validating! +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt new file mode 100644 index 00000000..f635a685 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt @@ -0,0 +1,14 @@ +HTML.SafeEmbed +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ 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. + Highly experimental. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt new file mode 100644 index 00000000..32967b88 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt @@ -0,0 +1,14 @@ +HTML.SafeObject +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ 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. + Highly experimental. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt new file mode 100644 index 00000000..a8b1de56 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt new file mode 100644 index 00000000..b4c271b7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt new file mode 100644 index 00000000..4186ccd0 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt @@ -0,0 +1,24 @@ +HTML.TidyLevel +TYPE: string +VERSION: 2.0.0 +DEFAULT: 'medium' +--DESCRIPTION-- + +

General level of cleanliness the Tidy module should enforce. +There are four allowed values:

+
+
none
+
No extra tidying should be done
+
light
+
Only fix elements that would be discarded otherwise due to + lack of support in doctype
+
medium
+
Enforce best practices
+
heavy
+
Transform all deprecated elements and attributes to standards + compliant equivalents
+
+ +--ALLOWED-- +'none', 'light', 'medium', 'heavy' +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt new file mode 100644 index 00000000..996762bd --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt new file mode 100644 index 00000000..89133b1a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt new file mode 100644 index 00000000..2a47e384 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.txt new file mode 100644 index 00000000..f32ceb5b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.txt @@ -0,0 +1,3 @@ +HTML +DESCRIPTION: Configuration regarding allowed HTML. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt new file mode 100644 index 00000000..08921fde --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt new file mode 100644 index 00000000..79f8ad82 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt @@ -0,0 +1,13 @@ +Output.Newline +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt new file mode 100644 index 00000000..232b0236 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt @@ -0,0 +1,14 @@ +Output.SortAttr +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ If true, HTML Purifier will sort attributes by name before writing them back + to the document, converting a tag like: <el b="" a="" c="" /> + to <el a="" b="" c="" />. 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt new file mode 100644 index 00000000..06bab00a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt @@ -0,0 +1,25 @@ +Output.TidyFormat +TYPE: bool +VERSION: 1.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Determines whether or not to run Tidy on the final output for pretty + formatting reasons, such as indentation and wrap. +

+

+ 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. +

+

+ 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). +

+--ALIASES-- +Core.TidyFormat +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.txt new file mode 100644 index 00000000..7849d60d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.txt @@ -0,0 +1,3 @@ +Output +DESCRIPTION: Configuration relating to the generation of (X)HTML. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt new file mode 100644 index 00000000..071bc029 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.txt new file mode 100644 index 00000000..5025f9d1 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.txt @@ -0,0 +1,3 @@ +Test +DESCRIPTION: Developer testing configuration for our unit tests. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt new file mode 100644 index 00000000..98fdfe92 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt new file mode 100644 index 00000000..876f0680 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt @@ -0,0 +1,17 @@ +URI.Base +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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). + Warning: If set, the scheme on this URI + overrides the one specified by %URI.DefaultScheme. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt new file mode 100644 index 00000000..728e378c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt @@ -0,0 +1,10 @@ +URI.DefaultScheme +TYPE: string +DEFAULT: 'http' +--DESCRIPTION-- + +

+ Defines through what scheme the output will be served, in order to + select the proper object validator when no scheme information is present. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt new file mode 100644 index 00000000..f05312ba --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt @@ -0,0 +1,11 @@ +URI.DefinitionID +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Unique identifier for a custom-built URI definition. If you want + to add custom URIFilters, you must specify this value. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt new file mode 100644 index 00000000..80cfea93 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt @@ -0,0 +1,11 @@ +URI.DefinitionRev +TYPE: int +VERSION: 2.1.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ Revision identifier for your custom definition. See + %HTML.DefinitionRev for details. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt new file mode 100644 index 00000000..71ce025a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt @@ -0,0 +1,14 @@ +URI.Disable +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- + +

+ 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). +

+ +--ALIASES-- +Attr.DisableURI +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt new file mode 100644 index 00000000..13c122c8 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt new file mode 100644 index 00000000..abcc1efd --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt new file mode 100644 index 00000000..51e6ea91 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -0,0 +1,12 @@ +URI.DisableResources +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt new file mode 100644 index 00000000..ee83b121 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt @@ -0,0 +1,19 @@ +URI.Host +TYPE: string/null +VERSION: 1.2.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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. + Note: 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt new file mode 100644 index 00000000..0b6df762 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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 moo.com will catch moo.com.example.com. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt new file mode 100644 index 00000000..4214900a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt @@ -0,0 +1,13 @@ +URI.MakeAbsolute +TYPE: bool +VERSION: 2.1.0 +DEFAULT: false +--DESCRIPTION-- + +

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt new file mode 100644 index 00000000..58c81dcc --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt @@ -0,0 +1,83 @@ +URI.Munge +TYPE: string/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ 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 %s where + the url-encoded original URI should be inserted (sample: + http://www.google.com/url?q=%s). +

+

+ Uses for this directive: +

+
    +
  • + 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). Notice: + Many security experts believe that this form of protection does not deter spam-bots. +
  • +
  • + 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. +
  • +
+

+ 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 meta tags. To revert to + previous behavior, please use %URI.MungeResources. +

+

+ 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. +

+

+ Starting with HTML Purifier 3.1.1, there is also these substitutions: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescriptionExample <a href="">
%r1 - The URI embeds a resource
(blank) - The URI is merely a link
%nThe name of the tag this URI came froma
%mThe name of the attribute this URI came fromhref
%pThe name of the CSS property this URI came from, or blank if irrelevant
+

+ 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. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt new file mode 100644 index 00000000..6fce0fdc --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt @@ -0,0 +1,17 @@ +URI.MungeResources +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ If true, any URI munging directives like %URI.Munge + will also apply to embedded resources, such as <img src="">. + Be careful enabling this directive if you have a redirector script + that does not use the Location HTTP header; all of your images + and other embedded resources will break. +

+

+ Warning: It is strongly advised you use this in conjunction + %URI.MungeSecretKey to mitigate the security risk of an open redirector. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt new file mode 100644 index 00000000..0d00f62e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt @@ -0,0 +1,30 @@ +URI.MungeSecretKey +TYPE: string/null +VERSION: 3.1.1 +DEFAULT: NULL +--DESCRIPTION-- +

+ 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: +

+ +
$checksum === sha1($secret_key . ':' . $url)
+ +

+ If the output is TRUE, the redirector script should accept the URI. +

+ +

+ 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. +

+ +

+ Remember this has no effect if %URI.Munge is not on. +

+--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt new file mode 100644 index 00000000..23331a4e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.txt b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.txt new file mode 100644 index 00000000..a13060f3 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.txt @@ -0,0 +1,3 @@ +URI +DESCRIPTION: Features regarding Uniform Resource Identifiers. +--# vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini new file mode 100644 index 00000000..58e0ce4a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini @@ -0,0 +1,3 @@ +name = "HTML Purifier" + +; vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ContentSets.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ContentSets.php new file mode 100644 index 00000000..3b6e96f5 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ContentSets.php @@ -0,0 +1,155 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Context.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Context.php new file mode 100644 index 00000000..9ddf0c54 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Context.php @@ -0,0 +1,82 @@ +_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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Definition.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Definition.php new file mode 100644 index 00000000..a7408c97 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Definition.php @@ -0,0 +1,39 @@ +setup) return; + $this->setup = true; + $this->doSetup($config); + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache.php new file mode 100644 index 00000000..fa5a087b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache.php @@ -0,0 +1,108 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php new file mode 100644 index 00000000..b0fb6d0c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php @@ -0,0 +1,62 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php new file mode 100644 index 00000000..d4cc35c4 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php @@ -0,0 +1,43 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in new file mode 100644 index 00000000..f33fe37a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in @@ -0,0 +1,47 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README new file mode 100644 index 00000000..ba005de7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php new file mode 100644 index 00000000..3adefbb6 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php @@ -0,0 +1,91 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Doctype.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Doctype.php new file mode 100644 index 00000000..1e3c574c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Doctype.php @@ -0,0 +1,60 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php new file mode 100644 index 00000000..d6552aa5 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php @@ -0,0 +1,103 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ElementDef.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ElementDef.php new file mode 100644 index 00000000..b55c7bd7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ElementDef.php @@ -0,0 +1,175 @@ +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 .= ' | ' . $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Encoder.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Encoder.php new file mode 100644 index 00000000..79a42d26 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Encoder.php @@ -0,0 +1,426 @@ + 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 + * , + * 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup.php new file mode 100644 index 00000000..b4dfce94 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup.php @@ -0,0 +1,44 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser new file mode 100644 index 00000000..f2b8b8f2 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityParser.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityParser.php new file mode 100644 index 00000000..8c384472 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/EntityParser.php @@ -0,0 +1,144 @@ + '"', + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorCollector.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorCollector.php new file mode 100644 index 00000000..6713eaf7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorCollector.php @@ -0,0 +1,209 @@ +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 '

' . $this->locale->getMessage('ErrorCollector: No errors') . '

'; + } else { + return '
  • ' . implode('
  • ', $ret) . '
'; + } + + } + + 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 .= '
'; + // W3C uses an icon to indicate the severity of the error. + $error = $this->locale->getErrorName($severity); + $string .= "$error "; + if (!is_null($line) && !is_null($col)) { + $string .= "Line $line, Column $col: "; + } else { + $string .= 'End of Document: '; + } + $string .= '' . $this->generator->escape($msg) . ' '; + $string .= '
'; + // Here, have a marker for the character on the column appropriate. + // Be sure to clip extremely long lines. + //$string .= '
';
+                //$string .= '';
+                //$string .= '
'; + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorStruct.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorStruct.php new file mode 100644 index 00000000..9bc8996e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/ErrorStruct.php @@ -0,0 +1,60 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Exception.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Exception.php new file mode 100644 index 00000000..be85b4c5 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Exception.php @@ -0,0 +1,12 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php new file mode 100644 index 00000000..970f9e0c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php @@ -0,0 +1,135 @@ + 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 #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 in a font-family prop). + if ($config->get('FilterParam', 'ExtractStyleBlocksEscaping')) { + $css = str_replace( + array('<', '>', '&'), + array('\3C ', '\3E ', '\26 '), + $css + ); + } + return $css; + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php new file mode 100644 index 00000000..aca972f6 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php @@ -0,0 +1,39 @@ +]+>.+?'. + 'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?#s'; + $pre_replace = '\1'; + return preg_replace($pre_regex, $pre_replace, $html); + } + + public function postFilter($html, $config, $context) { + $post_regex = '#([A-Za-z0-9\-_]+)#'; + 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 ''. + ''. + ''. + ''; + + } +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Generator.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Generator.php new file mode 100644 index 00000000..a1b96b9e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Generator.php @@ -0,0 +1,183 @@ + 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 'name . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + $attr = $this->generateAttributes($token->attr, $token->name); + return '<' . $token->name . ($attr ? ' ' : '') . $attr . + ( $this->_xhtml ? ' /': '' ) //
v.
+ . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Text) { + return $this->escape($token->data, ENT_NOQUOTES); + + } elseif ($token instanceof HTMLPurifier_Token_Comment) { + return ''; + } 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 + $data = preg_replace('#//\s*$#', '', $token->data); + return ''; + } + + /** + * 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php new file mode 100644 index 00000000..3368821c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php @@ -0,0 +1,420 @@ +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) { + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule.php new file mode 100644 index 00000000..072cf680 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule.php @@ -0,0 +1,244 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php new file mode 100644 index 00000000..3d66f1b4 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php @@ -0,0 +1,31 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php new file mode 100644 index 00000000..fdf7b323 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php @@ -0,0 +1,25 @@ + array( + 0 => array('Style'), + // 'xml:space' => false, + 'class' => 'NMTOKENS', + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php new file mode 100644 index 00000000..ff936905 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php @@ -0,0 +1,38 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php new file mode 100644 index 00000000..44c22f6f --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php @@ -0,0 +1,118 @@ + '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 . This one's a little complex + // because it maps to multiple elements. + + } +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php new file mode 100644 index 00000000..d7e9bdd2 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php @@ -0,0 +1,31 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php new file mode 100644 index 00000000..e6ed53ae --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php @@ -0,0 +1,40 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php new file mode 100644 index 00000000..df33927b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php @@ -0,0 +1,143 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php new file mode 100644 index 00000000..1d15f272 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php @@ -0,0 +1,35 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php new file mode 100644 index 00000000..d908a0ad --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php @@ -0,0 +1,18 @@ +addBlankElement($name); + $element->attr['name'] = 'ID'; + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php new file mode 100644 index 00000000..5f1b14ab --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php @@ -0,0 +1,14 @@ + array( + 'lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php new file mode 100644 index 00000000..193c1011 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php @@ -0,0 +1,47 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php new file mode 100644 index 00000000..8ff0b5ed --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php @@ -0,0 +1,36 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php new file mode 100644 index 00000000..dd36a3de --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php @@ -0,0 +1,33 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php new file mode 100644 index 00000000..b26a0a30 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php @@ -0,0 +1,27 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php new file mode 100644 index 00000000..635e8f2d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -0,0 +1,33 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php new file mode 100644 index 00000000..bbda7a21 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php @@ -0,0 +1,50 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php new file mode 100644 index 00000000..cecdea6c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php @@ -0,0 +1,54 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php new file mode 100644 index 00000000..eb78464c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php @@ -0,0 +1,24 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php new file mode 100644 index 00000000..f314ced3 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php @@ -0,0 +1,66 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php new file mode 100644 index 00000000..2b844ecc --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php @@ -0,0 +1,23 @@ +addBlankElement($name); + $e->attr = array( + 'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget() + ); + } + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php new file mode 100644 index 00000000..ae77c718 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php @@ -0,0 +1,71 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php new file mode 100644 index 00000000..c9d47064 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php @@ -0,0 +1,207 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php new file mode 100644 index 00000000..61ff85ce --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php @@ -0,0 +1,24 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php new file mode 100644 index 00000000..9960b1dd --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php @@ -0,0 +1,9 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php new file mode 100644 index 00000000..9c0e0319 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php @@ -0,0 +1,14 @@ + array( + 'xml:lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php new file mode 100644 index 00000000..78f38781 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php @@ -0,0 +1,403 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/IDAccumulator.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/IDAccumulator.php new file mode 100644 index 00000000..d546cd75 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/IDAccumulator.php @@ -0,0 +1,53 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector.php new file mode 100644 index 00000000..bc093b80 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector.php @@ -0,0 +1,233 @@ +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; + } + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php new file mode 100644 index 00000000..8cc95254 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php @@ -0,0 +1,340 @@ +armor['MakeWellFormed_TagClosedError'] = true; + return $par; + } + + public function handleText(&$token) { + $text = $token->data; + // Does the current parent allow

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:

PAR1\n\nPAR2 (see State 2) + // ------------ + $token = array($this->_pStart()); + $this->_splitText($text, $token); + } + } else { + // State 2:
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:
PAR1PAR1\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:
PAR1
+ // ---- + + // State 2.2.2:
PAR1PAR1
+ // ---- + } + } + // Is the current parent a

tag? + } elseif ( + !empty($this->currentNesting) && + $this->currentNesting[count($this->currentNesting)-1]->name == 'p' + ) { + // State 3.1: ...

PAR1 + // ---- + + // State 3.2: ...

PAR1\n\nPAR2 + // ------------ + $token = array(); + $this->_splitText($text, $token); + // Abort! + } else { + // State 4.1: ...PAR1 + // ---- + + // State 4.2: ...PAR1\n\nPAR2 + // ------------ + } + } + + public function handleElement(&$token) { + // We don't have to check if we're already in a

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:

... + // --- + + // 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:

PAR1

\n\n + // --- + + // Quite frankly, this should be handled by splitText + $token = array($this->_pStart(), $token); + } else { + // State 1.1.1:

PAR1

+ // --- + + // State 1.1.2:

+ // --- + + // State 1.1.3:
PAR + // --- + } + + } else { + // State 1.2.1:
+ // --- + + // Lookahead to see if

is needed. + if ($this->_pLookAhead()) { + // State 1.3.1:

PAR1\n\nPAR2 + // --- + $token = array($this->_pStart(), $token); + } else { + // State 1.3.2:
PAR1
+ // --- + + // State 1.3.3:
PAR1
\n\n
+ // --- + } + } + } else { + // State 2.3: ...
+ // ----- + } + } else { + if ($this->_isInline($token)) { + // State 3.1: + // --- + // This is where the {p} tag is inserted, not reflected in + // inputTokens yet, however. + $token = array($this->_pStart(), $token); + } else { + // State 3.2:
+ // ----- + } + + $i = null; + if ($this->backward($i, $prev)) { + if ( + !$prev instanceof HTMLPurifier_Token_Text + ) { + // State 3.1.1: ...

{p} + // --- + + // State 3.2.1: ...

+ // ----- + + if (!is_array($token)) $token = array($token); + array_unshift($token, new HTMLPurifier_Token_Text("\n\n")); + } else { + // State 3.1.2: ...

\n\n{p} + // --- + + // State 3.2.2: ...

\n\n
+ // ----- + + // Note: PAR cannot occur because PAR would have been + // wrapped in

tags. + } + } + } + } else { + // State 2.2:

  • + // ---- + + // State 2.4:

    + // --- + } + } + + /** + * 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

    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

    + } + + } + + /** + * 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

    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)) { + //

    PAR1
    + // ---- + // Terminate early, since we hit a block element + return false; + } + } elseif ($current instanceof HTMLPurifier_Token_Text) { + if (strpos($current->data, "\n\n") !== false) { + //
    PAR1PAR1\n\nPAR2 + // ---- + return true; + } else { + //
    PAR1PAR1... + // ---- + } + } + return null; + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php new file mode 100644 index 00000000..9dce9bd0 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php @@ -0,0 +1,26 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php new file mode 100644 index 00000000..296dac28 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php @@ -0,0 +1,46 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php new file mode 100644 index 00000000..3c706a33 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php @@ -0,0 +1,45 @@ + array('href')); + + public function prepare($config, $context) { + $this->docURL = $config->get('AutoFormatParam', 'PurifierLinkifyDocURL'); + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php new file mode 100644 index 00000000..d85ca97d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php @@ -0,0 +1,42 @@ +config = $config; + $this->context = $context; + $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 && $next->is_whitespace) 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php new file mode 100644 index 00000000..34158286 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php @@ -0,0 +1,87 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language.php new file mode 100644 index 00000000..3e2be03b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language.php @@ -0,0 +1,163 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php new file mode 100644 index 00000000..d52fcb7a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/classes/en-x-test.php @@ -0,0 +1,12 @@ + 'HTML Purifier X' +); + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php new file mode 100644 index 00000000..806c83fb --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en-x-testmini.php @@ -0,0 +1,12 @@ + 'HTML Purifier XNone' +); + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en.php new file mode 100644 index 00000000..aab2e52e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Language/messages/en.php @@ -0,0 +1,62 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/LanguageFactory.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/LanguageFactory.php new file mode 100644 index 00000000..baa4422e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/LanguageFactory.php @@ -0,0 +1,198 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Length.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Length.php new file mode 100644 index 00000000..8d2a46b7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Length.php @@ -0,0 +1,115 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer.php new file mode 100644 index 00000000..94588699 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer.php @@ -0,0 +1,298 @@ +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( + '//s', + array('HTMLPurifier_Lexer', 'CDATACallback'), + $string + ); + } + + /** + * Special CDATA case that is especially convoluted for )#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 "asdf" but not "asdf " + // 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
    , 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PEARSax3.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PEARSax3.php new file mode 100644 index 00000000..57cffa82 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PEARSax3.php @@ -0,0 +1,106 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php new file mode 100644 index 00000000..fa1bf973 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php @@ -0,0 +1,3906 @@ +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)-> // + getElementsByTagName('body')->item(0)-> // + getElementsByTagName('div')->item(0) //
    + , $tokens); + return $tokens; + } + +} + +/* + +Copyright 2007 Jeroen van der Meer + +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 (""), + 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' => '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' => '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PercentEncoder.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PercentEncoder.php new file mode 100644 index 00000000..a43c44f4 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PercentEncoder.php @@ -0,0 +1,98 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer.php new file mode 100644 index 00000000..e7eb82e8 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer.php @@ -0,0 +1,176 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php new file mode 100644 index 00000000..81f98659 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php @@ -0,0 +1,38 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css new file mode 100644 index 00000000..3ff1a88a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js new file mode 100644 index 00000000..cba00c9b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php new file mode 100644 index 00000000..744dec65 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php @@ -0,0 +1,368 @@ +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 .= '[Reset]'; + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php new file mode 100644 index 00000000..8a8f126b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php @@ -0,0 +1,272 @@ +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', + 'Block: ' . + $this->escape($this->listifyTagLookup($def->block->elements)),0,0); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element('td', + 'Inline: ' . + $this->escape($this->listifyTagLookup($def->inline->elements)),0,0); + + } elseif ($def->type == 'custom') { + + $ret .= $this->element('td', ''.ucfirst($def->type).': ' . + $def->dtd_regex); + + } else { + $ret .= $this->element('td', + ''.ucfirst($def->type).': ' . + $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 = " . $this->getClass($obj, 'AttrDef_') . ''; + } + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyList.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyList.php new file mode 100644 index 00000000..2b99fb7b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyList.php @@ -0,0 +1,86 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php new file mode 100644 index 00000000..8f250443 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php @@ -0,0 +1,32 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy.php new file mode 100644 index 00000000..24628652 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy.php @@ -0,0 +1,26 @@ +strategies as $strategy) { + $tokens = $strategy->execute($tokens, $config, $context); + } + return $tokens; + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/Core.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/Core.php new file mode 100644 index 00000000..d90e1586 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/Core.php @@ -0,0 +1,18 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php new file mode 100644 index 00000000..f8180239 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php @@ -0,0 +1,328 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php new file mode 100644 index 00000000..0ec811f8 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php @@ -0,0 +1,455 @@ +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) { + $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 '
    '; 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
    + $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
    + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php new file mode 100644 index 00000000..d30e456d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php @@ -0,0 +1,171 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php new file mode 100644 index 00000000..c3328a9d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php @@ -0,0 +1,39 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHash.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHash.php new file mode 100644 index 00000000..62085c5c --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHash.php @@ -0,0 +1,39 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHashParser.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHashParser.php new file mode 100644 index 00000000..f3e70c71 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/StringHashParser.php @@ -0,0 +1,110 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform.php new file mode 100644 index 00000000..210a4472 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform.php @@ -0,0 +1,36 @@ + '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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php new file mode 100644 index 00000000..0e36130f --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php @@ -0,0 +1,35 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token.php new file mode 100644 index 00000000..7900e6cb --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token.php @@ -0,0 +1,57 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Comment.php new file mode 100644 index 00000000..dc6bdcab --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Comment.php @@ -0,0 +1,22 @@ +data = $data; + $this->line = $line; + $this->col = $col; + } +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Empty.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Empty.php new file mode 100644 index 00000000..2a82b47a --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Empty.php @@ -0,0 +1,11 @@ +!empty($obj->is_tag) + * without having to use a function call is_a(). + */ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Text.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Text.php new file mode 100644 index 00000000..82efd823 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/Token/Text.php @@ -0,0 +1,33 @@ +data = $data; + $this->is_whitespace = ctype_space($data); + $this->line = $line; + $this->col = $col; + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TokenFactory.php new file mode 100644 index 00000000..7cf48fb4 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/TokenFactory.php @@ -0,0 +1,94 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URI.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URI.php new file mode 100644 index 00000000..8b50d0d1 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URI.php @@ -0,0 +1,173 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIDefinition.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIDefinition.php new file mode 100644 index 00000000..183fcfc7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIDefinition.php @@ -0,0 +1,93 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter.php new file mode 100644 index 00000000..c116f93d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter.php @@ -0,0 +1,45 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php new file mode 100644 index 00000000..881abc43 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php @@ -0,0 +1,12 @@ +get('EmbeddedURI', true)) return true; + return parent::filter($uri, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php new file mode 100644 index 00000000..bac56e8b --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php @@ -0,0 +1,21 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php new file mode 100644 index 00000000..f46ab263 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php @@ -0,0 +1,114 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php new file mode 100644 index 00000000..29ed0ed1 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php @@ -0,0 +1,54 @@ +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; + } + + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIParser.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIParser.php new file mode 100644 index 00000000..7179e4ab --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIParser.php @@ -0,0 +1,70 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme.php new file mode 100644 index 00000000..039710fd --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme.php @@ -0,0 +1,42 @@ +, 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php new file mode 100644 index 00000000..5849bf7f --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php @@ -0,0 +1,43 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/http.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/http.php new file mode 100644 index 00000000..b097a31d --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/http.php @@ -0,0 +1,20 @@ +userinfo = null; + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/https.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/https.php new file mode 100644 index 00000000..29e38091 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/https.php @@ -0,0 +1,12 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/news.php new file mode 100644 index 00000000..f5f54f4f --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/news.php @@ -0,0 +1,22 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php new file mode 100644 index 00000000..5bf93ea7 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php @@ -0,0 +1,20 @@ +userinfo = null; + $uri->query = null; + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php new file mode 100644 index 00000000..d24732c1 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php @@ -0,0 +1,69 @@ +get('URI', 'AllowedSchemes'); + if (!$config->get('URI', 'OverrideAllowedSchemes') && + !isset($allowed_schemes[$scheme]) + ) { + return $null; + } + + if (isset($this->schemes[$scheme])) return $this->schemes[$scheme]; + if (!isset($allowed_schemes[$scheme])) return $null; + + $class = 'HTMLPurifier_URIScheme_' . $scheme; + if (!class_exists($class)) return $null; + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/UnitConverter.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/UnitConverter.php new file mode 100644 index 00000000..545d4262 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/UnitConverter.php @@ -0,0 +1,254 @@ + 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 "
    n";
    +        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n
    \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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser.php new file mode 100644 index 00000000..68e72ae8 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser.php @@ -0,0 +1,154 @@ + 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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php new file mode 100644 index 00000000..c954250e --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php @@ -0,0 +1,96 @@ + $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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Native.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Native.php new file mode 100644 index 00000000..b02a6de5 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParser/Native.php @@ -0,0 +1,26 @@ +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/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParserException.php b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParserException.php new file mode 100644 index 00000000..5df34149 --- /dev/null +++ b/oss/vtiger/trunk/include/htmlpurifier/library/HTMLPurifier/VarParserException.php @@ -0,0 +1,11 @@ +(eo`i!!T& zGPJa|Nm+qjXpK}=S%QV6tg*$6ldflHXiHOOXLO3>S|;_v<&gz0SLlG>VO?rKrBd#?^{=Q?RneN?Ch)exFrhh{DCx|Ns9*Q*vl` zqE}&gs;$DUt*v}~dv$eazQEC*ptw+9g^iDtg^j7{>hN@Vn&IN=$;#Q%($dw{)|HsD z?d|RM>tT?SuFucUot>TW@$qqXnELwrwYSRY%rR|pmBg7e`0Fd?$unkglx}*Oi-B9s zqBeGYoz~dn+}-Ec+T^CJ!D?}p&e7ety3C-Wy34jjZFH7kZIJx@{P_6zQDKX$uEOu{ z^m2Hbfr5kHxim~$g3Zs=y}r=Q&D-7H==k{h#>m%IWsPZdmRDtoTxX1!nzZlNFZ1;F z>+JEt!qdvk+Un}+=;-g3mX=m#kf^G_rKY^Mz0AkU*lBQ+dWE8)q`RJ^yJ~Q4jE<|4 zn6jg#yr-$Z;lDJRp}6|`{K%a(PhW^fSbj}hgI;ZyU~ih|=kE6CEO>#SaCMieuftel ziCboi&d=O*baa-TwTO$T+T7)oo3vhGZNQc^wY$uYm$AFP&)?wbtF5$ge4TcBo35|K zMo(r+Pg=;x$VXRyXKs>9TZ5N+K!=d4bA5TGrKO~&uUKc2b$^)R#D$^f_U>&xJ5J|+JWKOJJEm%W?tN^gu zgq`c~3%4g{)2=0`rwtwgk{Hh2wX0L7U0}V4U}40VI-h$7ogf0Z;VuS9WH};0Ac><` z;5^x3z`%|jKTk4rx_4;rzimJWEF;a}7lBp{G-%MFBf_yk5qKL}p<4_KKkam(q}T{j zGGM^y_<4qefGi`P!1+@(V#KLafB-fB7>`87js}2qX!uTP27@E&4x*tzuYmn1_`RVr zXe7O>+TJ8bB4#t$VdH&7#!AixC-9KujQ2V1dliViif zaUd^`JaUE>TCm}wi(;s7ixw6Df?@^_;6OzM1T``M7eEHd1qL|;umd}%+%U(AY>;Bb z9CIk4ge_E7>CPJ&C}E%#IjquvI8C@A5E?^>Vy2lvc3?>W5m@j`C8V67n+A61z(x)^ zxS;2rE}()+4s%#wix{i0@xuZr4MKq+7&MB(nOqpdL?LXjlZF*WG-F918L**&GABIn zKnA3i$_x)PJaC3BEZkz(*Q1NFl~4y-?%D z3^P#TY>SAjt^gtsu6B+W`=UJita8D-@!~7<}9ihzln$kWDYE zuwd`KEHFWU6dBAx$to^1kV+Gcpo74{-+J4EF@>bz!VHi|l7t);1b_@T4_MGkHp6%k zhzKBud;=j*=p%+c`oR0b6p*Zu#mg@rj6fC-)L=;tm;9j(30Dw8KpI74yfG*`s1eE0 zN2@^t58N;_3JdP!z{|?*fB-@lSa$*Q8Y~ndLJqO063QM|Bw;Z#@b=OND4Z~}$lHrt zA~z3=SkjF2>)1w>68zAVqLH5HO1#0m#D045Vnl$uJ=(k_i^T2ag30 znImBd5VlNRx;ZE`j{@p-kV69&PmIzA7B%F-1QBt#a>)kBzyc6NkO5NYfEtPbg%3DUg9bWq2dLly5n>Pl z2mrwfr&!q#!ob1#&98n!xCKr|@rNfAAreJ+!YXXhfL_3X10nE18w^3i8Zto-7Z_G0 z3IPNIu1o@(2qFjnJ178qr~wF!$b<$S(5zK-V-6?q#O@RbiUFyhZ9_pfLbQNk&d)M1}t%lRu}>Rw4nwZ6!A$P_+bo7D26sFPyuc1>;kiJ z1Pus)1zz;x8ux&L35c-{1=!M-HmHRf;vfk=RDuC;z(ya2p+_I|!4VZe1R|0^&x!FM z2^H8t4D?X{i4*i-5+#^OI)2~_1sF5|-aH2&m_dUZ(0~lvSOGgh5kn5tf&lYWfeIG4 zI5+Hp5UenR9#~-k4J6VD0x`h?q)||Vx)dJnV1_JIF$*-5q82NF11BI+iclnBqZhcq zAO>NAdN^Yc7ih!|5J7`@T_YH+5eO87A&qZT!=(z4KsQ)`1qh4`6#*CnD`rs)p90kc zB-q0qG{J*CkbnRiK!i5VpaWdIKo@G^$014}4tT)B6@Ya`8>A44LS&*0h#}sW`Kt@m|+PjD1#oB zU{<03rqvD3-~kVg+6YIG01XW=f?X?gkSjnzmQzTs0lcsZ5ir3F8`xPWX!}#!Y9hC~ zy}%BRVFDF+!4&&&02Sf`6m>|WRkvWm^z<-@D+~g%Cb)t#oWTz_@IwVLI0gyiFp3c{ zAQwWw>s8(`4jr@#4|y;H1PGuF6%bXeBuJ|X2;dJin1H_{@B}U-5QL~+0>>!B zP;1bJ8TY^eDbR~+sz3_{u)_+Z&<7vHI)(r!whV;OC;^&b3OYSG1f^f z5i@{*A;>U`C1@iLr4WNDbihemTm#L6Q1d|aN{b&b;1ma9$AHjtiV^IYllaJm1gOvi z3kmuk{D49}qJaYjXuuTTX*8cb!2o>tnGOq3hgvQ@kkJM~93H?2Sd`I&2~0o&LI}nb zpdcnz8^pLxSU@6#kP6hkAQf~-!~&Xdh*kUg*Ht)x6Pl3R+Z^fq{oX2gCuH!N3wf;iTv4y%w)+r#_lCsY2mtZ~V!Q4y7eBx7E7Y<3H_qV-x>$ zS5twbhF%?qOVY{+4sMs^4A0k*{g;nsC%({QYG5!6NiNu%c)&ts=P$GF<{HBl>kU6v R))wsFQLfe4)GWwg4FKxVMV|lw literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/blank.gif b/oss/vtiger/trunk/include/images/blank.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf GIT binary patch literal 43 scmZ?wbhEHbWMp7uX!y@?;J^U}1_s5SEQ~;kK?g*DWEhy3To@Uw0n;G|I{*Lx literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginAlpha.gif b/oss/vtiger/trunk/include/images/loginAlpha.gif new file mode 100644 index 0000000000000000000000000000000000000000..a8902bb47491deaac106f93b60839e6f81d1dc18 GIT binary patch literal 761 zcmZ?wbhEHb^k6V#xXJ(m|NsC0_U+r3FJC@?{`~3Fr;i^$e)#a={rmUt-o1PK_U)TD zZ(hHC{p!`LXV0G9xpU|C?c29*-MV`9>e;hr&zw1P^ytyOd-v|yvuF42-CMS7S-W=a znl)?Y&Ye4D%9OsozTV#6=H}+Qy1Lrh+T!Bk+}zxxq@=jGxS*gQKR-VwCntM*dpkQj zLqkJ-eSJkmMR|F70RaJ^d%*4@ictK?!pH#hiVjFM$WIJx{~a0&JanY`Pb?`qnPWBo z!io~FwNdMDtSLMFwrc-_4HZ6mv(LZSQgt?O_x(iAG(YuIFN@t-&u>4k>Rl}vs=(0J zZXoN_+TJFq**no?0)x7zf?kJ0aF3qC6o!VL-u{Wb&O*I>?y_w@yU9&i)Jdmp>*ixjqHPAN_qd*SHki83=O}}!C-?0;cNEWPT~-smeSf0Vy*918 z+;R*C{FfQT-n@}&fARG;@1MVam6kEg=(xa;u!Q$N&)N^y6Iu_jY4A);c%k29BP?PZ zWU|AciL;fJck2y@NecFT{|qFY6c0VL?iGxZIJ9D-eDiFJLaC6#;=_vn*{q9Sg-rjP z?7-(F=`calVCCiIj*=3!j3FwXlbjhp?BPt5>RPQ8#uc0O_0{Fgq4!r-6mqOQ=n=Wy z=tsf}r!WSAqEp7LVp%E@@%j65MZ}k%5wp~k5t?YcVBzLpO5XE)c5ZrldWLcKx-|^~xm(V6*Or%;R|Kz)JG*P^>+1`JPt5b(z3uJo9mTKjo!!0t{rv;Y-12^R%$gpb zn5-RtZqLro&o3NE3=&DrD&lS}+Ac?ZsVVX{`$?1PV!WDD_Y!#a-~)?8{`I>}i&n z$-ZZ@6RkEuj7HoFA&R2XgG$6giG7FbC&cE|<+_!HpzIkH-USI2^Xy?OLrinM}G|F0a=MNGuiu z)@rqaQZAR5N~KgP<@5Onf&j{3FhB|*Oj|4#-EMa<7>q2w;*VOr<{Lqai=$>j5S z9LEcV0lfI$vaY@2 zp4E5tPju3LBUak%BRQZ zoXDM7N>sO=xa78l$qCFkbn93134R&%a6}EpTR+Q0_8Lcd0y1n2a)}=MQ$nDO9-w|U z&-z=cV5rto6mStpIA)o(bu^V&H{IbH`k)4*3{g`__C{Oi?{DK zyYRww`orC{ZY^FE&DT0vW7oW;rH+^67TbmQ=#N%2Gx_29{qpj{$12*@eR1aA%F>s) L=otMt6FKq+?b1D* literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginBottomBg.gif b/oss/vtiger/trunk/include/images/loginBottomBg.gif new file mode 100644 index 0000000000000000000000000000000000000000..26ead7a9401426c711d2c1f25a3534a1a3f4f83b GIT binary patch literal 108 zcmZ?wbhEHbWMklF*v!B%bLPzF&!2DFv}yP5-Pf;QzkmP!#EBCZE?l^B<;tT+kDfbs xjsX=Y{$v3Q=zvI&8O$t_1}7p{samW)JNdt}23JbwoEHn%IlPuCkYQx71^{KUC%*sy literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginBottomURL.gif b/oss/vtiger/trunk/include/images/loginBottomURL.gif new file mode 100644 index 0000000000000000000000000000000000000000..a23146b3f3aab038678019081ea2b41ca05e7236 GIT binary patch literal 444 zcmV;t0Ym;rNk%w1VPpUm0J8u9goK2#v9YG6rqa^V>FMctd3j@FWAya&o12@wyu5C1 zZjXM)j$~<` zXsWJkyCs4v&xDXjLNV{`-~_zWVj%$_B!$4Rczh(64!g2E5P#=&2#i2!Ur zMPX%F52FqS0G|OqF%F3wJgrnfE z0v!tEs-63k&O(_lsp5=^DBx?V?V5{zmkiIX%OHxNkEQ|MIz9Sg&t zvv6q8nh7Nz4-yzyP^cLoIt>asiD73>fdh74<|#K5X^;{J m91!_RmPCSADv6d|+aco|1%ueiolCc_-Me`6>fM_+0suS2u(9U= literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginBtnSignin.gif b/oss/vtiger/trunk/include/images/loginBtnSignin.gif new file mode 100644 index 0000000000000000000000000000000000000000..eeb7b0d82472d01168b302daa3ec75ccb439d5a8 GIT binary patch literal 1778 zcmeHH`&ZIu97aVX@6DFYq3CiJYKo`QN>Or>tc#MZnI}{{R^H}?xie9Vd7YQa)5S9| zozg{Q>I9TTPcy+v6iGChnUR5*phDmc$xH0p{)zpv&kxUe&U>EE=lwj-Iqw_(?Xg3L z6E1?>Kx-h7zrTM-NQjS*kG;J;0)eo#wVjxl7#<#e{`|Q{qnVtX)a&(XwR&M;K`xh1 zPfyRx%#4hT00NatB^HZkXJ_;C^YilZGBYztBoaVSC{#v9MsRR2nM@W6g^i7kK|w() zD=WFVxz*Lx01pTVxOM9molXZ@0FJ}qFc?g0YbzWM9~v401i%_frLw1|2bjR)@%sAu z0L;$Lj){p`T3Vt~sg;$L>FMcACNnZJl1L<;KY!lN&Q2r}0b8}Twbj?xOC%CN79Aa} zP$=+te0O(uYHBKlLb-hTa!pMQ&;m>#5C|3jR#a3}e}6x4?(6GYRaM33^T)@>84N~U zUEP%{SK{O20dYh`gj6a`NlA%~jRhRB*=)dILPCOzi_6s16kv03aIm(vwzIPn=mpHK zuC4+B&dtpMxV*ePIy$px7u?F0DV+r2;Gr#E2% zHG+?wrj7egI9@dK=cCw4ABuGC=J_nuwdwLeulz}_u@xdhwqAes)?d~Frgj2~=o z9HJl-g>{HCgEp>v!l3SjVO8P=bg`xLaSmhjKK8aLCUjbC?RsPy`}^^S&40I?>qvbg zeCT!78J!xIlGxPxsv~)B6{f9n`6gJ?PE^W{;YoW zFnb{mb+b_WmRD?LL_w}KD9kuWsI07Crxe!&mPPce>nd#8)7}pEJktA;+eyY0lUYph z&Ja<4yw(^Z*3TsUxzE2Dn1%PkiD`3@OZ!|80x{06E7sAbucL^5c za|JYWAA=jOn0!)4u-LUOFZ$9dD%q{{RJC6L+!;F|yNdKYblUvDc(UU9p~esr9a+XcQ`70MHDol7?i3eC#5)q`-Iwkpg`%Gk6Cs|-R0kA6b+_E3nQ<=@D!2H;u0$q) z?uF$fHs^Dx1B2I?lI_hRAIK9`S})BztJ-dLWauG+sGF{eZ;o+ z^JQk7-(>Sd*S{*$Al%u>W@fwpf_JL}##O_Se4nP__@DGDg`0;Fir|Qethya9=tMji zVMyrX2#y}ra1kV{HV&>XU(;iV3fykKsbA3cf|eg)to;Z`j0u^5F0|Zs23ZRC!xr+1 zWE_%hx#hww?zQ%k`t=6F9CZWk#=x84y-lr4LhmM#iHTsl4lK0JE?DA|S-Pjh&$47? zmI%1AM0oH7jsuR+Nta%nYzaE;j0@klD)}xteN}QcD}QwypQBj~%P!fcpSVCt*T1^Z zAuIblnXA#i&WbGINq>5ozV?QsM46kQR>U9>H)&>{L000Ju@+|G-e#D-7VzGnILj)? zGV!J^8s1Tp+Hw^RbMg;LTA9zf>H#HVeU8x-kq7r;5d{k@fzO7T^D<*Y)1_?NQ2$WA zz0@uj?7OLZ>X^B?IIxGgxg>Jbn%V9@Km@}i(V1JT6G_Z1y);L=wKlFC4YT>>2Cvve z)x`X`uI6e#Zs-KQ#?ATV5eeM=p!|uw<%$;gHmzwO3oOsnNml@Y71`an;ZTHj-{w+5 Z1Mk7|p&oATwu3ZCzyRNNr@JdC@E?Dg&tL!m literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginSIBottomLeft.gif b/oss/vtiger/trunk/include/images/loginSIBottomLeft.gif new file mode 100644 index 0000000000000000000000000000000000000000..610b9ff676c0547116f8b7d598f1f18e590b8cf6 GIT binary patch literal 99 zcmZ?wbhEHbWM^Pw*v!E2<;TCbU;h66_kYfzFPESEI(Fx0(TWc)J+J@&|IdI8DE?#t g3+sSLkQodt0s<#H8Kyo_Qhq0SFHxjXgpt7-0RF%sK>z>% literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginSIBottomRight.gif b/oss/vtiger/trunk/include/images/loginSIBottomRight.gif new file mode 100644 index 0000000000000000000000000000000000000000..9e73c0225fad86059333ba1c1f7e4abe0f6a9bda GIT binary patch literal 98 zcmZ?wbhEHbWMg1s*v!CCu>8Z@FMt33`(L!;!<<83E??6*mKKL7v! np8*vp{$v3Q=zvI&84N4}2C6AdLF{*8kL^2B&bdg4k--`OXJ99@ literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginSITopRight.gif b/oss/vtiger/trunk/include/images/loginSITopRight.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2b8dc017146169466c99b475b04f7c7ff83643f GIT binary patch literal 98 zcmZ?wbhEHbWMg1u*v!E2??6*mKKL7v! mp8*vp{$v3Q=zvI&84N7^0ldvYK@pvkLpkR%wYab_SOWkp*(M_Z literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginTopBg.gif b/oss/vtiger/trunk/include/images/loginTopBg.gif new file mode 100644 index 0000000000000000000000000000000000000000..d2c321c38783d524bf53e1ef06a0d68f27beaaec GIT binary patch literal 1068 zcmZ?wbhEHbRZafeQh}pDbX@bwDI2Pq1_RXRzdu@d!|8Zsrh{<2kTlp>rFPlGhxG zj}1q=C3LH9oY?r-O-RAHi{ab%&;vr2T?YJ{LHa^-n!TZ77nx=-Q zr)TI&-#D}La~kIY=WaIHMO$88UMjjG?rhZ7=Ia}h_xCyM-j;idwdnP|xz#Q2?;ofQ zm+;&3qk!wgWNr6*3wC~fepcOMzF%xr)9V|X3wNckW!bZr!e3yH>1N!GHl2 zf3kqZbU-A?3}zO!f)k$VJ1rg`+Wp>9qc16G-iyjLIj6Y_bdEdZ-ZxwH{!iYY0}2LA G4AuYu)+*Nk literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/loginVtigerCRM.gif b/oss/vtiger/trunk/include/images/loginVtigerCRM.gif new file mode 100644 index 0000000000000000000000000000000000000000..20177cfae13bfc2ba728d4dc2d33f3dff9a3874a GIT binary patch literal 2708 zcmeHG`#;nD8{THN<mUg)sjrmuFhW9+Sgcw)5iy%Z zQ<#-uo63qYF;)m^q#bzT`+oFCe1G{~KfGV}eO=e*zVFxddVRM3<>S7NQVem0s3DM` zpdbo`5)~DN!C=s6w5zMDm6g@EZ{JiZ)%Q7odUSMDsZ>r*PO8=FpFe*B3B2Ru3+Y2G*~Vn>5>X574mC0F^Hb2vy)F?d3g28aUlrQjRPjJ?-O4*{ zi7lSmd=t0x>7i!UBVnG)u@?GYY^?u!_p`&@&Fm*{?-3I9mikr}y?@|ij62*?SuA-H z@WA(uFX!pUl6~x>xA>frj&d?TZK>b$(oS}WC4RaYnknbzCx5?`>s2x+TM_lRDw2j4Qh6YqL+E5AyLc9sN* zxtx&><}u-2N7?mnvg)%VJ>r_G@4fYj`WVtn_i@jroAyUqU%vP;`0jzi+F#R}A0xfxb=PNSAqbp29&Q>U zPe5Ud*E6_Be(lQ)+Q!Lxjxr>@A_!jkfGcI`R!<<`ES zW}W#2+$I||z6G^8_jvB9;M=~H#>Z%s?DtTMutsePG>F)oNDta`dkg(owrxBe-+sXc zlVln?bUzgvilb2`0)`fzZcru_=IjZzee^fXPVtb)+z(l(`}*t9oihk>(Sj&gU@t4zT<1w(yosUE=CrcaMo4P@GnV@&e? zj6gY5jZO$1ukjumIO;HL3+zklgAE*zn+SBgT!c5$f#^Ym9w}G(Z}Et6yaYdCuu6Dh zRKyZdE*pCpP!Ns^$aW&(2KiFQCs`Knmu9fMZpLQw ztd7K^x>uQ*QU{%(z%5c2&PY>Ws#A9J5MjI-WnPlNAN}{Qk*)!hGEq0V7;Ygwgt%E> zuRrh|>5(ci#%dBwxymapN!Eta932Z|GScx+XhDeFYiaV;lrvF%2DAKgRO>D#d>irT zyllC*g@tvpb9Xm}1bJf`9N@EB3jJ`v>h=7~WP>wyJY>528@onk^5&#MWP4Mi8+SZo zuYtrs>aOEvk&qcSoPdm|f4sExJt{1J`G*K)&bnkI}GNA4A$iP7(2u-EQN>#44Blfv{wI zq@NpyjC{N+!6?YW@8Ht~x3+|w&?iMwm*qfcXDV%s8PVQ`%pE>9E+_Ee=0vyg9k-fA zym`CGN$x%JTLR(}k3d^ri#T?{Y7x&otr)tD5$LlnL(1cyOgmOVYEP?JD zPc=wd{lIc}F@1&k)~p%li_2XwsPB0V8=3OUHr>6(=ahjK3`U|^>AfAN^CZTTWwHKo zcKZ1qu<_G&wIKeyoyinFNrK+ko9Mg3m zboX1TtDF4GqVi66J25sH2B%Wb^)i#4T-02txGcI#_-SlG9#n2v9wYGUnaEea9P%%} z*dgpuan-NJ#EF*%{d%Xm)U_IM`DL}RcVQY4sz}{R>|bz`W`rYEr0o^;o7QOFxMWtOpCl?QWtv7e zQherlk>ZbOjeuapx{*Tsylk#1!H$YkX_A%3wtsBPKT%F3-O{ep3TbY@ISVpd-lbKnext7(n)Eu`i2xyr_*`ijH zmS)eS6HZOr%rg&~W|C^l7@9T8B7cX9njg5Wz@?~OTVsLQq!Gi}wLql4v_QQt{&z?Pd^ytyZ$jIlT%ZeUBeMe*XNqUax=q z_U)@zuR1$B=jP_X|3lnau$R58SRD4;KtZXeGS?7 z-sb*Cr_Kp=JNa<*O78s~zrEI9&kw)!d~)XZ!iSSz=5VR3NUl&8m+aeLdZ6rJI0TNA zgZYO}L?1Rbq0wQghX)m|t%F@_oL~;igu=@OpE~(#sy4AnyH1Y8=Hsz za)3dg+s@;dS;pBcGPE*xSjg*CaIM>j#x_Zo)>alA=DpYX48sLZ3n}RgCdZukYH|;S z;~`;l$<4U2Ex+PDnGEe?xH!-JG8iAAgnQUOPpJoluq+QY;x*MsZ+p%&aPgs{ZO(a4 zw-5~$pHxF}vIQk$Nw9nlQJWAkPKyxzaea`zBtDG>o56=;*9-7g2MV1ZTn39@hP*)@ts%+MG&K<7=CEoCo{ZyW78)^~u~#dX9{!w2&OO`2{Ze9?Avu{Pk^M3hHt zgSEcGsEEb(`XgIF3MfKd(R7XGPmgj_iiqKJI6zq37SBUP-*((!r_ebf47{Q!NDE^* zuPwf2DB1V7A%5F7y#G=G$P3;!?>F2YUCHw?yQzmA| zG8PEVa%sQRD#&g)=(aZO^R8u@*{el|A|eBXVfp!zkcuc=&$Wfrow8EdWq~+(_GUJZc6ByMQ zK^7x{SpV!$&$UmaZe=n`s%0GhDZvE>s>Ymc4KeBnli!Mp0JU>`lfiE`^syr`>HH`2 zSq;95oLW{(bbT6-kXDG`qQ=`LYQ4DgZMn&D{wrM!{EsK zJupljp>$U@H-;e^>A-esX_Fb6sOHZvlkUZb-0XApqcO|BYrKldI!alUu8vCSMqX9^Ydml=#+e5l2ZnjEybZTx1nlx z4sp&(2+>i+oN0txrb1$1LItr#A?nMw*NER!U=X;)Lx&y1Ae5kUu%~oRRpd zAc)cr9ZYQb+6QpkU@7#Z`xe=6_f><|?V!$2$4S9j7;aL8Tq^YM6RcXGi!=>;x6vez zR32)>XV3-bEK{hhUzbD2qVQqMXdgw-P<&`;y1>f#?(HqG1oJ3~A*w|R*YE(O)46D= zYlU>`ZhzE&;QC$$0-T4#p^b*bVqZy}Gh0v*u1>Zr8Cq>?Pcy~sWyYqX*GVoCU=*Y2 zwx{B-Jt(?~G&4RNS4M{SpUA3sO+tq{UW5Ta{!p>_%t!F+OqanNS|u~;R);K@x6|Ze zVl{1`_r9sE^O>DnGzR)0B3sx`a1|5S_58a_5i=HBRd&E1H)o@Vv$m^Qd0N zOA3Tw!iSvh9FY5XkSY#aEzd~qqOeOU%^dfTmIreYr;5N!g+!by;? zN7u4(?m4li*A-5hh-WkJII;aYcfUNUO|o*)xB@+YH#;e{p5Z*A+(x79_h-r+lc=5) zKV9A0l7BIR_K`e-xIgdA31ipq35C_I1um}erhS%+p9`hul6lV=Ek7w8htg0ZL6Rj| z8`O1Q01;Kd(6i_}esvV3UD=QrqPe7qz!P#v0IqC~abhq^l|{kO(e%D; zv;!7Pc0g-Mr4B8WD?;UMVRLD+deN3I*`$u>kNA_*5cV`g9_FAci`VRAP~%8UwS?l3 zYGV<)L7tnd0;zs4s3dcp#dX%zIILNFmh;qPZw%$#^5Pr*dtNP`NNfSa)mU*A$s(lr zR;s|`;0?C&RKWMZ=X+O!I^<13#yaDm^HUw2y7dd#IYwW{$2h(BqW38Wep-Po%D-3L zPl$SJL{SP)wyC{GPOpa!2BtCARxGPFj%iJv`Nb@Gt8yy1=3;z|6*(aFdK>&h6G_j8 z1z7)LFD4~zS<$XKtd$?PEo4Eqc=g=$56JDObmP}$!kmYKhBEpR!=Y!cJ85@DW|%dZ zyVe$EoWVDE&17Gpfuiz<!Y44`{VM8$)jJP;-eOyhCQJsF)NA^^Io89woP^;8w2cbZ22^nH)#%n)^5NaF zPq#_j?8Ei;M1dgSw-FpNDj(o21ZA*D7d=;9=#-Oh-17;!cs$vhW6@t6`n`Knur3-0 z9*1j@VRL_~E_^Oj?7ze|_xwmc_52P`EJg~jHG&O0B^hhf;BI7|Ctqke>izWE`UUl5 z1_m*%BjyZpE}1y=9_qNZIeM+tLbkJ^7s}Fa4bl1T7G%Ss?|gf*d%Q!5)vKYPLKz=^ z9!UJ!PHJ|Fb_L?)S5pGDfF$u?2&$5fgb#X1T7&i86}oqTfI^zQ)qV3FDGSEOX4Jfmp>%<+=AxyAr2X2=Ri`` z<(;>Z&=YWWh`4081ltU_q24cEa1Y_f?yIk`HySPffAiYe{UUWB~4QK3X3 zfnBkE6*F;Fya12*HH&7*Oo@LqXL}Bo)jt(mBH^7C6~U^kES-~Q4)IJ5YFAwxu2TGf zLpNH9>$1@ySz&!96ndPjs)(6XpcCe$5o)Nooy_Nen>ol{C2Dp#d5s_z=lfj{rnQce z2W!g`7)eqMm?&{(P3Prmp`VWst#q0B0@zij%=bDZmq4)k!26zry0dH6R|6pfy+ng- z?jp|!sZxoOe2q0yPR@!yP!$Q!`k;0guRGa1U+m`kE0G4a6zYJJO0{Ylxa&$Y1k8^< zy96!pqA}#xUG|xnt-C6Gt*-EL5y}#*iK7Cq1ZeJM4#}<`j!z@nX>CscJB|!O^t4hk z!9``e3FrKE2mNu1Xu{>DY5*HR5wk>w3bw37k2Y0AV|&fJz*n?+s>pJH*FlX+yv4PvBH> zIKW&5!BoN@@!*!u%1Q-$aA3IFkWoo&50F<-VFTU%EdmGtLcKfGh*|PfC$~s)L{fn7 zKGv!AI(3(7a+PTfjwELIdd!-j9O#q-dt&J1q#km@PmAJDue?hgY*ok(mrn{HcYU!e z1G&@$ZMuQ-smwXUtxFKh`ydsz!Ya%<5N9gEN_$)`IiAo|BgSkw)YG|o*tPnYJ%#8` zgG_yon&SDc<7?L^K%wi&KX?0h=MqshoMmu!x5U;#58$WgQbaH;hE4Dv83~XD6?(s~ zc5O98!??ozBvcGpVK7lx7ca4eIc~lG?4pCI03INn-^7Ov^}w71*<nSjj4oB>OGJ};QW6~HtxY@vc&sE~a%0D}6Jf|Dj{aHs>a)mc+? zwWU9$IqQJIKb0^z;vD5XU--ZRVskFoFa0SW7#&sBU8%Zw^X5hV@6Rs&@%f_0vZ2Md sp*6PQc5cI+s)ml64V}*#9(-=lS~foNZG0Tt_$0Tnuc|SFihyPR1>*D)(f|Me literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/right_arc.gif b/oss/vtiger/trunk/include/images/right_arc.gif new file mode 100644 index 0000000000000000000000000000000000000000..4778bcc9de9c438a2c93e436ff0392f83096d9fc GIT binary patch literal 49 zcmZ?wbhEHbWMyDwXkcXc|NlP&1B2pE7DfgJMg|=q8z|1o;K0PuA}hII$2raz+6n+uGR>4cCS~6k^72OBMbahWXH>wCt>@ea}Z_oGfHNF+E$E*H4n(9;9$?Lbi& z0fz&0I`I4PQ1EzQZVtM;L8XFF2m%4{`Jl5CW@kaG1+^M98c--eE(e(my1H;P3IveH zgWPU3JBz~Ms8);oep;hJ9UZ8>9fNddCkh5}iB>3RkB1HfXrB+MR7fI0YBlZk(sDVH z$&gZsQ>53UPzX7lSOj^!h@wz;H|AoHRw`+i3)$_+=c83BtcDy8Or-DKdrgu^EJkuU zK0a-?14#n$zrBwOq%JIeH#Xy~fBO>z1Bf_g3SZpBNn|9YMM-fgwPYnX$IcZs=O_Ox z6i)GLit7L1R0@Z}q839&NqyT9!^Nu&H#W^TRpdyWx3Y@9_e8I|>WsI}3@0S-V;R}$ z{D#H@(w2V?WfWiM|8=bQ%C)=2Ha2VYcJ26sJ!5Y6{=-XBQiLN`j!yQ`BlyN;x%q=W_H&nM_2xtCD4*xS+0bp+!`5A zxus%J+aXaSsoZ*;Ef!Fp@l@OVOBI#2a#FQD$RulGzsy$e_-@TW1GiMC-dP!E66MzN zG@t$#KOq&AlA2vVtT0nGx)pg^^+MzqU#Qr4v-W0k$YvA@MB1M|ID67Pb51bGT2I9D z<5u%nLi3t~j3)E1j7$ZGeTtoGxln%DLS1A}TMAaCR8{A*VlI4WtxdbaE?Aq_(w4?K zvDjk0TFKi?{ogyzX!2kt##_qOUJ8H!Lk{EOau2t#!_r6EZ_pF literal 0 HcmV?d00001 diff --git a/oss/vtiger/trunk/include/images/vtigercrm_icon.ico b/oss/vtiger/trunk/include/images/vtigercrm_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..12406bdf8e2e32e2b6f40df79423b00d6c871d0b GIT binary patch literal 1406 zcmeH{Un~_+9LGP`O(?<*mrBw9|4FHo6jG?J>fUl)3R1`7=TYXla2LFNlbUfUjTS^=m>yLm@X89zB95 zPr%m~o;_n|Xo$kXLK+$xXl`zXZ{H{{FNcQ@!P^^VX5iyTnwpwmd>l+BsHst4qNJn* zN=u=*7&0;-Fp&QKeg*~x7#SIXSFhBPag|cJA3wm|9h{u#>+6HcO6ck$IXM{~KPEUh zST#U(z;<>>2X=yM$4RLXdj*h~cH=xxrF)=}HZ7n>1 z4h01m3T2lj#%wmz+1W{BV}}&Dgq`aNlZ+HgaoLlz+$mbRaK>0 zR7`AaEGa1|L`O$MWF!Oyfrke?eM(zf8@;`~5FZck-(xfyQI{nzFAsctK&Jz}p1Qg^ zsILcqe?_O_)7g3MLHztVhyV3I(Sh{&i|-uW6dHK>WNy@HC+D4dIVbZL%%XGbENniK z=`d?J6-sF@w=Wl4?WC`YT=$R~*BFs3gq_Ro)j9IoGP$ro{T)IaJS&7&`gzI?dwp#F zj(9gWJ9)(Z*|$x1>C9F>aC9;p0UjC_wI6 zE9EgE((c{$UX?G*iH3DjF1eAl$VEh)IDdO3_jOV(aNiZa;(>6GDN-(8Zz~gdPP@g? jX3e+Sw3J?4m_`3X8^iYL_HVNU-}E$kL|)bZEy=$C@P7i@ literal 0 HcmV?d00001