aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSilvio Rhatto <rhatto@riseup.net>2014-03-14 21:25:01 -0300
committerSilvio Rhatto <rhatto@riseup.net>2014-03-14 21:25:01 -0300
commit3651c99a195685f3a868e159e72c4daf8cb371d3 (patch)
treecb004dd7b6ca55215a2c20112fe0c5209d98c18e
parent97e689213ff4e829f251af526ed4e796a3cc2b71 (diff)
parentc2707bb867ddb285af85d7a0e75db26ef692d68c (diff)
downloadelgg-3651c99a195685f3a868e159e72c4daf8cb371d3.tar.gz
elgg-3651c99a195685f3a868e159e72c4daf8cb371d3.tar.bz2
Merge branch 'master' into saravea
Conflicts: mod/blog/views/default/blog/sidebar/archives.php
-rw-r--r--.gitignore3
-rw-r--r--CHANGES.txt113
-rw-r--r--COPYRIGHT.txt1
-rw-r--r--README.md28
-rw-r--r--README.txt24
-rw-r--r--actions/admin/site/regenerate_secret.php11
-rw-r--r--actions/admin/site/update_advanced.php6
-rw-r--r--actions/admin/site/update_basic.php2
-rw-r--r--actions/avatar/remove.php52
-rw-r--r--actions/comments/delete.php5
-rw-r--r--actions/friends/collections/add.php2
-rw-r--r--actions/login.php5
-rw-r--r--actions/profile/edit.php7
-rw-r--r--documentation/info/manifest.xml2
-rw-r--r--engine/classes/ElggAccess.php4
-rw-r--r--engine/classes/ElggAttributeLoader.php69
-rw-r--r--engine/classes/ElggAutoP.php38
-rw-r--r--engine/classes/ElggBatch.php68
-rw-r--r--engine/classes/ElggCache.php4
-rw-r--r--engine/classes/ElggCrypto.php208
-rw-r--r--engine/classes/ElggData.php2
-rw-r--r--engine/classes/ElggDiskFilestore.php28
-rw-r--r--engine/classes/ElggEntity.php37
-rw-r--r--engine/classes/ElggFile.php9
-rw-r--r--engine/classes/ElggFileCache.php4
-rw-r--r--engine/classes/ElggGroup.php18
-rw-r--r--engine/classes/ElggLRUCache.php181
-rw-r--r--engine/classes/ElggMenuBuilder.php18
-rw-r--r--engine/classes/ElggObject.php19
-rw-r--r--engine/classes/ElggPlugin.php29
-rw-r--r--engine/classes/ElggPluginPackage.php1
-rw-r--r--engine/classes/ElggPriorityList.php13
-rw-r--r--engine/classes/ElggSite.php14
-rw-r--r--engine/classes/ElggStaticVariableCache.php6
-rw-r--r--engine/classes/ElggTranslit.php55
-rw-r--r--engine/classes/ElggUser.php23
-rw-r--r--engine/classes/ElggVolatileMetadataCache.php92
-rw-r--r--engine/classes/ElggWidget.php7
-rw-r--r--engine/classes/ElggXMLElement.php20
-rw-r--r--engine/handlers/cache_handler.php8
-rw-r--r--engine/lib/actions.php100
-rw-r--r--engine/lib/admin.php24
-rw-r--r--engine/lib/annotations.php40
-rw-r--r--engine/lib/cache.php1
-rw-r--r--engine/lib/configuration.php4
-rw-r--r--engine/lib/database.php75
-rw-r--r--engine/lib/deprecated-1.7.php2
-rw-r--r--engine/lib/deprecated-1.8.php46
-rw-r--r--engine/lib/elgglib.php31
-rw-r--r--engine/lib/entities.php166
-rw-r--r--engine/lib/extender.php18
-rw-r--r--engine/lib/group.php4
-rw-r--r--engine/lib/input.php4
-rw-r--r--engine/lib/languages.php3
-rw-r--r--engine/lib/location.php2
-rw-r--r--engine/lib/memcache.php20
-rw-r--r--engine/lib/metadata.php39
-rw-r--r--engine/lib/metastrings.php21
-rw-r--r--engine/lib/navigation.php9
-rw-r--r--engine/lib/notification.php15
-rw-r--r--engine/lib/opendd.php4
-rw-r--r--engine/lib/output.php52
-rw-r--r--engine/lib/pageowner.php4
-rw-r--r--engine/lib/plugins.php57
-rw-r--r--engine/lib/relationships.php2
-rw-r--r--engine/lib/river.php16
-rw-r--r--engine/lib/sessions.php9
-rw-r--r--engine/lib/statistics.php10
-rw-r--r--engine/lib/system_log.php11
-rw-r--r--engine/lib/upgrade.php18
-rw-r--r--engine/lib/upgrades/2009102801.php5
-rw-r--r--engine/lib/upgrades/2010033101.php2
-rw-r--r--engine/lib/upgrades/2010061501.php6
-rw-r--r--engine/lib/upgrades/2010071001.php5
-rw-r--r--engine/lib/upgrades/2010071002.php5
-rw-r--r--engine/lib/upgrades/2011052801.php5
-rw-r--r--engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php2
-rw-r--r--engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php4
-rw-r--r--engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php28
-rw-r--r--engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php12
-rw-r--r--engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php16
-rw-r--r--engine/lib/user_settings.php2
-rw-r--r--engine/lib/users.php34
-rw-r--r--engine/lib/views.php48
-rw-r--r--engine/lib/web_services.php13
-rw-r--r--engine/schema/mysql.sql2
-rw-r--r--engine/tests/api/access_collections.php3
-rw-r--r--engine/tests/api/annotations.php80
-rw-r--r--engine/tests/api/entity_getter_functions.php4
-rw-r--r--engine/tests/api/helpers.php103
-rw-r--r--engine/tests/api/metadata.php16
-rw-r--r--engine/tests/api/metadata_cache.php7
-rw-r--r--engine/tests/api/metastrings.php33
-rw-r--r--engine/tests/api/plugins.php4
-rw-r--r--engine/tests/objects/entities.php2
-rw-r--r--engine/tests/objects/objects.php2
-rw-r--r--engine/tests/objects/users.php21
-rw-r--r--engine/tests/regression/trac_bugs.php190
-rw-r--r--engine/tests/test_files/plugin_18/manifest.xml2
-rw-r--r--engine/tests/test_files/xxe/external_entity.txt1
-rw-r--r--engine/tests/test_files/xxe/request.xml8
-rw-r--r--htaccess_dist8
-rw-r--r--install/ElggInstaller.php2
-rw-r--r--install/cli/sample_installer.php40
-rw-r--r--js/lib/elgglib.js4
-rw-r--r--js/lib/languages.js3
-rw-r--r--js/lib/session.js32
-rw-r--r--js/lib/ui.userpicker.js4
-rw-r--r--js/tests/ElggLibTest.js11
-rw-r--r--js/tests/README5
-rw-r--r--languages/en.php25
-rw-r--r--mod/blog/actions/blog/save.php11
-rw-r--r--mod/blog/start.php15
-rw-r--r--mod/blog/views/default/blog/sidebar/archives.php4
-rw-r--r--mod/blog/views/default/forms/blog/save.php2
-rw-r--r--mod/blog/views/default/river/object/blog/create.php6
-rw-r--r--mod/bookmarks/languages/en.php2
-rw-r--r--mod/bookmarks/pages/bookmarks/all.php3
-rw-r--r--mod/bookmarks/pages/bookmarks/friends.php2
-rw-r--r--mod/bookmarks/pages/bookmarks/owner.php3
-rw-r--r--mod/bookmarks/start.php21
-rw-r--r--mod/developers/languages/en.php3
-rw-r--r--mod/developers/manifest.xml2
-rw-r--r--mod/developers/start.php9
-rw-r--r--mod/embed/start.php26
-rw-r--r--mod/externalpages/start.php8
-rw-r--r--mod/file/actions/file/upload.php23
-rw-r--r--mod/file/pages/file/friends.php2
-rw-r--r--mod/file/pages/file/owner.php3
-rw-r--r--mod/file/pages/file/world.php3
-rw-r--r--mod/groups/actions/groups/membership/invite.php73
-rw-r--r--mod/groups/lib/discussion.php5
-rw-r--r--mod/groups/lib/groups.php38
-rw-r--r--mod/groups/start.php4
-rw-r--r--mod/groups/views/default/groups/sidebar/my_status.php2
-rw-r--r--mod/groups/views/default/object/groupforumtopic.php5
-rw-r--r--mod/htmlawed/start.php4
-rw-r--r--mod/htmlawed/tests/tags.php78
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawed.php82
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawedTest.php89
-rwxr-xr-x[-rw-r--r--]mod/htmlawed/vendors/htmLawed/htmLawed_README.htm4336
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawed_README.txt3433
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt66
-rw-r--r--mod/logbrowser/views/default/forms/logbrowser/refine.php4
-rw-r--r--mod/logbrowser/views/default/logbrowser/refine.php2
-rw-r--r--mod/logbrowser/views/default/logbrowser/table.php2
-rw-r--r--mod/logrotate/languages/en.php3
-rw-r--r--mod/logrotate/start.php51
-rw-r--r--mod/logrotate/views/default/plugins/logrotate/settings.php1
-rw-r--r--mod/members/pages/members/search.php8
-rw-r--r--mod/messageboard/pages/messageboard/owner.php1
-rw-r--r--mod/messages/start.php36
-rw-r--r--mod/oauth_api/manifest.xml25
-rw-r--r--mod/oauth_api/start.php24
-rw-r--r--mod/oauth_api/vendors/oauth/LICENSE21
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/INSTALL53
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/init.php127
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl2
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl2
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl13
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl21
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl41
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/hello.php65
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/index.php37
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/logon.php55
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/oauth.php77
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/register.php28
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php71
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php226
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthException.php50
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequest.php801
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php274
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php209
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php262
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequester.php508
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthServer.php232
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthStore.php86
-rw-r--r--mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php129
-rw-r--r--mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php143
-rw-r--r--mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php304
-rw-r--r--mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt101
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php69
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php115
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php95
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php80
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php136
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php149
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php265
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php1879
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/mysql/install.php32
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql219
-rw-r--r--mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds78
-rw-r--r--mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds73
-rw-r--r--mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds81
-rw-r--r--mod/oauth_api/vendors/oauth/test/oauth_test.php188
-rw-r--r--mod/openid_client/start.php4
-rw-r--r--mod/pages/actions/annotations/page/delete.php20
-rw-r--r--mod/pages/actions/pages/delete.php26
-rw-r--r--mod/pages/languages/en.php7
-rw-r--r--mod/pages/lib/pages.php7
-rw-r--r--mod/pages/pages/pages/edit.php13
-rw-r--r--mod/pages/pages/pages/friends.php2
-rw-r--r--mod/pages/pages/pages/history.php4
-rw-r--r--mod/pages/pages/pages/owner.php2
-rw-r--r--mod/pages/start.php48
-rw-r--r--mod/pages/upgrades/2012061800.php49
-rw-r--r--mod/pages/views/default/annotation/page.php18
-rw-r--r--mod/pages/views/default/object/page_top.php26
-rw-r--r--mod/pages/views/default/pages/sidebar/history.php1
-rw-r--r--mod/profile/icondirect.php6
-rw-r--r--mod/profile/views/default/profile/details.php20
-rw-r--r--mod/reportedcontent/views/default/object/reported_content.php20
-rw-r--r--mod/search/README.txt2
-rw-r--r--mod/search/pages/search/index.php10
-rw-r--r--mod/search/search_hooks.php140
-rw-r--r--mod/search/views/default/search/comments/entity.php11
-rw-r--r--mod/search/views/default/search/list.php14
-rw-r--r--mod/search/views/rss/search/comments/entity.php11
-rw-r--r--mod/simplepie/README8
-rw-r--r--mod/simplepie/actions/simplepie/save_group_feed.php14
-rw-r--r--mod/simplepie/languages/en.php6
-rw-r--r--mod/simplepie/languages/es.php14
-rw-r--r--mod/simplepie/manifest.xml2
-rw-r--r--mod/simplepie/start.php10
-rw-r--r--mod/simplepie/views/default/forms/simplepie/save_group_feed.php39
-rw-r--r--mod/simplepie/views/default/simplepie/group_module.php109
-rw-r--r--mod/thewire/pages/thewire/everyone.php2
-rw-r--r--mod/thewire/pages/thewire/friends.php2
-rw-r--r--mod/thewire/pages/thewire/owner.php4
-rw-r--r--mod/tinymce/views/default/js/tinymce.php12
-rw-r--r--mod/twitter/graphics/thewire_speech_bubble.gifbin560 -> 0 bytes
-rw-r--r--mod/twitter/graphics/twitter16px.pngbin724 -> 0 bytes
-rw-r--r--mod/twitter/languages/en.php17
-rw-r--r--mod/twitter/manifest.xml16
-rw-r--r--mod/twitter/start.php14
-rw-r--r--mod/twitter/views/default/twitter/css.php63
-rw-r--r--mod/twitter/views/default/widgets/twitter/content.php42
-rw-r--r--mod/twitter/views/default/widgets/twitter/edit.php24
-rw-r--r--mod/twitter_api/languages/en.php4
-rw-r--r--mod/twitter_api/lib/twitter_api.php41
-rw-r--r--mod/twitter_api/manifest.xml10
-rw-r--r--mod/twitter_api/pages/twitter_api/interstitial.php4
-rw-r--r--mod/twitter_api/start.php33
-rw-r--r--mod/twitter_api/vendors/twitteroauth/OAuth.php390
-rw-r--r--mod/twitter_api/vendors/twitteroauth/README117
-rw-r--r--mod/twitter_api/vendors/twitteroauth/twitterOAuth.php16
-rw-r--r--mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php7
-rw-r--r--mod/twitter_api/views/default/plugins/twitter_api/settings.php15
-rw-r--r--pages/account/forgotten_password.php11
-rw-r--r--pages/account/login.php14
-rw-r--r--pages/account/register.php11
-rw-r--r--pages/account/reset_password.php11
-rw-r--r--pages/avatar/edit.php5
-rw-r--r--pages/river.php1
-rw-r--r--upgrade.php2
-rw-r--r--version.php4
-rw-r--r--views/default/admin/settings/advanced/site_secret.php11
-rw-r--r--views/default/css/admin.php25
-rw-r--r--views/default/css/elements/forms.php1
-rw-r--r--views/default/css/elements/navigation.php7
-rw-r--r--views/default/css/ie.php8
-rw-r--r--views/default/css/ie7.php1
-rw-r--r--views/default/forms/admin/site/regenerate_secret.php24
-rw-r--r--views/default/forms/plugins/settings/save.php6
-rw-r--r--views/default/forms/profile/edit.php13
-rw-r--r--views/default/input/userpicker.php8
-rw-r--r--views/default/js/elgg.php2
-rw-r--r--views/default/js/languages.php24
-rw-r--r--views/default/js/walled_garden.php28
-rw-r--r--views/default/object/default.php1
-rw-r--r--views/default/object/elements/full.php4
-rw-r--r--views/default/object/elements/summary.php2
-rw-r--r--views/default/output/access.php2
-rw-r--r--views/default/output/tag.php16
-rw-r--r--views/default/output/tags.php20
-rw-r--r--views/default/page/walled_garden.php15
-rw-r--r--views/default/river/elements/summary.php3
277 files changed, 7911 insertions, 12624 deletions
diff --git a/.gitignore b/.gitignore
index c90f6b85b..2303faa0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,7 +40,7 @@
!/mod/uservalidationbyemail/
!/mod/zaudio/
-# ignore IDE/hidden/OS cache files
+# ignore IDE/hidden/testing/OS cache files
.*
*~
/nbproject
@@ -51,6 +51,7 @@ Session.vim
tmtags
Thumbs.db
Desktop.ini
+/JsTestDriver-*.jar
# don't ignore travis config
!/.travis.yml
diff --git a/CHANGES.txt b/CHANGES.txt
index 797fb9c62..f6974a3ae 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,116 @@
+Version 1.8.18
+(January 11, 2014 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Juho Jaakkola
+ * Steve Clay
+
+ Bugfixes:
+ * Fixes notify_user() broken in 1.8.17
+
+
+Version 1.8.17
+(January 1, 2014 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Brett Profitt
+ * Cash Costello
+ * Ed Lyons
+ * Evan Winslow
+ * Jeroen Dalsem
+ * Jerome Bakker
+ * Juho Jaakkola
+ * Matt Beckett
+ * Paweł Sroka
+ * Sem
+ * Steve Clay
+
+ Security Fixes:
+ * Specially-crafted request could return the contents of sensitive files.
+ * Reflected XSS attack was possible against 1.8 systems.
+ * The cryptographic key used for various purposes may have been generated with weak entropy, particularly on Windows.
+
+ Bugfixes:
+ * URLs with non-ASCII usernames again work
+ * Floated images are now properly cleared in content areas
+ * The activity page title now matches the document title
+ * Search again supports multiple comments on the same entity
+ * Blog archive sidebar now reverse chronological
+ * URLs with matching parens can now be auto-linked
+ * Log browser links for users now work
+ * Disabling over 50 objects should no longer result in an infinite loop
+ * Radio/checkbox inputs no longer have border radius (for IE10)
+ * User picker: the Only Friends checkbox again works
+ * Group bookmarklet no longer shown to non-members
+ * Widget reordering fixed when moving across columns
+ * Refuse to deactivate plugins needed as dependencies
+
+ Enhancements:
+ * Group member listings are ordered by name
+ * The system_log table can now store IPv6 addresses
+ * Web services auth_gettoken() now accepts email address
+ * List functions: no need to specify pagination for unlimited queries
+ * Htmlawed was upgraded to 1.1.16
+
+
+Version 1.8.16
+(June 25, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Brett Profitt
+ * Cash Costello
+ * Jeff Tilson
+ * Jerome Bakker
+ * Paweł Sroka
+ * Steve Clay
+
+ Security Fixes:
+ * Fixed avatar removal bug (thanks to Jerome Bakker for the first report of this)
+
+ Bugfixes:
+ * Fixed infinite loop when deleting/disabling an entity with > 50 annotations
+ * Fixed deleting log tables in log rotate plugin
+ * Added full text index for groups if missing
+ * Added workaround for IE8 and jumping user avatar
+ * Fixed pagination for members pages
+ * Fixed several internal cache issues
+ * Plus many more bug fixes
+
+
+Version 1.8.15
+(April 23, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Cash Costello
+ * Ismayil Khayredinov
+ * Jeff Tilson
+ * Juho Jaakkola
+ * Matt Beckett
+ * Paweł Sroka
+ * Sem
+ * Steve Clay
+ * Tom Voorneveld
+
+ Bugfixes:
+ * Not displaying http:// on profiles when website isn't set
+ * Fixed pagination display issue for small screens
+ * Not hiding subpages of top level pages that have been deleted
+ * Stop corrupting JavaScript views with elgg deprecation messages
+ * Fixed out of memory error due to query cache
+ * Fixed bug preventing users authorizing Twitter account access
+ * Fixed friends access level for editing pages
+ * Fixed uploading files within the embed dialog
+
+ Enhancements:
+ * Added browser caching of language JS files
+ * Adding nofollow on user posted URLs for spam deterrence (thanks to Hellekin)
+ * Auto-registering views for simplecache when their URL is requested
+ * Display helpful message for those who have site URL configuration issues
+ * Can revert to a previous revision with pages plugin
+ * Site owners can turn off posting wire messages to Twitter
+ * Search results are sorted by relevance
+
+ Dropped Plugins:
+ * Twitter widget due to changes in Twitter API and terms of service
+ * OAuth API plugin due to conflicts with the Twitter API plugin
+
+
Version 1.8.14
(March 12, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
Contributing Developers:
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 76781f25a..262515386 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -6,6 +6,7 @@ The MITRE Corportation (jricher@mitre.org)
Curverider Ltd (info@elgg.com)
Individuals:
+Steve Clay (steve@mrclay.org)
Cash Costello (cash.costello@gmail.com)
Brett Profitt (brett.profitt@gmail.com)
Dave Tosh (davidgtosh@gmail.com)
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..a1b3e1cbf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+Lorea
+=====
+
+(Re-)Taking the Networks!
+
+The [lorea](https://lorea.org) code aims at providing individuals and teams with privacy-aware, security-conscious, and user-controlled-data collaborative tools over the Web, and around it.
+
+The main application is based on [Elgg](http://elgg.org), a PHP-based social networking platform. Lorea extends it with plugins to provide better privacy features, including strong encryption, *OStatus-based federation* with other Lorea/Elgg installations and OStatus-compliant projects, etc.
+
+It also integrates other popular technologies such as [DokuWiki](http://www.dokuwiki.org), [Etherpad](http://etherpad.org), [XMPP](http://xmpp.org), etc., and provides *GPG-encrypted mailing-lists* to groups.
+
+### Installation
+
+Our code is divided in two git repositories: elgg and lorea-plugins. You can get it using the following commands.
+
+<pre>
+$ git clone git://gitorious.org/lorea/elgg.git
+$ cd elgg
+$ git remote add lorea-plugins git://gitorious.org/lorea/lorea-plugins.git
+$ git pull lorea-plugins master
+</pre>
+
+You can update the code to the latest release using:
+
+<pre>
+$ git pull origin master
+$ git pull lorea-plugins master
+</pre>
diff --git a/README.txt b/README.txt
deleted file mode 100644
index dd604fd2b..000000000
--- a/README.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Elgg
-Copyright (c) 2008-2013 See COPYRIGHT.txt
-
-See CONTRIBUTORS.txt for development credits.
-
-Elgg is managed by the Elgg Foundation, a nonprofit organization that was
-founded to govern, protect, and promote the Elgg open source social network
-engine. The Foundation aims to provide a stable, commercially and
-individually independent organization that operates in the best interest of Elgg
-as an open source project.
-
-The project site can be found at http://elgg.org/
-
-The Elgg project was started in 2004 by:
-Ben Werdmuller <ben@benwerd.com, http://benwerd.com> and
-Dave Tosh <davidgtosh@gmail.com>
-
-Elgg is released under the GNU General Public License (GPL) Version 2 and the
-Massachusetts Institute of Technology (MIT) License. See LICENSE.txt
-in the root of the package you downloaded.
-
-For installation instructions, see INSTALL.txt.
-
-For upgrade instructions, see UPGRADE.txt.
diff --git a/actions/admin/site/regenerate_secret.php b/actions/admin/site/regenerate_secret.php
new file mode 100644
index 000000000..3112fb5f3
--- /dev/null
+++ b/actions/admin/site/regenerate_secret.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Generate a new site secret
+ */
+
+init_site_secret();
+elgg_reset_system_cache();
+
+system_message(elgg_echo('admin:site:secret_regenerated'));
+
+forward(REFERER);
diff --git a/actions/admin/site/update_advanced.php b/actions/admin/site/update_advanced.php
index 0fd8d1f35..4888b0a8d 100644
--- a/actions/admin/site/update_advanced.php
+++ b/actions/admin/site/update_advanced.php
@@ -14,10 +14,10 @@ if ($site = elgg_get_site_entity()) {
throw new InstallationException(elgg_echo('InvalidParameterException:NonElggSite'));
}
- $site->url = get_input('wwwroot');
+ $site->url = rtrim(get_input('wwwroot', '', false), '/') . '/';
- datalist_set('path', sanitise_filepath(get_input('path')));
- $dataroot = sanitise_filepath(get_input('dataroot'));
+ datalist_set('path', sanitise_filepath(get_input('path', '', false)));
+ $dataroot = sanitise_filepath(get_input('dataroot', '', false));
// check for relative paths
if (stripos(PHP_OS, 'win') === 0) {
diff --git a/actions/admin/site/update_basic.php b/actions/admin/site/update_basic.php
index 97d258b65..9765182cc 100644
--- a/actions/admin/site/update_basic.php
+++ b/actions/admin/site/update_basic.php
@@ -16,7 +16,7 @@ if ($site = elgg_get_site_entity()) {
}
$site->description = get_input('sitedescription');
- $site->name = get_input('sitename');
+ $site->name = strip_tags(get_input('sitename'));
$site->email = get_input('siteemail');
$site->save();
diff --git a/actions/avatar/remove.php b/actions/avatar/remove.php
index cd38e456a..9cb40a760 100644
--- a/actions/avatar/remove.php
+++ b/actions/avatar/remove.php
@@ -3,32 +3,34 @@
* Avatar remove action
*/
-$guid = get_input('guid');
-$user = get_entity($guid);
-if ($user) {
- // Delete all icons from diskspace
- $icon_sizes = elgg_get_config('icon_sizes');
- foreach ($icon_sizes as $name => $size_info) {
- $file = new ElggFile();
- $file->owner_guid = $guid;
- $file->setFilename("profile/{$guid}{$name}.jpg");
- $filepath = $file->getFilenameOnFilestore();
- if (!$file->delete()) {
- elgg_log("Avatar file remove failed. Remove $filepath manually, please.", 'WARNING');
- }
- }
-
- // Remove crop coords
- unset($user->x1);
- unset($user->x2);
- unset($user->y1);
- unset($user->y2);
-
- // Remove icon
- unset($user->icontime);
- system_message(elgg_echo('avatar:remove:success'));
-} else {
+$user_guid = get_input('guid');
+$user = get_user($user_guid);
+
+if (!$user || !$user->canEdit()) {
register_error(elgg_echo('avatar:remove:fail'));
+ forward(REFERER);
}
+// Delete all icons from diskspace
+$icon_sizes = elgg_get_config('icon_sizes');
+foreach ($icon_sizes as $name => $size_info) {
+ $file = new ElggFile();
+ $file->owner_guid = $user_guid;
+ $file->setFilename("profile/{$user_guid}{$name}.jpg");
+ $filepath = $file->getFilenameOnFilestore();
+ if (!$file->delete()) {
+ elgg_log("Avatar file remove failed. Remove $filepath manually, please.", 'WARNING');
+ }
+}
+
+// Remove crop coords
+unset($user->x1);
+unset($user->x2);
+unset($user->y1);
+unset($user->y2);
+
+// Remove icon
+unset($user->icontime);
+
+system_message(elgg_echo('avatar:remove:success'));
forward(REFERER);
diff --git a/actions/comments/delete.php b/actions/comments/delete.php
index f2c058ff4..c6b481da4 100644
--- a/actions/comments/delete.php
+++ b/actions/comments/delete.php
@@ -5,11 +5,6 @@
* @package Elgg
*/
-// Ensure we're logged in
-if (!elgg_is_logged_in()) {
- forward();
-}
-
// Make sure we can get the comment in question
$annotation_id = (int) get_input('annotation_id');
$comment = elgg_get_annotation_from_id($annotation_id);
diff --git a/actions/friends/collections/add.php b/actions/friends/collections/add.php
index 9dc17b37e..e63a149f7 100644
--- a/actions/friends/collections/add.php
+++ b/actions/friends/collections/add.php
@@ -6,7 +6,7 @@
* @subpackage Friends.Collections
*/
-$collection_name = get_input('collection_name');
+$collection_name = htmlspecialchars(get_input('collection_name', '', false), ENT_QUOTES, 'UTF-8');
$friends = get_input('friends_collection');
if (!$collection_name) {
diff --git a/actions/login.php b/actions/login.php
index 1e5e92ede..bd7f91299 100644
--- a/actions/login.php
+++ b/actions/login.php
@@ -9,7 +9,6 @@
// set forward url
if (!empty($_SESSION['last_forward_from'])) {
$forward_url = $_SESSION['last_forward_from'];
- unset($_SESSION['last_forward_from']);
} elseif (get_input('returntoreferer')) {
$forward_url = REFERER;
} else {
@@ -62,5 +61,9 @@ if ($user->language) {
$message = elgg_echo('loginok');
}
+if (isset($_SESSION['last_forward_from'])) {
+ unset($_SESSION['last_forward_from']);
+}
+
system_message($message);
forward($forward_url);
diff --git a/actions/profile/edit.php b/actions/profile/edit.php
index 63fb31600..e1f066e82 100644
--- a/actions/profile/edit.php
+++ b/actions/profile/edit.php
@@ -4,6 +4,8 @@
*
*/
+elgg_make_sticky_form('profile:edit');
+
$guid = get_input('guid');
$owner = get_entity($guid);
@@ -48,7 +50,7 @@ foreach ($profile_fields as $shortname => $valuetype) {
forward(REFERER);
}
- if ($valuetype == 'url' && !preg_match('~^https?\://~i', $value)) {
+ if ($value && $valuetype == 'url' && !preg_match('~^https?\://~i', $value)) {
$value = "http://$value";
}
@@ -80,7 +82,7 @@ if (sizeof($input) > 0) {
);
elgg_delete_metadata($options);
- if(!is_null($value) && ($value !== '')){
+ if (!is_null($value) && ($value !== '')) {
// only create metadata for non empty values (0 is allowed) to prevent metadata records with empty string values #4858
if (isset($accesslevel[$shortname])) {
@@ -107,6 +109,7 @@ if (sizeof($input) > 0) {
// Notify of profile update
elgg_trigger_event('profileupdate', $owner->type, $owner);
+ elgg_clear_sticky_form('profile:edit');
system_message(elgg_echo("profile:saved"));
}
diff --git a/documentation/info/manifest.xml b/documentation/info/manifest.xml
index 494158481..4fd4be8ce 100644
--- a/documentation/info/manifest.xml
+++ b/documentation/info/manifest.xml
@@ -7,7 +7,7 @@
<description>This is a longer, more interesting description of my plugin, its features, and other important information.</description>
<website>http://www.elgg.org/</website>
<repository>https://github.com/Elgg/Elgg</repository>
- <bugtracker>http://trac.elgg.org</bugtracker>
+ <bugtracker>https://github.com/Elgg/Elgg/issues</bugtracker>
<donations>http://elgg.org/supporter.php</donations>
<copyright>(C) Elgg 2011</copyright>
<license>GNU General Public License version 2</license>
diff --git a/engine/classes/ElggAccess.php b/engine/classes/ElggAccess.php
index 6f8d9bb4b..0aed477fc 100644
--- a/engine/classes/ElggAccess.php
+++ b/engine/classes/ElggAccess.php
@@ -16,6 +16,7 @@ class ElggAccess {
*/
private $ignore_access;
+ // @codingStandardsIgnoreStart
/**
* Get current ignore access setting.
*
@@ -26,6 +27,7 @@ class ElggAccess {
elgg_deprecated_notice('ElggAccess::get_ignore_access() is deprecated by ElggAccess::getIgnoreAccess()', 1.8);
return $this->getIgnoreAccess();
}
+ // @codingStandardsIgnoreEnd
/**
* Get current ignore access setting.
@@ -36,6 +38,7 @@ class ElggAccess {
return $this->ignore_access;
}
+ // @codingStandardsIgnoreStart
/**
* Set ignore access.
*
@@ -49,6 +52,7 @@ class ElggAccess {
elgg_deprecated_notice('ElggAccess::set_ignore_access() is deprecated by ElggAccess::setIgnoreAccess()', 1.8);
return $this->setIgnoreAccess($ignore);
}
+ // @codingStandardsIgnoreEnd
/**
* Set ignore access.
diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php
index 2d1c1abde..ffc80b02d 100644
--- a/engine/classes/ElggAttributeLoader.php
+++ b/engine/classes/ElggAttributeLoader.php
@@ -4,6 +4,9 @@
* Loads ElggEntity attributes from DB or validates those passed in via constructor
*
* @access private
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel
*/
class ElggAttributeLoader {
@@ -21,7 +24,7 @@ class ElggAttributeLoader {
'time_created',
'time_updated',
'last_action',
- 'enabled'
+ 'enabled',
);
/**
@@ -65,9 +68,11 @@ class ElggAttributeLoader {
public $full_loader = '';
/**
- * @param string $class class of object being loaded
- * @param string $required_type entity type this is being used to populate
- * @param array $initialized_attrs attributes after initializeAttributes() has been run
+ * Constructor
+ *
+ * @param string $class class of object being loaded
+ * @param string $required_type entity type this is being used to populate
+ * @param array $initialized_attrs attributes after initializeAttributes() has been run
* @throws InvalidArgumentException
*/
public function __construct($class, $required_type, array $initialized_attrs) {
@@ -87,14 +92,33 @@ class ElggAttributeLoader {
$this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);
}
+ /**
+ * Get primary attributes missing that are missing
+ *
+ * @param stdClass $row Database row
+ * @return array
+ */
protected function isMissingPrimaries($row) {
return array_diff(self::$primary_attr_names, array_keys($row)) !== array();
}
+ /**
+ * Get secondary attributes that are missing
+ *
+ * @param stdClass $row Database row
+ * @return array
+ */
protected function isMissingSecondaries($row) {
return array_diff($this->secondary_attr_names, array_keys($row)) !== array();
}
+ /**
+ * Check that the type is correct
+ *
+ * @param stdClass $row Database row
+ * @return void
+ * @throws InvalidClassException
+ */
protected function checkType($row) {
if ($row['type'] !== $this->required_type) {
$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($row['guid'], $this->class));
@@ -176,6 +200,8 @@ class ElggAttributeLoader {
// saved, these are stored w/ type "site", but with no sites_entity row. These
// are probably only created in the unit tests.
// @todo Don't save vanilla ElggEntities with type "site"
+
+ $row = $this->filterAddedColumns($row);
$row['guid'] = (int) $row['guid'];
return $row;
}
@@ -185,15 +211,38 @@ class ElggAttributeLoader {
}
}
- // loading complete: re-check missing and check type
- if (($was_missing_primaries && $this->isMissingPrimaries($row))
- || ($was_missing_secondaries && $this->isMissingSecondaries($row))) {
- throw new LogicException('Attribute loaders failed to return proper attributes');
- }
+ $row = $this->filterAddedColumns($row);
+
+ // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let
+ // this pass so the upgrades can run.
- // guid needs to be an int http://trac.elgg.org/ticket/4111
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
$row['guid'] = (int) $row['guid'];
return $row;
}
+
+ /**
+ * Filter out keys returned by the query which should not appear in the entity's attributes
+ *
+ * @param array $row All columns from the query
+ * @return array Columns acceptable for the entity's attributes
+ */
+ protected function filterAddedColumns($row) {
+ // make an array with keys as acceptable attribute names
+ $acceptable_attrs = self::$primary_attr_names;
+ array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names);
+ $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs);
+
+ // @todo remove these when #4584 is in place
+ $acceptable_attrs['tables_split'] = true;
+ $acceptable_attrs['tables_loaded'] = true;
+
+ foreach ($row as $key => $val) {
+ if (!isset($acceptable_attrs[$key])) {
+ unset($row[$key]);
+ }
+ }
+ return $row;
+ }
}
diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php
index f3c7cc972..05842d1b2 100644
--- a/engine/classes/ElggAutoP.php
+++ b/engine/classes/ElggAutoP.php
@@ -7,6 +7,9 @@
*
* In DIV elements, Ps are only added when there would be at
* least two of them.
+ *
+ * @package Elgg.Core
+ * @subpackage Output
*/
class ElggAutoP {
@@ -51,8 +54,12 @@ class ElggAutoP {
protected $_alterList = 'article aside blockquote body details div footer header
section';
+ /** @var string */
protected $_unique = '';
+ /**
+ * Constructor
+ */
public function __construct() {
$this->_blocks = preg_split('@\\s+@', $this->_blocks);
$this->_descendList = preg_split('@\\s+@', $this->_descendList);
@@ -98,21 +105,28 @@ class ElggAutoP {
$html = str_replace('&', $this->_unique . 'AMP', $html);
$this->_doc = new DOMDocument();
-
+
// parse to DOM, suppressing loadHTML warnings
// http://www.php.net/manual/en/domdocument.loadhtml.php#95463
libxml_use_internal_errors(true);
+ // Do not load entities. May be unnecessary, better safe than sorry
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
if (!$this->_doc->loadHTML("<html><meta http-equiv='content-type' "
. "content='text/html; charset={$this->encoding}'><body>{$html}</body>"
. "</html>")) {
+
+ libxml_disable_entity_loader($disable_load_entities);
return false;
}
+ libxml_disable_entity_loader($disable_load_entities);
+
$this->_xpath = new DOMXPath($this->_doc);
// start processing recursively at the BODY element
$nodeList = $this->_xpath->query('//body[1]');
- $this->_addParagraphs($nodeList->item(0));
+ $this->addParagraphs($nodeList->item(0));
// serialize back to HTML
$html = $this->_doc->saveHTML();
@@ -128,9 +142,16 @@ class ElggAutoP {
// re-parse so we can handle new AUTOP elements
+ // Do not load entities. May be unnecessary, better safe than sorry
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
if (!$this->_doc->loadHTML($html)) {
+ libxml_disable_entity_loader($disable_load_entities);
return false;
}
+
+ libxml_disable_entity_loader($disable_load_entities);
+
// must re-create XPath object after DOM load
$this->_xpath = new DOMXPath($this->_doc);
@@ -187,15 +208,16 @@ class ElggAutoP {
/**
* Add P and BR elements as necessary
*
- * @param DOMElement $el
+ * @param DOMElement $el DOM element
+ * @return void
*/
- protected function _addParagraphs(DOMElement $el) {
+ protected function addParagraphs(DOMElement $el) {
// no need to call recursively, just queue up
$elsToProcess = array($el);
$inlinesToProcess = array();
while ($el = array_shift($elsToProcess)) {
// if true, we can alter all child nodes, if not, we'll just call
- // _addParagraphs on each element in the descendInto list
+ // addParagraphs on each element in the descendInto list
$alterInline = in_array($el->nodeName, $this->_alterList);
// inside affected elements, we want to trim leading whitespace from
@@ -229,8 +251,8 @@ class ElggAutoP {
if ($alterInline) {
$isText = ($node->nodeType === XML_TEXT_NODE);
$isLastInline = (! $node->nextSibling
- || ($node->nextSibling->nodeType === XML_ELEMENT_NODE
- && in_array($node->nextSibling->nodeName, $this->_blocks)));
+ || ($node->nextSibling->nodeType === XML_ELEMENT_NODE
+ && in_array($node->nextSibling->nodeName, $this->_blocks)));
if ($isElement) {
$isFollowingBr = ($node->nodeName === 'br');
}
@@ -263,7 +285,7 @@ class ElggAutoP {
if ($isBlock) {
if (in_array($node->nodeName, $this->_descendList)) {
$elsToProcess[] = $node;
- //$this->_addParagraphs($node);
+ //$this->addParagraphs($node);
}
}
$openP = true;
diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php
index 5d59425d0..d810ea066 100644
--- a/engine/classes/ElggBatch.php
+++ b/engine/classes/ElggBatch.php
@@ -150,6 +150,20 @@ class ElggBatch
private $incrementOffset = true;
/**
+ * Entities that could not be instantiated during a fetch
+ *
+ * @var stdClass[]
+ */
+ private $incompleteEntities = array();
+
+ /**
+ * Total number of incomplete entities fetched
+ *
+ * @var int
+ */
+ private $totalIncompletes = 0;
+
+ /**
* Batches operations on any elgg_get_*() or compatible function that supports
* an options array.
*
@@ -222,16 +236,22 @@ class ElggBatch
}
/**
+ * Tell the process that an entity was incomplete during a fetch
+ *
+ * @param stdClass $row
+ *
+ * @access private
+ */
+ public function reportIncompleteEntity(stdClass $row) {
+ $this->incompleteEntities[] = $row;
+ }
+
+ /**
* Fetches the next chunk of results
*
* @return bool
*/
private function getNextResultsChunk() {
- // reset memory caches after first chunk load
- if ($this->chunkIndex > 0) {
- global $DB_QUERY_CACHE, $ENTITY_CACHE;
- $DB_QUERY_CACHE = $ENTITY_CACHE = array();
- }
// always reset results.
$this->results = array();
@@ -265,27 +285,47 @@ class ElggBatch
if ($this->incrementOffset) {
$offset = $this->offset + $this->retrievedResults;
} else {
- $offset = $this->offset;
+ $offset = $this->offset + $this->totalIncompletes;
}
$current_options = array(
'limit' => $limit,
- 'offset' => $offset
+ 'offset' => $offset,
+ '__ElggBatch' => $this,
);
$options = array_merge($this->options, $current_options);
- $getter = $this->getter;
- if (is_string($getter)) {
- $this->results = $getter($options);
- } else {
- $this->results = call_user_func_array($getter, array($options));
+ $this->incompleteEntities = array();
+ $this->results = call_user_func_array($this->getter, array($options));
+
+ $num_results = count($this->results);
+ $num_incomplete = count($this->incompleteEntities);
+
+ $this->totalIncompletes += $num_incomplete;
+
+ if ($this->incompleteEntities) {
+ // pad the front of the results with nulls representing the incompletes
+ array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null));
+ // ...and skip past them
+ reset($this->results);
+ for ($i = 0; $i < $num_incomplete; $i++) {
+ next($this->results);
+ }
}
if ($this->results) {
$this->chunkIndex++;
- $this->resultIndex = 0;
- $this->retrievedResults += count($this->results);
+
+ // let the system know we've jumped past the nulls
+ $this->resultIndex = $num_incomplete;
+
+ $this->retrievedResults += ($num_results + $num_incomplete);
+ if ($num_results == 0) {
+ // This fetch was *all* incompletes! We need to fetch until we can either
+ // offer at least one row to iterate over, or give up.
+ return $this->getNextResultsChunk();
+ }
return true;
} else {
return false;
diff --git a/engine/classes/ElggCache.php b/engine/classes/ElggCache.php
index 4317f4be9..909eab39b 100644
--- a/engine/classes/ElggCache.php
+++ b/engine/classes/ElggCache.php
@@ -21,6 +21,7 @@ abstract class ElggCache implements ArrayAccess {
$this->variables = array();
}
+ // @codingStandardsIgnoreStart
/**
* Set a cache variable.
*
@@ -35,6 +36,7 @@ abstract class ElggCache implements ArrayAccess {
elgg_deprecated_notice('ElggCache::set_variable() is deprecated by ElggCache::setVariable()', 1.8);
$this->setVariable($variable, $value);
}
+ // @codingStandardsIgnoreEnd
/**
* Set a cache variable.
@@ -52,6 +54,7 @@ abstract class ElggCache implements ArrayAccess {
$this->variables[$variable] = $value;
}
+ // @codingStandardsIgnoreStart
/**
* Get variables for this cache.
*
@@ -65,6 +68,7 @@ abstract class ElggCache implements ArrayAccess {
elgg_deprecated_notice('ElggCache::get_variable() is deprecated by ElggCache::getVariable()', 1.8);
return $this->getVariable($variable);
}
+ // @codingStandardsIgnoreEnd
/**
* Get variables for this cache.
diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php
new file mode 100644
index 000000000..317d371e4
--- /dev/null
+++ b/engine/classes/ElggCrypto.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * ElggCrypto
+ *
+ * @package Elgg.Core
+ * @subpackage Crypto
+ *
+ * @access private
+ */
+class ElggCrypto {
+
+ /**
+ * Character set for temp passwords (no risk of embedded profanity/glyphs that look similar)
+ */
+ const CHARS_PASSWORD = 'bcdfghjklmnpqrstvwxyz2346789';
+
+ /**
+ * Generate a string of highly randomized bytes (over the full 8-bit range).
+ *
+ * @param int $length Number of bytes needed
+ * @return string Random bytes
+ *
+ * @author George Argyros <argyros.george@gmail.com>
+ * @copyright 2012, George Argyros. All rights reserved.
+ * @license Modified BSD
+ * @link https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/blob/master/srand.php Original
+ *
+ * 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 the <organization> 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 GEORGE ARGYROS 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.
+ */
+ public function getRandomBytes($length) {
+ /**
+ * Our primary choice for a cryptographic strong randomness function is
+ * openssl_random_pseudo_bytes.
+ */
+ $SSLstr = '4'; // http://xkcd.com/221/
+ if (function_exists('openssl_random_pseudo_bytes')
+ && (version_compare(PHP_VERSION, '5.3.4') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $SSLstr = openssl_random_pseudo_bytes($length, $strong);
+ if ($strong) {
+ return $SSLstr;
+ }
+ }
+
+ /**
+ * If mcrypt extension is available then we use it to gather entropy from
+ * the operating system's PRNG. This is better than reading /dev/urandom
+ * directly since it avoids reading larger blocks of data than needed.
+ * Older versions of mcrypt_create_iv may be broken or take too much time
+ * to finish so we only use this function with PHP 5.3.7 and above.
+ * @see https://bugs.php.net/bug.php?id=55169
+ */
+ if (function_exists('mcrypt_create_iv')
+ && (version_compare(PHP_VERSION, '5.3.7') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
+ if ($str !== false) {
+ return $str;
+ }
+ }
+
+ /**
+ * No build-in crypto randomness function found. We collect any entropy
+ * available in the PHP core PRNGs along with some filesystem info and memory
+ * stats. To make this data cryptographically strong we add data either from
+ * /dev/urandom or if its unavailable, we gather entropy by measuring the
+ * time needed to compute a number of SHA-1 hashes.
+ */
+ $str = '';
+ $bits_per_round = 2; // bits of entropy collected in each clock drift round
+ $msec_per_round = 400; // expected running time of each round in microseconds
+ $hash_len = 20; // SHA-1 Hash length
+ $total = $length; // total bytes of entropy to collect
+
+ $handle = @fopen('/dev/urandom', 'rb');
+ if ($handle && function_exists('stream_set_read_buffer')) {
+ @stream_set_read_buffer($handle, 0);
+ }
+
+ do {
+ $bytes = ($total > $hash_len) ? $hash_len : $total;
+ $total -= $bytes;
+
+ //collect any entropy available from the PHP system and filesystem
+ $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
+ $entropy .= implode('', @fstat(@fopen(__FILE__, 'r')));
+ $entropy .= memory_get_usage() . getmypid();
+ $entropy .= serialize($_ENV) . serialize($_SERVER);
+ if (function_exists('posix_times')) {
+ $entropy .= serialize(posix_times());
+ }
+ if (function_exists('zend_thread_id')) {
+ $entropy .= zend_thread_id();
+ }
+
+ if ($handle) {
+ $entropy .= @fread($handle, $bytes);
+ } else {
+ // Measure the time that the operations will take on average
+ for ($i = 0; $i < 3; $i++) {
+ $c1 = microtime(true);
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < 50; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = microtime(true);
+ $entropy .= $c1 . $c2;
+ }
+
+ // Based on the above measurement determine the total rounds
+ // in order to bound the total running time.
+ $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000));
+
+ // Take the additional measurements. On average we can expect
+ // at least $bits_per_round bits of entropy from each measurement.
+ $iter = $bytes * (int) (ceil(8 / $bits_per_round));
+
+ for ($i = 0; $i < $iter; $i++) {
+ $c1 = microtime();
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < $rounds; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = microtime();
+ $entropy .= $c1 . $c2;
+ }
+ }
+
+ // We assume sha1 is a deterministic extractor for the $entropy variable.
+ $str .= sha1($entropy, true);
+
+ } while ($length > strlen($str));
+
+ if ($handle) {
+ @fclose($handle);
+ }
+
+ return substr($str, 0, $length);
+ }
+
+ /**
+ * Generate a random string of specified length.
+ *
+ * Uses supplied character list for generating the new string.
+ * If no character list provided - uses Base64 URL character set.
+ *
+ * @param int $length Desired length of the string
+ * @param string|null $chars Characters to be chosen from randomly. If not given, the Base64 URL
+ * charset will be used.
+ *
+ * @return string The random string
+ *
+ * @throws InvalidArgumentException
+ *
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ *
+ * @see https://github.com/zendframework/zf2/blob/master/library/Zend/Math/Rand.php#L179
+ */
+ public static function getRandomString($length, $chars = null) {
+ if ($length < 1) {
+ throw new InvalidArgumentException('Length should be >= 1');
+ }
+
+ if (empty($chars)) {
+ $numBytes = ceil($length * 0.75);
+ $bytes = self::getRandomBytes($numBytes);
+ $string = substr(rtrim(base64_encode($bytes), '='), 0, $length);
+
+ // Base64 URL
+ return strtr($string, '+/', '-_');
+ }
+
+ $listLen = strlen($chars);
+
+ if ($listLen == 1) {
+ return str_repeat($chars, $length);
+ }
+
+ $bytes = self::getRandomBytes($length);
+ $pos = 0;
+ $result = '';
+ for ($i = 0; $i < $length; $i++) {
+ $pos = ($pos + ord($bytes[$i])) % $listLen;
+ $result .= $chars[$pos];
+ }
+
+ return $result;
+ }
+}
diff --git a/engine/classes/ElggData.php b/engine/classes/ElggData.php
index 426248ca3..4f843cde4 100644
--- a/engine/classes/ElggData.php
+++ b/engine/classes/ElggData.php
@@ -26,6 +26,7 @@ abstract class ElggData implements
*/
protected $attributes = array();
+ // @codingStandardsIgnoreStart
/**
* Initialise the attributes array.
*
@@ -44,6 +45,7 @@ abstract class ElggData implements
elgg_deprecated_notice('initialise_attributes() is deprecated by initializeAttributes()', 1.8);
}
}
+ // @codingStandardsIgnoreEnd
/**
* Initialize the attributes array.
diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php
index 7374aad35..6e2354012 100644
--- a/engine/classes/ElggDiskFilestore.php
+++ b/engine/classes/ElggDiskFilestore.php
@@ -194,7 +194,9 @@ class ElggDiskFilestore extends ElggFilestore {
}
/**
- * Returns the filename as saved on disk for an ElggFile object
+ * Get the filename as saved on disk for an ElggFile object
+ *
+ * Returns an empty string if no filename set
*
* @param ElggFile $file File object
*
@@ -213,7 +215,12 @@ class ElggDiskFilestore extends ElggFilestore {
throw new InvalidParameterException($msg);
}
- return $this->dir_root . $this->makefileMatrix($owner_guid) . $file->getFilename();
+ $filename = $file->getFilename();
+ if (!$filename) {
+ return '';
+ }
+
+ return $this->dir_root . $this->makeFileMatrix($owner_guid) . $filename;
}
/**
@@ -221,7 +228,7 @@ class ElggDiskFilestore extends ElggFilestore {
*
* @param ElggFile $file File object
*
- * @return mixed
+ * @return string
*/
public function grabFile(ElggFile $file) {
return file_get_contents($file->getFilenameOnFilestore());
@@ -235,6 +242,9 @@ class ElggDiskFilestore extends ElggFilestore {
* @return bool
*/
public function exists(ElggFile $file) {
+ if (!$file->getFilename()) {
+ return false;
+ }
return file_exists($this->getFilenameOnFilestore($file));
}
@@ -248,12 +258,13 @@ class ElggDiskFilestore extends ElggFilestore {
*/
public function getSize($prefix = '', $container_guid) {
if ($container_guid) {
- return get_dir_size($this->dir_root . $this->makefileMatrix($container_guid) . $prefix);
+ return get_dir_size($this->dir_root . $this->makeFileMatrix($container_guid) . $prefix);
} else {
return false;
}
}
+ // @codingStandardsIgnoreStart
/**
* Create a directory $dirroot
*
@@ -268,6 +279,7 @@ class ElggDiskFilestore extends ElggFilestore {
return $this->makeDirectoryRoot($dirroot);
}
+ // @codingStandardsIgnoreEnd
/**
* Create a directory $dirroot
@@ -287,6 +299,7 @@ class ElggDiskFilestore extends ElggFilestore {
return true;
}
+ // @codingStandardsIgnoreStart
/**
* Multibyte string tokeniser.
*
@@ -318,7 +331,9 @@ class ElggDiskFilestore extends ElggFilestore {
return str_split($string);
}
}
+ // @codingStandardsIgnoreEnd
+ // @codingStandardsIgnoreStart
/**
* Construct a file path matrix for an entity.
*
@@ -330,8 +345,9 @@ class ElggDiskFilestore extends ElggFilestore {
protected function make_file_matrix($identifier) {
elgg_deprecated_notice('ElggDiskFilestore::make_file_matrix() is deprecated by ::makeFileMatrix()', 1.8);
- return $this->makefileMatrix($identifier);
+ return $this->makeFileMatrix($identifier);
}
+ // @codingStandardsIgnoreEnd
/**
* Construct a file path matrix for an entity.
@@ -352,6 +368,7 @@ class ElggDiskFilestore extends ElggFilestore {
return "$time_created/$entity->guid/";
}
+ // @codingStandardsIgnoreStart
/**
* Construct a filename matrix.
*
@@ -370,6 +387,7 @@ class ElggDiskFilestore extends ElggFilestore {
return $this->makeFileMatrix($guid);
}
+ // @codingStandardsIgnoreEnd
/**
* Returns a list of attributes to save to the database when saving
diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php
index f44e73023..a563f6fad 100644
--- a/engine/classes/ElggEntity.php
+++ b/engine/classes/ElggEntity.php
@@ -24,7 +24,7 @@
*
* @package Elgg.Core
* @subpackage DataModel.Entities
- *
+ *
* @property string $type object, user, group, or site (read-only after save)
* @property string $subtype Further clarifies the nature of the entity (read-only after save)
* @property int $guid The unique identifier for this entity (read only)
@@ -352,8 +352,8 @@ abstract class ElggEntity extends ElggData implements
'limit' => 0
);
// @todo in 1.9 make this return false if can't add metadata
- // http://trac.elgg.org/ticket/4520
- //
+ // https://github.com/elgg/elgg/issues/4520
+ //
// need to remove access restrictions right now to delete
// because this is the expected behavior
$ia = elgg_set_ignore_access(true);
@@ -375,12 +375,11 @@ abstract class ElggEntity extends ElggData implements
}
return $result;
- }
-
- // unsaved entity. store in temp array
- // returning single entries instead of an array of 1 element is decided in
- // getMetaData(), just like pulling from the db.
- else {
+ } else {
+ // unsaved entity. store in temp array
+ // returning single entries instead of an array of 1 element is decided in
+ // getMetaData(), just like pulling from the db.
+ //
// if overwrite, delete first
if (!$multiple || !isset($this->temp_metadata[$name])) {
$this->temp_metadata[$name] = array();
@@ -965,7 +964,7 @@ abstract class ElggEntity extends ElggData implements
*
* @tip Can be overridden by registering for the permissions_check:comment,
* <entity type> plugin hook.
- *
+ *
* @param int $user_guid User guid (default is logged in user)
*
* @return bool
@@ -1271,15 +1270,23 @@ abstract class ElggEntity extends ElggData implements
public function save() {
$guid = $this->getGUID();
if ($guid > 0) {
- cache_entity($this);
- return update_entity(
+ // See #5600. This ensures the lower level can_edit_entity() check will use a
+ // fresh entity from the DB so it sees the persisted owner_guid
+ _elgg_disable_caching_for_entity($guid);
+
+ $ret = update_entity(
$guid,
$this->get('owner_guid'),
$this->get('access_id'),
$this->get('container_guid'),
$this->get('time_created')
);
+
+ _elgg_enable_caching_for_entity($guid);
+ _elgg_cache_entity($this);
+
+ return $ret;
} else {
// Create a new entity (nb: using attribute array directly
// 'cos set function does something special!)
@@ -1321,7 +1328,7 @@ abstract class ElggEntity extends ElggData implements
$this->attributes['subtype'] = get_subtype_id($this->attributes['type'],
$this->attributes['subtype']);
- cache_entity($this);
+ _elgg_cache_entity($this);
return $this->attributes['guid'];
}
@@ -1358,12 +1365,12 @@ abstract class ElggEntity extends ElggData implements
$this->attributes['tables_loaded']++;
}
- // guid needs to be an int http://trac.elgg.org/ticket/4111
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
$this->attributes['guid'] = (int)$this->attributes['guid'];
// Cache object handle
if ($this->attributes['guid']) {
- cache_entity($this);
+ _elgg_cache_entity($this);
}
return true;
diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php
index 3e9c24c17..23080834b 100644
--- a/engine/classes/ElggFile.php
+++ b/engine/classes/ElggFile.php
@@ -275,9 +275,14 @@ class ElggFile extends ElggObject {
*/
public function delete() {
$fs = $this->getFilestore();
- if ($fs->delete($this)) {
- return parent::delete();
+
+ $result = $fs->delete($this);
+
+ if ($this->getGUID() && $result) {
+ $result = parent::delete();
}
+
+ return $result;
}
/**
diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php
index e654f1db2..94143f777 100644
--- a/engine/classes/ElggFileCache.php
+++ b/engine/classes/ElggFileCache.php
@@ -26,6 +26,7 @@ class ElggFileCache extends ElggCache {
}
}
+ // @codingStandardsIgnoreStart
/**
* Create and return a handle to a file.
*
@@ -41,6 +42,7 @@ class ElggFileCache extends ElggCache {
return $this->createFile($filename, $rw);
}
+ // @codingStandardsIgnoreEnd
/**
* Create and return a handle to a file.
@@ -72,6 +74,7 @@ class ElggFileCache extends ElggCache {
return fopen($path . $filename, $rw);
}
+ // @codingStandardsIgnoreStart
/**
* Create a sanitised filename for the file.
*
@@ -86,6 +89,7 @@ class ElggFileCache extends ElggCache {
return $filename;
}
+ // @codingStandardsIgnoreEnd
/**
* Create a sanitised filename for the file.
diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php
index 61f699f1a..7e69b7a84 100644
--- a/engine/classes/ElggGroup.php
+++ b/engine/classes/ElggGroup.php
@@ -48,21 +48,18 @@ class ElggGroup extends ElggEntity
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // Is $guid is an ElggGroup? Use a copy constructor
} else if ($guid instanceof ElggGroup) {
+ // $guid is an ElggGroup so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggGroup constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggGroup = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo why separate from else
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggGroup'));
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load entity
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -338,7 +335,7 @@ class ElggGroup extends ElggEntity
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -355,7 +352,12 @@ class ElggGroup extends ElggEntity
}
// Now save specific stuff
- return create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
+
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
}
// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
diff --git a/engine/classes/ElggLRUCache.php b/engine/classes/ElggLRUCache.php
new file mode 100644
index 000000000..f51af2ed7
--- /dev/null
+++ b/engine/classes/ElggLRUCache.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * Least Recently Used Cache
+ *
+ * A fixed sized cache that removes the element used last when it reaches its
+ * size limit.
+ *
+ * Based on https://github.com/cash/LRUCache
+ *
+ * @access private
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+class ElggLRUCache implements ArrayAccess {
+ /** @var int */
+ protected $maximumSize;
+
+ /**
+ * The front of the array contains the LRU element
+ *
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * Create a LRU Cache
+ *
+ * @param int $size The size of the cache
+ * @throws InvalidArgumentException
+ */
+ public function __construct($size) {
+ if (!is_int($size) || $size <= 0) {
+ throw new InvalidArgumentException();
+ }
+ $this->maximumSize = $size;
+ }
+
+ /**
+ * Get the value cached with this key
+ *
+ * @param int|string $key The key. Strings that are ints are cast to ints.
+ * @param mixed $default The value to be returned if key not found. (Optional)
+ * @return mixed
+ */
+ public function get($key, $default = null) {
+ if (isset($this->data[$key])) {
+ $this->recordAccess($key);
+ return $this->data[$key];
+ } else {
+ return $default;
+ }
+ }
+
+ /**
+ * Add something to the cache
+ *
+ * @param int|string $key The key. Strings that are ints are cast to ints.
+ * @param mixed $value The value to cache
+ * @return void
+ */
+ public function set($key, $value) {
+ if (isset($this->data[$key])) {
+ $this->data[$key] = $value;
+ $this->recordAccess($key);
+ } else {
+ $this->data[$key] = $value;
+ if ($this->size() > $this->maximumSize) {
+ // remove least recently used element (front of array)
+ reset($this->data);
+ unset($this->data[key($this->data)]);
+ }
+ }
+ }
+
+ /**
+ * Get the number of elements in the cache
+ *
+ * @return int
+ */
+ public function size() {
+ return count($this->data);
+ }
+
+ /**
+ * Does the cache contain an element with this key
+ *
+ * @param int|string $key The key
+ * @return boolean
+ */
+ public function containsKey($key) {
+ return isset($this->data[$key]);
+ }
+
+ /**
+ * Remove the element with this key.
+ *
+ * @param int|string $key The key
+ * @return mixed Value or null if not set
+ */
+ public function remove($key) {
+ if (isset($this->data[$key])) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ return $value;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Clear the cache
+ *
+ * @return void
+ */
+ public function clear() {
+ $this->data = array();
+ }
+
+ /**
+ * Moves the element from current position to end of array
+ *
+ * @param int|string $key The key
+ * @return void
+ */
+ protected function recordAccess($key) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ $this->data[$key] = $value;
+ }
+
+ /**
+ * Assigns a value for the specified key
+ *
+ * @see ArrayAccess::offsetSet()
+ *
+ * @param int|string $key The key to assign the value to.
+ * @param mixed $value The value to set.
+ * @return void
+ */
+ public function offsetSet($key, $value) {
+ $this->set($key, $value);
+ }
+
+ /**
+ * Get the value for specified key
+ *
+ * @see ArrayAccess::offsetGet()
+ *
+ * @param int|string $key The key to retrieve.
+ * @return mixed
+ */
+ public function offsetGet($key) {
+ return $this->get($key);
+ }
+
+ /**
+ * Unsets a key.
+ *
+ * @see ArrayAccess::offsetUnset()
+ *
+ * @param int|string $key The key to unset.
+ * @return void
+ */
+ public function offsetUnset($key) {
+ $this->remove($key);
+ }
+
+ /**
+ * Does key exist?
+ *
+ * @see ArrayAccess::offsetExists()
+ *
+ * @param int|string $key A key to check for.
+ * @return boolean
+ */
+ public function offsetExists($key) {
+ return $this->containsKey($key);
+ }
+}
diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php
index f6ec9dcd3..b463143d8 100644
--- a/engine/classes/ElggMenuBuilder.php
+++ b/engine/classes/ElggMenuBuilder.php
@@ -128,8 +128,10 @@ class ElggMenuBuilder {
$parent_name = $menu_item->getParentName();
if (array_key_exists($parent_name, $current_gen)) {
$next_gen[$menu_item->getName()] = $menu_item;
- $current_gen[$parent_name]->addChild($menu_item);
- $menu_item->setParent($current_gen[$parent_name]);
+ if (!in_array($menu_item, $current_gen[$parent_name]->getData('children'))) {
+ $current_gen[$parent_name]->addChild($menu_item);
+ $menu_item->setParent($current_gen[$parent_name]);
+ }
unset($children[$index]);
}
}
@@ -235,8 +237,8 @@ class ElggMenuBuilder {
/**
* Compare two menu items by their display text
*
- * @param ElggMenuItem $a
- * @param ElggMenuItem $b
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
* @return bool
*/
public static function compareByText($a, $b) {
@@ -253,8 +255,8 @@ class ElggMenuBuilder {
/**
* Compare two menu items by their identifiers
*
- * @param ElggMenuItem $a
- * @param ElggMenuItem $b
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
* @return bool
*/
public static function compareByName($a, $b) {
@@ -271,8 +273,8 @@ class ElggMenuBuilder {
/**
* Compare two menu items by their priority
*
- * @param ElggMenuItem $a
- * @param ElggMenuItem $b
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
* @return bool
*
* @todo change name to compareByPriority
diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php
index 6263f84f6..aeaa3ba5c 100644
--- a/engine/classes/ElggObject.php
+++ b/engine/classes/ElggObject.php
@@ -66,21 +66,18 @@ class ElggObject extends ElggEntity {
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // Is $guid is an ElggObject? Use a copy constructor
} else if ($guid instanceof ElggObject) {
+ // $guid is an ElggObject so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggObject constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggObject = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo remove - do not need separate exception
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggObject'));
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -110,7 +107,7 @@ class ElggObject extends ElggEntity {
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -129,8 +126,12 @@ class ElggObject extends ElggEntity {
}
// Save ElggObject-specific attributes
- return create_object_entity($this->get('guid'), $this->get('title'),
- $this->get('description'));
+
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_object_entity($this->get('guid'), $this->get('title'), $this->get('description'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
}
/**
diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php
index ae447bddb..545b9a53c 100644
--- a/engine/classes/ElggPlugin.php
+++ b/engine/classes/ElggPlugin.php
@@ -299,17 +299,15 @@ class ElggPlugin extends ElggObject {
$private_settings = get_data($q);
- if ($private_settings) {
- $return = array();
+ $return = array();
+ if ($private_settings) {
foreach ($private_settings as $setting) {
$return[$setting->name] = $setting->value;
}
-
- return $return;
}
- return false;
+ return $return;
}
/**
@@ -350,11 +348,14 @@ class ElggPlugin extends ElggObject {
*/
public function unsetAllSettings() {
$db_prefix = get_config('dbprefix');
- $ps_prefix = elgg_namespace_plugin_private_setting('setting', '');
+
+ $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+ $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID());
$q = "DELETE FROM {$db_prefix}private_settings
WHERE entity_guid = $this->guid
- AND name NOT LIKE '$ps_prefix%'";
+ AND name NOT LIKE '$us_prefix%'
+ AND name NOT LIKE '$is_prefix%'";
return delete_data($q);
}
@@ -420,20 +421,18 @@ class ElggPlugin extends ElggObject {
$private_settings = get_data($q);
- if ($private_settings) {
- $return = array();
+ $return = array();
+ if ($private_settings) {
foreach ($private_settings as $setting) {
$name = substr($setting->name, $ps_prefix_len);
$value = $setting->value;
$return[$name] = $value;
}
-
- return $return;
}
- return false;
+ return $return;
}
/**
@@ -546,7 +545,7 @@ class ElggPlugin extends ElggObject {
* Returns if the plugin is complete, meaning has all required files
* and Elgg can read them and they make sense.
*
- * @todo bad name? This could be confused with isValid() from ElggPackage.
+ * @todo bad name? This could be confused with isValid() from ElggPluginPackage.
*
* @return bool
*/
@@ -649,8 +648,8 @@ class ElggPlugin extends ElggObject {
// Note: this will not run re-run the init hooks!
if ($return) {
if ($this->canReadFile('activate.php')) {
- $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES
- | ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;
+ $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES |
+ ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;
$this->start($flags);
diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php
index 209242288..37eb4bf4d 100644
--- a/engine/classes/ElggPluginPackage.php
+++ b/engine/classes/ElggPluginPackage.php
@@ -294,6 +294,7 @@ class ElggPluginPackage {
return true;
}
+ $this->errorMsg = elgg_echo('unknown_error');
return false;
}
diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php
index b5f8fe163..416df885c 100644
--- a/engine/classes/ElggPriorityList.php
+++ b/engine/classes/ElggPriorityList.php
@@ -165,9 +165,9 @@ class ElggPriorityList
/**
* Move an existing element to a new priority.
*
- * @param mixed $element The element to move
- * @param int $new_priority The new priority for the element
- * @param bool $strict Whether to check the type of the element match
+ * @param mixed $element The element to move
+ * @param int $new_priority The new priority for the element
+ * @param bool $strict Whether to check the type of the element match
* @return bool
*/
public function move($element, $new_priority, $strict = false) {
@@ -354,7 +354,12 @@ class ElggPriorityList
return ($key !== NULL && $key !== FALSE);
}
- // Countable
+ /**
+ * Countable interface
+ *
+ * @see Countable::count()
+ * @return int
+ */
public function count() {
return count($this->elements);
}
diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php
index 1a34df195..dd996fe98 100644
--- a/engine/classes/ElggSite.php
+++ b/engine/classes/ElggSite.php
@@ -77,28 +77,24 @@ class ElggSite extends ElggEntity {
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // Is $guid is an ElggSite? Use a copy constructor
} else if ($guid instanceof ElggSite) {
+ // $guid is an ElggSite so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggSite constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggSite = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo remove and just use else clause
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggSite'));
-
- // See if this is a URL
} else if (strpos($guid, "http") !== false) {
+ // url so retrieve by url
$guid = get_site_by_url($guid);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -128,7 +124,7 @@ class ElggSite extends ElggEntity {
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php
index 17d849400..9c14fdfba 100644
--- a/engine/classes/ElggStaticVariableCache.php
+++ b/engine/classes/ElggStaticVariableCache.php
@@ -11,7 +11,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {
/**
* The cache.
*
- * @var unknown_type
+ * @var array
*/
private static $__cache;
@@ -22,7 +22,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {
* memory, optionally with a given namespace (to avoid overlap).
*
* @param string $namespace The namespace for this cache to write to.
- * @note namespaces of the same name are shared!
+ * @warning namespaces of the same name are shared!
*/
function __construct($namespace = 'default') {
$this->setNamespace($namespace);
@@ -80,7 +80,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {
}
/**
- * This was probably meant to delete everything?
+ * Clears the cache for a particular namespace
*
* @return void
*/
diff --git a/engine/classes/ElggTranslit.php b/engine/classes/ElggTranslit.php
index 676c59fc8..b4bf87797 100644
--- a/engine/classes/ElggTranslit.php
+++ b/engine/classes/ElggTranslit.php
@@ -20,11 +20,10 @@
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*
- * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
- * @author Jonathan H. Wage <jonwage@gmail.com>
- *
- * @author Steve Clay <steve@mrclay.org>
- * @package Elgg.Core
+ * @package Elgg.Core
+ * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ * @author Steve Clay <steve@mrclay.org>
*
* @access private Plugin authors should not use this directly
*/
@@ -32,8 +31,9 @@ class ElggTranslit {
/**
* Create a version of a string for embedding in a URL
- * @param string $string a UTF-8 string
- * @param string $separator
+ *
+ * @param string $string A UTF-8 string
+ * @param string $separator The character to separate words with
* @return string
*/
static public function urlize($string, $separator = '-') {
@@ -49,24 +49,29 @@ class ElggTranslit {
// Internationalization, AND 日本語!
$string = self::transliterateAscii($string);
- // more translation
+ // allow HTML tags in titles
+ $string = preg_replace('~<([a-zA-Z][^>]*)>~', ' $1 ', $string);
+
+ // more substitutions
+ // @todo put these somewhere else
$string = strtr($string, array(
- // Euro/GBP
- "\xE2\x82\xAC" /* € */ => 'E', "\xC2\xA3" /* £ */ => 'GBP',
+ // currency
+ "\xE2\x82\xAC" /* € */ => ' E ',
+ "\xC2\xA3" /* £ */ => ' GBP ',
));
// remove all ASCII except 0-9a-zA-Z, hyphen, underscore, and whitespace
// note: "x" modifier did not work with this pattern.
$string = preg_replace('~['
- . '\x00-\x08' # control chars
- . '\x0b\x0c' # vert tab, form feed
- . '\x0e-\x1f' # control chars
- . '\x21-\x2c' # ! ... ,
- . '\x2e\x2f' # . slash
- . '\x3a-\x40' # : ... @
- . '\x5b-\x5e' # [ ... ^
- . '\x60' # `
- . '\x7b-\x7f' # { ... DEL
+ . '\x00-\x08' // control chars
+ . '\x0b\x0c' // vert tab, form feed
+ . '\x0e-\x1f' // control chars
+ . '\x21-\x2c' // ! ... ,
+ . '\x2e\x2f' // . slash
+ . '\x3a-\x40' // : ... @
+ . '\x5b-\x5e' // [ ... ^
+ . '\x60' // `
+ . '\x7b-\x7f' // { ... DEL
. ']~', '', $string);
$string = strtr($string, '', '');
@@ -80,10 +85,10 @@ class ElggTranslit {
// note: we cannot use [^0-9a-zA-Z] because that matches multibyte chars.
// note: "x" modifier did not work with this pattern.
$pattern = '~['
- . '\x00-\x2f' # controls ... slash
- . '\x3a-\x40' # : ... @
- . '\x5b-\x60' # [ ... `
- . '\x7b-\x7f' # { ... DEL
+ . '\x00-\x2f' // controls ... slash
+ . '\x3a-\x40' // : ... @
+ . '\x5b-\x60' // [ ... `
+ . '\x7b-\x7f' // { ... DEL
. ']+~x';
// ['internationalization', 'and', '日本語']
@@ -98,6 +103,7 @@ class ElggTranslit {
/**
* Transliterate Western multibyte chars to ASCII
+ *
* @param string $utf8 a UTF-8 string
* @return string
*/
@@ -247,6 +253,7 @@ class ElggTranslit {
/**
* Tests that "normalizer_normalize" exists and works
+ *
* @return bool
*/
static public function hasNormalizerSupport() {
@@ -255,7 +262,7 @@ class ElggTranslit {
$form_c = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
$form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A)
$ret = (function_exists('normalizer_normalize')
- && $form_c === normalizer_normalize($form_d));
+ && $form_c === normalizer_normalize($form_d));
}
return $ret;
}
diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php
index 6c1cdc1de..6163f9b62 100644
--- a/engine/classes/ElggUser.php
+++ b/engine/classes/ElggUser.php
@@ -40,6 +40,9 @@ class ElggUser extends ElggEntity
$this->attributes['code'] = NULL;
$this->attributes['banned'] = "no";
$this->attributes['admin'] = 'no';
+ $this->attributes['prev_last_action'] = NULL;
+ $this->attributes['last_login'] = NULL;
+ $this->attributes['prev_last_login'] = NULL;
$this->attributes['tables_split'] = 2;
}
@@ -65,30 +68,26 @@ class ElggUser extends ElggEntity
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // See if this is a username
} else if (is_string($guid)) {
+ // $guid is a username
$user = get_user_by_username($guid);
if ($user) {
foreach ($user->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
}
-
- // Is $guid is an ElggUser? Use a copy constructor
} else if ($guid instanceof ElggUser) {
+ // $guid is an ElggUser so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggUser = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo why have a special case here
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser'));
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load entity
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -116,7 +115,7 @@ class ElggUser extends ElggEntity
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -133,9 +132,13 @@ class ElggUser extends ElggEntity
}
// Now save specific stuff
- return create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),
$this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'),
$this->get('code'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
}
/**
diff --git a/engine/classes/ElggVolatileMetadataCache.php b/engine/classes/ElggVolatileMetadataCache.php
index 8a33c198d..4acda7cee 100644
--- a/engine/classes/ElggVolatileMetadataCache.php
+++ b/engine/classes/ElggVolatileMetadataCache.php
@@ -33,9 +33,11 @@ class ElggVolatileMetadataCache {
protected $ignoreAccess = null;
/**
- * @param int $entity_guid
- *
- * @param array $values
+ * Cache metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param array $values The metadata values to cache
+ * @return void
*/
public function saveAll($entity_guid, array $values) {
if (!$this->getIgnoreAccess()) {
@@ -45,8 +47,9 @@ class ElggVolatileMetadataCache {
}
/**
- * @param int $entity_guid
- *
+ * Get the metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
* @return array
*/
public function loadAll($entity_guid) {
@@ -61,15 +64,17 @@ class ElggVolatileMetadataCache {
* Declare that there may be fetch-able metadata names in storage that this
* cache doesn't know about
*
- * @param int $entity_guid
+ * @param int $entity_guid The GUID of the entity
+ * @return void
*/
public function markOutOfSync($entity_guid) {
unset($this->isSynchronized[$entity_guid]);
}
/**
- * @param $entity_guid
- *
+ * Have all the metadata for this entity been cached?
+ *
+ * @param int $entity_guid The GUID of the entity
* @return bool
*/
public function isSynchronized($entity_guid) {
@@ -77,13 +82,15 @@ class ElggVolatileMetadataCache {
}
/**
- * @param int $entity_guid
- *
- * @param string $name
- *
- * @param array|int|string|null $value null means it is known that there is no
- * fetch-able metadata under this name
- * @param bool $allow_multiple
+ * Cache a piece of metadata
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @param array|int|string|null $value The metadata value. null means it is
+ * known that there is no fetch-able
+ * metadata under this name
+ * @param bool $allow_multiple Can the metadata be an array
+ * @return void
*/
public function save($entity_guid, $name, $value, $allow_multiple = false) {
if ($this->getIgnoreAccess()) {
@@ -115,10 +122,8 @@ class ElggVolatileMetadataCache {
* function's return value should be trusted (otherwise a null return value
* is ambiguous).
*
- * @param int $entity_guid
- *
- * @param string $name
- *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
* @return array|string|int|null null = value does not exist
*/
public function load($entity_guid, $name) {
@@ -133,9 +138,9 @@ class ElggVolatileMetadataCache {
* Forget about this metadata entry. We don't want to try to guess what the
* next fetch from storage will return
*
- * @param int $entity_guid
- *
- * @param string $name
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @return void
*/
public function markUnknown($entity_guid, $name) {
unset($this->values[$entity_guid][$name]);
@@ -145,10 +150,8 @@ class ElggVolatileMetadataCache {
/**
* If true, load() will return an accurate value for this name
*
- * @param int $entity_guid
- *
- * @param string $name
- *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
* @return bool
*/
public function isKnown($entity_guid, $name) {
@@ -163,10 +166,8 @@ class ElggVolatileMetadataCache {
/**
* Declare that metadata under this name is known to be not fetch-able from storage
*
- * @param int $entity_guid
- *
- * @param string $name
- *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
* @return array
*/
public function markEmpty($entity_guid, $name) {
@@ -176,7 +177,8 @@ class ElggVolatileMetadataCache {
/**
* Forget about all metadata for an entity
*
- * @param int $entity_guid
+ * @param int $entity_guid The GUID of the entity
+ * @return void
*/
public function clear($entity_guid) {
$this->values[$entity_guid] = array();
@@ -185,6 +187,8 @@ class ElggVolatileMetadataCache {
/**
* Clear entire cache and mark all entities as out of sync
+ *
+ * @return void
*/
public function flush() {
$this->values = array();
@@ -197,7 +201,8 @@ class ElggVolatileMetadataCache {
*
* This setting makes this component a little more loosely-coupled.
*
- * @param bool $ignore
+ * @param bool $ignore Whether to ignore access or not
+ * @return void
*/
public function setIgnoreAccess($ignore) {
$this->ignoreAccess = (bool) $ignore;
@@ -205,12 +210,16 @@ class ElggVolatileMetadataCache {
/**
* Tell the cache to call elgg_get_ignore_access() to determing access status.
+ *
+ * @return void
*/
public function unsetIgnoreAccess() {
$this->ignoreAccess = null;
}
/**
+ * Get the ignore access value
+ *
* @return bool
*/
protected function getIgnoreAccess() {
@@ -225,12 +234,10 @@ class ElggVolatileMetadataCache {
* Invalidate based on options passed to the global *_metadata functions
*
* @param string $action Action performed on metadata. "delete", "disable", or "enable"
- *
- * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
- *
- * "guid" if given, invalidation will be limited to this entity
- *
- * "metadata_name" if given, invalidation will be limited to metadata with this name
+ * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * "guid" if given, invalidation will be limited to this entity
+ * "metadata_name" if given, invalidation will be limited to metadata with this name
+ * @return void
*/
public function invalidateByOptions($action, array $options) {
// remove as little as possible, optimizing for common cases
@@ -254,7 +261,10 @@ class ElggVolatileMetadataCache {
}
/**
- * @param int|array $guids
+ * Populate the cache from a set of entities
+ *
+ * @param int|array $guids Array of or single GUIDs
+ * @return void
*/
public function populateFromEntities($guids) {
if (empty($guids)) {
@@ -318,9 +328,7 @@ class ElggVolatileMetadataCache {
* cache if RAM usage becomes an issue.
*
* @param array $guids GUIDs of entities to examine
- *
- * @param int $limit Limit in characters of all metadata (with ints casted to strings)
- *
+ * @param int $limit Limit in characters of all metadata (with ints casted to strings)
* @return array
*/
public function filterMetadataHeavyEntities(array $guids, $limit = 1024000) {
diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php
index c123e5032..66191bf47 100644
--- a/engine/classes/ElggWidget.php
+++ b/engine/classes/ElggWidget.php
@@ -146,10 +146,15 @@ class ElggWidget extends ElggObject {
}
}
+ $bottom_rank = count($widgets);
+ if ($column == $this->column) {
+ $bottom_rank--;
+ }
+
if ($rank == 0) {
// top of the column
$this->order = reset($widgets)->order - 10;
- } elseif ($rank == (count($widgets) - 1)) {
+ } elseif ($rank == $bottom_rank) {
// bottom of the column of active widgets
$this->order = end($widgets)->order + 10;
} else {
diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php
index 4e4b7e63c..cbd3fc5ce 100644
--- a/engine/classes/ElggXMLElement.php
+++ b/engine/classes/ElggXMLElement.php
@@ -20,7 +20,12 @@ class ElggXMLElement {
if ($xml instanceof SimpleXMLElement) {
$this->_element = $xml;
} else {
+ // do not load entities
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
$this->_element = new SimpleXMLElement($xml);
+
+ libxml_disable_entity_loader($disable_load_entities);
}
}
@@ -76,6 +81,12 @@ class ElggXMLElement {
return $result;
}
+ /**
+ * Override ->
+ *
+ * @param string $name Property name
+ * @return mixed
+ */
function __get($name) {
switch ($name) {
case 'name':
@@ -94,6 +105,12 @@ class ElggXMLElement {
return null;
}
+ /**
+ * Override isset
+ *
+ * @param string $name Property name
+ * @return boolean
+ */
function __isset($name) {
switch ($name) {
case 'name':
@@ -111,5 +128,4 @@ class ElggXMLElement {
}
return false;
}
-
-} \ No newline at end of file
+}
diff --git a/engine/handlers/cache_handler.php b/engine/handlers/cache_handler.php
index 9848d3531..36fc665bb 100644
--- a/engine/handlers/cache_handler.php
+++ b/engine/handlers/cache_handler.php
@@ -88,20 +88,18 @@ header("ETag: \"$etag\"");
$filename = $dataroot . 'views_simplecache/' . md5($viewtype . $view);
if (file_exists($filename)) {
- $contents = file_get_contents($filename);
+ readfile($filename);
} else {
// someone trying to access a non-cached file or a race condition with cache flushing
mysql_close($mysql_dblink);
require_once(dirname(dirname(__FILE__)) . "/start.php");
global $CONFIG;
- if (!isset($CONFIG->views->simplecache[$view])) {
+ if (!in_array($view, $CONFIG->views->simplecache)) {
header("HTTP/1.1 404 Not Found");
exit;
}
elgg_set_viewtype($viewtype);
- $contents = elgg_view($view);
+ echo elgg_view($view);
}
-
-echo $contents;
diff --git a/engine/lib/actions.php b/engine/lib/actions.php
index f78ca63df..8047914ac 100644
--- a/engine/lib/actions.php
+++ b/engine/lib/actions.php
@@ -74,8 +74,7 @@ function action($action, $forwarder = "") {
);
if (!in_array($action, $exceptions)) {
- // All actions require a token.
- action_gatekeeper();
+ action_gatekeeper($action);
}
$forwarder = str_replace(elgg_get_site_url(), "", $forwarder);
@@ -188,6 +187,26 @@ function elgg_unregister_action($action) {
}
/**
+ * Is the token timestamp within acceptable range?
+ *
+ * @param int $ts timestamp from the CSRF token
+ *
+ * @return bool
+ */
+function _elgg_validate_token_timestamp($ts) {
+ $action_token_timeout = elgg_get_config('action_token_timeout');
+ // default is 2 hours
+ $timeout = ($action_token_timeout !== null) ? $action_token_timeout : 2;
+
+ $hour = 60 * 60;
+ $timeout = $timeout * $hour;
+ $now = time();
+
+ // Validate time to ensure its not crazy
+ return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout));
+}
+
+/**
* Validate an action token.
*
* Calls to actions will automatically validate tokens. If tokens are not
@@ -205,8 +224,6 @@ function elgg_unregister_action($action) {
* @access private
*/
function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) {
- global $CONFIG;
-
if (!$token) {
$token = get_input('__elgg_token');
}
@@ -215,29 +232,18 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)
$ts = get_input('__elgg_ts');
}
- if (!isset($CONFIG->action_token_timeout)) {
- // default to 2 hours
- $timeout = 2;
- } else {
- $timeout = $CONFIG->action_token_timeout;
- }
-
$session_id = session_id();
if (($token) && ($ts) && ($session_id)) {
// generate token, check with input and forward if invalid
- $generated_token = generate_action_token($ts);
+ $required_token = generate_action_token($ts);
// Validate token
- if ($token == $generated_token) {
- $hour = 60 * 60;
- $timeout = $timeout * $hour;
- $now = time();
-
- // Validate time to ensure its not crazy
- if ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)) {
+ if ($token == $required_token) {
+
+ if (_elgg_validate_token_timestamp($ts)) {
// We have already got this far, so unless anything
- // else says something to the contry we assume we're ok
+ // else says something to the contrary we assume we're ok
$returnval = true;
$returnval = elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', array(
@@ -293,12 +299,33 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)
* This function verifies form input for security features (like a generated token),
* and forwards if they are invalid.
*
+ * @param string $action The action being performed
+ *
* @return mixed True if valid or redirects.
* @access private
*/
-function action_gatekeeper() {
- if (validate_action_token()) {
- return TRUE;
+function action_gatekeeper($action) {
+ if ($action === 'login') {
+ if (validate_action_token(false)) {
+ return true;
+ }
+
+ $token = get_input('__elgg_token');
+ $ts = (int)get_input('__elgg_ts');
+ if ($token && _elgg_validate_token_timestamp($ts)) {
+ // The tokens are present and the time looks valid: this is probably a mismatch due to the
+ // login form being on a different domain.
+ register_error(elgg_echo('actiongatekeeper:crosssitelogin'));
+
+
+ forward('login', 'csrf');
+ }
+
+ // let the validator send an appropriate msg
+ validate_action_token();
+
+ } elseif (validate_action_token()) {
+ return true;
}
forward(REFERER, 'csrf');
@@ -337,16 +364,19 @@ function generate_action_token($timestamp) {
}
/**
- * Initialise the site secret hash.
+ * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL).
*
* Used during installation and saves as a datalist.
*
+ * Note: Old secrets were hex encoded.
+ *
* @return mixed The site secret hash or false
* @access private
* @todo Move to better file.
*/
function init_site_secret() {
- $secret = md5(rand() . microtime());
+ $secret = 'z' . ElggCrypto::getRandomString(31);
+
if (datalist_set('__site_secret__', $secret)) {
return $secret;
}
@@ -373,6 +403,26 @@ function get_site_secret() {
}
/**
+ * Get the strength of the site secret
+ *
+ * @return string "strong", "moderate", or "weak"
+ * @access private
+ */
+function _elgg_get_site_secret_strength() {
+ $secret = get_site_secret();
+ if ($secret[0] !== 'z') {
+ $rand_max = getrandmax();
+ if ($rand_max < pow(2, 16)) {
+ return 'weak';
+ }
+ if ($rand_max < pow(2, 32)) {
+ return 'moderate';
+ }
+ }
+ return 'strong';
+}
+
+/**
* Check if an action is registered and its script exists.
*
* @param string $action Action name
diff --git a/engine/lib/admin.php b/engine/lib/admin.php
index ec19a5476..f36f29668 100644
--- a/engine/lib/admin.php
+++ b/engine/lib/admin.php
@@ -134,11 +134,11 @@ function elgg_delete_admin_notice($id) {
}
/**
- * List all admin messages.
+ * Get admin notices. An admin must be logged in since the notices are private.
*
* @param int $limit Limit
*
- * @return array List of admin notices
+ * @return array Array of admin notices
* @since 1.8.0
*/
function elgg_get_admin_notices($limit = 10) {
@@ -158,11 +158,13 @@ function elgg_get_admin_notices($limit = 10) {
* @since 1.8.0
*/
function elgg_admin_notice_exists($id) {
+ $old_ia = elgg_set_ignore_access(true);
$notice = elgg_get_entities_from_metadata(array(
'type' => 'object',
'subtype' => 'admin_notice',
'metadata_name_value_pair' => array('name' => 'admin_notice_id', 'value' => $id)
));
+ elgg_set_ignore_access($old_ia);
return ($notice) ? TRUE : FALSE;
}
@@ -234,6 +236,7 @@ function admin_init() {
elgg_register_action('admin/site/update_advanced', '', 'admin');
elgg_register_action('admin/site/flush_cache', '', 'admin');
elgg_register_action('admin/site/unlock_upgrade', '', 'admin');
+ elgg_register_action('admin/site/regenerate_secret', '', 'admin');
elgg_register_action('admin/menu/save', '', 'admin');
@@ -289,6 +292,7 @@ function admin_init() {
elgg_register_admin_menu_item('configure', 'settings', null, 100);
elgg_register_admin_menu_item('configure', 'basic', 'settings', 10);
elgg_register_admin_menu_item('configure', 'advanced', 'settings', 20);
+ elgg_register_admin_menu_item('configure', 'advanced/site_secret', 'settings', 25);
elgg_register_admin_menu_item('configure', 'menu_items', 'appearance', 30);
elgg_register_admin_menu_item('configure', 'profile_fields', 'appearance', 40);
// default widgets is added via an event handler elgg_default_widgets_init() in widgets.php
@@ -468,14 +472,18 @@ function admin_page_handler($page) {
$vars = array('page' => $page);
// special page for plugin settings since we create the form for them
- if ($page[0] == 'plugin_settings' && isset($page[1]) &&
- (elgg_view_exists("settings/{$page[1]}/edit") || elgg_view_exists("plugins/{$page[1]}/settings"))) {
+ if ($page[0] == 'plugin_settings') {
+ if (isset($page[1]) && (elgg_view_exists("settings/{$page[1]}/edit") ||
+ elgg_view_exists("plugins/{$page[1]}/settings"))) {
- $view = 'admin/plugin_settings';
- $plugin = elgg_get_plugin_from_id($page[1]);
- $vars['plugin'] = $plugin;
+ $view = 'admin/plugin_settings';
+ $plugin = elgg_get_plugin_from_id($page[1]);
+ $vars['plugin'] = $plugin;
- $title = elgg_echo("admin:{$page[0]}");
+ $title = elgg_echo("admin:{$page[0]}");
+ } else {
+ forward('', '404');
+ }
} else {
$view = 'admin/' . implode('/', $page);
$title = elgg_echo("admin:{$page[0]}");
diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php
index bd5ea1a1f..5e9b530de 100644
--- a/engine/lib/annotations.php
+++ b/engine/lib/annotations.php
@@ -224,7 +224,7 @@ function elgg_get_annotations(array $options = array()) {
* annotation_name(s), annotation_value(s), or guid(s) must be set.
*
* @param array $options An options array. {@See elgg_get_annotations()}
- * @return mixed Null if the metadata name is invalid. Bool on success or fail.
+ * @return bool|null true on success, false on failure, null if no annotations to delete.
* @since 1.8.0
*/
function elgg_delete_annotations(array $options) {
@@ -242,16 +242,20 @@ function elgg_delete_annotations(array $options) {
* @warning Unlike elgg_get_annotations() this will not accept an empty options array!
*
* @param array $options An options array. {@See elgg_get_annotations()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no annotations disabled.
* @since 1.8.0
*/
function elgg_disable_annotations(array $options) {
if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) {
return false;
}
+
+ // if we can see hidden (disabled) we need to use the offset
+ // otherwise we risk an infinite loop if there are more than 50
+ $inc_offset = access_get_show_hidden_status();
$options['metastring_type'] = 'annotations';
- return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false);
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);
}
/**
@@ -259,8 +263,11 @@ function elgg_disable_annotations(array $options) {
*
* @warning Unlike elgg_get_annotations() this will not accept an empty options array!
*
+ * @warning In order to enable annotations, you must first use
+ * {@link access_show_hidden_entities()}.
+ *
* @param array $options An options array. {@See elgg_get_annotations()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no metadata enabled.
* @since 1.8.0
*/
function elgg_enable_annotations(array $options) {
@@ -416,8 +423,8 @@ function elgg_list_entities_from_annotations($options = array()) {
function elgg_get_entities_from_annotation_calculation($options) {
$db_prefix = elgg_get_config('dbprefix');
$defaults = array(
- 'calculation' => 'sum',
- 'order_by' => 'annotation_calculation desc'
+ 'calculation' => 'sum',
+ 'order_by' => 'annotation_calculation desc'
);
$options = array_merge($defaults, $options);
@@ -454,6 +461,12 @@ function elgg_get_entities_from_annotation_calculation($options) {
* @return string
*/
function elgg_list_entities_from_annotation_calculation($options) {
+ $defaults = array(
+ 'calculation' => 'sum',
+ 'order_by' => 'annotation_calculation desc'
+ );
+ $options = array_merge($defaults, $options);
+
return elgg_list_entities($options, 'elgg_get_entities_from_annotation_calculation');
}
@@ -532,15 +545,16 @@ function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NU
return FALSE;
}
- $entity_guid = (int)$entity_guid;
- $annotation_type = sanitise_string($annotation_type);
+ $entity_guid = sanitize_int($entity_guid);
+ $owner_guid = sanitize_int($owner_guid);
+ $annotation_type = sanitize_string($annotation_type);
- $sql = "select a.id" .
- " FROM {$CONFIG->dbprefix}annotations a, {$CONFIG->dbprefix}metastrings m " .
- " WHERE a.owner_guid={$owner_guid} AND a.entity_guid={$entity_guid} " .
- " AND a.name_id=m.id AND m.string='{$annotation_type}'";
+ $sql = "SELECT a.id FROM {$CONFIG->dbprefix}annotations a" .
+ " JOIN {$CONFIG->dbprefix}metastrings m ON a.name_id = m.id" .
+ " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" .
+ " AND m.string = '$annotation_type'";
- if ($check_annotation = get_data_row($sql)) {
+ if (get_data_row($sql)) {
return TRUE;
}
diff --git a/engine/lib/cache.php b/engine/lib/cache.php
index 59359124e..3116c1a9b 100644
--- a/engine/lib/cache.php
+++ b/engine/lib/cache.php
@@ -208,6 +208,7 @@ function elgg_get_simplecache_url($type, $view) {
global $CONFIG;
$lastcache = (int)$CONFIG->lastcache;
$viewtype = elgg_get_viewtype();
+ elgg_register_simplecache_view("$type/$view");// see #5302
if (elgg_is_simplecache_enabled()) {
$url = elgg_get_site_url() . "cache/$type/$viewtype/$view.$lastcache.$type";
} else {
diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php
index a0f297f0c..55e5bbd36 100644
--- a/engine/lib/configuration.php
+++ b/engine/lib/configuration.php
@@ -486,9 +486,9 @@ function get_config($name, $site_guid = 0) {
// @todo these haven't really been implemented in Elgg 1.8. Complete in 1.9.
// show dep message
if ($new_name) {
- // $msg = "Config value $name has been renamed as $new_name";
+ // $msg = "Config value $name has been renamed as $new_name";
$name = $new_name;
- // elgg_deprecated_notice($msg, $dep_version);
+ // elgg_deprecated_notice($msg, $dep_version);
}
// decide from where to return the value
diff --git a/engine/lib/database.php b/engine/lib/database.php
index 2b348366d..a7949788d 100644
--- a/engine/lib/database.php
+++ b/engine/lib/database.php
@@ -12,17 +12,19 @@
/**
* Query cache for all queries.
*
- * Each query and its results are stored in this array as:
+ * Each query and its results are stored in this cache as:
* <code>
- * $DB_QUERY_CACHE[$query] => array(result1, result2, ... resultN)
+ * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN)
* </code>
+ * @see elgg_query_runner() for details on the hash.
*
- * @warning be array this var may be an array or ElggStaticVariableCache depending on when called :(
+ * @warning Elgg used to set this as an empty array to turn off the cache
*
- * @global ElggStaticVariableCache|array $DB_QUERY_CACHE
+ * @global ElggLRUCache|null $DB_QUERY_CACHE
+ * @access private
*/
global $DB_QUERY_CACHE;
-$DB_QUERY_CACHE = array();
+$DB_QUERY_CACHE = null;
/**
* Queries to be executed upon shutdown.
@@ -40,6 +42,7 @@ $DB_QUERY_CACHE = array();
* </code>
*
* @global array $DB_DELAYED_QUERIES
+ * @access private
*/
global $DB_DELAYED_QUERIES;
$DB_DELAYED_QUERIES = array();
@@ -51,6 +54,7 @@ $DB_DELAYED_QUERIES = array();
* $dblink as $dblink[$name] => resource. Use get_db_link($name) to retrieve it.
*
* @global resource[] $dblink
+ * @access private
*/
global $dblink;
$dblink = array();
@@ -61,6 +65,7 @@ $dblink = array();
* Each call to the database increments this counter.
*
* @global integer $dbcalls
+ * @access private
*/
global $dbcalls;
$dbcalls = 0;
@@ -123,9 +128,8 @@ function establish_db_link($dblinkname = "readwrite") {
// Set up cache if global not initialized and query cache not turned off
if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) {
- // @todo everywhere else this is assigned to array(), making it dangerous to call
- // object methods on this. We should consider making this an plain array
- $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache');
+ // @todo if we keep this cache in 1.9, expose the size as a config parameter
+ $DB_QUERY_CACHE = new ElggLRUCache(200);
}
}
@@ -395,16 +399,14 @@ function elgg_query_runner($query, $callback = null, $single = false) {
// Since we want to cache results of running the callback, we need to
// need to namespace the query with the callback and single result request.
- // http://trac.elgg.org/ticket/4049
+ // https://github.com/elgg/elgg/issues/4049
$hash = (string)$callback . (int)$single . $query;
// Is cached?
if ($DB_QUERY_CACHE) {
- $cached_query = $DB_QUERY_CACHE[$hash];
-
- if ($cached_query !== FALSE) {
+ if (isset($DB_QUERY_CACHE[$hash])) {
elgg_log("DB query $query results returned from cache (hash: $hash)", 'NOTICE');
- return $cached_query;
+ return $DB_QUERY_CACHE[$hash];
}
}
@@ -456,19 +458,12 @@ function elgg_query_runner($query, $callback = null, $single = false) {
* @access private
*/
function insert_data($query) {
- global $DB_QUERY_CACHE;
elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- /* @var ElggStaticVariableCache $DB_QUERY_CACHE */
- $DB_QUERY_CACHE->clear();
- }
-
- elgg_log("Query cache invalidated", 'NOTICE');
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return mysql_insert_id($dblink);
@@ -478,7 +473,7 @@ function insert_data($query) {
}
/**
- * Update a row in the database.
+ * Update the database.
*
* @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
*
@@ -488,18 +483,12 @@ function insert_data($query) {
* @access private
*/
function update_data($query) {
- global $DB_QUERY_CACHE;
elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- /* @var ElggStaticVariableCache $DB_QUERY_CACHE */
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated", 'NOTICE');
- }
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return TRUE;
@@ -509,7 +498,7 @@ function update_data($query) {
}
/**
- * Remove a row from the database.
+ * Remove data from the database.
*
* @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
*
@@ -519,18 +508,12 @@ function update_data($query) {
* @access private
*/
function delete_data($query) {
- global $DB_QUERY_CACHE;
elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- /* @var ElggStaticVariableCache $DB_QUERY_CACHE */
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated", 'NOTICE');
- }
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return mysql_affected_rows($dblink);
@@ -539,6 +522,22 @@ function delete_data($query) {
return FALSE;
}
+/**
+ * Invalidate the query cache
+ *
+ * @access private
+ */
+function _elgg_invalidate_query_cache() {
+ global $DB_QUERY_CACHE;
+ if ($DB_QUERY_CACHE instanceof ElggLRUCache) {
+ $DB_QUERY_CACHE->clear();
+ elgg_log("Query cache invalidated", 'NOTICE');
+ } elseif ($DB_QUERY_CACHE) {
+ // In case someone sets the cache to an array and primes it with data
+ $DB_QUERY_CACHE = array();
+ elgg_log("Query cache invalidated", 'NOTICE');
+ }
+}
/**
* Return tables matching the database prefix {@link $CONFIG->dbprefix}% in the currently
@@ -669,7 +668,7 @@ function run_sql_script($scriptlocation) {
/**
* Format a query string for logging
- *
+ *
* @param string $query Query string
* @return string
* @access private
diff --git a/engine/lib/deprecated-1.7.php b/engine/lib/deprecated-1.7.php
index 519eea89d..ee95b5611 100644
--- a/engine/lib/deprecated-1.7.php
+++ b/engine/lib/deprecated-1.7.php
@@ -1137,6 +1137,7 @@ function make_register_object($register_name, $register_value, $children_array =
* @param int $guid GUID
*
* @return 1
+ * @deprecated 1.7
*/
function delete_object_entity($guid) {
system_message(elgg_echo('deprecatedfunction', array('delete_user_entity')));
@@ -1154,6 +1155,7 @@ function delete_object_entity($guid) {
* @param int $guid User GUID
*
* @return 1
+ * @deprecated 1.7
*/
function delete_user_entity($guid) {
system_message(elgg_echo('deprecatedfunction', array('delete_user_entity')));
diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php
index 2b4ffcc4f..91068d047 100644
--- a/engine/lib/deprecated-1.8.php
+++ b/engine/lib/deprecated-1.8.php
@@ -3414,6 +3414,7 @@ function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {
* @param unknown_type $timeupper
* @param unknown_type $calculation
* @internal Don't use this at all.
+ * @deprecated 1.8 Use elgg_get_annotations()
*/
function elgg_deprecated_annotation_calculation($entity_guid = 0, $entity_type = "", $entity_subtype = "",
$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0,
@@ -4667,6 +4668,7 @@ function display_widget(ElggObject $widget) {
*
* @param ElggEntity $entity
* @return int Number of comments
+ * @deprecated 1.8 Use ElggEntity->countComments()
*/
function elgg_count_comments($entity) {
elgg_deprecated_notice('elgg_count_comments() is deprecated by ElggEntity->countComments()', 1.8);
@@ -4772,3 +4774,47 @@ function default_page_handler($page, $handler) {
return FALSE;
}
+
+/**
+ * Invalidate this class's entry in the cache.
+ *
+ * @param int $guid The entity guid
+ *
+ * @return void
+ * @access private
+ * @deprecated 1.8
+ */
+function invalidate_cache_for_entity($guid) {
+ elgg_deprecated_notice('invalidate_cache_for_entity() is a private function and should not be used.', 1.8);
+ _elgg_invalidate_cache_for_entity($guid);
+}
+
+/**
+ * Cache an entity.
+ *
+ * Stores an entity in $ENTITY_CACHE;
+ *
+ * @param ElggEntity $entity Entity to cache
+ *
+ * @return void
+ * @access private
+ * @deprecated 1.8
+ */
+function cache_entity(ElggEntity $entity) {
+ elgg_deprecated_notice('cache_entity() is a private function and should not be used.', 1.8);
+ _elgg_cache_entity($entity);
+}
+
+/**
+ * Retrieve a entity from the cache.
+ *
+ * @param int $guid The guid
+ *
+ * @return ElggEntity|bool false if entity not cached, or not fully loaded
+ * @access private
+ * @deprecated 1.8
+ */
+function retrieve_cached_entity($guid) {
+ elgg_deprecated_notice('retrieve_cached_entity() is a private function and should not be used.', 1.8);
+ return _elgg_retrieve_cached_entity($guid);
+}
diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php
index 74b70f9fb..34111c69d 100644
--- a/engine/lib/elgglib.php
+++ b/engine/lib/elgglib.php
@@ -93,10 +93,17 @@ function elgg_register_library($name, $location) {
* @return void
* @throws InvalidParameterException
* @since 1.8.0
+ * @todo return boolean in 1.9 to indicate whether the library has been loaded
*/
function elgg_load_library($name) {
global $CONFIG;
+ static $loaded_libraries = array();
+
+ if (in_array($name, $loaded_libraries)) {
+ return;
+ }
+
if (!isset($CONFIG->libraries)) {
$CONFIG->libraries = array();
}
@@ -113,6 +120,8 @@ function elgg_load_library($name) {
);
throw new InvalidParameterException($error);
}
+
+ $loaded_libraries[] = $name;
}
/**
@@ -128,7 +137,7 @@ function elgg_load_library($name) {
* @throws SecurityException
*/
function forward($location = "", $reason = 'system') {
- if (!headers_sent()) {
+ if (!headers_sent($file, $line)) {
if ($location === REFERER) {
$location = $_SERVER['HTTP_REFERER'];
}
@@ -147,7 +156,7 @@ function forward($location = "", $reason = 'system') {
exit;
}
} else {
- throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect'));
+ throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect', array($file, $line)));
}
}
@@ -737,7 +746,7 @@ function elgg_unregister_event_handler($event, $object_type, $callback) {
* @tip When referring to events, the preferred syntax is "event, type".
*
* @internal Only rarely should events be changed, added, or removed in core.
- * When making changes to events, be sure to first create a ticket in trac.
+ * When making changes to events, be sure to first create a ticket on Github.
*
* @internal @tip Think of $object_type as the primary namespace element, and
* $event as the secondary namespace.
@@ -1185,6 +1194,11 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') {
$to_screen = FALSE;
}
+ // Do not want to write to JS or CSS pages
+ if (elgg_in_context('js') || elgg_in_context('css')) {
+ $to_screen = FALSE;
+ }
+
if ($to_screen == TRUE) {
echo '<pre>';
print_r($value);
@@ -1336,7 +1350,7 @@ function full_url() {
"" : (":" . $_SERVER["SERVER_PORT"]);
// This is here to prevent XSS in poorly written browsers used by 80% of the population.
- // {@trac [5813]}
+ // https://github.com/Elgg/Elgg/commit/0c947e80f512cb0a482b1864fd0a6965c8a0cd4a
$quotes = array('\'', '"');
$encoded = array('%27', '%22');
@@ -1383,8 +1397,8 @@ function elgg_http_build_url(array $parts, $html_encode = TRUE) {
* add tokens to the action. The form view automatically handles
* tokens.
*
- * @param string $url Full action URL
- * @param bool $html_encode HTML encode the url? (default: false)
+ * @param string $url Full action URL
+ * @param bool $html_encode HTML encode the url? (default: false)
*
* @return string URL with action tokens
* @since 1.7.0
@@ -1446,7 +1460,7 @@ function elgg_http_remove_url_query_element($url, $element) {
* Adds an element or elements to a URL's query string.
*
* @param string $url The URL
- * @param array $elements Key/value pairs to add to the URL
+ * @param array $elements Key/value pairs to add to the URL
*
* @return string The new URL with the query strings added
* @since 1.7.0
@@ -2233,6 +2247,9 @@ function elgg_api_test($hook, $type, $value, $params) {
/**#@+
* Controls access levels on ElggEntity entities, metadata, and annotations.
*
+ * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not
+ * use it when saving an entity.
+ *
* @var int
*/
define('ACCESS_DEFAULT', -1);
diff --git a/engine/lib/entities.php b/engine/lib/entities.php
index 156eec040..4fcf1c657 100644
--- a/engine/lib/entities.php
+++ b/engine/lib/entities.php
@@ -17,6 +17,15 @@ global $ENTITY_CACHE;
$ENTITY_CACHE = array();
/**
+ * GUIDs of entities banned from the entity cache (during this request)
+ *
+ * @global array $ENTITY_CACHE_DISABLED_GUIDS
+ * @access private
+ */
+global $ENTITY_CACHE_DISABLED_GUIDS;
+$ENTITY_CACHE_DISABLED_GUIDS = array();
+
+/**
* Cache subtypes and related class names.
*
* @global array|null $SUBTYPE_CACHE array once populated from DB, initially null
@@ -26,14 +35,42 @@ global $SUBTYPE_CACHE;
$SUBTYPE_CACHE = null;
/**
+ * Remove this entity from the entity cache and make sure it is not re-added
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ * @todo this is a workaround until #5604 can be implemented
+ */
+function _elgg_disable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
+
+ _elgg_invalidate_cache_for_entity($guid);
+ $ENTITY_CACHE_DISABLED_GUIDS[$guid] = true;
+}
+
+/**
+ * Allow this entity to be stored in the entity cache
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ */
+function _elgg_enable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
+
+ unset($ENTITY_CACHE_DISABLED_GUIDS[$guid]);
+}
+
+/**
* Invalidate this class's entry in the cache.
*
* @param int $guid The entity guid
*
- * @return null
+ * @return void
* @access private
*/
-function invalidate_cache_for_entity($guid) {
+function _elgg_invalidate_cache_for_entity($guid) {
global $ENTITY_CACHE;
$guid = (int)$guid;
@@ -50,14 +87,14 @@ function invalidate_cache_for_entity($guid) {
*
* @param ElggEntity $entity Entity to cache
*
- * @return null
- * @see retrieve_cached_entity()
- * @see invalidate_cache_for_entity()
+ * @return void
+ * @see _elgg_retrieve_cached_entity()
+ * @see _elgg_invalidate_cache_for_entity()
* @access private
- * TODO(evan): Use an ElggCache object
+ * @todo Use an ElggCache object
*/
-function cache_entity(ElggEntity $entity) {
- global $ENTITY_CACHE;
+function _elgg_cache_entity(ElggEntity $entity) {
+ global $ENTITY_CACHE, $ENTITY_CACHE_DISABLED_GUIDS;
// Don't cache non-plugin entities while access control is off, otherwise they could be
// exposed to users who shouldn't see them when control is re-enabled.
@@ -65,8 +102,13 @@ function cache_entity(ElggEntity $entity) {
return;
}
+ $guid = $entity->getGUID();
+ if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) {
+ return;
+ }
+
// Don't store too many or we'll have memory problems
- // TODO(evan): Pick a less arbitrary limit
+ // @todo Pick a less arbitrary limit
if (count($ENTITY_CACHE) > 256) {
$random_guid = array_rand($ENTITY_CACHE);
@@ -79,7 +121,7 @@ function cache_entity(ElggEntity $entity) {
elgg_get_metadata_cache()->clear($random_guid);
}
- $ENTITY_CACHE[$entity->guid] = $entity;
+ $ENTITY_CACHE[$guid] = $entity;
}
/**
@@ -88,11 +130,11 @@ function cache_entity(ElggEntity $entity) {
* @param int $guid The guid
*
* @return ElggEntity|bool false if entity not cached, or not fully loaded
- * @see cache_entity()
- * @see invalidate_cache_for_entity()
+ * @see _elgg_cache_entity()
+ * @see _elgg_invalidate_cache_for_entity()
* @access private
*/
-function retrieve_cached_entity($guid) {
+function _elgg_retrieve_cached_entity($guid) {
global $ENTITY_CACHE;
if (isset($ENTITY_CACHE[$guid])) {
@@ -105,31 +147,6 @@ function retrieve_cached_entity($guid) {
}
/**
- * As retrieve_cached_entity, but returns the result as a stdClass
- * (compatible with load functions that expect a database row.)
- *
- * @param int $guid The guid
- *
- * @return mixed
- * @todo unused
- * @access private
- */
-function retrieve_cached_entity_row($guid) {
- $obj = retrieve_cached_entity($guid);
- if ($obj) {
- $tmp = new stdClass;
-
- foreach ($obj as $k => $v) {
- $tmp->$k = $v;
- }
-
- return $tmp;
- }
-
- return false;
-}
-
-/**
* Return the id for a given subtype.
*
* ElggEntity objects have a type and a subtype. Subtypes
@@ -432,7 +449,7 @@ function update_subtype($type, $subtype, $class = '') {
* @param int $time_created The time creation timestamp
*
* @return bool
- * @link http://docs.elgg.org/DataModel/Entities
+ * @throws InvalidParameterException
* @access private
*/
function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $time_created = null) {
@@ -455,6 +472,10 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $
$time_created = (int) $time_created;
}
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
+
if ($entity && $entity->canEdit()) {
if (elgg_trigger_event('update', $entity->type, $entity)) {
$ret = update_data("UPDATE {$CONFIG->dbprefix}entities
@@ -581,7 +602,6 @@ $container_guid = 0) {
$type = sanitise_string($type);
$subtype_id = add_subtype($type, $subtype);
$owner_guid = (int)$owner_guid;
- $access_id = (int)$access_id;
$time = time();
if ($site_guid == 0) {
$site_guid = $CONFIG->site_guid;
@@ -590,6 +610,10 @@ $container_guid = 0) {
if ($container_guid == 0) {
$container_guid = $owner_guid;
}
+ $access_id = (int)$access_id;
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
$user_guid = elgg_get_logged_in_user_guid();
if (!can_write_to_container($user_guid, $owner_guid, $type, $subtype)) {
@@ -737,7 +761,7 @@ function get_entity($guid) {
// @todo We need a single Memcache instance with a shared pool of namespace wrappers. This function would pull an instance from the pool.
static $shared_cache;
- // We could also use: if (!(int) $guid) { return FALSE },
+ // We could also use: if (!(int) $guid) { return FALSE },
// but that evaluates to a false positive for $guid = TRUE.
// This is a bit slower, but more thorough.
if (!is_numeric($guid) || $guid === 0 || $guid === '0') {
@@ -745,7 +769,7 @@ function get_entity($guid) {
}
// Check local cache first
- $new_entity = retrieve_cached_entity($guid);
+ $new_entity = _elgg_retrieve_cached_entity($guid);
if ($new_entity) {
return $new_entity;
}
@@ -767,7 +791,7 @@ function get_entity($guid) {
if ($shared_cache) {
$cached_entity = $shared_cache->load($guid);
- // @todo store ACLs in memcache http://trac.elgg.org/ticket/3018#comment:3
+ // @todo store ACLs in memcache https://github.com/elgg/elgg/issues/3018#issuecomment-13662617
if ($cached_entity) {
// @todo use ACL and cached entity access_id to determine if user can see it
return $cached_entity;
@@ -782,7 +806,7 @@ function get_entity($guid) {
}
if ($new_entity) {
- cache_entity($new_entity);
+ _elgg_cache_entity($new_entity);
}
return $new_entity;
}
@@ -909,6 +933,8 @@ function elgg_get_entities(array $options = array()) {
'joins' => array(),
'callback' => 'entity_row_to_elggstar',
+
+ '__ElggBatch' => null,
);
$options = array_merge($defaults, $options);
@@ -1026,7 +1052,7 @@ function elgg_get_entities(array $options = array()) {
}
if ($options['callback'] === 'entity_row_to_elggstar') {
- $dt = _elgg_fetch_entities_from_sql($query);
+ $dt = _elgg_fetch_entities_from_sql($query, $options['__ElggBatch']);
} else {
$dt = get_data($query, $options['callback']);
}
@@ -1037,7 +1063,7 @@ function elgg_get_entities(array $options = array()) {
foreach ($dt as $item) {
// A custom callback could result in items that aren't ElggEntity's, so check for them
if ($item instanceof ElggEntity) {
- cache_entity($item);
+ _elgg_cache_entity($item);
// plugins usually have only settings
if (!$item instanceof ElggPlugin) {
$guids[] = $item->guid;
@@ -1061,13 +1087,14 @@ function elgg_get_entities(array $options = array()) {
/**
* Return entities from an SQL query generated by elgg_get_entities.
*
- * @param string $sql
+ * @param string $sql
+ * @param ElggBatch $batch
* @return ElggEntity[]
*
* @access private
* @throws LogicException
*/
-function _elgg_fetch_entities_from_sql($sql) {
+function _elgg_fetch_entities_from_sql($sql, ElggBatch $batch = null) {
static $plugin_subtype;
if (null === $plugin_subtype) {
$plugin_subtype = get_subtype_id('object', 'plugin');
@@ -1102,7 +1129,7 @@ function _elgg_fetch_entities_from_sql($sql) {
if (empty($row->guid) || empty($row->type)) {
throw new LogicException('Entity row missing guid or type');
}
- if ($entity = retrieve_cached_entity($row->guid)) {
+ if ($entity = _elgg_retrieve_cached_entity($row->guid)) {
$rows[$i] = $entity;
continue;
}
@@ -1144,6 +1171,11 @@ function _elgg_fetch_entities_from_sql($sql) {
} catch (IncompleteEntityException $e) {
// don't let incomplete entities throw fatal errors
unset($rows[$i]);
+
+ // report incompletes to the batch process that spawned this query
+ if ($batch) {
+ $batch->reportIncompleteEntity($row);
+ }
}
}
}
@@ -1441,8 +1473,10 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti
global $autofeed;
$autofeed = true;
+ $offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
+
$defaults = array(
- 'offset' => (int) max(get_input('offset', 0), 0),
+ 'offset' => (int) max(get_input($offset_key, 0), 0),
'limit' => (int) max(get_input('limit', 10), 0),
'full_view' => TRUE,
'list_type_toggle' => FALSE,
@@ -1628,7 +1662,7 @@ function disable_entity($guid, $reason = "", $recursive = true) {
$entity->disableMetadata();
$entity->disableAnnotations();
- invalidate_cache_for_entity($guid);
+ _elgg_invalidate_cache_for_entity($guid);
$res = update_data("UPDATE {$CONFIG->dbprefix}entities
SET enabled = 'no'
@@ -1644,8 +1678,8 @@ function disable_entity($guid, $reason = "", $recursive = true) {
/**
* Enable an entity.
*
- * @warning In order to enable an entity using ElggEntity::enable(),
- * you must first use {@link access_show_hidden_entities()}.
+ * @warning In order to enable an entity, you must first use
+ * {@link access_show_hidden_entities()}.
*
* @param int $guid GUID of entity to enable
* @param bool $recursive Recursively enable all entities disabled with the entity?
@@ -1726,7 +1760,7 @@ function delete_entity($guid, $recursive = true) {
// delete cache
if (isset($ENTITY_CACHE[$guid])) {
- invalidate_cache_for_entity($guid);
+ _elgg_invalidate_cache_for_entity($guid);
}
// If memcache is available then delete this entry from the cache
@@ -1773,6 +1807,10 @@ function delete_entity($guid, $recursive = true) {
elgg_set_ignore_access($ia);
}
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
// Now delete the entity itself
$entity->deleteMetadata();
$entity->deleteOwnedMetadata();
@@ -1780,6 +1818,9 @@ function delete_entity($guid, $recursive = true) {
$entity->deleteOwnedAnnotations();
$entity->deleteRelationships();
+ access_show_hidden_entities($entity_disable_override);
+ elgg_set_ignore_access($ia);
+
elgg_delete_river(array('subject_guid' => $guid));
elgg_delete_river(array('object_guid' => $guid));
remove_all_private_settings($guid);
@@ -2087,7 +2128,7 @@ function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null
$return = null;
- if ($metadata->owner_guid == 0) {
+ if ($metadata && ($metadata->owner_guid == 0)) {
$return = true;
}
if (is_null($return)) {
@@ -2488,11 +2529,18 @@ function update_entity_last_action($guid, $posted = NULL) {
function entities_gc() {
global $CONFIG;
- $tables = array ('sites_entity', 'objects_entity', 'groups_entity', 'users_entity');
+ $tables = array(
+ 'site' => 'sites_entity',
+ 'object' => 'objects_entity',
+ 'group' => 'groups_entity',
+ 'user' => 'users_entity'
+ );
- foreach ($tables as $table) {
- delete_data("DELETE from {$CONFIG->dbprefix}{$table}
- where guid NOT IN (SELECT guid from {$CONFIG->dbprefix}entities)");
+ foreach ($tables as $type => $table) {
+ delete_data("DELETE FROM {$CONFIG->dbprefix}{$table}
+ WHERE guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}entities)");
+ delete_data("DELETE FROM {$CONFIG->dbprefix}entities
+ WHERE type = '$type' AND guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}{$table})");
}
}
diff --git a/engine/lib/extender.php b/engine/lib/extender.php
index 8756e051b..8323bd3ce 100644
--- a/engine/lib/extender.php
+++ b/engine/lib/extender.php
@@ -126,14 +126,20 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params)
* @return bool
*/
function can_edit_extender($extender_id, $type, $user_guid = 0) {
- if (!elgg_is_logged_in()) {
- return false;
+ // @todo Since Elgg 1.0, Elgg has returned false from can_edit_extender()
+ // if no user was logged in. This breaks the access override. This is a
+ // temporary work around. This function needs to be rewritten in Elgg 1.9
+ if (!elgg_check_access_overrides($user_guid)) {
+ if (!elgg_is_logged_in()) {
+ return false;
+ }
}
$user_guid = (int)$user_guid;
- $user = get_entity($user_guid);
+ $user = get_user($user_guid);
if (!$user) {
$user = elgg_get_logged_in_user_entity();
+ $user_guid = elgg_get_logged_in_user_guid();
}
$functionname = "elgg_get_{$type}_from_id";
@@ -149,16 +155,16 @@ function can_edit_extender($extender_id, $type, $user_guid = 0) {
/* @var ElggExtender $extender */
// If the owner is the specified user, great! They can edit.
- if ($extender->getOwnerGUID() == $user->getGUID()) {
+ if ($extender->getOwnerGUID() == $user_guid) {
return true;
}
// If the user can edit the entity this is attached to, great! They can edit.
- if (can_edit_entity($extender->entity_guid, $user->getGUID())) {
+ if (can_edit_entity($extender->entity_guid, $user_guid)) {
return true;
}
- // Trigger plugin hooks
+ // Trigger plugin hook - note that $user may be null
$params = array('entity' => $extender->getEntity(), 'user' => $user);
return elgg_trigger_plugin_hook('permissions_check', $type, $params, false);
}
diff --git a/engine/lib/group.php b/engine/lib/group.php
index 624029d98..6ded8a825 100644
--- a/engine/lib/group.php
+++ b/engine/lib/group.php
@@ -240,9 +240,11 @@ function leave_group($group_guid, $user_guid) {
*/
function get_users_membership($user_guid) {
$options = array(
+ 'type' => 'group',
'relationship' => 'member',
'relationship_guid' => $user_guid,
- 'inverse_relationship' => FALSE
+ 'inverse_relationship' => false,
+ 'limit' => false,
);
return elgg_get_entities_from_relationship($options);
}
diff --git a/engine/lib/input.php b/engine/lib/input.php
index 2d9bae4dd..80b0b8766 100644
--- a/engine/lib/input.php
+++ b/engine/lib/input.php
@@ -60,8 +60,8 @@ function get_input($variable, $default = NULL, $filter_result = TRUE) {
*
* Note: this function does not handle nested arrays (ex: form input of param[m][n])
*
- * @param string $variable The name of the variable
- * @param string $value The value of the variable
+ * @param string $variable The name of the variable
+ * @param string|string[] $value The value of the variable
*
* @return void
*/
diff --git a/engine/lib/languages.php b/engine/lib/languages.php
index 17db14d98..61ba91ddb 100644
--- a/engine/lib/languages.php
+++ b/engine/lib/languages.php
@@ -139,6 +139,9 @@ function get_language() {
return false;
}
+/**
+ * @access private
+ */
function _elgg_load_translations() {
global $CONFIG;
diff --git a/engine/lib/location.php b/engine/lib/location.php
index b319bb3bb..1534c7d7b 100644
--- a/engine/lib/location.php
+++ b/engine/lib/location.php
@@ -139,7 +139,7 @@ function elgg_get_entities_from_location(array $options = array()) {
/**
* Returns a viewable list of entities from location
*
- * @param array $options
+ * @param array $options Options array
*
* @see elgg_list_entities()
* @see elgg_get_entities_from_location()
diff --git a/engine/lib/memcache.php b/engine/lib/memcache.php
index f79fba4a9..79b87e850 100644
--- a/engine/lib/memcache.php
+++ b/engine/lib/memcache.php
@@ -35,3 +35,23 @@ function is_memcache_available() {
return $memcache_available;
}
+
+/**
+ * Invalidate an entity in memcache
+ *
+ * @param int $entity_guid The GUID of the entity to invalidate
+ *
+ * @return void
+ * @access private
+ */
+function _elgg_invalidate_memcache_for_entity($entity_guid) {
+ static $newentity_cache;
+
+ if ((!$newentity_cache) && (is_memcache_available())) {
+ $newentity_cache = new ElggMemcache('new_entity_cache');
+ }
+
+ if ($newentity_cache) {
+ $newentity_cache->delete($entity_guid);
+ }
+} \ No newline at end of file
diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php
index 305e9918b..fdb1b85f6 100644
--- a/engine/lib/metadata.php
+++ b/engine/lib/metadata.php
@@ -191,19 +191,19 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i
}
// Add the metastring
- $value = add_metastring($value);
- if (!$value) {
+ $value_id = add_metastring($value);
+ if (!$value_id) {
return false;
}
- $name = add_metastring($name);
- if (!$name) {
+ $name_id = add_metastring($name);
+ if (!$name_id) {
return false;
}
// If ok then add it
$query = "UPDATE {$CONFIG->dbprefix}metadata"
- . " set name_id='$name', value_id='$value', value_type='$value_type', access_id=$access_id,"
+ . " set name_id='$name_id', value_id='$value_id', value_type='$value_type', access_id=$access_id,"
. " owner_guid=$owner_guid where id=$id";
$result = update_data($query);
@@ -300,10 +300,8 @@ function elgg_get_metadata(array $options = array()) {
* This requires at least one constraint: metadata_owner_guid(s),
* metadata_name(s), metadata_value(s), or guid(s) must be set.
*
- * @warning This returns null on no ops.
- *
* @param array $options An options array. {@see elgg_get_metadata()}
- * @return mixed Null if the metadata name is invalid. Bool on success or fail.
+ * @return bool|null true on success, false on failure, null if no metadata to delete.
* @since 1.8.0
*/
function elgg_delete_metadata(array $options) {
@@ -325,10 +323,8 @@ function elgg_delete_metadata(array $options) {
*
* @warning Unlike elgg_get_metadata() this will not accept an empty options array!
*
- * @warning This returns null on no ops.
- *
* @param array $options An options array. {@See elgg_get_metadata()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no metadata disabled.
* @since 1.8.0
*/
function elgg_disable_metadata(array $options) {
@@ -337,9 +333,13 @@ function elgg_disable_metadata(array $options) {
}
elgg_get_metadata_cache()->invalidateByOptions('disable', $options);
+
+ // if we can see hidden (disabled) we need to use the offset
+ // otherwise we risk an infinite loop if there are more than 50
+ $inc_offset = access_get_show_hidden_status();
$options['metastring_type'] = 'metadata';
- return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false);
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);
}
/**
@@ -347,10 +347,11 @@ function elgg_disable_metadata(array $options) {
*
* @warning Unlike elgg_get_metadata() this will not accept an empty options array!
*
- * @warning This returns null on no ops.
+ * @warning In order to enable metadata, you must first use
+ * {@link access_show_hidden_entities()}.
*
* @param array $options An options array. {@See elgg_get_metadata()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no metadata enabled.
* @since 1.8.0
*/
function elgg_enable_metadata(array $options) {
@@ -405,9 +406,11 @@ function elgg_enable_metadata(array $options) {
* 'operand' => '=',
* 'case_sensitive' => TRUE
* )
- * Currently if multiple values are sent via
+ * Currently if multiple values are sent via
* an array (value => array('value1', 'value2')
* the pair's operand will be forced to "IN".
+ * If passing "IN" as the operand and a string as the value,
+ * the value must be a properly quoted and escaped string.
*
* metadata_name_value_pairs_operator => NULL|STR The operator to use for combining
* (name = value) OPERATOR (name = value); default AND
@@ -619,6 +622,8 @@ $owner_guids = NULL) {
// if the operand is IN don't quote it because quoting should be done already.
if (is_numeric($pair['value'])) {
$value = sanitise_string($pair['value']);
+ } else if (is_bool($pair['value'])) {
+ $value = (int) $pair['value'];
} else if (is_array($pair['value'])) {
$values_array = array();
@@ -920,8 +925,8 @@ function elgg_get_metadata_cache() {
* Invalidate the metadata cache based on options passed to various *_metadata functions
*
* @param string $action Action performed on metadata. "delete", "disable", or "enable"
- *
- * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * @return void
*/
function elgg_invalidate_metadata_cache($action, array $options) {
// remove as little as possible, optimizing for common cases
diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php
index f49b4a163..57d876c06 100644
--- a/engine/lib/metastrings.php
+++ b/engine/lib/metastrings.php
@@ -421,9 +421,11 @@ function elgg_get_metastring_based_objects($options) {
if ($metastring_clauses) {
$wheres = array_merge($wheres, $metastring_clauses['wheres']);
$joins = array_merge($joins, $metastring_clauses['joins']);
+ } else {
+ $wheres[] = get_access_sql_suffix('n_table');
}
- if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) {
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
$selects = array_unique($selects);
// evalutate selects
$select_str = '';
@@ -434,6 +436,9 @@ function elgg_get_metastring_based_objects($options) {
}
$query = "SELECT DISTINCT n_table.*{$select_str} FROM {$db_prefix}$type n_table";
+ } elseif ($options['count']) {
+ // count is over the entities
+ $query = "SELECT count(DISTINCT e.guid) as calculation FROM {$db_prefix}$type n_table";
} else {
$query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
}
@@ -462,7 +467,7 @@ function elgg_get_metastring_based_objects($options) {
$defaults['order_by']);
}
- if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) {
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
if (isset($options['group_by'])) {
$options['group_by'] = sanitise_string($options['group_by']);
$query .= " GROUP BY {$options['group_by']}";
@@ -510,7 +515,7 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,
&& !$ids
&& (!$pairs && $pairs !== 0)) {
- return '';
+ return array();
}
$db_prefix = elgg_get_config('dbprefix');
@@ -520,8 +525,6 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,
// only supported on values.
$binary = ($case_sensitive) ? ' BINARY ' : '';
- $access = get_access_sql_suffix($table);
-
$return = array (
'joins' => array (),
'wheres' => array()
@@ -586,13 +589,15 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,
}
if ($names_where && $values_where) {
- $wheres[] = "($names_where AND $values_where AND $access)";
+ $wheres[] = "($names_where AND $values_where)";
} elseif ($names_where) {
- $wheres[] = "($names_where AND $access)";
+ $wheres[] = $names_where;
} elseif ($values_where) {
- $wheres[] = "($values_where AND $access)";
+ $wheres[] = $values_where;
}
+ $wheres[] = get_access_sql_suffix($table);
+
if ($where = implode(' AND ', $wheres)) {
$return['wheres'][] = "($where)";
}
diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php
index 118a7214c..ab9cc05e8 100644
--- a/engine/lib/navigation.php
+++ b/engine/lib/navigation.php
@@ -218,7 +218,7 @@ function elgg_push_breadcrumb($title, $link = NULL) {
}
// avoid key collisions.
- $CONFIG->breadcrumbs[] = array('title' => $title, 'link' => $link);
+ $CONFIG->breadcrumbs[] = array('title' => elgg_get_excerpt($title, 100), 'link' => $link);
}
/**
@@ -323,7 +323,8 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {
}
if (!$selected) {
- // nothing selected, match name to context
+ // nothing selected, match name to context or match url
+ $current_url = current_page_url();
foreach ($return as $section_name => $section) {
foreach ($section as $key => $item) {
// only highlight internal links
@@ -332,6 +333,10 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {
$return[$section_name][$key]->setSelected(true);
break 2;
}
+ if ($item->getHref() == $current_url) {
+ $return[$section_name][$key]->setSelected(true);
+ break 2;
+ }
}
}
}
diff --git a/engine/lib/notification.php b/engine/lib/notification.php
index b6399b3c6..be0c359d4 100644
--- a/engine/lib/notification.php
+++ b/engine/lib/notification.php
@@ -110,12 +110,15 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
// Are we overriding delivery?
$methods = $methods_override;
if (!$methods) {
- $tmp = (array)get_user_notification_settings($guid);
+ $tmp = get_user_notification_settings($guid);
$methods = array();
- foreach ($tmp as $k => $v) {
- // Add method if method is turned on for user!
- if ($v) {
- $methods[] = $k;
+ // $tmp may be false. don't cast
+ if (is_object($tmp)) {
+ foreach ($tmp as $k => $v) {
+ // Add method if method is turned on for user!
+ if ($v) {
+ $methods[] = $k;
+ }
}
}
}
@@ -165,7 +168,7 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
*
* @param int $user_guid The user id
*
- * @return stdClass
+ * @return stdClass|false
*/
function get_user_notification_settings($user_guid = 0) {
$user_guid = (int)$user_guid;
diff --git a/engine/lib/opendd.php b/engine/lib/opendd.php
index f00ea6aab..7d635a295 100644
--- a/engine/lib/opendd.php
+++ b/engine/lib/opendd.php
@@ -7,6 +7,8 @@
* @version 0.4
*/
+// @codingStandardsIgnoreStart
+
/**
* Attempt to construct an ODD object out of a XmlElement or sub-elements.
*
@@ -103,3 +105,5 @@ function ODD_Import($xml) {
function ODD_Export(ODDDocument $document) {
return "$document";
}
+
+// @codingStandardsIgnoreEnd
diff --git a/engine/lib/output.php b/engine/lib/output.php
index da8e1ab86..de4f911fb 100644
--- a/engine/lib/output.php
+++ b/engine/lib/output.php
@@ -13,28 +13,33 @@
* @param string $text The input string
*
* @return string The output string with formatted links
- **/
+ */
function parse_urls($text) {
+
+ // URI specification: http://www.ietf.org/rfc/rfc3986.txt
+ // This varies from the specification in the following ways:
+ // * Supports non-ascii characters
+ // * Does not allow parentheses and single quotes
+ // * Cuts off commas, exclamation points, and periods off as last character
+
// @todo this causes problems with <attr = "val">
// must be in <attr="val"> format (no space).
// By default htmlawed rewrites tags to this format.
// if PHP supported conditional negative lookbehinds we could use this:
// $r = preg_replace_callback('/(?<!=)(?<![ ])?(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i',
- //
- // we can put , in the list of excluded char but need to keep . because of domain names.
- // it is removed in the callback.
- $r = preg_replace_callback('/(?<!=)(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i',
+ $r = preg_replace_callback('/(?<![=\/"\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\']+)/i',
create_function(
'$matches',
'
$url = $matches[1];
- $period = \'\';
- if (substr($url, -1, 1) == \'.\') {
- $period = \'.\';
- $url = trim($url, \'.\');
+ $punc = "";
+ $last = substr($url, -1, 1);
+ if (in_array($last, array(".", "!", ",", "(", ")"))) {
+ $punc = $last;
+ $url = rtrim($url, ".!,()");
}
$urltext = str_replace("/", "/<wbr />", $url);
- return "<a href=\"$url\">$urltext</a>$period";
+ return "<a href=\"$url\" rel=\"nofollow\">$urltext</a>$punc";
'
), $text);
@@ -284,11 +289,9 @@ function elgg_get_friendly_title($title) {
return $result;
}
- // handle some special cases
- $title = str_replace('&amp;', 'and', $title);
- // quotes and angle brackets stored in the database as html encoded
- $title = htmlspecialchars_decode($title);
-
+ // titles are often stored HTML encoded
+ $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
+
$title = ElggTranslit::urlize($title);
return $title;
@@ -418,6 +421,25 @@ function _elgg_html_decode($string) {
}
/**
+ * Prepares query string for output to prevent CSRF attacks.
+ *
+ * @param string $string
+ * @return string
+ *
+ * @access private
+ */
+function _elgg_get_display_query($string) {
+ //encode <,>,&, quotes and characters above 127
+ if (function_exists('mb_convert_encoding')) {
+ $display_query = mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
+ } else {
+ // if no mbstring extension, we just strip characters
+ $display_query = preg_replace("/[^\x01-\x7F]/", "", $string);
+ }
+ return htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false);
+}
+
+/**
* Unit tests for Output
*
* @param string $hook unit_test
diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php
index 7fd79e68a..4aaffc160 100644
--- a/engine/lib/pageowner.php
+++ b/engine/lib/pageowner.php
@@ -29,7 +29,9 @@ function elgg_get_page_owner_guid($guid = 0) {
// return guid of page owner entity
$guid = elgg_trigger_plugin_hook('page_owner', 'system', NULL, 0);
- $page_owner_guid = $guid;
+ if ($guid) {
+ $page_owner_guid = $guid;
+ }
return $guid;
}
diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php
index f281b1416..d5d3db466 100644
--- a/engine/lib/plugins.php
+++ b/engine/lib/plugins.php
@@ -312,10 +312,10 @@ function elgg_is_active_plugin($plugin_id, $site_guid = null) {
*/
function elgg_load_plugins() {
$plugins_path = elgg_get_plugins_path();
- $start_flags = ELGG_PLUGIN_INCLUDE_START
- | ELGG_PLUGIN_REGISTER_VIEWS
- | ELGG_PLUGIN_REGISTER_LANGUAGES
- | ELGG_PLUGIN_REGISTER_CLASSES;
+ $start_flags = ELGG_PLUGIN_INCLUDE_START |
+ ELGG_PLUGIN_REGISTER_VIEWS |
+ ELGG_PLUGIN_REGISTER_LANGUAGES |
+ ELGG_PLUGIN_REGISTER_CLASSES;
if (!$plugins_path) {
return false;
@@ -865,7 +865,7 @@ function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_
* Unsets a user-specific plugin setting
*
* @param string $name Name of the setting
- * @param int $user_guid Defaults to logged in user
+ * @param int $user_guid Defaults to logged in user
* @param string $plugin_id Defaults to contextual plugin name
*
* @return bool
@@ -1105,6 +1105,49 @@ function plugins_test($hook, $type, $value, $params) {
}
/**
+ * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case.
+ *
+ * @param string $event deactivate
+ * @param string $type plugin
+ * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key
+ * @return bool false to block plugin deactivation action
+ *
+ * @access private
+ */
+function _plugins_deactivate_dependency_check($event, $type, $params) {
+ $plugin_id = $params['plugin_entity']->getManifest()->getPluginID();
+ $plugin_name = $params['plugin_entity']->getManifest()->getName();
+
+ $active_plugins = elgg_get_plugins();
+
+ $dependents = array();
+ foreach ($active_plugins as $plugin) {
+ $manifest = $plugin->getManifest();
+ $requires = $manifest->getRequires();
+
+ foreach ($requires as $required) {
+ if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) {
+ // there are active dependents
+ $dependents[$manifest->getPluginID()] = $plugin;
+ }
+ }
+ }
+
+ if ($dependents) {
+ $list = '<ul>';
+ // construct error message and prevent disabling
+ foreach ($dependents as $dependent) {
+ $list .= '<li>' . $dependent->getManifest()->getName() . '</li>';
+ }
+ $list .= '</ul>';
+
+ register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list)));
+
+ return false;
+ }
+}
+
+/**
* Initialize the plugin system
* Listens to system init and registers actions
*
@@ -1115,6 +1158,10 @@ function plugin_init() {
run_function_once("plugin_run_once");
elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test');
+
+ // note - plugins are booted by the time this handler is registered
+ // deactivation due to error may have already occurred
+ elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check');
elgg_register_action("plugins/settings/save", '', 'admin');
elgg_register_action("plugins/usersettings/save");
diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php
index fe0b8364d..b0cd627fc 100644
--- a/engine/lib/relationships.php
+++ b/engine/lib/relationships.php
@@ -363,7 +363,7 @@ $relationship_guid = NULL, $inverse_relationship = FALSE) {
/**
* Returns a viewable list of entities by relationship
*
- * @param array $options
+ * @param array $options Options array for retrieval of entities
*
* @see elgg_list_entities()
* @see elgg_get_entities_from_relationship()
diff --git a/engine/lib/river.php b/engine/lib/river.php
index f2ec1e101..e92040eb7 100644
--- a/engine/lib/river.php
+++ b/engine/lib/river.php
@@ -120,7 +120,7 @@ $posted = 0, $annotation_id = 0) {
* subtypes => STR|ARR Entity subtype string(s)
* type_subtype_pairs => ARR Array of type => subtype pairs where subtype
* can be an array of subtype strings
- *
+ *
* posted_time_lower => INT The lower bound on the time posted
* posted_time_upper => INT The upper bound on the time posted
*
@@ -380,10 +380,10 @@ function _elgg_prefetch_river_entities(array $river_items) {
// prefetch objects and subjects
$guids = array();
foreach ($river_items as $item) {
- if ($item->subject_guid && !retrieve_cached_entity($item->subject_guid)) {
+ if ($item->subject_guid && !_elgg_retrieve_cached_entity($item->subject_guid)) {
$guids[$item->subject_guid] = true;
}
- if ($item->object_guid && !retrieve_cached_entity($item->object_guid)) {
+ if ($item->object_guid && !_elgg_retrieve_cached_entity($item->object_guid)) {
$guids[$item->object_guid] = true;
}
}
@@ -402,7 +402,7 @@ function _elgg_prefetch_river_entities(array $river_items) {
$guids = array();
foreach ($river_items as $item) {
$object = $item->getObjectEntity();
- if ($object->container_guid && !retrieve_cached_entity($object->container_guid)) {
+ if ($object->container_guid && !_elgg_retrieve_cached_entity($object->container_guid)) {
$guids[$object->container_guid] = true;
}
}
@@ -434,8 +434,13 @@ function elgg_list_river(array $options = array()) {
'pagination' => TRUE,
'list_class' => 'elgg-list-river elgg-river', // @todo remove elgg-river in Elgg 1.9
);
-
+
$options = array_merge($defaults, $options);
+
+ if (!$options["limit"] && !$options["offset"]) {
+ // no need for pagination if listing is unlimited
+ $options["pagination"] = false;
+ }
$options['count'] = TRUE;
$count = elgg_get_river($options);
@@ -445,6 +450,7 @@ function elgg_list_river(array $options = array()) {
$options['count'] = $count;
$options['items'] = $items;
+
return elgg_view('page/components/list', $options);
}
diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php
index a34c2045b..e3d5ce9cd 100644
--- a/engine/lib/sessions.php
+++ b/engine/lib/sessions.php
@@ -87,6 +87,9 @@ function elgg_is_admin_logged_in() {
*/
function elgg_is_admin_user($user_guid) {
global $CONFIG;
+
+ $user_guid = (int)$user_guid;
+
// cannot use magic metadata here because of recursion
// must support the old way of getting admin from metadata
@@ -323,6 +326,12 @@ function login(ElggUser $user, $persistent = false) {
set_last_login($_SESSION['guid']);
reset_login_failure_count($user->guid); // Reset any previous failed login attempts
+ // if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143
+ if (is_memcache_available()) {
+ // this needs to happen with a shutdown function because of the timing with set_last_login()
+ register_shutdown_function("_elgg_invalidate_memcache_for_entity", $_SESSION['guid']);
+ }
+
return true;
}
diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php
index 0c9a3c945..4cb0bb0b8 100644
--- a/engine/lib/statistics.php
+++ b/engine/lib/statistics.php
@@ -95,13 +95,17 @@ function get_number_users($show_deactivated = false) {
* @return string
*/
function get_online_users() {
- $count = find_active_users(600, 10, 0, true);
- $objects = find_active_users(600, 10);
+ $limit = max(0, (int) get_input("limit", 10));
+ $offset = max(0, (int) get_input("offset", 0));
+
+ $count = find_active_users(600, $limit, $offset, true);
+ $objects = find_active_users(600, $limit, $offset);
if ($objects) {
return elgg_view_entity_list($objects, array(
'count' => $count,
- 'limit' => 10,
+ 'limit' => $limit,
+ 'offset' => $offset
));
}
return '';
diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php
index 5a153afb2..84302632e 100644
--- a/engine/lib/system_log.php
+++ b/engine/lib/system_log.php
@@ -187,7 +187,16 @@ function system_log($object, $event) {
$object_subtype = $object->getSubtype();
$event = sanitise_string($event);
$time = time();
- $ip_address = sanitise_string($_SERVER['REMOTE_ADDR']);
+
+ if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
+ } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
+ $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_REAL_IP']));
+ } else {
+ $ip_address = $_SERVER['REMOTE_ADDR'];
+ }
+ $ip_address = sanitise_string($ip_address);
+
$performed_by = elgg_get_logged_in_user_guid();
if (isset($object->access_id)) {
diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php
index d684af862..158ec9ec1 100644
--- a/engine/lib/upgrade.php
+++ b/engine/lib/upgrade.php
@@ -245,7 +245,7 @@ function version_upgrade() {
// No version number? Oh snap...this is an upgrade from a clean installation < 1.7.
// Run all upgrades without error reporting and hope for the best.
- // See http://trac.elgg.org/elgg/ticket/1432 for more.
+ // See https://github.com/elgg/elgg/issues/1432 for more.
$quiet = !$dbversion;
// Note: Database upgrades are deprecated as of 1.8. Use code upgrades. See #1433
@@ -354,16 +354,12 @@ function _elgg_upgrade_unlock() {
* @access private
*/
function _elgg_upgrade_is_locked() {
- global $CONFIG, $DB_QUERY_CACHE;
-
+ global $CONFIG;
+
$is_locked = count(get_data("show tables like '{$CONFIG->dbprefix}upgrade_lock'"));
-
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- /* @var ElggStaticVariableCache $DB_QUERY_CACHE */
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated", 'NOTICE');
- }
-
+
+ // @todo why?
+ _elgg_invalidate_query_cache();
+
return $is_locked;
}
diff --git a/engine/lib/upgrades/2009102801.php b/engine/lib/upgrades/2009102801.php
index cab9a6835..3ad113fb2 100644
--- a/engine/lib/upgrades/2009102801.php
+++ b/engine/lib/upgrades/2009102801.php
@@ -203,14 +203,15 @@ function user_file_matrix($guid) {
return "$time_created/$user->guid/";
}
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE;
+global $ENTITY_CACHE, $CONFIG;
/**
* Upgrade file locations
*/
$users = mysql_query("SELECT guid, username
FROM {$CONFIG->dbprefix}users_entity WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$to = $CONFIG->dataroot . user_file_matrix($user->guid);
foreach (array('1_0', '1_1', '1_6') as $version) {
diff --git a/engine/lib/upgrades/2010033101.php b/engine/lib/upgrades/2010033101.php
index 0bffee001..4779295fd 100644
--- a/engine/lib/upgrades/2010033101.php
+++ b/engine/lib/upgrades/2010033101.php
@@ -1,7 +1,7 @@
<?php
/**
- * Conditional upgrade for UTF8 as described in http://trac.elgg.org/ticket/1928
+ * Conditional upgrade for UTF8 as described in https://github.com/elgg/elgg/issues/1928
*/
// get_version() returns the code version.
diff --git a/engine/lib/upgrades/2010061501.php b/engine/lib/upgrades/2010061501.php
index 9ff7d3102..744c28fd5 100644
--- a/engine/lib/upgrades/2010061501.php
+++ b/engine/lib/upgrades/2010061501.php
@@ -45,7 +45,7 @@ if ($dbversion < 2009100701) {
}
}
- global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE;
+ global $ENTITY_CACHE;
/**
Upgrade file locations
@@ -60,7 +60,9 @@ if ($dbversion < 2009100701) {
$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
WHERE username != ''", $link);
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
+
$to = $CONFIG->dataroot . user_file_matrix($user->guid);
foreach (array('1_0', '1_1', '1_6') as $version) {
diff --git a/engine/lib/upgrades/2010071001.php b/engine/lib/upgrades/2010071001.php
index 1b5d379d8..5594493a8 100644
--- a/engine/lib/upgrades/2010071001.php
+++ b/engine/lib/upgrades/2010071001.php
@@ -30,11 +30,12 @@ function user_file_matrix_2010071001($guid) {
$sizes = array('large', 'medium', 'small', 'tiny', 'master', 'topbar');
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+global $ENTITY_CACHE, $CONFIG;
$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$user_directory = user_file_matrix_2010071001($user->guid);
if (!$user_directory) {
diff --git a/engine/lib/upgrades/2010071002.php b/engine/lib/upgrades/2010071002.php
index 30bd6538c..52aa15ef5 100644
--- a/engine/lib/upgrades/2010071002.php
+++ b/engine/lib/upgrades/2010071002.php
@@ -4,12 +4,13 @@
*/
// loop through all users checking collections and notifications
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+global $ENTITY_CACHE, $CONFIG;
global $NOTIFICATION_HANDLERS;
$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$user = get_entity($user->guid);
foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
diff --git a/engine/lib/upgrades/2011052801.php b/engine/lib/upgrades/2011052801.php
index 8084bc06c..b5a8e1018 100644
--- a/engine/lib/upgrades/2011052801.php
+++ b/engine/lib/upgrades/2011052801.php
@@ -2,7 +2,7 @@
/**
* Make sure all users have the relationship member_of_site
*/
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+global $ENTITY_CACHE;
$db_prefix = get_config('dbprefix');
$limit = 100;
@@ -17,7 +17,8 @@ $q = "SELECT e.* FROM {$db_prefix}entities e
$users = get_data($q);
while ($users) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
// do manually to not trigger any events because these aren't new users.
foreach ($users as $user) {
diff --git a/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php
index 07732f261..780038c32 100644
--- a/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php
+++ b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php
@@ -3,7 +3,7 @@
* Elgg 1.8.3 upgrade 2012041801
* multiple_user_tokens
*
- * Fixes http://trac.elgg.org/ticket/4291
+ * Fixes https://github.com/elgg/elgg/issues/4291
* Removes the unique index on users_apisessions for user_guid and site_guid
*/
diff --git a/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
index b38eb5100..8eccf05e2 100644
--- a/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
+++ b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
@@ -7,8 +7,6 @@
* This script turns that back into a string.
*/
-global $DB_QUERY_CACHE;
-
$ia = elgg_set_ignore_access(true);
$options = array(
'type' => 'user',
@@ -17,7 +15,7 @@ $options = array(
$batch = new ElggBatch('elgg_get_entities', $options);
foreach ($batch as $entity) {
- $DB_QUERY_CACHE = array();
+ _elgg_invalidate_query_cache();
if (is_array($entity->location)) {
$entity->location = implode(', ', $entity->location);
diff --git a/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php
new file mode 100644
index 000000000..ee99bdbc8
--- /dev/null
+++ b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013051700
+ * add_missing_group_index
+ *
+ * Some Elgg sites are missing the groups_entity full text index on name and
+ * description. This checks if it exists and adds it if it does not.
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+
+$full_text_index_exists = false;
+$results = get_data("SHOW INDEX FROM {$db_prefix}groups_entity");
+if ($results) {
+ foreach ($results as $result) {
+ if ($result->Index_type === 'FULLTEXT') {
+ $full_text_index_exists = true;
+ }
+ }
+}
+
+if ($full_text_index_exists == false) {
+ $query = "ALTER TABLE {$db_prefix}groups_entity
+ ADD FULLTEXT name_2 (name, description)";
+ if (!update_data($query)) {
+ elgg_log("Failed to add full text index to groups_entity table", 'ERROR');
+ }
+}
diff --git a/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php
new file mode 100644
index 000000000..d333a6cd2
--- /dev/null
+++ b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013052900
+ * ipv6_in_syslog
+ *
+ * Upgrade the ip column in system_log to be able to store ipv6 addresses
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+$q = "ALTER TABLE {$db_prefix}system_log MODIFY COLUMN ip_address varchar(46) NOT NULL";
+
+update_data($q); \ No newline at end of file
diff --git a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php
new file mode 100644
index 000000000..538d74dd6
--- /dev/null
+++ b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013060900
+ * site_secret
+ *
+ * Description
+ */
+
+$strength = _elgg_get_site_secret_strength();
+
+if ($strength !== 'strong') {
+ // a new key is needed immediately
+ register_translations(elgg_get_root_path() . 'languages/');
+
+ elgg_add_admin_notice('weak_site_key', elgg_echo("upgrade:site_secret_warning:$strength"));
+}
diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php
index 3466c25f9..0e36dc46d 100644
--- a/engine/lib/user_settings.php
+++ b/engine/lib/user_settings.php
@@ -308,7 +308,7 @@ function usersettings_page_handler($page) {
$user = get_user_by_username($page[1]);
elgg_set_page_owner_guid($user->guid);
} else {
- $user = elgg_get_logged_in_user_guid();
+ $user = elgg_get_logged_in_user_entity();
elgg_set_page_owner_guid($user->guid);
}
diff --git a/engine/lib/users.php b/engine/lib/users.php
index 4a585c07f..a8fb9121c 100644
--- a/engine/lib/users.php
+++ b/engine/lib/users.php
@@ -237,7 +237,7 @@ function make_user_admin($user_guid) {
}
$r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='yes' where guid=$user_guid");
- invalidate_cache_for_entity($user_guid);
+ _elgg_invalidate_cache_for_entity($user_guid);
return $r;
}
@@ -273,7 +273,7 @@ function remove_user_admin($user_guid) {
}
$r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='no' where guid=$user_guid");
- invalidate_cache_for_entity($user_guid);
+ _elgg_invalidate_cache_for_entity($user_guid);
return $r;
}
@@ -553,13 +553,18 @@ function get_user($guid) {
function get_user_by_username($username) {
global $CONFIG, $USERNAME_TO_GUID_MAP_CACHE;
+ // Fixes #6052. Username is frequently sniffed from the path info, which,
+ // unlike $_GET, is not URL decoded. If the username was not URL encoded,
+ // this is harmless.
+ $username = rawurldecode($username);
+
$username = sanitise_string($username);
$access = get_access_sql_suffix('e');
// Caching
if ((isset($USERNAME_TO_GUID_MAP_CACHE[$username]))
- && (retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) {
- return retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
+ && (_elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) {
+ return _elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
}
$query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u
@@ -592,9 +597,9 @@ function get_user_by_code($code) {
// Caching
if ((isset($CODE_TO_GUID_MAP_CACHE[$code]))
- && (retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) {
+ && (_elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) {
- return retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]);
+ return _elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]);
}
$query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u
@@ -705,18 +710,18 @@ function send_new_password_request($user_guid) {
* @return bool
*/
function force_user_password_reset($user_guid, $password) {
- global $CONFIG;
-
$user = get_entity($user_guid);
if ($user instanceof ElggUser) {
- $salt = generate_random_cleartext_password(); // Reset the salt
- $user->salt = $salt;
+ $ia = elgg_set_ignore_access();
- $hash = generate_user_password($user, $password);
+ $user->salt = generate_random_cleartext_password();
+ $hash = generate_user_password($user, $password);
+ $user->password = $hash;
+ $result = (bool)$user->save();
- $query = "UPDATE {$CONFIG->dbprefix}users_entity
- set password='$hash', salt='$salt' where guid=$user_guid";
- return update_data($query);
+ elgg_set_ignore_access($ia);
+
+ return $result;
}
return false;
@@ -1091,6 +1096,7 @@ function friends_page_handler($segments, $handler) {
* @access private
*/
function collections_page_handler($page_elements) {
+ gatekeeper();
elgg_set_context('friends');
$base = elgg_get_config('path');
if (isset($page_elements[0])) {
diff --git a/engine/lib/views.php b/engine/lib/views.php
index cfceccec0..1142461fe 100644
--- a/engine/lib/views.php
+++ b/engine/lib/views.php
@@ -218,7 +218,7 @@ function elgg_register_ajax_view($view) {
/**
* Unregister a view for ajax calls
- *
+ *
* @param string $view The view name
* @return void
* @since 1.8.3
@@ -369,7 +369,7 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) {
* view, $view_name plugin hook.
*
* @warning Any variables in $_SESSION will override passed vars
- * upon name collision. See {@trac #2124}.
+ * upon name collision. See https://github.com/Elgg/Elgg/issues/2124
*
* @param string $view The name and location of the view to use
* @param array $vars Variables to pass to the view.
@@ -795,7 +795,7 @@ function elgg_view_menu($menu_name, array $vars = array()) {
* - bool 'full_view' Whether to show a full or condensed view.
*
* @tip This function can automatically appends annotations to entities if in full
- * view and a handler is registered for the entity:annotate. See {@trac 964} and
+ * view and a handler is registered for the entity:annotate. See https://github.com/Elgg/Elgg/issues/964 and
* {@link elgg_view_entity_annotations()}.
*
* @param ElggEntity $entity The entity to display
@@ -992,6 +992,11 @@ function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(),
function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true,
$list_type_toggle = true, $pagination = true) {
+ if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ }
+
if (!is_int($offset)) {
$offset = (int)get_input('offset', 0);
}
@@ -1064,8 +1069,13 @@ function elgg_view_annotation_list($annotations, array $vars = array()) {
'full_view' => true,
'offset_key' => 'annoff',
);
-
+
$vars = array_merge($defaults, $vars);
+
+ if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ }
return elgg_view('page/components/list', $vars);
}
@@ -1107,7 +1117,7 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) {
* This is a shortcut for {@elgg_view page/elements/title}.
*
* @param string $title The page title
- * @param array $vars View variables (was submenu be displayed? (deprecated))
+ * @param array $vars View variables (was submenu be displayed? (deprecated))
*
* @return string The HTML (etc)
*/
@@ -1179,7 +1189,7 @@ function elgg_view_comments($entity, $add_comment = true, array $vars = array())
*
* @param string $image The icon and other information
* @param string $body Description content
- * @param array $vars Additional parameters for the view
+ * @param array $vars Additional parameters for the view
*
* @return string
* @since 1.8.0
@@ -1236,15 +1246,17 @@ function elgg_view_river_item($item, array $vars = array()) {
// subject is disabled or subject/object deleted
return '';
}
+
+ // @todo this needs to be cleaned up
// Don't hide objects in closed groups that a user can see.
- // see http://trac.elgg.org/ticket/4789
-// else {
-// // hide based on object's container
-// $visibility = ElggGroupItemVisibility::factory($object->container_guid);
-// if ($visibility->shouldHideItems) {
-// return '';
-// }
-// }
+ // see https://github.com/elgg/elgg/issues/4789
+ // else {
+ // // hide based on object's container
+ // $visibility = ElggGroupItemVisibility::factory($object->container_guid);
+ // if ($visibility->shouldHideItems) {
+ // return '';
+ // }
+ // }
$vars['item'] = $item;
@@ -1332,12 +1344,12 @@ function elgg_view_list_item($item, array $vars = array()) {
/**
* View one of the elgg sprite icons
- *
+ *
* Shorthand for <span class="elgg-icon elgg-icon-$name"></span>
- *
+ *
* @param string $name The specific icon to display
* @param string $class Additional class: float, float-alt, or custom class
- *
+ *
* @return string The html for displaying an icon
*/
function elgg_view_icon($name, $class = '') {
@@ -1636,7 +1648,7 @@ function elgg_views_boot() {
}
// set default icon sizes - can be overridden in settings.php or with plugin
- if (!$CONFIG->icon_sizes) {
+ if (!isset($CONFIG->icon_sizes)) {
$icon_sizes = array(
'topbar' => array('w' => 16, 'h' => 16, 'square' => TRUE, 'upscale' => TRUE),
'tiny' => array('w' => 25, 'h' => 25, 'square' => TRUE, 'upscale' => TRUE),
diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php
index b440e3afb..51cad6f39 100644
--- a/engine/lib/web_services.php
+++ b/engine/lib/web_services.php
@@ -1166,6 +1166,17 @@ function list_all_apis() {
* @access private
*/
function auth_gettoken($username, $password) {
+ // check if username is an email address
+ if (is_email_address($username)) {
+ $users = get_user_by_email($username);
+
+ // check if we have a unique user
+ if (is_array($users) && (count($users) == 1)) {
+ $username = $users[0]->username;
+ }
+ }
+
+ // validate username and password
if (true === elgg_authenticate($username, $password)) {
$token = create_user_token($username);
if ($token) {
@@ -1195,7 +1206,7 @@ $ERRORS = array();
*
* @return void
* @access private
- *
+ *
* @throws Exception
*/
function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
diff --git a/engine/schema/mysql.sql b/engine/schema/mysql.sql
index 6c6e9db89..4714b71bb 100644
--- a/engine/schema/mysql.sql
+++ b/engine/schema/mysql.sql
@@ -361,7 +361,7 @@ CREATE TABLE `prefix_system_log` (
`access_id` int(11) NOT NULL,
`enabled` enum('yes','no') NOT NULL DEFAULT 'yes',
`time_created` int(11) NOT NULL,
- `ip_address` varchar(15) NOT NULL,
+ `ip_address` varchar(46) NOT NULL,
PRIMARY KEY (`id`),
KEY `object_id` (`object_id`),
KEY `object_class` (`object_class`),
diff --git a/engine/tests/api/access_collections.php b/engine/tests/api/access_collections.php
index ebcd7d318..4acfae596 100644
--- a/engine/tests/api/access_collections.php
+++ b/engine/tests/api/access_collections.php
@@ -54,7 +54,6 @@ class ElggCoreAccessCollectionsTest extends ElggCoreUnitTest {
}
public function testCreateGetDeleteACL() {
- global $DB_QUERY_CACHE;
$acl_name = 'test access collection';
$acl_id = create_access_collection($acl_name);
@@ -67,8 +66,6 @@ class ElggCoreAccessCollectionsTest extends ElggCoreUnitTest {
$this->assertEqual($acl->id, $acl_id);
if ($acl) {
- $DB_QUERY_CACHE = array();
-
$this->assertEqual($acl->name, $acl_name);
$result = delete_access_collection($acl_id);
diff --git a/engine/tests/api/annotations.php b/engine/tests/api/annotations.php
index 947292970..c0b0687cc 100644
--- a/engine/tests/api/annotations.php
+++ b/engine/tests/api/annotations.php
@@ -65,6 +65,86 @@ class ElggCoreAnnotationAPITest extends ElggCoreUnitTest {
$annotations = elgg_get_annotations($options);
$this->assertTrue(empty($annotations));
+ // nothing to delete so null returned
+ $this->assertNull(elgg_delete_annotations($options));
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggDisableAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $this->assertTrue(elgg_disable_annotations($options));
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertTrue(empty($annotations));
+
+ access_show_hidden_entities(true);
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+ access_show_hidden_entities(false);
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggEnableAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $this->assertTrue(elgg_disable_annotations($options));
+
+ // cannot see any annotations so returns null
+ $this->assertNull(elgg_enable_annotations($options));
+
+ access_show_hidden_entities(true);
+ $this->assertTrue(elgg_enable_annotations($options));
+ access_show_hidden_entities(false);
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggAnnotationExists() {
+ $e = new ElggObject();
+ $e->save();
+ $guid = $e->getGUID();
+
+ $this->assertFalse(elgg_annotation_exists($guid, 'test_annotation'));
+
+ $e->annotate('test_annotation', rand(0, 10000));
+ $this->assertTrue(elgg_annotation_exists($guid, 'test_annotation'));
+ // this metastring should always exist but an annotation of this name should not
+ $this->assertFalse(elgg_annotation_exists($guid, 'email'));
+
+ $options = array(
+ 'guid' => $guid,
+ 'limit' => 0
+ );
+ $this->assertTrue(elgg_disable_annotations($options));
+ $this->assertTrue(elgg_annotation_exists($guid, 'test_annotation'));
+
$this->assertTrue($e->delete());
+ $this->assertFalse(elgg_annotation_exists($guid, 'test_annotation'));
}
}
diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php
index 7bf8ef04a..fef9dc0c5 100644
--- a/engine/tests/api/entity_getter_functions.php
+++ b/engine/tests/api/entity_getter_functions.php
@@ -426,7 +426,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$options = array(
'types' => $types,
- 'subtype' => $subtype
+ 'subtypes' => $subtype
);
$es = elgg_get_entities($options);
@@ -2755,7 +2755,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
'calculation' => 'count',
'count' => true,
);
- $count = (int)elgg_get_entities_from_annotation_calculation($options);
+ $count = elgg_get_entities_from_annotation_calculation($options);
$this->assertEqual(1, $count);
}
diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php
index 62e4471e0..414fb4145 100644
--- a/engine/tests/api/helpers.php
+++ b/engine/tests/api/helpers.php
@@ -519,7 +519,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
$this->assertIdentical($elements_sorted_string, $test_elements);
}
- // see http://trac.elgg.org/ticket/4288
+ // see https://github.com/elgg/elgg/issues/4288
public function testElggBatchIncOffset() {
// normal increment
$options = array(
@@ -578,6 +578,107 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
$this->assertEqual(11, $j);
}
+ public function testElggBatchReadHandlesBrokenEntities() {
+ $num_test_entities = 8;
+ $guids = array();
+ for ($i = $num_test_entities; $i > 0; $i--) {
+ $entity = new ElggObject();
+ $entity->type = 'object';
+ $entity->subtype = 'test_5357_subtype';
+ $entity->access_id = ACCESS_PUBLIC;
+ $entity->save();
+ $guids[] = $entity->guid;
+ _elgg_invalidate_cache_for_entity($entity->guid);
+ }
+
+ // break entities such that the first fetch has one incomplete
+ // and the second and third fetches have only incompletes!
+ $db_prefix = elgg_get_config('dbprefix');
+ delete_data("
+ DELETE FROM {$db_prefix}objects_entity
+ WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]})
+ ");
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'test_5357_subtype',
+ 'order_by' => 'e.guid',
+ );
+
+ $entities_visited = array();
+ $batch = new ElggBatch('elgg_get_entities', $options, null, 2);
+ /* @var ElggEntity[] $batch */
+ foreach ($batch as $entity) {
+ $entities_visited[] = $entity->guid;
+ }
+
+ // The broken entities should not have been visited
+ $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7]));
+
+ // cleanup (including leftovers from previous tests)
+ $entity_rows = elgg_get_entities(array_merge($options, array(
+ 'callback' => '',
+ 'limit' => false,
+ )));
+ $guids = array();
+ foreach ($entity_rows as $row) {
+ $guids[] = $row->guid;
+ }
+ delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")");
+ delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")");
+ }
+
+ public function testElggBatchDeleteHandlesBrokenEntities() {
+ $num_test_entities = 8;
+ $guids = array();
+ for ($i = $num_test_entities; $i > 0; $i--) {
+ $entity = new ElggObject();
+ $entity->type = 'object';
+ $entity->subtype = 'test_5357_subtype';
+ $entity->access_id = ACCESS_PUBLIC;
+ $entity->save();
+ $guids[] = $entity->guid;
+ _elgg_invalidate_cache_for_entity($entity->guid);
+ }
+
+ // break entities such that the first fetch has one incomplete
+ // and the second and third fetches have only incompletes!
+ $db_prefix = elgg_get_config('dbprefix');
+ delete_data("
+ DELETE FROM {$db_prefix}objects_entity
+ WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]})
+ ");
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'test_5357_subtype',
+ 'order_by' => 'e.guid',
+ );
+
+ $entities_visited = array();
+ $batch = new ElggBatch('elgg_get_entities', $options, null, 2, false);
+ /* @var ElggEntity[] $batch */
+ foreach ($batch as $entity) {
+ $entities_visited[] = $entity->guid;
+ $entity->delete();
+ }
+
+ // The broken entities should not have been visited
+ $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7]));
+
+ // cleanup (including leftovers from previous tests)
+ $entity_rows = elgg_get_entities(array_merge($options, array(
+ 'callback' => '',
+ 'limit' => false,
+ )));
+ $guids = array();
+ foreach ($entity_rows as $row) {
+ $guids[] = $row->guid;
+ }
+ delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")");
+ delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")");
+ }
+
static function elgg_batch_callback_test($options, $reset = false) {
static $count = 1;
diff --git a/engine/tests/api/metadata.php b/engine/tests/api/metadata.php
index 825290d80..d23510c6a 100644
--- a/engine/tests/api/metadata.php
+++ b/engine/tests/api/metadata.php
@@ -123,9 +123,23 @@ class ElggCoreMetadataAPITest extends ElggCoreUnitTest {
$e->delete();
}
+ /**
+ * https://github.com/Elgg/Elgg/issues/4867
+ */
+ public function testElggGetEntityMetadataWhereSqlWithFalseValue() {
+ $pair = array('name' => 'test' , 'value' => false);
+ $result = elgg_get_entity_metadata_where_sql('e', 'metadata', null, null, $pair);
+ $where = preg_replace( '/\s+/', ' ', $result['wheres'][0]);
+ $this->assertTrue(strpos($where, "msn1.string = 'test' AND BINARY msv1.string = 0") > 0);
+
+ $result = elgg_get_entity_metadata_where_sql('e', 'metadata', array('test'), array(false));
+ $where = preg_replace( '/\s+/', ' ', $result['wheres'][0]);
+ $this->assertTrue(strpos($where, "msn.string IN ('test')) AND ( BINARY msv.string IN ('0')"));
+ }
+
// Make sure metadata with multiple values is correctly deleted when re-written
// by another user
- // http://trac.elgg.org/ticket/2776
+ // https://github.com/elgg/elgg/issues/2776
public function test_elgg_metadata_multiple_values() {
$u1 = new ElggUser();
$u1->username = rand();
diff --git a/engine/tests/api/metadata_cache.php b/engine/tests/api/metadata_cache.php
index 846116a7b..7fb328169 100644
--- a/engine/tests/api/metadata_cache.php
+++ b/engine/tests/api/metadata_cache.php
@@ -166,4 +166,11 @@ class ElggCoreMetadataCacheTest extends ElggCoreUnitTest {
$actual = $this->cache->filterMetadataHeavyEntities($guids, 6000);
$this->assertIdentical($actual, $expected);
}
+
+ public function testCreateMetadataInvalidates() {
+ $this->obj1->foo = 1;
+ create_metadata($this->guid1, 'foo', 2, '', elgg_get_logged_in_user_guid(), ACCESS_FRIENDS);
+
+ $this->assertEqual($this->obj1->foo, 2);
+ }
}
diff --git a/engine/tests/api/metastrings.php b/engine/tests/api/metastrings.php
index 0a8945084..5efdab972 100644
--- a/engine/tests/api/metastrings.php
+++ b/engine/tests/api/metastrings.php
@@ -55,8 +55,11 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest {
* Called after each test method.
*/
public function tearDown() {
- // do not allow SimpleTest to interpret Elgg notices as exceptions
- $this->swallowErrors();
+ access_show_hidden_entities(true);
+ elgg_delete_annotations(array(
+ 'guid' => $this->object->guid,
+ ));
+ access_show_hidden_entities(false);
}
/**
@@ -98,6 +101,31 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest {
}
}
+ public function testGetMetastringObjectFromIDWithDisabledAnnotation() {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->assertTrue($annotation->disable());
+
+ $test = elgg_get_metastring_based_object_from_id($id, 'annotation');
+ $this->assertEqual(false, $test);
+ }
+
+ public function testGetMetastringBasedObjectWithDisabledAnnotation() {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->assertTrue($annotation->disable());
+
+ $test = elgg_get_metastring_based_objects(array(
+ 'metastring_type' => 'annotations',
+ 'guid' => $this->object->guid,
+ ));
+ $this->assertEqual(array(), $test);
+ }
+
public function testEnableDisableByID() {
$db_prefix = elgg_get_config('dbprefix');
$annotations = $this->createAnnotations(1);
@@ -119,7 +147,6 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest {
// enable
$ashe = access_get_show_hidden_status();
access_show_hidden_entities(true);
- flush();
$this->assertTrue(elgg_set_metastring_based_object_enabled_by_id($id, 'yes', $type));
$test = get_data($q);
diff --git a/engine/tests/api/plugins.php b/engine/tests/api/plugins.php
index 114f3991b..d0f111c48 100644
--- a/engine/tests/api/plugins.php
+++ b/engine/tests/api/plugins.php
@@ -69,7 +69,7 @@ class ElggCorePluginsAPITest extends ElggCoreUnitTest {
'description' => 'A longer, more interesting description.',
'website' => 'http://www.elgg.org/',
'repository' => 'https://github.com/Elgg/Elgg',
- 'bugtracker' => 'http://trac.elgg.org',
+ 'bugtracker' => 'https://github.com/elgg/elgg/issues',
'donations' => 'http://elgg.org/supporter.php',
'copyright' => '(C) Elgg Foundation 2011',
'license' => 'GNU General Public License version 2',
@@ -174,7 +174,7 @@ class ElggCorePluginsAPITest extends ElggCoreUnitTest {
}
public function testElggPluginManifestGetBugtracker() {
- $this->assertEqual($this->manifest18->getBugTrackerURL(), 'http://trac.elgg.org');
+ $this->assertEqual($this->manifest18->getBugTrackerURL(), 'https://github.com/elgg/elgg/issues');
$this->assertEqual($this->manifest17->getBugTrackerURL(), '');
}
diff --git a/engine/tests/objects/entities.php b/engine/tests/objects/entities.php
index 248b85c9e..bac72079e 100644
--- a/engine/tests/objects/entities.php
+++ b/engine/tests/objects/entities.php
@@ -271,7 +271,7 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->save_entity();
// test deleting incorrectly
- // @link http://trac.elgg.org/ticket/2273
+ // @link https://github.com/elgg/elgg/issues/2273
$this->assertNull($this->entity->deleteMetadata('impotent'));
$this->assertEqual($this->entity->important, 'indeed!');
diff --git a/engine/tests/objects/objects.php b/engine/tests/objects/objects.php
index 915594e0a..263ab2414 100644
--- a/engine/tests/objects/objects.php
+++ b/engine/tests/objects/objects.php
@@ -194,7 +194,7 @@ class ElggCoreObjectTest extends ElggCoreUnitTest {
$old = elgg_set_ignore_access(true);
}
- // see http://trac.elgg.org/ticket/1196
+ // see https://github.com/elgg/elgg/issues/1196
public function testElggEntityRecursiveDisableWhenLoggedOut() {
$e1 = new ElggObject();
$e1->access_id = ACCESS_PUBLIC;
diff --git a/engine/tests/objects/users.php b/engine/tests/objects/users.php
index a3573acb6..8a1033ac4 100644
--- a/engine/tests/objects/users.php
+++ b/engine/tests/objects/users.php
@@ -65,6 +65,9 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$attributes['code'] = NULL;
$attributes['banned'] = 'no';
$attributes['admin'] = 'no';
+ $attributes['prev_last_action'] = NULL;
+ $attributes['last_login'] = NULL;
+ $attributes['prev_last_login'] = NULL;
ksort($attributes);
$entity_attributes = $this->user->expose_attributes();
@@ -142,7 +145,7 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
}
public function testElggUserNameCache() {
- // Trac #1305
+ // issue https://github.com/elgg/elgg/issues/1305
// very unlikely a user would have this username
$name = (string)time();
@@ -156,6 +159,22 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$this->assertFalse($user);
}
+ public function testGetUserByUsernameAcceptsUrlEncoded() {
+ $username = (string)time();
+ $this->user->username = $username;
+ $guid = $this->user->save();
+
+ // percent encode first letter
+ $first_letter = $username[0];
+ $first_letter = str_pad('%' . dechex(ord($first_letter)), 2, '0', STR_PAD_LEFT);
+ $username = $first_letter . substr($username, 1);
+
+ $user = get_user_by_username($username);
+ $this->assertTrue((bool) $user);
+ $this->assertEqual($guid, $user->guid);
+
+ $this->user->delete();
+ }
public function testElggUserMakeAdmin() {
global $CONFIG;
diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php
index 691433a41..689275661 100644
--- a/engine/tests/regression/trac_bugs.php
+++ b/engine/tests/regression/trac_bugs.php
@@ -1,7 +1,7 @@
<?php
/**
- * Elgg Regression Tests -- Trac Bugfixes
- * Any bugfixes from Trac that require testing belong here.
+ * Elgg Regression Tests -- GitHub Bugfixes
+ * Any bugfixes from GitHub that require testing belong here.
*
* @package Elgg
* @subpackage Test
@@ -201,26 +201,28 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
}
/**
- * http://trac.elgg.org/ticket/3210 - Don't remove -s in friendly titles
- * http://trac.elgg.org/ticket/2276 - improve char encoding
+ * https://github.com/elgg/elgg/issues/3210 - Don't remove -s in friendly titles
+ * https://github.com/elgg/elgg/issues/2276 - improve char encoding
*/
public function test_friendly_title() {
$cases = array(
+ // acid test
+ "B&N > Amazon, OK? <bold> 'hey!' $34"
+ => "bn-amazon-ok-bold-hey-34",
+
// hyphen, underscore and ASCII whitespace replaced by separator,
// other non-alphanumeric ASCII removed
- "a-a_a a\na\ra\ta\va!a\"a#a\$a%a&a'a(a)a*a+a,a.a/a:a;a<a=a>a?a@a[a\\a]a^a`a{a|a}a~a"
- => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-
+ "a-a_a a\na\ra\ta\va!a\"a#a\$a%aa'a(a)a*a+a,a.a/a:a;a=a?a@a[a\\a]a^a`a{a|a}a~a"
+ => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+
// separators trimmed
- "-_ hello _-" => "hello",
+ "-_ hello _-"
+ => "hello",
// accents removed, lower case, other multibyte chars are URL encoded
"I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n, AND \xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"
// Iñtërnâtiônàlizætiøn, AND 日本語
=> 'internationalizaetion-and-%E6%97%A5%E6%9C%AC%E8%AA%9E',
-
- // some HTML entity replacements
- "Me &amp; You" => 'me-and-you',
);
// where available, string is converted to NFC before transliteration
@@ -234,4 +236,170 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
$this->assertIdentical($expected, $friendly_title);
}
}
+
+ /**
+ * Test #5369 -- parse_urls()
+ * https://github.com/Elgg/Elgg/issues/5369
+ */
+ public function test_parse_urls() {
+
+ $cases = array(
+ 'no.link.here' =>
+ 'no.link.here',
+ 'simple link http://example.org test' =>
+ 'simple link <a href="http://example.org" rel="nofollow">http:/<wbr />/<wbr />example.org</a> test',
+ 'non-ascii http://ñew.org/ test' =>
+ 'non-ascii <a href="http://ñew.org/" rel="nofollow">http:/<wbr />/<wbr />ñew.org/<wbr /></a> test',
+
+ // section 2.1
+ 'percent encoded http://example.org/a%20b test' =>
+ 'percent encoded <a href="http://example.org/a%20b" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a%20b</a> test',
+ // section 2.2: skipping single quote and parenthese
+ 'reserved characters http://example.org/:/?#[]@!$&*+,;= test' =>
+ 'reserved characters <a href="http://example.org/:/?#[]@!$&*+,;=" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />:/<wbr />?#[]@!$&*+,;=</a> test',
+ // section 2.3
+ 'unreserved characters http://example.org/a1-._~ test' =>
+ 'unreserved characters <a href="http://example.org/a1-._~" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a1-._~</a> test',
+
+ 'parameters http://example.org/?val[]=1&val[]=2 test' =>
+ 'parameters <a href="http://example.org/?val[]=1&val[]=2" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />?val[]=1&val[]=2</a> test',
+ 'port http://example.org:80/ test' =>
+ 'port <a href="http://example.org:80/" rel="nofollow">http:/<wbr />/<wbr />example.org:80/<wbr /></a> test',
+
+ 'parentheses (http://www.google.com) test' =>
+ 'parentheses (<a href="http://www.google.com" rel="nofollow">http:/<wbr />/<wbr />www.google.com</a>) test',
+ 'comma http://elgg.org, test' =>
+ 'comma <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>, test',
+ 'period http://elgg.org. test' =>
+ 'period <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>. test',
+ 'exclamation http://elgg.org! test' =>
+ 'exclamation <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>! test',
+
+ 'already anchor <a href="http://twitter.com/">twitter</a> test' =>
+ 'already anchor <a href="http://twitter.com/">twitter</a> test',
+
+ 'ssl https://example.org/ test' =>
+ 'ssl <a href="https://example.org/" rel="nofollow">https:/<wbr />/<wbr />example.org/<wbr /></a> test',
+ 'ftp ftp://example.org/ test' =>
+ 'ftp <a href="ftp://example.org/" rel="nofollow">ftp:/<wbr />/<wbr />example.org/<wbr /></a> test',
+
+ 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>' =>
+ 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>',
+
+ 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>' =>
+ 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>',
+
+ 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>' =>
+ 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>',
+
+ 'parens in uri http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx' =>
+ 'parens in uri <a href="http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx" rel="nofollow">http:/<wbr />/<wbr />thedailywtf.com/<wbr />Articles/<wbr />A-(Long-Overdue)-BuildMaster-Introduction.aspx</a>'
+ );
+ foreach ($cases as $input => $output) {
+ $this->assertEqual($output, parse_urls($input));
+ }
+ }
+
+ /**
+ * Ensure additional select columns do not end up in entity attributes.
+ *
+ * https://github.com/Elgg/Elgg/issues/5538
+ */
+ public function test_extra_columns_dont_appear_in_attributes() {
+ global $ENTITY_CACHE;
+
+ // may not have groups in DB - let's create one
+ $group = new ElggGroup();
+ $group->name = 'test_group';
+ $group->access_id = ACCESS_PUBLIC;
+ $this->assertTrue($group->save() !== false);
+
+ // entity cache interferes with our test
+ $ENTITY_CACHE = array();
+
+ foreach (array('site', 'user', 'group', 'object') as $type) {
+ $entities = elgg_get_entities(array(
+ 'type' => $type,
+ 'selects' => array('1 as _nonexistent_test_column'),
+ 'limit' => 1,
+ ));
+ if (!$this->assertTrue($entities, "Query for '$type' did not return an entity.")) {
+ continue;
+ }
+ $entity = $entities[0];
+ $this->assertNull($entity->_nonexistent_test_column, "Additional select columns are leaking to attributes for '$type'");
+ }
+
+ $group->delete();
+ }
+
+ /**
+ * Ensure that ElggBatch doesn't go into infinite loop when disabling annotations recursively when show hidden is enabled.
+ *
+ * https://github.com/Elgg/Elgg/issues/5952
+ */
+ public function test_disabling_annotations_infinite_loop() {
+
+ //let's have some entity
+ $group = new ElggGroup();
+ $group->name = 'test_group';
+ $group->access_id = ACCESS_PUBLIC;
+ $this->assertTrue($group->save() !== false);
+
+ $total = 51;
+ //add some annotations
+ for ($cnt = 0; $cnt < $total; $cnt++) {
+ $group->annotate('test_annotation', 'value_' . $total);
+ }
+
+ //disable them
+ $show_hidden = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $options = array(
+ 'guid' => $group->guid,
+ 'limit' => $total, //using strict limit to avoid real infinite loop and just see ElggBatch limiting on it before finishing the work
+ );
+ elgg_disable_annotations($options);
+ access_show_hidden_entities($show_hidden);
+
+ //confirm all being disabled
+ $annotations = $group->getAnnotations(array(
+ 'limit' => $total,
+ ));
+ foreach ($annotations as $annotation) {
+ $this->assertTrue($annotation->enabled == 'no');
+ }
+
+ //delete group and annotations
+ $group->delete();
+ }
+
+ public function test_ElggXMLElement_does_not_load_external_entities() {
+ $elLast = libxml_disable_entity_loader(false);
+
+ // build payload that should trigger loading of external entity
+ $payload = file_get_contents(dirname(dirname(__FILE__)) . '/test_files/xxe/request.xml');
+ $path = realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt');
+ $path = str_replace('\\', '/', $path);
+ if ($path[0] != '/') {
+ $path = '/' . $path;
+ }
+ $path = 'file://' . $path;
+ $payload = sprintf($payload, $path);
+
+ // make sure we can actually this in this environment
+ $element = new SimpleXMLElement($payload);
+ $can_load_entity = preg_match('/secret/', (string)$element->methodName);
+
+ $this->skipUnless($can_load_entity, "XXE vulnerability cannot be tested on this system");
+
+ if ($can_load_entity) {
+ $el = new ElggXMLElement($payload);
+ $chidren = $el->getChildren();
+ $content = $chidren[0]->getContent();
+ $this->assertNoPattern('/secret/', $content);
+ }
+
+ libxml_disable_entity_loader($elLast);
+ }
}
diff --git a/engine/tests/test_files/plugin_18/manifest.xml b/engine/tests/test_files/plugin_18/manifest.xml
index 5d788616a..c8b407511 100644
--- a/engine/tests/test_files/plugin_18/manifest.xml
+++ b/engine/tests/test_files/plugin_18/manifest.xml
@@ -7,7 +7,7 @@
<description>A longer, more interesting description.</description>
<website>http://www.elgg.org/</website>
<repository>https://github.com/Elgg/Elgg</repository>
- <bugtracker>http://trac.elgg.org</bugtracker>
+ <bugtracker>https://github.com/elgg/elgg/issues</bugtracker>
<donations>http://elgg.org/supporter.php</donations>
<copyright>(C) Elgg Foundation 2011</copyright>
<license>GNU General Public License version 2</license>
diff --git a/engine/tests/test_files/xxe/external_entity.txt b/engine/tests/test_files/xxe/external_entity.txt
new file mode 100644
index 000000000..536aca34d
--- /dev/null
+++ b/engine/tests/test_files/xxe/external_entity.txt
@@ -0,0 +1 @@
+secret \ No newline at end of file
diff --git a/engine/tests/test_files/xxe/request.xml b/engine/tests/test_files/xxe/request.xml
new file mode 100644
index 000000000..4390f9db2
--- /dev/null
+++ b/engine/tests/test_files/xxe/request.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo [
+<!ELEMENT methodName ANY >
+<!ENTITY xxe SYSTEM "%s" >
+]>
+<methodCall>
+ <methodName>test&xxe;test</methodName>
+</methodCall>
diff --git a/htaccess_dist b/htaccess_dist
index 898fa22fb..44d129475 100644
--- a/htaccess_dist
+++ b/htaccess_dist
@@ -112,6 +112,14 @@ RewriteEngine on
#
#RewriteBase /
+
+# If your users receive the message "Sorry, logging in from a different domain is not permitted"
+# you must make sure your login form is served from the same hostname as your site pages.
+# See http://docs.elgg.org/wiki/Login_token_mismatch_error for more info.
+#
+# If you must add RewriteRules to change hostname, add them directly below (above all the others)
+
+
# In for backwards compatibility
RewriteRule ^pg\/([A-Za-z0-9\_\-]+)$ engine/handlers/page_handler.php?handler=$1&%{QUERY_STRING} [L]
RewriteRule ^pg\/([A-Za-z0-9\_\-]+)\/(.*)$ engine/handlers/page_handler.php?handler=$1&page=$2&%{QUERY_STRING} [L]
diff --git a/install/ElggInstaller.php b/install/ElggInstaller.php
index 93716f7cd..78cdde90f 100644
--- a/install/ElggInstaller.php
+++ b/install/ElggInstaller.php
@@ -1414,7 +1414,7 @@ class ElggInstaller {
$submissionVars['wwwroot'] = sanitise_filepath($submissionVars['wwwroot']);
$site = new ElggSite();
- $site->name = $submissionVars['sitename'];
+ $site->name = strip_tags($submissionVars['sitename']);
$site->url = $submissionVars['wwwroot'];
$site->access_id = ACCESS_PUBLIC;
$site->email = $submissionVars['siteemail'];
diff --git a/install/cli/sample_installer.php b/install/cli/sample_installer.php
index 0bae0cd23..a51f9aae4 100644
--- a/install/cli/sample_installer.php
+++ b/install/cli/sample_installer.php
@@ -1,28 +1,12 @@
<?php
+
/**
* Sample cli installer script
*/
+// change to true to run this script. Change back to false when done.
$enabled = false;
-// Do not edit below this line. //////////////////////////////
-
-
-if (!$enabled) {
- echo "To enable this script, change \$enabled to true.\n";
- echo "You *must* disable this script after a successful installation.\n";
- exit;
-}
-
-if (PHP_SAPI !== 'cli') {
- echo "You must use the command line to run this script.";
- exit;
-}
-
-require_once(dirname(dirname(__FILE__)) . "/ElggInstaller.php");
-
-$installer = new ElggInstaller();
-
// none of the following may be empty
$params = array(
// database parameters
@@ -43,11 +27,29 @@ $params = array(
'password' => '',
);
+
+// Do not edit below this line. //////////////////////////////
+
+
+if (!$enabled) {
+ echo "To enable this script, change \$enabled to true.\n";
+ echo "You *must* disable this script after a successful installation.\n";
+ exit;
+}
+
+if (PHP_SAPI !== 'cli') {
+ echo "You must use the command line to run this script.";
+ exit;
+}
+
+require_once(dirname(dirname(__FILE__)) . "/ElggInstaller.php");
+
+$installer = new ElggInstaller();
+
// install and create the .htaccess file
$installer->batchInstall($params, TRUE);
// at this point installation has completed (otherwise an exception halted execution).
-
// try to rewrite the script to disable it.
if (is_writable(__FILE__)) {
$code = file_get_contents(__FILE__);
diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js
index af2c94000..a8e187f1d 100644
--- a/js/lib/elgglib.js
+++ b/js/lib/elgglib.js
@@ -474,8 +474,8 @@ elgg.parse_str = function(string) {
re = /([^&=]+)=?([^&]*)/g;
while (result = re.exec(string)) {
- key = decodeURIComponent(result[1])
- value = decodeURIComponent(result[2])
+ key = decodeURIComponent(result[1].replace(/\+/g, ' '));
+ value = decodeURIComponent(result[2].replace(/\+/g, ' '));
params[key] = value;
}
diff --git a/js/lib/languages.js b/js/lib/languages.js
index 44ea56d2b..d218cbc4f 100644
--- a/js/lib/languages.js
+++ b/js/lib/languages.js
@@ -30,6 +30,9 @@ elgg.reload_all_translations = function(language) {
var url, options;
url = 'ajax/view/js/languages';
options = {data: {language: lang}};
+ if (elgg.config.simplecache_enabled) {
+ options.data.lc = elgg.config.lastcache;
+ }
options['success'] = function(json) {
elgg.add_translation(lang, json);
diff --git a/js/lib/session.js b/js/lib/session.js
index fa3d60aa9..a8d52733c 100644
--- a/js/lib/session.js
+++ b/js/lib/session.js
@@ -14,9 +14,9 @@ elgg.provide('elgg.session');
* {string} options[domain]
* {boolean} options[secure]
*
- * @return {string} The value of the cookie, if only name is specified
+ * @return {string|undefined} The value of the cookie, if only name is specified. Undefined if no value set
*/
-elgg.session.cookie = function (name, value, options) {
+elgg.session.cookie = function(name, value, options) {
var cookies = [], cookie = [], i = 0, date, valid = true;
//elgg.session.cookie()
@@ -47,21 +47,19 @@ elgg.session.cookie = function (name, value, options) {
}
cookies.push(name + '=' + value);
-
- if (elgg.isNumber(options.expires)) {
- if (elgg.isNumber(options.expires)) {
- date = new Date();
- date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
- } else if (options.expires.toUTCString) {
- date = options.expires;
- } else {
- valid = false;
- }
-
- if (valid) {
- cookies.push('expires=' + date.toUTCString());
- }
- }
+
+ if (options.expires) {
+ if (elgg.isNumber(options.expires)) {
+ date = new Date();
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+ } else if (options.expires.toUTCString) {
+ date = options.expires;
+ }
+
+ if (date) {
+ cookies.push('expires=' + date.toUTCString());
+ }
+ }
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
diff --git a/js/lib/ui.userpicker.js b/js/lib/ui.userpicker.js
index 7298da114..669b84cdb 100644
--- a/js/lib/ui.userpicker.js
+++ b/js/lib/ui.userpicker.js
@@ -107,11 +107,11 @@ elgg.userpicker.viewUser = function(info) {
* @return Object
*/
elgg.userpicker.getSearchParams = function(obj) {
- if (obj.element.siblings('[name=match_on]').attr('checked')) {
+ if (obj.element.parent('.elgg-user-picker').find('input[name=match_on]').attr('checked')) {
return {'match_on[]': 'friends', 'term' : obj.term};
} else {
return {'match_on[]': 'users', 'term' : obj.term};
}
};
-elgg.register_hook_handler('init', 'system', elgg.userpicker.init); \ No newline at end of file
+elgg.register_hook_handler('init', 'system', elgg.userpicker.init);
diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js
index 2a676e22a..bd39e7fb3 100644
--- a/js/tests/ElggLibTest.js
+++ b/js/tests/ElggLibTest.js
@@ -78,6 +78,7 @@ ElggLibTest.prototype.testNormalizeUrl = function() {
['https://example.com', 'https://example.com'],
['http://example-time.com', 'http://example-time.com'],
['//example.com', '//example.com'],
+ ['mod/my_plugin/graphics/image.jpg', elgg.config.wwwroot + 'mod/my_plugin/graphics/image.jpg'],
['ftp://example.com/file', 'ftp://example.com/file'],
['mailto:brett@elgg.org', 'mailto:brett@elgg.org'],
@@ -127,3 +128,13 @@ ElggLibTest.prototype.testParseUrl = function() {
});
};
+ElggLibTest.prototype.testParseStr = function() {
+
+ [
+ ["A+%2B+B=A+%2B+B", {"A + B": "A + B"}]
+
+ ].forEach(function(args) {
+ assertEquals(args[1], elgg.parse_str(args[0]));
+ });
+};
+
diff --git a/js/tests/README b/js/tests/README
index 4f86b27c6..f43c0c89d 100644
--- a/js/tests/README
+++ b/js/tests/README
@@ -12,9 +12,10 @@ based debuggers. Visit its wiki at the Google Code site for more information.
Sample Usage
============
1. Put jar file in the base directory of Elgg
- 2. Run the server: java -jar JsTestDriver-1.3.3d.jar --port 4224
+ 2. Run the server: java -jar JsTestDriver-1.3.5.jar --port 4224
3. Point a web browser at http://localhost:4224
- 4. Run the tests: java -jar JsTestDriver-1.3.3d.jar --config js/tests/jsTestDriver.conf --basePath . --tests all
+ 4. Click "Capture this browser"
+ 5. Run the tests: java -jar JsTestDriver-1.3.5.jar --config js/tests/jsTestDriver.conf --basePath . --tests all
Configuration Hints
diff --git a/languages/en.php b/languages/en.php
index fe450b8a2..07407d1e1 100644
--- a/languages/en.php
+++ b/languages/en.php
@@ -105,6 +105,8 @@ $english = array(
'ElggPlugin:Dependencies:Priority:Before' => 'Before %s',
'ElggPlugin:Dependencies:Priority:Uninstalled' => '%s is not installed',
'ElggPlugin:Dependencies:Suggests:Unsatisfied' => 'Missing',
+
+ 'ElggPlugin:Dependencies:ActiveDependent' => 'There are other plugins that list %s as a dependency. You must disable the following plugins before disabling this one: %s',
'ElggPlugin:InvalidAndDeactivated' => '%s is an invalid plugin and has been deactivated.',
@@ -175,7 +177,7 @@ $english = array(
'ConfigurationException:NoSiteID' => "No site ID has been specified.",
'SecurityException:APIAccessDenied' => "Sorry, API access has been disabled by the administrator.",
'SecurityException:NoAuthMethods' => "No authentication methods were found that could authenticate this API request.",
- 'SecurityException:ForwardFailedToRedirect' => 'Redirect could not be issued due to headers already being sent. Halting execution for security. Search http://docs.elgg.org/ for more information.',
+ 'SecurityException:ForwardFailedToRedirect' => 'Redirect could not be issued due to headers already being sent. Halting execution for security. Output started in file %s at line %d. Search http://docs.elgg.org/ for more information.',
'InvalidParameterException:APIMethodOrFunctionNotSet' => "Method or function not set in call in expose_method()",
'InvalidParameterException:APIParametersArrayStructure' => "Parameters array structure is incorrect for call to expose method '%s'",
'InvalidParameterException:UnrecognisedHttpMethod' => "Unrecognised http method %s for api method '%s'",
@@ -359,6 +361,7 @@ $english = array(
'friendspicker:chararray' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'avatar' => 'Avatar',
+ 'avatar:noaccess' => "You're not allowed to edit this user's avatar",
'avatar:create' => 'Create your avatar',
'avatar:edit' => 'Edit avatar',
'avatar:preview' => 'Preview',
@@ -594,10 +597,24 @@ $english = array(
'admin:settings' => 'Settings',
'admin:settings:basic' => 'Basic Settings',
'admin:settings:advanced' => 'Advanced Settings',
+ 'admin:settings:advanced/site_secret' => 'Site Secret',
'admin:site:description' => "This admin panel allows you to control global settings for your site. Choose an option below to get started.",
+ 'admin:settings:advanced:site_secret' => 'Site Secret',
'admin:site:opt:linktext' => "Configure site...",
'admin:site:access:warning' => "Changing the access setting only affects the permissions on content created in the future.",
+ 'admin:site:secret:intro' => 'Elgg uses a key to create security tokens for various purposes.',
+ 'admin:site:secret_regenerated' => "Your site secret has been regenerated.",
+ 'admin:site:secret:regenerate' => "Regenerate site secret",
+ 'admin:site:secret:regenerate:help' => "Note: This may inconvenience some users by invalidating tokens used in \"remember me\" cookies, e-mail validation requests, invitation codes, etc.",
+ 'site_secret:current_strength' => 'Key Strength',
+ 'site_secret:strength:weak' => "Weak",
+ 'site_secret:strength_msg:weak' => "We strongly recommend that you regenerate your site secret.",
+ 'site_secret:strength:moderate' => "Moderate",
+ 'site_secret:strength_msg:moderate' => "We recommend you regenerate your site secret for the best site security.",
+ 'site_secret:strength:strong' => "Strong",
+ 'site_secret:strength_msg:strong' => "&#x2713; Your site secret is sufficiently strong.",
+
'admin:dashboard' => 'Dashboard',
'admin:widget:online_users' => 'Online users',
'admin:widget:online_users:help' => 'Lists the users currently on the site',
@@ -902,6 +919,7 @@ $english = array(
'total' => 'Total',
'learnmore' => "Click here to learn more.",
+ 'unknown_error' => 'Unknown error',
'content' => "content",
'content:latest' => 'Latest activity',
@@ -1060,7 +1078,7 @@ Once you have logged in, we highly recommend that you change your password.
'upgrade:unlock' => 'Unlock upgrade',
'upgrade:unlock:confirm' => "The database is locked for another upgrade. Running concurrent upgrades is dangerous. You should only continue if you know there is not another upgrade running. Unlock?",
'upgrade:locked' => "Cannot upgrade. Another upgrade is running. To clear the upgrade lock, visit the Admin section.",
- 'upgrade:unlock:success' => "Upgrade unlocked suscessfully.",
+ 'upgrade:unlock:success' => "Upgrade unlocked successfully.",
'upgrade:unable_to_upgrade' => 'Unable to upgrade.',
'upgrade:unable_to_upgrade_info' =>
'This installation cannot be upgraded because legacy views
@@ -1075,6 +1093,8 @@ Once you have logged in, we highly recommend that you change your password.
'update:twitter_api:deactivated' => 'Twitter API (previously Twitter Service) was deactivated during the upgrade. Please activate it manually if required.',
'update:oauth_api:deactivated' => 'OAuth API (previously OAuth Lib) was deactivated during the upgrade. Please activate it manually if required.',
+ 'upgrade:site_secret_warning:moderate' => "You are encouraged to regenerate your site key to improve system security. See Configure &gt; Settings &gt; Site Secret",
+ 'upgrade:site_secret_warning:weak' => "You are strongly encouraged to regenerate your site key to improve system security. See Configure &gt; Settings &gt; Site Secret",
'deprecated:function' => '%s() was deprecated by %s()',
@@ -1193,6 +1213,7 @@ You cannot reply to this email.",
'actiongatekeeper:timeerror' => 'The page you were using has expired. Please refresh and try again.',
'actiongatekeeper:pluginprevents' => 'A extension has prevented this form from being submitted.',
'actiongatekeeper:uploadexceeded' => 'The size of file(s) uploaded exceeded the limit set by your site administrator',
+ 'actiongatekeeper:crosssitelogin' => "Sorry, logging in from a different domain is not permitted. Please try again.",
/**
diff --git a/mod/blog/actions/blog/save.php b/mod/blog/actions/blog/save.php
index 9256610cc..82a9e6c51 100644
--- a/mod/blog/actions/blog/save.php
+++ b/mod/blog/actions/blog/save.php
@@ -79,11 +79,7 @@ foreach ($values as $name => $default) {
switch ($name) {
case 'tags':
- if ($value) {
- $values[$name] = string_to_tag_array($value);
- } else {
- unset ($values[$name]);
- }
+ $values[$name] = string_to_tag_array($value);
break;
case 'excerpt':
@@ -125,10 +121,7 @@ if ($values['status'] == 'draft') {
// assign values to the entity, stopping on error.
if (!$error) {
foreach ($values as $name => $value) {
- if (FALSE === ($blog->$name = $value)) {
- $error = elgg_echo('blog:error:cannot_save' . "$name=$value");
- break;
- }
+ $blog->$name = $value;
}
}
diff --git a/mod/blog/start.php b/mod/blog/start.php
index 25cd81935..e724b91c2 100644
--- a/mod/blog/start.php
+++ b/mod/blog/start.php
@@ -113,14 +113,23 @@ function blog_page_handler($page) {
switch ($page_type) {
case 'owner':
$user = get_user_by_username($page[1]);
+ if (!$user) {
+ forward('', '404');
+ }
$params = blog_get_page_content_list($user->guid);
break;
case 'friends':
$user = get_user_by_username($page[1]);
+ if (!$user) {
+ forward('', '404');
+ }
$params = blog_get_page_content_friends($user->guid);
break;
case 'archive':
$user = get_user_by_username($page[1]);
+ if (!$user) {
+ forward('', '404');
+ }
$params = blog_get_page_content_archive($user->guid, $page[2], $page[3]);
break;
case 'view':
@@ -139,7 +148,11 @@ function blog_page_handler($page) {
$params = blog_get_page_content_edit($page_type, $page[1], $page[2]);
break;
case 'group':
- if ($page[2] == 'all') {
+ $group = get_entity($page[1]);
+ if (!elgg_instanceof($group, 'group')) {
+ forward('', '404');
+ }
+ if (!isset($page[2]) || $page[2] == 'all') {
$params = blog_get_page_content_list($page[1]);
} else {
$params = blog_get_page_content_archive($page[1], $page[3], $page[4]);
diff --git a/mod/blog/views/default/blog/sidebar/archives.php b/mod/blog/views/default/blog/sidebar/archives.php
index 030261094..6529887d4 100644
--- a/mod/blog/views/default/blog/sidebar/archives.php
+++ b/mod/blog/views/default/blog/sidebar/archives.php
@@ -17,8 +17,8 @@ if (elgg_instanceof($page_owner, 'user')) {
// This is a limitation of the URL schema.
if ($page_owner && $vars['page'] != 'friends') {
- $dates = get_entity_dates('object', 'blog', $page_owner->getGUID());
-
+ $dates = array_reverse(get_entity_dates('object', 'blog', $page_owner->getGUID()));
+
if ($dates) {
$title = elgg_echo('blog:archives');
$content = '<ul class="blog-archives">';
diff --git a/mod/blog/views/default/forms/blog/save.php b/mod/blog/views/default/forms/blog/save.php
index 36fa2e0e8..f825acca1 100644
--- a/mod/blog/views/default/forms/blog/save.php
+++ b/mod/blog/views/default/forms/blog/save.php
@@ -10,7 +10,7 @@ $vars['entity'] = $blog;
$draft_warning = $vars['draft_warning'];
if ($draft_warning) {
- $draft_warning = '<span class="message warning">' . $draft_warning . '</span>';
+ $draft_warning = '<span class="mbm elgg-text-help">' . $draft_warning . '</span>';
}
$action_buttons = '';
diff --git a/mod/blog/views/default/river/object/blog/create.php b/mod/blog/views/default/river/object/blog/create.php
index a054c1061..b808f1bdc 100644
--- a/mod/blog/views/default/river/object/blog/create.php
+++ b/mod/blog/views/default/river/object/blog/create.php
@@ -4,10 +4,12 @@
*/
$object = $vars['item']->getObjectEntity();
-$excerpt = strip_tags($object->excerpt);
+
+$excerpt = $object->excerpt ? $object->excerpt : $object->description;
+$excerpt = strip_tags($excerpt);
$excerpt = elgg_get_excerpt($excerpt);
echo elgg_view('river/elements/layout', array(
'item' => $vars['item'],
'message' => $excerpt,
-)); \ No newline at end of file
+));
diff --git a/mod/bookmarks/languages/en.php b/mod/bookmarks/languages/en.php
index d4980280d..970b39415 100644
--- a/mod/bookmarks/languages/en.php
+++ b/mod/bookmarks/languages/en.php
@@ -9,7 +9,7 @@ $english = array(
* Menu items and titles
*/
'bookmarks' => "Bookmarks",
- 'bookmarks:add' => "Add bookmark",
+ 'bookmarks:add' => "Add a bookmark",
'bookmarks:edit' => "Edit bookmark",
'bookmarks:owner' => "%s's bookmarks",
'bookmarks:friends' => "Friends' bookmarks",
diff --git a/mod/bookmarks/pages/bookmarks/all.php b/mod/bookmarks/pages/bookmarks/all.php
index bdb8fc793..5c6011ad9 100644
--- a/mod/bookmarks/pages/bookmarks/all.php
+++ b/mod/bookmarks/pages/bookmarks/all.php
@@ -13,9 +13,8 @@ elgg_register_title_button();
$content = elgg_list_entities(array(
'type' => 'object',
'subtype' => 'bookmarks',
- 'limit' => 10,
'full_view' => false,
- 'view_toggle_type' => false
+ 'view_toggle_type' => false,
));
if (!$content) {
diff --git a/mod/bookmarks/pages/bookmarks/friends.php b/mod/bookmarks/pages/bookmarks/friends.php
index 15b1da098..173996346 100644
--- a/mod/bookmarks/pages/bookmarks/friends.php
+++ b/mod/bookmarks/pages/bookmarks/friends.php
@@ -7,7 +7,7 @@
$page_owner = elgg_get_page_owner_entity();
if (!$page_owner) {
- forward('bookmarks/all');
+ forward('', '404');
}
elgg_push_breadcrumb($page_owner->name, "bookmarks/owner/$page_owner->username");
diff --git a/mod/bookmarks/pages/bookmarks/owner.php b/mod/bookmarks/pages/bookmarks/owner.php
index a024ff352..b7b907916 100644
--- a/mod/bookmarks/pages/bookmarks/owner.php
+++ b/mod/bookmarks/pages/bookmarks/owner.php
@@ -7,7 +7,7 @@
$page_owner = elgg_get_page_owner_entity();
if (!$page_owner) {
- forward('bookmarks/all');
+ forward('', '404');
}
elgg_push_breadcrumb($page_owner->name);
@@ -18,7 +18,6 @@ $content .= elgg_list_entities(array(
'type' => 'object',
'subtype' => 'bookmarks',
'container_guid' => $page_owner->guid,
- 'limit' => 10,
'full_view' => false,
'view_toggle_type' => false
));
diff --git a/mod/bookmarks/start.php b/mod/bookmarks/start.php
index 3846f5165..caea43587 100644
--- a/mod/bookmarks/start.php
+++ b/mod/bookmarks/start.php
@@ -56,6 +56,9 @@ function bookmarks_init() {
// Listen to notification events and supply a more useful message
elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'bookmarks_notify_message');
+ // Register bookmarks view for ecml parsing
+ elgg_register_plugin_hook_handler('get_views', 'ecml', 'bookmarks_ecml_views_hook');
+
// Register a URL handler for bookmarks
elgg_register_entity_url_handler('object', 'bookmarks', 'bookmark_url');
@@ -282,8 +285,11 @@ function bookmarks_page_menu($hook, $type, $return, $params) {
if (!$page_owner) {
$page_owner = elgg_get_logged_in_user_entity();
}
-
+
if ($page_owner instanceof ElggGroup) {
+ if (!$page_owner->isMember()) {
+ return $return;
+ }
$title = elgg_echo('bookmarks:bookmarklet:group');
} else {
$title = elgg_echo('bookmarks:bookmarklet');
@@ -295,3 +301,16 @@ function bookmarks_page_menu($hook, $type, $return, $params) {
return $return;
}
+
+/**
+ * Return bookmarks views to parse for ecml
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $return
+ * @param array $params
+ */
+function bookmarks_ecml_views_hook($hook, $type, $return, $params) {
+ $return['object/bookmarks'] = elgg_echo('item:object:bookmarks');
+ return $return;
+}
diff --git a/mod/developers/languages/en.php b/mod/developers/languages/en.php
index 856efe008..266b5406e 100644
--- a/mod/developers/languages/en.php
+++ b/mod/developers/languages/en.php
@@ -28,7 +28,8 @@ $english = array(
'developers:label:show_strings' => "Show raw translation strings",
'developers:help:show_strings' => "This displays the translation strings used by elgg_echo().",
'developers:label:wrap_views' => "Wrap views",
- 'developers:help:wrap_views' => "This wraps almost every view with HTML comments. Useful for finding the view creating particular HTML.",
+ 'developers:help:wrap_views' => "This wraps almost every view with HTML comments. Useful for finding the view creating particular HTML.
+ This can break non-HTML views in the default viewtype. See developers_wrap_views() for details.",
'developers:label:log_events' => "Log events and plugin hooks",
'developers:help:log_events' => "Write events and plugin hooks to the log. Warning: there are many of these per page.",
diff --git a/mod/developers/manifest.xml b/mod/developers/manifest.xml
index e31998872..23e726e2b 100644
--- a/mod/developers/manifest.xml
+++ b/mod/developers/manifest.xml
@@ -8,7 +8,7 @@
<blurb>Developer tools for Elgg</blurb>
<description>A set of tools for writing plugins and themes. It is recommended that you have this plugin at the top of the plugin list.</description>
<website>http://www.elgg.org/</website>
- <bugtracker>http://trac.elgg.org</bugtracker>
+ <bugtracker>https://github.com/Elgg/Elgg/issues</bugtracker>
<copyright>See COPYRIGHT.txt</copyright>
<license>GNU General Public License version 2</license>
diff --git a/mod/developers/start.php b/mod/developers/start.php
index 413a8ed9b..94d0f652c 100644
--- a/mod/developers/start.php
+++ b/mod/developers/start.php
@@ -89,6 +89,15 @@ function developers_clear_strings() {
/**
* Post-process a view to add wrapper comments to it
+ *
+ * 1. Only process views served with the 'default' viewtype.
+ * 2. Does not wrap views that begin with js/ or css/ as they are not HTML.
+ * 3. Does not wrap views that are images (start with icon/). Is this still true?
+ * 4. Does not wrap input and output views (why?).
+ * 5. Does not wrap html head or the primary page shells
+ *
+ * @warning this will break views in the default viewtype that return non-HTML data
+ * that do not match the above restrictions.
*/
function developers_wrap_views($hook, $type, $result, $params) {
if (elgg_get_viewtype() != "default") {
diff --git a/mod/embed/start.php b/mod/embed/start.php
index e8a3f8c14..1da35aa46 100644
--- a/mod/embed/start.php
+++ b/mod/embed/start.php
@@ -13,14 +13,19 @@ elgg_register_event_handler('init', 'system', 'embed_init');
*/
function embed_init() {
elgg_extend_view('css/elgg', 'embed/css');
-
- elgg_register_plugin_hook_handler('register', 'menu:longtext', 'embed_longtext_menu');
+ elgg_extend_view('css/admin', 'embed/css');
+
+ if (elgg_is_logged_in()) {
+ elgg_register_plugin_hook_handler('register', 'menu:longtext', 'embed_longtext_menu');
+ }
elgg_register_plugin_hook_handler('register', 'menu:embed', 'embed_select_tab', 1000);
// Page handler for the modal media embed
elgg_register_page_handler('embed', 'embed_page_handler');
- elgg_register_js('elgg.embed', 'js/embed/embed.js', 'footer');
+ $embed_js = elgg_get_simplecache_url('js', 'embed/embed');
+ elgg_register_simplecache_view('js/embed/embed');
+ elgg_register_js('elgg.embed', $embed_js, 'footer');
}
/**
@@ -39,10 +44,12 @@ function embed_longtext_menu($hook, $type, $items, $vars) {
}
$url = 'embed';
- if (elgg_get_page_owner_guid()) {
- $url = 'embed?container_guid=' . elgg_get_page_owner_guid();
+
+ $page_owner = elgg_get_page_owner_entity();
+ if (elgg_instanceof($page_owner, 'group') && $page_owner->isMember()) {
+ $url = 'embed?container_guid=' . $page_owner->getGUID();
}
-
+
$items[] = ElggMenuItem::factory(array(
'name' => 'embed',
'href' => $url,
@@ -95,7 +102,12 @@ function embed_page_handler($page) {
$container_guid = (int)get_input('container_guid');
if ($container_guid) {
- elgg_set_page_owner_guid($container_guid);
+ $container = get_entity($container_guid);
+
+ if (elgg_instanceof($container, 'group') && $container->isMember()) {
+ // embedding inside a group so save file to group files
+ elgg_set_page_owner_guid($container_guid);
+ }
}
echo elgg_view('embed/layout');
diff --git a/mod/externalpages/start.php b/mod/externalpages/start.php
index 74da7f828..f0ffa6b9d 100644
--- a/mod/externalpages/start.php
+++ b/mod/externalpages/start.php
@@ -12,7 +12,7 @@ function expages_init() {
elgg_register_page_handler('terms', 'expages_page_handler');
elgg_register_page_handler('privacy', 'expages_page_handler');
elgg_register_page_handler('expages', 'expages_page_handler');
-
+
// Register public external pages
elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'expages_public');
@@ -65,7 +65,7 @@ function expages_page_handler($page, $handler) {
$type = strtolower($handler);
$title = elgg_echo("expages:$type");
- $content = elgg_view_title($title);
+ $header = elgg_view_title($title);
$object = elgg_get_entities(array(
'type' => 'object',
@@ -80,11 +80,11 @@ function expages_page_handler($page, $handler) {
$content = elgg_view('expages/wrapper', array('content' => $content));
if (elgg_is_logged_in() || !elgg_get_config('walled_garden')) {
- $body = elgg_view_layout('one_sidebar', array('content' => $content));
+ $body = elgg_view_layout('one_sidebar', array('title' => $title, 'content' => $content));
echo elgg_view_page($title, $body);
} else {
elgg_load_css('elgg.walled_garden');
- $body = elgg_view_layout('walled_garden', array('content' => $content));
+ $body = elgg_view_layout('walled_garden', array('content' => $header . $content));
echo elgg_view_page($title, $body, 'walled_garden');
}
return true;
diff --git a/mod/file/actions/file/upload.php b/mod/file/actions/file/upload.php
index d6dce2528..e20c4079f 100644
--- a/mod/file/actions/file/upload.php
+++ b/mod/file/actions/file/upload.php
@@ -71,9 +71,7 @@ $file->title = $title;
$file->description = $desc;
$file->access_id = $access_id;
$file->container_guid = $container_guid;
-
-$tags = explode(",", $tags);
-$file->tags = $tags;
+$file->tags = string_to_tag_array($tags);
// we have a file upload, so process it
if (isset($_FILES['upload']['name']) && !empty($_FILES['upload']['name'])) {
@@ -167,6 +165,23 @@ if (isset($_FILES['upload']['name']) && !empty($_FILES['upload']['name'])) {
$file->largethumb = $prefix."largethumb".$filestorename;
unset($thumblarge);
}
+ } elseif ($file->icontime) {
+ // if it is not an image, we do not need thumbnails
+ unset($file->icontime);
+
+ $thumb = new ElggFile();
+
+ $thumb->setFilename($prefix . "thumb" . $filestorename);
+ $thumb->delete();
+ unset($file->thumbnail);
+
+ $thumb->setFilename($prefix . "smallthumb" . $filestorename);
+ $thumb->delete();
+ unset($file->smallthumb);
+
+ $thumb->setFilename($prefix . "largethumb" . $filestorename);
+ $thumb->delete();
+ unset($file->largethumb);
}
} else {
// not saving a file but still need to save the entity to push attributes to database
@@ -204,4 +219,4 @@ if ($new_file) {
}
forward($file->getURL());
-}
+}
diff --git a/mod/file/pages/file/friends.php b/mod/file/pages/file/friends.php
index f504bdc1f..d55c1e62b 100644
--- a/mod/file/pages/file/friends.php
+++ b/mod/file/pages/file/friends.php
@@ -7,7 +7,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('file/all');
+ forward('', '404');
}
elgg_push_breadcrumb(elgg_echo('file'), "file/all");
diff --git a/mod/file/pages/file/owner.php b/mod/file/pages/file/owner.php
index d7f057f2a..99cf62714 100644
--- a/mod/file/pages/file/owner.php
+++ b/mod/file/pages/file/owner.php
@@ -10,7 +10,7 @@ group_gatekeeper();
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('file/all');
+ forward('', '404');
}
elgg_push_breadcrumb(elgg_echo('file'), "file/all");
@@ -39,7 +39,6 @@ $content = elgg_list_entities(array(
'type' => 'object',
'subtype' => 'file',
'container_guid' => $owner->guid,
- 'limit' => 10,
'full_view' => FALSE,
));
if (!$content) {
diff --git a/mod/file/pages/file/world.php b/mod/file/pages/file/world.php
index 8e6c87f26..96c8de785 100644
--- a/mod/file/pages/file/world.php
+++ b/mod/file/pages/file/world.php
@@ -9,14 +9,11 @@ elgg_push_breadcrumb(elgg_echo('file'));
elgg_register_title_button();
-$limit = get_input("limit", 10);
-
$title = elgg_echo('file:all');
$content = elgg_list_entities(array(
'type' => 'object',
'subtype' => 'file',
- 'limit' => $limit,
'full_view' => FALSE
));
if (!$content) {
diff --git a/mod/groups/actions/groups/membership/invite.php b/mod/groups/actions/groups/membership/invite.php
index db90ecf3a..a96165b0e 100644
--- a/mod/groups/actions/groups/membership/invite.php
+++ b/mod/groups/actions/groups/membership/invite.php
@@ -7,43 +7,48 @@
$logged_in_user = elgg_get_logged_in_user_entity();
-$user_guid = get_input('user_guid');
-if (!is_array($user_guid)) {
- $user_guid = array($user_guid);
+$user_guids = get_input('user_guid');
+if (!is_array($user_guids)) {
+ $user_guids = array($user_guids);
}
$group_guid = get_input('group_guid');
+$group = get_entity($group_guid);
-if (sizeof($user_guid)) {
- foreach ($user_guid as $u_id) {
- $user = get_entity($u_id);
- $group = get_entity($group_guid);
-
- if ($user && $group && ($group instanceof ElggGroup) && $group->canEdit()) {
-
- if (!check_entity_relationship($group->guid, 'invited', $user->guid)) {
-
- // Create relationship
- add_entity_relationship($group->guid, 'invited', $user->guid);
-
- // Send email
- $url = elgg_normalize_url("groups/invitations/$user->username");
- $result = notify_user($user->getGUID(), $group->owner_guid,
- elgg_echo('groups:invite:subject', array($user->name, $group->name)),
- elgg_echo('groups:invite:body', array(
- $user->name,
- $logged_in_user->name,
- $group->name,
- $url,
- )),
- NULL);
- if ($result) {
- system_message(elgg_echo("groups:userinvited"));
- } else {
- register_error(elgg_echo("groups:usernotinvited"));
- }
- } else {
- register_error(elgg_echo("groups:useralreadyinvited"));
- }
+if (count($user_guids) > 0 && elgg_instanceof($group, 'group') && $group->canEdit()) {
+ foreach ($user_guids as $guid) {
+ $user = get_user($guid);
+ if (!$user) {
+ continue;
+ }
+
+ if (check_entity_relationship($group->guid, 'invited', $user->guid)) {
+ register_error(elgg_echo("groups:useralreadyinvited"));
+ continue;
+ }
+
+ if (check_entity_relationship($user->guid, 'member', $group->guid)) {
+ // @todo add error message
+ continue;
+ }
+
+ // Create relationship
+ add_entity_relationship($group->guid, 'invited', $user->guid);
+
+ // Send notification
+ $url = elgg_normalize_url("groups/invitations/$user->username");
+ $result = notify_user($user->getGUID(), $group->owner_guid,
+ elgg_echo('groups:invite:subject', array($user->name, $group->name)),
+ elgg_echo('groups:invite:body', array(
+ $user->name,
+ $logged_in_user->name,
+ $group->name,
+ $url,
+ )),
+ NULL);
+ if ($result) {
+ system_message(elgg_echo("groups:userinvited"));
+ } else {
+ register_error(elgg_echo("groups:usernotinvited"));
}
}
}
diff --git a/mod/groups/lib/discussion.php b/mod/groups/lib/discussion.php
index ab2fe4849..874e21b2d 100644
--- a/mod/groups/lib/discussion.php
+++ b/mod/groups/lib/discussion.php
@@ -39,9 +39,8 @@ function discussion_handle_list_page($guid) {
elgg_set_page_owner_guid($guid);
$group = get_entity($guid);
- if (!$group) {
- register_error(elgg_echo('group:notfound'));
- forward();
+ if (!elgg_instanceof($group, 'group')) {
+ forward('', '404');
}
elgg_push_breadcrumb($group->name);
diff --git a/mod/groups/lib/groups.php b/mod/groups/lib/groups.php
index 0557d41eb..d5bec1862 100644
--- a/mod/groups/lib/groups.php
+++ b/mod/groups/lib/groups.php
@@ -55,7 +55,7 @@ function groups_handle_all_page() {
}
$filter = elgg_view('groups/group_sort_menu', array('selected' => $selected_tab));
-
+
$sidebar = elgg_view('groups/sidebar/find');
$sidebar .= elgg_view('groups/sidebar/featured');
@@ -73,7 +73,8 @@ function groups_search_page() {
elgg_push_breadcrumb(elgg_echo('search'));
$tag = get_input("tag");
- $title = elgg_echo('groups:search:title', array($tag));
+ $display_query = _elgg_get_display_query($tag);
+ $title = elgg_echo('groups:search:title', array($display_query));
// groups plugin saves tags as "interests" - see groups_fields_setup() in start.php
$params = array(
@@ -115,7 +116,9 @@ function groups_handle_owned_page() {
}
elgg_push_breadcrumb($title);
- elgg_register_title_button();
+ if (elgg_get_plugin_setting('limited_groups', 'groups') != 'yes' || elgg_is_admin_logged_in()) {
+ elgg_register_title_button();
+ }
$content = elgg_list_entities(array(
'type' => 'group',
@@ -150,7 +153,9 @@ function groups_handle_mine_page() {
}
elgg_push_breadcrumb($title);
- elgg_register_title_button();
+ if (elgg_get_plugin_setting('limited_groups', 'groups') != 'yes' || elgg_is_admin_logged_in()) {
+ elgg_register_title_button();
+ }
$content = elgg_list_entities_from_relationship(array(
'type' => 'group',
@@ -181,7 +186,7 @@ function groups_handle_mine_page() {
*/
function groups_handle_edit_page($page, $guid = 0) {
gatekeeper();
-
+
if ($page == 'add') {
elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
$title = elgg_echo('groups:add');
@@ -204,7 +209,7 @@ function groups_handle_edit_page($page, $guid = 0) {
$content = elgg_echo('groups:noaccess');
}
}
-
+
$params = array(
'content' => $content,
'title' => $title,
@@ -255,8 +260,8 @@ function groups_handle_profile_page($guid) {
elgg_push_context('group_profile');
$group = get_entity($guid);
- if (!$group) {
- forward('groups/all');
+ if (!elgg_instanceof($group, 'group')) {
+ forward('', '404');
}
elgg_push_breadcrumb($group->name);
@@ -266,7 +271,7 @@ function groups_handle_profile_page($guid) {
$content = elgg_view('groups/profile/layout', array('entity' => $group));
$sidebar = '';
- if (group_gatekeeper(false)) {
+ if (group_gatekeeper(false)) {
if (elgg_is_active_plugin('search')) {
$sidebar .= elgg_view('groups/sidebar/search', array('entity' => $group));
}
@@ -275,18 +280,18 @@ function groups_handle_profile_page($guid) {
$subscribed = false;
if (elgg_is_active_plugin('notifications')) {
global $NOTIFICATION_HANDLERS;
-
+
foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
$relationship = check_entity_relationship(elgg_get_logged_in_user_guid(),
'notify' . $method, $guid);
-
+
if ($relationship) {
$subscribed = true;
break;
}
}
}
-
+
$sidebar .= elgg_view('groups/sidebar/my_status', array(
'entity' => $group,
'subscribed' => $subscribed
@@ -334,7 +339,7 @@ function groups_handle_activity_page($guid) {
if (!$content) {
$content = '<p>' . elgg_echo('groups:activity:none') . '</p>';
}
-
+
$params = array(
'content' => $content,
'title' => $title,
@@ -366,12 +371,15 @@ function groups_handle_members_page($guid) {
elgg_push_breadcrumb($group->name, $group->getURL());
elgg_push_breadcrumb(elgg_echo('groups:members'));
+ $db_prefix = elgg_get_config('dbprefix');
$content = elgg_list_entities_from_relationship(array(
'relationship' => 'member',
'relationship_guid' => $group->guid,
'inverse_relationship' => true,
'type' => 'user',
'limit' => 20,
+ 'joins' => array("JOIN {$db_prefix}users_entity u ON e.guid=u.guid"),
+ 'order_by' => 'u.name ASC',
));
$params = array(
@@ -424,7 +432,7 @@ function groups_handle_invite_page($guid) {
/**
* Manage requests to join a group
- *
+ *
* @param int $guid Group entity GUID
*/
function groups_handle_requests_page($guid) {
@@ -440,7 +448,7 @@ function groups_handle_requests_page($guid) {
if ($group && $group->canEdit()) {
elgg_push_breadcrumb($group->name, $group->getURL());
elgg_push_breadcrumb($title);
-
+
$requests = elgg_get_entities_from_relationship(array(
'type' => 'user',
'relationship' => 'membership_request',
diff --git a/mod/groups/start.php b/mod/groups/start.php
index 46ab0e636..6002a535c 100644
--- a/mod/groups/start.php
+++ b/mod/groups/start.php
@@ -142,6 +142,10 @@ function groups_setup_sidebar_menus() {
$page_owner = elgg_get_page_owner_entity();
if (elgg_in_context('group_profile')) {
+ if (!elgg_instanceof($page_owner, 'group')) {
+ forward('', '404');
+ }
+
if (elgg_is_logged_in() && $page_owner->canEdit() && !$page_owner->isPublicMembership()) {
$url = elgg_get_site_url() . "groups/requests/{$page_owner->getGUID()}";
diff --git a/mod/groups/views/default/groups/sidebar/my_status.php b/mod/groups/views/default/groups/sidebar/my_status.php
index 5951cbd28..1e4e84b80 100644
--- a/mod/groups/views/default/groups/sidebar/my_status.php
+++ b/mod/groups/views/default/groups/sidebar/my_status.php
@@ -41,7 +41,7 @@ if ($is_owner) {
}
// notification info
-if (elgg_is_active_plugin('notifications')) {
+if (elgg_is_active_plugin('notifications') && $is_member) {
if ($subscribed) {
elgg_register_menu_item('groups:my_status', array(
'name' => 'subscription_status',
diff --git a/mod/groups/views/default/object/groupforumtopic.php b/mod/groups/views/default/object/groupforumtopic.php
index 34e0ee3cc..e6988d16e 100644
--- a/mod/groups/views/default/object/groupforumtopic.php
+++ b/mod/groups/views/default/object/groupforumtopic.php
@@ -73,7 +73,10 @@ if ($full) {
$info = elgg_view_image_block($poster_icon, $list_body);
- $body = elgg_view('output/longtext', array('value' => $topic->description));
+ $body = elgg_view('output/longtext', array(
+ 'value' => $topic->description,
+ 'class' => 'clearfix',
+ ));
echo <<<HTML
$info
diff --git a/mod/htmlawed/start.php b/mod/htmlawed/start.php
index 12b6470a3..25a70a4aa 100644
--- a/mod/htmlawed/start.php
+++ b/mod/htmlawed/start.php
@@ -156,10 +156,8 @@ function htmlawed_tag_post_processor($element, $attributes = false) {
* Runs unit tests for htmlawed
*
* @return array
- * */
+ */
function htmlawed_test($hook, $type, $value, $params) {
- global $CONFIG;
-
$value[] = dirname(__FILE__) . '/tests/tags.php';
return $value;
}
diff --git a/mod/htmlawed/tests/tags.php b/mod/htmlawed/tests/tags.php
index b3914a9d6..05fe829f4 100644
--- a/mod/htmlawed/tests/tags.php
+++ b/mod/htmlawed/tests/tags.php
@@ -1,45 +1,47 @@
<?php
+
/**
* Dupplicated tags in htmlawed
*/
class HtmLawedDuplicateTagsTest extends ElggCoreUnitTest {
- /**
- * Called before each test object.
- */
- public function __construct() {
- parent::__construct();
- }
-
- /**
- * Called before each test method.
- */
- public function setUp() {
- }
-
- /**
- * Called after each test method.
- */
- public function tearDown() {
- // do not allow SimpleTest to interpret Elgg notices as exceptions
- $this->swallowErrors();
- }
-
- /**
- * Called after each test object.
- */
- public function __destruct() {
- elgg_set_ignore_access($this->ia);
- // all __destruct() code should go above here
- parent::__destruct();
- }
-
- public function testNotDuplicateTags() {
- $filter_html = '<ul><li>item</li></ul>';
- set_input('test', $filter_html);
-
- $expected = $filter_html;
- $result = get_input('test');
- $this->assertEqual($result, $expected);
- }
+ /**
+ * Called before each test object.
+ */
+ public function __construct() {
+ parent::__construct();
+ }
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ // do not allow SimpleTest to interpret Elgg notices as exceptions
+ $this->swallowErrors();
+ }
+
+ /**
+ * Called after each test object.
+ */
+ public function __destruct() {
+ // all __destruct() code should go above here
+ parent::__destruct();
+ }
+
+ public function testNotDuplicateTags() {
+ $filter_html = '<ul><li>item</li></ul>';
+ set_input('test', $filter_html);
+
+ $expected = $filter_html;
+ $result = get_input('test');
+ $this->assertEqual($result, $expected);
+ }
+
} \ No newline at end of file
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed.php b/mod/htmlawed/vendors/htmLawed/htmLawed.php
index 0d9624961..63f8c4162 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed.php
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed.php
@@ -1,9 +1,9 @@
<?php
/*
-htmLawed 1.1.11, 5 June 2012
+htmLawed 1.1.16, 29 August 2013
Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
+Dual licensed with LGPL 3 and GPL 2+
A PHP Labware internal utility; www.bioinformatics.org/phplabware/internal_utilities/htmLawed
See htmLawed_README.txt/htm
@@ -194,7 +194,10 @@ for($i=-1, $ci=count($t); ++$i<$ci;){
echo '&lt;', $s, $e, $a, '&gt;';
}
if(isset($x[0])){
- if($do < 3 or isset($ok['#pcdata'])){echo $x;}
+ if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){
+ echo '<div>', $x, '</div>';
+ }
+ elseif($do < 3 or isset($ok['#pcdata'])){echo $x;}
elseif(strpos($x, "\x02\x04")){
foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){
echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
@@ -202,7 +205,7 @@ for($i=-1, $ci=count($t); ++$i<$ci;){
}elseif($do > 4){echo preg_replace('`\S`', '', $x);}
}
// get markup
- if(!preg_match('`^(/?)([a-zA-Z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
+ if(!preg_match('`^(/?)([a-z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
$s = null; $e = null; $a = null; $x = null; list($all, $s, $e, $a, $x) = $r;
// close tag
if($s){
@@ -333,7 +336,7 @@ $c = isset($C['schemes'][$c]) ? $C['schemes'][$c] : $C['schemes']['*'];
static $d = 'denied:';
if(isset($c['!']) && substr($p, 0, 7) != $d){$p = "$d$p";}
if(isset($c['*']) or !strcspn($p, '#?;') or (substr($p, 0, 7) == $d)){return "{$b}{$p}{$a}";} // All ok, frag, query, param
-if(preg_match('`^([a-z\d\-+.&#; ]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
+if(preg_match('`^([^:?[@!$()*,=/\'\]]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
return "{$b}{$d}{$p}{$a}";
}
if($C['abs_url']){
@@ -488,7 +491,7 @@ global $S;
$rl = isset($S[$e]) ? $S[$e] : array();
$a = array(); $nfr = 0;
foreach($aA as $k=>$v){
- if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) or isset($rl[$k])) && ((!isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e])))){
+ if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e]))) && !isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])){
if(isset($aNE[$k])){$v = $k;}
elseif(!empty($lcase) && (($e != 'button' or $e != 'input') or $k == 'type')){ // Rather loose but ?not cause issues
$v = (isset($aNL[($v2 = strtolower($v))])) ? $v2 : $v;
@@ -622,7 +625,7 @@ if($e == 'u'){$e = 'span'; return 'text-decoration: underline;';}
static $fs = array('0'=>'xx-small', '1'=>'xx-small', '2'=>'small', '3'=>'medium', '4'=>'large', '5'=>'x-large', '6'=>'xx-large', '7'=>'300%', '-1'=>'smaller', '-2'=>'60%', '+1'=>'larger', '+2'=>'150%', '+3'=>'200%', '+4'=>'300%');
if($e == 'font'){
$a2 = '';
- if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=\s*([^"])(\S+)`i', $a, $m)){
+ if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=(\s*)(\S+)`i', $a, $m)){
$a2 .= ' font-family: '. str_replace('"', '\'', trim($m[2])). ';';
}
if(preg_match('`color\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m)){
@@ -641,41 +644,50 @@ return '';
function hl_tidy($t, $w, $p){
// Tidy/compact HTM
if(strpos(' pre,script,textarea', "$p,")){return $t;}
-$t = str_replace(' </', '</', preg_replace(array('`(<\w[^>]*(?<!/)>)\s+`', '`\s+`', '`(<\w[^>]*(?<!/)>) `'), array(' $1', ' ', '$1'), preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t)));
+$t = preg_replace('`\s+`', ' ', preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t));
if(($w = strtolower($w)) == -1){
return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t);
}
$s = strpos(" $w", 't') ? "\t" : ' ';
$s = preg_match('`\d`', $w, $m) ? str_repeat($s, $m[0]) : str_repeat($s, ($s == "\t" ? 1 : 2));
-$n = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
+$N = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
$a = array('br'=>1);
-$b = array('button'=>1, 'input'=>1, 'option'=>1);
+$b = array('button'=>1, 'input'=>1, 'option'=>1, 'param'=>1);
$c = array('caption'=>1, 'dd'=>1, 'dt'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'isindex'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'object'=>1, 'p'=>1, 'pre'=>1, 'td'=>1, 'textarea'=>1, 'th'=>1);
-$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
-ob_start();
-if(isset($d[$p])){echo str_repeat($s, ++$n);}
-$t = explode('<', $t);
-echo ltrim(array_shift($t));
-for($i=-1, $j=count($t); ++$i<$j;){
- $r = ''; list($e, $r) = explode('>', $t[$i]);
- $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
- $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
- $e = "<$e>";
- if(isset($d[$y])){
- if(!$x){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
- else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
- echo ltrim($r); continue;
+$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
+$T = explode('<', $t);
+$X = 1;
+while($X){
+ $n = $N;
+ $t = $T;
+ ob_start();
+ if(isset($d[$p])){echo str_repeat($s, ++$n);}
+ echo ltrim(array_shift($t));
+ for($i=-1, $j=count($t); ++$i<$j;){
+ $r = ''; list($e, $r) = explode('>', $t[$i]);
+ $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
+ $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
+ $e = "<$e>";
+ if(isset($d[$y])){
+ if(!$x){
+ if($n){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
+ else{++$N; ob_end_clean(); continue 2;}
+ }
+ else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
+ echo $r; continue;
+ }
+ $f = "\n". str_repeat($s, $n);
+ if(isset($c[$y])){
+ if(!$x){echo $e, $f, $r;}
+ else{echo $f, $e, $r;}
+ }elseif(isset($b[$y])){echo $f, $e, $r;
+ }elseif(isset($a[$y])){echo $e, $f, $r;
+ }elseif(!$y){echo $f, $e, $f, $r;
+ }else{echo $e, $r;}
}
- $f = "\n". str_repeat($s, $n);
- if(isset($c[$y])){
- if(!$x){echo $e, $f, ltrim($r);}
- else{echo $f, $e, $r;}
- }elseif(isset($b[$y])){echo $f, $e, $r;
- }elseif(isset($a[$y])){echo $e, $f, ltrim($r);
- }elseif(!$y){echo $f, $e, $f, ltrim($r);
- }else{echo $e, $r;}
-}
-$t = preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents());
+ $X = 0;
+}
+$t = str_replace(array("\n ", " \n"), "\n", preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents()));
ob_end_clean();
if(($l = strpos(" $w", 'r') ? (strpos(" $w", 'n') ? "\r\n" : "\r") : 0)){
$t = str_replace("\n", $l, $t);
@@ -686,7 +698,7 @@ return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array(
function hl_version(){
// rel
-return '1.1.11';
+return '1.1.16';
// eof
}
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawedTest.php b/mod/htmlawed/vendors/htmLawed/htmLawedTest.php
index 806aa4641..3a5b92155 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawedTest.php
+++ b/mod/htmlawed/vendors/htmLawed/htmLawedTest.php
@@ -1,10 +1,10 @@
<?php
/*
-htmLawedTest.php, 22 October 2011
-htmLawed 1.1.11, 5 June 2012
+htmLawedTest.php, 28 May 2013
+htmLawed 1.1.16, 29 August 2013
Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
+Dual licensed with LGPL 3 and GPL 2+
A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
Test htmLawed; user provides text input; input and processed input are shown as highlighted code and rendered HTML; also shown are execution time and peak memory usage
@@ -22,7 +22,7 @@ $_sid = 'sid'; // session name; alphanum.
$_slife = 30; // session life in min.
// errors
-error_reporting(E_ALL | (defined('E_STRICT') ? E_STRICT : 1));
+error_reporting(E_ALL | (defined('E_STRICT') ? E_STRICT : 0));
ini_set('display_errors', $_errs);
// session
@@ -144,15 +144,16 @@ body{background-color:#efefef;}
body, button, div, html, input, p{font-size:13px; font-family:'Lucida grande', Verdana, Arial, Helvetica, sans-serif;}
button, input{font-size: 85%;}
div.help{border-top: 1px dotted gray; margin-top: 15px; padding-top: 15px; color:#999999;}
-#inputC, #inputD, #inputF, #inputR, #outputD, #outputF, #outputH, #outputR, #settingF{display:block;}
+#inputC, #inputD, #inputF, #inputR, #outputD, #outputF, #outputH, #outputR, #settingF, #diff{display:block;}
#inputC, #settingF{background-color:white; border:1px gray solid; padding:3px;}
#inputC li{margin: 0; padding: 0;}
#inputC ul{margin: 0; padding: 0; margin-left: 14px;}
#inputC input{margin: 0; margin-left: 2px; margin-right: 2px; padding: 1px; vertical-align: middle;}
#inputD{overflow:auto; background-color:#ffff99; border:1px #cc9966 solid; padding:3px;}
#inputR{overflow:auto; background-color:#ffffcc; border:1px #ffcc99 solid; padding:3px;}
-#inputC, #settingF, #inputD, #inputR, #outputD, #outputR, textarea{font-size:100%; font-family:'Bitstream vera sans mono', 'courier new', 'courier', monospace;}
-#outputD{overflow:auto; background-color: #99ffcc; border:1px #66cc99 solid; padding:3px;}
+#inputC, #settingF, #inputD, #inputR, #outputD, #outputR, #diff, textarea{font-size:100%; font-family:'Bitstream vera sans mono', 'courier new', 'courier', monospace;}
+#outputD{overflow:auto; background-color: #99ffcc; border:1px #66cc99 solid; padding:3px;}
+#diff{overflow:auto; background-color: white; border:1px #dcdcdc solid; padding:3px;}
#outputH{overflow:auto; background-color:white; padding:3px; border:1px #dcdcdc solid;}
#outputR{overflow:auto; background-color: #ccffcc; border:1px #99cc99 solid; padding:3px;}
span.cmtcdata{color: orange;}
@@ -261,9 +262,6 @@ function sndUnproc(){
var i = document.getElementById('text');
if(!i){return;}
i = i.value;
- i = i.replace(/>/g, '&gt;');
- i = i.replace(/</g, '&lt;');
- i = i.replace(/"/g, '&quot;');
var w = window.open('htmLawedTest.php?pre=1', 'hlprehtm');
var f = document.createElement('form');
f.enctype = 'application/x-www-form-urlencoded';
@@ -271,10 +269,14 @@ function sndUnproc(){
f.acceptCharset = '<?php echo htmlspecialchars($_POST['enc']); ?>';
if(f.style){f.style.display = 'none';}
else{f.visibility = 'hidden';}
- f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="token" id="token" value="<?php echo $token; ?>" /><input style="display:none;" type="hidden" name="<?php echo htmlspecialchars($_sid); ?>" id="<?php echo htmlspecialchars($_sid); ?>" value="' + readCookie('<?php echo htmlspecialchars($_sid); ?>') + '" /><input style="display:none;" type="hidden" name="inputH" id="inputH" value="'+ i+ '" /></p>';
+ f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="token" id="token" value="<?php echo $token; ?>" /><input style="display:none;" type="hidden" name="<?php echo htmlspecialchars($_sid); ?>" id="<?php echo htmlspecialchars($_sid); ?>" value="' + readCookie('<?php echo htmlspecialchars($_sid); ?>') + '" /></p>';
f.action = 'htmLawedTest.php?pre=1';
f.target = 'hlprehtm';
f.method = 'post';
+ var t = document.createElement('textarea');
+ t.name = 'inputH';
+ t.value = i;
+ f.appendChild(t);
var b = document.getElementsByTagName('body')[0];
b.appendChild(f);
f.submit();
@@ -284,9 +286,6 @@ function sndValidn(id, type){
var i = document.getElementById(id);
if(!i){return;}
i = i.value;
- i = i.replace(/>/g, '&gt;');
- i = i.replace(/</g, '&lt;');
- i = i.replace(/"/g, '&quot;');
var w = window.open('http://validator.w3.org/check', 'validate'+id+type);
var f = document.createElement('form');
f.enctype = 'application/x-www-form-urlencoded';
@@ -294,9 +293,13 @@ function sndValidn(id, type){
f.acceptCharset = '<?php echo htmlspecialchars($_POST['enc']); ?>';
if(f.style){f.style.display = 'none';}
else{f.visibility = 'hidden';}
- f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="fragment" id="fragment" value="'+ i+ '" /><input style="display:none;" type="hidden" name="prefill" id="prefill" value="1" /><input style="display:none;" type="hidden" name="prefill_doctype" id="prefill_doctype" value="'+ type+ '" /><input style="display:none;" type="hidden" name="group" id="group" value="1" /><input type="hidden" name="ss" id="ss" value="1" /></p>';
+ f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="prefill" id="prefill" value="1" /><input style="display:none;" type="hidden" name="prefill_doctype" id="prefill_doctype" value="'+ type+ '" /><input style="display:none;" type="hidden" name="group" id="group" value="1" /><input type="hidden" name="ss" id="ss" value="1" /></p>';
f.action = 'http://validator.w3.org/check';
f.target = 'validate'+id+type;
+ var t = document.createElement('textarea');
+ t.name = 'fragment';
+ t.value = i;
+ f.appendChild(t);
var b = document.getElementsByTagName('body')[0];
b.appendChild(f);
f.submit();
@@ -376,6 +379,58 @@ tRs = {
}
};
tRs.adEv(window, 'load', tRs.adBtn);
+// Diff Match and Patch javascript code by Neil Fraser; Apache license 2.0; http://code.google.com/p/google-diff-match-patch/
+(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
+diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b),c=a.substring(0,f),a=a.substring(f),b=b.substring(f),f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f),a=a.substring(0,a.length-f),b=b.substring(0,b.length-f),a=this.diff_compute_(a,
+b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
+diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);if(-1!=g)return c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c;if(1==f.length)return[[-1,a],[1,b]];return(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,
+b,d):this.diff_bisect_(a,b,d)};
+diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b),a=d.chars1,b=d.chars2,d=d.lineArray,a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case 1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case 0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
+diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var k=d-e,p=0!=k%2,q=0,s=0,o=0,v=0,u=0;u<f&&!((new Date).getTime()>c);u++){for(var n=-u+q;n<=u-s;n+=2){var l=g+n,m;m=n==-u||n!=u&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var r=m-n;m<d&&r<e&&a.charAt(m)==b.charAt(r);)m++,r++;j[l]=m;if(m>d)s+=2;else if(r>e)q+=2;else if(p&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var t=d-i[l];if(m>=
+t)return this.diff_bisectSplit_(a,b,m,r,c)}}for(n=-u+o;n<=u-v;n+=2){l=g+n;t=n==-u||n!=u&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=t-n;t<d&&m<e&&a.charAt(d-t-1)==b.charAt(e-m-1);)t++,m++;i[l]=t;if(t>d)v+=2;else if(m>e)o+=2;else if(!p&&(l=g+k-n,0<=l&&l<h&&-1!=j[l]&&(m=j[l],r=g+m-l,t=d-t,m>=t)))return this.diff_bisectSplit_(a,b,m,r,c)}}return[[-1,a],[1,b]]};
+diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d),a=a.substring(c),b=b.substring(d),f=this.diff_main(f,g,!1,e),e=this.diff_main(a,b,!1,e);return f.concat(e)};
+diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var q=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(q):void 0!==e[q])?b+=String.fromCharCode(e[q]):(b+=String.fromCharCode(g),e[q]=g,d[g++]=q)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
+diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
+diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
+diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
+diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var m=f.diff_commonPrefix(a.substring(c),b.substring(e)),r=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<r+m&&(g=b.substring(e-r,e)+b.substring(e,e+m),h=a.substring(0,c-r),j=a.substring(c+m),n=b.substring(0,e-r),l=b.substring(e+m))}return 2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
+var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return null;h=d?g?g[4].length>d[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
+diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(j,i)&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
+d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
+diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
+return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
+h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
+diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
+diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case 1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case 0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
+g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
+a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
+diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],j=a[g][1],j=j.replace(c,"&amp;").replace(d,"&lt;").replace(e,"&gt;").replace(f,"<span style=\"color: #dcdcdc;\">&not;</span><br>");switch(h){case 1:b[g]='<ins style="background:#ccffcc; text-decoration: none;">'+j+"</ins>";break;case -1:b[g]='<del style="background:#ffffcc; text-decoration: line-through; color: orange;">'+j+"</del>";break;case 0:b[g]="<span>"+j+"</span>"}}return b.join("")};
+diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case 1:c+=g.length;break;case -1:d+=g.length;break;case 0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
+diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case 1:b[c]="+"+encodeURI(a[c][1]);break;case -1:b[c]="-"+a[c][1].length;break;case 0:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
+diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
+f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
+diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<<b.length-1,h=-1,i,k,p=b.length+a.length,q,s=0;s<b.length;s++){i=0;for(k=p;i<k;)d(s,c+
+k)<=g?i=k:p=k,k=Math.floor((p-i)/2+i);p=k;i=Math.max(1,c-k+1);var o=Math.min(c+k,a.length)+b.length;k=Array(o+2);for(k[o+1]=(1<<s)-1;o>=i;o--){var v=e[a.charAt(o-1)];k[o]=0===s?(k[o+1]<<1|1)&v:(k[o+1]<<1|1)&v|(q[o+1]|q[o])<<1|1|q[o+1];if(k[o]&j&&(v=d(s,o-1),v<=g))if(g=v,h=o-1,h>c)i=Math.max(1,2*c-h);else break}if(d(s+1,c)>g)break;q=k}return h};
+diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
+diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
+c.length+d.length;a.length2+=c.length+d.length}};
+diff_match_patch.prototype.patch_make=function(a,b,c){var d;if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
+if(0===b.length)return[];for(var c=[],a=new diff_match_patch.patch_obj,e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var i=b[j][0],k=b[j][1];if(!e&&0!==i)a.start1=f,a.start2=g;switch(i){case 1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case -1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case 0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
+e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
+diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];var a=this.patch_deepCopy(a),c=this.patch_addPadding(a),b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
+if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;i<a[f].diffs.length;i++){var p=a[f].diffs[i];0!==p[0]&&(k=this.diff_xIndex(g,h));1===p[0]?b=b.substring(0,
+j+k)+p[1]+b.substring(j+k):-1===p[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+p[1].length)));-1!==p[0]&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
+diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
+c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
+diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;if(""!==g)h.length1=h.length2=g.length,h.diffs.push([0,g]);for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
+j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
+(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
+diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;for(var a=a.split("\n"),c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
+parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else if("+"==e)f.diffs.push([1,g]);else if(" "==e)f.diffs.push([0,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
+diff_match_patch.patch_obj.prototype.toString=function(){var a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@ -"+a+" +"+b+" @@\n"];var c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case 1:c="+";break;case -1:c="-";break;case 0:c=" "}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return a.join("").replace(/%20/g," ")};
+this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
+var dmp = new diff_match_patch(); function diffLaunch(){var text1 = document.getElementById('text').value; var text2 = document.getElementById('text2').value; dmp.Diff_Timeout = 0; dmp.Diff_EditCost = 4; var d = dmp.diff_main(text1, text2); var ds = dmp.diff_prettyHtml(d); document.getElementById('diff').innerHTML = ds;
+}
//--><!]]></script>
<title>htmLawed (<?php echo hl_version();?>) test</title>
</head>
@@ -545,7 +600,7 @@ if($do){
$st = microtime();
$out = htmLawed($_POST['text'], $cfg, $_POST['spec']);
$et = microtime();
- echo '<br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'inputR\'); return false;"><span class="notice">Input code &raquo;</span></a> <span class="help" title="tags estimated as half of total &gt; and &lt; chars; values may be inaccurate for non-ASCII text"><small><big>', strlen($_POST['text']), '</big> chars, ~<big>', ($tag = round((substr_count($_POST['text'], '>') + substr_count($_POST['text'], '<'))/2)), '</big> tag', ($tag > 1 ? 's' : ''), '</small>&nbsp;</span><div id="inputR" style="display: none;">', format($_POST['text']), '</div><script type="text/javascript">hl(\'inputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? ' <a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'inputD\'); return false;"><span class="notice">Input binary &raquo;&nbsp;</span></a><div id="inputD" style="display: none;">'. hexdump($_POST['text']). '</div>' : ''), ' <a href="htmLawedTest.php" title="[toggle visibility] finalized internal settings as interpreted by htmLawed; for developers" onclick="javascript:toggle(\'settingF\'); return false;"><span class="notice">Finalized internal settings &raquo;&nbsp;</span></a> <div id="settingF" style="display: none;">', str_replace(array(' ', "\t", ' '), array(' ', '&nbsp; ', '&nbsp; '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['config'], true)))), '</div><script type="text/javascript">hl(\'settingF\');</script>', '<br /><a href="htmLawedTest.php" title="[toggle visibility] suitable for copy-paste" onclick="javascript:toggle(\'outputF\'); return false;"><span class="notice">Output &raquo;</span></a> <span class="help" title="approx., server-specific value excluding the \'include()\' call"><small>htmLawed processing time <big>', number_format(((substr($et,0,9)) + (substr($et,-10)) - (substr($st,0,9)) - (substr($st,-10))),4), '</big> s</small></span>', (($mem = memory_get_peak_usage()) !== false ? '<span class="help"><small>, peak memory usage <big>'. round(($mem-$pre_mem)/1048576, 2). '</big> <small>MB</small>' : ''), '</small></span><div id="outputF" style="display: block;"><div><textarea id="text2" class="textarea" name="text2" rows="5" cols="100" style="width: 100%;">', htmlspecialchars($out), '</textarea></div><button type="button" onclick="javascript:document.getElementById(\'text2\').focus();document.getElementById(\'text2\').select()" title="select all to copy" style="float:right;">Select all</button>';
+ echo '<br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'inputR\'); return false;"><span class="notice">Input code &raquo;</span></a> <span class="help" title="tags estimated as half of total &gt; and &lt; chars; values may be inaccurate for non-ASCII text"><small><big>', strlen($_POST['text']), '</big> chars, ~<big>', ($tag = round((substr_count($_POST['text'], '>') + substr_count($_POST['text'], '<'))/2)), '</big> tag', ($tag > 1 ? 's' : ''), '</small>&nbsp;</span><div id="inputR" style="display: none;">', format($_POST['text']), '</div><script type="text/javascript">hl(\'inputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? ' <a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'inputD\'); return false;"><span class="notice">Input binary &raquo;&nbsp;</span></a><div id="inputD" style="display: none;">'. hexdump($_POST['text']). '</div>' : ''), ' <a href="htmLawedTest.php" title="[toggle visibility] finalized internal settings as interpreted by htmLawed; for developers" onclick="javascript:toggle(\'settingF\'); return false;"><span class="notice">Finalized internal settings &raquo;&nbsp;</span></a> <div id="settingF" style="display: none;">$config: ', str_replace(array(' ', "\t", ' '), array(' ', '&nbsp; ', '&nbsp; '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['config'], true)))), '<br />$spec: ', str_replace(array(' ', "\t", ' '), array(' ', '&nbsp; ', '&nbsp; '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['spec'], true)))), '</div><script type="text/javascript">hl(\'settingF\');</script>', '<br /><a href="htmLawedTest.php" title="[toggle visibility] suitable for copy-paste" onclick="javascript:toggle(\'outputF\'); return false;"><span class="notice">Output &raquo;</span></a> <span class="help" title="approx., server-specific value excluding the \'include()\' call"><small>htmLawed processing time <big>', number_format(((substr($et,0,9)) + (substr($et,-10)) - (substr($st,0,9)) - (substr($st,-10))),4), '</big> s</small></span>', (($mem = memory_get_peak_usage()) !== false ? '<span class="help"><small>, peak memory usage <big>'. round(($mem-$pre_mem)/1048576, 2). '</big> <small>MB</small>' : ''), '</small></span><div id="outputF" style="display: block;"><div><textarea id="text2" class="textarea" name="text2" rows="5" cols="100" style="width: 100%;">', htmlspecialchars($out), '</textarea></div><button type="button" onclick="javascript:document.getElementById(\'text2\').focus();document.getElementById(\'text2\').select()" title="select all to copy" style="float:right;">Select all</button>';
if($_w3c_validate && $validation)
{
?>
@@ -555,7 +610,7 @@ if($do){
<?php
}
- echo '</div><br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'outputR\'); return false;"><span class="notice">Output code &raquo;</span></a><div id="outputR" style="display: block;">', format($out), '</div><script type="text/javascript">hl(\'outputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? '<br /><a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'outputD\'); return false;"><span class="notice">Output binary &raquo;</span></a><div id="outputD" style="display: none;">'. hexdump($out). '</div>' : ''), '<br /><a href="htmLawedTest.php" title="[toggle visibility] XHTML 1 Transitional doctype" onclick="javascript:toggle(\'outputH\'); return false;"><span class="notice">Output rendered &raquo;</span></a><div id="outputH" style="display: block;">', $out, '</div>';
+ echo '</div><br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'outputR\'); return false;"><span class="notice">Output code &raquo;</span></a><div id="outputR" style="display: block;">', format($out), '</div><script type="text/javascript">hl(\'outputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? ' <a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'outputD\'); return false;"><span class="notice">Output binary &raquo;</span></a><div id="outputD" style="display: none;">'. hexdump($out). '</div>' : ''), ' <a href="htmLawedTest.php" title="[toggle visibility] inline output-input diff; might not be perfectly accurate, semantically or otherwise " onclick="javascript:toggle(\'diff\'); diffLaunch(); return false;"><span class="notice">Diff &raquo;</span></a> <div id="diff" style="display: none;"></div><br /><a href="htmLawedTest.php" title="[toggle visibility] XHTML 1 Transitional doctype" onclick="javascript:toggle(\'outputH\'); return false;"><span class="notice">Output rendered &raquo;</span></a><div id="outputH" style="display: block;">', $out, '</div>';
}
else{
?>
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm
index 6dd78fb2e..819b39e06 100644..100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm
@@ -1,2160 +1,2178 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta http-equiv="Content-Language" content="en" />
-<meta name="description" content="htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter - htmLawed_README.txt - presented with rTxt2htm, a PHP Labware utility" />
-<meta name="keywords" content="htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements, htmLawed_README.txt, rTxt2htm, PHP Labware" />
-<style type="text/css" media="all">
-<!--/*--><![CDATA[/*><!--*/
-a {text-decoration:none; color: blue;}
-
-a:hover {color: red;}
-
-a:visited {color: blue;}
-
-body {margin: 0; padding: 0;}
-
-body, div, html, p {font-family: Georgia, 'Times new roman', Times;}
-
-code.code {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-div.comment {padding: 5px; color: #999999; font-size: 80%;}
-
-div.comment a {color: #6699cc;}
-
-div#body {width: 70%; margin: 5px; padding: 5px;} /* holds non-toc content */
-
-div#toc {position: fixed; top: 5px; left: 73%; z-index: 2; margin-top: 5px; margin-left: 5px; border: 1px solid gray; padding: 5px; background-color: #ededed; width: 23%; overflow: auto; max-height:94%; font-size: 90%;} /* holds content table (toc) */
-
-div#top {font-size: 14px; margin: 5px; padding: 5px;} /* holds all content */
-
-div.monospace {overflow: auto; font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-div.sub-section {padding-left: 15px;}
-
-div.sub-sub-section {padding-left: 30px;}
-
-h1 {font-size: 22px; margin-top: 5px; margin-bottom: 5px;}
-
-h2 {font-size: 20px; float: left; margin-top: 15px; margin-bottom: 5px;}
-
-h3 {font-size: 18px; float: left; margin-top: 15px; margin-bottom: 5px;}
-
-h4 {font-size: 16px; float: left; margin-top: 15px; margin-bottom: 5px;}
-
-hr {margin-top: 15px; margin-bottom: 5px;}
-
-input, textarea {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-p.subtle {color: gray; padding: 0; padding-top: 10px; margin: 0;}
-
-p.subtle a, p.subtle a:visited {color: #6699cc;}
-
-span.item-no {color: black;}
-
-span.subtle {color: gray; margin: 0; padding:0;}
-
-span.subtle a, span.subtle a:visited {color: #6699cc;}
-
-span.term {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-span.toc-item {color: black;}
-
-span.totop {float: right; margin-top: 15px; margin-bottom: 5px;}
-
-span.totop a, span.totop a:visited {color: #6699cc;}
-
-@media screen { /* fixes for old IE */
-
- * html, * html body {overflow-y: auto!important; height: 100%; margin: 0; padding: 0;}
-
- * html div#body {height: 100%; overflow-y: auto; position: relative;}
-
- * html div#toc {position: absolute;}
-
-}
-
-/*]]>*/-->
-</style>
-<title>htmLawed documentation | htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter</title>
-</head>
-<body>
-<div id="top">
-<h1><a id="peak" name="peak"></a>htmLawed documentation</h1>
-
-<div id="toc"><span class="toc-item"><a href="#s1"><span class="item-no">1</span>&#160; About htmLawed</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.1"><span class="item-no">1.1</span>&#160; Example uses</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.2"><span class="item-no">1.2</span>&#160; Features</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.3"><span class="item-no">1.3</span>&#160; History</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.4"><span class="item-no">1.4</span>&#160; License &amp; copyright</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.5"><span class="item-no">1.5</span>&#160; Terms used here</a></span><br />
-<span class="toc-item"><a href="#s2"><span class="item-no">2</span>&#160; Usage</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.1"><span class="item-no">2.1</span>&#160; Simple</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.2"><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.3"><span class="item-no">2.3</span>&#160; Extra HTML specifications using the <span class="term">$spec</span>&#160;parameter</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.4"><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.5"><span class="item-no">2.5</span>&#160; Some security risks to keep in mind</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.6"><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.7"><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.8"><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.9"><span class="item-no">2.9</span>&#160; Examples of usage</a></span><br />
-<span class="toc-item"><a href="#s3"><span class="item-no">3</span>&#160; Details</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.1"><span class="item-no">3.1</span>&#160; Invalid/dangerous characters</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.2"><span class="item-no">3.2</span>&#160; Character references/entities</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.3"><span class="item-no">3.3</span>&#160; HTML elements</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.1"><span class="item-no">3.3.1</span>&#160; HTML comments and <span class="term">CDATA</span>&#160;sections</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.2"><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.3"><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.4"><span class="item-no">3.3.4</span>&#160; Elements requiring child elements</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.5"><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.4"><span class="item-no">3.4</span>&#160; Attributes</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.1"><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.2"><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.3"><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.4"><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.5"><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.6"><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.7"><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span></a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.8"><span class="item-no">3.4.8</span>&#160; Inline style properties</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.9"><span class="item-no">3.4.9</span>&#160; Hook function for tag content</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.5"><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.6"><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.7"><span class="item-no">3.7</span>&#160; Using a hook function</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.8"><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.9"><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup</a></span><br />
-<span class="toc-item"><a href="#s4"><span class="item-no">4</span>&#160; Other</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.1"><span class="item-no">4.1</span>&#160; Support</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.2"><span class="item-no">4.2</span>&#160; Known issues</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.3"><span class="item-no">4.3</span>&#160; Change-log</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.4"><span class="item-no">4.4</span>&#160; Testing</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.5"><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.6"><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span></a></span><br />
-&#160; <span class="toc-item"><a href="#s4.7"><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.8"><span class="item-no">4.8</span>&#160; Use in non-PHP applications</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.9"><span class="item-no">4.9</span>&#160; Donate</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.10"><span class="item-no">4.10</span>&#160; Acknowledgements</a></span><br />
-<span class="toc-item"><a href="#s5"><span class="item-no">5</span>&#160; Appendices</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.1"><span class="item-no">5.1</span>&#160; Characters discouraged in HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.2"><span class="item-no">5.2</span>&#160; Valid attribute-element combinations</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.3"><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.4"><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.5"><span class="item-no">5.5</span>&#160; URL format</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.6"><span class="item-no">5.6</span>&#160; Brief on htmLawed code</a></span></div><!-- ended div toc -->
-
-<div id="body">
-<br />
-<div class="comment">htmLawed_README.txt, 8 June 2012<br />
-htmLawed 1.1.11, 5 June 2012<br />
-Copyright Santosh Patnaik<br />
-Dual licensed with LGPL 3 and GPL 2 or later<br />
-A PHP Labware internal utility &#45; <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>&#160;</div>
-<br />
-
-<div class="section"><h2>
-<a name="s1" id="s1"></a><span class="item-no">1</span>&#160; About htmLawed
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed is a highly customizable single-file PHP script to make text secure, and standard- and admin policy-compliant for use in the body of HTML 4, XHTML 1 or 1.1, or generic XML documents. It is thus a configurable input (X)HTML filter, processor, purifier, sanitizer, beautifier, etc., and an alternative to the <a href="http://tidy.sourceforge.net">HTMLTidy</a>&#160;application.<br />
-<br />
-&#160; The <em>lawing in</em>&#160;of input text is needed to ensure that HTML code in the text is standard-compliant, does not introduce security vulnerabilities, and does not break the aesthetics, design or layout of web-pages. htmLawed tries to do this by, for example, making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting (<span class="term">XSS</span>) attacks, and allowing only specified HTML elements/tags and attributes.<br />
-
-<div class="sub-section"><h3>
-<a name="s1.1" id="s1.1"></a><span class="item-no">1.1</span>&#160; Example uses
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; * &#160;Filtering of text submitted as comments on blogs to allow only certain HTML elements<br />
-<br />
-&#160; * &#160;Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant<br />
-<br />
-&#160; * &#160;Text processing for stricter XML standard-compliance: e.g., to have lowercased <span class="term">x</span>&#160;in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as <span class="term">application/xml</span><br />
-<br />
-&#160; * &#160;Scraping text or data from web-pages<br />
-<br />
-&#160; * &#160;Pretty-printing HTML code<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.2" id="s1.2"></a><span class="item-no">1.2</span>&#160; Features
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Key: <span class="term">&#42;</span>&#160;security feature, <span class="term">^</span>&#160;standard compliance, <span class="term">~</span>&#160;requires setting right options, <span class="term">&#96;</span>&#160;different from <span class="term">Kses</span><br />
-<br />
-&#160; * &#160;make input more <strong>secure</strong>&#160;and <strong>standard-compliant</strong><br />
-&#160; * &#160;use for HTML 4, XHTML 1.0 or 1.1, or even generic <strong>XML</strong>&#160;documents &#160;^~`<br />
-<br />
-&#160; * &#160;<strong>beautify</strong>&#160;or <strong>compact</strong>&#160;HTML &#160;^~`<br />
-<br />
-&#160; * &#160;<strong>restrict elements</strong>&#160; ^~`<br />
-&#160; * &#160;proper closure of empty elements like <span class="term">img</span>&#160; ^`<br />
-&#160; * &#160;<strong>transform deprecated elements</strong>&#160;like <span class="term">u</span>&#160; ^~`<br />
-&#160; * &#160;HTML <strong>comments</strong>&#160;and <span class="term">CDATA</span>&#160;sections can be permitted &#160;^~`<br />
-&#160; * &#160;elements like <span class="term">script</span>, <span class="term">object</span>&#160;and <span class="term">form</span>&#160;can be permitted &#160;~<br />
-<br />
-&#160; * &#160;<strong>restrict attributes</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
-&#160; * &#160;remove <strong>invalid attributes</strong>&#160; ^`<br />
-&#160; * &#160;element and attribute names are <strong>lower-cased</strong>&#160; ^<br />
-&#160; * &#160;provide <strong>required attributes</strong>, like <span class="term">alt</span>&#160;for <span class="term">image</span>&#160; ^`<br />
-&#160; * &#160;<strong>transform deprecated attributes</strong>&#160; ^~`<br />
-&#160; * &#160;attributes <strong>declared only once</strong>&#160; ^`<br />
-<br />
-&#160; * &#160;<strong>restrict attribute values</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
-&#160; * &#160;a value is declared for <em>empty</em>&#160;(<em>minimized</em>) attributes like <span class="term">checked</span>&#160; ^<br />
-&#160; * &#160;check for potentially dangerous attribute values &#160;*~<br />
-&#160; * &#160;ensure <strong>unique</strong>&#160;<span class="term">id</span>&#160;attribute values &#160;^~`<br />
-&#160; * &#160;<strong>double-quote</strong>&#160;attribute values &#160;^<br />
-&#160; * &#160;lower-case <strong>standard attribute values</strong>&#160;like <span class="term">password</span>&#160; ^`<br />
-<br />
-&#160; * &#160;<strong>attribute-specific URL protocol/scheme restriction</strong>&#160; *~`<br />
-&#160; * &#160;disable <strong>dynamic expressions</strong>&#160;in <span class="term">style</span>&#160;values &#160;*~`<br />
-<br />
-&#160; * &#160;neutralize invalid named character entities &#160;^`<br />
-&#160; * &#160;<strong>convert</strong>&#160;hexadecimal numeric entities to decimal ones, or vice versa &#160;^~`<br />
-&#160; * &#160;convert named entities to numeric ones for generic XML use &#160;^~`<br />
-<br />
-&#160; * &#160;remove <strong>null</strong>&#160;characters &#160;*<br />
-&#160; * &#160;neutralize potentially dangerous proprietary Netscape <strong>Javascript entities</strong>&#160; *<br />
-&#160; * &#160;replace potentially dangerous <strong>soft-hyphen</strong>&#160;character in URL-accepting attribute values with spaces &#160;*<br />
-<br />
-&#160; * &#160;remove common <strong>invalid characters</strong>&#160;not allowed in HTML or XML &#160;^`<br />
-&#160; * &#160;replace <strong>characters from Microsoft applications</strong>&#160;like <span class="term">Word</span>&#160;that are discouraged in HTML or XML &#160;^~`<br />
-&#160; * &#160;neutralize entities for characters invalid or discouraged in HTML or XML &#160;^`<br />
-&#160; * &#160;appropriately neutralize <span class="term">&lt;</span>, <span class="term">&amp;</span>, <span class="term">"</span>, and <span class="term">&gt;</span>&#160;characters &#160;^*`<br />
-<br />
-&#160; * &#160;understands improperly spaced tag content (like, spread over more than a line) and properly spaces them &#160;`<br />
-&#160; * &#160;attempts to <strong>balance tags</strong>&#160;for well-formedness &#160;^~`<br />
-&#160; * &#160;understands when <strong>omitable closing tags</strong>&#160;like <span class="term">&lt;/p&gt;</span>&#160;(allowed in HTML 4, transitional, e.g.) are missing &#160;^~`<br />
-&#160; * &#160;attempts to permit only <strong>validly nested tags</strong>&#160; ^~`<br />
-&#160; * &#160;option to <strong>remove or neutralize bad content</strong>&#160;^~`<br />
-&#160; * &#160;attempts to <strong>rectify common errors of plain-text misplacement</strong>&#160;(e.g., directly inside <span class="term">blockquote</span>) ^~`<br />
-<br />
-&#160; * &#160;fast, <strong>non-OOP</strong>&#160;code of ~45 kb incurring peak basal memory usage of ~0.5 MB<br />
-&#160; * &#160;<strong>compatible</strong>&#160;with pre-existing code using <span class="term">Kses</span>&#160;(the filter used by <span class="term">WordPress</span>)<br />
-<br />
-&#160; * &#160;optional <strong>anti-spam</strong>&#160;measures such as addition of <span class="term">rel="nofollow"</span>&#160;and link-disabling &#160;~`<br />
-&#160; * &#160;optionally makes <strong>relative URLs absolute</strong>, and vice versa &#160;~`<br />
-<br />
-&#160; * &#160;optionally mark <span class="term">&amp;</span>&#160;to identify the entities for <span class="term">&amp;</span>, <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;introduced by htmLawed &#160;~`<br />
-<br />
-&#160; * &#160;allows deployment of powerful <strong>hook functions</strong>&#160;to <strong>inject</strong>&#160;HTML, <strong>consolidate</strong>&#160;<span class="term">style</span>&#160;attributes to <span class="term">class</span>, finely check attribute values, etc. &#160;~`<br />
-<br />
-&#160; * &#160;<strong>independent of character encoding</strong>&#160;of input and does not affect it<br />
-<br />
-&#160; * &#160;<strong>tolerance for ill-written HTML</strong>&#160;to a certain degree<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.3" id="s1.3"></a><span class="item-no">1.3</span>&#160; History
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed was developed for use with <span class="term">LabWiki</span>, a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like <span class="term">Kses</span>&#160;and <span class="term">HTMLPurifier</span>&#160;were deemed inadequate, slow, resource-intensive, or dependent on external applications like <span class="term">HTML Tidy</span>.<br />
-<br />
-&#160; htmLawed started as a modification of Ulf Harnhammar's <span class="term">Kses</span>&#160;(version 0.2.2) software, and is compatible with code that uses <span class="term">Kses</span>; see <a href="#s2.6">section 2.6</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.4" id="s1.4"></a><span class="item-no">1.4</span>&#160; License &amp; copyright
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed is free and open-source software dual licensed under LGPL license version <a href="http://www.gnu.org/licenses/lgpl-3.0.txt">3</a>&#160;and GPL license version <a href="http://www.gnu.org/licenses/gpl-2.0.txt">2</a>&#160;or later, and copyrighted by Santosh Patnaik, MD, PhD.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.5" id="s1.5"></a><span class="item-no">1.5</span>&#160; Terms used here
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; * &#160;<em>administrator</em>&#160;- or admin; person setting up the code to pass input through htmLawed; also, <em>user</em><br />
-&#160; * &#160;<em>attributes</em>&#160;- name-value pairs like <span class="term">href="http&#58;//x.com"</span>&#160;in opening tags<br />
-&#160; * &#160;<em>author</em>&#160;- <em>writer</em><br />
-&#160; * &#160;<em>character</em>&#160;- atomic unit of text; internally represented by a numeric <em>code-point</em>&#160;as specified by the <em>encoding</em>&#160;or <em>charset</em>&#160;in use<br />
-&#160; * &#160;<em>entity</em>&#160;- markup like <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;#160;</span>&#160;used to refer to a character<br />
-&#160; * &#160;<em>element</em>&#160;- HTML element like <span class="term">a</span>&#160;and <span class="term">img</span><br />
-&#160; * &#160;<em>element content</em>&#160;- &#160;content between the opening and closing tags of an element, like <span class="term">click</span>&#160;of <span class="term">&lt;a href="x"&gt;click&lt;/a&gt;</span><br />
-&#160; * &#160;<em>HTML</em>&#160;- implies XHTML unless specified otherwise<br />
-&#160; * &#160;<em>input</em>&#160;- text string given to htmLawed to process<br />
-&#160; * &#160;<em>processing</em>&#160;- involves filtering, correction, etc., of input<br />
-&#160; * &#160;<em>safe</em>&#160;- absence or reduction of certain characters and HTML elements and attributes in the input that can otherwise potentially and circumstantially expose web-site users to security vulnerabilities like cross-site scripting attacks (XSS)<br />
-&#160; * &#160;<em>scheme</em>&#160;- URL protocol like <span class="term">http</span>&#160;and <span class="term">ftp</span><br />
-&#160; * &#160;<em>specs</em>&#160;- standard specifications<br />
-&#160; * &#160;<em>style property</em>&#160;- terms like <span class="term">border</span>&#160;and <span class="term">height</span>&#160;for which declarations are made in values for the <span class="term">style</span>&#160;attribute of elements<br />
-&#160; * &#160;<em>tag</em>&#160;- markers like <span class="term">&lt;a href="x"&gt;</span>&#160;and <span class="term">&lt;/a&gt;</span>&#160;delineating element content; the opening tag can contain attributes<br />
-&#160; * &#160;<em>tag content</em>&#160;- consists of tag markers <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, element names like <span class="term">div</span>, and possibly attributes<br />
-&#160; * &#160;<em>user</em>&#160;- administrator<br />
-&#160; * &#160;<em>writer</em>&#160;- end-user like a blog commenter providing the input that is to be processed; also, <em>author</em><br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s2" id="s2"></a><span class="item-no">2</span>&#160; Usage
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed should work with PHP 4.4 and higher. Either <span class="term">include()</span>&#160;the <span class="term">htmLawed.php</span>&#160;file or copy-paste the entire code.<br />
-<br />
-&#160; To easily <strong>test</strong>&#160;htmLawed using a form-based interface, use the provided <a href="htmLawedTest.php">demo</a>&#160;(<span class="term">htmLawed.php</span>&#160;and <span class="term">htmLawedTest.php</span>&#160;should be in the same directory on the web-server).<br />
-<br />
-&#160; <strong>Note</strong>: For code for usage of the htmLawed class (for htmLawed in OOP), please refer to the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website; the filtering itself can be configured, etc., as described here.<br />
-
-<div class="sub-section"><h3>
-<a name="s2.1" id="s2.1"></a><span class="item-no">2.1</span>&#160; Simple
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The input text to be processed, <span class="term">$text</span>, is passed as an argument of type string; <span class="term">htmLawed()</span>&#160;returns the processed string:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
-<br />
-<br />
-&#160; <strong>Note</strong>: If input is from a <span class="term">$_GET</span>&#160;or <span class="term">$_POST</span>&#160;value, and <span class="term">magic quotes</span>&#160;are enabled on the PHP setup, run <span class="term">stripslashes()</span>&#160;on the input before passing to htmLawed.<br />
-<br />
-&#160; By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow <span class="term">CDATA</span>&#160;sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- <span class="term">$config</span>&#160;and <span class="term">$spec</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
-<br />
-<br />
-&#160; These extra parameters are detailed below. Some examples are shown in <a href="#s2.9">section 2.9</a>.<br />
-<br />
-&#160; <strong>Note</strong>: For maximum protection against <span class="term">XSS</span>&#160;and other scripting attacks (e.g., by disallowing Javascript code), consider using the <span class="term">safe</span>&#160;parameter; see <a href="#s3.6">section 3.6</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.2" id="s2.2"></a><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; <span class="term">$config</span>&#160;instructs htmLawed on how to tackle certain tasks. When <span class="term">$config</span>&#160;is not specified, or not set as an array (e.g., <span class="term">$config = 1</span>), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in <span class="term">$config</span>&#160;as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1);</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config);</code>
-<br />
-<br />
-&#160; Or,<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1));</code>
-<br />
-<br />
-&#160; Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).<br />
-<br />
-&#160; Key: <span class="term">&#42;</span>&#160;default, <span class="term">^</span>&#160;different default when htmLawed is used in the Kses-compatible mode (see <a href="#s2.6">section 2.6</a>), <span class="term">~</span>&#160;different default when <span class="term">valid_xhtml</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.5">section 3.5</a>), <span class="term">"</span>&#160;different default when <span class="term">safe</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.6">section 3.6</a>)<br />
-<br />
-&#160; <strong>abs_url</strong><br />
-&#160; Make URLs absolute or relative; <span class="term">$config["base_url"]</span>&#160;needs to be set; see <a href="#s3.4.4">section 3.4.4</a><br />
-<br />
-&#160; <span class="term">-1</span>&#160;- make relative<br />
-&#160; <span class="term">0</span>&#160;- no action &#160;*<br />
-&#160; <span class="term">1</span>&#160;- make absolute<br />
-<br />
-&#160; <strong>and_mark</strong><br />
-&#160; Mark <span class="term">&amp;</span>&#160;characters in the original input; see <a href="#s3.2">section 3.2</a><br />
-<br />
-&#160; <strong>anti_link_spam</strong><br />
-&#160; Anti-link-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
-&#160; <span class="term">array("regex1", "regex2")</span>&#160;- will ensure a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value in case the <span class="term">href</span>&#160;attribute value matches the regular expression pattern <span class="term">regex1</span>, and/or will remove <span class="term">href</span>&#160;if its value matches the regular expression pattern <span class="term">regex2</span>. E.g., <span class="term">array("/./", "/&#58;//\W&#42;(?!(abc\.com|xyz\.org))/")</span>; see <a href="#s3.4.7">section 3.4.7</a>&#160;for more.<br />
-<br />
-&#160; <strong>anti_mail_spam</strong><br />
-&#160; Anti-mail-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
-&#160; <span class="term">word</span>&#160;- <span class="term">@</span>&#160;in mail address in <span class="term">href</span>&#160;attribute value is replaced with specified <span class="term">word</span><br />
-<br />
-&#160; <strong>balance</strong><br />
-&#160; Balance tags for well-formedness and proper nesting; see <a href="#s3.3.3">section 3.3.3</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no<br />
-&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
-<br />
-&#160; <strong>base_url</strong><br />
-&#160; Base URL value that needs to be set if <span class="term">$config["abs_url"]</span>&#160;is not <span class="term">0</span>; see <a href="#s3.4.4">section 3.4.4</a><br />
-<br />
-&#160; <strong>cdata</strong><br />
-&#160; Handling of <span class="term">CDATA</span>&#160;sections; see <a href="#s3.3.1">section 3.3.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- don't consider <span class="term">CDATA</span>&#160;sections as markup and proceed as if plain text &#160;^"<br />
-&#160; <span class="term">1</span>&#160;- remove<br />
-&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting them to named entities<br />
-&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
-<br />
-&#160; <strong>clean_ms_char</strong><br />
-&#160; Replace discouraged characters introduced by Microsoft Word, etc.; see <a href="#s3.1">section 3.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- yes<br />
-&#160; <span class="term">2</span>&#160;- yes, but replace special single &amp; double quotes with ordinary ones<br />
-<br />
-&#160; <strong>comment</strong><br />
-&#160; Handling of HTML comments; see <a href="#s3.3.1">section 3.3.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- don't consider comments as markup and proceed as if plain text &#160;^"<br />
-&#160; <span class="term">1</span>&#160;- remove<br />
-&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting to named entities<br />
-&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
-<br />
-&#160; <strong>css_expression</strong><br />
-&#160; Allow dynamic CSS expression by not removing the expression from CSS property values in <span class="term">style</span>&#160;attributes; see <a href="#s3.4.8">section 3.4.8</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- remove &#160;*<br />
-&#160; <span class="term">1</span>&#160;- allow<br />
-<br />
-&#160; <strong>deny_attribute</strong><br />
-&#160; Denied HTML attributes; see <a href="#s3.4">section 3.4</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- none &#160;*<br />
-&#160; <span class="term">string</span>&#160;- dictated by values in <span class="term">string</span><br />
-&#160; <span class="term">on&#42;</span>&#160;(like <span class="term">onfocus</span>) attributes not allowed - "<br />
-<br />
-&#160; <strong>direct_nest_list</strong><br />
-&#160; Allow direct nesting of a list within another without requiring it to be a list item; see <a href="#s3.3.4">section 3.3.4</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- yes<br />
-<br />
-&#160; <strong>elements</strong><br />
-&#160; Allowed HTML elements; see <a href="#s3.3">section 3.3</a><br />
-<br />
-&#160; <span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;- &#160;~<br />
-&#160; <span class="term">applet, embed, iframe, object, script</span>&#160;not allowed - "<br />
-<br />
-&#160; <strong>hexdec_entity</strong><br />
-&#160; Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see <a href="#s3.2">section 3.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no<br />
-&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
-&#160; <span class="term">2</span>&#160;- convert decimal to hexadecimal ones<br />
-<br />
-&#160; <strong>hook</strong><br />
-&#160; Name of an optional hook function to alter the input string, <span class="term">$config</span>&#160;or <span class="term">$spec</span>&#160;before htmLawed starts its main work; see <a href="#s3.7">section 3.7</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
-&#160; <span class="term">name</span>&#160;- <span class="term">name</span>&#160;is name of the hook function (<span class="term">kses_hook</span>&#160; ^)<br />
-<br />
-&#160; <strong>hook_tag</strong><br />
-&#160; Name of an optional hook function to alter tag content finalized by htmLawed; see <a href="#s3.4.9">section 3.4.9</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
-&#160; <span class="term">name</span>&#160;- <span class="term">name</span>&#160;is name of the hook function<br />
-<br />
-&#160; <strong>keep_bad</strong><br />
-&#160; Neutralize bad tags by converting <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;to entities, or remove them; see <a href="#s3.3.3">section 3.3.3</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- remove &#160;^<br />
-&#160; <span class="term">1</span>&#160;- neutralize both tags and element content<br />
-&#160; <span class="term">2</span>&#160;- remove tags but neutralize element content<br />
-&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>&#160;but remove if text (<span class="term">pcdata</span>) is invalid in parent element<br />
-&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;* - &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>&#160;but line-breaks, tabs and spaces are left<br />
-<br />
-&#160; <strong>lc_std_val</strong><br />
-&#160; For XHTML compliance, predefined, standard attribute values, like <span class="term">get</span>&#160;for the <span class="term">method</span>&#160;attribute of <span class="term">form</span>, must be lowercased; see <a href="#s3.4.5">section 3.4.5</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no<br />
-&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
-<br />
-&#160; <strong>make_tag_strict</strong><br />
-&#160; Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: <span class="term">applet</span>&#160;<span class="term">center</span>&#160;<span class="term">dir</span>&#160;<span class="term">embed</span>&#160;<span class="term">font</span>&#160;<span class="term">isindex</span>&#160;<span class="term">menu</span>&#160;<span class="term">s</span>&#160;<span class="term">strike</span>&#160;<span class="term">u</span>; see <a href="#s3.3.2">section 3.3.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;^<br />
-&#160; <span class="term">1</span>&#160;- yes, but leave <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements that currently can't be transformed &#160;*<br />
-&#160; <span class="term">2</span>&#160;- yes, removing <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements and their contents (nested elements remain) &#160;~<br />
-<br />
-&#160; <strong>named_entity</strong><br />
-&#160; Allow non-universal named HTML entities, or convert to numeric ones; see <a href="#s3.2">section 3.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- convert<br />
-&#160; <span class="term">1</span>&#160;- allow &#160;*<br />
-<br />
-&#160; <strong>no_deprecated_attr</strong><br />
-&#160; Allow deprecated attributes or transform them; see <a href="#s3.4.6">section 3.4.6</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- allow &#160;^<br />
-&#160; <span class="term">1</span>&#160;- transform, but <span class="term">name</span>&#160;attributes for <span class="term">a</span>&#160;and <span class="term">map</span>&#160;are retained &#160;*<br />
-&#160; <span class="term">2</span>&#160;- transform<br />
-<br />
-&#160; <strong>parent</strong><br />
-&#160; Name of the parent element, possibly imagined, that will hold the input; see <a href="#s3.3">section 3.3</a><br />
-<br />
-&#160; <strong>safe</strong><br />
-&#160; Magic parameter to make input the most secure against XSS without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.6">section 3.6</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">"</span>&#160;in this list)<br />
-<br />
-&#160; <strong>schemes</strong><br />
-&#160; Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or <span class="term">!</span>&#160;to <em>deny</em>&#160;any URL); <span class="term">&#42;</span>&#160;covers all unspecified attributes; see <a href="#s3.4.3">section 3.4.3</a><br />
-<br />
-&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; &#42;&#58;file, http, https</span>&#160; *<br />
-&#160; <span class="term">&#42;&#58; ftp, gopher, http, https, mailto, news, nntp, telnet</span>&#160; ^<br />
-&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style&#58; !; &#42;&#58;file, http, https</span>&#160; "<br />
-<br />
-&#160; <strong>show_setting</strong><br />
-&#160; Name of a PHP variable to assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values; see <a href="#s3.8">section 3.8</a><br />
-<br />
-&#160; <strong>style_pass</strong><br />
-&#160; Do not look at <span class="term">style</span>&#160;attribute values, letting them through without any alteration<br />
-<br />
-&#160; <span class="term">0</span>&#160;- no *<br />
-&#160; <span class="term">1</span>&#160;- htmLawed will let through any <span class="term">style</span>&#160;value; see <a href="#s3.4.8">section 3.4.8</a><br />
-<br />
-&#160; <strong>tidy</strong><br />
-&#160; Beautify or compact HTML code; see <a href="#s3.3.5">section 3.3.5</a><br />
-<br />
-&#160; <span class="term">-1</span>&#160;- compact<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;or <span class="term">string</span>&#160;- beautify (custom format specified by <span class="term">string</span>)<br />
-<br />
-&#160; <strong>unique_ids</strong><br />
-&#160; <span class="term">id</span>&#160;attribute value checks; see <a href="#s3.4.2">section 3.4.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;^<br />
-&#160; <span class="term">1</span>&#160;- remove duplicate and/or invalid ones &#160;*<br />
-&#160; <span class="term">word</span>&#160;- remove invalid ones and replace duplicate ones with new and unique ones based on the <span class="term">word</span>; the admin-specified <span class="term">word</span>, like <span class="term">my_</span>, should begin with a letter (a-z) and can contain letters, digits, <span class="term">.</span>, <span class="term">_</span>, <span class="term">-</span>, and <span class="term">&#58;</span>.<br />
-<br />
-&#160; <strong>valid_xhtml</strong><br />
-&#160; Magic parameter to make input the most valid XHTML without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.5">section 3.5</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in this list)<br />
-<br />
-&#160; <strong>xml:lang</strong><br />
-&#160; Auto-adding <span class="term">xml&#58;lang</span>&#160;attribute; see <a href="#s3.4.1">section 3.4.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present<br />
-&#160; <span class="term">2</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present, and remove <span class="term">lang</span>&#160; ~<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.3" id="s2.3"></a><span class="item-no">2.3</span>&#160; Extra HTML specifications using the $spec parameter
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The <span class="term">$spec</span>&#160;argument can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policy compliance. <span class="term">$spec</span>&#160;is specified as a string of text containing one or more <em>rules</em>, with multiple rules separated from each other by a semi-colon (<span class="term">;</span>). E.g.,<br />
-<br />
-
-<code class="code">&#160; &#160; $spec = &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
-<br />
-<br />
-&#160; Or,<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config, &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;);</code>
-<br />
-<br />
-&#160; A rule begins with an HTML <strong>element</strong>&#160;name(s) (<em>rule-element</em>), for which the rule applies, followed by an equal (<span class="term">=</span>) sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., <span class="term">th,td,tr=</span>.<br />
-<br />
-&#160; Rest of the rule consists of comma-separated HTML <strong>attribute names</strong>. A minus (<span class="term">-</span>) character before an attribute means that the attribute is not permitted inside the rule-element. E.g., <span class="term">-width</span>. To deny all attributes, <span class="term">-&#42;</span>&#160;can be used.<br />
-<br />
-&#160; Following shows examples of rule excerpts with rule-element <span class="term">a</span>&#160;and the attributes that are being permitted:<br />
-<br />
-&#160; * &#160;<span class="term">a=</span>&#160;- all<br />
-&#160; * &#160;<span class="term">a=id</span>&#160;- all<br />
-&#160; * &#160;<span class="term">a=href, title, -id, -onclick</span>&#160;- all except <span class="term">id</span>&#160;and <span class="term">onclick</span><br />
-&#160; * &#160;<span class="term">a=&#42;, id, -id</span>&#160;- all except <span class="term">id</span><br />
-&#160; * &#160;<span class="term">a=-&#42;</span>&#160;- none<br />
-&#160; * &#160;<span class="term">a=-&#42;, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
-&#160; * &#160;<span class="term">a=-&#42;, -id, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
-<br />
-&#160; Rules regarding <strong>attribute values</strong>&#160;are optionally specified inside round brackets after attribute names in slash ('/')-separated <em>parameter = value</em>&#160;pairs. E.g., <span class="term">title(maxlen=30/minlen=5)</span>. None, or one or more of the following parameters may be specified:<br />
-<br />
-&#160; * &#160;<span class="term">oneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should match; if only one choice is provided, then the value must match that choice<br />
-<br />
-&#160; * &#160;<span class="term">noneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should not match<br />
-<br />
-&#160; * &#160;<span class="term">maxlen</span>&#160;and <span class="term">minlen</span>&#160;- upper and lower limits for the number of characters in the attribute value; specified in numbers<br />
-<br />
-&#160; * &#160;<span class="term">maxval</span>&#160;and <span class="term">minval</span>&#160;- upper and lower limits for the numerical value specified in the attribute value; specified in numbers<br />
-<br />
-&#160; * &#160;<span class="term">match</span>&#160;and <span class="term">nomatch</span>&#160;- pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers<br />
-<br />
-&#160; * &#160;<span class="term">default</span>&#160;- a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters<br />
-<br />
-&#160; If <span class="term">default</span>&#160;is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The <span class="term">default</span>&#160;value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., <span class="term">maxlen</span>&#160;to <span class="term">-1</span>).<br />
-<br />
-&#160; Examples with <em>input</em>&#160;<span class="term">&lt;input title="WIDTH" value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span>&#160;are shown below.<br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(maxlen=60/minlen=6), value</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(), value(maxval=8/default=6)</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input title="WIDTH" value="6" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(nomatch=%w.d%i), value(match=%em%/default=6em)</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="6em" /&gt;</span><br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(oneof=height|depth/default=depth), value(noneof=5|6)</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input title="depth" value="10em" /&gt;&lt;input title="depth" /&gt;</span><br />
-<br />
-&#160; <strong>Special characters</strong>: The characters <span class="term">;</span>, <span class="term">,</span>, <span class="term">/</span>, <span class="term">(</span>, <span class="term">)</span>, <span class="term">|</span>, <span class="term">~</span>&#160;and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be <em>escaped</em>&#160;by enclosing in pairs of double-quotes (<span class="term">"</span>). A back-tick (<span class="term">&#96;</span>) can be used to escape a literal <span class="term">"</span>. An example rule illustrating this is <span class="term">input=value(maxlen=30/match="/^\w/"/default="your &#96;"ID&#96;"")</span>.<br />
-<br />
-&#160; <strong>Note</strong>: To deny an attribute for all elements for which it is legal, <span class="term">$config["deny_attribute"]</span>&#160;(see <a href="#s3.4">section 3.4</a>) can be used instead of <span class="term">$spec</span>. Also, attributes can be allowed element-specifically through <span class="term">$spec</span>&#160;while being denied globally through <span class="term">$config["deny_attribute"]</span>. The <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) can also be used to implement the <span class="term">$spec</span>&#160;functionality.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.4" id="s2.4"></a><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The time and memory used by htmLawed depends on its configuration and the size of the input, and the amount, nestedness and well-formedness of the HTML markup within it. In particular, tag balancing and beautification each can increase the processing time by about a quarter.<br />
-<br />
-&#160; The htmLawed <a href="htmLawedTest.php">demo</a>&#160;can be used to evaluate the performance and effects of different types of input and <span class="term">$config</span>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.5" id="s2.5"></a><span class="item-no">2.5</span>&#160; Some security risks to keep in mind
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially <em>dangerous</em>&#160;HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc.<br />
-<br />
-&#160; Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permission has to be kept in mind. For example, following increase security risks:<br />
-<br />
-&#160; * &#160;Allowing <span class="term">script</span>, <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>&#160;or <span class="term">object</span>&#160;elements, or certain of their attributes like <span class="term">allowscriptaccess</span><br />
-<br />
-&#160; * &#160;Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., <span class="term">&lt;!--[if gte IE 4]&gt;&lt;script&gt;alert("xss");&lt;/script&gt;&lt;![endif]--&gt;</span><br />
-<br />
-&#160; * &#160;Allowing dynamic CSS expressions (a feature of the IE browser)<br />
-<br />
-&#160; * &#160;Allowing the <span class="term">style</span>&#160;attribute<br />
-<br />
-&#160; To remove <em>unsecure</em>&#160;HTML, code-developers using htmLawed must set <span class="term">$config</span>&#160;appropriately. E.g., <span class="term">$config["elements"] = "&#42; -script"</span>&#160;to deny the <span class="term">script</span>&#160;element (<a href="#s3.3">section 3.3</a>), <span class="term">$config["safe"] = 1</span>&#160;to auto-configure ceratin htmLawed parameters for maximizing security (<a href="#s3.6">section 3.6</a>), etc.<br />
-<br />
-&#160; Permitting the <span class="term">&#42;style&#42;</span>&#160;attribute brings in risks of <em>click-jacking</em>, <em>phishing</em>, web-page overlays, etc., <em>even</em>&#160;when the <span class="term">safe</span>&#160;parameter is enabled (see <a href="#s3.6">section 3.6</a>). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing <span class="term">style</span>&#160;completely and relying on CSS classes and stylesheet files is recommended.<br />
-<br />
-&#160; htmLawed does not check or correct the character <strong>encoding</strong>&#160;of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.6" id="s2.6"></a><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The <span class="term">Kses</span>&#160;PHP script is used by many applications (like <span class="term">WordPress</span>). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the <span class="term">kses()</span>&#160;function declared in the <span class="term">Kses</span>&#160;file (usually named <span class="term">kses.php</span>). E.g., application code like this will continue to work after replacing <span class="term">Kses</span>&#160;with htmLawed:<br />
-<br />
-
-<code class="code">&#160; &#160; $comment_filtered = kses($comment_input, array(&#39;a&#39;=&gt;array(), &#39;b&#39;=&gt;array(), &#39;i&#39;=&gt;array()));</code>
-<br />
-<br />
-&#160; For some of the <span class="term">$config</span>&#160;parameters, htmLawed will use values other than the default ones. These are indicated by <span class="term">^</span>&#160;in <a href="#s2.2">section 2.2</a>. To force htmLawed to use other values, function <span class="term">kses()</span>&#160;in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.<br />
-<br />
-&#160; If the application uses a <span class="term">Kses</span>&#160;file that has the <span class="term">kses()</span>&#160;function declared, then, to have the application use htmLawed instead of <span class="term">Kses</span>, simply rename <span class="term">htmLawed.php</span>&#160;(to <span class="term">kses.php</span>, e.g.) and replace the <span class="term">Kses</span>&#160;file (or just replace the code in the <span class="term">Kses</span>&#160;file with the htmLawed code). If the <span class="term">kses()</span>&#160;function in the <span class="term">Kses</span>&#160;file had been renamed by the application developer (e.g., in <span class="term">WordPress</span>, it is named <span class="term">wp_kses()</span>), then appropriately rename the <span class="term">kses()</span>&#160;function in the htmLawed code.<br />
-<br />
-&#160; If the <span class="term">Kses</span>&#160;file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with <span class="term">WordPress</span>, it is best to copy the htmLawed code to <span class="term">wp_includes/kses.php</span>, rename the newly added function <span class="term">kses()</span>&#160;to <span class="term">wp_kses()</span>, and delete the code for the original <span class="term">wp_kses()</span>&#160;function.<br />
-<br />
-&#160; If the <span class="term">Kses</span>&#160;code has a non-empty hook function (e.g., <span class="term">wp_kses_hook()</span>&#160;in case of <span class="term">WordPress</span>), then the code for htmLawed's <span class="term">kses_hook()</span>&#160;function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With <span class="term">WordPress</span>, the hook function is an essential one. The following code is suggested for the htmLawed <span class="term">kses_hook()</span>&#160;in case of <span class="term">WordPress</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; function kses_hook($string, &amp;$cf, &amp;$spec){</code>
-<br />
-
-<code class="code">&#160; &#160; // kses compatibility</code>
-<br />
-
-<code class="code">&#160; &#160; $allowed_html = $spec;</code>
-<br />
-
-<code class="code">&#160; &#160; $allowed_protocols = array();</code>
-<br />
-
-<code class="code">&#160; &#160; foreach($cf[&#39;schemes&#39;] as $v){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;foreach($v as $k2=&gt;$v2){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if(!in_array($k2, $allowed_protocols)){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;$allowed_protocols[] = $k2;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;}</code>
-<br />
-
-<code class="code">&#160; &#160; }</code>
-<br />
-
-<code class="code">&#160; &#160; return wp_kses_hook($string, $allowed_html, $allowed_protocols);</code>
-<br />
-
-<code class="code">&#160; &#160; // eof</code>
-<br />
-
-<code class="code">&#160; &#160; }</code>
-<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.7" id="s2.7"></a><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be <em>read</em>&#160;as HTML, and be considered mere plain text instead. Following statements indicate the degree of <em>looseness</em>&#160;that htmLawed can work with, and can be provided in instructions to writers:<br />
-<br />
-&#160; * &#160;Tags must be flanked by <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;with no <span class="term">&gt;</span>&#160;inside -- any needed <span class="term">&gt;</span>&#160;should be put in as <span class="term">&amp;gt;</span>. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and <span class="term">&gt;</span>, like <span class="term">&lt;div &gt;</span>&#160;and <span class="term">&lt;img / &gt;</span>, but not after the <span class="term">&lt;</span>.<br />
-<br />
-&#160; * &#160;Element and attribute names need not be lower-cased.<br />
-<br />
-&#160; * &#160;Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.<br />
-<br />
-&#160; * &#160;Attribute values may not be double-quoted, or may be single-quoted.<br />
-<br />
-&#160; * &#160;Left-padding of numeric entities (like, <span class="term">&amp;#0160;</span>, <span class="term">&amp;x07ff;</span>) with <span class="term">0</span>&#160;is okay as long as the number of characters between between the <span class="term">&amp;</span>&#160;and the <span class="term">;</span>&#160;does not exceed 8. All entities must end with <span class="term">;</span>&#160;though.<br />
-<br />
-&#160; * &#160;Named character entities must be properly cased. E.g., <span class="term">&amp;Lt;</span>&#160;or <span class="term">&amp;TILDE;</span>&#160;will not be let through without modification.<br />
-<br />
-&#160; * &#160;HTML comments should not be inside element tags (okay between tags), and should begin with <span class="term">&lt;!--</span>&#160;and end with <span class="term">--&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">--&gt;</span>&#160;inside should be put in as <span class="term">--&amp;gt;</span>. Any <span class="term">--</span>&#160;inside will be automatically converted to <span class="term">-</span>, and a space will be added before the comment delimiter <span class="term">--&gt;</span>.<br />
-<br />
-&#160; * &#160;<span class="term">CDATA</span>&#160;sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with <span class="term">&lt;[CDATA[</span>&#160;and end with <span class="term">]]&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">]]&gt;</span>&#160;inside should be put in as <span class="term">]]&amp;gt;</span>.<br />
-<br />
-&#160; * &#160;For attribute values, character entities <span class="term">&amp;lt;</span>, <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;amp;</span>&#160;should be used instead of characters <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;(when <span class="term">&amp;</span>&#160;is not part of a character entity). This applies even for Javascript code in values of attributes like <span class="term">onclick</span>.<br />
-<br />
-&#160; * &#160;Characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>&#160;and <span class="term">"</span>&#160;that are part of actual Javascript, etc., code in <span class="term">script</span>&#160;elements should be used as such and not be put in as entities like <span class="term">&amp;gt;</span>. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside <span class="term">CDATA</span>&#160;sections.<br />
-<br />
-&#160; * &#160;Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on <span class="term">$config["keep_bad"]</span>, some code/text may be lost.<br />
-<br />
-&#160; * &#160;Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.<br />
-<br />
-&#160; * &#160;With <span class="term">$config["unique_ids"]</span>&#160;not <span class="term">0</span>&#160;and the <span class="term">id</span>&#160;attribute being permitted, writers should carefully avoid using duplicate or invalid <span class="term">id</span>&#160;values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when <span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>&#160;is processed into<br />
-<span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="prefix_home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>.<br />
-<br />
-&#160; * &#160;Note that even if intended HTML is lost in a highly ill-written input, the processed output will be more secure and standard-compliant.<br />
-<br />
-&#160; * &#160;For URLs, unless <span class="term">$config["scheme"]</span>&#160;is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., <span class="term">htt&amp;#112;</span>&#160;(which many browsers will read as the harmless <span class="term">http</span>) may be considered bad by htmLawed.<br />
-<br />
-&#160; * &#160;htmLawed will attempt to put plain text present directly inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;elements (illegal as per the specs) inside auto-generated <span class="term">div</span>&#160;elements.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.8" id="s2.8"></a><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed's main objective is to make the input text <em>more</em>&#160;standard-compliant, secure for web-page readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.<br />
-<br />
-&#160; It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specs (like asking for normalization of white-spacing within <span class="term">textarea</span>&#160;elements) are clearly wrong. Regarding security, note that <em>unsafe</em>&#160;HTML code is not necessarily legally invalid.<br />
-<br />
-&#160; * &#160;htmLawed is meant for input that goes into the <span class="term">body</span>&#160;of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>.<br />
-<br />
-&#160; * &#160;It cannot transform the non-standard <span class="term">embed</span>&#160;elements to the standard-compliant <span class="term">object</span>&#160;elements. Yet, it can allow <span class="term">embed</span>&#160;elements if permitted (<span class="term">embed</span>&#160;is widely used and supported). Admins can certainly use the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to deploy a custom embed-to-object converter function.<br />
-<br />
-&#160; * &#160;The only non-standard element that may be permitted is <span class="term">embed</span>; others like <span class="term">noembed</span>&#160;and <span class="term">nobr</span>&#160;cannot be permitted without modifying the htmLawed code.<br />
-<br />
-&#160; * &#160;It cannot handle input that has non-HTML code like <span class="term">SVG</span>&#160;and <span class="term">MathML</span>. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in <a href="#s3.9">section 3.9</a>. A third way may be to some how take advantage of the <span class="term">$config["and_mark"]</span>&#160;parameter (see <a href="#s3.2">section 3.2</a>).<br />
-<br />
-&#160; * &#160;By default, htmLawed won't check many attribute values for standard compliance. E.g., <span class="term">width="20m"</span>&#160;with the dimension in non-standard <span class="term">m</span>&#160;is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;to enforce finer checks.<br />
-<br />
-&#160; * &#160;The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specs. Only a few of the proprietary attributes are supported.<br />
-<br />
-&#160; * &#160;Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;for finer checks. Perhaps the best option is to disallow <span class="term">style</span>&#160;but allow <span class="term">class</span>&#160;attributes with the right <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;values for <span class="term">class</span>, and have the various class style properties in <span class="term">.css</span>&#160;CSS stylesheet files.<br />
-<br />
-&#160; * &#160;htmLawed does not parse emoticons, decode <em>BBcode</em>, or <em>wikify</em>, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to <span class="term">br</span>&#160;elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.<br />
-<br />
-&#160; * &#160;htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate <span class="term">target</span>&#160;and <span class="term">onclick</span>&#160;attributes to <span class="term">a</span>). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;Nesting-based checks are not possible. E.g., one cannot disallow <span class="term">p</span>&#160;elements specifically inside <span class="term">td</span>&#160;while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert <span class="term">http</span>&#160;to <span class="term">https</span>. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;Pairs of opening and closing tags that do not enclose any content (like <span class="term">&lt;em&gt;&lt;/em&gt;</span>) are not removed. This may be against the standard specs for certain elements (e.g., <span class="term">table</span>). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.<br />
-<br />
-&#160; * &#160;htmLawed does not check for certain element orderings described in the standard specs (e.g., in a <span class="term">table</span>, <span class="term">tbody</span>&#160;is allowed before <span class="term">tfoot</span>). Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;htmLawed does not check the number of nested elements. E.g., it will allow two <span class="term">caption</span>&#160;elements in a <span class="term">table</span>&#160;element, illegal as per the specs. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (<span class="term">/&#42;</span>) in <span class="term">style</span>&#160;attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like <span class="term">&amp;#101;xpression...</span>. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to more specifically identify CSS expressions in the <span class="term">style</span>&#160;attribute values. Also, using <span class="term">$config["style_pass"]</span>, it is possible to have htmLawed pass <span class="term">style</span>&#160;attribute values without even looking at them (<a href="#s3.4.8">section 3.4.8</a>).<br />
-<br />
-&#160; * &#160;htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <span class="term">&lt;a href="http&#58;//x%22+style=%22background-image&#58;xss"&gt;x&lt;/a&gt;</span>). These arise when browsers mis-identify markup in <em>escaped</em>&#160;text, defeating the very purpose of escaping text (a bad browser will read the given example as <span class="term">&lt;a href="http&#58;//x" style="background-image&#58;xss"&gt;x&lt;/a&gt;</span>).<br />
-<br />
-&#160; * &#160;Because of poor Unicode support in PHP, htmLawed does not remove the <em>high value</em>&#160;HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see <a href="#s3.1">section 3.1</a>).<br />
-<br />
-&#160; * &#160;htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).<br />
-<br />
-&#160; * &#160;Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.9" id="s2.9"></a><span class="item-no">2.9</span>&#160; Examples of usage
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Safest, allowing only <em>safe</em>&#160;HTML markup --<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in);</code>
-<br />
-<br />
-&#160; Simplest, allowing all valid HTML markup except <span class="term">javascript&#58;</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in);</code>
-<br />
-<br />
-&#160; Allowing all valid HTML markup including <span class="term">javascript&#58;</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Allowing only <span class="term">safe</span>&#160;HTML and the elements <span class="term">a</span>, <span class="term">em</span>, and <span class="term">strong</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1, &#39;elements&#39;=&gt;&#39;a, em, strong&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Not allowing elements <span class="term">script</span>&#160;and <span class="term">object</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;elements&#39;=&gt;&#39;&#42; -script -object&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Not allowing attributes <span class="term">id</span>&#160;and <span class="term">style</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;id, style&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Permitting only attributes <span class="term">title</span>&#160;and <span class="term">href</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;&#42; -title -href&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Remove bad/disallowed tags altogether instead of converting them to entities --<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;keep_bad&#39;=&gt;0);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Allowing attribute <span class="term">title</span>&#160;only in <span class="term">a</span>&#160;and not allowing attributes <span class="term">id</span>, <span class="term">style</span>, or scriptable <em>on*</em>&#160;attributes like <span class="term">onclick</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;title, id, style, on&#42;&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $spec = &#39;a=title&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config, $spec);</code>
-<br />
-<br />
-&#160; Some case-studies are presented below.<br />
-<br />
-&#160; <strong>1.</strong>&#160;A blog administrator wants to allow only <span class="term">a</span>, <span class="term">em</span>, <span class="term">strike</span>, <span class="term">strong</span>&#160;and <span class="term">u</span>&#160;in comments, but needs <span class="term">strike</span>&#160;and <span class="term">u</span>&#160;transformed to <span class="term">span</span>&#160;for better XHTML 1-strict compliance, and, he wants the <span class="term">a</span>&#160;links to be to <span class="term">http</span>&#160;or <span class="term">https</span>&#160;resources:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;a, em, strike, strong, u&#39;, &#39;make_tag_strict&#39;=&gt;1, &#39;safe&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;http, https&#39;), &#39;a=href&#39;);</code>
-<br />
-<br />
-&#160; <strong>2.</strong>&#160;An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional <em>bad</em>&#160;characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;css_expression&#39;=&gt;1, &#39;keep_bad&#39;=&gt;1, &#39;make_tag_strict&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;, &#39;valid_xhtml&#39;=&gt;1));</code>
-<br />
-<br />
-&#160; For the final submission process, <span class="term">keep_bad</span>&#160;is set to <span class="term">6</span>. A value of <span class="term">1</span>&#160;for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.<br />
-<br />
-&#160; <strong>3.</strong>&#160;A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;tr, td&#39;, &#39;tidy&#39;=&gt;-1), &#39;tr, td =&#39;);</code>
-<br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s3" id="s3"></a><span class="item-no">3</span>&#160; Details
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<div class="sub-section"><h3>
-<a name="s3.1" id="s3.1"></a><span class="item-no">3.1</span>&#160; Invalid/dangerous characters
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, <span class="term">9</span>, <span class="term">a</span>, <span class="term">d</span>, <span class="term">20</span>&#160;to <span class="term">d7ff</span>, and <span class="term">e000</span>&#160;to <span class="term">10ffff</span>, except <span class="term">fffe</span>&#160;and <span class="term">ffff</span>&#160;(decimally, <span class="term">9</span>, <span class="term">10</span>, <span class="term">13</span>, <span class="term">32</span>&#160;to <span class="term">55295</span>, and <span class="term">57344</span>&#160;to <span class="term">1114111</span>, except <span class="term">65534</span>&#160;and <span class="term">65535</span>). htmLawed removes the invalid characters <span class="term">0</span>&#160;to <span class="term">8</span>, <span class="term">b</span>, <span class="term">c</span>, and <span class="term">e</span>&#160;to <span class="term">1f</span>.<br />
-<br />
-&#160; Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.<br />
-<br />
-&#160; Characters that are discouraged (see <a href="#s5.1">section 5.1</a>) but not invalid are not removed by htmLawed.<br />
-<br />
-&#160; It (function <span class="term">hl_tag()</span>) also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, <span class="term">ad</span>, or decimally, <span class="term">173</span>) in attribute values with spaces. Where required, the characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>, and <span class="term">"</span>&#160;are converted to entities.<br />
-<br />
-&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">1</span>&#160;or <span class="term">2</span>, many of the discouraged characters (decimal code-points <span class="term">127</span>&#160;to <span class="term">159</span>&#160;except <span class="term">133</span>) that many Microsoft applications incorrectly use (as per the <span class="term">Windows 1252</span>&#160;[<span class="term">Cp-1252</span>] or a similar encoding system), and the character for decimal code-point <span class="term">133</span>, are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in <a href="#s5.4">section 5.4</a>. This can help avoid some display issues arising from copying-pasting of content.<br />
-<br />
-&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">2</span>, characters for the hexadecimal code-points <span class="term">82</span>, <span class="term">91</span>, and <span class="term">92</span>&#160;(for special single-quotes), and <span class="term">84</span>, <span class="term">93</span>, and <span class="term">94</span>&#160;(for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.<br />
-<br />
-&#160; The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.<br />
-<br />
-&#160; The <span class="term">$config["clean_ms_char"]</span>&#160;parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the <span class="term">Windows 1252</span>&#160;(<span class="term">Cp-1252</span>) or a similar encoding like <span class="term">Cp-1251</span>. Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.2" id="s3.2"></a><span class="item-no">3.2</span>&#160; Character references/entities
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Valid character entities take the form <span class="term">&amp;&#42;;</span>&#160;where <span class="term">&#42;</span>&#160;is <span class="term">#x</span>&#160;followed by a hexadecimal number (hexadecimal numeric entity; like <span class="term">&amp;#xA0;</span>&#160;for non-breaking space), or alphanumeric like <span class="term">gt</span>&#160;(external or named entity; like <span class="term">&amp;nbsp;</span>&#160;for non-breaking space), or <span class="term">#</span>&#160;followed by a number (decimal numeric entity; like <span class="term">&amp;#160;</span>&#160;for non-breaking space). Character entities referring to the soft-hyphen character (the <span class="term">&amp;shy;</span>&#160;or <span class="term">\xad</span>&#160;character; hexadecimal code-point <span class="term">ad</span>&#160;[decimal <span class="term">173</span>]) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.<br />
-<br />
-&#160; htmLawed (function <span class="term">hl_ent()</span>):<br />
-<br />
-&#160; * &#160;Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)<br />
-<br />
-&#160; * &#160;Lowercases the <span class="term">X</span>&#160;(for XML-compliance) and <span class="term">A-F</span>&#160;of hexadecimal numeric entities<br />
-<br />
-&#160; * &#160;Neutralizes entities referring to characters that are HTML-invalid (see <a href="#s3.1">section 3.1</a>)<br />
-<br />
-&#160; * &#160;Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, and <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, or decimally, <span class="term">127</span>&#160;to <span class="term">132</span>, <span class="term">134</span>&#160;to <span class="term">159</span>, and <span class="term">64991</span>&#160;to <span class="term">64976</span>). Entities referring to the remaining discouraged characters (see <a href="#s5.1">section 5.1</a>&#160;for a full list) are let through.<br />
-<br />
-&#160; * &#160;Neutralizes named entities that are not in the specs.<br />
-<br />
-&#160; * &#160;Optionally converts valid HTML-specific named entities except <span class="term">&amp;gt;</span>, <span class="term">&amp;lt;</span>, <span class="term">&amp;quot;</span>, and <span class="term">&amp;amp;</span>&#160;to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is <span class="term">2</span>) for generic XML-compliance. For this, <span class="term">$config["named_entity"]</span>&#160;should be <span class="term">1</span>.<br />
-<br />
-&#160; * &#160;Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">0</span>.<br />
-<br />
-&#160; * &#160;Optionally converts decimal numeric entities to the hexadecimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">2</span>.<br />
-<br />
-&#160; <em>Neutralization</em>&#160;refers to the <em>entitification</em>&#160;of <span class="term">&amp;</span>&#160;to <span class="term">&amp;amp;</span>.<br />
-<br />
-&#160; <strong>Note</strong>: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's <span class="term">html_entity_decode</span>&#160;<a href="http://www.php.net/html_entity_decode">function</a>&#160;for that.<br />
-<br />
-&#160; <strong>Note</strong>: If <span class="term">$config["and_mark"]</span>&#160;is set, and set to a value other than <span class="term">0</span>, then the <span class="term">&amp;</span>&#160;characters in the original input are replaced with the control character for the hexadecimal code-point <span class="term">6</span>&#160;(<span class="term">\x06</span>; <span class="term">&amp;</span>&#160;characters introduced by htmLawed, e.g., after converting <span class="term">&lt;</span>&#160;to <span class="term">&amp;lt;</span>, are not affected). This allows one to distinguish, say, an <span class="term">&amp;gt;</span>&#160;introduced by htmLawed and an <span class="term">&amp;gt;</span>&#160;put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence <span class="term">o(&gt;&lt;)o</span>&#160;to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the <span class="term">\x06</span>&#160;can break documents. Before use in such documents, and preferably before any storage, any remaining <span class="term">\x06</span>&#160;should be changed back to <span class="term">&amp;</span>, e.g., with:<br />
-<br />
-
-<code class="code">&#160; &#160; $final = str_replace("\x06", &#39;&amp;&#39;, $prelim);</code>
-<br />
-<br />
-&#160; Also, see <a href="#s3.9">section 3.9</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3" id="s3.3"></a><span class="item-no">3.3</span>&#160; HTML elements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on <span class="term">$config["keep_bad"]</span>, are either <em>neutralized</em>&#160;(converted to plain text by entitification of <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>) or removed.<br />
-<br />
-&#160; E.g., with only <span class="term">em</span>&#160;permitted:<br />
-<br />
-&#160; Input:<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &lt;a href="http&#58;//a.com&gt;a.com&lt;/a&gt;.</code>
-<br />
-<br />
-&#160; Output, with <span class="term">$config["keep_bad"] = 0</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is a.com.</code>
-<br />
-<br />
-&#160; Output, with <span class="term">$config["keep_bad"]</span>&#160;not <span class="term">0</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &amp;lt;a href=""&amp;gt;a.com&amp;lt;/a&amp;gt;.</code>
-<br />
-<br />
-&#160; See <a href="#s3.3.3">section 3.3.3</a>&#160;for differences between the various non-zero <span class="term">$config["keep_bad"]</span>&#160;values.<br />
-<br />
-&#160; htmLawed by default permits these 86 elements:<br />
-<br />
-
-<code class="code">&#160; &#160; a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var</code>
-<br />
-<br />
-&#160; Except for <span class="term">embed</span>&#160;(included because of its wide-spread use) and the Ruby elements (<span class="term">rb</span>, <span class="term">rbc</span>, <span class="term">rp</span>, <span class="term">rt</span>, <span class="term">rtc</span>, <span class="term">ruby</span>; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude <span class="term">center</span>, <span class="term">dir</span>, <span class="term">font</span>, <span class="term">isindex</span>, <span class="term">menu</span>, <span class="term">s</span>, <span class="term">strike</span>, and <span class="term">u</span>.<br />
-<br />
-&#160; With <span class="term">$config["safe"] = 1</span>, the default set will exclude <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>; see <a href="#s3.6">section 3.6</a>.<br />
-<br />
-&#160; When <span class="term">$config["elements"]</span>, which specifies allowed elements, is <em>properly</em>&#160;defined, and neither empty nor set to <span class="term">0</span>&#160;or <span class="term">&#42;</span>, the default set is not used. To have elements added to or removed from the default set, a <span class="term">+/-</span>&#160;notation is used. E.g., <span class="term">&#42;-script-object</span>&#160;implies that only <span class="term">script</span>&#160;and <span class="term">object</span>&#160;are disallowed, whereas <span class="term">&#42;+embed</span>&#160;means that <span class="term">noembed</span>&#160;is also allowed. Elements can also be specified as comma separated names. E.g., <span class="term">a, b, i</span>&#160;means only <span class="term">a</span>, <span class="term">b</span>&#160;and <span class="term">i</span>&#160;are permitted. In this notation, <span class="term">&#42;</span>, <span class="term">+</span>&#160;and <span class="term">-</span>&#160;have no significance and can actually cause a mis-reading.<br />
-<br />
-&#160; Some more examples of <span class="term">$config["elements"]</span>&#160;values indicating permitted elements (note that empty spaces are liberally allowed for clarity):<br />
-<br />
-&#160; * &#160;<span class="term">a, blockquote, code, em, strong</span>&#160;-- only <span class="term">a</span>, <span class="term">blockquote</span>, <span class="term">code</span>, <span class="term">em</span>, and <span class="term">strong</span><br />
-&#160; * &#160;<span class="term">&#42;-script</span>&#160;-- all excluding <span class="term">script</span><br />
-&#160; * &#160;<span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;-- only XHTML-Strict elements<br />
-&#160; * &#160;<span class="term">&#42;+noembed-script</span>&#160;-- all including <span class="term">noembed</span>&#160;excluding <span class="term">script</span><br />
-<br />
-&#160; Some mis-usages (and the resulting permitted elements) that can be avoided:<br />
-<br />
-&#160; * &#160;<span class="term">-&#42;</span>&#160;-- none; instead of htmLawed, one might just use, e.g., the <span class="term">htmlspecialchars()</span>&#160;PHP function<br />
-&#160; * &#160;<span class="term">&#42;, -script</span>&#160;-- all except <span class="term">script</span>; admin probably meant <span class="term">&#42;-script</span><br />
-&#160; * &#160;<span class="term">-&#42;, a, em, strong</span>&#160;-- all; admin probably meant <span class="term">a, em, strong</span><br />
-&#160; * &#160;<span class="term">&#42;</span>&#160;-- all; admin need not have set <span class="term">elements</span><br />
-&#160; * &#160;<span class="term">&#42;-form+form</span>&#160;-- all; a <span class="term">+</span>&#160;will always over-ride any <span class="term">-</span><br />
-&#160; * &#160;<span class="term">&#42;, noembed</span>&#160;-- only <span class="term">noembed</span>; admin probably meant <span class="term">&#42;+noembed</span><br />
-&#160; * &#160;<span class="term">a, +b, i</span>&#160;-- only <span class="term">a</span>&#160;and <span class="term">i</span>; admin probably meant <span class="term">a, b, i</span><br />
-<br />
-&#160; Basically, when using the <span class="term">+/-</span>&#160;notation, commas (<span class="term">,</span>) should not be used, and vice versa, and <span class="term">&#42;</span>&#160;should be used with the former but not the latter.<br />
-<br />
-&#160; <strong>Note</strong>: Even if an element that is not in the default set is allowed through <span class="term">$config["elements"]</span>, like <span class="term">noembed</span>&#160;in the last example, it will eventually be removed during tag balancing unless such balancing is turned off (<span class="term">$config["balance"]</span>&#160;set to <span class="term">0</span>). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function <span class="term">hl_bal()</span>&#160;to accommodate the element and its nesting properties.<br />
-<br />
-&#160; <strong>A possibly second way to specify allowed elements</strong>&#160;is to set <span class="term">$config["parent"]</span>&#160;to an element name that supposedly will hold the input, and to set <span class="term">$config["balance"]</span>&#160;to <span class="term">1</span>. During tag balancing (see <a href="#s3.3.3">section 3.3.3</a>), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to <span class="term">div</span>&#160;if <span class="term">$config["parent"]</span>&#160;is empty, <span class="term">body</span>, or an element not in htmLawed's default set of 86 elements.<br />
-<br />
-&#160; <em>Tag transformation</em>&#160;is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see <a href="#s3.3.2">section 3.3.2</a>.<br />
-
-<div class="sub-sub-section"><h4>
-<a name="s3.3.1" id="s3.3.1"></a><span class="item-no">3.3.1</span>&#160; Handling of comments and CDATA sections
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; <span class="term">CDATA</span>&#160;sections have the format <span class="term">&lt;![CDATA[...anything but not "]]&gt;"...]]&gt;</span>, and HTML comments, <span class="term">&lt;!--...anything but not "--&gt;"... --&gt;</span>. Neither HTML comments nor <span class="term">CDATA</span>&#160;sections can reside inside tags. HTML comments can exist anywhere else, but <span class="term">CDATA</span>&#160;sections can exist only where plain text is allowed (e.g., immediately inside <span class="term">td</span>&#160;element content but not immediately inside <span class="term">tr</span>&#160;element content).<br />
-<br />
-&#160; htmLawed (function <span class="term">hl_cmtcd()</span>) handles HTML comments or <span class="term">CDATA</span>&#160;sections depending on the values of <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>. If <span class="term">0</span>, such markup is not looked for and the text is processed like plain text. If <span class="term">1</span>, it is removed completely. If <span class="term">2</span>, it is preserved but any <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;inside are changed to entities. If <span class="term">3</span>, they are left as such.<br />
-<br />
-&#160; Note that for the last two cases, HTML comments and <span class="term">CDATA</span>&#160;sections will always be removed from tag content (function <span class="term">hl_tag()</span>).<br />
-<br />
-&#160; Examples:<br />
-<br />
-&#160; Input:<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 0, $config["cdata"] = 2</span>):<br />
-
-<code class="code">&#160; &#160; &amp;lt;-- home link --&amp;gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 1, $config["cdata"] = 2</span>):<br />
-
-<code class="code">&#160; &#160; &lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 2</span>):<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 1</span>):<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 3, $config["cdata"] = 3</span>):<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-<br />
-&#160; For standard-compliance, comments are given the form <span class="term">&lt;!--comment --&gt;</span>, and any <span class="term">--</span>&#160;in the content is made <span class="term">-</span>.<br />
-<br />
-&#160; When <span class="term">$config["safe"] = 1</span>, CDATA sections and comments are considered plain text unless <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>&#160;is explicitly specified; see <a href="#s3.6">section 3.6</a>.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.3.2" id="s3.3.2"></a><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["make_tag_strict"]</span>&#160;is set and not <span class="term">0</span>, following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function <span class="term">hl_tag2()</span>):<br />
-<br />
-&#160; * &#160;applet - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
-&#160; * &#160;center - <span class="term">div style="text-align&#58; center;"</span><br />
-&#160; * &#160;dir - <span class="term">ul</span><br />
-&#160; * &#160;embed - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
-&#160; * &#160;font (face, size, color) - &#160; &#160;<span class="term">span style="font-family&#58; ; font-size&#58; ; color&#58; ;"</span>&#160;(size transformation <a href="http://style.cleverchimp.com/font_size_intervals/altintervals.html">reference</a>)<br />
-&#160; * &#160;isindex - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
-&#160; * &#160;menu - <span class="term">ul</span><br />
-&#160; * &#160;s - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
-&#160; * &#160;strike - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
-&#160; * &#160;u - <span class="term">span style="text-decoration&#58; underline;"</span><br />
-<br />
-&#160; For an element with a pre-existing <span class="term">style</span>&#160;attribute value, the extra style properties are appended.<br />
-<br />
-&#160; Example input:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;center&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;The PHP &lt;s&gt;software&lt;/s&gt; script used for this &lt;strike&gt;web-page&lt;/strike&gt; web-page is &lt;font style="font-weight&#58; bold " face=arial size=&#39;+3&#39; color &#160; = &#160;"red &#160;"&gt;htmLawedTest.php&lt;/font&gt;, from &lt;u style= &#39;color&#58;green&#39;&gt;PHP Labware&lt;/u&gt;.</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/center&gt;</code>
-<br />
-<br />
-&#160; The output:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;div style="text-align&#58; center;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;The PHP &lt;span style="text-decoration&#58; line-through;"&gt;software&lt;/span&gt; script used for this &lt;span style="text-decoration&#58; line-through;"&gt;web-page&lt;/span&gt; web-page is &lt;span style="font-weight&#58; bold; font-family&#58; arial; color&#58; red; font-size&#58; 200%;"&gt;htmLawedTest.php&lt;/span&gt;, from &lt;span style="color&#58;green; text-decoration&#58; underline;"&gt;PHP Labware&lt;/span&gt;.</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/div&gt;</code>
-<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3.3" id="s3.3.3"></a><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>, htmLawed (function <span class="term">hl_bal()</span>) checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).<br />
-<br />
-&#160; Depending on the value of <span class="term">$config["keep_bad"]</span>&#160;(see <a href="#s2.2">section 2.2</a>&#160;and <a href="#s3.3">section 3.3</a>), illegal content may be removed or neutralized to plain text by converting &lt; and &gt; to entities:<br />
-<br />
-&#160; <span class="term">0</span>&#160;- remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see <a href="#s2.6">section 2.6</a>)<br />
-&#160; <span class="term">1</span>&#160;- neutralize tags and keep element content<br />
-&#160; <span class="term">2</span>&#160;- remove tags but keep element content<br />
-&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>, but keep element content only if text (<span class="term">pcdata</span>) is valid in parent element as per specs<br />
-&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;- &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>, but line-breaks, tabs and spaces are left<br />
-<br />
-&#160; Example input (disallowing the <span class="term">p</span>&#160;element):<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;&#42;&gt; Pseudo-tags &lt;&#42;&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; The output with <span class="term">$config["keep_bad"] = 1</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; The output with <span class="term">$config["keep_bad"] = 3</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; The output with <span class="term">$config["keep_bad"] = 6</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Non-HTML tag xml</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; An option like <span class="term">1</span>&#160;is useful, e.g., when a writer previews his submission, whereas one like <span class="term">3</span>&#160;is useful before content is finalized and made available to all.<br />
-<br />
-&#160; <strong>Note:</strong>&#160;In the example above, unlike <span class="term">&lt;&#42;&gt;</span>, <span class="term">&lt;xml&gt;</span>&#160;gets considered as a tag (even though there is no HTML element named <span class="term">xml</span>). In general, text matching the regular expression pattern <span class="term">&lt;(/?)([a-zA-Z][a-zA-Z1-6]&#42;)([^&gt;]&#42;?)\s?&gt;</span>&#160;is considered a tag (phrase enclosed by the angled brackets <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...).<br />
-<br />
-&#160; Nesting/content rules for each of the 86 elements in htmLawed's default set (see <a href="#s3.3">section 3.3</a>) are defined in function <span class="term">hl_bal()</span>. This means that if a non-standard element besides <span class="term">embed</span>&#160;is being permitted through <span class="term">$config["elements"]</span>, the element's tag content will end up getting removed if <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>.<br />
-<br />
-&#160; Plain text and/or certain elements nested inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as <span class="term">form</span>, the input <span class="term">B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;</span>&#160;is converted to <span class="term">&lt;div&gt;B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;&lt;/div&gt;</span>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3.4" id="s3.3.4"></a><span class="item-no">3.3.4</span>&#160; Elements requiring child elements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; As per specs, the following elements require legal child elements nested inside them:<br />
-<br />
-
-<code class="code">&#160; &#160; blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul</code>
-<br />
-<br />
-&#160; In some cases, the specs stipulate the number and/or the ordering of the child elements. A <span class="term">table</span>&#160;can have 0 or 1 <span class="term">caption</span>, <span class="term">tbody</span>, <span class="term">tfoot</span>, and <span class="term">thead</span>, but they must be in this order: <span class="term">caption</span>, <span class="term">thead</span>, <span class="term">tfoot</span>, <span class="term">tbody</span>.<br />
-<br />
-&#160; htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.<br />
-<br />
-&#160; With <span class="term">$config["direct_list_nest"]</span>&#160;set to <span class="term">1</span>, htmLawed will allow direct nesting of an <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;list within another <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;without requiring the child list to be within an <span class="term">li</span>&#160;of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter <span class="term">$config["direct_list_nest"]</span>&#160;has no effect if tag-balancing (<a href="#s3.3.3">section 3.3.3</a>) is turned off.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3.5" id="s3.3.5"></a><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; By default, htmLawed will neither <em>beautify</em>&#160;HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)<br />
-<br />
-&#160; As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside <span class="term">pre</span>&#160;elements) are all considered equivalent, and referred to as <em>white-spaces</em>. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space <em>normalization</em>&#160;allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such <em>pretty</em>&#160;HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.<br />
-<br />
-&#160; With the <span class="term">$config</span>&#160;parameter <span class="term">tidy</span>, htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides <span class="term">pre</span>, the <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, CDATA sections, and HTML comments are not subjected to the tidying process.<br />
-<br />
-&#160; To <em>compact</em>, use <span class="term">$config["tidy"] = -1</span>; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.<br />
-<br />
-&#160; To <em>beautify</em>, <span class="term">$config["tidy"]</span>&#160;is set as <span class="term">1</span>, or for customized tidying, as a string like <span class="term">2s2n</span>. The <span class="term">s</span>&#160;or <span class="term">t</span>&#160;character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The <span class="term">r</span>&#160;and <span class="term">n</span>&#160;characters are used to specify line-break characters: <span class="term">n</span>&#160;for <span class="term">\n</span>&#160;(Unix/Mac OS X line-breaks), <span class="term">rn</span>&#160;or <span class="term">nr</span>&#160;for <span class="term">\r\n</span>&#160;(Windows/DOS line-breaks), or <span class="term">r</span>&#160;for <span class="term">\r</span>.<br />
-<br />
-&#160; The <span class="term">$config["tidy"]</span>&#160;value of <span class="term">1</span>&#160;is equivalent to <span class="term">2s0n</span>. Other <span class="term">$config["tidy"]</span>&#160;values are read loosely: a value of <span class="term">4</span>&#160;is equivalent to <span class="term">4s0n</span>; <span class="term">t2</span>, to <span class="term">1t2n</span>; <span class="term">s</span>, to <span class="term">2s0n</span>; <span class="term">2TR</span>, to <span class="term">2t0r</span>; <span class="term">T1</span>, to <span class="term">1t1n</span>; <span class="term">nr3</span>, to <span class="term">3s0nr</span>, and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.<br />
-<br />
-&#160; Input formatting using <span class="term">$config["tidy"]</span>&#160;is not recommended when input text has mixed markup (like HTML + PHP).<br />
-
-</div>
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4" id="s3.4"></a><span class="item-no">3.4</span>&#160; Attributes
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the <span class="term">embed</span>&#160;element (the non-standard <span class="term">embed</span>&#160;element is supported in htmLawed because of its widespread use), and the the <span class="term">xml&#58;space</span>&#160;attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in <a href="#s5.2">section 5.2</a>.<br />
-<br />
-&#160; When <span class="term">$config["deny_attribute"]</span>&#160;is not set, or set to <span class="term">0</span>, or empty (<span class="term">""</span>), all the 111 attributes are permitted. Otherwise, <span class="term">$config["deny_attribute"]</span>&#160;can be set as a list of comma-separated names of the denied attributes. <span class="term">on&#42;</span>&#160;can be used to refer to the group of potentially dangerous, script-accepting attributes: <span class="term">onblur</span>, <span class="term">onchange</span>, <span class="term">onclick</span>, <span class="term">ondblclick</span>, <span class="term">onfocus</span>, <span class="term">onkeydown</span>, <span class="term">onkeypress</span>, <span class="term">onkeyup</span>, <span class="term">onmousedown</span>, <span class="term">onmousemove</span>, <span class="term">onmouseout</span>, <span class="term">onmouseover</span>, <span class="term">onmouseup</span>, <span class="term">onreset</span>, <span class="term">onselect</span>&#160;and <span class="term">onsubmit</span>.<br />
-<br />
-&#160; Note that attributes specified in <span class="term">$config["deny_attribute"]</span>&#160;are denied globally, for all elements. To deny attributes for only specific elements, <span class="term">$spec</span>&#160;(see <a href="#s2.3">section 2.3</a>) can be used. <span class="term">$spec</span>&#160;can also be used to element-specifically permit an attribute otherwise denied through <span class="term">$config["deny_attribute"]</span>.<br />
-<br />
-&#160; With <span class="term">$config["safe"] = 1</span>&#160;(<a href="#s3.6">section 3.6</a>), the <span class="term">on&#42;</span>&#160;attributes are automatically disallowed.<br />
-<br />
-&#160; <strong>Note</strong>: To deny all but a few attributes globally, a simpler way to specify <span class="term">$config["deny_attribute"]</span>&#160;would be to use the notation <span class="term">&#42; -attribute1 -attribute2 ...</span>. Thus, a value of <span class="term">&#42; -title -href</span>&#160;implies that except <span class="term">href</span>&#160;and <span class="term">title</span>&#160;(where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter <span class="term">safe</span>&#160;(<a href="#s3.6">section 3.6</a>) will have no effect on <span class="term">deny_attribute</span>.<br />
-<br />
-&#160; htmLawed (function <span class="term">hl_tag()</span>) also:<br />
-<br />
-&#160; * &#160;Lower-cases attribute names<br />
-&#160; * &#160;Removes duplicate attributes (last one stays)<br />
-&#160; * &#160;Gives attributes the form <span class="term">name="value"</span>&#160;and single-spaces them, removing unnecessary white-spacing<br />
-&#160; * &#160;Provides <em>required</em>&#160;attributes (see <a href="#s3.4.1">section 3.4.1</a>)<br />
-&#160; * &#160;Double-quotes values and escapes any <span class="term">"</span>&#160;inside them<br />
-&#160; * &#160;Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point <span class="term">ad</span>) in the values with spaces<br />
-&#160; * &#160;Allows custom function to additionally filter/modify attribute values (see <a href="#s3.4.9">section 3.4.9</a>)<br />
-
-<div class="sub-sub-section"><h4>
-<a name="s3.4.1" id="s3.4.1"></a><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If indicated attributes for the following elements are found missing, htmLawed (function <span class="term">hl_tag()</span>) will add them (with values same as attribute names unless indicated otherwise below):<br />
-<br />
-&#160; * &#160;area - alt (<span class="term">area</span>)<br />
-&#160; * &#160;area, img - src, alt (<span class="term">image</span>)<br />
-&#160; * &#160;bdo - dir (<span class="term">ltr</span>)<br />
-&#160; * &#160;form - action<br />
-&#160; * &#160;map - name<br />
-&#160; * &#160;optgroup - label<br />
-&#160; * &#160;param - name<br />
-&#160; * &#160;script - type (<span class="term">text/javascript</span>)<br />
-&#160; * &#160;textarea - rows (<span class="term">10</span>), cols (<span class="term">50</span>)<br />
-<br />
-&#160; Additionally, with <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">1</span>&#160;or <span class="term">2</span>, if the <span class="term">lang</span>&#160;but not the <span class="term">xml&#58;lang</span>&#160;attribute is declared, then the latter is added too, with a value copied from that of <span class="term">lang</span>. This is for better standard-compliance. With <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">2</span>, the <span class="term">lang</span>&#160;attribute is removed (XHTML 1.1 specs).<br />
-<br />
-&#160; Note that the <span class="term">name</span>&#160;attribute for <span class="term">map</span>, invalid in XHTML 1.1, is also transformed if required -- see <a href="#s3.4.6">section 3.4.6</a>.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.2" id="s3.4.2"></a><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["unique_ids"]</span>&#160;is <span class="term">1</span>, htmLawed (function <span class="term">hl_tag()</span>) removes <span class="term">id</span>&#160;attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">-</span>&#160;and <span class="term">_</span>) or duplicate. If <span class="term">$config["unique_ids"]</span>&#160;is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">_</span>&#160;and <span class="term">-</span>.<br />
-<br />
-&#160; Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of <span class="term">id</span>&#160;values as it uses a global variable (<span class="term">$GLOBALS["hl_Ids"]</span>&#160;array). Further, an admin can restrict the use of certain <span class="term">id</span>&#160;values by presetting this variable before htmLawed is called into use. E.g.:<br />
-<br />
-
-<code class="code">&#160; &#160; $GLOBALS[&#39;hl_Ids&#39;] = array(&#39;top&#39;=&gt;1, &#39;bottom&#39;=&gt;1, &#39;myform&#39;=&gt;1); // id values not allowed in input</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text); // filter input</code>
-<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.3" id="s3.4.3"></a><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the <span class="term">afp</span>&#160;scheme is not permitted, then <span class="term">&lt;a href="afp&#58;//domain.org"&gt;</span>&#160;becomes <span class="term">&lt;a href="denied&#58;afp&#58;//domain.org"&gt;</span>, and if Javascript is not permitted <span class="term">&lt;a onclick="javascript&#58;xss();"&gt;</span>&#160;becomes <span class="term">&lt;a onclick="denied&#58;javascript&#58;xss();"&gt;</span>.<br />
-<br />
-&#160; By default htmLawed permits these schemes in URLs for the <span class="term">href</span>&#160;attribute:<br />
-<br />
-
-<code class="code">&#160; &#160; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet</code>
-<br />
-<br />
-&#160; Also, only <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted in attributes whose names start with <span class="term">o</span>&#160;(like <span class="term">onmouseover</span>), and in these attributes that accept URLs:<br />
-<br />
-
-<code class="code">&#160; &#160; action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap</code>
-<br />
-<br />
-&#160; These default sets are used when <span class="term">$config["schemes"]</span>&#160;is not set (see <a href="#s2.2">section 2.2</a>). To over-ride the defaults, <span class="term">$config["schemes"]</span>&#160;is defined as a string of semi-colon-separated sub-strings of type <span class="term">attribute&#58; comma-separated schemes</span>. E.g., <span class="term">href&#58; mailto, http, https; onclick&#58; javascript; src&#58; http, https</span>. For unspecified attributes, <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted. This can be changed by passing schemes for <span class="term">&#42;</span>&#160;in <span class="term">$config["schemes"]</span>. E.g., <span class="term">href&#58; mailto, http, https; &#42;&#58; https, https</span>.<br />
-<br />
-&#160; <span class="term">&#42;</span>&#160;can be put in the list of schemes to permit all protocols. E.g., <span class="term">style&#58; &#42;; img&#58; http, https</span>&#160;results in protocols not being checked in <span class="term">style</span>&#160;attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (<a href="#s3.4.4">section 3.4.4</a>) is not done.<br />
-<br />
-&#160; Thus, <em>to allow Javascript</em>, one can set <span class="term">$config["schemes"]</span>&#160;as <span class="term">href&#58; mailto, http, https; &#42;&#58; http, https, javascript</span>, or <span class="term">href&#58; mailto, http, https, javascript; &#42;&#58; http, https, javascript</span>, or <span class="term">&#42;&#58; &#42;</span>, and so on.<br />
-<br />
-&#160; As a side-note, one may find <span class="term">style&#58; &#42;</span>&#160;useful as URLs in <span class="term">style</span>&#160;attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.<br />
-<br />
-&#160; <span class="term">!</span>&#160;can be put in the list of schemes to disallow all protocols as well as <em>local</em>&#160;URLs. Thus, with <span class="term">href&#58; http, style&#58; !</span>, '&lt;a href="http://cnn.com" style="background-image: url('local.jpg');"&gt;CNN&lt;/a&gt;' will become '&lt;a href="http://cnn.com" style="background-image: url('denied:local.jpg');"&gt;CNN&lt;/a&gt;'.<br />
-<br />
-&#160; <strong>Note</strong>: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string <span class="term">src</span>&#160;(e.g., <span class="term">dynsrc</span>) or starts with <span class="term">o</span>&#160;(e.g., <span class="term">onbeforecopy</span>).<br />
-<br />
-&#160; With <span class="term">$config["safe"] = 1</span>, all URLs are disallowed in the <span class="term">style</span>&#160;attribute values.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.4" id="s3.4.4"></a><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs in attribute values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can make absolute URLs in attributes like <span class="term">href</span>&#160;relative (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">-1</span>), and vice versa (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">1</span>). URLs in scripts are not considered for this, and so are URLs like <span class="term">#section_6</span>&#160;(fragment), <span class="term">?name=Tim#show</span>&#160;(starting with query string), and <span class="term">;var=1?name=Tim#show</span>&#160;(starting with parameters). Further, this requires that <span class="term">$config["base_url"]</span>&#160;be set properly, with the <span class="term">&#58;//</span>&#160;and a trailing slash (<span class="term">/</span>), with no query string, etc. E.g., <span class="term">file&#58;///D&#58;/page/</span>, <span class="term">https&#58;//abc.com/x/y/</span>, or <span class="term">http&#58;//localhost/demo/</span>&#160;are okay, but <span class="term">file&#58;///D&#58;/page/?help=1</span>, <span class="term">abc.com/x/y/</span>&#160;and <span class="term">http&#58;//localhost/demo/index.htm</span>&#160;are not.<br />
-<br />
-&#160; For making absolute URLs relative, only those URLs that have the <span class="term">$config["base_url"]</span>&#160;string at the beginning are converted. E.g., with <span class="term">$config["base_url"] = "https&#58;//abc.com/x/y/"</span>, <span class="term">https&#58;//abc.com/x/y/a.gif</span>&#160;and <span class="term">https&#58;//abc.com/x/y/z/b.gif</span>&#160;become <span class="term">a.gif</span>&#160;and <span class="term">z/b.gif</span>&#160;respectively, while <span class="term">https&#58;//abc.com/x/c.gif</span>&#160;is not changed.<br />
-<br />
-&#160; When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See <a href="#s5.5">section 5.5</a>&#160;for more about the URL specification as per RFC <a href="http://www.ietf.org/rfc/rfc1808.txt">1808</a>.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.5" id="s3.4.5"></a><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Optionally, for standard-compliance, htmLawed (function <span class="term">hl_tag()</span>) lower-cases standard attribute values to give, e.g., <span class="term">input type="password"</span>&#160;instead of <span class="term">input type="Password"</span>, if <span class="term">$config["lc_std_val"]</span>&#160;is <span class="term">1</span>. Attribute values matching those listed below for any of the elements (plus those for the <span class="term">type</span>&#160;attribute of <span class="term">button</span>&#160;or <span class="term">input</span>) are lower-cased:<br />
-<br />
-
-<code class="code">&#160; &#160; all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml&#58;space</code>
-<br />
-<br />
-&#160; The following <em>empty</em>&#160;(<em>minimized</em>) attributes are always assigned lower-cased values (same as the names):<br />
-<br />
-
-<code class="code">&#160; &#160; checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected</code>
-<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.6" id="s3.4.6"></a><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["no_deprecated_attr"]</span>&#160;is <span class="term">0</span>, then deprecated attributes (see appendix in <a href="#s5.2">section 5.2</a>) are removed and, in most cases, their values are transformed to CSS style properties and added to the <span class="term">style</span>&#160;attributes (function <span class="term">hl_tag()</span>). Except for <span class="term">bordercolor</span>&#160;for <span class="term">table</span>, <span class="term">tr</span>&#160;and <span class="term">td</span>, the scores of proprietary attributes that were never part of any cross-browser standard are not supported.<br />
-<br />
-&#160; <strong>Note</strong>: The attribute <span class="term">target</span>&#160;for <span class="term">a</span>&#160;is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.<br />
-<br />
-&#160; * &#160;align - for <span class="term">img</span>&#160;with value of <span class="term">left</span>&#160;or <span class="term">right</span>, becomes, e.g., <span class="term">float&#58; left</span>; for <span class="term">div</span>&#160;and <span class="term">table</span>&#160;with value <span class="term">center</span>, becomes <span class="term">margin&#58; auto</span>; all others become, e.g., <span class="term">text-align&#58; right</span><br />
-<br />
-&#160; * &#160;bgcolor - E.g., <span class="term">bgcolor="#ffffff"</span>&#160;becomes <span class="term">background-color&#58; #ffffff</span><br />
-&#160; * &#160;border - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
-&#160; * &#160;bordercolor - E.g., <span class="term">bordercolor=#999999</span>&#160;becomes <span class="term">border-color&#58; #999999;</span><br />
-&#160; * &#160;compact - <span class="term">font-size&#58; 85%</span><br />
-&#160; * &#160;clear - E.g., 'clear="all" becomes <span class="term">clear&#58; both</span><br />
-<br />
-&#160; * &#160;height - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span>&#160;and <span class="term">height="&#42;"</span>&#160;becomes <span class="term">height&#58; auto</span><br />
-<br />
-&#160; * &#160;hspace - E.g., <span class="term">hspace="10"</span>&#160;becomes <span class="term">margin-left&#58; 10px; margin-right&#58; 10px</span><br />
-&#160; * &#160;language - <span class="term">language="VBScript"</span>&#160;becomes <span class="term">type="text/vbscript"</span><br />
-&#160; * &#160;name - E.g., <span class="term">name="xx"</span>&#160;becomes <span class="term">id="xx"</span><br />
-&#160; * &#160;noshade - <span class="term">border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray</span><br />
-&#160; * &#160;nowrap - <span class="term">white-space&#58; nowrap</span><br />
-&#160; * &#160;size - E.g., <span class="term">size="10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
-&#160; * &#160;start - removed<br />
-&#160; * &#160;type - E.g., <span class="term">type="i"</span>&#160;becomes <span class="term">list-style-type&#58; lower-roman</span><br />
-&#160; * &#160;value - removed<br />
-&#160; * &#160;vspace - E.g., <span class="term">vspace="10"</span>&#160;becomes <span class="term">margin-top&#58; 10px; margin-bottom&#58; 10px</span><br />
-&#160; * &#160;width - like <span class="term">height</span><br />
-<br />
-&#160; Example input:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" name="dad&#39;s" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" name="dad" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br clear="left" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;hr noshade size="1" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding&#58;5px;" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;table width="50em" align="center" bgcolor="red"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td width="20%"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;div align="center"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;h3 align="right"&gt;Section&lt;/h3&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;p align="right"&gt;Para&lt;/p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;ol type="a" start="e"&gt;&lt;li value="x"&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td width="&#42;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;ol type="1"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/table&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br clear="all" /&gt;</code>
-<br />
-<br />
-&#160; And the output with <span class="term">$config["no_deprecated_attr"] = 1</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br style="clear&#58; left;" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;hr style="border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray; size&#58; 1px;" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;img src="i.gif" alt="image" width="10em" height="20" style="padding&#58;5px; float&#58; left; margin-left&#58; 10px; margin-right&#58; 10px; margin-top&#58; 10px; margin-bottom&#58; 10px; border&#58; 1px;" id="img" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;table width="50em" style="margin&#58; auto; background-color&#58; red;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; 20%;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;div style="margin&#58; auto;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;h3 style="text-align&#58; right;"&gt;Section&lt;/h3&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;p style="text-align&#58; right;"&gt;Para&lt;/p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;ol style="list-style-type&#58; lower-latin;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; auto;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;ol style="list-style-type&#58; decimal;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/table&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br style="clear&#58; both;" /&gt;</code>
-<br />
-<br />
-&#160; For <span class="term">lang</span>, deprecated in XHTML 1.1, transformation is taken care of through <span class="term">$config["xml&#58;lang"]</span>; see <a href="#s3.4.1">section 3.4.1</a>.<br />
-<br />
-&#160; The attribute <span class="term">name</span>&#160;is deprecated in <span class="term">form</span>, <span class="term">iframe</span>, and <span class="term">img</span>, and is replaced with <span class="term">id</span>&#160;if an <span class="term">id</span>&#160;attribute doesn't exist and if the <span class="term">name</span>&#160;value is appropriate for <span class="term">id</span>. For such replacements for <span class="term">a</span>&#160;and <span class="term">map</span>, for which the <span class="term">name</span>&#160;attribute is deprecated in XHTML 1.1, <span class="term">$config["no_deprecated_attr"]</span>&#160;should be set to <span class="term">2</span>&#160;(when set to <span class="term">1</span>, for these two elements, the <span class="term">name</span>&#160;attribute is retained).<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4.7" id="s3.4.7"></a><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span>
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed (function <span class="term">hl_tag()</span>) can check the <span class="term">href</span>&#160;attribute values (link addresses) as an anti-spam (email or link spam) measure.<br />
-<br />
-&#160; If <span class="term">$config["anti_mail_spam"]</span>&#160;is not <span class="term">0</span>, the <span class="term">@</span>&#160;of email addresses in <span class="term">href</span>&#160;values like <span class="term">mailto&#58;a@b.com</span>&#160;is replaced with text specified by <span class="term">$config["anti_mail_spam"]</span>. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., <span class="term">&lt;remove_this_antispam&gt;@</span>&#160;(makes the example address <span class="term">a&lt;remove_this_antispam&gt;@b.com</span>).<br />
-<br />
-&#160; For regular links, one can choose to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the <span class="term">href</span>&#160;value altogether (disable the link).<br />
-<br />
-&#160; For use of these options, <span class="term">$config["anti_link_spam"]</span>&#160;should be set as an array with values <span class="term">regex1</span>&#160;and <span class="term">regex2</span>, both or one of which can be empty (like <span class="term">array("", "regex2")</span>) to indicate that that option is not to be used. Otherwise, <span class="term">regex1</span>&#160;or <span class="term">regex2</span>&#160;should be PHP- and PCRE-compatible regular expression patterns: <span class="term">href</span>&#160;values will be matched against them and those matching the pattern will accordingly be treated.<br />
-<br />
-&#160; Note that the regular expressions should have <em>delimiters</em>, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.<br />
-<br />
-&#160; An example, to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;for all links, and to disable links that do not point to domains <span class="term">abc.com</span>&#160;and <span class="term">xyz.org</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; $config["anti_link_spam"] = array(&#39;&#96;.&#96;&#39;, &#39;&#96;&#58;//\W&#42;(?!(abc\.com|xyz\.org))&#96;&#39;);</code>
-<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4.8" id="s3.4.8"></a><span class="item-no">3.4.8</span>&#160; Inline style properties
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the <span class="term">style</span>&#160;attributes. (CSS properties like <span class="term">background-image</span>&#160;that accept URLs in their values are noted in <a href="#s5.3">section 5.3</a>.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting <span class="term">$config["css_expression"]</span>&#160;to <span class="term">1</span>&#160;(default setting). Note that when <span class="term">$config["css_expression"]</span>&#160;is set to <span class="term">1</span>, htmLawed will remove <span class="term">/&#42;</span>&#160;from the <span class="term">style</span>&#160;values.<br />
-<br />
-&#160; <strong>Note</strong>: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the <span class="term">style</span>&#160;attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off (<span class="term">$config["schemes"] = "...style&#58;&#42;..."</span>, see <a href="#s3.4.3">section 3.4.3</a>, and <span class="term">$config["css_expression"] = 0</span>). Alternately, admins can use their own custom function for finer handling of <span class="term">style</span>&#160;values through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; It is also possible to have htmLawed let through any <span class="term">style</span>&#160;value by setting <span class="term">$config["style_pass"]</span>&#160;to <span class="term">1</span>.<br />
-<br />
-&#160; As such, it is better to set up a CSS file with class declarations, disallow the <span class="term">style</span>&#160;attribute, set a <span class="term">$spec</span>&#160;rule (see <a href="#s2.3">section 2.3</a>) for <span class="term">class</span>&#160;for the <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;parameter, and ask writers to make use of the <span class="term">class</span>&#160;attribute.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4.9" id="s3.4.9"></a><span class="item-no">3.4.9</span>&#160; Hook function for tag content
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).<br />
-<br />
-&#160; When <span class="term">$config</span>&#160;parameter <span class="term">hook_tag</span>&#160;is set to the name of a function, htmLawed (function <span class="term">hl_tag()</span>) will pass on the element name, and, in the case of an opening tag, the <em>finalized</em>&#160;attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like <span class="term">&lt;element_name attribute_1_name="attribute_1_value"...&gt;</span>&#160;(for empty elements like <span class="term">img</span>&#160;and <span class="term">input</span>, the element-closing slash <span class="term">/</span>&#160;should also be included), etc.<br />
-<br />
-&#160; Any <span class="term">hook_tag</span>&#160;function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as <span class="term">a</span>&#160;in the closing <span class="term">&lt;/a&gt;</span>&#160;tag of the element <span class="term">&lt;a href="http&#58;//cnn.com"&gt;CNN&lt;/a&gt;</span>. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like <span class="term">&lt;/a&gt;</span>).<br />
-<br />
-&#160; This is a <strong>powerful functionality</strong>&#160;that can be exploited for various objectives: consolidate-and-convert inline <span class="term">style</span>&#160;attributes to <span class="term">class</span>, convert <span class="term">embed</span>&#160;elements to <span class="term">object</span>, permit only one <span class="term">caption</span>&#160;element in a <span class="term">table</span>&#160;element, disallow embedding of certain types of media, <strong>inject HTML</strong>, use <a href="http://csstidy.sourceforge.net">CSSTidy</a>&#160;to sanitize <span class="term">style</span>&#160;attribute values, etc.<br />
-<br />
-&#160; As an example, the custom hook code below can be used to force a series of specifically ordered <span class="term">id</span>&#160;attributes on all elements, and a specific <span class="term">param</span>&#160;element inside all <span class="term">object</span>&#160;elements:<br />
-<br />
-
-<code class="code">&#160; &#160; function my_tag_function($element, $attribute_array=0){</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; // If second argument is not received, it means a closing tag is being handled</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if(is_numeric($attribute_array)){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; return "&lt;/$element&gt;";</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; static $id = 0;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; // Remove any duplicate element</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if($element == &#39;param&#39; &amp;&amp; isset($attribute_array[&#39;allowscriptaccess&#39;])){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; return &#39;&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; $new_element = &#39;&#39;;</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; // Force a serialized ID number</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; $attribute_array[&#39;id&#39;] = &#39;my_&#39;. $id;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; ++$id;</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; // Inject param for allowscriptaccess</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if($element == &#39;object&#39;){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; $new_element = &#39;&lt;param id=&#39;my_&#39;. $id; allowscriptaccess="never" /&gt;&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; ++$id;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; $string = &#39;&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; foreach($attribute_array as $k=&gt;$v){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; $string .= " {$k}=\"{$v}\"";</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; static $empty_elements = array(&#39;area&#39;=&gt;1, &#39;br&#39;=&gt;1, &#39;col&#39;=&gt;1, &#39;embed&#39;=&gt;1, &#39;hr&#39;=&gt;1, &#39;img&#39;=&gt;1, &#39;input&#39;=&gt;1, &#39;isindex&#39;=&gt;1, &#39;param&#39;=&gt;1);</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; return "&lt;{$element}{$string}". (isset($in_array($element, $empty_elements) ? &#39; /&#39; &#58; &#39;&#39;). &#39;&gt;&#39;. $new_element;</code>
-<br />
-
-<code class="code">&#160; &#160; }</code>
-<br />
-<br />
-&#160; The <span class="term">hook_tag</span>&#160;parameter is different from the <span class="term">hook</span>&#160;parameter (<a href="#s3.7">section 3.7</a>).<br />
-<br />
-&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
-
-</div>
-</div>
-<div class="sub-section"><h3>
-<a name="s3.5" id="s3.5"></a><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["valid_xhtml"]</span>&#160;is set to <span class="term">1</span>, some relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in <a href="#s2.2">section 2.2</a>) are auto-adjusted. This allows one to pass the <span class="term">$config</span>&#160;argument with a simpler value. If a value for a parameter auto-set through <span class="term">valid_xhtml</span>&#160;is still manually provided, then that value will over-ride the auto-set value.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.6" id="s3.6"></a><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; <em>Safe</em>&#160;HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as <span class="term">script</span>&#160;and <span class="term">object</span>, and attributes such as <span class="term">onmouseover</span>&#160;and <span class="term">style</span>&#160;are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered <span class="term">safe</span>&#160;depends on the nature of the web application and the trust-level accorded to its users.<br />
-<br />
-&#160; htmLawed allows an admin to use <span class="term">$config["safe"]</span>&#160;to auto-adjust multiple <span class="term">$config</span>&#160;parameters (such as <span class="term">elements</span>&#160;which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by <span class="term">"</span>&#160;in <a href="#s2.2">section 2.2</a>). Thus, one can pass the <span class="term">$config</span>&#160;argument with a simpler value.<br />
-<br />
-&#160; With the value of <span class="term">1</span>, htmLawed considers <span class="term">CDATA</span>&#160;sections and HTML comments as plain text, and prohibits the <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>&#160;elements, and the <span class="term">on&#42;</span>&#160;attributes like <span class="term">onclick</span>. ( There are <span class="term">$config</span>&#160;parameters like <span class="term">css_expression</span>&#160;that are not affected by the value set for <span class="term">safe</span>&#160;but whose default values still contribute towards a more <em>safe</em>&#160;output.) Further, URLs with schemes (see <a href="#s3.4.3">section 3.4.3</a>) are neutralized so that, e.g., <span class="term">style="moz-binding&#58;url(http&#58;//danger)"</span>&#160;becomes <span class="term">style="moz-binding&#58;url(denied&#58;http&#58;//danger)"</span>.<br />
-<br />
-&#160; Admins, however, may still want to completely deny the <span class="term">style</span>&#160;attribute, e.g., with code like<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;safe&#39;=&gt;1, &#39;deny_attribute&#39;=&gt;&#39;style&#39;));</code>
-<br />
-<br />
-&#160; Permitting the <span class="term">style</span>&#160;attribute brings in risks of <em>click-jacking</em>, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check <span class="term">style</span>&#160;values. It does provide ways for the code-developer implementing htmLawed to do such checks through the <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.<br />
-<br />
-&#160; If a value for a parameter auto-set through <span class="term">safe</span>&#160;is still manually provided, then that value can over-ride the auto-set value. E.g., with <span class="term">$config["safe"] = 1</span>&#160;and <span class="term">$config["elements"] = "&#42;+script"</span>, <span class="term">script</span>, but not <span class="term">applet</span>, is allowed.<br />
-<br />
-&#160; A page illustrating the efficacy of htmLawed's anti-XSS abilities with <span class="term">safe</span>&#160;set to <span class="term">1</span>&#160;against XSS vectors listed by <a href="http://ha.ckers.org/xss.html">RSnake</a>&#160;may be available <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm">here</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.7" id="s3.7"></a><span class="item-no">3.7</span>&#160; Using a hook function
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["hook"]</span>&#160;is not set to <span class="term">0</span>, then htmLawed will allow preliminarily processed input to be altered by a hook function named by <span class="term">$config["hook"]</span>&#160;before starting the main work (but after handling of characters, entities, HTML comments and <span class="term">CDATA</span>&#160;sections -- see code for function <span class="term">htmLawed()</span>).<br />
-<br />
-&#160; The hook function also allows one to alter the <em>finalized</em>&#160;values of <span class="term">$config</span>&#160;and <span class="term">$spec</span>.<br />
-<br />
-&#160; Note that the <span class="term">hook</span>&#160;parameter is different from the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.8" id="s3.8"></a><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values to a variable named by <span class="term">$config["show_setting"]</span>. The variable, made global by htmLawed, is set as an array with three keys: <span class="term">config</span>, with the <span class="term">$config</span>&#160;value, <span class="term">spec</span>, with the <span class="term">$spec</span>&#160;value, and <span class="term">time</span>, with a value that is the Unix time (the output of PHP's <span class="term">microtime()</span>&#160;function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.<br />
-<br />
-&#160; The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.9" id="s3.9"></a><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed does not remove certain characters that though invalid are nevertheless discouraged in HTML documents as per the specs (see <a href="#s5.1">section 5.1</a>). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).<br />
-<br />
-&#160; To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters with some of the HTML-discouraged characters (see <a href="#s3.1.2">section 3.1.2</a>). Post-htmLawed processing, the replacements are reverted.<br />
-<br />
-&#160; An example (mixed HTML and PHP code in input text):<br />
-<br />
-
-<code class="code">&#160; &#160; $text = preg_replace(&#39;&#96;&lt;\?php(.+?)\?&gt;&#96;sm&#39;, "\x83?php\\1?\x84", $text);</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = preg_replace(&#39;&#96;\x83\?php(.+?)\?\x84&#96;sm&#39;, &#39;&lt;?php$1?&gt;&#39;, $processed);</code>
-<br />
-<br />
-&#160; This code will not work if <span class="term">$config["clean_ms_char"]</span>&#160;is set to <span class="term">1</span>&#160;(<a href="#s3.1">section 3.1</a>), in which case one should instead deploy a hook function (<a href="#s3.7">section 3.7</a>). (htmLawed internally uses certain control characters, code-points <span class="term">1</span>&#160;to <span class="term">7</span>, and use of these characters as markers in the logic of hook functions may cause issues.)<br />
-<br />
-&#160; Admins may also be able to use <span class="term">$config["and_mark"]</span>&#160;to deal with such mixed markup; see <a href="#s3.2">section 3.2</a>.<br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s4" id="s4"></a><span class="item-no">4</span>&#160; Other
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<div class="sub-section"><h3>
-<a name="s4.1" id="s4.1"></a><span class="item-no">4.1</span>&#160; Support
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; A careful re-reading of this documentation will very likely answer your questions.<br />
-<br />
-&#160; Software updates and forum-based community-support may be found at <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at <a href="http://php.net">http://php.net</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.2" id="s4.2"></a><span class="item-no">4.2</span>&#160; Known issues
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; See <a href="#s2.8">section 2.8</a>.<br />
-<br />
-&#160; Readers are advised to cross-check information given in this document.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.3" id="s4.3"></a><span class="item-no">4.3</span>&#160; Change-log
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the <span class="term">htmLawed.php</span>&#160;file may be updated independently if the secondary files are revised.)<br />
-<br />
-&#160; <em>Version number - Release date. Notes</em><br />
-<br />
-&#160; 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. <span class="term">$config["hook_tag"]</span>, if specified, now receives names of elements in closing tags.<br />
-<br />
-&#160; 1.1.10 - 22 October 2011. Fix for a bug in the <span class="term">tidy</span>&#160;functionality that caused the entire input to be replaced with a single space; new parameter, <span class="term">$config["direct_list_nest"]</span>&#160;to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)<br />
-<br />
-&#160; 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of <span class="term">li</span>&#160;within <span class="term">dir</span><br />
-<br />
-&#160; 1.1.9.4 - 3 July 2010. Parameter <span class="term">schemes</span>&#160;now accepts <span class="term">!</span>&#160;so any URL, even a local one, can be <em>denied</em>. An issue in which a second URL value in <span class="term">style</span>&#160;properties was not checked was fixed.<br />
-<br />
-&#160; 1.1.9.3 - 17 May 2010. Checks for correct nesting of <span class="term">param</span><br />
-<br />
-&#160; 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes<br />
-<br />
-&#160; 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for <span class="term">flashvars</span>&#160;attribute for <span class="term">embed</span><br />
-<br />
-&#160; 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values<br />
-<br />
-&#160; 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice<br />
-<br />
-&#160; 1.1.8 - 23 April 2009. Parameter <span class="term">deny_attribute</span>&#160;now accepts the wild-card <span class="term">&#42;</span>, making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting <span class="term">$spec</span><br />
-<br />
-&#160; 1.1.7 - 11-12 March 2009. Attributes globally denied through <span class="term">deny_attribute</span>&#160;can be allowed element-specifically through <span class="term">$spec</span>; <span class="term">$config["style_pass"]</span>&#160;allowing letting through any <span class="term">style</span>&#160;value introduced; altered logic to catch certain types of dynamic crafted CSS expressions<br />
-<br />
-&#160; 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions<br />
-<br />
-&#160; 1.1.2 - 22 January 2009. Fixed bug in parsing of <span class="term">font</span>&#160;attributes during tag transformation<br />
-<br />
-&#160; 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent<br />
-<br />
-&#160; 1.1 - 29 June 2008. <span class="term">$config["hook_tag"]</span>&#160;and <span class="term">$config["format"]</span>&#160;introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug<br />
-<br />
-&#160; 1.0.9 - 11 June 2008. Fixed bug in invalid HTML code-point entity check<br />
-<br />
-&#160; 1.0.8 - 15 May 2008. <span class="term">bordercolor</span>&#160;attribute for <span class="term">table</span>, <span class="term">td</span>&#160;and <span class="term">tr</span><br />
-<br />
-&#160; 1.0.7 - 1 May 2008. Support for <span class="term">wmode</span>&#160;attribute for <span class="term">embed</span>; <span class="term">$config["show_setting"]</span>&#160;introduced; improved <span class="term">$config["elements"]</span>&#160;evaluation<br />
-<br />
-&#160; 1.0.6 - 20 April 2008. <span class="term">$config["and_mark"]</span>&#160;introduced<br />
-<br />
-&#160; 1.0.5 - 12 March 2008. <span class="term">style</span>&#160;URL schemes essentially disallowed when $config <span class="term">safe</span>&#160;is on; improved regex for CSS expression search<br />
-<br />
-&#160; 1.0.4 - 10 March 2008. Improved corrections for <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span><br />
-<br />
-&#160; 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); a bug allowing <span class="term">td</span>&#160;directly inside <span class="term">table</span>&#160;fixed; <span class="term">safe</span>&#160;<span class="term">$config</span>&#160;parameter added<br />
-<br />
-&#160; 1.0.2 - 13 February 2008. Improved implementation of <span class="term">$config["keep_bad"]</span><br />
-<br />
-&#160; 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions (<span class="term">hl_tag()</span>&#160;and <span class="term">hl_prot()</span>); no error display with <span class="term">hl_regex()</span><br />
-<br />
-&#160; 1.0 - 2 November 2007. First release<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.4" id="s4.4"></a><span class="item-no">4.4</span>&#160; Testing
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; To test htmLawed using a form interface, a <a href="htmLawedTest.php">demo</a>&#160;web-page is provided with the htmLawed distribution (<span class="term">htmLawed.php</span>&#160;and <span class="term">htmLawedTest.php</span>&#160;should be in the same directory on the web-server). A file with <a href="htmLawed_TESTCASE.txt">test-cases</a>&#160;is also provided.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.5" id="s4.5"></a><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Upgrading is as simple as replacing the previous version of <span class="term">htmLawed.php</span>&#160;(assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.<br />
-<br />
-&#160; <strong>Important</strong>&#160; The following upgrades may affect the functionality of a specific htmLawed as indicated by their corresponding notes:<br />
-<br />
-&#160; (1) From version 1.1-1.1.10 to 1.1.11, if a <span class="term">hook_tag</span>&#160;function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a <span class="term">hook_tag</span>&#160;function receives only the element name. The <span class="term">hook_tag</span>&#160;function therefore may have to be edited. See <a href="#s3.4.9">section 3.4.9</a>.<br />
-<br />
-&#160; Old versions of htmLawed may be available online. E.g., for version 1.0, check <a href="http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip">http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip</a>, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.6" id="s4.6"></a><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span>
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of mid-2009):<br />
-<br />
-&#160; * &#160;does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)<br />
-<br />
-&#160; * &#160;is 15-20 times bigger (scores of files totalling more than 750 kb)<br />
-<br />
-&#160; * &#160;consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)<br />
-<br />
-&#160; * &#160;is expectedly slower<br />
-<br />
-&#160; * &#160;does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like <span class="term">script</span>&#160;illegal)<br />
-<br />
-&#160; * &#160;lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)<br />
-<br />
-&#160; * &#160;has poor documentation<br />
-<br />
-&#160; However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier <a href="http://htmlpurifier.org">website</a>&#160;for updated information.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.7" id="s4.7"></a><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.8" id="s4.8"></a><span class="item-no">4.8</span>&#160; Use in non-PHP applications
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.9" id="s4.9"></a><span class="item-no">4.9</span>&#160; Donate
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; A donation in any currency and amount to appreciate or support this software can be sent by <a href="http://paypal.com">PayPal</a>&#160;to this email address: drpatnaik at yahoo dot com.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.10" id="s4.10"></a><span class="item-no">4.10</span>&#160; Acknowledgements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Edward Yang, and many anonymous users.<br />
-<br />
-&#160; Thank you!<br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s5" id="s5"></a><span class="item-no">5</span>&#160; Appendices
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<div class="sub-section"><h3>
-<a name="s5.1" id="s5.1"></a><span class="item-no">5.1</span>&#160; Characters discouraged in XHTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Characters represented by the following hexadecimal code-points are <em>not</em>&#160;invalid, even though some validators may issue messages stating otherwise.<br />
-<br />
-&#160; <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, <span class="term">1fffe</span>, <span class="term">1ffff</span>, <span class="term">2fffe</span>, <span class="term">2ffff</span>, <span class="term">3fffe</span>, <span class="term">3ffff</span>, <span class="term">4fffe</span>, <span class="term">4ffff</span>, <span class="term">5fffe</span>, <span class="term">5ffff</span>, <span class="term">6fffe</span>, <span class="term">6ffff</span>, <span class="term">7fffe</span>, <span class="term">7ffff</span>, <span class="term">8fffe</span>, <span class="term">8ffff</span>, <span class="term">9fffe</span>, <span class="term">9ffff</span>, <span class="term">afffe</span>, <span class="term">affff</span>, <span class="term">bfffe</span>, <span class="term">bffff</span>, <span class="term">cfffe</span>, <span class="term">cffff</span>, <span class="term">dfffe</span>, <span class="term">dffff</span>, <span class="term">efffe</span>, <span class="term">effff</span>, <span class="term">ffffe</span>, <span class="term">fffff</span>, <span class="term">10fffe</span>&#160;and <span class="term">10ffff</span><br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.2" id="s5.2"></a><span class="item-no">5.2</span>&#160; Valid attribute-element combinations
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Valid attribute-element combinations as per W3C specs.<br />
-<br />
-&#160; * &#160;includes deprecated attributes (marked <span class="term">^</span>), attributes for the non-standard <span class="term">embed</span>&#160;element (marked <span class="term">&#42;</span>), and the proprietary <span class="term">bordercolor</span>&#160;(marked <span class="term">~</span>)<br />
-&#160; * &#160;only non-frameset, HTML body elements<br />
-&#160; * &#160;<span class="term">name</span>&#160;for <span class="term">a</span>&#160;and <span class="term">map</span>, and <span class="term">lang</span>&#160;are invalid in XHTML 1.1<br />
-&#160; * &#160;<span class="term">target</span>&#160;is valid for <span class="term">a</span>&#160;in XHTML 1.1 and higher<br />
-&#160; * &#160;<span class="term">xml&#58;space</span>&#160;is only for XHTML 1.1<br />
-<br />
-&#160; abbr - td, th<br />
-&#160; accept - form, input<br />
-&#160; accept-charset - form<br />
-&#160; accesskey - a, area, button, input, label, legend, textarea<br />
-&#160; action - form<br />
-&#160; align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; alt - applet, area, img, input<br />
-&#160; archive - applet, object<br />
-&#160; axis - td, th<br />
-&#160; bgcolor - embed, table^, tr^, td^, th^<br />
-&#160; border - table, img^, object^<br />
-&#160; bordercolor~ - table, td, tr<br />
-&#160; cellpadding - table<br />
-&#160; cellspacing - table<br />
-&#160; char - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; charoff - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; charset - a, script<br />
-&#160; checked - input<br />
-&#160; cite - blockquote, q, del, ins<br />
-&#160; classid - object<br />
-&#160; clear - br^<br />
-&#160; code - applet<br />
-&#160; codebase - object, applet<br />
-&#160; codetype - object<br />
-&#160; color - font<br />
-&#160; cols - textarea<br />
-&#160; colspan - td, th<br />
-&#160; compact - dir, dl^, menu, ol^, ul^<br />
-&#160; coords - area, a<br />
-&#160; data - object<br />
-&#160; datetime - del, ins<br />
-&#160; declare - object<br />
-&#160; defer - script<br />
-&#160; dir - bdo<br />
-&#160; disabled - button, input, optgroup, option, select, textarea<br />
-&#160; enctype - form<br />
-&#160; face - font<br />
-&#160; flashvars* - embed<br />
-&#160; for - label<br />
-&#160; frame - table<br />
-&#160; frameborder - iframe<br />
-&#160; headers - td, th<br />
-&#160; height - embed, iframe, td^, th^, img, object, applet<br />
-&#160; href - a, area<br />
-&#160; hreflang - a<br />
-&#160; hspace - applet, img^, object^<br />
-&#160; ismap - img, input<br />
-&#160; label - option, optgroup<br />
-&#160; language - script^<br />
-&#160; longdesc - img, iframe<br />
-&#160; marginheight - iframe<br />
-&#160; marginwidth - iframe<br />
-&#160; maxlength - input<br />
-&#160; method - form<br />
-&#160; model* - embed<br />
-&#160; multiple - select<br />
-&#160; name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param<br />
-&#160; nohref - area<br />
-&#160; noshade - hr^<br />
-&#160; nowrap - td^, th^<br />
-&#160; object - applet<br />
-&#160; onblur - a, area, button, input, label, select, textarea<br />
-&#160; onchange - input, select, textarea<br />
-&#160; onfocus - a, area, button, input, label, select, textarea<br />
-&#160; onreset - form<br />
-&#160; onselect - input, textarea<br />
-&#160; onsubmit - form<br />
-&#160; pluginspage* - embed<br />
-&#160; pluginurl* - embed<br />
-&#160; prompt - isindex<br />
-&#160; readonly - textarea, input<br />
-&#160; rel - a<br />
-&#160; rev - a<br />
-&#160; rows - textarea<br />
-&#160; rowspan - td, th<br />
-&#160; rules - table<br />
-&#160; scope - td, th<br />
-&#160; scrolling - iframe<br />
-&#160; selected - option<br />
-&#160; shape - area, a<br />
-&#160; size - hr^, font, input, select<br />
-&#160; span - col, colgroup<br />
-&#160; src - embed, script, input, iframe, img<br />
-&#160; standby - object<br />
-&#160; start - ol^<br />
-&#160; summary - table<br />
-&#160; tabindex - a, area, button, input, object, select, textarea<br />
-&#160; target - a^, area, form<br />
-&#160; type - a, embed, object, param, script, input, li^, ol^, ul^, button<br />
-&#160; usemap - img, input, object<br />
-&#160; valign - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; value - input, option, param, button, li^<br />
-&#160; valuetype - param<br />
-&#160; vspace - applet, img^, object^<br />
-&#160; width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^<br />
-&#160; wmode - embed<br />
-&#160; xml:space - pre, script, style<br />
-<br />
-&#160; These are allowed in all but the shown elements:<br />
-<br />
-&#160; class - param, script<br />
-&#160; dir - applet, bdo, br, iframe, param, script<br />
-&#160; id - script<br />
-&#160; lang - applet, br, iframe, param, script<br />
-&#160; onclick - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; ondblclick - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onkeydown - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onkeypress - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onkeyup - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmousedown - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmousemove - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmouseout - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmouseover - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmouseup - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; style - param, script<br />
-&#160; title - param, script<br />
-&#160; xml:lang - applet, br, iframe, param, script<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.3" id="s5.3"></a><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; background<br />
-&#160; background-image<br />
-&#160; content<br />
-&#160; cue-after<br />
-&#160; cue-before<br />
-&#160; cursor<br />
-&#160; list-style<br />
-&#160; list-style-image<br />
-&#160; play-during<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.4" id="s5.4"></a><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Key: <span class="term">d</span>&#160;double, <span class="term">l</span>&#160;left, <span class="term">q</span>&#160;quote, <span class="term">r</span>&#160;right, <span class="term">s.</span>&#160;single<br />
-<br />
-&#160; Code-point (decimal) - hexadecimal value - replacement entity - represented character<br />
-<br />
-&#160; 127 - 7f - (removed) - (not used)<br />
-&#160; 128 - 80 - &amp;#8364; - euro<br />
-&#160; 129 - 81 - (removed) - (not used)<br />
-&#160; 130 - 82 - &amp;#8218; - baseline s. q<br />
-&#160; 131 - 83 - &amp;#402; - florin<br />
-&#160; 132 - 84 - &amp;#8222; - baseline d q<br />
-&#160; 133 - 85 - &amp;#8230; - ellipsis<br />
-&#160; 134 - 86 - &amp;#8224; - dagger<br />
-&#160; 135 - 87 - &amp;#8225; - d dagger<br />
-&#160; 136 - 88 - &amp;#710; - circumflex accent<br />
-&#160; 137 - 89 - &amp;#8240; - permile<br />
-&#160; 138 - 8a - &amp;#352; - S Hacek<br />
-&#160; 139 - 8b - &amp;#8249; - l s. guillemet<br />
-&#160; 140 - 8c - &amp;#338; - OE ligature<br />
-&#160; 141 - 8d - (removed) - (not used)<br />
-&#160; 142 - 8e - &amp;#381; - Z dieresis<br />
-&#160; 143 - 8f - (removed) - (not used)<br />
-&#160; 144 - 90 - (removed) - (not used)<br />
-&#160; 145 - 91 - &amp;#8216; - l s. q<br />
-&#160; 146 - 92 - &amp;#8217; - r s. q<br />
-&#160; 147 - 93 - &amp;#8220; - l d q<br />
-&#160; 148 - 94 - &amp;#8221; - r d q<br />
-&#160; 149 - 95 - &amp;#8226; - bullet<br />
-&#160; 150 - 96 - &amp;#8211; - en dash<br />
-&#160; 151 - 97 - &amp;#8212; - em dash<br />
-&#160; 152 - 98 - &amp;#732; - tilde accent<br />
-&#160; 153 - 99 - &amp;#8482; - trademark<br />
-&#160; 154 - 9a - &amp;#353; - s Hacek<br />
-&#160; 155 - 9b - &amp;#8250; - r s. guillemet<br />
-&#160; 156 - 9c - &amp;#339; - oe ligature<br />
-&#160; 157 - 9d - (removed) - (not used)<br />
-&#160; 158 - 9e - &amp;#382; - z dieresis<br />
-&#160; 159 - 9f - &amp;#376; - Y dieresis<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.5" id="s5.5"></a><span class="item-no">5.5</span>&#160; URL format
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; An <em>absolute</em>&#160;URL has a <span class="term">protocol</span>&#160;or <span class="term">scheme</span>, a <span class="term">network location</span>&#160;or <span class="term">hostname</span>, and, optional <span class="term">path</span>, <span class="term">parameters</span>, <span class="term">query</span>&#160;and <span class="term">fragment</span>&#160;segments. Thus, an absolute URL has this generic structure:<br />
-<br />
-
-<code class="code">&#160; &#160; (scheme) &#58; (//network location) /(path) ;(parameters) ?(query) #(fragment)</code>
-<br />
-<br />
-&#160; The schemes can only contain letters, digits, <span class="term">+</span>, <span class="term">.</span>&#160;and <span class="term">-</span>. Hostname is the portion after the <span class="term">//</span>&#160;and up to the first <span class="term">/</span>&#160;(if any; else, up to the end) when <span class="term">&#58;</span>&#160;is followed by a <span class="term">//</span>&#160;(e.g., <span class="term">abc.com</span>&#160;in <span class="term">ftp&#58;//abc.com/def</span>); otherwise, it consists of everything after the <span class="term">&#58;</span>&#160;(e.g., <span class="term">def@abc.com</span>&#160;in mailto:def@abc.com').<br />
-<br />
-&#160; <em>Relative</em>&#160;URLs do not have explicit schemes and network locations; such values are inherited from a <em>base</em>&#160;URL.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.6" id="s5.6"></a><span class="item-no">5.6</span>&#160; Brief on htmLawed code
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Much of the code's logic and reasoning can be understood from the documentation above.<br />
-<br />
-&#160; The <strong>output</strong>&#160;of htmLawed is a text string containing the processed input. There is no custom error tracking.<br />
-<br />
-&#160; <strong>Function arguments</strong>&#160;for htmLawed are:<br />
-<br />
-&#160; * &#160;<span class="term">$in</span>&#160;- 1st argument; a text string; the <strong>input text</strong>&#160;to be processed. Any extraneous slashes added by PHP when <em>magic quotes</em>&#160;are enabled should be removed beforehand using PHP's <span class="term">stripslashes()</span>&#160;function.<br />
-<br />
-&#160; * &#160;<span class="term">$config</span>&#160;- 2nd argument; an associative array; optional (named <span class="term">$C</span>&#160;in htmLawed code). The array has keys with names like <span class="term">balance</span>&#160;and <span class="term">keep_bad</span>, and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the <strong>configurable parameters</strong>&#160;(indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through <span class="term">$config</span>. <em>Finalized</em>&#160;<span class="term">$config</span>&#160;is thus a filtered and possibly larger array.<br />
-<br />
-&#160; * &#160;<span class="term">$spec</span>&#160;- 3rd argument; a text string; optional. The string has rules, written in an htmLawed-designated format, <strong>specifying</strong>&#160;element-specific attribute and attribute value restrictions. Function <span class="term">hl_spec()</span>&#160;is used to convert the string to an associative-array for internal use. <em>Finalized</em>&#160;<span class="term">$spec</span>&#160;is thus an array.<br />
-<br />
-&#160; <em>Finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;are made <strong>global variables</strong>&#160;while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the <em>finalized</em>&#160;values, the <span class="term">show_settings</span>&#160;parameter of <span class="term">$config</span>&#160;should be used). Depending on <span class="term">$config</span>, another global variable <span class="term">hl_Ids</span>, to track <span class="term">id</span>&#160;attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.<br />
-<br />
-&#160; Except for the main function <span class="term">htmLawed()</span>&#160;and the functions <span class="term">kses()</span>&#160;and <span class="term">kses_hook()</span>, htmLawed's functions are <strong>name-spaced</strong>&#160;using the <span class="term">hl_</span>&#160;prefix. The <strong>functions</strong>&#160;and their roles are:<br />
-<br />
-&#160; * &#160;<span class="term">hl_attrval</span>&#160;- checking attribute values against $spec<br />
-&#160; * &#160;<span class="term">hl_bal</span>&#160;- tag balancing<br />
-&#160; * &#160;<span class="term">hl_cmtcd</span>&#160;- handling CDATA sections and HTML comments<br />
-&#160; * &#160;<span class="term">hl_ent</span>&#160;- entity handling<br />
-&#160; * &#160;<span class="term">hl_prot</span>&#160;- checking a URL scheme/protocol<br />
-&#160; * &#160;<span class="term">hl_regex</span>&#160;- checking syntax of a regular expression<br />
-&#160; * &#160;<span class="term">hl_spec</span>&#160;- converting user-supplied $spec value to one used by htmLawed internally<br />
-&#160; * &#160;<span class="term">hl_tag</span>&#160;- handling tags<br />
-&#160; * &#160;<span class="term">hl_tag2</span>&#160;- transforming tags<br />
-&#160; * &#160;<span class="term">hl_tidy</span>&#160;- compact/beautify HTML<br />
-&#160; * &#160;<span class="term">hl_version</span>&#160;- reporting htmLawed version<br />
-&#160; * &#160;<span class="term">htmLawed</span>&#160;- main function<br />
-&#160; * &#160;<span class="term">kses</span>&#160;- main function of <span class="term">kses</span><br />
-&#160; * &#160;<span class="term">kses_hook</span>&#160;- hook function of <span class="term">kses</span><br />
-<br />
-&#160; The last two are for compatibility with pre-existing code using the <span class="term">kses</span>&#160;script. htmLawed's <span class="term">kses()</span>&#160;basically passes on the filtering task to <span class="term">htmLawed()</span>&#160;function after deciphering <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;from the argument values supplied to it. <span class="term">kses_hook()</span>&#160;is an empty function and is meant for being filled with custom code if the <span class="term">kses</span>&#160;script users were using one.<br />
-<br />
-&#160; <span class="term">htmLawed()</span>&#160;finalizes <span class="term">$spec</span>&#160;(with the help of <span class="term">hl_spec()</span>) and <span class="term">$config</span>, and globalizes them. Finalization of <span class="term">$config</span>&#160;involves setting default values if an inappropriate or invalid one is supplied. This includes calling <span class="term">hl_regex()</span>&#160;to check well-formedness of regular expression patterns if such expressions are user-supplied through <span class="term">$config</span>. <span class="term">htmLawed()</span>&#160;then removes invalid characters like nulls and <span class="term">x01</span>&#160;and appropriately handles entities using <span class="term">hl_ent()</span>. HTML comments and CDATA sections are identified and treated as per <span class="term">$config</span>&#160;with the help of <span class="term">hl_cmtcd()</span>. When retained, the <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;characters identifying them, and the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside them, are replaced with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>) till any tag balancing is completed.<br />
-<br />
-&#160; After this <em>initial processing</em>&#160;<span class="term">htmLawed()</span>&#160;identifies tags using regex and processes them with the help of <span class="term">hl_tag()</span>&#160;-- &#160;a large function that analyzes tag content, filtering it as per HTML standards, <span class="term">$config</span>&#160;and <span class="term">$spec</span>. Among other things, <span class="term">hl_tag()</span>&#160;transforms deprecated elements using <span class="term">hl_tag2()</span>, removes attributes from closing tags, checks attribute values as per <span class="term">$spec</span>&#160;rules using <span class="term">hl_attrval()</span>, and checks URL protocols using <span class="term">hl_prot()</span>. <span class="term">htmLawed()</span>&#160;performs tag balancing and nesting checks with a call to <span class="term">hl_bal()</span>, and optionally compacts/beautifies the output with proper white-spacing with a call to <span class="term">hl_tidy()</span>. The latter temporarily replaces white-space, and <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside <span class="term">pre</span>, <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, and HTML comments and CDATA sections with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>, and <span class="term">7</span>).<br />
-<br />
-&#160; htmLawed permits the use of custom code or <strong>hook functions</strong>&#160;at two stages. The first, called inside <span class="term">htmLawed()</span>, allows the input text as well as the finalized $config and $spec values to be altered right after the initial processing (see <a href="#s3.7">section 3.7</a>). The second is called by <span class="term">hl_tag()</span>&#160;once the tag content is finalized (see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; Being dictated by the external and stable HTML standard, htmLawed's objective is very clear-cut and less concerned with tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specs will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid in quickly grasping the logic, at least when viewed with code syntax highlighted.
-</div>
-</div>
-<br />
-<hr /><br /><br /><span class="subtle"><small>HTM version of <em><a href="htmLawed_README.txt">htmLawed_README.txt</a></em> generated on 06 Jun, 2012 using <a href="http://www.bioinformatics.org/phplabware/internal_utilities">rTxt2htm</a> from PHP Labware</small></span>
-</div><!-- ended div body -->
-</div><!-- ended div top -->
-</body>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta http-equiv="Content-Language" content="en" />
+<meta name="description" content="htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter - htmLawed_README.txt - presented with rTxt2htm, a PHP Labware utility" />
+<meta name="keywords" content="htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements, htmLawed_README.txt, rTxt2htm, PHP Labware" />
+<style type="text/css" media="all">
+<!--/*--><![CDATA[/*><!--*/
+a {text-decoration:none; color: blue;}
+a:hover {color: red;}
+a:visited {color: blue;}
+body {margin: 0; padding: 0;}
+body, div, html, p {font-family: Georgia, 'Times new roman', Times;}
+code.code {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+div.comment {padding: 5px; color: #999999; font-size: 80%;}
+div.comment a {color: #6699cc;}
+div#body {width: 70%; margin: 5px; padding: 5px;} /* holds non-toc content */
+div#toc {position: fixed; top: 5px; left: 73%; z-index: 2; margin-top: 5px; margin-left: 5px; border: 1px solid gray; padding: 5px; background-color: #ededed; width: 23%; overflow: auto; max-height:94%; font-size: 90%;} /* holds content table (toc) */
+div#top {font-size: 14px; margin: 5px; padding: 5px;} /* holds all content */
+div.monospace {overflow: auto; font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+div.sub-section {padding-left: 15px;}
+div.sub-sub-section {padding-left: 30px;}
+h1 {font-size: 22px; margin-top: 5px; margin-bottom: 5px;}
+h2 {font-size: 20px; float: left; margin-top: 15px; margin-bottom: 5px;}
+h3 {font-size: 18px; float: left; margin-top: 15px; margin-bottom: 5px;}
+h4 {font-size: 16px; float: left; margin-top: 15px; margin-bottom: 5px;}
+hr {margin-top: 15px; margin-bottom: 5px;}
+input, textarea {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+p.subtle {color: gray; padding: 0; padding-top: 10px; margin: 0;}
+p.subtle a, p.subtle a:visited {color: #6699cc;}
+span.item-no {color: black;}
+span.subtle {color: gray; margin: 0; padding:0;}
+span.subtle a, span.subtle a:visited {color: #6699cc;}
+span.term {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+span.toc-item {color: black;}
+span.totop {float: right; margin-top: 15px; margin-bottom: 5px;}
+span.totop a, span.totop a:visited {color: #6699cc;}
+@media screen { /* fixes for old IE */
+ * html, * html body {overflow-y: auto!important; height: 100%; margin: 0; padding: 0;}
+ * html div#body {height: 100%; overflow-y: auto; position: relative;}
+ * html div#toc {position: absolute;}
+}
+/*]]>*/-->
+</style>
+<title>htmLawed documentation | htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter</title>
+</head>
+<body>
+<div id="top">
+<h1><a id="peak" name="peak"></a>htmLawed documentation</h1>
+
+<div id="toc"><span class="toc-item"><a href="#s1"><span class="item-no">1</span>&#160; About htmLawed</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.1"><span class="item-no">1.1</span>&#160; Example uses</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.2"><span class="item-no">1.2</span>&#160; Features</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.3"><span class="item-no">1.3</span>&#160; History</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.4"><span class="item-no">1.4</span>&#160; License &amp; copyright</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.5"><span class="item-no">1.5</span>&#160; Terms used here</a></span><br />
+<span class="toc-item"><a href="#s2"><span class="item-no">2</span>&#160; Usage</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.1"><span class="item-no">2.1</span>&#160; Simple</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.2"><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.3"><span class="item-no">2.3</span>&#160; Extra HTML specifications using the <span class="term">$spec</span>&#160;parameter</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.4"><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.5"><span class="item-no">2.5</span>&#160; Some security risks to keep in mind</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.6"><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.7"><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.8"><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.9"><span class="item-no">2.9</span>&#160; Examples of usage</a></span><br />
+<span class="toc-item"><a href="#s3"><span class="item-no">3</span>&#160; Details</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.1"><span class="item-no">3.1</span>&#160; Invalid/dangerous characters</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.2"><span class="item-no">3.2</span>&#160; Character references/entities</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.3"><span class="item-no">3.3</span>&#160; HTML elements</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.1"><span class="item-no">3.3.1</span>&#160; HTML comments and <span class="term">CDATA</span>&#160;sections</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.2"><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.3"><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.4"><span class="item-no">3.3.4</span>&#160; Elements requiring child elements</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.5"><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.4"><span class="item-no">3.4</span>&#160; Attributes</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.1"><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.2"><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.3"><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.4"><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.5"><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.6"><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.7"><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span></a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.8"><span class="item-no">3.4.8</span>&#160; Inline style properties</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.9"><span class="item-no">3.4.9</span>&#160; Hook function for tag content</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.5"><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.6"><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.7"><span class="item-no">3.7</span>&#160; Using a hook function</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.8"><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.9"><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup</a></span><br />
+<span class="toc-item"><a href="#s4"><span class="item-no">4</span>&#160; Other</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.1"><span class="item-no">4.1</span>&#160; Support</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.2"><span class="item-no">4.2</span>&#160; Known issues</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.3"><span class="item-no">4.3</span>&#160; Change-log</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.4"><span class="item-no">4.4</span>&#160; Testing</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.5"><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.6"><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span></a></span><br />
+&#160; <span class="toc-item"><a href="#s4.7"><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.8"><span class="item-no">4.8</span>&#160; Use in non-PHP applications</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.9"><span class="item-no">4.9</span>&#160; Donate</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.10"><span class="item-no">4.10</span>&#160; Acknowledgements</a></span><br />
+<span class="toc-item"><a href="#s5"><span class="item-no">5</span>&#160; Appendices</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.1"><span class="item-no">5.1</span>&#160; Characters discouraged in HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.2"><span class="item-no">5.2</span>&#160; Valid attribute-element combinations</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.3"><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.4"><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.5"><span class="item-no">5.5</span>&#160; URL format</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.6"><span class="item-no">5.6</span>&#160; Brief on htmLawed code</a></span></div><!-- ended div toc -->
+
+<div id="body">
+<br />
+<div class="comment">htmLawed_README.txt, 29 August 2013<br />
+htmLawed 1.1.16, 29 August 2013<br />
+Copyright Santosh Patnaik<br />
+Dual licensed with LGPL 3 and GPL 2+<br />
+A PHP Labware internal utility &#45; <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>&#160;</div>
+<br />
+
+<div class="section"><h2>
+<a name="s1" id="s1"></a><span class="item-no">1</span>&#160; About htmLawed
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed is a PHP script to process text with HTML markup to make it more compliant with HTML standards and administrative policies. It works by making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting (XSS) attacks, allowing only specified HTML tags and attributes, and so on. Such <em>lawing in</em>&#160;of HTML in text used in (X)HTML or XML documents ensures that it is in accordance with the aesthetics, safety and usability requirements set by administrators.<br />
+<br />
+&#160; htmLawed is highly customizable, and fast with low memory usage. Its free and open-source code is in one small file, does not require extensions or libraries, and works in older versions of PHP as well. It is a good alternative to the HTML <a href="http://tidy.sourceforge.net">Tidy</a>&#160;application.<br />
+
+<div class="sub-section"><h3>
+<a name="s1.1" id="s1.1"></a><span class="item-no">1.1</span>&#160; Example uses
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; * &#160;Filtering of text submitted as comments on blogs to allow only certain HTML elements<br />
+<br />
+&#160; * &#160;Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant<br />
+<br />
+&#160; * &#160;Text processing for stricter XML standard-compliance: e.g., to have lowercased <span class="term">x</span>&#160;in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as <span class="term">application/xml</span><br />
+<br />
+&#160; * &#160;Scraping text or data from web-pages<br />
+<br />
+&#160; * &#160;Pretty-printing HTML code<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.2" id="s1.2"></a><span class="item-no">1.2</span>&#160; Features
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Key: <span class="term">&#42;</span>&#160;security feature, <span class="term">^</span>&#160;standard compliance, <span class="term">~</span>&#160;requires setting right options, <span class="term">&#96;</span>&#160;different from <span class="term">Kses</span><br />
+<br />
+&#160; * &#160;make input more <strong>secure</strong>&#160;and <strong>standard-compliant</strong><br />
+&#160; * &#160;use for HTML 4, XHTML 1.0 or 1.1, or even generic <strong>XML</strong>&#160;documents &#160;^~`<br />
+<br />
+&#160; * &#160;<strong>beautify</strong>&#160;or <strong>compact</strong>&#160;HTML &#160;^~`<br />
+<br />
+&#160; * &#160;can <strong>restrict elements</strong>&#160; ^~`<br />
+&#160; * &#160;ensures proper closure of empty elements like <span class="term">img</span>&#160; ^`<br />
+&#160; * &#160;<strong>transform deprecated elements</strong>&#160;like <span class="term">u</span>&#160; ^~`<br />
+&#160; * &#160;HTML <strong>comments</strong>&#160;and <span class="term">CDATA</span>&#160;sections can be permitted &#160;^~`<br />
+&#160; * &#160;elements like <span class="term">script</span>, <span class="term">object</span>&#160;and <span class="term">form</span>&#160;can be permitted &#160;~<br />
+<br />
+&#160; * &#160;<strong>restrict attributes</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
+&#160; * &#160;remove <strong>invalid attributes</strong>&#160; ^`<br />
+&#160; * &#160;element and attribute names are <strong>lower-cased</strong>&#160; ^<br />
+&#160; * &#160;provide <strong>required attributes</strong>, like <span class="term">alt</span>&#160;for <span class="term">image</span>&#160; ^`<br />
+&#160; * &#160;<strong>transforms deprecated attributes</strong>&#160; ^~`<br />
+&#160; * &#160;attributes <strong>declared only once</strong>&#160; ^`<br />
+<br />
+&#160; * &#160;<strong>restrict attribute values</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
+&#160; * &#160;a value is declared for <em>empty</em>&#160;(<em>minimized</em>) attributes like <span class="term">checked</span>&#160; ^<br />
+&#160; * &#160;check for potentially dangerous attribute values &#160;*~<br />
+&#160; * &#160;ensure <strong>unique</strong>&#160;<span class="term">id</span>&#160;attribute values &#160;^~`<br />
+&#160; * &#160;<strong>double-quote</strong>&#160;attribute values &#160;^<br />
+&#160; * &#160;lower-case <strong>standard attribute values</strong>&#160;like <span class="term">password</span>&#160; ^`<br />
+&#160; * &#160;permit custom, non-standard attributes as well as custom rules for standard attributes &#160;~`<br />
+<br />
+&#160; * &#160;<strong>attribute-specific URL protocol/scheme restriction</strong>&#160; *~`<br />
+&#160; * &#160;disable <strong>dynamic expressions</strong>&#160;in <span class="term">style</span>&#160;values &#160;*~`<br />
+<br />
+&#160; * &#160;neutralize invalid named character entities &#160;^`<br />
+&#160; * &#160;<strong>convert</strong>&#160;hexadecimal numeric entities to decimal ones, or vice versa &#160;^~`<br />
+&#160; * &#160;convert named entities to numeric ones for generic XML use &#160;^~`<br />
+<br />
+&#160; * &#160;remove <strong>null</strong>&#160;characters &#160;*<br />
+&#160; * &#160;neutralize potentially dangerous proprietary Netscape <strong>Javascript entities</strong>&#160; *<br />
+&#160; * &#160;replace potentially dangerous <strong>soft-hyphen</strong>&#160;character in URL-accepting attribute values with spaces &#160;*<br />
+<br />
+&#160; * &#160;remove common <strong>invalid characters</strong>&#160;not allowed in HTML or XML &#160;^`<br />
+&#160; * &#160;replace <strong>characters from Microsoft applications</strong>&#160;like <span class="term">Word</span>&#160;that are discouraged in HTML or XML &#160;^~`<br />
+&#160; * &#160;neutralize entities for characters invalid or discouraged in HTML or XML &#160;^`<br />
+&#160; * &#160;appropriately neutralize <span class="term">&lt;</span>, <span class="term">&amp;</span>, <span class="term">"</span>, and <span class="term">&gt;</span>&#160;characters &#160;^*`<br />
+<br />
+&#160; * &#160;understands improperly spaced tag content (like, spread over more than a line) and properly spaces them &#160;`<br />
+&#160; * &#160;attempts to <strong>balance tags</strong>&#160;for well-formedness &#160;^~`<br />
+&#160; * &#160;understands when <strong>omitable closing tags</strong>&#160;like <span class="term">&lt;/p&gt;</span>&#160;(allowed in HTML 4, transitional, e.g.) are missing &#160;^~`<br />
+&#160; * &#160;attempts to permit only <strong>validly nested tags</strong>&#160; ^~`<br />
+&#160; * &#160;option to <strong>remove or neutralize bad content</strong>&#160;^~`<br />
+&#160; * &#160;attempts to <strong>rectify common errors of plain-text misplacement</strong>&#160;(e.g., directly inside <span class="term">blockquote</span>) ^~`<br />
+<br />
+&#160; * &#160;fast, <strong>non-OOP</strong>&#160;code of ~45 kb incurring peak basal memory usage of ~0.5 MB<br />
+&#160; * &#160;<strong>compatible</strong>&#160;with pre-existing code using <span class="term">Kses</span>&#160;(the filter used by <span class="term">WordPress</span>)<br />
+<br />
+&#160; * &#160;optional <strong>anti-spam</strong>&#160;measures such as addition of <span class="term">rel="nofollow"</span>&#160;and link-disabling &#160;~`<br />
+&#160; * &#160;optionally makes <strong>relative URLs absolute</strong>, and vice versa &#160;~`<br />
+<br />
+&#160; * &#160;optionally mark <span class="term">&amp;</span>&#160;to identify the entities for <span class="term">&amp;</span>, <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;introduced by htmLawed &#160;~`<br />
+<br />
+&#160; * &#160;allows deployment of powerful <strong>hook functions</strong>&#160;to <strong>inject</strong>&#160;HTML, <strong>consolidate</strong>&#160;<span class="term">style</span>&#160;attributes to <span class="term">class</span>, finely check attribute values, etc. &#160;~`<br />
+<br />
+&#160; * &#160;<strong>independent of character encoding</strong>&#160;of input and does not affect it<br />
+<br />
+&#160; * &#160;<strong>tolerance for ill-written HTML</strong>&#160;to a certain degree<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.3" id="s1.3"></a><span class="item-no">1.3</span>&#160; History
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed was created in 2007 for use with <span class="term">LabWiki</span>, a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like <span class="term">Kses</span>&#160;and <span class="term">HTMLPurifier</span>&#160;were deemed inadequate, slow, resource-intensive, or dependent on an extension or external application like <span class="term">HTML Tidy</span>. The core logic of htmLawed, that of identifying HTML elements and attributes, was based on the <span class="term">Kses</span>&#160;(version 0.2.2) HTML filter software of Ulf Harnhammar (it can still be used with code that uses <span class="term">Kses</span>; see <a href="#s2.6">section 2.6</a>.).<br />
+<br />
+&#160; See <a href="#s4.3">section 4.3</a>&#160;for a detailed log of changes in htmLawed over the years, and <a href="#s4.10">section 4.10</a>&#160;for acknowledgements.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.4" id="s1.4"></a><span class="item-no">1.4</span>&#160; License &amp; copyright
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed is free and open-source software dual copyrighted by Santosh Patnaik, MD, PhD, and licensed under LGPL license version <a href="http://www.gnu.org/licenses/lgpl-3.0.txt">3</a>, and GPL license version <a href="http://www.gnu.org/licenses/gpl-2.0.txt">2</a>&#160;(or later).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.5" id="s1.5"></a><span class="item-no">1.5</span>&#160; Terms used here
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; In this document, only HTML body-level elements are considered. htmLawed does not have support for head-level elements, <span class="term">body</span>, and the frame-level elements, <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>, and these elements are ignored here.<br />
+<br />
+&#160; * &#160;<em>administrator</em>&#160;- or admin; person setting up the code that utilizes htmLawed; also, <em>user</em><br />
+&#160; * &#160;<em>attributes</em>&#160;- name-value pairs like <span class="term">href="http&#58;//x.com"</span>&#160;in opening tags<br />
+&#160; * &#160;<em>author</em>&#160;- see <em>writer</em><br />
+&#160; * &#160;<em>character</em>&#160;- atomic unit of text; internally represented by a numeric <em>code-point</em>&#160;as specified by the <em>encoding</em>&#160;or <em>charset</em>&#160;in use<br />
+&#160; * &#160;<em>entity</em>&#160;- markup like <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;#160;</span>&#160;used to refer to a character<br />
+&#160; * &#160;<em>element</em>&#160;- HTML element like <span class="term">a</span>&#160;and <span class="term">img</span><br />
+&#160; * &#160;<em>element content</em>&#160;- &#160;content between the opening and closing tags of an element, like <span class="term">click</span>&#160;of the <span class="term">&lt;a href="x"&gt;click&lt;/a&gt;</span>&#160;element<br />
+&#160; * &#160;<em>HTML</em>&#160;- implies XHTML unless specified otherwise<br />
+&#160; * &#160;<em>HTML body</em>&#160;- Complete HTML documents typically have a <em>head</em>&#160;and a <em>body</em>&#160;container. Information in <em>head</em>&#160;specifies title of the document, etc., whereas that in the body informs what is to be displayed on a web-page; it is only the elements for <em>body</em>, except <span class="term">frames</span>, <span class="term">frameset</span>&#160;and <span class="term">noframes</span>&#160;that htmLawed is concerned with<br />
+&#160; * &#160;<em>input</em>&#160;- text given to htmLawed to process<br />
+&#160; * &#160;<em>processing</em>&#160;- involves filtering, correction, etc., of input<br />
+&#160; * &#160;<em>safe</em>&#160;- absence or reduction of certain characters and HTML elements and attributes in HTML of text that can otherwise potentially, and circumstantially, expose text readers to security vulnerabilities like cross-site scripting attacks (XSS)<br />
+&#160; * &#160;<em>scheme</em>&#160;- a URL protocol like <span class="term">http</span>&#160;and <span class="term">ftp</span><br />
+&#160; * &#160;<em>specifications</em>&#160;- standard specifications, for HTML4, HTML5, Ruby, etc.<br />
+&#160; * &#160;<em>style property</em>&#160;- terms like <span class="term">border</span>&#160;and <span class="term">height</span>&#160;for which declarations are made in values for the <span class="term">style</span>&#160;attribute of elements<br />
+&#160; * &#160;<em>tag</em>&#160;- markers like <span class="term">&lt;a href="x"&gt;</span>&#160;and <span class="term">&lt;/a&gt;</span>&#160;delineating element content; the opening tag can contain attributes<br />
+&#160; * &#160;<em>tag content</em>&#160;- consists of tag markers <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, element names like <span class="term">div</span>, and possibly attributes<br />
+&#160; * &#160;<em>user</em>&#160;- administrator<br />
+&#160; * &#160;<em>writer</em>&#160;- end-user like a blog commenter providing the input that is to be processed; also, <em>author</em><br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.6" id="s1.6"></a><span class="item-no">1.6</span>&#160; Availability
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can be downloaded for free at its <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">website</a>. Besides the <span class="term">htmLawed.php</span>&#160;file, the download has the htmLawed documentation (this document) in plain <a href="htmLawed_README.txt">text</a>&#160;and <a href="htmLawed_README.htm">HTML</a>&#160;formats, a script for <a href="htmLawedTest.php">testing</a>, and a text file for <a href="htmLawed_TESTCASE.txt">test-cases</a>. htmLawed is also available as a PHP class (OOP code) on its website.<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s2" id="s2"></a><span class="item-no">2</span>&#160; Usage
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed works in PHP version 4.4 or higher. Either <span class="term">include()</span>&#160;the <span class="term">htmLawed.php</span>&#160;file, or copy-paste the entire code. To use with PHP 4.3, have the following code included:<br />
+<br />
+
+<code class="code">&#160; &#160; if(!function_exists(&#39;ctype_digit&#39;)){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;function ctype_digit($var){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; return ((int) $var == $var);</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;}</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+
+<div class="sub-section"><h3>
+<a name="s2.1" id="s2.1"></a><span class="item-no">2.1</span>&#160; Simple
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The input text to be processed, <span class="term">$text</span>, is passed as an argument of type string; <span class="term">htmLawed()</span>&#160;returns the processed string:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
+<br />
+<br />
+&#160; With the <span class="term">htmLawed class</span>&#160;(<a href="#s1.6">section 1.6</a>), usage is:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed&#58;&#58;hl($text);</code>
+<br />
+<br />
+&#160; <strong>Notes</strong>: (1) If input is from a <span class="term">$_GET</span>&#160;or <span class="term">$_POST</span>&#160;value, and <span class="term">magic quotes</span>&#160;are enabled on the PHP setup, run <span class="term">stripslashes()</span>&#160;on the input before passing to htmLawed. (2) htmLawed does not have support for head-level elements, <span class="term">body</span>, and the frame-level elements, <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>.<br />
+<br />
+&#160; By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow <span class="term">CDATA</span>&#160;sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- <span class="term">$config</span>&#160;and <span class="term">$spec</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
+<br />
+<br />
+&#160; The <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;arguments are detailed below. Some examples are shown in <a href="#s2.9">section 2.9</a>. For maximum protection against <span class="term">XSS</span>&#160;and other scripting attacks (e.g., by disallowing Javascript code), consider using the <span class="term">safe</span>&#160;parameter; see <a href="#s3.6">section 3.6</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.2" id="s2.2"></a><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; <span class="term">$config</span>&#160;instructs htmLawed on how to tackle certain tasks. When <span class="term">$config</span>&#160;is not specified, or not set as an array (e.g., <span class="term">$config = 1</span>), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in <span class="term">$config</span>&#160;as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1);</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config);</code>
+<br />
+<br />
+&#160; Or,<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1));</code>
+<br />
+<br />
+&#160; Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).<br />
+<br />
+&#160; Key: <span class="term">&#42;</span>&#160;default, <span class="term">^</span>&#160;different default when htmLawed is used in the Kses-compatible mode (see <a href="#s2.6">section 2.6</a>), <span class="term">~</span>&#160;different default when <span class="term">valid_xhtml</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.5">section 3.5</a>), <span class="term">"</span>&#160;different default when <span class="term">safe</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.6">section 3.6</a>)<br />
+<br />
+&#160; <strong>abs_url</strong><br />
+&#160; Make URLs absolute or relative; <span class="term">$config["base_url"]</span>&#160;needs to be set; see <a href="#s3.4.4">section 3.4.4</a><br />
+<br />
+&#160; <span class="term">-1</span>&#160;- make relative<br />
+&#160; <span class="term">0</span>&#160;- no action &#160;*<br />
+&#160; <span class="term">1</span>&#160;- make absolute<br />
+<br />
+&#160; <strong>and_mark</strong><br />
+&#160; Mark <span class="term">&amp;</span>&#160;characters in the original input; see <a href="#s3.2">section 3.2</a><br />
+<br />
+&#160; <strong>anti_link_spam</strong><br />
+&#160; Anti-link-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
+&#160; <em>array("regex1", "regex2")</em>&#160;- will ensure a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value in case the <span class="term">href</span>&#160;attribute value matches the regular expression pattern <span class="term">regex1</span>, and/or will remove <span class="term">href</span>&#160;if its value matches the regular expression pattern <span class="term">regex2</span>. E.g., <span class="term">array("/./", "/&#58;//\W&#42;(?!(abc\.com|xyz\.org))/")</span>; see <a href="#s3.4.7">section 3.4.7</a>&#160;for more.<br />
+<br />
+&#160; <strong>anti_mail_spam</strong><br />
+&#160; Anti-mail-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
+&#160; <em>word</em>&#160;- <span class="term">@</span>&#160;in mail address in <span class="term">href</span>&#160;attribute value is replaced with specified <em>word</em><br />
+<br />
+&#160; <strong>balance</strong><br />
+&#160; Balance tags for well-formedness and proper nesting; see <a href="#s3.3.3">section 3.3.3</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no<br />
+&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
+<br />
+&#160; <strong>base_url</strong><br />
+&#160; Base URL value that needs to be set if <span class="term">$config["abs_url"]</span>&#160;is not <span class="term">0</span>; see <a href="#s3.4.4">section 3.4.4</a><br />
+<br />
+&#160; <strong>cdata</strong><br />
+&#160; Handling of <span class="term">CDATA</span>&#160;sections; see <a href="#s3.3.1">section 3.3.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- don't consider <span class="term">CDATA</span>&#160;sections as markup and proceed as if plain text &#160;^"<br />
+&#160; <span class="term">1</span>&#160;- remove<br />
+&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting them to named entities<br />
+&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
+<br />
+&#160; <strong>clean_ms_char</strong><br />
+&#160; Replace discouraged characters introduced by Microsoft Word, etc.; see <a href="#s3.1">section 3.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- yes<br />
+&#160; <span class="term">2</span>&#160;- yes, but replace special single &amp; double quotes with ordinary ones<br />
+<br />
+&#160; <strong>comment</strong><br />
+&#160; Handling of HTML comments; see <a href="#s3.3.1">section 3.3.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- don't consider comments as markup and proceed as if plain text &#160;^"<br />
+&#160; <span class="term">1</span>&#160;- remove<br />
+&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting to named entities<br />
+&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
+<br />
+&#160; <strong>css_expression</strong><br />
+&#160; Allow dynamic CSS expression by not removing the expression from CSS property values in <span class="term">style</span>&#160;attributes; see <a href="#s3.4.8">section 3.4.8</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- remove &#160;*<br />
+&#160; <span class="term">1</span>&#160;- allow<br />
+<br />
+&#160; <strong>deny_attribute</strong><br />
+&#160; Denied HTML attributes; see <a href="#s3.4">section 3.4</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- none &#160;*<br />
+&#160; <em>string</em>&#160;- dictated by values in <em>string</em><br />
+&#160; <span class="term">on&#42;</span>&#160;(like <span class="term">onfocus</span>) attributes not allowed - "<br />
+<br />
+&#160; <strong>direct_nest_list</strong><br />
+&#160; Allow direct nesting of a list within another without requiring it to be a list item; see <a href="#s3.3.4">section 3.3.4</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- yes<br />
+<br />
+&#160; <strong>elements</strong><br />
+&#160; Allowed HTML elements; see <a href="#s3.3">section 3.3</a><br />
+<br />
+&#160; <span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;- &#160;~<br />
+&#160; <span class="term">applet, embed, iframe, object, script</span>&#160;not allowed - "<br />
+<br />
+&#160; <strong>hexdec_entity</strong><br />
+&#160; Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see <a href="#s3.2">section 3.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no<br />
+&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
+&#160; <span class="term">2</span>&#160;- convert decimal to hexadecimal ones<br />
+<br />
+&#160; <strong>hook</strong><br />
+&#160; Name of an optional hook function to alter the input string, <span class="term">$config</span>&#160;or <span class="term">$spec</span>&#160;before htmLawed starts its main work; see <a href="#s3.7">section 3.7</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
+&#160; <em>name</em>&#160;- <em>name</em>&#160;is name of the hook function (<span class="term">kses_hook</span>&#160; ^)<br />
+<br />
+&#160; <strong>hook_tag</strong><br />
+&#160; Name of an optional hook function to alter tag content finalized by htmLawed; see <a href="#s3.4.9">section 3.4.9</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
+&#160; <em>name</em>&#160;- <em>name</em>&#160;is name of the hook function<br />
+<br />
+&#160; <strong>keep_bad</strong><br />
+&#160; Neutralize bad tags by converting <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;to entities, or remove them; see <a href="#s3.3.3">section 3.3.3</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- remove &#160;^<br />
+&#160; <span class="term">1</span>&#160;- neutralize both tags and element content<br />
+&#160; <span class="term">2</span>&#160;- remove tags but neutralize element content<br />
+&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>&#160;but remove if text (<span class="term">pcdata</span>) is invalid in parent element<br />
+&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;* - &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>&#160;but line-breaks, tabs and spaces are left<br />
+<br />
+&#160; <strong>lc_std_val</strong><br />
+&#160; For XHTML compliance, predefined, standard attribute values, like <span class="term">get</span>&#160;for the <span class="term">method</span>&#160;attribute of <span class="term">form</span>, must be lowercased; see <a href="#s3.4.5">section 3.4.5</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no<br />
+&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
+<br />
+&#160; <strong>make_tag_strict</strong><br />
+&#160; Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: <span class="term">applet</span>&#160;<span class="term">center</span>&#160;<span class="term">dir</span>&#160;<span class="term">embed</span>&#160;<span class="term">font</span>&#160;<span class="term">isindex</span>&#160;<span class="term">menu</span>&#160;<span class="term">s</span>&#160;<span class="term">strike</span>&#160;<span class="term">u</span>; see <a href="#s3.3.2">section 3.3.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;^<br />
+&#160; <span class="term">1</span>&#160;- yes, but leave <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements that currently can't be transformed &#160;*<br />
+&#160; <span class="term">2</span>&#160;- yes, removing <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements and their contents (nested elements remain) &#160;~<br />
+<br />
+&#160; <strong>named_entity</strong><br />
+&#160; Allow non-universal named HTML entities, or convert to numeric ones; see <a href="#s3.2">section 3.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- convert<br />
+&#160; <span class="term">1</span>&#160;- allow &#160;*<br />
+<br />
+&#160; <strong>no_deprecated_attr</strong><br />
+&#160; Allow deprecated attributes or transform them; see <a href="#s3.4.6">section 3.4.6</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- allow &#160;^<br />
+&#160; <span class="term">1</span>&#160;- transform, but <span class="term">name</span>&#160;attributes for <span class="term">a</span>&#160;and <span class="term">map</span>&#160;are retained &#160;*<br />
+&#160; <span class="term">2</span>&#160;- transform<br />
+<br />
+&#160; <strong>parent</strong><br />
+&#160; Name of the parent element, possibly imagined, that will hold the input; see <a href="#s3.3">section 3.3</a><br />
+<br />
+&#160; <strong>safe</strong><br />
+&#160; Magic parameter to make input the most secure against XSS without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.6">section 3.6</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">"</span>&#160;in this list)<br />
+<br />
+&#160; <strong>schemes</strong><br />
+&#160; Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or <span class="term">!</span>&#160;to <em>deny</em>&#160;any URL); <span class="term">&#42;</span>&#160;covers all unspecified attributes; see <a href="#s3.4.3">section 3.4.3</a><br />
+<br />
+&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; &#42;&#58;file, http, https</span>&#160; *<br />
+&#160; <span class="term">&#42;&#58; ftp, gopher, http, https, mailto, news, nntp, telnet</span>&#160; ^<br />
+&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style&#58; !; &#42;&#58;file, http, https</span>&#160; "<br />
+<br />
+&#160; <strong>show_setting</strong><br />
+&#160; Name of a PHP variable to assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values; see <a href="#s3.8">section 3.8</a><br />
+<br />
+&#160; <strong>style_pass</strong><br />
+&#160; Do not look at <span class="term">style</span>&#160;attribute values, letting them through without any alteration<br />
+<br />
+&#160; <span class="term">0</span>&#160;- no *<br />
+&#160; <span class="term">1</span>&#160;- htmLawed will let through any <span class="term">style</span>&#160;value; see <a href="#s3.4.8">section 3.4.8</a><br />
+<br />
+&#160; <strong>tidy</strong><br />
+&#160; Beautify or compact HTML code; see <a href="#s3.3.5">section 3.3.5</a><br />
+<br />
+&#160; <span class="term">-1</span>&#160;- compact<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;or <span class="term">string</span>&#160;- beautify (custom format specified by <span class="term">string</span>)<br />
+<br />
+&#160; <strong>unique_ids</strong><br />
+&#160; <span class="term">id</span>&#160;attribute value checks; see <a href="#s3.4.2">section 3.4.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;^<br />
+&#160; <span class="term">1</span>&#160;- remove duplicate and/or invalid ones &#160;*<br />
+&#160; <em>word</em>&#160;- remove invalid ones and replace duplicate ones with new and unique ones based on the <em>word</em>; the admin-specified <em>word</em>, like <span class="term">my_</span>, should begin with a letter (a-z) and can contain letters, digits, <span class="term">.</span>, <span class="term">_</span>, <span class="term">-</span>, and <span class="term">&#58;</span>.<br />
+<br />
+&#160; <strong>valid_xhtml</strong><br />
+&#160; Magic parameter to make input the most valid XHTML without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.5">section 3.5</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in this list)<br />
+<br />
+&#160; <strong>xml:lang</strong><br />
+&#160; Auto-adding <span class="term">xml&#58;lang</span>&#160;attribute; see <a href="#s3.4.1">section 3.4.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present<br />
+&#160; <span class="term">2</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present, and remove <span class="term">lang</span>&#160; ~<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.3" id="s2.3"></a><span class="item-no">2.3</span>&#160; Extra HTML specifications using the $spec parameter
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The <span class="term">$spec</span>&#160;argument of htmLawed can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policies. <span class="term">$spec</span>&#160;is specified as a string of text containing one or more <em>rules</em>, with multiple rules separated from each other by a semi-colon (<span class="term">;</span>). E.g.,<br />
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
+<br />
+<br />
+&#160; Or,<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config, &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;);</code>
+<br />
+<br />
+&#160; A rule begins with an HTML <strong>element</strong>&#160;name(s) (<em>rule-element</em>), for which the rule applies, followed by an equal (<span class="term">=</span>) sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., <span class="term">th,td,tr=</span>.<br />
+<br />
+&#160; Rest of the rule consists of comma-separated HTML <strong>attribute names</strong>. A minus (<span class="term">-</span>) character before an attribute means that the attribute is not permitted inside the rule-element. E.g., <span class="term">-width</span>. To deny all attributes, <span class="term">-&#42;</span>&#160;can be used.<br />
+<br />
+&#160; Following shows examples of rule excerpts with rule-element <span class="term">a</span>&#160;and the attributes that are being permitted:<br />
+<br />
+&#160; * &#160;<span class="term">a=</span>&#160;- all<br />
+&#160; * &#160;<span class="term">a=id</span>&#160;- all<br />
+&#160; * &#160;<span class="term">a=href, title, -id, -onclick</span>&#160;- all except <span class="term">id</span>&#160;and <span class="term">onclick</span><br />
+&#160; * &#160;<span class="term">a=&#42;, id, -id</span>&#160;- all except <span class="term">id</span><br />
+&#160; * &#160;<span class="term">a=-&#42;</span>&#160;- none<br />
+&#160; * &#160;<span class="term">a=-&#42;, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
+&#160; * &#160;<span class="term">a=-&#42;, -id, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
+<br />
+&#160; Rules regarding <strong>attribute values</strong>&#160;are optionally specified inside round brackets after attribute names in slash ('/')-separated <em>parameter = value</em>&#160;pairs. E.g., <span class="term">title(maxlen=30/minlen=5)</span>. None or one or more of the following parameters may be specified:<br />
+<br />
+&#160; * &#160;<span class="term">oneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should match; if only one choice is provided, then the value must match that choice<br />
+<br />
+&#160; * &#160;<span class="term">noneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should not match<br />
+<br />
+&#160; * &#160;<span class="term">maxlen</span>&#160;and <span class="term">minlen</span>&#160;- upper and lower limits for the number of characters in the attribute value; specified in numbers<br />
+<br />
+&#160; * &#160;<span class="term">maxval</span>&#160;and <span class="term">minval</span>&#160;- upper and lower limits for the numerical value specified in the attribute value; specified in numbers<br />
+<br />
+&#160; * &#160;<span class="term">match</span>&#160;and <span class="term">nomatch</span>&#160;- pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers<br />
+<br />
+&#160; * &#160;<span class="term">default</span>&#160;- a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters<br />
+<br />
+&#160; If <span class="term">default</span>&#160;is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The <span class="term">default</span>&#160;value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., <span class="term">maxlen</span>&#160;to <span class="term">-1</span>).<br />
+<br />
+&#160; Examples with <em>input</em>&#160;<span class="term">&lt;input title="WIDTH" value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span>&#160;are shown below.<br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(maxlen=60/minlen=6), value</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(), value(maxval=8/default=6)</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input title="WIDTH" value="6" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(nomatch=%w.d%i), value(match=%em%/default=6em)</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="6em" /&gt;</span><br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(oneof=height|depth/default=depth), value(noneof=5|6)</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input title="depth" value="10em" /&gt;&lt;input title="depth" /&gt;</span><br />
+<br />
+&#160; <strong>Special characters</strong>: The characters <span class="term">;</span>, <span class="term">,</span>, <span class="term">/</span>, <span class="term">(</span>, <span class="term">)</span>, <span class="term">|</span>, <span class="term">~</span>&#160;and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be <em>escaped</em>&#160;by enclosing in pairs of double-quotes (<span class="term">"</span>). A back-tick (<span class="term">&#96;</span>) can be used to escape a literal <span class="term">"</span>. An example rule illustrating this is <span class="term">input=value(maxlen=30/match="/^\w/"/default="your &#96;"ID&#96;"")</span>.<br />
+<br />
+&#160; <strong>Note</strong>: To deny an attribute for all elements for which it is legal, <span class="term">$config["deny_attribute"]</span>&#160;(see <a href="#s3.4">section 3.4</a>) can be used instead of <span class="term">$spec</span>. Also, attributes can be allowed element-specifically through <span class="term">$spec</span>&#160;while being denied globally through <span class="term">$config["deny_attribute"]</span>. The <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) can also be possibly used to implement a functionality like that achieved using <span class="term">$spec</span>&#160;functionality.<br />
+<br />
+&#160; <span class="term">$spec</span>&#160;can also be used to permit custom, non-standard attributes as well as custom rules for standard attributes. Thus, the following value of <span class="term">$spec</span>&#160;will permit the custom uses of the standard <span class="term">rel</span>&#160;attribute in <span class="term">input</span>&#160;(not permitted as per standards) and of a non-standard attribute, <span class="term">vFlag</span>, in <span class="term">img</span>.<br />
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;img=vFlag; input=rel&#39;</code>
+<br />
+<br />
+&#160; The attribute names can contain alphabets, colons (:) and hyphens (-), but they must start with an alphabet.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.4" id="s2.4"></a><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The time and memory consumed during text processing by htmLawed depends on its configuration, the size of the input, and the amount, nestedness and well-formedness of the HTML markup within the input. In particular, tag balancing and beautification each can increase the processing time by about a quarter.<br />
+<br />
+&#160; The htmLawed <a href="htmLawedTest.php">demo</a>&#160;can be used to evaluate the performance and effects of different types of input and <span class="term">$config</span>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.5" id="s2.5"></a><span class="item-no">2.5</span>&#160; Some security risks to keep in mind
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially <em>dangerous</em>&#160;HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc. Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permitted through htmLawed's setting should be considered. For example, following increase security risks:<br />
+<br />
+&#160; * &#160;Allowing <span class="term">script</span>, <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>&#160;or <span class="term">object</span>&#160;elements, or certain of their attributes like <span class="term">allowscriptaccess</span><br />
+<br />
+&#160; * &#160;Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., <span class="term">&lt;!--[if gte IE 4]&gt;&lt;script&gt;alert("xss");&lt;/script&gt;&lt;![endif]--&gt;</span><br />
+<br />
+&#160; * &#160;Allowing dynamic CSS expressions (some Internet Explorer versions are vulnerable)<br />
+<br />
+&#160; * &#160;Allowing the <span class="term">style</span>&#160;attribute<br />
+<br />
+&#160; To remove <em>unsecure</em>&#160;HTML, code-developers using htmLawed must set <span class="term">$config</span>&#160;appropriately. E.g., <span class="term">$config["elements"] = "&#42; -script"</span>&#160;to deny the <span class="term">script</span>&#160;element (<a href="#s3.3">section 3.3</a>), <span class="term">$config["safe"] = 1</span>&#160;to auto-configure ceratin htmLawed parameters for maximizing security (<a href="#s3.6">section 3.6</a>), etc.<br />
+<br />
+&#160; Permitting the <span class="term">&#42;style&#42;</span>&#160;attribute brings in risks of <em>click-jacking</em>, <em>phishing</em>, web-page overlays, etc., <em>even</em>&#160;when the <span class="term">safe</span>&#160;parameter is enabled (see <a href="#s3.6">section 3.6</a>). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing <span class="term">style</span>&#160;completely and relying on CSS classes and stylesheet files is recommended.<br />
+<br />
+&#160; htmLawed does not check or correct the character <strong>encoding</strong>&#160;of the input it receives. In conjunction with permissive circumstances, such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can allow for an exploit (like Google's <em>UTF-7/XSS</em>&#160;vulnerability of the past).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.6" id="s2.6"></a><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The <span class="term">Kses</span>&#160;PHP script is used by many applications (like <span class="term">WordPress</span>). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the <span class="term">kses()</span>&#160;function declared in the <span class="term">Kses</span>&#160;file (usually named <span class="term">kses.php</span>). E.g., application code like this will continue to work after replacing <span class="term">Kses</span>&#160;with htmLawed:<br />
+<br />
+
+<code class="code">&#160; &#160; $comment_filtered = kses($comment_input, array(&#39;a&#39;=&gt;array(), &#39;b&#39;=&gt;array(), &#39;i&#39;=&gt;array()));</code>
+<br />
+<br />
+&#160; For some of the <span class="term">$config</span>&#160;parameters, htmLawed will use values other than the default ones. These are indicated by <span class="term">^</span>&#160;in <a href="#s2.2">section 2.2</a>. To force htmLawed to use other values, function <span class="term">kses()</span>&#160;in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.<br />
+<br />
+&#160; If the application uses a <span class="term">Kses</span>&#160;file that has the <span class="term">kses()</span>&#160;function declared, then, to have the application use htmLawed instead of <span class="term">Kses</span>, simply rename <span class="term">htmLawed.php</span>&#160;(to <span class="term">kses.php</span>, e.g.) and replace the <span class="term">Kses</span>&#160;file (or just replace the code in the <span class="term">Kses</span>&#160;file with the htmLawed code). If the <span class="term">kses()</span>&#160;function in the <span class="term">Kses</span>&#160;file had been renamed by the application developer (e.g., in <span class="term">WordPress</span>, it is named <span class="term">wp_kses()</span>), then appropriately rename the <span class="term">kses()</span>&#160;function in the htmLawed code.<br />
+<br />
+&#160; If the <span class="term">Kses</span>&#160;file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with <span class="term">WordPress</span>, it is best to copy the htmLawed code to <span class="term">wp_includes/kses.php</span>, rename the newly added function <span class="term">kses()</span>&#160;to <span class="term">wp_kses()</span>, and delete the code for the original <span class="term">wp_kses()</span>&#160;function.<br />
+<br />
+&#160; If the <span class="term">Kses</span>&#160;code has a non-empty hook function (e.g., <span class="term">wp_kses_hook()</span>&#160;in case of <span class="term">WordPress</span>), then the code for htmLawed's <span class="term">kses_hook()</span>&#160;function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With <span class="term">WordPress</span>, the hook function is an essential one. The following code is suggested for the htmLawed <span class="term">kses_hook()</span>&#160;in case of <span class="term">WordPress</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; function kses_hook($string, &amp;$cf, &amp;$spec){</code>
+<br />
+
+<code class="code">&#160; &#160; // kses compatibility</code>
+<br />
+
+<code class="code">&#160; &#160; $allowed_html = $spec;</code>
+<br />
+
+<code class="code">&#160; &#160; $allowed_protocols = array();</code>
+<br />
+
+<code class="code">&#160; &#160; foreach($cf[&#39;schemes&#39;] as $v){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;foreach($v as $k2=&gt;$v2){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if(!in_array($k2, $allowed_protocols)){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;$allowed_protocols[] = $k2;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;}</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+
+<code class="code">&#160; &#160; return wp_kses_hook($string, $allowed_html, $allowed_protocols);</code>
+<br />
+
+<code class="code">&#160; &#160; // eof</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.7" id="s2.7"></a><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be <em>read</em>&#160;as HTML, and may therefore get identified as mere plain text. Following statements indicate the degree of <em>looseness</em>&#160;that htmLawed can work with, and can be provided in instructions to writers:<br />
+<br />
+&#160; * &#160;Tags must be flanked by <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;with no <span class="term">&gt;</span>&#160;inside -- any needed <span class="term">&gt;</span>&#160;should be put in as <span class="term">&amp;gt;</span>. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and <span class="term">&gt;</span>, like <span class="term">&lt;div &gt;</span>&#160;and <span class="term">&lt;img / &gt;</span>, but not after the <span class="term">&lt;</span>.<br />
+<br />
+&#160; * &#160;Element and attribute names need not be lower-cased.<br />
+<br />
+&#160; * &#160;Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.<br />
+<br />
+&#160; * &#160;Attribute values may be single- and not double-quoted.<br />
+<br />
+&#160; * &#160;Left-padding of numeric entities (like, <span class="term">&amp;#0160;</span>, <span class="term">&amp;x07ff;</span>) with <span class="term">0</span>&#160;is okay as long as the number of characters between between the <span class="term">&amp;</span>&#160;and the <span class="term">;</span>&#160;does not exceed 8. All entities must end with <span class="term">;</span>&#160;though.<br />
+<br />
+&#160; * &#160;Named character entities must be properly cased. Thus, <span class="term">&amp;Lt;</span>&#160;or <span class="term">&amp;TILDE;</span>&#160;will not be recognized as entities and will be <em>neutralized</em>.<br />
+<br />
+&#160; * &#160;HTML comments should not be inside element tags (they can be between tags), and should begin with <span class="term">&lt;!--</span>&#160;and end with <span class="term">--&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">--&gt;</span>&#160;inside should be put in as <span class="term">--&amp;gt;</span>. Any <span class="term">--</span>&#160;inside will be automatically converted to <span class="term">-</span>, and a space will be added before the comment delimiter <span class="term">--&gt;</span>.<br />
+<br />
+&#160; * &#160;<span class="term">CDATA</span>&#160;sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with <span class="term">&lt;[CDATA[</span>&#160;and end with <span class="term">]]&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">]]&gt;</span>&#160;inside should be put in as <span class="term">]]&amp;gt;</span>.<br />
+<br />
+&#160; * &#160;For attribute values, character entities <span class="term">&amp;lt;</span>, <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;amp;</span>&#160;should be used instead of characters <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;(when <span class="term">&amp;</span>&#160;is not part of a character entity). This applies even for Javascript code in values of attributes like <span class="term">onclick</span>.<br />
+<br />
+&#160; * &#160;Characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>&#160;and <span class="term">"</span>&#160;that are part of actual Javascript, etc., code in <span class="term">script</span>&#160;elements should be used as such and not be put in as entities like <span class="term">&amp;gt;</span>. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside <span class="term">CDATA</span>&#160;sections.<br />
+<br />
+&#160; * &#160;Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on <span class="term">$config["keep_bad"]</span>, some code/text may be lost.<br />
+<br />
+&#160; * &#160;Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.<br />
+<br />
+&#160; * &#160;With <span class="term">$config["unique_ids"]</span>&#160;not <span class="term">0</span>&#160;and the <span class="term">id</span>&#160;attribute being permitted, writers should carefully avoid using duplicate or invalid <span class="term">id</span>&#160;values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when <span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>&#160;is processed into<br />
+<span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="prefix_home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>.<br />
+<br />
+&#160; * &#160;Even if intended HTML is lost from an ill-written input, the processed output will be more secure and standard-compliant.<br />
+<br />
+&#160; * &#160;For URLs, unless <span class="term">$config["scheme"]</span>&#160;is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., <span class="term">htt&amp;#112;</span>&#160;(which many browsers will read as the harmless <span class="term">http</span>) may be considered bad by htmLawed.<br />
+<br />
+&#160; * &#160;htmLawed will attempt to put plain text present directly inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;elements (illegal as per the specifications) inside auto-generated <span class="term">div</span>&#160;elements.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.8" id="s2.8"></a><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed's main objective is to make the input text <em>more</em>&#160;standard-compliant, secure for readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.<br />
+<br />
+&#160; It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specifications (like asking for normalization of white-spacing within <span class="term">textarea</span>&#160;elements) are clearly wrong. Regarding security, note that <em>unsafe</em>&#160;HTML code is not legally invalid per se.<br />
+<br />
+&#160; * &#160;htmLawed is meant for input that goes into the <span class="term">body</span>&#160;of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>. Content of the latter elements can, however, be individually filtered through htmLawed.<br />
+<br />
+&#160; * &#160;It cannot transform the non-standard <span class="term">embed</span>&#160;elements to the standard-compliant <span class="term">object</span>&#160;elements. Yet, it can allow <span class="term">embed</span>&#160;elements if permitted (<span class="term">embed</span>&#160;is widely used and supported). Admins can certainly use the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to deploy a custom embed-to-object converter function.<br />
+<br />
+&#160; * &#160;The only non-standard element that may be permitted is <span class="term">embed</span>; others like <span class="term">noembed</span>&#160;and <span class="term">nobr</span>&#160;cannot be permitted without modifying the htmLawed code.<br />
+<br />
+&#160; * &#160;It cannot handle input that has non-HTML code like <span class="term">SVG</span>&#160;and <span class="term">MathML</span>. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in <a href="#s3.9">section 3.9</a>. A third way may be to some how take advantage of the <span class="term">$config["and_mark"]</span>&#160;parameter (see <a href="#s3.2">section 3.2</a>).<br />
+<br />
+&#160; * &#160;By default, htmLawed won't check many attribute values for standard compliance. E.g., <span class="term">width="20m"</span>&#160;with the dimension in non-standard <span class="term">m</span>&#160;is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;to enforce finer checks.<br />
+<br />
+&#160; * &#160;The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specifications. Only a few of the proprietary attributes are supported.<br />
+<br />
+&#160; * &#160;Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;for finer checks. Perhaps the best option is to disallow <span class="term">style</span>&#160;but allow <span class="term">class</span>&#160;attributes with the right <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;values for <span class="term">class</span>, and have the various class style properties in <span class="term">.css</span>&#160;CSS stylesheet files.<br />
+<br />
+&#160; * &#160;htmLawed does not parse emoticons, decode <em>BBcode</em>, or <em>wikify</em>, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to <span class="term">br</span>&#160;elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.<br />
+<br />
+&#160; * &#160;htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate <span class="term">target</span>&#160;and <span class="term">onclick</span>&#160;attributes to <span class="term">a</span>). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;Nesting-based checks are not possible. E.g., one cannot disallow <span class="term">p</span>&#160;elements specifically inside <span class="term">td</span>&#160;while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert <span class="term">http</span>&#160;to <span class="term">https</span>. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;Pairs of opening and closing tags that do not enclose any content (like <span class="term">&lt;em&gt;&lt;/em&gt;</span>) are not removed. This may be against the standard specifications for certain elements (e.g., <span class="term">table</span>). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.<br />
+<br />
+&#160; * &#160;htmLawed does not check for certain element orderings described in the standard specifications (e.g., in a <span class="term">table</span>, <span class="term">tbody</span>&#160;is allowed before <span class="term">tfoot</span>). Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;htmLawed does not check the number of nested elements. E.g., it will allow two <span class="term">caption</span>&#160;elements in a <span class="term">table</span>&#160;element, illegal as per the specifications. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (<span class="term">/&#42;</span>) in <span class="term">style</span>&#160;attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like <span class="term">&amp;#101;xpression...</span>. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to more specifically identify CSS expressions in the <span class="term">style</span>&#160;attribute values. Also, using <span class="term">$config["style_pass"]</span>, it is possible to have htmLawed pass <span class="term">style</span>&#160;attribute values without even looking at them (<a href="#s3.4.8">section 3.4.8</a>).<br />
+<br />
+&#160; * &#160;htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <span class="term">&lt;a href="http&#58;//x%22+style=%22background-image&#58;xss"&gt;x&lt;/a&gt;</span>). These arise when browsers mis-identify markup in <em>escaped</em>&#160;text, defeating the very purpose of escaping text (a bad browser will read the given example as <span class="term">&lt;a href="http&#58;//x" style="background-image&#58;xss"&gt;x&lt;/a&gt;</span>).<br />
+<br />
+&#160; * &#160;Because of poor Unicode support in PHP, htmLawed does not remove the <em>high value</em>&#160;HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see <a href="#s3.1">section 3.1</a>).<br />
+<br />
+&#160; * &#160;htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can permit an exploit (like Google's <em>UTF-7/XSS</em>&#160;vulnerability of the past). Also, htmLawed can mangle input text if it is not well-formed in terms of character encoding. Administrators can consider using code available elsewhere to check well-formedness of input text characters to correct any defect.<br />
+<br />
+&#160; * &#160;htmLawed is expected to work with input texts in ASCII-compatible single byte encodings such as national variants of ASCII (like ISO-646-DE/German of the ISO 646 standard), extended ASCII variants (like ISO 8859-10/Turkish of the ISO 8859/ISO Latin standard), ISO 8859-based Windows variants (like Windows 1252), EBCDIC, Shift JIS (Japanese), GB-Roman (Chinese), and KS-Roman (Korean). It should also properly handle texts with variable byte encodings like UTF-7 (Unicode) and UTF-8 (Unicode). However, htmLawed may mangle input texts with double byte encodings like UTF-16 (Unicode), JIS X 0208:1997 (Japanese) and K SX 1001:1992 (Korean), or the UTF-32 (Unicode) quadruple byte encoding. If an input text has such an encoding, administrators can use PHP's <a href="http://php.net/manual/en/book.iconv.php">iconv</a>&#160;functions, or some other mean, to convert text to UTF-8 before passing it to htmLawed.<br />
+<br />
+&#160; * &#160;Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.9" id="s2.9"></a><span class="item-no">2.9</span>&#160; Examples of usage
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Safest, allowing only <em>safe</em>&#160;HTML markup --<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in);</code>
+<br />
+<br />
+&#160; Simplest, allowing all valid HTML markup except <span class="term">javascript&#58;</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in);</code>
+<br />
+<br />
+&#160; Allowing all valid HTML markup including <span class="term">javascript&#58;</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Allowing only <span class="term">safe</span>&#160;HTML and the elements <span class="term">a</span>, <span class="term">em</span>, and <span class="term">strong</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1, &#39;elements&#39;=&gt;&#39;a, em, strong&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Not allowing elements <span class="term">script</span>&#160;and <span class="term">object</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;elements&#39;=&gt;&#39;&#42; -script -object&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Not allowing attributes <span class="term">id</span>&#160;and <span class="term">style</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;id, style&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Permitting only attributes <span class="term">title</span>&#160;and <span class="term">href</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;&#42; -title -href&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Remove bad/disallowed tags altogether instead of converting them to entities --<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;keep_bad&#39;=&gt;0);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Allowing attribute <span class="term">title</span>&#160;only in <span class="term">a</span>&#160;and not allowing attributes <span class="term">id</span>, <span class="term">style</span>, or scriptable <em>on*</em>&#160;attributes like <span class="term">onclick</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;title, id, style, on&#42;&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;a=title&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config, $spec);</code>
+<br />
+<br />
+&#160; Allowing a custom attribute, <span class="term">vFlag</span>, in <span class="term">img</span>&#160;and permitting custom use of the standard attribute, <span class="term">rel</span>, in <span class="term">input</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;img=vFlag; input=rel&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config, $spec);</code>
+<br />
+<br />
+&#160; Some case-studies are presented below.<br />
+<br />
+&#160; <strong>1.</strong>&#160;A blog administrator wants to allow only <span class="term">a</span>, <span class="term">em</span>, <span class="term">strike</span>, <span class="term">strong</span>&#160;and <span class="term">u</span>&#160;in comments, but needs <span class="term">strike</span>&#160;and <span class="term">u</span>&#160;transformed to <span class="term">span</span>&#160;for better XHTML 1-strict compliance, and, he wants the <span class="term">a</span>&#160;links to point only to <span class="term">http</span>&#160;or <span class="term">https</span>&#160;resources:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;a, em, strike, strong, u&#39;, &#39;make_tag_strict&#39;=&gt;1, &#39;safe&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;http, https&#39;), &#39;a=href&#39;);</code>
+<br />
+<br />
+&#160; <strong>2.</strong>&#160;An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional <em>bad</em>&#160;characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;css_expression&#39;=&gt;1, &#39;keep_bad&#39;=&gt;1, &#39;make_tag_strict&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;, &#39;valid_xhtml&#39;=&gt;1));</code>
+<br />
+<br />
+&#160; For the final submission process, <span class="term">keep_bad</span>&#160;is set to <span class="term">6</span>. A value of <span class="term">1</span>&#160;for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.<br />
+<br />
+&#160; <strong>3.</strong>&#160;A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;tr, td&#39;, &#39;tidy&#39;=&gt;-1), &#39;tr, td =&#39;);</code>
+<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s3" id="s3"></a><span class="item-no">3</span>&#160; Details
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<div class="sub-section"><h3>
+<a name="s3.1" id="s3.1"></a><span class="item-no">3.1</span>&#160; Invalid/dangerous characters
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, <span class="term">9</span>, <span class="term">a</span>, <span class="term">d</span>, <span class="term">20</span>&#160;to <span class="term">d7ff</span>, and <span class="term">e000</span>&#160;to <span class="term">10ffff</span>, except <span class="term">fffe</span>&#160;and <span class="term">ffff</span>&#160;(decimally, <span class="term">9</span>, <span class="term">10</span>, <span class="term">13</span>, <span class="term">32</span>&#160;to <span class="term">55295</span>, and <span class="term">57344</span>&#160;to <span class="term">1114111</span>, except <span class="term">65534</span>&#160;and <span class="term">65535</span>). htmLawed removes the invalid characters <span class="term">0</span>&#160;to <span class="term">8</span>, <span class="term">b</span>, <span class="term">c</span>, and <span class="term">e</span>&#160;to <span class="term">1f</span>.<br />
+<br />
+&#160; Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.<br />
+<br />
+&#160; Characters that are discouraged (see <a href="#s5.1">section 5.1</a>) but not invalid are not removed by htmLawed.<br />
+<br />
+&#160; It (function <span class="term">hl_tag()</span>) also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, <span class="term">ad</span>, or decimally, <span class="term">173</span>) in attribute values with spaces. Where required, the characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>, and <span class="term">"</span>&#160;are converted to entities.<br />
+<br />
+&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">1</span>&#160;or <span class="term">2</span>, many of the discouraged characters (decimal code-points <span class="term">127</span>&#160;to <span class="term">159</span>&#160;except <span class="term">133</span>) that many Microsoft applications incorrectly use (as per the <span class="term">Windows 1252</span>&#160;[<span class="term">Cp-1252</span>] or a similar encoding system), and the character for decimal code-point <span class="term">133</span>, are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in <a href="#s5.4">section 5.4</a>. This can help avoid some display issues arising from copying-pasting of content.<br />
+<br />
+&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">2</span>, characters for the hexadecimal code-points <span class="term">82</span>, <span class="term">91</span>, and <span class="term">92</span>&#160;(for special single-quotes), and <span class="term">84</span>, <span class="term">93</span>, and <span class="term">94</span>&#160;(for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.<br />
+<br />
+&#160; The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.<br />
+<br />
+&#160; The <span class="term">$config["clean_ms_char"]</span>&#160;parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the <span class="term">Windows 1252</span>&#160;(<span class="term">Cp-1252</span>) or a similar encoding like <span class="term">Cp-1251</span>&#160;(otherwise, for example when UTF-8 encoding is in use, Japanese or Korean characters can get mangled). Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.2" id="s3.2"></a><span class="item-no">3.2</span>&#160; Character references/entities
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Valid character entities take the form <span class="term">&amp;&#42;;</span>&#160;where <span class="term">&#42;</span>&#160;is <span class="term">#x</span>&#160;followed by a hexadecimal number (hexadecimal numeric entity; like <span class="term">&amp;#xA0;</span>&#160;for non-breaking space), or alphanumeric like <span class="term">gt</span>&#160;(external or named entity; like <span class="term">&amp;nbsp;</span>&#160;for non-breaking space), or <span class="term">#</span>&#160;followed by a number (decimal numeric entity; like <span class="term">&amp;#160;</span>&#160;for non-breaking space). Character entities referring to the soft-hyphen character (the <span class="term">&amp;shy;</span>&#160;or <span class="term">\xad</span>&#160;character; hexadecimal code-point <span class="term">ad</span>&#160;[decimal <span class="term">173</span>]) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.<br />
+<br />
+&#160; htmLawed (function <span class="term">hl_ent()</span>):<br />
+<br />
+&#160; * &#160;Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)<br />
+<br />
+&#160; * &#160;Lowercases the <span class="term">X</span>&#160;(for XML-compliance) and <span class="term">A-F</span>&#160;of hexadecimal numeric entities<br />
+<br />
+&#160; * &#160;Neutralizes entities referring to characters that are HTML-invalid (see <a href="#s3.1">section 3.1</a>)<br />
+<br />
+&#160; * &#160;Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, and <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, or decimally, <span class="term">127</span>&#160;to <span class="term">132</span>, <span class="term">134</span>&#160;to <span class="term">159</span>, and <span class="term">64991</span>&#160;to <span class="term">64976</span>). Entities referring to the remaining discouraged characters (see <a href="#s5.1">section 5.1</a>&#160;for a full list) are let through.<br />
+<br />
+&#160; * &#160;Neutralizes named entities that are not in the specs.<br />
+<br />
+&#160; * &#160;Optionally converts valid HTML-specific named entities except <span class="term">&amp;gt;</span>, <span class="term">&amp;lt;</span>, <span class="term">&amp;quot;</span>, and <span class="term">&amp;amp;</span>&#160;to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is <span class="term">2</span>) for generic XML-compliance. For this, <span class="term">$config["named_entity"]</span>&#160;should be <span class="term">1</span>.<br />
+<br />
+&#160; * &#160;Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">0</span>.<br />
+<br />
+&#160; * &#160;Optionally converts decimal numeric entities to the hexadecimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">2</span>.<br />
+<br />
+&#160; <em>Neutralization</em>&#160;refers to the <em>entitification</em>&#160;of <span class="term">&amp;</span>&#160;to <span class="term">&amp;amp;</span>.<br />
+<br />
+&#160; <strong>Note</strong>: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's <span class="term">html_entity_decode</span>&#160;<a href="http://www.php.net/html_entity_decode">function</a>&#160;for that.<br />
+<br />
+&#160; <strong>Note</strong>: If <span class="term">$config["and_mark"]</span>&#160;is set, and set to a value other than <span class="term">0</span>, then the <span class="term">&amp;</span>&#160;characters in the original input are replaced with the control character for the hexadecimal code-point <span class="term">6</span>&#160;(<span class="term">\x06</span>; <span class="term">&amp;</span>&#160;characters introduced by htmLawed, e.g., after converting <span class="term">&lt;</span>&#160;to <span class="term">&amp;lt;</span>, are not affected). This allows one to distinguish, say, an <span class="term">&amp;gt;</span>&#160;introduced by htmLawed and an <span class="term">&amp;gt;</span>&#160;put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence <span class="term">o(&gt;&lt;)o</span>&#160;to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the <span class="term">\x06</span>&#160;can break documents. Before use in such documents, and preferably before any storage, any remaining <span class="term">\x06</span>&#160;should be changed back to <span class="term">&amp;</span>, e.g., with:<br />
+<br />
+
+<code class="code">&#160; &#160; $final = str_replace("\x06", &#39;&amp;&#39;, $prelim);</code>
+<br />
+<br />
+&#160; Also, see <a href="#s3.9">section 3.9</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3" id="s3.3"></a><span class="item-no">3.3</span>&#160; HTML elements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on <span class="term">$config["keep_bad"]</span>, are either <em>neutralized</em>&#160;(converted to plain text by entitification of <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>) or removed.<br />
+<br />
+&#160; E.g., with only <span class="term">em</span>&#160;permitted:<br />
+<br />
+&#160; Input:<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &lt;a href="http&#58;//a.com&gt;a.com&lt;/a&gt;.</code>
+<br />
+<br />
+&#160; Output, with <span class="term">$config["keep_bad"] = 0</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is a.com.</code>
+<br />
+<br />
+&#160; Output, with <span class="term">$config["keep_bad"]</span>&#160;not <span class="term">0</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &amp;lt;a href=""&amp;gt;a.com&amp;lt;/a&amp;gt;.</code>
+<br />
+<br />
+&#160; See <a href="#s3.3.3">section 3.3.3</a>&#160;for differences between the various non-zero <span class="term">$config["keep_bad"]</span>&#160;values.<br />
+<br />
+&#160; htmLawed by default permits these 86 elements:<br />
+<br />
+
+<code class="code">&#160; &#160; a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var</code>
+<br />
+<br />
+&#160; Except for <span class="term">embed</span>&#160;(included because of its wide-spread use) and the Ruby elements (<span class="term">rb</span>, <span class="term">rbc</span>, <span class="term">rp</span>, <span class="term">rt</span>, <span class="term">rtc</span>, <span class="term">ruby</span>; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude <span class="term">center</span>, <span class="term">dir</span>, <span class="term">font</span>, <span class="term">isindex</span>, <span class="term">menu</span>, <span class="term">s</span>, <span class="term">strike</span>, and <span class="term">u</span>.<br />
+<br />
+&#160; With <span class="term">$config["safe"] = 1</span>, the default set will exclude <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>; see <a href="#s3.6">section 3.6</a>.<br />
+<br />
+&#160; When <span class="term">$config["elements"]</span>, which specifies allowed elements, is <em>properly</em>&#160;defined, and neither empty nor set to <span class="term">0</span>&#160;or <span class="term">&#42;</span>, the default set is not used. To have elements added to or removed from the default set, a <span class="term">+/-</span>&#160;notation is used. E.g., <span class="term">&#42;-script-object</span>&#160;implies that only <span class="term">script</span>&#160;and <span class="term">object</span>&#160;are disallowed, whereas <span class="term">&#42;+embed</span>&#160;means that <span class="term">noembed</span>&#160;is also allowed. Elements can also be specified as comma separated names. E.g., <span class="term">a, b, i</span>&#160;means only <span class="term">a</span>, <span class="term">b</span>&#160;and <span class="term">i</span>&#160;are permitted. In this notation, <span class="term">&#42;</span>, <span class="term">+</span>&#160;and <span class="term">-</span>&#160;have no significance and can actually cause a mis-reading.<br />
+<br />
+&#160; Some more examples of <span class="term">$config["elements"]</span>&#160;values indicating permitted elements (note that empty spaces are liberally allowed for clarity):<br />
+<br />
+&#160; * &#160;<span class="term">a, blockquote, code, em, strong</span>&#160;-- only <span class="term">a</span>, <span class="term">blockquote</span>, <span class="term">code</span>, <span class="term">em</span>, and <span class="term">strong</span><br />
+&#160; * &#160;<span class="term">&#42;-script</span>&#160;-- all excluding <span class="term">script</span><br />
+&#160; * &#160;<span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;-- only XHTML-Strict elements<br />
+&#160; * &#160;<span class="term">&#42;+noembed-script</span>&#160;-- all including <span class="term">noembed</span>&#160;excluding <span class="term">script</span><br />
+<br />
+&#160; Some mis-usages (and the resulting permitted elements) that can be avoided:<br />
+<br />
+&#160; * &#160;<span class="term">-&#42;</span>&#160;-- none; instead of htmLawed, one might just use, e.g., the <span class="term">htmlspecialchars()</span>&#160;PHP function<br />
+&#160; * &#160;<span class="term">&#42;, -script</span>&#160;-- all except <span class="term">script</span>; admin probably meant <span class="term">&#42;-script</span><br />
+&#160; * &#160;<span class="term">-&#42;, a, em, strong</span>&#160;-- all; admin probably meant <span class="term">a, em, strong</span><br />
+&#160; * &#160;<span class="term">&#42;</span>&#160;-- all; admin need not have set <span class="term">elements</span><br />
+&#160; * &#160;<span class="term">&#42;-form+form</span>&#160;-- all; a <span class="term">+</span>&#160;will always over-ride any <span class="term">-</span><br />
+&#160; * &#160;<span class="term">&#42;, noembed</span>&#160;-- only <span class="term">noembed</span>; admin probably meant <span class="term">&#42;+noembed</span><br />
+&#160; * &#160;<span class="term">a, +b, i</span>&#160;-- only <span class="term">a</span>&#160;and <span class="term">i</span>; admin probably meant <span class="term">a, b, i</span><br />
+<br />
+&#160; Basically, when using the <span class="term">+/-</span>&#160;notation, commas (<span class="term">,</span>) should not be used, and vice versa, and <span class="term">&#42;</span>&#160;should be used with the former but not the latter.<br />
+<br />
+&#160; <strong>Note</strong>: Even if an element that is not in the default set is allowed through <span class="term">$config["elements"]</span>, like <span class="term">noembed</span>&#160;in the last example, it will eventually be removed during tag balancing unless such balancing is turned off (<span class="term">$config["balance"]</span>&#160;set to <span class="term">0</span>). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function <span class="term">hl_bal()</span>&#160;to accommodate the element and its nesting properties.<br />
+<br />
+&#160; <strong>A possibly second way to specify allowed elements</strong>&#160;is to set <span class="term">$config["parent"]</span>&#160;to an element name that supposedly will hold the input, and to set <span class="term">$config["balance"]</span>&#160;to <span class="term">1</span>. During tag balancing (see <a href="#s3.3.3">section 3.3.3</a>), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to <span class="term">div</span>&#160;if <span class="term">$config["parent"]</span>&#160;is empty, <span class="term">body</span>, or an element not in htmLawed's default set of 86 elements.<br />
+<br />
+&#160; <em>Tag transformation</em>&#160;is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see <a href="#s3.3.2">section 3.3.2</a>.<br />
+
+<div class="sub-sub-section"><h4>
+<a name="s3.3.1" id="s3.3.1"></a><span class="item-no">3.3.1</span>&#160; Handling of comments and CDATA sections
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; <span class="term">CDATA</span>&#160;sections have the format <span class="term">&lt;![CDATA[...anything but not "]]&gt;"...]]&gt;</span>, and HTML comments, <span class="term">&lt;!--...anything but not "--&gt;"... --&gt;</span>. Neither HTML comments nor <span class="term">CDATA</span>&#160;sections can reside inside tags. HTML comments can exist anywhere else, but <span class="term">CDATA</span>&#160;sections can exist only where plain text is allowed (e.g., immediately inside <span class="term">td</span>&#160;element content but not immediately inside <span class="term">tr</span>&#160;element content).<br />
+<br />
+&#160; htmLawed (function <span class="term">hl_cmtcd()</span>) handles HTML comments or <span class="term">CDATA</span>&#160;sections depending on the values of <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>. If <span class="term">0</span>, such markup is not looked for and the text is processed like plain text. If <span class="term">1</span>, it is removed completely. If <span class="term">2</span>, it is preserved but any <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;inside are changed to entities. If <span class="term">3</span>, they are left as such.<br />
+<br />
+&#160; Note that for the last two cases, HTML comments and <span class="term">CDATA</span>&#160;sections will always be removed from tag content (function <span class="term">hl_tag()</span>).<br />
+<br />
+&#160; Examples:<br />
+<br />
+&#160; Input:<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 0, $config["cdata"] = 2</span>):<br />
+
+<code class="code">&#160; &#160; &amp;lt;-- home link --&amp;gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 1, $config["cdata"] = 2</span>):<br />
+
+<code class="code">&#160; &#160; &lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 2</span>):<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 1</span>):<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 3, $config["cdata"] = 3</span>):<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+<br />
+&#160; For standard-compliance, comments are given the form <span class="term">&lt;!--comment --&gt;</span>, and any <span class="term">--</span>&#160;in the content is made <span class="term">-</span>.<br />
+<br />
+&#160; When <span class="term">$config["safe"] = 1</span>, CDATA sections and comments are considered plain text unless <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>&#160;is explicitly specified; see <a href="#s3.6">section 3.6</a>.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.3.2" id="s3.3.2"></a><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["make_tag_strict"]</span>&#160;is set and not <span class="term">0</span>, following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function <span class="term">hl_tag2()</span>):<br />
+<br />
+&#160; * &#160;applet - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
+&#160; * &#160;center - <span class="term">div style="text-align&#58; center;"</span><br />
+&#160; * &#160;dir - <span class="term">ul</span><br />
+&#160; * &#160;embed - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
+&#160; * &#160;font (face, size, color) - &#160; &#160;<span class="term">span style="font-family&#58; ; font-size&#58; ; color&#58; ;"</span>&#160;(size transformation <a href="http://style.cleverchimp.com/font_size_intervals/altintervals.html">reference</a>)<br />
+&#160; * &#160;isindex - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
+&#160; * &#160;menu - <span class="term">ul</span><br />
+&#160; * &#160;s - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
+&#160; * &#160;strike - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
+&#160; * &#160;u - <span class="term">span style="text-decoration&#58; underline;"</span><br />
+<br />
+&#160; For an element with a pre-existing <span class="term">style</span>&#160;attribute value, the extra style properties are appended.<br />
+<br />
+&#160; Example input:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;center&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;The PHP &lt;s&gt;software&lt;/s&gt; script used for this &lt;strike&gt;web-page&lt;/strike&gt; web-page is &lt;font style="font-weight&#58; bold " face=arial size=&#39;+3&#39; color &#160; = &#160;"red &#160;"&gt;htmLawedTest.php&lt;/font&gt;, from &lt;u style= &#39;color&#58;green&#39;&gt;PHP Labware&lt;/u&gt;.</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/center&gt;</code>
+<br />
+<br />
+&#160; The output:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;div style="text-align&#58; center;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;The PHP &lt;span style="text-decoration&#58; line-through;"&gt;software&lt;/span&gt; script used for this &lt;span style="text-decoration&#58; line-through;"&gt;web-page&lt;/span&gt; web-page is &lt;span style="font-weight&#58; bold; font-family&#58; arial; color&#58; red; font-size&#58; 200%;"&gt;htmLawedTest.php&lt;/span&gt;, from &lt;span style="color&#58;green; text-decoration&#58; underline;"&gt;PHP Labware&lt;/span&gt;.</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/div&gt;</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3.3" id="s3.3.3"></a><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>, htmLawed (function <span class="term">hl_bal()</span>) checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).<br />
+<br />
+&#160; Depending on the value of <span class="term">$config["keep_bad"]</span>&#160;(see <a href="#s2.2">section 2.2</a>&#160;and <a href="#s3.3">section 3.3</a>), illegal content may be removed or neutralized to plain text by converting &lt; and &gt; to entities:<br />
+<br />
+&#160; <span class="term">0</span>&#160;- remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see <a href="#s2.6">section 2.6</a>)<br />
+&#160; <span class="term">1</span>&#160;- neutralize tags and keep element content<br />
+&#160; <span class="term">2</span>&#160;- remove tags but keep element content<br />
+&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>, but keep element content only if text (<span class="term">pcdata</span>) is valid in parent element as per specs<br />
+&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;- &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>, but line-breaks, tabs and spaces are left<br />
+<br />
+&#160; Example input (disallowing the <span class="term">p</span>&#160;element):<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;&#42;&gt; Pseudo-tags &lt;&#42;&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; The output with <span class="term">$config["keep_bad"] = 1</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; The output with <span class="term">$config["keep_bad"] = 3</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; The output with <span class="term">$config["keep_bad"] = 6</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Non-HTML tag xml</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; An option like <span class="term">1</span>&#160;is useful, e.g., when a writer previews his submission, whereas one like <span class="term">3</span>&#160;is useful before content is finalized and made available to all.<br />
+<br />
+&#160; <strong>Note:</strong>&#160;In the example above, unlike <span class="term">&lt;&#42;&gt;</span>, <span class="term">&lt;xml&gt;</span>&#160;gets considered as a tag (even though there is no HTML element named <span class="term">xml</span>). Thus, the <span class="term">keep_bad</span>&#160;parameter's value affects <span class="term">&lt;xml&gt;</span>&#160;but not <span class="term">&lt;&#42;&gt;</span>. In general, text matching the regular expression pattern <span class="term">&lt;(/?)([a-zA-Z][a-zA-Z1-6]&#42;)([^&gt;]&#42;?)\s?&gt;</span>&#160;is considered a tag (phrase enclosed by the angled brackets <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...), and is subjected to the <span class="term">keep_bad</span>&#160;value.<br />
+<br />
+&#160; Nesting/content rules for each of the 86 elements in htmLawed's default set (see <a href="#s3.3">section 3.3</a>) are defined in function <span class="term">hl_bal()</span>. This means that if a non-standard element besides <span class="term">embed</span>&#160;is being permitted through <span class="term">$config["elements"]</span>, the element's tag content will end up getting removed if <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>.<br />
+<br />
+&#160; Plain text and/or certain elements nested inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as <span class="term">form</span>, the input <span class="term">B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;</span>&#160;is converted to <span class="term">&lt;div&gt;B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;&lt;/div&gt;</span>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3.4" id="s3.3.4"></a><span class="item-no">3.3.4</span>&#160; Elements requiring child elements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; As per specs, the following elements require legal child elements nested inside them:<br />
+<br />
+
+<code class="code">&#160; &#160; blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul</code>
+<br />
+<br />
+&#160; In some cases, the specs stipulate the number and/or the ordering of the child elements. A <span class="term">table</span>&#160;can have 0 or 1 <span class="term">caption</span>, <span class="term">tbody</span>, <span class="term">tfoot</span>, and <span class="term">thead</span>, but they must be in this order: <span class="term">caption</span>, <span class="term">thead</span>, <span class="term">tfoot</span>, <span class="term">tbody</span>.<br />
+<br />
+&#160; htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.<br />
+<br />
+&#160; With <span class="term">$config["direct_list_nest"]</span>&#160;set to <span class="term">1</span>, htmLawed will allow direct nesting of an <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;list within another <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;without requiring the child list to be within an <span class="term">li</span>&#160;of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter <span class="term">$config["direct_list_nest"]</span>&#160;has no effect if tag-balancing (<a href="#s3.3.3">section 3.3.3</a>) is turned off.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3.5" id="s3.3.5"></a><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; By default, htmLawed will neither <em>beautify</em>&#160;HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)<br />
+<br />
+&#160; As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside <span class="term">pre</span>&#160;elements) are all considered equivalent, and referred to as <em>white-spaces</em>. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space <em>normalization</em>&#160;allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such <em>pretty</em>&#160;HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.<br />
+<br />
+&#160; With the <span class="term">$config</span>&#160;parameter <span class="term">tidy</span>, htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides <span class="term">pre</span>, the <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, CDATA sections, and HTML comments are not subjected to the tidying process.<br />
+<br />
+&#160; To <em>compact</em>, use <span class="term">$config["tidy"] = -1</span>; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.<br />
+<br />
+&#160; To <em>beautify</em>, <span class="term">$config["tidy"]</span>&#160;is set as <span class="term">1</span>, or for customized tidying, as a string like <span class="term">2s2n</span>. The <span class="term">s</span>&#160;or <span class="term">t</span>&#160;character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The <span class="term">r</span>&#160;and <span class="term">n</span>&#160;characters are used to specify line-break characters: <span class="term">n</span>&#160;for <span class="term">\n</span>&#160;(Unix/Mac OS X line-breaks), <span class="term">rn</span>&#160;or <span class="term">nr</span>&#160;for <span class="term">\r\n</span>&#160;(Windows/DOS line-breaks), or <span class="term">r</span>&#160;for <span class="term">\r</span>.<br />
+<br />
+&#160; The <span class="term">$config["tidy"]</span>&#160;value of <span class="term">1</span>&#160;is equivalent to <span class="term">2s0n</span>. Other <span class="term">$config["tidy"]</span>&#160;values are read loosely: a value of <span class="term">4</span>&#160;is equivalent to <span class="term">4s0n</span>; <span class="term">t2</span>, to <span class="term">1t2n</span>; <span class="term">s</span>, to <span class="term">2s0n</span>; <span class="term">2TR</span>, to <span class="term">2t0r</span>; <span class="term">T1</span>, to <span class="term">1t1n</span>; <span class="term">nr3</span>, to <span class="term">3s0nr</span>, and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.<br />
+<br />
+&#160; Input formatting using <span class="term">$config["tidy"]</span>&#160;is not recommended when input text has mixed markup (like HTML + PHP).<br />
+
+</div>
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4" id="s3.4"></a><span class="item-no">3.4</span>&#160; Attributes
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the <span class="term">embed</span>&#160;element (the non-standard <span class="term">embed</span>&#160;element is supported in htmLawed because of its widespread use), and the the <span class="term">xml&#58;space</span>&#160;attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in <a href="#s5.2">section 5.2</a>. Using the <span class="term">$spec</span>&#160;argument, htmLawed can be forced to permit custom, non-standard attributes as well as custom rules for standard attributes (<a href="#s2.3">section 2.3</a>).<br />
+<br />
+&#160; When <span class="term">$config["deny_attribute"]</span>&#160;is not set, or set to <span class="term">0</span>, or empty (<span class="term">""</span>), all the 111 attributes are permitted. Otherwise, <span class="term">$config["deny_attribute"]</span>&#160;can be set as a list of comma-separated names of the denied attributes. <span class="term">on&#42;</span>&#160;can be used to refer to the group of potentially dangerous, script-accepting attributes: <span class="term">onblur</span>, <span class="term">onchange</span>, <span class="term">onclick</span>, <span class="term">ondblclick</span>, <span class="term">onfocus</span>, <span class="term">onkeydown</span>, <span class="term">onkeypress</span>, <span class="term">onkeyup</span>, <span class="term">onmousedown</span>, <span class="term">onmousemove</span>, <span class="term">onmouseout</span>, <span class="term">onmouseover</span>, <span class="term">onmouseup</span>, <span class="term">onreset</span>, <span class="term">onselect</span>&#160;and <span class="term">onsubmit</span>.<br />
+<br />
+&#160; Note that attributes specified in <span class="term">$config["deny_attribute"]</span>&#160;are denied globally, for all elements. To deny attributes for only specific elements, <span class="term">$spec</span>&#160;(see <a href="#s2.3">section 2.3</a>) can be used. <span class="term">$spec</span>&#160;can also be used to element-specifically permit an attribute otherwise denied through <span class="term">$config["deny_attribute"]</span>.<br />
+<br />
+&#160; With <span class="term">$config["safe"] = 1</span>&#160;(<a href="#s3.6">section 3.6</a>), the <span class="term">on&#42;</span>&#160;attributes are automatically disallowed.<br />
+<br />
+&#160; <strong>Note</strong>: To deny all but a few attributes globally, a simpler way to specify <span class="term">$config["deny_attribute"]</span>&#160;would be to use the notation <span class="term">&#42; -attribute1 -attribute2 ...</span>. Thus, a value of <span class="term">&#42; -title -href</span>&#160;implies that except <span class="term">href</span>&#160;and <span class="term">title</span>&#160;(where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter <span class="term">safe</span>&#160;(<a href="#s3.6">section 3.6</a>) will have no effect on <span class="term">deny_attribute</span>.<br />
+<br />
+&#160; htmLawed (function <span class="term">hl_tag()</span>) also:<br />
+<br />
+&#160; * &#160;Lower-cases attribute names<br />
+&#160; * &#160;Removes duplicate attributes (last one stays)<br />
+&#160; * &#160;Gives attributes the form <span class="term">name="value"</span>&#160;and single-spaces them, removing unnecessary white-spacing<br />
+&#160; * &#160;Provides <em>required</em>&#160;attributes (see <a href="#s3.4.1">section 3.4.1</a>)<br />
+&#160; * &#160;Double-quotes values and escapes any <span class="term">"</span>&#160;inside them<br />
+&#160; * &#160;Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point <span class="term">ad</span>) in the values with spaces<br />
+&#160; * &#160;Allows custom function to additionally filter/modify attribute values (see <a href="#s3.4.9">section 3.4.9</a>)<br />
+
+<div class="sub-sub-section"><h4>
+<a name="s3.4.1" id="s3.4.1"></a><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If indicated attributes for the following elements are found missing, htmLawed (function <span class="term">hl_tag()</span>) will add them (with values same as attribute names unless indicated otherwise below):<br />
+<br />
+&#160; * &#160;area - alt (<span class="term">area</span>)<br />
+&#160; * &#160;area, img - src, alt (<span class="term">image</span>)<br />
+&#160; * &#160;bdo - dir (<span class="term">ltr</span>)<br />
+&#160; * &#160;form - action<br />
+&#160; * &#160;map - name<br />
+&#160; * &#160;optgroup - label<br />
+&#160; * &#160;param - name<br />
+&#160; * &#160;script - type (<span class="term">text/javascript</span>)<br />
+&#160; * &#160;textarea - rows (<span class="term">10</span>), cols (<span class="term">50</span>)<br />
+<br />
+&#160; Additionally, with <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">1</span>&#160;or <span class="term">2</span>, if the <span class="term">lang</span>&#160;but not the <span class="term">xml&#58;lang</span>&#160;attribute is declared, then the latter is added too, with a value copied from that of <span class="term">lang</span>. This is for better standard-compliance. With <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">2</span>, the <span class="term">lang</span>&#160;attribute is removed (XHTML 1.1 specs).<br />
+<br />
+&#160; Note that the <span class="term">name</span>&#160;attribute for <span class="term">map</span>, invalid in XHTML 1.1, is also transformed if required -- see <a href="#s3.4.6">section 3.4.6</a>.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.2" id="s3.4.2"></a><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["unique_ids"]</span>&#160;is <span class="term">1</span>, htmLawed (function <span class="term">hl_tag()</span>) removes <span class="term">id</span>&#160;attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">-</span>&#160;and <span class="term">_</span>) or duplicate. If <span class="term">$config["unique_ids"]</span>&#160;is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">_</span>&#160;and <span class="term">-</span>.<br />
+<br />
+&#160; Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of <span class="term">id</span>&#160;values as it uses a global variable (<span class="term">$GLOBALS["hl_Ids"]</span>&#160;array). Further, an admin can restrict the use of certain <span class="term">id</span>&#160;values by presetting this variable before htmLawed is called into use. E.g.:<br />
+<br />
+
+<code class="code">&#160; &#160; $GLOBALS[&#39;hl_Ids&#39;] = array(&#39;top&#39;=&gt;1, &#39;bottom&#39;=&gt;1, &#39;myform&#39;=&gt;1); // id values not allowed in input</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text); // filter input</code>
+<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.3" id="s3.4.3"></a><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the <span class="term">afp</span>&#160;scheme is not permitted, then <span class="term">&lt;a href="afp&#58;//domain.org"&gt;</span>&#160;becomes <span class="term">&lt;a href="denied&#58;afp&#58;//domain.org"&gt;</span>, and if Javascript is not permitted <span class="term">&lt;a onclick="javascript&#58;xss();"&gt;</span>&#160;becomes <span class="term">&lt;a onclick="denied&#58;javascript&#58;xss();"&gt;</span>.<br />
+<br />
+&#160; By default htmLawed permits these schemes in URLs for the <span class="term">href</span>&#160;attribute:<br />
+<br />
+
+<code class="code">&#160; &#160; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet</code>
+<br />
+<br />
+&#160; Also, only <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted in attributes whose names start with <span class="term">o</span>&#160;(like <span class="term">onmouseover</span>), and in these attributes that accept URLs:<br />
+<br />
+
+<code class="code">&#160; &#160; action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap</code>
+<br />
+<br />
+&#160; These default sets are used when <span class="term">$config["schemes"]</span>&#160;is not set (see <a href="#s2.2">section 2.2</a>). To over-ride the defaults, <span class="term">$config["schemes"]</span>&#160;is defined as a string of semi-colon-separated sub-strings of type <span class="term">attribute&#58; comma-separated schemes</span>. E.g., <span class="term">href&#58; mailto, http, https; onclick&#58; javascript; src&#58; http, https</span>. For unspecified attributes, <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted. This can be changed by passing schemes for <span class="term">&#42;</span>&#160;in <span class="term">$config["schemes"]</span>. E.g., <span class="term">href&#58; mailto, http, https; &#42;&#58; https, https</span>.<br />
+<br />
+&#160; <span class="term">&#42;</span>&#160;can be put in the list of schemes to permit all protocols. E.g., <span class="term">style&#58; &#42;; img&#58; http, https</span>&#160;results in protocols not being checked in <span class="term">style</span>&#160;attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (<a href="#s3.4.4">section 3.4.4</a>) is not done.<br />
+<br />
+&#160; Thus, <em>to allow Javascript</em>, one can set <span class="term">$config["schemes"]</span>&#160;as <span class="term">href&#58; mailto, http, https; &#42;&#58; http, https, javascript</span>, or <span class="term">href&#58; mailto, http, https, javascript; &#42;&#58; http, https, javascript</span>, or <span class="term">&#42;&#58; &#42;</span>, and so on.<br />
+<br />
+&#160; As a side-note, one may find <span class="term">style&#58; &#42;</span>&#160;useful as URLs in <span class="term">style</span>&#160;attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.<br />
+<br />
+&#160; <span class="term">!</span>&#160;can be put in the list of schemes to disallow all protocols as well as <em>local</em>&#160;URLs. Thus, with <span class="term">href&#58; http, style&#58; !</span>, '&lt;a href="http://cnn.com" style="background-image: url('local.jpg');"&gt;CNN&lt;/a&gt;' will become '&lt;a href="http://cnn.com" style="background-image: url('denied:local.jpg');"&gt;CNN&lt;/a&gt;'.<br />
+<br />
+&#160; <strong>Note</strong>: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string <span class="term">src</span>&#160;(e.g., <span class="term">dynsrc</span>) or starts with <span class="term">o</span>&#160;(e.g., <span class="term">onbeforecopy</span>).<br />
+<br />
+&#160; With <span class="term">$config["safe"] = 1</span>, all URLs are disallowed in the <span class="term">style</span>&#160;attribute values.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.4" id="s3.4.4"></a><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs in attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can make absolute URLs in attributes like <span class="term">href</span>&#160;relative (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">-1</span>), and vice versa (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">1</span>). URLs in scripts are not considered for this, and so are URLs like <span class="term">#section_6</span>&#160;(fragment), <span class="term">?name=Tim#show</span>&#160;(starting with query string), and <span class="term">;var=1?name=Tim#show</span>&#160;(starting with parameters). Further, this requires that <span class="term">$config["base_url"]</span>&#160;be set properly, with the <span class="term">&#58;//</span>&#160;and a trailing slash (<span class="term">/</span>), with no query string, etc. E.g., <span class="term">file&#58;///D&#58;/page/</span>, <span class="term">https&#58;//abc.com/x/y/</span>, or <span class="term">http&#58;//localhost/demo/</span>&#160;are okay, but <span class="term">file&#58;///D&#58;/page/?help=1</span>, <span class="term">abc.com/x/y/</span>&#160;and <span class="term">http&#58;//localhost/demo/index.htm</span>&#160;are not.<br />
+<br />
+&#160; For making absolute URLs relative, only those URLs that have the <span class="term">$config["base_url"]</span>&#160;string at the beginning are converted. E.g., with <span class="term">$config["base_url"] = "https&#58;//abc.com/x/y/"</span>, <span class="term">https&#58;//abc.com/x/y/a.gif</span>&#160;and <span class="term">https&#58;//abc.com/x/y/z/b.gif</span>&#160;become <span class="term">a.gif</span>&#160;and <span class="term">z/b.gif</span>&#160;respectively, while <span class="term">https&#58;//abc.com/x/c.gif</span>&#160;is not changed.<br />
+<br />
+&#160; When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See <a href="#s5.5">section 5.5</a>&#160;for more about the URL specification as per RFC <a href="http://www.ietf.org/rfc/rfc1808.txt">1808</a>.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.5" id="s3.4.5"></a><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Optionally, for standard-compliance, htmLawed (function <span class="term">hl_tag()</span>) lower-cases standard attribute values to give, e.g., <span class="term">input type="password"</span>&#160;instead of <span class="term">input type="Password"</span>, if <span class="term">$config["lc_std_val"]</span>&#160;is <span class="term">1</span>. Attribute values matching those listed below for any of the elements (plus those for the <span class="term">type</span>&#160;attribute of <span class="term">button</span>&#160;or <span class="term">input</span>) are lower-cased:<br />
+<br />
+
+<code class="code">&#160; &#160; all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml&#58;space</code>
+<br />
+<br />
+&#160; The following <em>empty</em>&#160;(<em>minimized</em>) attributes are always assigned lower-cased values (same as the names):<br />
+<br />
+
+<code class="code">&#160; &#160; checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected</code>
+<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.6" id="s3.4.6"></a><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["no_deprecated_attr"]</span>&#160;is <span class="term">0</span>, then deprecated attributes (see appendix in <a href="#s5.2">section 5.2</a>) are removed and, in most cases, their values are transformed to CSS style properties and added to the <span class="term">style</span>&#160;attributes (function <span class="term">hl_tag()</span>). Except for <span class="term">bordercolor</span>&#160;for <span class="term">table</span>, <span class="term">tr</span>&#160;and <span class="term">td</span>, the scores of proprietary attributes that were never part of any cross-browser standard are not supported.<br />
+<br />
+&#160; <strong>Note</strong>: The attribute <span class="term">target</span>&#160;for <span class="term">a</span>&#160;is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.<br />
+<br />
+&#160; * &#160;align - for <span class="term">img</span>&#160;with value of <span class="term">left</span>&#160;or <span class="term">right</span>, becomes, e.g., <span class="term">float&#58; left</span>; for <span class="term">div</span>&#160;and <span class="term">table</span>&#160;with value <span class="term">center</span>, becomes <span class="term">margin&#58; auto</span>; all others become, e.g., <span class="term">text-align&#58; right</span><br />
+<br />
+&#160; * &#160;bgcolor - E.g., <span class="term">bgcolor="#ffffff"</span>&#160;becomes <span class="term">background-color&#58; #ffffff</span><br />
+&#160; * &#160;border - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
+&#160; * &#160;bordercolor - E.g., <span class="term">bordercolor=#999999</span>&#160;becomes <span class="term">border-color&#58; #999999;</span><br />
+&#160; * &#160;compact - <span class="term">font-size&#58; 85%</span><br />
+&#160; * &#160;clear - E.g., 'clear="all" becomes <span class="term">clear&#58; both</span><br />
+<br />
+&#160; * &#160;height - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span>&#160;and <span class="term">height="&#42;"</span>&#160;becomes <span class="term">height&#58; auto</span><br />
+<br />
+&#160; * &#160;hspace - E.g., <span class="term">hspace="10"</span>&#160;becomes <span class="term">margin-left&#58; 10px; margin-right&#58; 10px</span><br />
+&#160; * &#160;language - <span class="term">language="VBScript"</span>&#160;becomes <span class="term">type="text/vbscript"</span><br />
+&#160; * &#160;name - E.g., <span class="term">name="xx"</span>&#160;becomes <span class="term">id="xx"</span><br />
+&#160; * &#160;noshade - <span class="term">border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray</span><br />
+&#160; * &#160;nowrap - <span class="term">white-space&#58; nowrap</span><br />
+&#160; * &#160;size - E.g., <span class="term">size="10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
+&#160; * &#160;start - removed<br />
+&#160; * &#160;type - E.g., <span class="term">type="i"</span>&#160;becomes <span class="term">list-style-type&#58; lower-roman</span><br />
+&#160; * &#160;value - removed<br />
+&#160; * &#160;vspace - E.g., <span class="term">vspace="10"</span>&#160;becomes <span class="term">margin-top&#58; 10px; margin-bottom&#58; 10px</span><br />
+&#160; * &#160;width - like <span class="term">height</span><br />
+<br />
+&#160; Example input:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" name="dad&#39;s" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" name="dad" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br clear="left" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;hr noshade size="1" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding&#58;5px;" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;table width="50em" align="center" bgcolor="red"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td width="20%"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;div align="center"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;h3 align="right"&gt;Section&lt;/h3&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;p align="right"&gt;Para&lt;/p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;ol type="a" start="e"&gt;&lt;li value="x"&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td width="&#42;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;ol type="1"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/table&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br clear="all" /&gt;</code>
+<br />
+<br />
+&#160; And the output with <span class="term">$config["no_deprecated_attr"] = 1</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br style="clear&#58; left;" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;hr style="border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray; size&#58; 1px;" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;img src="i.gif" alt="image" width="10em" height="20" style="padding&#58;5px; float&#58; left; margin-left&#58; 10px; margin-right&#58; 10px; margin-top&#58; 10px; margin-bottom&#58; 10px; border&#58; 1px;" id="img" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;table width="50em" style="margin&#58; auto; background-color&#58; red;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; 20%;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;div style="margin&#58; auto;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;h3 style="text-align&#58; right;"&gt;Section&lt;/h3&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;p style="text-align&#58; right;"&gt;Para&lt;/p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;ol style="list-style-type&#58; lower-latin;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; auto;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;ol style="list-style-type&#58; decimal;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/table&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br style="clear&#58; both;" /&gt;</code>
+<br />
+<br />
+&#160; For <span class="term">lang</span>, deprecated in XHTML 1.1, transformation is taken care of through <span class="term">$config["xml&#58;lang"]</span>; see <a href="#s3.4.1">section 3.4.1</a>.<br />
+<br />
+&#160; The attribute <span class="term">name</span>&#160;is deprecated in <span class="term">form</span>, <span class="term">iframe</span>, and <span class="term">img</span>, and is replaced with <span class="term">id</span>&#160;if an <span class="term">id</span>&#160;attribute doesn't exist and if the <span class="term">name</span>&#160;value is appropriate for <span class="term">id</span>. For such replacements for <span class="term">a</span>&#160;and <span class="term">map</span>, for which the <span class="term">name</span>&#160;attribute is deprecated in XHTML 1.1, <span class="term">$config["no_deprecated_attr"]</span>&#160;should be set to <span class="term">2</span>&#160;(when set to <span class="term">1</span>, for these two elements, the <span class="term">name</span>&#160;attribute is retained).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4.7" id="s3.4.7"></a><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span>
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed (function <span class="term">hl_tag()</span>) can check the <span class="term">href</span>&#160;attribute values (link addresses) as an anti-spam (email or link spam) measure.<br />
+<br />
+&#160; If <span class="term">$config["anti_mail_spam"]</span>&#160;is not <span class="term">0</span>, the <span class="term">@</span>&#160;of email addresses in <span class="term">href</span>&#160;values like <span class="term">mailto&#58;a@b.com</span>&#160;is replaced with text specified by <span class="term">$config["anti_mail_spam"]</span>. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., <span class="term">&lt;remove_this_antispam&gt;@</span>&#160;(makes the example address <span class="term">a&lt;remove_this_antispam&gt;@b.com</span>).<br />
+<br />
+&#160; For regular links, one can choose to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the <span class="term">href</span>&#160;value altogether (disable the link).<br />
+<br />
+&#160; For use of these options, <span class="term">$config["anti_link_spam"]</span>&#160;should be set as an array with values <span class="term">regex1</span>&#160;and <span class="term">regex2</span>, both or one of which can be empty (like <span class="term">array("", "regex2")</span>) to indicate that that option is not to be used. Otherwise, <span class="term">regex1</span>&#160;or <span class="term">regex2</span>&#160;should be PHP- and PCRE-compatible regular expression patterns: <span class="term">href</span>&#160;values will be matched against them and those matching the pattern will accordingly be treated.<br />
+<br />
+&#160; Note that the regular expressions should have <em>delimiters</em>, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.<br />
+<br />
+&#160; An example, to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;for all links, and to disable links that do not point to domains <span class="term">abc.com</span>&#160;and <span class="term">xyz.org</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; $config["anti_link_spam"] = array(&#39;&#96;.&#96;&#39;, &#39;&#96;&#58;//\W&#42;(?!(abc\.com|xyz\.org))&#96;&#39;);</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4.8" id="s3.4.8"></a><span class="item-no">3.4.8</span>&#160; Inline style properties
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the <span class="term">style</span>&#160;attributes. (CSS properties like <span class="term">background-image</span>&#160;that accept URLs in their values are noted in <a href="#s5.3">section 5.3</a>.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting <span class="term">$config["css_expression"]</span>&#160;to <span class="term">1</span>&#160;(default setting). Note that when <span class="term">$config["css_expression"]</span>&#160;is set to <span class="term">1</span>, htmLawed will remove <span class="term">/&#42;</span>&#160;from the <span class="term">style</span>&#160;values.<br />
+<br />
+&#160; <strong>Note</strong>: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the <span class="term">style</span>&#160;attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off (<span class="term">$config["schemes"] = "...style&#58;&#42;..."</span>, see <a href="#s3.4.3">section 3.4.3</a>, and <span class="term">$config["css_expression"] = 0</span>). Alternately, admins can use their own custom function for finer handling of <span class="term">style</span>&#160;values through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; It is also possible to have htmLawed let through any <span class="term">style</span>&#160;value by setting <span class="term">$config["style_pass"]</span>&#160;to <span class="term">1</span>.<br />
+<br />
+&#160; As such, it is better to set up a CSS file with class declarations, disallow the <span class="term">style</span>&#160;attribute, set a <span class="term">$spec</span>&#160;rule (see <a href="#s2.3">section 2.3</a>) for <span class="term">class</span>&#160;for the <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;parameter, and ask writers to make use of the <span class="term">class</span>&#160;attribute.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4.9" id="s3.4.9"></a><span class="item-no">3.4.9</span>&#160; Hook function for tag content
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).<br />
+<br />
+&#160; When <span class="term">$config</span>&#160;parameter <span class="term">hook_tag</span>&#160;is set to the name of a function, htmLawed (function <span class="term">hl_tag()</span>) will pass on the element name, and, in the case of an opening tag, the <em>finalized</em>&#160;attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like <span class="term">&lt;element_name attribute_1_name="attribute_1_value"...&gt;</span>&#160;(for empty elements like <span class="term">img</span>&#160;and <span class="term">input</span>, the element-closing slash <span class="term">/</span>&#160;should also be included), etc.<br />
+<br />
+&#160; Any <span class="term">hook_tag</span>&#160;function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as <span class="term">a</span>&#160;in the closing <span class="term">&lt;/a&gt;</span>&#160;tag of the element <span class="term">&lt;a href="http&#58;//cnn.com"&gt;CNN&lt;/a&gt;</span>. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like <span class="term">&lt;/a&gt;</span>).<br />
+<br />
+&#160; This is a <strong>powerful functionality</strong>&#160;that can be exploited for various objectives: consolidate-and-convert inline <span class="term">style</span>&#160;attributes to <span class="term">class</span>, convert <span class="term">embed</span>&#160;elements to <span class="term">object</span>, permit only one <span class="term">caption</span>&#160;element in a <span class="term">table</span>&#160;element, disallow embedding of certain types of media, <strong>inject HTML</strong>, use <a href="http://csstidy.sourceforge.net">CSSTidy</a>&#160;to sanitize <span class="term">style</span>&#160;attribute values, etc.<br />
+<br />
+&#160; As an example, the custom hook code below can be used to force a series of specifically ordered <span class="term">id</span>&#160;attributes on all elements, and a specific <span class="term">param</span>&#160;element inside all <span class="term">object</span>&#160;elements:<br />
+<br />
+
+<code class="code">&#160; &#160; function my_tag_function($element, $attribute_array=0){</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; // If second argument is not received, it means a closing tag is being handled</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if(is_numeric($attribute_array)){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; return "&lt;/$element&gt;";</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; static $id = 0;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; // Remove any duplicate element</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if($element == &#39;param&#39; &amp;&amp; isset($attribute_array[&#39;allowscriptaccess&#39;])){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; return &#39;&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; $new_element = &#39;&#39;;</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; // Force a serialized ID number</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; $attribute_array[&#39;id&#39;] = &#39;my_&#39;. $id;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; ++$id;</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; // Inject param for allowscriptaccess</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if($element == &#39;object&#39;){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; $new_element = &#39;&lt;param id=&#39;my_&#39;. $id; allowscriptaccess="never" /&gt;&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; ++$id;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; $string = &#39;&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; foreach($attribute_array as $k=&gt;$v){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; $string .= " {$k}=\"{$v}\"";</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; static $empty_elements = array(&#39;area&#39;=&gt;1, &#39;br&#39;=&gt;1, &#39;col&#39;=&gt;1, &#39;embed&#39;=&gt;1, &#39;hr&#39;=&gt;1, &#39;img&#39;=&gt;1, &#39;input&#39;=&gt;1, &#39;isindex&#39;=&gt;1, &#39;param&#39;=&gt;1);</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; return "&lt;{$element}{$string}". (isset($in_array($element, $empty_elements) ? &#39; /&#39; &#58; &#39;&#39;). &#39;&gt;&#39;. $new_element;</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+<br />
+&#160; The <span class="term">hook_tag</span>&#160;parameter is different from the <span class="term">hook</span>&#160;parameter (<a href="#s3.7">section 3.7</a>).<br />
+<br />
+&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
+
+</div>
+</div>
+<div class="sub-section"><h3>
+<a name="s3.5" id="s3.5"></a><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["valid_xhtml"]</span>&#160;is set to <span class="term">1</span>, some relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in <a href="#s2.2">section 2.2</a>) are auto-adjusted. This allows one to pass the <span class="term">$config</span>&#160;argument with a simpler value. If a value for a parameter auto-set through <span class="term">valid_xhtml</span>&#160;is still manually provided, then that value will over-ride the auto-set value.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.6" id="s3.6"></a><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; <em>Safe</em>&#160;HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as <span class="term">script</span>&#160;and <span class="term">object</span>, and attributes such as <span class="term">onmouseover</span>&#160;and <span class="term">style</span>&#160;are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered <span class="term">safe</span>&#160;depends on the nature of the web application and the trust-level accorded to its users.<br />
+<br />
+&#160; htmLawed allows an admin to use <span class="term">$config["safe"]</span>&#160;to auto-adjust multiple <span class="term">$config</span>&#160;parameters (such as <span class="term">elements</span>&#160;which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by <span class="term">"</span>&#160;in <a href="#s2.2">section 2.2</a>). Thus, one can pass the <span class="term">$config</span>&#160;argument with a simpler value.<br />
+<br />
+&#160; With the value of <span class="term">1</span>, htmLawed considers <span class="term">CDATA</span>&#160;sections and HTML comments as plain text, and prohibits the <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>&#160;elements, and the <span class="term">on&#42;</span>&#160;attributes like <span class="term">onclick</span>. ( There are <span class="term">$config</span>&#160;parameters like <span class="term">css_expression</span>&#160;that are not affected by the value set for <span class="term">safe</span>&#160;but whose default values still contribute towards a more <em>safe</em>&#160;output.) Further, URLs with schemes (see <a href="#s3.4.3">section 3.4.3</a>) are neutralized so that, e.g., <span class="term">style="moz-binding&#58;url(http&#58;//danger)"</span>&#160;becomes <span class="term">style="moz-binding&#58;url(denied&#58;http&#58;//danger)"</span>.<br />
+<br />
+&#160; Admins, however, may still want to completely deny the <span class="term">style</span>&#160;attribute, e.g., with code like<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;safe&#39;=&gt;1, &#39;deny_attribute&#39;=&gt;&#39;style&#39;));</code>
+<br />
+<br />
+&#160; Permitting the <span class="term">style</span>&#160;attribute brings in risks of <em>click-jacking</em>, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check <span class="term">style</span>&#160;values. It does provide ways for the code-developer implementing htmLawed to do such checks through the <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.<br />
+<br />
+&#160; If a value for a parameter auto-set through <span class="term">safe</span>&#160;is still manually provided, then that value can over-ride the auto-set value. E.g., with <span class="term">$config["safe"] = 1</span>&#160;and <span class="term">$config["elements"] = "&#42;+script"</span>, <span class="term">script</span>, but not <span class="term">applet</span>, is allowed.<br />
+<br />
+&#160; A page illustrating the efficacy of htmLawed's anti-XSS abilities with <span class="term">safe</span>&#160;set to <span class="term">1</span>&#160;against XSS vectors listed by <a href="http://ha.ckers.org/xss.html">RSnake</a>&#160;may be available <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm">here</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.7" id="s3.7"></a><span class="item-no">3.7</span>&#160; Using a hook function
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["hook"]</span>&#160;is not set to <span class="term">0</span>, then htmLawed will allow preliminarily processed input to be altered by a hook function named by <span class="term">$config["hook"]</span>&#160;before starting the main work (but after handling of characters, entities, HTML comments and <span class="term">CDATA</span>&#160;sections -- see code for function <span class="term">htmLawed()</span>).<br />
+<br />
+&#160; The hook function also allows one to alter the <em>finalized</em>&#160;values of <span class="term">$config</span>&#160;and <span class="term">$spec</span>.<br />
+<br />
+&#160; Note that the <span class="term">hook</span>&#160;parameter is different from the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.8" id="s3.8"></a><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values to a variable named by <span class="term">$config["show_setting"]</span>. The variable, made global by htmLawed, is set as an array with three keys: <span class="term">config</span>, with the <span class="term">$config</span>&#160;value, <span class="term">spec</span>, with the <span class="term">$spec</span>&#160;value, and <span class="term">time</span>, with a value that is the Unix time (the output of PHP's <span class="term">microtime()</span>&#160;function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.<br />
+<br />
+&#160; The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.9" id="s3.9"></a><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed does not remove certain characters that, though invalid, are nevertheless <em>discouraged</em>&#160;in HTML documents as per the specifications (see <a href="#s5.1">section 5.1</a>). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).<br />
+<br />
+&#160; To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters with some of the HTML-discouraged characters (see <a href="#s3.1.2">section 3.1.2</a>). Post-htmLawed processing, the replacements are reverted.<br />
+<br />
+&#160; An example (mixed HTML and PHP code in input text):<br />
+<br />
+
+<code class="code">&#160; &#160; $text = preg_replace(&#39;&#96;&lt;\?php(.+?)\?&gt;&#96;sm&#39;, "\x83?php\\1?\x84", $text);</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = preg_replace(&#39;&#96;\x83\?php(.+?)\?\x84&#96;sm&#39;, &#39;&lt;?php$1?&gt;&#39;, $processed);</code>
+<br />
+<br />
+&#160; This code will not work if <span class="term">$config["clean_ms_char"]</span>&#160;is set to <span class="term">1</span>&#160;(<a href="#s3.1">section 3.1</a>), in which case one should instead deploy a hook function (<a href="#s3.7">section 3.7</a>). (htmLawed internally uses certain control characters, code-points <span class="term">1</span>&#160;to <span class="term">7</span>, and use of these characters as markers in the logic of hook functions may cause issues.)<br />
+<br />
+&#160; Admins may also be able to use <span class="term">$config["and_mark"]</span>&#160;to deal with such mixed markup; see <a href="#s3.2">section 3.2</a>.<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s4" id="s4"></a><span class="item-no">4</span>&#160; Other
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<div class="sub-section"><h3>
+<a name="s4.1" id="s4.1"></a><span class="item-no">4.1</span>&#160; Support
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; A careful reading of this documentation may provide an answer.<br />
+<br />
+&#160; Software updates and forum-based community-support may be found at <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at <a href="http://php.net">http://php.net</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.2" id="s4.2"></a><span class="item-no">4.2</span>&#160; Known issues
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; See <a href="#s2.8">section 2.8</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.3" id="s4.3"></a><span class="item-no">4.3</span>&#160; Change-log
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the <span class="term">htmLawed.php</span>&#160;file, may be updated without a change-log entry if the secondary files, but not htmLawed per se, are revised.)<br />
+<br />
+&#160; <em>Version number - Release date. Notes</em><br />
+<br />
+&#160; 1.1.16 - 29 August 2013. Fix for a potential security vulnerability arising from specialy encoded space characters in URL schemes/protocols<br />
+<br />
+&#160; 1.1.15 - 11 August 2013. Improved tidying/prettifying functionality<br />
+<br />
+&#160; 1.1.14 - 8 August 2012. Fix for possible segmental loss of incremental indentation during <span class="term">tidying</span>&#160;when <span class="term">balance</span>&#160;is disabled; fix for non-effectuation under some circumstances of a corrective behavior to preserve plain text within elements like <span class="term">blockquote</span>.<br />
+<br />
+&#160; 1.1.13 - 22 July 2012. Added feature allowing use of custom, non-standard attributes or custom rules for standard attributes<br />
+<br />
+&#160; 1.1.12 - 5 July 2012. Fix for a bug in identifying an unquoted value of the <span class="term">face</span>&#160;attribute<br />
+<br />
+&#160; 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. <span class="term">$config["hook_tag"]</span>, if specified, now receives names of elements in closing tags.<br />
+<br />
+&#160; 1.1.10 - 22 October 2011. Fix for a bug in the <span class="term">tidy</span>&#160;functionality that caused the entire input to be replaced with a single space; new parameter, <span class="term">$config["direct_list_nest"]</span>&#160;to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)<br />
+<br />
+&#160; 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of <span class="term">li</span>&#160;within <span class="term">dir</span><br />
+<br />
+&#160; 1.1.9.4 - 3 July 2010. Parameter <span class="term">schemes</span>&#160;now accepts <span class="term">!</span>&#160;so any URL, even a local one, can be <em>denied</em>. An issue in which a second URL value in <span class="term">style</span>&#160;properties was not checked was fixed.<br />
+<br />
+&#160; 1.1.9.3 - 17 May 2010. Checks for correct nesting of <span class="term">param</span><br />
+<br />
+&#160; 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes<br />
+<br />
+&#160; 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for <span class="term">flashvars</span>&#160;attribute for <span class="term">embed</span><br />
+<br />
+&#160; 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values<br />
+<br />
+&#160; 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice<br />
+<br />
+&#160; 1.1.8 - 23 April 2009. Parameter <span class="term">deny_attribute</span>&#160;now accepts the wild-card <span class="term">&#42;</span>, making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting <span class="term">$spec</span><br />
+<br />
+&#160; 1.1.7 - 11-12 March 2009. Attributes globally denied through <span class="term">deny_attribute</span>&#160;can be allowed element-specifically through <span class="term">$spec</span>; <span class="term">$config["style_pass"]</span>&#160;allowing letting through any <span class="term">style</span>&#160;value introduced; altered logic to catch certain types of dynamic crafted CSS expressions<br />
+<br />
+&#160; 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions<br />
+<br />
+&#160; 1.1.2 - 22 January 2009. Fixed bug in parsing of <span class="term">font</span>&#160;attributes during tag transformation<br />
+<br />
+&#160; 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent<br />
+<br />
+&#160; 1.1 - 29 June 2008. <span class="term">$config["hook_tag"]</span>&#160;and <span class="term">$config["tidy"]</span>&#160;introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug<br />
+<br />
+&#160; 1.0.9 - 11 June 2008. Fix for a bug in checks for invalid HTML code-point entities<br />
+<br />
+&#160; 1.0.8 - 15 May 2008. Permit <span class="term">bordercolor</span>&#160;attribute for <span class="term">table</span>, <span class="term">td</span>&#160;and <span class="term">tr</span><br />
+<br />
+&#160; 1.0.7 - 1 May 2008. Support for <span class="term">wmode</span>&#160;attribute for <span class="term">embed</span>; <span class="term">$config["show_setting"]</span>&#160;introduced; improved <span class="term">$config["elements"]</span>&#160;evaluation<br />
+<br />
+&#160; 1.0.6 - 20 April 2008. <span class="term">$config["and_mark"]</span>&#160;introduced<br />
+<br />
+&#160; 1.0.5 - 12 March 2008. <span class="term">style</span>&#160;URL schemes essentially disallowed when $config <span class="term">safe</span>&#160;is on; improved regex for CSS expression search<br />
+<br />
+&#160; 1.0.4 - 10 March 2008. Improved corrections for <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span><br />
+<br />
+&#160; 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); fix for a bug allowing <span class="term">td</span>&#160;directly inside <span class="term">table</span>; <span class="term">$config["safe"]</span>&#160;introduced<br />
+<br />
+&#160; 1.0.2 - 13 February 2008. Improved implementation of <span class="term">$config["keep_bad"]</span><br />
+<br />
+&#160; 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions (<span class="term">hl_tag()</span>&#160;and <span class="term">hl_prot()</span>); no error display with <span class="term">hl_regex()</span><br />
+<br />
+&#160; 1.0 - 2 November 2007. First release<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.4" id="s4.4"></a><span class="item-no">4.4</span>&#160; Testing
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; To test htmLawed using a form interface, a <a href="htmLawedTest.php">demo</a>&#160;web-page is provided with the htmLawed distribution (<span class="term">htmLawed.php</span>&#160;and <span class="term">htmLawedTest.php</span>&#160;should be in the same directory on the web-server). A file with <a href="htmLawed_TESTCASE.txt">test-cases</a>&#160;is also provided.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.5" id="s4.5"></a><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Upgrading is as simple as replacing the previous version of <span class="term">htmLawed.php</span>&#160;(assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.<br />
+<br />
+&#160; <strong>Important</strong>&#160; The following upgrades may affect the functionality of a specific htmLawed installation:<br />
+<br />
+&#160; (1) From version 1.1-1.1.10 to 1.1.11 (or later), if a <span class="term">hook_tag</span>&#160;function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a <span class="term">hook_tag</span>&#160;function receives only the element name. The <span class="term">hook_tag</span>&#160;function therefore may have to be edited. See <a href="#s3.4.9">section 3.4.9</a>.<br />
+<br />
+&#160; Old versions of htmLawed may be available online. E.g., for version 1.0, check <a href="http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip">http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip</a>, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.6" id="s4.6"></a><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span>
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of year 2010):<br />
+<br />
+&#160; * &#160;does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)<br />
+<br />
+&#160; * &#160;is 15-20 times bigger (scores of files totalling more than 750 kb)<br />
+<br />
+&#160; * &#160;consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)<br />
+<br />
+&#160; * &#160;is expectedly slower<br />
+<br />
+&#160; * &#160;does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like <span class="term">script</span>&#160;illegal)<br />
+<br />
+&#160; * &#160;lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)<br />
+<br />
+&#160; * &#160;has poor documentation<br />
+<br />
+&#160; However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier <a href="http://htmlpurifier.org">website</a>&#160;for updated information.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.7" id="s4.7"></a><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.8" id="s4.8"></a><span class="item-no">4.8</span>&#160; Use in non-PHP applications
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.9" id="s4.9"></a><span class="item-no">4.9</span>&#160; Donate
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; A donation in any currency and amount to appreciate or support this software can be sent by <a href="http://paypal.com">PayPal</a>&#160;to this email address: drpatnaik at yahoo dot com.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.10" id="s4.10"></a><span class="item-no">4.10</span>&#160; Acknowledgements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Dac Chartrand, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Harro Verton, Edward Yang, and many anonymous users.<br />
+<br />
+&#160; Thank you!<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s5" id="s5"></a><span class="item-no">5</span>&#160; Appendices
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<div class="sub-section"><h3>
+<a name="s5.1" id="s5.1"></a><span class="item-no">5.1</span>&#160; Characters discouraged in XHTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Characters represented by the following hexadecimal code-points are <em>not</em>&#160;invalid, even though some validators may issue messages stating otherwise.<br />
+<br />
+&#160; <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, <span class="term">1fffe</span>, <span class="term">1ffff</span>, <span class="term">2fffe</span>, <span class="term">2ffff</span>, <span class="term">3fffe</span>, <span class="term">3ffff</span>, <span class="term">4fffe</span>, <span class="term">4ffff</span>, <span class="term">5fffe</span>, <span class="term">5ffff</span>, <span class="term">6fffe</span>, <span class="term">6ffff</span>, <span class="term">7fffe</span>, <span class="term">7ffff</span>, <span class="term">8fffe</span>, <span class="term">8ffff</span>, <span class="term">9fffe</span>, <span class="term">9ffff</span>, <span class="term">afffe</span>, <span class="term">affff</span>, <span class="term">bfffe</span>, <span class="term">bffff</span>, <span class="term">cfffe</span>, <span class="term">cffff</span>, <span class="term">dfffe</span>, <span class="term">dffff</span>, <span class="term">efffe</span>, <span class="term">effff</span>, <span class="term">ffffe</span>, <span class="term">fffff</span>, <span class="term">10fffe</span>&#160;and <span class="term">10ffff</span><br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.2" id="s5.2"></a><span class="item-no">5.2</span>&#160; Valid attribute-element combinations
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Valid attribute-element combinations as per <a href="http://www.w3c.org">W3C</a>&#160;specs.<br />
+<br />
+&#160; * &#160;includes deprecated attributes (marked <span class="term">^</span>), attributes for the non-standard <span class="term">embed</span>&#160;element (marked <span class="term">&#42;</span>), and the proprietary <span class="term">bordercolor</span>&#160;(marked <span class="term">~</span>)<br />
+&#160; * &#160;only non-frameset, HTML body elements<br />
+&#160; * &#160;<span class="term">name</span>&#160;for <span class="term">a</span>&#160;and <span class="term">map</span>, and <span class="term">lang</span>&#160;are invalid in XHTML 1.1<br />
+&#160; * &#160;<span class="term">target</span>&#160;is valid for <span class="term">a</span>&#160;in XHTML 1.1 and higher<br />
+&#160; * &#160;<span class="term">xml&#58;space</span>&#160;is only for XHTML 1.1<br />
+<br />
+&#160; abbr - td, th<br />
+&#160; accept - form, input<br />
+&#160; accept-charset - form<br />
+&#160; accesskey - a, area, button, input, label, legend, textarea<br />
+&#160; action - form<br />
+&#160; align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; alt - applet, area, img, input<br />
+&#160; archive - applet, object<br />
+&#160; axis - td, th<br />
+&#160; bgcolor - embed, table^, tr^, td^, th^<br />
+&#160; border - table, img^, object^<br />
+&#160; bordercolor~ - table, td, tr<br />
+&#160; cellpadding - table<br />
+&#160; cellspacing - table<br />
+&#160; char - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; charoff - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; charset - a, script<br />
+&#160; checked - input<br />
+&#160; cite - blockquote, q, del, ins<br />
+&#160; classid - object<br />
+&#160; clear - br^<br />
+&#160; code - applet<br />
+&#160; codebase - object, applet<br />
+&#160; codetype - object<br />
+&#160; color - font<br />
+&#160; cols - textarea<br />
+&#160; colspan - td, th<br />
+&#160; compact - dir, dl^, menu, ol^, ul^<br />
+&#160; coords - area, a<br />
+&#160; data - object<br />
+&#160; datetime - del, ins<br />
+&#160; declare - object<br />
+&#160; defer - script<br />
+&#160; dir - bdo<br />
+&#160; disabled - button, input, optgroup, option, select, textarea<br />
+&#160; enctype - form<br />
+&#160; face - font<br />
+&#160; flashvars* - embed<br />
+&#160; for - label<br />
+&#160; frame - table<br />
+&#160; frameborder - iframe<br />
+&#160; headers - td, th<br />
+&#160; height - embed, iframe, td^, th^, img, object, applet<br />
+&#160; href - a, area<br />
+&#160; hreflang - a<br />
+&#160; hspace - applet, img^, object^<br />
+&#160; ismap - img, input<br />
+&#160; label - option, optgroup<br />
+&#160; language - script^<br />
+&#160; longdesc - img, iframe<br />
+&#160; marginheight - iframe<br />
+&#160; marginwidth - iframe<br />
+&#160; maxlength - input<br />
+&#160; method - form<br />
+&#160; model* - embed<br />
+&#160; multiple - select<br />
+&#160; name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param<br />
+&#160; nohref - area<br />
+&#160; noshade - hr^<br />
+&#160; nowrap - td^, th^<br />
+&#160; object - applet<br />
+&#160; onblur - a, area, button, input, label, select, textarea<br />
+&#160; onchange - input, select, textarea<br />
+&#160; onfocus - a, area, button, input, label, select, textarea<br />
+&#160; onreset - form<br />
+&#160; onselect - input, textarea<br />
+&#160; onsubmit - form<br />
+&#160; pluginspage* - embed<br />
+&#160; pluginurl* - embed<br />
+&#160; prompt - isindex<br />
+&#160; readonly - textarea, input<br />
+&#160; rel - a<br />
+&#160; rev - a<br />
+&#160; rows - textarea<br />
+&#160; rowspan - td, th<br />
+&#160; rules - table<br />
+&#160; scope - td, th<br />
+&#160; scrolling - iframe<br />
+&#160; selected - option<br />
+&#160; shape - area, a<br />
+&#160; size - hr^, font, input, select<br />
+&#160; span - col, colgroup<br />
+&#160; src - embed, script, input, iframe, img<br />
+&#160; standby - object<br />
+&#160; start - ol^<br />
+&#160; summary - table<br />
+&#160; tabindex - a, area, button, input, object, select, textarea<br />
+&#160; target - a^, area, form<br />
+&#160; type - a, embed, object, param, script, input, li^, ol^, ul^, button<br />
+&#160; usemap - img, input, object<br />
+&#160; valign - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; value - input, option, param, button, li^<br />
+&#160; valuetype - param<br />
+&#160; vspace - applet, img^, object^<br />
+&#160; width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^<br />
+&#160; wmode - embed<br />
+&#160; xml:space - pre, script, style<br />
+<br />
+&#160; These are allowed in all but the shown elements:<br />
+<br />
+&#160; class - param, script<br />
+&#160; dir - applet, bdo, br, iframe, param, script<br />
+&#160; id - script<br />
+&#160; lang - applet, br, iframe, param, script<br />
+&#160; onclick - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; ondblclick - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onkeydown - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onkeypress - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onkeyup - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmousedown - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmousemove - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmouseout - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmouseover - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmouseup - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; style - param, script<br />
+&#160; title - param, script<br />
+&#160; xml:lang - applet, br, iframe, param, script<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.3" id="s5.3"></a><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; background<br />
+&#160; background-image<br />
+&#160; content<br />
+&#160; cue-after<br />
+&#160; cue-before<br />
+&#160; cursor<br />
+&#160; list-style<br />
+&#160; list-style-image<br />
+&#160; play-during<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.4" id="s5.4"></a><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Key: <span class="term">d</span>&#160;double, <span class="term">l</span>&#160;left, <span class="term">q</span>&#160;quote, <span class="term">r</span>&#160;right, <span class="term">s.</span>&#160;single<br />
+<br />
+&#160; Code-point (decimal) - hexadecimal value - replacement entity - represented character<br />
+<br />
+&#160; 127 - 7f - (removed) - (not used)<br />
+&#160; 128 - 80 - &amp;#8364; - euro<br />
+&#160; 129 - 81 - (removed) - (not used)<br />
+&#160; 130 - 82 - &amp;#8218; - baseline s. q<br />
+&#160; 131 - 83 - &amp;#402; - florin<br />
+&#160; 132 - 84 - &amp;#8222; - baseline d q<br />
+&#160; 133 - 85 - &amp;#8230; - ellipsis<br />
+&#160; 134 - 86 - &amp;#8224; - dagger<br />
+&#160; 135 - 87 - &amp;#8225; - d dagger<br />
+&#160; 136 - 88 - &amp;#710; - circumflex accent<br />
+&#160; 137 - 89 - &amp;#8240; - permile<br />
+&#160; 138 - 8a - &amp;#352; - S Hacek<br />
+&#160; 139 - 8b - &amp;#8249; - l s. guillemet<br />
+&#160; 140 - 8c - &amp;#338; - OE ligature<br />
+&#160; 141 - 8d - (removed) - (not used)<br />
+&#160; 142 - 8e - &amp;#381; - Z dieresis<br />
+&#160; 143 - 8f - (removed) - (not used)<br />
+&#160; 144 - 90 - (removed) - (not used)<br />
+&#160; 145 - 91 - &amp;#8216; - l s. q<br />
+&#160; 146 - 92 - &amp;#8217; - r s. q<br />
+&#160; 147 - 93 - &amp;#8220; - l d q<br />
+&#160; 148 - 94 - &amp;#8221; - r d q<br />
+&#160; 149 - 95 - &amp;#8226; - bullet<br />
+&#160; 150 - 96 - &amp;#8211; - en dash<br />
+&#160; 151 - 97 - &amp;#8212; - em dash<br />
+&#160; 152 - 98 - &amp;#732; - tilde accent<br />
+&#160; 153 - 99 - &amp;#8482; - trademark<br />
+&#160; 154 - 9a - &amp;#353; - s Hacek<br />
+&#160; 155 - 9b - &amp;#8250; - r s. guillemet<br />
+&#160; 156 - 9c - &amp;#339; - oe ligature<br />
+&#160; 157 - 9d - (removed) - (not used)<br />
+&#160; 158 - 9e - &amp;#382; - z dieresis<br />
+&#160; 159 - 9f - &amp;#376; - Y dieresis<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.5" id="s5.5"></a><span class="item-no">5.5</span>&#160; URL format
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; An <em>absolute</em>&#160;URL has a <span class="term">protocol</span>&#160;or <span class="term">scheme</span>, a <span class="term">network location</span>&#160;or <span class="term">hostname</span>, and, optional <span class="term">path</span>, <span class="term">parameters</span>, <span class="term">query</span>&#160;and <span class="term">fragment</span>&#160;segments. Thus, an absolute URL has this generic structure:<br />
+<br />
+
+<code class="code">&#160; &#160; (scheme) &#58; (//network location) /(path) ;(parameters) ?(query) #(fragment)</code>
+<br />
+<br />
+&#160; The schemes can only contain letters, digits, <span class="term">+</span>, <span class="term">.</span>&#160;and <span class="term">-</span>. Hostname is the portion after the <span class="term">//</span>&#160;and up to the first <span class="term">/</span>&#160;(if any; else, up to the end) when <span class="term">&#58;</span>&#160;is followed by a <span class="term">//</span>&#160;(e.g., <span class="term">abc.com</span>&#160;in <span class="term">ftp&#58;//abc.com/def</span>); otherwise, it consists of everything after the <span class="term">&#58;</span>&#160;(e.g., <span class="term">def@abc.com</span>&#160;in mailto:def@abc.com').<br />
+<br />
+&#160; <em>Relative</em>&#160;URLs do not have explicit schemes and network locations; such values are inherited from a <em>base</em>&#160;URL.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.6" id="s5.6"></a><span class="item-no">5.6</span>&#160; Brief on htmLawed code
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Much of the code's logic and reasoning can be understood from the documentation above.<br />
+<br />
+&#160; The <strong>output</strong>&#160;of htmLawed is a text string containing the processed input. There is no custom error tracking.<br />
+<br />
+&#160; <strong>Function arguments</strong>&#160;for htmLawed are:<br />
+<br />
+&#160; * &#160;<span class="term">$in</span>&#160;- first argument; a text string; the <strong>input text</strong>&#160;to be processed. Any extraneous slashes added by PHP when <em>magic quotes</em>&#160;are enabled should be removed beforehand using PHP's <span class="term">stripslashes()</span>&#160;function.<br />
+<br />
+&#160; * &#160;<span class="term">$config</span>&#160;- second argument; an associative array; optional; named <span class="term">$C</span>&#160;within htmLawed code. The array has keys with names like <span class="term">balance</span>&#160;and <span class="term">keep_bad</span>, and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the <strong>configurable parameters</strong>&#160;(indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through <span class="term">$config</span>. <em>Finalized</em>&#160;<span class="term">$config</span>&#160;is thus a filtered and possibly larger array.<br />
+<br />
+&#160; * &#160;<span class="term">$spec</span>&#160;- third argument; a text string; optional. The string has rules, written in an htmLawed-designated format, <strong>specifying</strong>&#160;element-specific attribute and attribute value restrictions. Function <span class="term">hl_spec()</span>&#160;is used to convert the string to an associative-array, named <span class="term">$S</span>&#160;within htmLawed code, for internal use. <em>Finalized</em>&#160;<span class="term">$spec</span>&#160;is thus an array.<br />
+<br />
+&#160; <em>Finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;are made <strong>global variables</strong>&#160;while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the <em>finalized</em>&#160;values, the <span class="term">show_settings</span>&#160;parameter of <span class="term">$config</span>&#160;should be used). Depending on <span class="term">$config</span>, another global variable <span class="term">hl_Ids</span>, to track <span class="term">id</span>&#160;attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.<br />
+<br />
+&#160; Except for the main function <span class="term">htmLawed()</span>&#160;and the functions <span class="term">kses()</span>&#160;and <span class="term">kses_hook()</span>, htmLawed's functions are <strong>name-spaced</strong>&#160;using the <span class="term">hl_</span>&#160;prefix. The <strong>functions</strong>&#160;and their roles are:<br />
+<br />
+&#160; * &#160;<span class="term">hl_attrval</span>&#160;- checking attribute values against $spec<br />
+&#160; * &#160;<span class="term">hl_bal</span>&#160;- tag balancing<br />
+&#160; * &#160;<span class="term">hl_cmtcd</span>&#160;- handling CDATA sections and HTML comments<br />
+&#160; * &#160;<span class="term">hl_ent</span>&#160;- entity handling<br />
+&#160; * &#160;<span class="term">hl_prot</span>&#160;- checking a URL scheme/protocol<br />
+&#160; * &#160;<span class="term">hl_regex</span>&#160;- checking syntax of a regular expression<br />
+&#160; * &#160;<span class="term">hl_spec</span>&#160;- converting user-supplied $spec value to one used by htmLawed internally<br />
+&#160; * &#160;<span class="term">hl_tag</span>&#160;- handling tags<br />
+&#160; * &#160;<span class="term">hl_tag2</span>&#160;- transforming tags<br />
+&#160; * &#160;<span class="term">hl_tidy</span>&#160;- compact/beautify HTML<br />
+&#160; * &#160;<span class="term">hl_version</span>&#160;- reporting htmLawed version<br />
+&#160; * &#160;<span class="term">htmLawed</span>&#160;- main function<br />
+&#160; * &#160;<span class="term">kses</span>&#160;- main function of <span class="term">kses</span><br />
+&#160; * &#160;<span class="term">kses_hook</span>&#160;- hook function of <span class="term">kses</span><br />
+<br />
+&#160; The last two are for compatibility with pre-existing code using the <span class="term">kses</span>&#160;script. htmLawed's <span class="term">kses()</span>&#160;basically passes on the filtering task to <span class="term">htmLawed()</span>&#160;function after deciphering <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;from the argument values supplied to it. <span class="term">kses_hook()</span>&#160;is an empty function and is meant for being filled with custom code if the <span class="term">kses</span>&#160;script users were using one.<br />
+<br />
+&#160; <span class="term">htmLawed()</span>&#160;finalizes <span class="term">$spec</span>&#160;(with the help of <span class="term">hl_spec()</span>) and <span class="term">$config</span>, and globalizes them. Finalization of <span class="term">$config</span>&#160;involves setting default values if an inappropriate or invalid one is supplied. This includes calling <span class="term">hl_regex()</span>&#160;to check well-formedness of regular expression patterns if such expressions are user-supplied through <span class="term">$config</span>. <span class="term">htmLawed()</span>&#160;then removes invalid characters like nulls and <span class="term">x01</span>&#160;and appropriately handles entities using <span class="term">hl_ent()</span>. HTML comments and CDATA sections are identified and treated as per <span class="term">$config</span>&#160;with the help of <span class="term">hl_cmtcd()</span>. When retained, the <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;characters identifying them, and the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside them, are replaced with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>) till any tag balancing is completed.<br />
+<br />
+&#160; After this <em>initial processing</em>&#160;<span class="term">htmLawed()</span>&#160;identifies tags using regex and processes them with the help of <span class="term">hl_tag()</span>&#160;-- &#160;a large function that analyzes tag content, filtering it as per HTML standards, <span class="term">$config</span>&#160;and <span class="term">$spec</span>. Among other things, <span class="term">hl_tag()</span>&#160;transforms deprecated elements using <span class="term">hl_tag2()</span>, removes attributes from closing tags, checks attribute values as per <span class="term">$spec</span>&#160;rules using <span class="term">hl_attrval()</span>, and checks URL protocols using <span class="term">hl_prot()</span>. <span class="term">htmLawed()</span>&#160;performs tag balancing and nesting checks with a call to <span class="term">hl_bal()</span>, and optionally compacts/beautifies the output with proper white-spacing with a call to <span class="term">hl_tidy()</span>. The latter temporarily replaces white-space, and <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside <span class="term">pre</span>, <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, and HTML comments and CDATA sections with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>, and <span class="term">7</span>).<br />
+<br />
+&#160; htmLawed permits the use of custom code or <strong>hook functions</strong>&#160;at two stages. The first, called inside <span class="term">htmLawed()</span>, allows the input text as well as the finalized <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values to be altered right after the initial processing (see <a href="#s3.7">section 3.7</a>). The second is called by <span class="term">hl_tag()</span>&#160;once the tag content is finalized (see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; The functionality of htmLawed is dictated by the external HTML standard. It is thus coded for a clear-cut objective with not much concern for tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specifications will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid a quick grasp of the logic.
+</div>
+</div>
+<br />
+<hr /><br /><br /><span class="subtle"><small>HTM version of <em><a href="htmLawed_README.txt">htmLawed_README.txt</a></em> generated on 29 Aug, 2013 using <a href="http://www.bioinformatics.org/phplabware/internal_utilities">rTxt2htm</a> from PHP Labware</small></span>
+</div><!-- ended div body -->
+</div><!-- ended div top -->
+</body>
</html> \ No newline at end of file
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt
index e4027e465..5e19605e5 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt
@@ -1,1701 +1,1734 @@
-/*
-htmLawed_README.txt, 8 June 2012
-htmLawed 1.1.11, 5 June 2012
-Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
-A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
-*/
-
-
-== Content ==========================================================
-
-
-1 About htmLawed
- 1.1 Example uses
- 1.2 Features
- 1.3 History
- 1.4 License & copyright
- 1.5 Terms used here
-2 Usage
- 2.1 Simple
- 2.2 Configuring htmLawed using the '$config' parameter
- 2.3 Extra HTML specifications using the '$spec' parameter
- 2.4 Performance time & memory usage
- 2.5 Some security risks to keep in mind
- 2.6 Use without modifying old 'kses()' code
- 2.7 Tolerance for ill-written HTML
- 2.8 Limitations & work-arounds
- 2.9 Examples of usage
-3 Details
- 3.1 Invalid/dangerous characters
- 3.2 Character references/entities
- 3.3 HTML elements
- 3.3.1 HTML comments and 'CDATA' sections
- 3.3.2 Tag-transformation for better XHTML-Strict
- 3.3.3 Tag balancing and proper nesting
- 3.3.4 Elements requiring child elements
- 3.3.5 Beautify or compact HTML
- 3.4 Attributes
- 3.4.1 Auto-addition of XHTML-required attributes
- 3.4.2 Duplicate/invalid 'id' values
- 3.4.3 URL schemes (protocols) and scripts in attribute values
- 3.4.4 Absolute & relative URLs
- 3.4.5 Lower-cased, standard attribute values
- 3.4.6 Transformation of deprecated attributes
- 3.4.7 Anti-spam & 'href'
- 3.4.8 Inline style properties
- 3.4.9 Hook function for tag content
- 3.5 Simple configuration directive for most valid XHTML
- 3.6 Simple configuration directive for most `safe` HTML
- 3.7 Using a hook function
- 3.8 Obtaining `finalized` parameter values
- 3.9 Retaining non-HTML tags in input with mixed markup
-4 Other
- 4.1 Support
- 4.2 Known issues
- 4.3 Change-log
- 4.4 Testing
- 4.5 Upgrade, & old versions
- 4.6 Comparison with 'HTMLPurifier'
- 4.7 Use through application plug-ins/modules
- 4.8 Use in non-PHP applications
- 4.9 Donate
- 4.10 Acknowledgements
-5 Appendices
- 5.1 Characters discouraged in HTML
- 5.2 Valid attribute-element combinations
- 5.3 CSS 2.1 properties accepting URLs
- 5.4 Microsoft Windows 1252 character replacements
- 5.5 URL format
- 5.6 Brief on htmLawed code
-
-
-== 1 About htmLawed ================================================
-
-
- htmLawed is a highly customizable single-file PHP script to make text secure, and standard- and admin policy-compliant for use in the body of HTML 4, XHTML 1 or 1.1, or generic XML documents. It is thus a configurable input (X)HTML filter, processor, purifier, sanitizer, beautifier, etc., and an alternative to the HTMLTidy:- http://tidy.sourceforge.net application.
-
- The `lawing in` of input text is needed to ensure that HTML code in the text is standard-compliant, does not introduce security vulnerabilities, and does not break the aesthetics, design or layout of web-pages. htmLawed tries to do this by, for example, making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting ('XSS') attacks, and allowing only specified HTML elements/tags and attributes.
-
-
--- 1.1 Example uses ------------------------------------------------
-
-
- * Filtering of text submitted as comments on blogs to allow only certain HTML elements
-
- * Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant
-
- * Text processing for stricter XML standard-compliance: e.g., to have lowercased 'x' in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as 'application/xml'
-
- * Scraping text or data from web-pages
-
- * Pretty-printing HTML code
-
-
--- 1.2 Features ---------------------------------------------------o
-
-
- Key: '*' security feature, '^' standard compliance, '~' requires setting right options, '`' different from 'Kses'
-
- * make input more *secure* and *standard-compliant*
- * use for HTML 4, XHTML 1.0 or 1.1, or even generic *XML* documents ^~`
-
- * *beautify* or *compact* HTML ^~`
-
- * *restrict elements* ^~`
- * proper closure of empty elements like 'img' ^`
- * *transform deprecated elements* like 'u' ^~`
- * HTML *comments* and 'CDATA' sections can be permitted ^~`
- * elements like 'script', 'object' and 'form' can be permitted ~
-
- * *restrict attributes*, including *element-specifically* ^~`
- * remove *invalid attributes* ^`
- * element and attribute names are *lower-cased* ^
- * provide *required attributes*, like 'alt' for 'image' ^`
- * *transform deprecated attributes* ^~`
- * attributes *declared only once* ^`
-
- * *restrict attribute values*, including *element-specifically* ^~`
- * a value is declared for `empty` (`minimized`) attributes like 'checked' ^
- * check for potentially dangerous attribute values *~
- * ensure *unique* 'id' attribute values ^~`
- * *double-quote* attribute values ^
- * lower-case *standard attribute values* like 'password' ^`
-
- * *attribute-specific URL protocol/scheme restriction* *~`
- * disable *dynamic expressions* in 'style' values *~`
-
- * neutralize invalid named character entities ^`
- * *convert* hexadecimal numeric entities to decimal ones, or vice versa ^~`
- * convert named entities to numeric ones for generic XML use ^~`
-
- * remove *null* characters *
- * neutralize potentially dangerous proprietary Netscape *Javascript entities* *
- * replace potentially dangerous *soft-hyphen* character in URL-accepting attribute values with spaces *
-
- * remove common *invalid characters* not allowed in HTML or XML ^`
- * replace *characters from Microsoft applications* like 'Word' that are discouraged in HTML or XML ^~`
- * neutralize entities for characters invalid or discouraged in HTML or XML ^`
- * appropriately neutralize '<', '&', '"', and '>' characters ^*`
-
- * understands improperly spaced tag content (like, spread over more than a line) and properly spaces them `
- * attempts to *balance tags* for well-formedness ^~`
- * understands when *omitable closing tags* like '</p>' (allowed in HTML 4, transitional, e.g.) are missing ^~`
- * attempts to permit only *validly nested tags* ^~`
- * option to *remove or neutralize bad content* ^~`
- * attempts to *rectify common errors of plain-text misplacement* (e.g., directly inside 'blockquote') ^~`
-
- * fast, *non-OOP* code of ~45 kb incurring peak basal memory usage of ~0.5 MB
- * *compatible* with pre-existing code using 'Kses' (the filter used by 'WordPress')
-
- * optional *anti-spam* measures such as addition of 'rel="nofollow"' and link-disabling ~`
- * optionally makes *relative URLs absolute*, and vice versa ~`
-
- * optionally mark '&' to identify the entities for '&', '<' and '>' introduced by htmLawed ~`
-
- * allows deployment of powerful *hook functions* to *inject* HTML, *consolidate* 'style' attributes to 'class', finely check attribute values, etc. ~`
-
- * *independent of character encoding* of input and does not affect it
-
- * *tolerance for ill-written HTML* to a certain degree
-
-
--- 1.3 History ----------------------------------------------------o
-
-
- htmLawed was developed for use with 'LabWiki', a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like 'Kses' and 'HTMLPurifier' were deemed inadequate, slow, resource-intensive, or dependent on external applications like 'HTML Tidy'.
-
- htmLawed started as a modification of Ulf Harnhammar's 'Kses' (version 0.2.2) software, and is compatible with code that uses 'Kses'; see section:- #2.6.
-
-
--- 1.4 License & copyright ----------------------------------------o
-
-
- htmLawed is free and open-source software dual licensed under LGPL license version 3:- http://www.gnu.org/licenses/lgpl-3.0.txt and GPL license version 2:- http://www.gnu.org/licenses/gpl-2.0.txt or later, and copyrighted by Santosh Patnaik, MD, PhD.
-
-
--- 1.5 Terms used here --------------------------------------------o
-
-
- * `administrator` - or admin; person setting up the code to pass input through htmLawed; also, `user`
- * `attributes` - name-value pairs like 'href="http://x.com"' in opening tags
- * `author` - `writer`
- * `character` - atomic unit of text; internally represented by a numeric `code-point` as specified by the `encoding` or `charset` in use
- * `entity` - markup like '&gt;' and '&#160;' used to refer to a character
- * `element` - HTML element like 'a' and 'img'
- * `element content` - content between the opening and closing tags of an element, like 'click' of '<a href="x">click</a>'
- * `HTML` - implies XHTML unless specified otherwise
- * `input` - text string given to htmLawed to process
- * `processing` - involves filtering, correction, etc., of input
- * `safe` - absence or reduction of certain characters and HTML elements and attributes in the input that can otherwise potentially and circumstantially expose web-site users to security vulnerabilities like cross-site scripting attacks (XSS)
- * `scheme` - URL protocol like 'http' and 'ftp'
- * `specs` - standard specifications
- * `style property` - terms like 'border' and 'height' for which declarations are made in values for the 'style' attribute of elements
- * `tag` - markers like '<a href="x">' and '</a>' delineating element content; the opening tag can contain attributes
- * `tag content` - consists of tag markers '<' and '>', element names like 'div', and possibly attributes
- * `user` - administrator
- * `writer` - end-user like a blog commenter providing the input that is to be processed; also, `author`
-
-
-== 2 Usage ========================================================oo
-
-
- htmLawed should work with PHP 4.4 and higher. Either 'include()' the 'htmLawed.php' file or copy-paste the entire code.
-
- To easily *test* htmLawed using a form-based interface, use the provided demo:- htmLawedTest.php ('htmLawed.php' and 'htmLawedTest.php' should be in the same directory on the web-server).
-
- *Note*: For code for usage of the htmLawed class (for htmLawed in OOP), please refer to the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website; the filtering itself can be configured, etc., as described here.
-
-
--- 2.1 Simple ------------------------------------------------------
-
-
- The input text to be processed, '$text', is passed as an argument of type string; 'htmLawed()' returns the processed string:
-
- $processed = htmLawed($text);
-
- *Note*: If input is from a '$_GET' or '$_POST' value, and 'magic quotes' are enabled on the PHP setup, run 'stripslashes()' on the input before passing to htmLawed.
-
- By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow 'CDATA' sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- '$config' and '$spec':
-
- $processed = htmLawed($text, $config, $spec);
-
- These extra parameters are detailed below. Some examples are shown in section:- #2.9.
-
- *Note*: For maximum protection against 'XSS' and other scripting attacks (e.g., by disallowing Javascript code), consider using the 'safe' parameter; see section:- #3.6.
-
-
--- 2.2 Configuring htmLawed using the '$config' parameter ---------o
-
-
- '$config' instructs htmLawed on how to tackle certain tasks. When '$config' is not specified, or not set as an array (e.g., '$config = 1'), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in '$config' as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.
-
- $config = array('comment'=>0, 'cdata'=>1);
- $processed = htmLawed($text, $config);
-
- Or,
-
- $processed = htmLawed($text, array('comment'=>0, 'cdata'=>1));
-
- Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).
-
- Key: '*' default, '^' different default when htmLawed is used in the Kses-compatible mode (see section:- #2.6), '~' different default when 'valid_xhtml' is set to '1' (see section:- #3.5), '"' different default when 'safe' is set to '1' (see section:- #3.6)
-
- *abs_url*
- Make URLs absolute or relative; '$config["base_url"]' needs to be set; see section:- #3.4.4
-
- '-1' - make relative
- '0' - no action *
- '1' - make absolute
-
- *and_mark*
- Mark '&' characters in the original input; see section:- #3.2
-
- *anti_link_spam*
- Anti-link-spam measure; see section:- #3.4.7
-
- '0' - no measure taken *
- 'array("regex1", "regex2")' - will ensure a 'rel' attribute with 'nofollow' in its value in case the 'href' attribute value matches the regular expression pattern 'regex1', and/or will remove 'href' if its value matches the regular expression pattern 'regex2'. E.g., 'array("/./", "/://\W*(?!(abc\.com|xyz\.org))/")'; see section:- #3.4.7 for more.
-
- *anti_mail_spam*
- Anti-mail-spam measure; see section:- #3.4.7
-
- '0' - no measure taken *
- 'word' - '@' in mail address in 'href' attribute value is replaced with specified 'word'
-
- *balance*
- Balance tags for well-formedness and proper nesting; see section:- #3.3.3
-
- '0' - no
- '1' - yes *
-
- *base_url*
- Base URL value that needs to be set if '$config["abs_url"]' is not '0'; see section:- #3.4.4
-
- *cdata*
- Handling of 'CDATA' sections; see section:- #3.3.1
-
- '0' - don't consider 'CDATA' sections as markup and proceed as if plain text ^"
- '1' - remove
- '2' - allow, but neutralize any '<', '>', and '&' inside by converting them to named entities
- '3' - allow *
-
- *clean_ms_char*
- Replace discouraged characters introduced by Microsoft Word, etc.; see section:- #3.1
-
- '0' - no *
- '1' - yes
- '2' - yes, but replace special single & double quotes with ordinary ones
-
- *comment*
- Handling of HTML comments; see section:- #3.3.1
-
- '0' - don't consider comments as markup and proceed as if plain text ^"
- '1' - remove
- '2' - allow, but neutralize any '<', '>', and '&' inside by converting to named entities
- '3' - allow *
-
- *css_expression*
- Allow dynamic CSS expression by not removing the expression from CSS property values in 'style' attributes; see section:- #3.4.8
-
- '0' - remove *
- '1' - allow
-
- *deny_attribute*
- Denied HTML attributes; see section:- #3.4
-
- '0' - none *
- 'string' - dictated by values in 'string'
- 'on*' (like 'onfocus') attributes not allowed - "
-
- *direct_nest_list*
- Allow direct nesting of a list within another without requiring it to be a list item; see section:- #3.3.4
-
- '0' - no *
- '1' - yes
-
- *elements*
- Allowed HTML elements; see section:- #3.3
-
- '* -center -dir -font -isindex -menu -s -strike -u' - ~
- 'applet, embed, iframe, object, script' not allowed - "
-
- *hexdec_entity*
- Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see section:- #3.2
-
- '0' - no
- '1' - yes *
- '2' - convert decimal to hexadecimal ones
-
- *hook*
- Name of an optional hook function to alter the input string, '$config' or '$spec' before htmLawed starts its main work; see section:- #3.7
-
- '0' - no hook function *
- 'name' - 'name' is name of the hook function ('kses_hook' ^)
-
- *hook_tag*
- Name of an optional hook function to alter tag content finalized by htmLawed; see section:- #3.4.9
-
- '0' - no hook function *
- 'name' - 'name' is name of the hook function
-
- *keep_bad*
- Neutralize bad tags by converting '<' and '>' to entities, or remove them; see section:- #3.3.3
-
- '0' - remove ^
- '1' - neutralize both tags and element content
- '2' - remove tags but neutralize element content
- '3' and '4' - like '1' and '2' but remove if text ('pcdata') is invalid in parent element
- '5' and '6' * - like '3' and '4' but line-breaks, tabs and spaces are left
-
- *lc_std_val*
- For XHTML compliance, predefined, standard attribute values, like 'get' for the 'method' attribute of 'form', must be lowercased; see section:- #3.4.5
-
- '0' - no
- '1' - yes *
-
- *make_tag_strict*
- Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: 'applet' 'center' 'dir' 'embed' 'font' 'isindex' 'menu' 's' 'strike' 'u'; see section:- #3.3.2
-
- '0' - no ^
- '1' - yes, but leave 'applet', 'embed' and 'isindex' elements that currently can't be transformed *
- '2' - yes, removing 'applet', 'embed' and 'isindex' elements and their contents (nested elements remain) ~
-
- *named_entity*
- Allow non-universal named HTML entities, or convert to numeric ones; see section:- #3.2
-
- '0' - convert
- '1' - allow *
-
- *no_deprecated_attr*
- Allow deprecated attributes or transform them; see section:- #3.4.6
-
- '0' - allow ^
- '1' - transform, but 'name' attributes for 'a' and 'map' are retained *
- '2' - transform
-
- *parent*
- Name of the parent element, possibly imagined, that will hold the input; see section:- #3.3
-
- *safe*
- Magic parameter to make input the most secure against XSS without needing to specify other relevant '$config' parameters; see section:- #3.6
-
- '0' - no *
- '1' - will auto-adjust other relevant '$config' parameters (indicated by '"' in this list)
-
- *schemes*
- Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or '!' to `deny` any URL); '*' covers all unspecified attributes; see section:- #3.4.3
-
- 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https' *
- '*: ftp, gopher, http, https, mailto, news, nntp, telnet' ^
- 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: !; *:file, http, https' "
-
- *show_setting*
- Name of a PHP variable to assign the `finalized` '$config' and '$spec' values; see section:- #3.8
-
- *style_pass*
- Do not look at 'style' attribute values, letting them through without any alteration
-
- '0' - no *
- '1' - htmLawed will let through any 'style' value; see section:- #3.4.8
-
- *tidy*
- Beautify or compact HTML code; see section:- #3.3.5
-
- '-1' - compact
- '0' - no *
- '1' or 'string' - beautify (custom format specified by 'string')
-
- *unique_ids*
- 'id' attribute value checks; see section:- #3.4.2
-
- '0' - no ^
- '1' - remove duplicate and/or invalid ones *
- 'word' - remove invalid ones and replace duplicate ones with new and unique ones based on the 'word'; the admin-specified 'word', like 'my_', should begin with a letter (a-z) and can contain letters, digits, '.', '_', '-', and ':'.
-
- *valid_xhtml*
- Magic parameter to make input the most valid XHTML without needing to specify other relevant '$config' parameters; see section:- #3.5
-
- '0' - no *
- '1' - will auto-adjust other relevant '$config' parameters (indicated by '~' in this list)
-
- *xml:lang*
- Auto-adding 'xml:lang' attribute; see section:- #3.4.1
-
- '0' - no *
- '1' - add if 'lang' attribute is present
- '2' - add if 'lang' attribute is present, and remove 'lang' ~
-
-
--- 2.3 Extra HTML specifications using the $spec parameter --------o
-
-
- The '$spec' argument can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policy compliance. '$spec' is specified as a string of text containing one or more `rules`, with multiple rules separated from each other by a semi-colon (';'). E.g.,
-
- $spec = 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt';
- $processed = htmLawed($text, $config, $spec);
-
- Or,
-
- $processed = htmLawed($text, $config, 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt');
-
- A rule begins with an HTML *element* name(s) (`rule-element`), for which the rule applies, followed by an equal ('=') sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., 'th,td,tr='.
-
- Rest of the rule consists of comma-separated HTML *attribute names*. A minus ('-') character before an attribute means that the attribute is not permitted inside the rule-element. E.g., '-width'. To deny all attributes, '-*' can be used.
-
- Following shows examples of rule excerpts with rule-element 'a' and the attributes that are being permitted:
-
- * 'a=' - all
- * 'a=id' - all
- * 'a=href, title, -id, -onclick' - all except 'id' and 'onclick'
- * 'a=*, id, -id' - all except 'id'
- * 'a=-*' - none
- * 'a=-*, href, title' - none except 'href' and 'title'
- * 'a=-*, -id, href, title' - none except 'href' and 'title'
-
- Rules regarding *attribute values* are optionally specified inside round brackets after attribute names in slash ('/')-separated `parameter = value` pairs. E.g., 'title(maxlen=30/minlen=5)'. None, or one or more of the following parameters may be specified:
-
- * 'oneof' - one or more choices separated by '|' that the value should match; if only one choice is provided, then the value must match that choice
-
- * 'noneof' - one or more choices separated by '|' that the value should not match
-
- * 'maxlen' and 'minlen' - upper and lower limits for the number of characters in the attribute value; specified in numbers
-
- * 'maxval' and 'minval' - upper and lower limits for the numerical value specified in the attribute value; specified in numbers
-
- * 'match' and 'nomatch' - pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers
-
- * 'default' - a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters
-
- If 'default' is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The 'default' value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., 'maxlen' to '-1').
-
- Examples with `input` '<input title="WIDTH" value="10em" /><input title="length" value="5" />' are shown below.
-
- `Rule`: 'input=title(maxlen=60/minlen=6), value'
- `Output`: '<input value="10em" /><input title="length" value="5" />'
-
- `Rule`: 'input=title(), value(maxval=8/default=6)'
- `Output`: '<input title="WIDTH" value="6" /><input title="length" value="5" />'
-
- `Rule`: 'input=title(nomatch=%w.d%i), value(match=%em%/default=6em)'
- `Output`: '<input value="10em" /><input title="length" value="6em" />'
-
- `Rule`: 'input=title(oneof=height|depth/default=depth), value(noneof=5|6)'
- `Output`: '<input title="depth" value="10em" /><input title="depth" />'
-
- *Special characters*: The characters ';', ',', '/', '(', ')', '|', '~' and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be `escaped` by enclosing in pairs of double-quotes ('"'). A back-tick ('`') can be used to escape a literal '"'. An example rule illustrating this is 'input=value(maxlen=30/match="/^\w/"/default="your `"ID`"")'.
-
- *Note*: To deny an attribute for all elements for which it is legal, '$config["deny_attribute"]' (see section:- #3.4) can be used instead of '$spec'. Also, attributes can be allowed element-specifically through '$spec' while being denied globally through '$config["deny_attribute"]'. The 'hook_tag' parameter (section:- #3.4.9) can also be used to implement the '$spec' functionality.
-
-
--- 2.4 Performance time & memory usage ----------------------------o
-
-
- The time and memory used by htmLawed depends on its configuration and the size of the input, and the amount, nestedness and well-formedness of the HTML markup within it. In particular, tag balancing and beautification each can increase the processing time by about a quarter.
-
- The htmLawed demo:- htmLawedTest.php can be used to evaluate the performance and effects of different types of input and '$config'.
-
-
--- 2.5 Some security risks to keep in mind ------------------------o
-
-
- When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially `dangerous` HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc.
-
- Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permission has to be kept in mind. For example, following increase security risks:
-
- * Allowing 'script', 'applet', 'embed', 'iframe' or 'object' elements, or certain of their attributes like 'allowscriptaccess'
-
- * Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., '<!--[if gte IE 4]><script>alert("xss");</script><![endif]-->'
-
- * Allowing dynamic CSS expressions (a feature of the IE browser)
-
- * Allowing the 'style' attribute
-
- To remove `unsecure` HTML, code-developers using htmLawed must set '$config' appropriately. E.g., '$config["elements"] = "* -script"' to deny the 'script' element (section:- #3.3), '$config["safe"] = 1' to auto-configure ceratin htmLawed parameters for maximizing security (section:- #3.6), etc.
-
- Permitting the '*style*' attribute brings in risks of `click-jacking`, `phishing`, web-page overlays, etc., `even` when the 'safe' parameter is enabled (see section:- #3.6). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing 'style' completely and relying on CSS classes and stylesheet files is recommended.
-
- htmLawed does not check or correct the character *encoding* of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).
-
-
--- 2.6 Use without modifying old 'kses()' code --------------------o
-
-
- The 'Kses' PHP script is used by many applications (like 'WordPress'). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the 'kses()' function declared in the 'Kses' file (usually named 'kses.php'). E.g., application code like this will continue to work after replacing 'Kses' with htmLawed:
-
- $comment_filtered = kses($comment_input, array('a'=>array(), 'b'=>array(), 'i'=>array()));
-
- For some of the '$config' parameters, htmLawed will use values other than the default ones. These are indicated by '^' in section:- #2.2. To force htmLawed to use other values, function 'kses()' in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.
-
- If the application uses a 'Kses' file that has the 'kses()' function declared, then, to have the application use htmLawed instead of 'Kses', simply rename 'htmLawed.php' (to 'kses.php', e.g.) and replace the 'Kses' file (or just replace the code in the 'Kses' file with the htmLawed code). If the 'kses()' function in the 'Kses' file had been renamed by the application developer (e.g., in 'WordPress', it is named 'wp_kses()'), then appropriately rename the 'kses()' function in the htmLawed code.
-
- If the 'Kses' file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with 'WordPress', it is best to copy the htmLawed code to 'wp_includes/kses.php', rename the newly added function 'kses()' to 'wp_kses()', and delete the code for the original 'wp_kses()' function.
-
- If the 'Kses' code has a non-empty hook function (e.g., 'wp_kses_hook()' in case of 'WordPress'), then the code for htmLawed's 'kses_hook()' function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With 'WordPress', the hook function is an essential one. The following code is suggested for the htmLawed 'kses_hook()' in case of 'WordPress':
-
- function kses_hook($string, &$cf, &$spec){
- // kses compatibility
- $allowed_html = $spec;
- $allowed_protocols = array();
- foreach($cf['schemes'] as $v){
- foreach($v as $k2=>$v2){
- if(!in_array($k2, $allowed_protocols)){
- $allowed_protocols[] = $k2;
- }
- }
- }
- return wp_kses_hook($string, $allowed_html, $allowed_protocols);
- // eof
- }
-
-
--- 2.7 Tolerance for ill-written HTML -----------------------------o
-
-
- htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be `read` as HTML, and be considered mere plain text instead. Following statements indicate the degree of `looseness` that htmLawed can work with, and can be provided in instructions to writers:
-
- * Tags must be flanked by '<' and '>' with no '>' inside -- any needed '>' should be put in as '&gt;'. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and '>', like '<div >' and '<img / >', but not after the '<'.
-
- * Element and attribute names need not be lower-cased.
-
- * Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.
-
- * Attribute values may not be double-quoted, or may be single-quoted.
-
- * Left-padding of numeric entities (like, '&#0160;', '&x07ff;') with '0' is okay as long as the number of characters between between the '&' and the ';' does not exceed 8. All entities must end with ';' though.
-
- * Named character entities must be properly cased. E.g., '&Lt;' or '&TILDE;' will not be let through without modification.
-
- * HTML comments should not be inside element tags (okay between tags), and should begin with '<!--' and end with '-->'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any '-->' inside should be put in as '--&gt;'. Any '--' inside will be automatically converted to '-', and a space will be added before the comment delimiter '-->'.
-
- * 'CDATA' sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with '<[CDATA[' and end with ']]>'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any ']]>' inside should be put in as ']]&gt;'.
-
- * For attribute values, character entities '&lt;', '&gt;' and '&amp;' should be used instead of characters '<' and '>', and '&' (when '&' is not part of a character entity). This applies even for Javascript code in values of attributes like 'onclick'.
-
- * Characters '<', '>', '&' and '"' that are part of actual Javascript, etc., code in 'script' elements should be used as such and not be put in as entities like '&gt;'. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside 'CDATA' sections.
-
- * Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on '$config["keep_bad"]', some code/text may be lost.
-
- * Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.
-
- * With '$config["unique_ids"]' not '0' and the 'id' attribute being permitted, writers should carefully avoid using duplicate or invalid 'id' values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when '<a id="home"></a><input id="home" /><label for="home"></label>' is processed into
-'<a id="home"></a><input id="prefix_home" /><label for="home"></label>'.
-
- * Note that even if intended HTML is lost in a highly ill-written input, the processed output will be more secure and standard-compliant.
-
- * For URLs, unless '$config["scheme"]' is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., 'htt&#112;' (which many browsers will read as the harmless 'http') may be considered bad by htmLawed.
-
- * htmLawed will attempt to put plain text present directly inside 'blockquote', 'form', 'map' and 'noscript' elements (illegal as per the specs) inside auto-generated 'div' elements.
-
-
--- 2.8 Limitations & work-arounds ---------------------------------o
-
-
- htmLawed's main objective is to make the input text `more` standard-compliant, secure for web-page readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.
-
- It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specs (like asking for normalization of white-spacing within 'textarea' elements) are clearly wrong. Regarding security, note that `unsafe` HTML code is not necessarily legally invalid.
-
- * htmLawed is meant for input that goes into the 'body' of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements 'frameset', 'frame' and 'noframes'.
-
- * It cannot transform the non-standard 'embed' elements to the standard-compliant 'object' elements. Yet, it can allow 'embed' elements if permitted ('embed' is widely used and supported). Admins can certainly use the 'hook_tag' parameter (section:- #3.4.9) to deploy a custom embed-to-object converter function.
-
- * The only non-standard element that may be permitted is 'embed'; others like 'noembed' and 'nobr' cannot be permitted without modifying the htmLawed code.
-
- * It cannot handle input that has non-HTML code like 'SVG' and 'MathML'. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in section:- #3.9. A third way may be to some how take advantage of the '$config["and_mark"]' parameter (see section:- #3.2).
-
- * By default, htmLawed won't check many attribute values for standard compliance. E.g., 'width="20m"' with the dimension in non-standard 'm' is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the 'hook_tag' parameter (section:- #3.4.9) or '$spec' to enforce finer checks.
-
- * The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specs. Only a few of the proprietary attributes are supported.
-
- * Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the 'hook_tag' parameter (section:- #3.4.9) or '$spec' for finer checks. Perhaps the best option is to disallow 'style' but allow 'class' attributes with the right 'oneof' or 'match' values for 'class', and have the various class style properties in '.css' CSS stylesheet files.
-
- * htmLawed does not parse emoticons, decode `BBcode`, or `wikify`, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to 'br' elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.
-
- * htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate 'target' and 'onclick' attributes to 'a'). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * Nesting-based checks are not possible. E.g., one cannot disallow 'p' elements specifically inside 'td' while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert 'http' to 'https'. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * Pairs of opening and closing tags that do not enclose any content (like '<em></em>') are not removed. This may be against the standard specs for certain elements (e.g., 'table'). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.
-
- * htmLawed does not check for certain element orderings described in the standard specs (e.g., in a 'table', 'tbody' is allowed before 'tfoot'). Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * htmLawed does not check the number of nested elements. E.g., it will allow two 'caption' elements in a 'table' element, illegal as per the specs. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers ('/*') in 'style' attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like '&#101;xpression...'. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the 'hook_tag' parameter (section:- #3.4.9) to more specifically identify CSS expressions in the 'style' attribute values. Also, using '$config["style_pass"]', it is possible to have htmLawed pass 'style' attribute values without even looking at them (section:- #3.4.8).
-
- * htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., '<a href="http://x%22+style=%22background-image:xss">x</a>'). These arise when browsers mis-identify markup in `escaped` text, defeating the very purpose of escaping text (a bad browser will read the given example as '<a href="http://x" style="background-image:xss">x</a>').
-
- * Because of poor Unicode support in PHP, htmLawed does not remove the `high value` HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see section:- #3.1).
-
- * htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).
-
- * Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.
-
-
--- 2.9 Examples of usage -------------------------------------------o
-
-
- Safest, allowing only `safe` HTML markup --
-
- $config = array('safe'=>1);
- $out = htmLawed($in);
-
- Simplest, allowing all valid HTML markup except 'javascript:' --
-
- $out = htmLawed($in);
-
- Allowing all valid HTML markup including 'javascript:' --
-
- $config = array('schemes'=>'*:*');
- $out = htmLawed($in, $config);
-
- Allowing only 'safe' HTML and the elements 'a', 'em', and 'strong' --
-
- $config = array('safe'=>1, 'elements'=>'a, em, strong');
- $out = htmLawed($in, $config);
-
- Not allowing elements 'script' and 'object' --
-
- $config = array('elements'=>'* -script -object');
- $out = htmLawed($in, $config);
-
- Not allowing attributes 'id' and 'style' --
-
- $config = array('deny_attribute'=>'id, style');
- $out = htmLawed($in, $config);
-
- Permitting only attributes 'title' and 'href' --
-
- $config = array('deny_attribute'=>'* -title -href');
- $out = htmLawed($in, $config);
-
- Remove bad/disallowed tags altogether instead of converting them to entities --
-
- $config = array('keep_bad'=>0);
- $out = htmLawed($in, $config);
-
- Allowing attribute 'title' only in 'a' and not allowing attributes 'id', 'style', or scriptable `on*` attributes like 'onclick' --
-
- $config = array('deny_attribute'=>'title, id, style, on*');
- $spec = 'a=title';
- $out = htmLawed($in, $config, $spec);
-
- Some case-studies are presented below.
-
- *1.* A blog administrator wants to allow only 'a', 'em', 'strike', 'strong' and 'u' in comments, but needs 'strike' and 'u' transformed to 'span' for better XHTML 1-strict compliance, and, he wants the 'a' links to be to 'http' or 'https' resources:
-
- $processed = htmLawed($in, array('elements'=>'a, em, strike, strong, u', 'make_tag_strict'=>1, 'safe'=>1, 'schemes'=>'*:http, https'), 'a=href');
-
- *2.* An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional `bad` characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:
-
- $processed = htmLawed($in, array('css_expression'=>1, 'keep_bad'=>1, 'make_tag_strict'=>1, 'schemes'=>'*:*', 'valid_xhtml'=>1));
-
- For the final submission process, 'keep_bad' is set to '6'. A value of '1' for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.
-
- *3.* A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:
-
- $processed = htmLawed($in, array('elements'=>'tr, td', 'tidy'=>-1), 'tr, td =');
-
-
-== 3 Details =====================================================oo
-
-
--- 3.1 Invalid/dangerous characters --------------------------------
-
-
- Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, '9', 'a', 'd', '20' to 'd7ff', and 'e000' to '10ffff', except 'fffe' and 'ffff' (decimally, '9', '10', '13', '32' to '55295', and '57344' to '1114111', except '65534' and '65535'). htmLawed removes the invalid characters '0' to '8', 'b', 'c', and 'e' to '1f'.
-
- Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.
-
- Characters that are discouraged (see section:- #5.1) but not invalid are not removed by htmLawed.
-
- It (function 'hl_tag()') also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, 'ad', or decimally, '173') in attribute values with spaces. Where required, the characters '<', '>', '&', and '"' are converted to entities.
-
- With '$config["clean_ms_char"]' set as '1' or '2', many of the discouraged characters (decimal code-points '127' to '159' except '133') that many Microsoft applications incorrectly use (as per the 'Windows 1252' ['Cp-1252'] or a similar encoding system), and the character for decimal code-point '133', are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in section:- #5.4. This can help avoid some display issues arising from copying-pasting of content.
-
- With '$config["clean_ms_char"]' set as '2', characters for the hexadecimal code-points '82', '91', and '92' (for special single-quotes), and '84', '93', and '94' (for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.
-
- The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.
-
- The '$config["clean_ms_char"]' parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the 'Windows 1252' ('Cp-1252') or a similar encoding like 'Cp-1251'. Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.
-
-
--- 3.2 Character references/entities ------------------------------o
-
-
- Valid character entities take the form '&*;' where '*' is '#x' followed by a hexadecimal number (hexadecimal numeric entity; like '&#xA0;' for non-breaking space), or alphanumeric like 'gt' (external or named entity; like '&nbsp;' for non-breaking space), or '#' followed by a number (decimal numeric entity; like '&#160;' for non-breaking space). Character entities referring to the soft-hyphen character (the '&shy;' or '\xad' character; hexadecimal code-point 'ad' [decimal '173']) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.
-
- htmLawed (function 'hl_ent()'):
-
- * Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)
-
- * Lowercases the 'X' (for XML-compliance) and 'A-F' of hexadecimal numeric entities
-
- * Neutralizes entities referring to characters that are HTML-invalid (see section:- #3.1)
-
- * Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, '7f' to '84', '86' to '9f', and 'fdd0' to 'fddf', or decimally, '127' to '132', '134' to '159', and '64991' to '64976'). Entities referring to the remaining discouraged characters (see section:- #5.1 for a full list) are let through.
-
- * Neutralizes named entities that are not in the specs.
-
- * Optionally converts valid HTML-specific named entities except '&gt;', '&lt;', '&quot;', and '&amp;' to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is '2') for generic XML-compliance. For this, '$config["named_entity"]' should be '1'.
-
- * Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, '$config["hexdec_entity"]' should be '0'.
-
- * Optionally converts decimal numeric entities to the hexadecimal ones. For this, '$config["hexdec_entity"]' should be '2'.
-
- `Neutralization` refers to the `entitification` of '&' to '&amp;'.
-
- *Note*: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's 'html_entity_decode' function:- http://www.php.net/html_entity_decode for that.
-
- *Note*: If '$config["and_mark"]' is set, and set to a value other than '0', then the '&' characters in the original input are replaced with the control character for the hexadecimal code-point '6' ('\x06'; '&' characters introduced by htmLawed, e.g., after converting '<' to '&lt;', are not affected). This allows one to distinguish, say, an '&gt;' introduced by htmLawed and an '&gt;' put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence 'o(><)o' to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the '\x06' can break documents. Before use in such documents, and preferably before any storage, any remaining '\x06' should be changed back to '&', e.g., with:
-
- $final = str_replace("\x06", '&', $prelim);
-
- Also, see section:- #3.9.
-
-
--- 3.3 HTML elements ----------------------------------------------o
-
-
- htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on '$config["keep_bad"]', are either `neutralized` (converted to plain text by entitification of '<' and '>') or removed.
-
- E.g., with only 'em' permitted:
-
- Input:
-
- <em>My</em> website is <a href="http://a.com>a.com</a>.
-
- Output, with '$config["keep_bad"] = 0':
-
- <em>My</em> website is a.com.
-
- Output, with '$config["keep_bad"]' not '0':
-
- <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;.
-
- See section:- #3.3.3 for differences between the various non-zero '$config["keep_bad"]' values.
-
- htmLawed by default permits these 86 elements:
-
- a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var
-
- Except for 'embed' (included because of its wide-spread use) and the Ruby elements ('rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby'; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude 'center', 'dir', 'font', 'isindex', 'menu', 's', 'strike', and 'u'.
-
- With '$config["safe"] = 1', the default set will exclude 'applet', 'embed', 'iframe', 'object' and 'script'; see section:- #3.6.
-
- When '$config["elements"]', which specifies allowed elements, is `properly` defined, and neither empty nor set to '0' or '*', the default set is not used. To have elements added to or removed from the default set, a '+/-' notation is used. E.g., '*-script-object' implies that only 'script' and 'object' are disallowed, whereas '*+embed' means that 'noembed' is also allowed. Elements can also be specified as comma separated names. E.g., 'a, b, i' means only 'a', 'b' and 'i' are permitted. In this notation, '*', '+' and '-' have no significance and can actually cause a mis-reading.
-
- Some more examples of '$config["elements"]' values indicating permitted elements (note that empty spaces are liberally allowed for clarity):
-
- * 'a, blockquote, code, em, strong' -- only 'a', 'blockquote', 'code', 'em', and 'strong'
- * '*-script' -- all excluding 'script'
- * '* -center -dir -font -isindex -menu -s -strike -u' -- only XHTML-Strict elements
- * '*+noembed-script' -- all including 'noembed' excluding 'script'
-
- Some mis-usages (and the resulting permitted elements) that can be avoided:
-
- * '-*' -- none; instead of htmLawed, one might just use, e.g., the 'htmlspecialchars()' PHP function
- * '*, -script' -- all except 'script'; admin probably meant '*-script'
- * '-*, a, em, strong' -- all; admin probably meant 'a, em, strong'
- * '*' -- all; admin need not have set 'elements'
- * '*-form+form' -- all; a '+' will always over-ride any '-'
- * '*, noembed' -- only 'noembed'; admin probably meant '*+noembed'
- * 'a, +b, i' -- only 'a' and 'i'; admin probably meant 'a, b, i'
-
- Basically, when using the '+/-' notation, commas (',') should not be used, and vice versa, and '*' should be used with the former but not the latter.
-
- *Note*: Even if an element that is not in the default set is allowed through '$config["elements"]', like 'noembed' in the last example, it will eventually be removed during tag balancing unless such balancing is turned off ('$config["balance"]' set to '0'). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function 'hl_bal()' to accommodate the element and its nesting properties.
-
- *A possibly second way to specify allowed elements* is to set '$config["parent"]' to an element name that supposedly will hold the input, and to set '$config["balance"]' to '1'. During tag balancing (see section:- #3.3.3), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to 'div' if '$config["parent"]' is empty, 'body', or an element not in htmLawed's default set of 86 elements.
-
- `Tag transformation` is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see section:- #3.3.2.
-
-
-.. 3.3.1 Handling of comments and CDATA sections ...................
-
-
- 'CDATA' sections have the format '<![CDATA[...anything but not "]]>"...]]>', and HTML comments, '<!--...anything but not "-->"... -->'. Neither HTML comments nor 'CDATA' sections can reside inside tags. HTML comments can exist anywhere else, but 'CDATA' sections can exist only where plain text is allowed (e.g., immediately inside 'td' element content but not immediately inside 'tr' element content).
-
- htmLawed (function 'hl_cmtcd()') handles HTML comments or 'CDATA' sections depending on the values of '$config["comment"]' or '$config["cdata"]'. If '0', such markup is not looked for and the text is processed like plain text. If '1', it is removed completely. If '2', it is preserved but any '<', '>' and '&' inside are changed to entities. If '3', they are left as such.
-
- Note that for the last two cases, HTML comments and 'CDATA' sections will always be removed from tag content (function 'hl_tag()').
-
- Examples:
-
- Input:
- <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
- Output ('$config["comment"] = 0, $config["cdata"] = 2'):
- &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
- Output ('$config["comment"] = 1, $config["cdata"] = 2'):
- <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
- Output ('$config["comment"] = 2, $config["cdata"] = 2'):
- <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
- Output ('$config["comment"] = 2, $config["cdata"] = 1'):
- <!-- home link --><a href="home.htm">Home</a>
- Output ('$config["comment"] = 3, $config["cdata"] = 3'):
- <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
-
- For standard-compliance, comments are given the form '<!--comment -->', and any '--' in the content is made '-'.
-
- When '$config["safe"] = 1', CDATA sections and comments are considered plain text unless '$config["comment"]' or '$config["cdata"]' is explicitly specified; see section:- #3.6.
-
-
-.. 3.3.2 Tag-transformation for better XHTML-Strict ................o
-
-
- If '$config["make_tag_strict"]' is set and not '0', following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function 'hl_tag2()'):
-
- * applet - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
- * center - 'div style="text-align: center;"'
- * dir - 'ul'
- * embed - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
- * font (face, size, color) - 'span style="font-family: ; font-size: ; color: ;"' (size transformation reference:- http://style.cleverchimp.com/font_size_intervals/altintervals.html)
- * isindex - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
- * menu - 'ul'
- * s - 'span style="text-decoration: line-through;"'
- * strike - 'span style="text-decoration: line-through;"'
- * u - 'span style="text-decoration: underline;"'
-
- For an element with a pre-existing 'style' attribute value, the extra style properties are appended.
-
- Example input:
-
- <center>
- The PHP <s>software</s> script used for this <strike>web-page</strike> web-page is <font style="font-weight: bold " face=arial size='+3' color = "red ">htmLawedTest.php</font>, from <u style= 'color:green'>PHP Labware</u>.
- </center>
-
- The output:
-
- <div style="text-align: center;">
- The PHP <span style="text-decoration: line-through;">software</span> script used for this <span style="text-decoration: line-through;">web-page</span> web-page is <span style="font-weight: bold; font-family: arial; color: red; font-size: 200%;">htmLawedTest.php</span>, from <span style="color:green; text-decoration: underline;">PHP Labware</span>.
- </div>
-
-
--- 3.3.3 Tag balancing and proper nesting -------------------------o
-
-
- If '$config["balance"]' is set to '1', htmLawed (function 'hl_bal()') checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).
-
- Depending on the value of '$config["keep_bad"]' (see section:- #2.2 and section:- #3.3), illegal content may be removed or neutralized to plain text by converting < and > to entities:
-
- '0' - remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see section:- #2.6)
- '1' - neutralize tags and keep element content
- '2' - remove tags but keep element content
- '3' and '4' - like '1' and '2', but keep element content only if text ('pcdata') is valid in parent element as per specs
- '5' and '6' - like '3' and '4', but line-breaks, tabs and spaces are left
-
- Example input (disallowing the 'p' element):
-
- <*> Pseudo-tags <*>
- <xml>Non-HTML tag xml</xml>
- <p>
- Disallowed tag p
- </p>
- <ul>Bad<li>OK</li></ul>
-
- The output with '$config["keep_bad"] = 1':
-
- &lt;*&gt; Pseudo-tags &lt;*&gt;
- &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
- &lt;p&gt;
- Disallowed tag p
- &lt;/p&gt;
- <ul>Bad<li>OK</li></ul>
-
- The output with '$config["keep_bad"] = 3':
-
- &lt;*&gt; Pseudo-tags &lt;*&gt;
- &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
- &lt;p&gt;
- Disallowed tag p
- &lt;/p&gt;
- <ul><li>OK</li></ul>
-
- The output with '$config["keep_bad"] = 6':
-
- &lt;*&gt; Pseudo-tags &lt;*&gt;
- Non-HTML tag xml
-
- Disallowed tag p
-
- <ul><li>OK</li></ul>
-
- An option like '1' is useful, e.g., when a writer previews his submission, whereas one like '3' is useful before content is finalized and made available to all.
-
- *Note:* In the example above, unlike '<*>', '<xml>' gets considered as a tag (even though there is no HTML element named 'xml'). In general, text matching the regular expression pattern '<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>' is considered a tag (phrase enclosed by the angled brackets '<' and '>', and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...).
-
- Nesting/content rules for each of the 86 elements in htmLawed's default set (see section:- #3.3) are defined in function 'hl_bal()'. This means that if a non-standard element besides 'embed' is being permitted through '$config["elements"]', the element's tag content will end up getting removed if '$config["balance"]' is set to '1'.
-
- Plain text and/or certain elements nested inside 'blockquote', 'form', 'map' and 'noscript' need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as 'form', the input 'B:<input type="text" value="b" />C:<input type="text" value="c" />' is converted to '<div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div>'.
-
-
--- 3.3.4 Elements requiring child elements ------------------------o
-
-
- As per specs, the following elements require legal child elements nested inside them:
-
- blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul
-
- In some cases, the specs stipulate the number and/or the ordering of the child elements. A 'table' can have 0 or 1 'caption', 'tbody', 'tfoot', and 'thead', but they must be in this order: 'caption', 'thead', 'tfoot', 'tbody'.
-
- htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.
-
- With '$config["direct_list_nest"]' set to '1', htmLawed will allow direct nesting of an 'ol' or 'ul' list within another 'ol' or 'ul' without requiring the child list to be within an 'li' of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter '$config["direct_list_nest"]' has no effect if tag-balancing (section:- #3.3.3) is turned off.
-
-
--- 3.3.5 Beautify or compact HTML ---------------------------------o
-
-
- By default, htmLawed will neither `beautify` HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)
-
- As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside 'pre' elements) are all considered equivalent, and referred to as `white-spaces`. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space `normalization` allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such `pretty` HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.
-
- With the '$config' parameter 'tidy', htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides 'pre', the 'script' and 'textarea' elements, CDATA sections, and HTML comments are not subjected to the tidying process.
-
- To `compact`, use '$config["tidy"] = -1'; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.
-
- To `beautify`, '$config["tidy"]' is set as '1', or for customized tidying, as a string like '2s2n'. The 's' or 't' character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The 'r' and 'n' characters are used to specify line-break characters: 'n' for '\n' (Unix/Mac OS X line-breaks), 'rn' or 'nr' for '\r\n' (Windows/DOS line-breaks), or 'r' for '\r'.
-
- The '$config["tidy"]' value of '1' is equivalent to '2s0n'. Other '$config["tidy"]' values are read loosely: a value of '4' is equivalent to '4s0n'; 't2', to '1t2n'; 's', to '2s0n'; '2TR', to '2t0r'; 'T1', to '1t1n'; 'nr3', to '3s0nr', and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.
-
- Input formatting using '$config["tidy"]' is not recommended when input text has mixed markup (like HTML + PHP).
-
-
--- 3.4 Attributes ------------------------------------------------oo
-
-
- htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the 'embed' element (the non-standard 'embed' element is supported in htmLawed because of its widespread use), and the the 'xml:space' attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in section:- #5.2.
-
- When '$config["deny_attribute"]' is not set, or set to '0', or empty ('""'), all the 111 attributes are permitted. Otherwise, '$config["deny_attribute"]' can be set as a list of comma-separated names of the denied attributes. 'on*' can be used to refer to the group of potentially dangerous, script-accepting attributes: 'onblur', 'onchange', 'onclick', 'ondblclick', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onreset', 'onselect' and 'onsubmit'.
-
- Note that attributes specified in '$config["deny_attribute"]' are denied globally, for all elements. To deny attributes for only specific elements, '$spec' (see section:- #2.3) can be used. '$spec' can also be used to element-specifically permit an attribute otherwise denied through '$config["deny_attribute"]'.
-
- With '$config["safe"] = 1' (section:- #3.6), the 'on*' attributes are automatically disallowed.
-
- *Note*: To deny all but a few attributes globally, a simpler way to specify '$config["deny_attribute"]' would be to use the notation '* -attribute1 -attribute2 ...'. Thus, a value of '* -title -href' implies that except 'href' and 'title' (where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter 'safe' (section:- #3.6) will have no effect on 'deny_attribute'.
-
- htmLawed (function 'hl_tag()') also:
-
- * Lower-cases attribute names
- * Removes duplicate attributes (last one stays)
- * Gives attributes the form 'name="value"' and single-spaces them, removing unnecessary white-spacing
- * Provides `required` attributes (see section:- #3.4.1)
- * Double-quotes values and escapes any '"' inside them
- * Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point 'ad') in the values with spaces
- * Allows custom function to additionally filter/modify attribute values (see section:- #3.4.9)
-
-
-.. 3.4.1 Auto-addition of XHTML-required attributes ................
-
-
- If indicated attributes for the following elements are found missing, htmLawed (function 'hl_tag()') will add them (with values same as attribute names unless indicated otherwise below):
-
- * area - alt ('area')
- * area, img - src, alt ('image')
- * bdo - dir ('ltr')
- * form - action
- * map - name
- * optgroup - label
- * param - name
- * script - type ('text/javascript')
- * textarea - rows ('10'), cols ('50')
-
- Additionally, with '$config["xml:lang"]' set to '1' or '2', if the 'lang' but not the 'xml:lang' attribute is declared, then the latter is added too, with a value copied from that of 'lang'. This is for better standard-compliance. With '$config["xml:lang"]' set to '2', the 'lang' attribute is removed (XHTML 1.1 specs).
-
- Note that the 'name' attribute for 'map', invalid in XHTML 1.1, is also transformed if required -- see section:- #3.4.6.
-
-
-.. 3.4.2 Duplicate/invalid 'id' values ............................o
-
-
- If '$config["unique_ids"]' is '1', htmLawed (function 'hl_tag()') removes 'id' attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, ':', '.', '-' and '_') or duplicate. If '$config["unique_ids"]' is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, ':', '.', '_' and '-'.
-
- Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of 'id' values as it uses a global variable ('$GLOBALS["hl_Ids"]' array). Further, an admin can restrict the use of certain 'id' values by presetting this variable before htmLawed is called into use. E.g.:
-
- $GLOBALS['hl_Ids'] = array('top'=>1, 'bottom'=>1, 'myform'=>1); // id values not allowed in input
- $processed = htmLawed($text); // filter input
-
-
-.. 3.4.3 URL schemes (protocols) and scripts in attribute values ............o
-
-
- htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the 'afp' scheme is not permitted, then '<a href="afp://domain.org">' becomes '<a href="denied:afp://domain.org">', and if Javascript is not permitted '<a onclick="javascript:xss();">' becomes '<a onclick="denied:javascript:xss();">'.
-
- By default htmLawed permits these schemes in URLs for the 'href' attribute:
-
- aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet
-
- Also, only 'file', 'http' and 'https' are permitted in attributes whose names start with 'o' (like 'onmouseover'), and in these attributes that accept URLs:
-
- action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap
-
- These default sets are used when '$config["schemes"]' is not set (see section:- #2.2). To over-ride the defaults, '$config["schemes"]' is defined as a string of semi-colon-separated sub-strings of type 'attribute: comma-separated schemes'. E.g., 'href: mailto, http, https; onclick: javascript; src: http, https'. For unspecified attributes, 'file', 'http' and 'https' are permitted. This can be changed by passing schemes for '*' in '$config["schemes"]'. E.g., 'href: mailto, http, https; *: https, https'.
-
- '*' can be put in the list of schemes to permit all protocols. E.g., 'style: *; img: http, https' results in protocols not being checked in 'style' attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (section:- #3.4.4) is not done.
-
- Thus, `to allow Javascript`, one can set '$config["schemes"]' as 'href: mailto, http, https; *: http, https, javascript', or 'href: mailto, http, https, javascript; *: http, https, javascript', or '*: *', and so on.
-
- As a side-note, one may find 'style: *' useful as URLs in 'style' attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.
-
- '!' can be put in the list of schemes to disallow all protocols as well as `local` URLs. Thus, with 'href: http, style: !', '<a href="http://cnn.com" style="background-image: url('local.jpg');">CNN</a>' will become '<a href="http://cnn.com" style="background-image: url('denied:local.jpg');">CNN</a>'.
-
- *Note*: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string 'src' (e.g., 'dynsrc') or starts with 'o' (e.g., 'onbeforecopy').
-
- With '$config["safe"] = 1', all URLs are disallowed in the 'style' attribute values.
-
-
-.. 3.4.4 Absolute & relative URLs in attribute values .............o
-
-
- htmLawed can make absolute URLs in attributes like 'href' relative ('$config["abs_url"]' is '-1'), and vice versa ('$config["abs_url"]' is '1'). URLs in scripts are not considered for this, and so are URLs like '#section_6' (fragment), '?name=Tim#show' (starting with query string), and ';var=1?name=Tim#show' (starting with parameters). Further, this requires that '$config["base_url"]' be set properly, with the '://' and a trailing slash ('/'), with no query string, etc. E.g., 'file:///D:/page/', 'https://abc.com/x/y/', or 'http://localhost/demo/' are okay, but 'file:///D:/page/?help=1', 'abc.com/x/y/' and 'http://localhost/demo/index.htm' are not.
-
- For making absolute URLs relative, only those URLs that have the '$config["base_url"]' string at the beginning are converted. E.g., with '$config["base_url"] = "https://abc.com/x/y/"', 'https://abc.com/x/y/a.gif' and 'https://abc.com/x/y/z/b.gif' become 'a.gif' and 'z/b.gif' respectively, while 'https://abc.com/x/c.gif' is not changed.
-
- When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See section:- #5.5 for more about the URL specification as per RFC 1808:- http://www.ietf.org/rfc/rfc1808.txt.
-
-
-.. 3.4.5 Lower-cased, standard attribute values ....................o
-
-
- Optionally, for standard-compliance, htmLawed (function 'hl_tag()') lower-cases standard attribute values to give, e.g., 'input type="password"' instead of 'input type="Password"', if '$config["lc_std_val"]' is '1'. Attribute values matching those listed below for any of the elements (plus those for the 'type' attribute of 'button' or 'input') are lower-cased:
-
- all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top
-
- a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml:space
-
- The following `empty` (`minimized`) attributes are always assigned lower-cased values (same as the names):
-
- checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected
-
-
-.. 3.4.6 Transformation of deprecated attributes ..................o
-
-
- If '$config["no_deprecated_attr"]' is '0', then deprecated attributes (see appendix in section:- #5.2) are removed and, in most cases, their values are transformed to CSS style properties and added to the 'style' attributes (function 'hl_tag()'). Except for 'bordercolor' for 'table', 'tr' and 'td', the scores of proprietary attributes that were never part of any cross-browser standard are not supported.
-
- *Note*: The attribute 'target' for 'a' is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.
-
- * align - for 'img' with value of 'left' or 'right', becomes, e.g., 'float: left'; for 'div' and 'table' with value 'center', becomes 'margin: auto'; all others become, e.g., 'text-align: right'
-
- * bgcolor - E.g., 'bgcolor="#ffffff"' becomes 'background-color: #ffffff'
- * border - E.g., 'height= "10"' becomes 'height: 10px'
- * bordercolor - E.g., 'bordercolor=#999999' becomes 'border-color: #999999;'
- * compact - 'font-size: 85%'
- * clear - E.g., 'clear="all" becomes 'clear: both'
-
- * height - E.g., 'height= "10"' becomes 'height: 10px' and 'height="*"' becomes 'height: auto'
-
- * hspace - E.g., 'hspace="10"' becomes 'margin-left: 10px; margin-right: 10px'
- * language - 'language="VBScript"' becomes 'type="text/vbscript"'
- * name - E.g., 'name="xx"' becomes 'id="xx"'
- * noshade - 'border-style: none; border: 0; background-color: gray; color: gray'
- * nowrap - 'white-space: nowrap'
- * size - E.g., 'size="10"' becomes 'height: 10px'
- * start - removed
- * type - E.g., 'type="i"' becomes 'list-style-type: lower-roman'
- * value - removed
- * vspace - E.g., 'vspace="10"' becomes 'margin-top: 10px; margin-bottom: 10px'
- * width - like 'height'
-
- Example input:
-
- <img src="j.gif" alt="image" name="dad's" /><img src="k.gif" alt="image" id="dad_off" name="dad" />
- <br clear="left" />
- <hr noshade size="1" />
- <img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding:5px;" />
- <table width="50em" align="center" bgcolor="red">
- <tr>
- <td width="20%">
- <div align="center">
- <h3 align="right">Section</h3>
- <p align="right">Para</p>
- <ol type="a" start="e"><li value="x">First item</li></ol>
- </div>
- </td>
- <td width="*">
- <ol type="1"><li>First item</li></ol>
- </td>
- </tr>
- </table>
- <br clear="all" />
-
- And the output with '$config["no_deprecated_attr"] = 1':
-
- <img src="j.gif" alt="image" /><img src="k.gif" alt="image" id="dad_off" />
- <br style="clear: left;" />
- <hr style="border-style: none; border: 0; background-color: gray; color: gray; size: 1px;" />
- <img src="i.gif" alt="image" width="10em" height="20" style="padding:5px; float: left; margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px; border: 1px;" id="img" />
- <table width="50em" style="margin: auto; background-color: red;">
- <tr>
- <td style="width: 20%;">
- <div style="margin: auto;">
- <h3 style="text-align: right;">Section</h3>
- <p style="text-align: right;">Para</p>
- <ol style="list-style-type: lower-latin;"><li>First item</li></ol>
- </div>
- </td>
- <td style="width: auto;">
- <ol style="list-style-type: decimal;"><li>First item</li></ol>
- </td>
- </tr>
- </table>
- <br style="clear: both;" />
-
- For 'lang', deprecated in XHTML 1.1, transformation is taken care of through '$config["xml:lang"]'; see section:- #3.4.1.
-
- The attribute 'name' is deprecated in 'form', 'iframe', and 'img', and is replaced with 'id' if an 'id' attribute doesn't exist and if the 'name' value is appropriate for 'id'. For such replacements for 'a' and 'map', for which the 'name' attribute is deprecated in XHTML 1.1, '$config["no_deprecated_attr"]' should be set to '2' (when set to '1', for these two elements, the 'name' attribute is retained).
-
-
--- 3.4.7 Anti-spam & 'href' ---------------------------------------o
-
-
- htmLawed (function 'hl_tag()') can check the 'href' attribute values (link addresses) as an anti-spam (email or link spam) measure.
-
- If '$config["anti_mail_spam"]' is not '0', the '@' of email addresses in 'href' values like 'mailto:a@b.com' is replaced with text specified by '$config["anti_mail_spam"]'. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., '<remove_this_antispam>@' (makes the example address 'a<remove_this_antispam>@b.com').
-
- For regular links, one can choose to have a 'rel' attribute with 'nofollow' in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the 'href' value altogether (disable the link).
-
- For use of these options, '$config["anti_link_spam"]' should be set as an array with values 'regex1' and 'regex2', both or one of which can be empty (like 'array("", "regex2")') to indicate that that option is not to be used. Otherwise, 'regex1' or 'regex2' should be PHP- and PCRE-compatible regular expression patterns: 'href' values will be matched against them and those matching the pattern will accordingly be treated.
-
- Note that the regular expressions should have `delimiters`, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.
-
- An example, to have a 'rel' attribute with 'nofollow' for all links, and to disable links that do not point to domains 'abc.com' and 'xyz.org':
-
- $config["anti_link_spam"] = array('`.`', '`://\W*(?!(abc\.com|xyz\.org))`');
-
-
--- 3.4.8 Inline style properties ----------------------------------o
-
-
- htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the 'style' attributes. (CSS properties like 'background-image' that accept URLs in their values are noted in section:- #5.3.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting '$config["css_expression"]' to '1' (default setting). Note that when '$config["css_expression"]' is set to '1', htmLawed will remove '/*' from the 'style' values.
-
- *Note*: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the 'style' attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off ('$config["schemes"] = "...style:*..."', see section:- #3.4.3, and '$config["css_expression"] = 0'). Alternately, admins can use their own custom function for finer handling of 'style' values through the 'hook_tag' parameter (see section:- #3.4.9).
-
- It is also possible to have htmLawed let through any 'style' value by setting '$config["style_pass"]' to '1'.
-
- As such, it is better to set up a CSS file with class declarations, disallow the 'style' attribute, set a '$spec' rule (see section:- #2.3) for 'class' for the 'oneof' or 'match' parameter, and ask writers to make use of the 'class' attribute.
-
-
--- 3.4.9 Hook function for tag content ----------------------------o
-
-
- It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).
-
- When '$config' parameter 'hook_tag' is set to the name of a function, htmLawed (function 'hl_tag()') will pass on the element name, and, in the case of an opening tag, the `finalized` attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like '<element_name attribute_1_name="attribute_1_value"...>' (for empty elements like 'img' and 'input', the element-closing slash '/' should also be included), etc.
-
- Any 'hook_tag' function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as 'a' in the closing '</a>' tag of the element '<a href="http://cnn.com">CNN</a>'. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like '</a>').
-
- This is a *powerful functionality* that can be exploited for various objectives: consolidate-and-convert inline 'style' attributes to 'class', convert 'embed' elements to 'object', permit only one 'caption' element in a 'table' element, disallow embedding of certain types of media, *inject HTML*, use CSSTidy:- http://csstidy.sourceforge.net to sanitize 'style' attribute values, etc.
-
- As an example, the custom hook code below can be used to force a series of specifically ordered 'id' attributes on all elements, and a specific 'param' element inside all 'object' elements:
-
- function my_tag_function($element, $attribute_array=0){
-
- // If second argument is not received, it means a closing tag is being handled
- if(is_numeric($attribute_array)){
- return "</$element>";
- }
-
- static $id = 0;
- // Remove any duplicate element
- if($element == 'param' && isset($attribute_array['allowscriptaccess'])){
- return '';
- }
-
- $new_element = '';
-
- // Force a serialized ID number
- $attribute_array['id'] = 'my_'. $id;
- ++$id;
-
- // Inject param for allowscriptaccess
- if($element == 'object'){
- $new_element = '<param id='my_'. $id; allowscriptaccess="never" />';
- ++$id;
- }
-
- $string = '';
- foreach($attribute_array as $k=>$v){
- $string .= " {$k}=\"{$v}\"";
- }
-
- static $empty_elements = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);
-
- return "<{$element}{$string}". (isset($in_array($element, $empty_elements) ? ' /' : ''). '>'. $new_element;
- }
-
- The 'hook_tag' parameter is different from the 'hook' parameter (section:- #3.7).
-
- Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
-
-
--- 3.5 Simple configuration directive for most valid XHTML -------oo
-
-
- If '$config["valid_xhtml"]' is set to '1', some relevant '$config' parameters (indicated by '~' in section:- #2.2) are auto-adjusted. This allows one to pass the '$config' argument with a simpler value. If a value for a parameter auto-set through 'valid_xhtml' is still manually provided, then that value will over-ride the auto-set value.
-
-
--- 3.6 Simple configuration directive for most `safe` HTML --------o
-
-
- `Safe` HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as 'script' and 'object', and attributes such as 'onmouseover' and 'style' are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered 'safe' depends on the nature of the web application and the trust-level accorded to its users.
-
- htmLawed allows an admin to use '$config["safe"]' to auto-adjust multiple '$config' parameters (such as 'elements' which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by '"' in section:- #2.2). Thus, one can pass the '$config' argument with a simpler value.
-
- With the value of '1', htmLawed considers 'CDATA' sections and HTML comments as plain text, and prohibits the 'applet', 'embed', 'iframe', 'object' and 'script' elements, and the 'on*' attributes like 'onclick'. ( There are '$config' parameters like 'css_expression' that are not affected by the value set for 'safe' but whose default values still contribute towards a more `safe` output.) Further, URLs with schemes (see section:- #3.4.3) are neutralized so that, e.g., 'style="moz-binding:url(http://danger)"' becomes 'style="moz-binding:url(denied:http://danger)"'.
-
- Admins, however, may still want to completely deny the 'style' attribute, e.g., with code like
-
- $processed = htmLawed($text, array('safe'=>1, 'deny_attribute'=>'style'));
-
- Permitting the 'style' attribute brings in risks of `click-jacking`, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check 'style' values. It does provide ways for the code-developer implementing htmLawed to do such checks through the '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.
-
- If a value for a parameter auto-set through 'safe' is still manually provided, then that value can over-ride the auto-set value. E.g., with '$config["safe"] = 1' and '$config["elements"] = "*+script"', 'script', but not 'applet', is allowed.
-
- A page illustrating the efficacy of htmLawed's anti-XSS abilities with 'safe' set to '1' against XSS vectors listed by RSnake:- http://ha.ckers.org/xss.html may be available here:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm.
-
-
--- 3.7 Using a hook function --------------------------------------o
-
-
- If '$config["hook"]' is not set to '0', then htmLawed will allow preliminarily processed input to be altered by a hook function named by '$config["hook"]' before starting the main work (but after handling of characters, entities, HTML comments and 'CDATA' sections -- see code for function 'htmLawed()').
-
- The hook function also allows one to alter the `finalized` values of '$config' and '$spec'.
-
- Note that the 'hook' parameter is different from the 'hook_tag' parameter (section:- #3.4.9).
-
- Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
-
-
--- 3.8 Obtaining `finalized` parameter values ---------------------o
-
-
- htmLawed can assign the `finalized` '$config' and '$spec' values to a variable named by '$config["show_setting"]'. The variable, made global by htmLawed, is set as an array with three keys: 'config', with the '$config' value, 'spec', with the '$spec' value, and 'time', with a value that is the Unix time (the output of PHP's 'microtime()' function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.
-
- The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.
-
-
--- 3.9 Retaining non-HTML tags in input with mixed markup ---------o
-
-
- htmLawed does not remove certain characters that though invalid are nevertheless discouraged in HTML documents as per the specs (see section:- #5.1). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the '<', '>' and '&' characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).
-
- To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the '<', '>' and '&' characters with some of the HTML-discouraged characters (see section:- #3.1.2). Post-htmLawed processing, the replacements are reverted.
-
- An example (mixed HTML and PHP code in input text):
-
- $text = preg_replace('`<\?php(.+?)\?>`sm', "\x83?php\\1?\x84", $text);
- $processed = htmLawed($text);
- $processed = preg_replace('`\x83\?php(.+?)\?\x84`sm', '<?php$1?>', $processed);
-
- This code will not work if '$config["clean_ms_char"]' is set to '1' (section:- #3.1), in which case one should instead deploy a hook function (section:- #3.7). (htmLawed internally uses certain control characters, code-points '1' to '7', and use of these characters as markers in the logic of hook functions may cause issues.)
-
- Admins may also be able to use '$config["and_mark"]' to deal with such mixed markup; see section:- #3.2.
-
-
-== 4 Other =======================================================oo
-
-
--- 4.1 Support -----------------------------------------------------
-
-
- A careful re-reading of this documentation will very likely answer your questions.
-
- Software updates and forum-based community-support may be found at http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at http://php.net.
-
-
--- 4.2 Known issues -----------------------------------------------o
-
-
- See section:- #2.8.
-
- Readers are advised to cross-check information given in this document.
-
-
--- 4.3 Change-log -------------------------------------------------o
-
-
- (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the 'htmLawed.php' file may be updated independently if the secondary files are revised.)
-
- `Version number - Release date. Notes`
-
- 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. '$config["hook_tag"]', if specified, now receives names of elements in closing tags.
-
- 1.1.10 - 22 October 2011. Fix for a bug in the 'tidy' functionality that caused the entire input to be replaced with a single space; new parameter, '$config["direct_list_nest"]' to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)
-
- 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of 'li' within 'dir'
-
- 1.1.9.4 - 3 July 2010. Parameter 'schemes' now accepts '!' so any URL, even a local one, can be `denied`. An issue in which a second URL value in 'style' properties was not checked was fixed.
-
- 1.1.9.3 - 17 May 2010. Checks for correct nesting of 'param'
-
- 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes
-
- 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for 'flashvars' attribute for 'embed'
-
- 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values
-
- 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice
-
- 1.1.8 - 23 April 2009. Parameter 'deny_attribute' now accepts the wild-card '*', making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting '$spec'
-
- 1.1.7 - 11-12 March 2009. Attributes globally denied through 'deny_attribute' can be allowed element-specifically through '$spec'; '$config["style_pass"]' allowing letting through any 'style' value introduced; altered logic to catch certain types of dynamic crafted CSS expressions
-
- 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions
-
- 1.1.2 - 22 January 2009. Fixed bug in parsing of 'font' attributes during tag transformation
-
- 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent
-
- 1.1 - 29 June 2008. '$config["hook_tag"]' and '$config["format"]' introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug
-
- 1.0.9 - 11 June 2008. Fixed bug in invalid HTML code-point entity check
-
- 1.0.8 - 15 May 2008. 'bordercolor' attribute for 'table', 'td' and 'tr'
-
- 1.0.7 - 1 May 2008. Support for 'wmode' attribute for 'embed'; '$config["show_setting"]' introduced; improved '$config["elements"]' evaluation
-
- 1.0.6 - 20 April 2008. '$config["and_mark"]' introduced
-
- 1.0.5 - 12 March 2008. 'style' URL schemes essentially disallowed when $config 'safe' is on; improved regex for CSS expression search
-
- 1.0.4 - 10 March 2008. Improved corrections for 'blockquote', 'form', 'map' and 'noscript'
-
- 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); a bug allowing 'td' directly inside 'table' fixed; 'safe' '$config' parameter added
-
- 1.0.2 - 13 February 2008. Improved implementation of '$config["keep_bad"]'
-
- 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions ('hl_tag()' and 'hl_prot()'); no error display with 'hl_regex()'
-
- 1.0 - 2 November 2007. First release
-
-
--- 4.4 Testing ----------------------------------------------------o
-
-
- To test htmLawed using a form interface, a demo:- htmLawedTest.php web-page is provided with the htmLawed distribution ('htmLawed.php' and 'htmLawedTest.php' should be in the same directory on the web-server). A file with test-cases:- htmLawed_TESTCASE.txt is also provided.
-
-
--- 4.5 Upgrade, & old versions ------------------------------------o
-
-
- Upgrading is as simple as replacing the previous version of 'htmLawed.php' (assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.
-
- *Important* The following upgrades may affect the functionality of a specific htmLawed as indicated by their corresponding notes:
-
- (1) From version 1.1-1.1.10 to 1.1.11, if a 'hook_tag' function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a 'hook_tag' function receives only the element name. The 'hook_tag' function therefore may have to be edited. See section:- #3.4.9.
-
- Old versions of htmLawed may be available online. E.g., for version 1.0, check http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.
-
-
--- 4.6 Comparison with 'HTMLPurifier' -----------------------------o
-
-
- The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of mid-2009):
-
- * does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)
-
- * is 15-20 times bigger (scores of files totalling more than 750 kb)
-
- * consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)
-
- * is expectedly slower
-
- * does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like 'script' illegal)
-
- * lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)
-
- * has poor documentation
-
- However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier website:- http://htmlpurifier.org for updated information.
-
-
--- 4.7 Use through application plug-ins/modules -------------------o
-
-
- Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
-
-
--- 4.8 Use in non-PHP applications --------------------------------o
-
-
- Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
-
-
--- 4.9 Donate -----------------------------------------------------o
-
-
- A donation in any currency and amount to appreciate or support this software can be sent by PayPal:- http://paypal.com to this email address: drpatnaik at yahoo dot com.
-
-
--- 4.10 Acknowledgements ------------------------------------------o
-
-
- Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Edward Yang, and many anonymous users.
-
- Thank you!
-
-
-== 5 Appendices ==================================================oo
-
-
--- 5.1 Characters discouraged in XHTML -----------------------------
-
-
- Characters represented by the following hexadecimal code-points are `not` invalid, even though some validators may issue messages stating otherwise.
-
- '7f' to '84', '86' to '9f', 'fdd0' to 'fddf', '1fffe', '1ffff', '2fffe', '2ffff', '3fffe', '3ffff', '4fffe', '4ffff', '5fffe', '5ffff', '6fffe', '6ffff', '7fffe', '7ffff', '8fffe', '8ffff', '9fffe', '9ffff', 'afffe', 'affff', 'bfffe', 'bffff', 'cfffe', 'cffff', 'dfffe', 'dffff', 'efffe', 'effff', 'ffffe', 'fffff', '10fffe' and '10ffff'
-
-
--- 5.2 Valid attribute-element combinations -----------------------o
-
-
- Valid attribute-element combinations as per W3C specs.
-
- * includes deprecated attributes (marked '^'), attributes for the non-standard 'embed' element (marked '*'), and the proprietary 'bordercolor' (marked '~')
- * only non-frameset, HTML body elements
- * 'name' for 'a' and 'map', and 'lang' are invalid in XHTML 1.1
- * 'target' is valid for 'a' in XHTML 1.1 and higher
- * 'xml:space' is only for XHTML 1.1
-
- abbr - td, th
- accept - form, input
- accept-charset - form
- accesskey - a, area, button, input, label, legend, textarea
- action - form
- align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr
- alt - applet, area, img, input
- archive - applet, object
- axis - td, th
- bgcolor - embed, table^, tr^, td^, th^
- border - table, img^, object^
- bordercolor~ - table, td, tr
- cellpadding - table
- cellspacing - table
- char - col, colgroup, tbody, td, tfoot, th, thead, tr
- charoff - col, colgroup, tbody, td, tfoot, th, thead, tr
- charset - a, script
- checked - input
- cite - blockquote, q, del, ins
- classid - object
- clear - br^
- code - applet
- codebase - object, applet
- codetype - object
- color - font
- cols - textarea
- colspan - td, th
- compact - dir, dl^, menu, ol^, ul^
- coords - area, a
- data - object
- datetime - del, ins
- declare - object
- defer - script
- dir - bdo
- disabled - button, input, optgroup, option, select, textarea
- enctype - form
- face - font
- flashvars* - embed
- for - label
- frame - table
- frameborder - iframe
- headers - td, th
- height - embed, iframe, td^, th^, img, object, applet
- href - a, area
- hreflang - a
- hspace - applet, img^, object^
- ismap - img, input
- label - option, optgroup
- language - script^
- longdesc - img, iframe
- marginheight - iframe
- marginwidth - iframe
- maxlength - input
- method - form
- model* - embed
- multiple - select
- name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param
- nohref - area
- noshade - hr^
- nowrap - td^, th^
- object - applet
- onblur - a, area, button, input, label, select, textarea
- onchange - input, select, textarea
- onfocus - a, area, button, input, label, select, textarea
- onreset - form
- onselect - input, textarea
- onsubmit - form
- pluginspage* - embed
- pluginurl* - embed
- prompt - isindex
- readonly - textarea, input
- rel - a
- rev - a
- rows - textarea
- rowspan - td, th
- rules - table
- scope - td, th
- scrolling - iframe
- selected - option
- shape - area, a
- size - hr^, font, input, select
- span - col, colgroup
- src - embed, script, input, iframe, img
- standby - object
- start - ol^
- summary - table
- tabindex - a, area, button, input, object, select, textarea
- target - a^, area, form
- type - a, embed, object, param, script, input, li^, ol^, ul^, button
- usemap - img, input, object
- valign - col, colgroup, tbody, td, tfoot, th, thead, tr
- value - input, option, param, button, li^
- valuetype - param
- vspace - applet, img^, object^
- width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^
- wmode - embed
- xml:space - pre, script, style
-
- These are allowed in all but the shown elements:
-
- class - param, script
- dir - applet, bdo, br, iframe, param, script
- id - script
- lang - applet, br, iframe, param, script
- onclick - applet, bdo, br, font, iframe, isindex, param, script
- ondblclick - applet, bdo, br, font, iframe, isindex, param, script
- onkeydown - applet, bdo, br, font, iframe, isindex, param, script
- onkeypress - applet, bdo, br, font, iframe, isindex, param, script
- onkeyup - applet, bdo, br, font, iframe, isindex, param, script
- onmousedown - applet, bdo, br, font, iframe, isindex, param, script
- onmousemove - applet, bdo, br, font, iframe, isindex, param, script
- onmouseout - applet, bdo, br, font, iframe, isindex, param, script
- onmouseover - applet, bdo, br, font, iframe, isindex, param, script
- onmouseup - applet, bdo, br, font, iframe, isindex, param, script
- style - param, script
- title - param, script
- xml:lang - applet, br, iframe, param, script
-
-
--- 5.3 CSS 2.1 properties accepting URLs ------------------------o
-
-
- background
- background-image
- content
- cue-after
- cue-before
- cursor
- list-style
- list-style-image
- play-during
-
-
--- 5.4 Microsoft Windows 1252 character replacements --------------o
-
-
- Key: 'd' double, 'l' left, 'q' quote, 'r' right, 's.' single
-
- Code-point (decimal) - hexadecimal value - replacement entity - represented character
-
- 127 - 7f - (removed) - (not used)
- 128 - 80 - &#8364; - euro
- 129 - 81 - (removed) - (not used)
- 130 - 82 - &#8218; - baseline s. q
- 131 - 83 - &#402; - florin
- 132 - 84 - &#8222; - baseline d q
- 133 - 85 - &#8230; - ellipsis
- 134 - 86 - &#8224; - dagger
- 135 - 87 - &#8225; - d dagger
- 136 - 88 - &#710; - circumflex accent
- 137 - 89 - &#8240; - permile
- 138 - 8a - &#352; - S Hacek
- 139 - 8b - &#8249; - l s. guillemet
- 140 - 8c - &#338; - OE ligature
- 141 - 8d - (removed) - (not used)
- 142 - 8e - &#381; - Z dieresis
- 143 - 8f - (removed) - (not used)
- 144 - 90 - (removed) - (not used)
- 145 - 91 - &#8216; - l s. q
- 146 - 92 - &#8217; - r s. q
- 147 - 93 - &#8220; - l d q
- 148 - 94 - &#8221; - r d q
- 149 - 95 - &#8226; - bullet
- 150 - 96 - &#8211; - en dash
- 151 - 97 - &#8212; - em dash
- 152 - 98 - &#732; - tilde accent
- 153 - 99 - &#8482; - trademark
- 154 - 9a - &#353; - s Hacek
- 155 - 9b - &#8250; - r s. guillemet
- 156 - 9c - &#339; - oe ligature
- 157 - 9d - (removed) - (not used)
- 158 - 9e - &#382; - z dieresis
- 159 - 9f - &#376; - Y dieresis
-
-
--- 5.5 URL format -------------------------------------------------o
-
-
- An `absolute` URL has a 'protocol' or 'scheme', a 'network location' or 'hostname', and, optional 'path', 'parameters', 'query' and 'fragment' segments. Thus, an absolute URL has this generic structure:
-
- (scheme) : (//network location) /(path) ;(parameters) ?(query) #(fragment)
-
- The schemes can only contain letters, digits, '+', '.' and '-'. Hostname is the portion after the '//' and up to the first '/' (if any; else, up to the end) when ':' is followed by a '//' (e.g., 'abc.com' in 'ftp://abc.com/def'); otherwise, it consists of everything after the ':' (e.g., 'def@abc.com' in mailto:def@abc.com').
-
- `Relative` URLs do not have explicit schemes and network locations; such values are inherited from a `base` URL.
-
-
--- 5.6 Brief on htmLawed code -------------------------------------o
-
-
- Much of the code's logic and reasoning can be understood from the documentation above.
-
- The *output* of htmLawed is a text string containing the processed input. There is no custom error tracking.
-
- *Function arguments* for htmLawed are:
-
- * '$in' - 1st argument; a text string; the *input text* to be processed. Any extraneous slashes added by PHP when `magic quotes` are enabled should be removed beforehand using PHP's 'stripslashes()' function.
-
- * '$config' - 2nd argument; an associative array; optional (named '$C' in htmLawed code). The array has keys with names like 'balance' and 'keep_bad', and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the *configurable parameters* (indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through '$config'. `Finalized` '$config' is thus a filtered and possibly larger array.
-
- * '$spec' - 3rd argument; a text string; optional. The string has rules, written in an htmLawed-designated format, *specifying* element-specific attribute and attribute value restrictions. Function 'hl_spec()' is used to convert the string to an associative-array for internal use. `Finalized` '$spec' is thus an array.
-
- `Finalized` '$config' and '$spec' are made *global variables* while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the `finalized` values, the 'show_settings' parameter of '$config' should be used). Depending on '$config', another global variable 'hl_Ids', to track 'id' attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.
-
- Except for the main function 'htmLawed()' and the functions 'kses()' and 'kses_hook()', htmLawed's functions are *name-spaced* using the 'hl_' prefix. The *functions* and their roles are:
-
- * 'hl_attrval' - checking attribute values against $spec
- * 'hl_bal' - tag balancing
- * 'hl_cmtcd' - handling CDATA sections and HTML comments
- * 'hl_ent' - entity handling
- * 'hl_prot' - checking a URL scheme/protocol
- * 'hl_regex' - checking syntax of a regular expression
- * 'hl_spec' - converting user-supplied $spec value to one used by htmLawed internally
- * 'hl_tag' - handling tags
- * 'hl_tag2' - transforming tags
- * 'hl_tidy' - compact/beautify HTML
- * 'hl_version' - reporting htmLawed version
- * 'htmLawed' - main function
- * 'kses' - main function of 'kses'
- * 'kses_hook' - hook function of 'kses'
-
- The last two are for compatibility with pre-existing code using the 'kses' script. htmLawed's 'kses()' basically passes on the filtering task to 'htmLawed()' function after deciphering '$config' and '$spec' from the argument values supplied to it. 'kses_hook()' is an empty function and is meant for being filled with custom code if the 'kses' script users were using one.
-
- 'htmLawed()' finalizes '$spec' (with the help of 'hl_spec()') and '$config', and globalizes them. Finalization of '$config' involves setting default values if an inappropriate or invalid one is supplied. This includes calling 'hl_regex()' to check well-formedness of regular expression patterns if such expressions are user-supplied through '$config'. 'htmLawed()' then removes invalid characters like nulls and 'x01' and appropriately handles entities using 'hl_ent()'. HTML comments and CDATA sections are identified and treated as per '$config' with the help of 'hl_cmtcd()'. When retained, the '<' and '>' characters identifying them, and the '<', '>' and '&' characters inside them, are replaced with control characters (code-points '1' to '5') till any tag balancing is completed.
-
- After this `initial processing` 'htmLawed()' identifies tags using regex and processes them with the help of 'hl_tag()' -- a large function that analyzes tag content, filtering it as per HTML standards, '$config' and '$spec'. Among other things, 'hl_tag()' transforms deprecated elements using 'hl_tag2()', removes attributes from closing tags, checks attribute values as per '$spec' rules using 'hl_attrval()', and checks URL protocols using 'hl_prot()'. 'htmLawed()' performs tag balancing and nesting checks with a call to 'hl_bal()', and optionally compacts/beautifies the output with proper white-spacing with a call to 'hl_tidy()'. The latter temporarily replaces white-space, and '<', '>' and '&' characters inside 'pre', 'script' and 'textarea' elements, and HTML comments and CDATA sections with control characters (code-points '1' to '5', and '7').
-
- htmLawed permits the use of custom code or *hook functions* at two stages. The first, called inside 'htmLawed()', allows the input text as well as the finalized $config and $spec values to be altered right after the initial processing (see section:- #3.7). The second is called by 'hl_tag()' once the tag content is finalized (see section:- #3.4.9).
-
- Being dictated by the external and stable HTML standard, htmLawed's objective is very clear-cut and less concerned with tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specs will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid in quickly grasping the logic, at least when viewed with code syntax highlighted.
-
-___________________________________________________________________oo
-
-
-@@description: htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter
-@@encoding: utf-8
-@@keywords: htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements
-@@language: en
+/*
+htmLawed_README.txt, 29 August 2013
+htmLawed 1.1.16, 29 August 2013
+Copyright Santosh Patnaik
+Dual licensed with LGPL 3 and GPL 2+
+A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
+*/
+
+
+== Content ==========================================================
+
+
+1 About htmLawed
+ 1.1 Example uses
+ 1.2 Features
+ 1.3 History
+ 1.4 License & copyright
+ 1.5 Terms used here
+2 Usage
+ 2.1 Simple
+ 2.2 Configuring htmLawed using the '$config' parameter
+ 2.3 Extra HTML specifications using the '$spec' parameter
+ 2.4 Performance time & memory usage
+ 2.5 Some security risks to keep in mind
+ 2.6 Use without modifying old 'kses()' code
+ 2.7 Tolerance for ill-written HTML
+ 2.8 Limitations & work-arounds
+ 2.9 Examples of usage
+3 Details
+ 3.1 Invalid/dangerous characters
+ 3.2 Character references/entities
+ 3.3 HTML elements
+ 3.3.1 HTML comments and 'CDATA' sections
+ 3.3.2 Tag-transformation for better XHTML-Strict
+ 3.3.3 Tag balancing and proper nesting
+ 3.3.4 Elements requiring child elements
+ 3.3.5 Beautify or compact HTML
+ 3.4 Attributes
+ 3.4.1 Auto-addition of XHTML-required attributes
+ 3.4.2 Duplicate/invalid 'id' values
+ 3.4.3 URL schemes (protocols) and scripts in attribute values
+ 3.4.4 Absolute & relative URLs
+ 3.4.5 Lower-cased, standard attribute values
+ 3.4.6 Transformation of deprecated attributes
+ 3.4.7 Anti-spam & 'href'
+ 3.4.8 Inline style properties
+ 3.4.9 Hook function for tag content
+ 3.5 Simple configuration directive for most valid XHTML
+ 3.6 Simple configuration directive for most `safe` HTML
+ 3.7 Using a hook function
+ 3.8 Obtaining `finalized` parameter values
+ 3.9 Retaining non-HTML tags in input with mixed markup
+4 Other
+ 4.1 Support
+ 4.2 Known issues
+ 4.3 Change-log
+ 4.4 Testing
+ 4.5 Upgrade, & old versions
+ 4.6 Comparison with 'HTMLPurifier'
+ 4.7 Use through application plug-ins/modules
+ 4.8 Use in non-PHP applications
+ 4.9 Donate
+ 4.10 Acknowledgements
+5 Appendices
+ 5.1 Characters discouraged in HTML
+ 5.2 Valid attribute-element combinations
+ 5.3 CSS 2.1 properties accepting URLs
+ 5.4 Microsoft Windows 1252 character replacements
+ 5.5 URL format
+ 5.6 Brief on htmLawed code
+
+
+== 1 About htmLawed ================================================
+
+
+ htmLawed is a PHP script to process text with HTML markup to make it more compliant with HTML standards and administrative policies. It works by making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting (XSS) attacks, allowing only specified HTML tags and attributes, and so on. Such `lawing in` of HTML in text used in (X)HTML or XML documents ensures that it is in accordance with the aesthetics, safety and usability requirements set by administrators.
+
+ htmLawed is highly customizable, and fast with low memory usage. Its free and open-source code is in one small file, does not require extensions or libraries, and works in older versions of PHP as well. It is a good alternative to the HTML Tidy:- http://tidy.sourceforge.net application.
+
+
+-- 1.1 Example uses ------------------------------------------------
+
+
+ * Filtering of text submitted as comments on blogs to allow only certain HTML elements
+
+ * Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant
+
+ * Text processing for stricter XML standard-compliance: e.g., to have lowercased 'x' in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as 'application/xml'
+
+ * Scraping text or data from web-pages
+
+ * Pretty-printing HTML code
+
+
+-- 1.2 Features ---------------------------------------------------o
+
+
+ Key: '*' security feature, '^' standard compliance, '~' requires setting right options, '`' different from 'Kses'
+
+ * make input more *secure* and *standard-compliant*
+ * use for HTML 4, XHTML 1.0 or 1.1, or even generic *XML* documents ^~`
+
+ * *beautify* or *compact* HTML ^~`
+
+ * can *restrict elements* ^~`
+ * ensures proper closure of empty elements like 'img' ^`
+ * *transform deprecated elements* like 'u' ^~`
+ * HTML *comments* and 'CDATA' sections can be permitted ^~`
+ * elements like 'script', 'object' and 'form' can be permitted ~
+
+ * *restrict attributes*, including *element-specifically* ^~`
+ * remove *invalid attributes* ^`
+ * element and attribute names are *lower-cased* ^
+ * provide *required attributes*, like 'alt' for 'image' ^`
+ * *transforms deprecated attributes* ^~`
+ * attributes *declared only once* ^`
+
+ * *restrict attribute values*, including *element-specifically* ^~`
+ * a value is declared for `empty` (`minimized`) attributes like 'checked' ^
+ * check for potentially dangerous attribute values *~
+ * ensure *unique* 'id' attribute values ^~`
+ * *double-quote* attribute values ^
+ * lower-case *standard attribute values* like 'password' ^`
+ * permit custom, non-standard attributes as well as custom rules for standard attributes ~`
+
+ * *attribute-specific URL protocol/scheme restriction* *~`
+ * disable *dynamic expressions* in 'style' values *~`
+
+ * neutralize invalid named character entities ^`
+ * *convert* hexadecimal numeric entities to decimal ones, or vice versa ^~`
+ * convert named entities to numeric ones for generic XML use ^~`
+
+ * remove *null* characters *
+ * neutralize potentially dangerous proprietary Netscape *Javascript entities* *
+ * replace potentially dangerous *soft-hyphen* character in URL-accepting attribute values with spaces *
+
+ * remove common *invalid characters* not allowed in HTML or XML ^`
+ * replace *characters from Microsoft applications* like 'Word' that are discouraged in HTML or XML ^~`
+ * neutralize entities for characters invalid or discouraged in HTML or XML ^`
+ * appropriately neutralize '<', '&', '"', and '>' characters ^*`
+
+ * understands improperly spaced tag content (like, spread over more than a line) and properly spaces them `
+ * attempts to *balance tags* for well-formedness ^~`
+ * understands when *omitable closing tags* like '</p>' (allowed in HTML 4, transitional, e.g.) are missing ^~`
+ * attempts to permit only *validly nested tags* ^~`
+ * option to *remove or neutralize bad content* ^~`
+ * attempts to *rectify common errors of plain-text misplacement* (e.g., directly inside 'blockquote') ^~`
+
+ * fast, *non-OOP* code of ~45 kb incurring peak basal memory usage of ~0.5 MB
+ * *compatible* with pre-existing code using 'Kses' (the filter used by 'WordPress')
+
+ * optional *anti-spam* measures such as addition of 'rel="nofollow"' and link-disabling ~`
+ * optionally makes *relative URLs absolute*, and vice versa ~`
+
+ * optionally mark '&' to identify the entities for '&', '<' and '>' introduced by htmLawed ~`
+
+ * allows deployment of powerful *hook functions* to *inject* HTML, *consolidate* 'style' attributes to 'class', finely check attribute values, etc. ~`
+
+ * *independent of character encoding* of input and does not affect it
+
+ * *tolerance for ill-written HTML* to a certain degree
+
+
+-- 1.3 History ----------------------------------------------------o
+
+
+ htmLawed was created in 2007 for use with 'LabWiki', a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like 'Kses' and 'HTMLPurifier' were deemed inadequate, slow, resource-intensive, or dependent on an extension or external application like 'HTML Tidy'. The core logic of htmLawed, that of identifying HTML elements and attributes, was based on the 'Kses' (version 0.2.2) HTML filter software of Ulf Harnhammar (it can still be used with code that uses 'Kses'; see section:- #2.6.).
+
+ See section:- #4.3 for a detailed log of changes in htmLawed over the years, and section:- #4.10 for acknowledgements.
+
+
+-- 1.4 License & copyright ----------------------------------------o
+
+
+ htmLawed is free and open-source software dual copyrighted by Santosh Patnaik, MD, PhD, and licensed under LGPL license version 3:- http://www.gnu.org/licenses/lgpl-3.0.txt, and GPL license version 2:- http://www.gnu.org/licenses/gpl-2.0.txt (or later).
+
+
+-- 1.5 Terms used here --------------------------------------------o
+
+
+ In this document, only HTML body-level elements are considered. htmLawed does not have support for head-level elements, 'body', and the frame-level elements, 'frameset', 'frame' and 'noframes', and these elements are ignored here.
+
+ * `administrator` - or admin; person setting up the code that utilizes htmLawed; also, `user`
+ * `attributes` - name-value pairs like 'href="http://x.com"' in opening tags
+ * `author` - see `writer`
+ * `character` - atomic unit of text; internally represented by a numeric `code-point` as specified by the `encoding` or `charset` in use
+ * `entity` - markup like '&gt;' and '&#160;' used to refer to a character
+ * `element` - HTML element like 'a' and 'img'
+ * `element content` - content between the opening and closing tags of an element, like 'click' of the '<a href="x">click</a>' element
+ * `HTML` - implies XHTML unless specified otherwise
+ * `HTML body` - Complete HTML documents typically have a `head` and a `body` container. Information in `head` specifies title of the document, etc., whereas that in the body informs what is to be displayed on a web-page; it is only the elements for `body`, except 'frames', 'frameset' and 'noframes' that htmLawed is concerned with
+ * `input` - text given to htmLawed to process
+ * `processing` - involves filtering, correction, etc., of input
+ * `safe` - absence or reduction of certain characters and HTML elements and attributes in HTML of text that can otherwise potentially, and circumstantially, expose text readers to security vulnerabilities like cross-site scripting attacks (XSS)
+ * `scheme` - a URL protocol like 'http' and 'ftp'
+ * `specifications` - standard specifications, for HTML4, HTML5, Ruby, etc.
+ * `style property` - terms like 'border' and 'height' for which declarations are made in values for the 'style' attribute of elements
+ * `tag` - markers like '<a href="x">' and '</a>' delineating element content; the opening tag can contain attributes
+ * `tag content` - consists of tag markers '<' and '>', element names like 'div', and possibly attributes
+ * `user` - administrator
+ * `writer` - end-user like a blog commenter providing the input that is to be processed; also, `author`
+
+
+-- 1.6 Availability ------------------------------------------------o
+
+
+ htmLawed can be downloaded for free at its website:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed. Besides the 'htmLawed.php' file, the download has the htmLawed documentation (this document) in plain text:- htmLawed_README.txt and HTML:- htmLawed_README.htm formats, a script for testing:- htmLawedTest.php, and a text file for test-cases:- htmLawed_TESTCASE.txt. htmLawed is also available as a PHP class (OOP code) on its website.
+
+
+== 2 Usage ========================================================oo
+
+
+ htmLawed works in PHP version 4.4 or higher. Either 'include()' the 'htmLawed.php' file, or copy-paste the entire code. To use with PHP 4.3, have the following code included:
+
+ if(!function_exists('ctype_digit')){
+ function ctype_digit($var){
+ return ((int) $var == $var);
+ }
+ }
+
+
+-- 2.1 Simple ------------------------------------------------------
+
+
+ The input text to be processed, '$text', is passed as an argument of type string; 'htmLawed()' returns the processed string:
+
+ $processed = htmLawed($text);
+
+ With the 'htmLawed class' (section:- #1.6), usage is:
+
+ $processed = htmLawed::hl($text);
+
+ *Notes*: (1) If input is from a '$_GET' or '$_POST' value, and 'magic quotes' are enabled on the PHP setup, run 'stripslashes()' on the input before passing to htmLawed. (2) htmLawed does not have support for head-level elements, 'body', and the frame-level elements, 'frameset', 'frame' and 'noframes'.
+
+ By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow 'CDATA' sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- '$config' and '$spec':
+
+ $processed = htmLawed($text, $config, $spec);
+
+ The '$config' and '$spec' arguments are detailed below. Some examples are shown in section:- #2.9. For maximum protection against 'XSS' and other scripting attacks (e.g., by disallowing Javascript code), consider using the 'safe' parameter; see section:- #3.6.
+
+
+-- 2.2 Configuring htmLawed using the '$config' parameter ---------o
+
+
+ '$config' instructs htmLawed on how to tackle certain tasks. When '$config' is not specified, or not set as an array (e.g., '$config = 1'), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in '$config' as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.
+
+ $config = array('comment'=>0, 'cdata'=>1);
+ $processed = htmLawed($text, $config);
+
+ Or,
+
+ $processed = htmLawed($text, array('comment'=>0, 'cdata'=>1));
+
+ Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).
+
+ Key: '*' default, '^' different default when htmLawed is used in the Kses-compatible mode (see section:- #2.6), '~' different default when 'valid_xhtml' is set to '1' (see section:- #3.5), '"' different default when 'safe' is set to '1' (see section:- #3.6)
+
+ *abs_url*
+ Make URLs absolute or relative; '$config["base_url"]' needs to be set; see section:- #3.4.4
+
+ '-1' - make relative
+ '0' - no action *
+ '1' - make absolute
+
+ *and_mark*
+ Mark '&' characters in the original input; see section:- #3.2
+
+ *anti_link_spam*
+ Anti-link-spam measure; see section:- #3.4.7
+
+ '0' - no measure taken *
+ `array("regex1", "regex2")` - will ensure a 'rel' attribute with 'nofollow' in its value in case the 'href' attribute value matches the regular expression pattern 'regex1', and/or will remove 'href' if its value matches the regular expression pattern 'regex2'. E.g., 'array("/./", "/://\W*(?!(abc\.com|xyz\.org))/")'; see section:- #3.4.7 for more.
+
+ *anti_mail_spam*
+ Anti-mail-spam measure; see section:- #3.4.7
+
+ '0' - no measure taken *
+ `word` - '@' in mail address in 'href' attribute value is replaced with specified `word`
+
+ *balance*
+ Balance tags for well-formedness and proper nesting; see section:- #3.3.3
+
+ '0' - no
+ '1' - yes *
+
+ *base_url*
+ Base URL value that needs to be set if '$config["abs_url"]' is not '0'; see section:- #3.4.4
+
+ *cdata*
+ Handling of 'CDATA' sections; see section:- #3.3.1
+
+ '0' - don't consider 'CDATA' sections as markup and proceed as if plain text ^"
+ '1' - remove
+ '2' - allow, but neutralize any '<', '>', and '&' inside by converting them to named entities
+ '3' - allow *
+
+ *clean_ms_char*
+ Replace discouraged characters introduced by Microsoft Word, etc.; see section:- #3.1
+
+ '0' - no *
+ '1' - yes
+ '2' - yes, but replace special single & double quotes with ordinary ones
+
+ *comment*
+ Handling of HTML comments; see section:- #3.3.1
+
+ '0' - don't consider comments as markup and proceed as if plain text ^"
+ '1' - remove
+ '2' - allow, but neutralize any '<', '>', and '&' inside by converting to named entities
+ '3' - allow *
+
+ *css_expression*
+ Allow dynamic CSS expression by not removing the expression from CSS property values in 'style' attributes; see section:- #3.4.8
+
+ '0' - remove *
+ '1' - allow
+
+ *deny_attribute*
+ Denied HTML attributes; see section:- #3.4
+
+ '0' - none *
+ `string` - dictated by values in `string`
+ 'on*' (like 'onfocus') attributes not allowed - "
+
+ *direct_nest_list*
+ Allow direct nesting of a list within another without requiring it to be a list item; see section:- #3.3.4
+
+ '0' - no *
+ '1' - yes
+
+ *elements*
+ Allowed HTML elements; see section:- #3.3
+
+ '* -center -dir -font -isindex -menu -s -strike -u' - ~
+ 'applet, embed, iframe, object, script' not allowed - "
+
+ *hexdec_entity*
+ Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see section:- #3.2
+
+ '0' - no
+ '1' - yes *
+ '2' - convert decimal to hexadecimal ones
+
+ *hook*
+ Name of an optional hook function to alter the input string, '$config' or '$spec' before htmLawed starts its main work; see section:- #3.7
+
+ '0' - no hook function *
+ `name` - `name` is name of the hook function ('kses_hook' ^)
+
+ *hook_tag*
+ Name of an optional hook function to alter tag content finalized by htmLawed; see section:- #3.4.9
+
+ '0' - no hook function *
+ `name` - `name` is name of the hook function
+
+ *keep_bad*
+ Neutralize bad tags by converting '<' and '>' to entities, or remove them; see section:- #3.3.3
+
+ '0' - remove ^
+ '1' - neutralize both tags and element content
+ '2' - remove tags but neutralize element content
+ '3' and '4' - like '1' and '2' but remove if text ('pcdata') is invalid in parent element
+ '5' and '6' * - like '3' and '4' but line-breaks, tabs and spaces are left
+
+ *lc_std_val*
+ For XHTML compliance, predefined, standard attribute values, like 'get' for the 'method' attribute of 'form', must be lowercased; see section:- #3.4.5
+
+ '0' - no
+ '1' - yes *
+
+ *make_tag_strict*
+ Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: 'applet' 'center' 'dir' 'embed' 'font' 'isindex' 'menu' 's' 'strike' 'u'; see section:- #3.3.2
+
+ '0' - no ^
+ '1' - yes, but leave 'applet', 'embed' and 'isindex' elements that currently can't be transformed *
+ '2' - yes, removing 'applet', 'embed' and 'isindex' elements and their contents (nested elements remain) ~
+
+ *named_entity*
+ Allow non-universal named HTML entities, or convert to numeric ones; see section:- #3.2
+
+ '0' - convert
+ '1' - allow *
+
+ *no_deprecated_attr*
+ Allow deprecated attributes or transform them; see section:- #3.4.6
+
+ '0' - allow ^
+ '1' - transform, but 'name' attributes for 'a' and 'map' are retained *
+ '2' - transform
+
+ *parent*
+ Name of the parent element, possibly imagined, that will hold the input; see section:- #3.3
+
+ *safe*
+ Magic parameter to make input the most secure against XSS without needing to specify other relevant '$config' parameters; see section:- #3.6
+
+ '0' - no *
+ '1' - will auto-adjust other relevant '$config' parameters (indicated by '"' in this list)
+
+ *schemes*
+ Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or '!' to `deny` any URL); '*' covers all unspecified attributes; see section:- #3.4.3
+
+ 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https' *
+ '*: ftp, gopher, http, https, mailto, news, nntp, telnet' ^
+ 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: !; *:file, http, https' "
+
+ *show_setting*
+ Name of a PHP variable to assign the `finalized` '$config' and '$spec' values; see section:- #3.8
+
+ *style_pass*
+ Do not look at 'style' attribute values, letting them through without any alteration
+
+ '0' - no *
+ '1' - htmLawed will let through any 'style' value; see section:- #3.4.8
+
+ *tidy*
+ Beautify or compact HTML code; see section:- #3.3.5
+
+ '-1' - compact
+ '0' - no *
+ '1' or 'string' - beautify (custom format specified by 'string')
+
+ *unique_ids*
+ 'id' attribute value checks; see section:- #3.4.2
+
+ '0' - no ^
+ '1' - remove duplicate and/or invalid ones *
+ `word` - remove invalid ones and replace duplicate ones with new and unique ones based on the `word`; the admin-specified `word`, like 'my_', should begin with a letter (a-z) and can contain letters, digits, '.', '_', '-', and ':'.
+
+ *valid_xhtml*
+ Magic parameter to make input the most valid XHTML without needing to specify other relevant '$config' parameters; see section:- #3.5
+
+ '0' - no *
+ '1' - will auto-adjust other relevant '$config' parameters (indicated by '~' in this list)
+
+ *xml:lang*
+ Auto-adding 'xml:lang' attribute; see section:- #3.4.1
+
+ '0' - no *
+ '1' - add if 'lang' attribute is present
+ '2' - add if 'lang' attribute is present, and remove 'lang' ~
+
+
+-- 2.3 Extra HTML specifications using the $spec parameter --------o
+
+
+ The '$spec' argument of htmLawed can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policies. '$spec' is specified as a string of text containing one or more `rules`, with multiple rules separated from each other by a semi-colon (';'). E.g.,
+
+ $spec = 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt';
+ $processed = htmLawed($text, $config, $spec);
+
+ Or,
+
+ $processed = htmLawed($text, $config, 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt');
+
+ A rule begins with an HTML *element* name(s) (`rule-element`), for which the rule applies, followed by an equal ('=') sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., 'th,td,tr='.
+
+ Rest of the rule consists of comma-separated HTML *attribute names*. A minus ('-') character before an attribute means that the attribute is not permitted inside the rule-element. E.g., '-width'. To deny all attributes, '-*' can be used.
+
+ Following shows examples of rule excerpts with rule-element 'a' and the attributes that are being permitted:
+
+ * 'a=' - all
+ * 'a=id' - all
+ * 'a=href, title, -id, -onclick' - all except 'id' and 'onclick'
+ * 'a=*, id, -id' - all except 'id'
+ * 'a=-*' - none
+ * 'a=-*, href, title' - none except 'href' and 'title'
+ * 'a=-*, -id, href, title' - none except 'href' and 'title'
+
+ Rules regarding *attribute values* are optionally specified inside round brackets after attribute names in slash ('/')-separated `parameter = value` pairs. E.g., 'title(maxlen=30/minlen=5)'. None or one or more of the following parameters may be specified:
+
+ * 'oneof' - one or more choices separated by '|' that the value should match; if only one choice is provided, then the value must match that choice
+
+ * 'noneof' - one or more choices separated by '|' that the value should not match
+
+ * 'maxlen' and 'minlen' - upper and lower limits for the number of characters in the attribute value; specified in numbers
+
+ * 'maxval' and 'minval' - upper and lower limits for the numerical value specified in the attribute value; specified in numbers
+
+ * 'match' and 'nomatch' - pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers
+
+ * 'default' - a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters
+
+ If 'default' is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The 'default' value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., 'maxlen' to '-1').
+
+ Examples with `input` '<input title="WIDTH" value="10em" /><input title="length" value="5" />' are shown below.
+
+ `Rule`: 'input=title(maxlen=60/minlen=6), value'
+ `Output`: '<input value="10em" /><input title="length" value="5" />'
+
+ `Rule`: 'input=title(), value(maxval=8/default=6)'
+ `Output`: '<input title="WIDTH" value="6" /><input title="length" value="5" />'
+
+ `Rule`: 'input=title(nomatch=%w.d%i), value(match=%em%/default=6em)'
+ `Output`: '<input value="10em" /><input title="length" value="6em" />'
+
+ `Rule`: 'input=title(oneof=height|depth/default=depth), value(noneof=5|6)'
+ `Output`: '<input title="depth" value="10em" /><input title="depth" />'
+
+ *Special characters*: The characters ';', ',', '/', '(', ')', '|', '~' and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be `escaped` by enclosing in pairs of double-quotes ('"'). A back-tick ('`') can be used to escape a literal '"'. An example rule illustrating this is 'input=value(maxlen=30/match="/^\w/"/default="your `"ID`"")'.
+
+ *Note*: To deny an attribute for all elements for which it is legal, '$config["deny_attribute"]' (see section:- #3.4) can be used instead of '$spec'. Also, attributes can be allowed element-specifically through '$spec' while being denied globally through '$config["deny_attribute"]'. The 'hook_tag' parameter (section:- #3.4.9) can also be possibly used to implement a functionality like that achieved using '$spec' functionality.
+
+ '$spec' can also be used to permit custom, non-standard attributes as well as custom rules for standard attributes. Thus, the following value of '$spec' will permit the custom uses of the standard 'rel' attribute in 'input' (not permitted as per standards) and of a non-standard attribute, 'vFlag', in 'img'.
+
+ $spec = 'img=vFlag; input=rel'
+
+ The attribute names can contain alphabets, colons (:) and hyphens (-), but they must start with an alphabet.
+
+
+-- 2.4 Performance time & memory usage ----------------------------o
+
+
+ The time and memory consumed during text processing by htmLawed depends on its configuration, the size of the input, and the amount, nestedness and well-formedness of the HTML markup within the input. In particular, tag balancing and beautification each can increase the processing time by about a quarter.
+
+ The htmLawed demo:- htmLawedTest.php can be used to evaluate the performance and effects of different types of input and '$config'.
+
+
+-- 2.5 Some security risks to keep in mind ------------------------o
+
+
+ When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially `dangerous` HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc. Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permitted through htmLawed's setting should be considered. For example, following increase security risks:
+
+ * Allowing 'script', 'applet', 'embed', 'iframe' or 'object' elements, or certain of their attributes like 'allowscriptaccess'
+
+ * Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., '<!--[if gte IE 4]><script>alert("xss");</script><![endif]-->'
+
+ * Allowing dynamic CSS expressions (some Internet Explorer versions are vulnerable)
+
+ * Allowing the 'style' attribute
+
+ To remove `unsecure` HTML, code-developers using htmLawed must set '$config' appropriately. E.g., '$config["elements"] = "* -script"' to deny the 'script' element (section:- #3.3), '$config["safe"] = 1' to auto-configure ceratin htmLawed parameters for maximizing security (section:- #3.6), etc.
+
+ Permitting the '*style*' attribute brings in risks of `click-jacking`, `phishing`, web-page overlays, etc., `even` when the 'safe' parameter is enabled (see section:- #3.6). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing 'style' completely and relying on CSS classes and stylesheet files is recommended.
+
+ htmLawed does not check or correct the character *encoding* of the input it receives. In conjunction with permissive circumstances, such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can allow for an exploit (like Google's `UTF-7/XSS` vulnerability of the past).
+
+
+-- 2.6 Use without modifying old 'kses()' code --------------------o
+
+
+ The 'Kses' PHP script is used by many applications (like 'WordPress'). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the 'kses()' function declared in the 'Kses' file (usually named 'kses.php'). E.g., application code like this will continue to work after replacing 'Kses' with htmLawed:
+
+ $comment_filtered = kses($comment_input, array('a'=>array(), 'b'=>array(), 'i'=>array()));
+
+ For some of the '$config' parameters, htmLawed will use values other than the default ones. These are indicated by '^' in section:- #2.2. To force htmLawed to use other values, function 'kses()' in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.
+
+ If the application uses a 'Kses' file that has the 'kses()' function declared, then, to have the application use htmLawed instead of 'Kses', simply rename 'htmLawed.php' (to 'kses.php', e.g.) and replace the 'Kses' file (or just replace the code in the 'Kses' file with the htmLawed code). If the 'kses()' function in the 'Kses' file had been renamed by the application developer (e.g., in 'WordPress', it is named 'wp_kses()'), then appropriately rename the 'kses()' function in the htmLawed code.
+
+ If the 'Kses' file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with 'WordPress', it is best to copy the htmLawed code to 'wp_includes/kses.php', rename the newly added function 'kses()' to 'wp_kses()', and delete the code for the original 'wp_kses()' function.
+
+ If the 'Kses' code has a non-empty hook function (e.g., 'wp_kses_hook()' in case of 'WordPress'), then the code for htmLawed's 'kses_hook()' function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With 'WordPress', the hook function is an essential one. The following code is suggested for the htmLawed 'kses_hook()' in case of 'WordPress':
+
+ function kses_hook($string, &$cf, &$spec){
+ // kses compatibility
+ $allowed_html = $spec;
+ $allowed_protocols = array();
+ foreach($cf['schemes'] as $v){
+ foreach($v as $k2=>$v2){
+ if(!in_array($k2, $allowed_protocols)){
+ $allowed_protocols[] = $k2;
+ }
+ }
+ }
+ return wp_kses_hook($string, $allowed_html, $allowed_protocols);
+ // eof
+ }
+
+
+-- 2.7 Tolerance for ill-written HTML -----------------------------o
+
+
+ htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be `read` as HTML, and may therefore get identified as mere plain text. Following statements indicate the degree of `looseness` that htmLawed can work with, and can be provided in instructions to writers:
+
+ * Tags must be flanked by '<' and '>' with no '>' inside -- any needed '>' should be put in as '&gt;'. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and '>', like '<div >' and '<img / >', but not after the '<'.
+
+ * Element and attribute names need not be lower-cased.
+
+ * Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.
+
+ * Attribute values may be single- and not double-quoted.
+
+ * Left-padding of numeric entities (like, '&#0160;', '&x07ff;') with '0' is okay as long as the number of characters between between the '&' and the ';' does not exceed 8. All entities must end with ';' though.
+
+ * Named character entities must be properly cased. Thus, '&Lt;' or '&TILDE;' will not be recognized as entities and will be `neutralized`.
+
+ * HTML comments should not be inside element tags (they can be between tags), and should begin with '<!--' and end with '-->'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any '-->' inside should be put in as '--&gt;'. Any '--' inside will be automatically converted to '-', and a space will be added before the comment delimiter '-->'.
+
+ * 'CDATA' sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with '<[CDATA[' and end with ']]>'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any ']]>' inside should be put in as ']]&gt;'.
+
+ * For attribute values, character entities '&lt;', '&gt;' and '&amp;' should be used instead of characters '<' and '>', and '&' (when '&' is not part of a character entity). This applies even for Javascript code in values of attributes like 'onclick'.
+
+ * Characters '<', '>', '&' and '"' that are part of actual Javascript, etc., code in 'script' elements should be used as such and not be put in as entities like '&gt;'. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside 'CDATA' sections.
+
+ * Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on '$config["keep_bad"]', some code/text may be lost.
+
+ * Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.
+
+ * With '$config["unique_ids"]' not '0' and the 'id' attribute being permitted, writers should carefully avoid using duplicate or invalid 'id' values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when '<a id="home"></a><input id="home" /><label for="home"></label>' is processed into
+'<a id="home"></a><input id="prefix_home" /><label for="home"></label>'.
+
+ * Even if intended HTML is lost from an ill-written input, the processed output will be more secure and standard-compliant.
+
+ * For URLs, unless '$config["scheme"]' is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., 'htt&#112;' (which many browsers will read as the harmless 'http') may be considered bad by htmLawed.
+
+ * htmLawed will attempt to put plain text present directly inside 'blockquote', 'form', 'map' and 'noscript' elements (illegal as per the specifications) inside auto-generated 'div' elements.
+
+
+-- 2.8 Limitations & work-arounds ---------------------------------o
+
+
+ htmLawed's main objective is to make the input text `more` standard-compliant, secure for readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.
+
+ It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specifications (like asking for normalization of white-spacing within 'textarea' elements) are clearly wrong. Regarding security, note that `unsafe` HTML code is not legally invalid per se.
+
+ * htmLawed is meant for input that goes into the 'body' of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements 'frameset', 'frame' and 'noframes'. Content of the latter elements can, however, be individually filtered through htmLawed.
+
+ * It cannot transform the non-standard 'embed' elements to the standard-compliant 'object' elements. Yet, it can allow 'embed' elements if permitted ('embed' is widely used and supported). Admins can certainly use the 'hook_tag' parameter (section:- #3.4.9) to deploy a custom embed-to-object converter function.
+
+ * The only non-standard element that may be permitted is 'embed'; others like 'noembed' and 'nobr' cannot be permitted without modifying the htmLawed code.
+
+ * It cannot handle input that has non-HTML code like 'SVG' and 'MathML'. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in section:- #3.9. A third way may be to some how take advantage of the '$config["and_mark"]' parameter (see section:- #3.2).
+
+ * By default, htmLawed won't check many attribute values for standard compliance. E.g., 'width="20m"' with the dimension in non-standard 'm' is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the 'hook_tag' parameter (section:- #3.4.9) or '$spec' to enforce finer checks.
+
+ * The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specifications. Only a few of the proprietary attributes are supported.
+
+ * Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the 'hook_tag' parameter (section:- #3.4.9) or '$spec' for finer checks. Perhaps the best option is to disallow 'style' but allow 'class' attributes with the right 'oneof' or 'match' values for 'class', and have the various class style properties in '.css' CSS stylesheet files.
+
+ * htmLawed does not parse emoticons, decode `BBcode`, or `wikify`, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to 'br' elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.
+
+ * htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate 'target' and 'onclick' attributes to 'a'). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * Nesting-based checks are not possible. E.g., one cannot disallow 'p' elements specifically inside 'td' while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert 'http' to 'https'. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * Pairs of opening and closing tags that do not enclose any content (like '<em></em>') are not removed. This may be against the standard specifications for certain elements (e.g., 'table'). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.
+
+ * htmLawed does not check for certain element orderings described in the standard specifications (e.g., in a 'table', 'tbody' is allowed before 'tfoot'). Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * htmLawed does not check the number of nested elements. E.g., it will allow two 'caption' elements in a 'table' element, illegal as per the specifications. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers ('/*') in 'style' attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like '&#101;xpression...'. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the 'hook_tag' parameter (section:- #3.4.9) to more specifically identify CSS expressions in the 'style' attribute values. Also, using '$config["style_pass"]', it is possible to have htmLawed pass 'style' attribute values without even looking at them (section:- #3.4.8).
+
+ * htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., '<a href="http://x%22+style=%22background-image:xss">x</a>'). These arise when browsers mis-identify markup in `escaped` text, defeating the very purpose of escaping text (a bad browser will read the given example as '<a href="http://x" style="background-image:xss">x</a>').
+
+ * Because of poor Unicode support in PHP, htmLawed does not remove the `high value` HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see section:- #3.1).
+
+ * htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can permit an exploit (like Google's `UTF-7/XSS` vulnerability of the past). Also, htmLawed can mangle input text if it is not well-formed in terms of character encoding. Administrators can consider using code available elsewhere to check well-formedness of input text characters to correct any defect.
+
+ * htmLawed is expected to work with input texts in ASCII-compatible single byte encodings such as national variants of ASCII (like ISO-646-DE/German of the ISO 646 standard), extended ASCII variants (like ISO 8859-10/Turkish of the ISO 8859/ISO Latin standard), ISO 8859-based Windows variants (like Windows 1252), EBCDIC, Shift JIS (Japanese), GB-Roman (Chinese), and KS-Roman (Korean). It should also properly handle texts with variable byte encodings like UTF-7 (Unicode) and UTF-8 (Unicode). However, htmLawed may mangle input texts with double byte encodings like UTF-16 (Unicode), JIS X 0208:1997 (Japanese) and K SX 1001:1992 (Korean), or the UTF-32 (Unicode) quadruple byte encoding. If an input text has such an encoding, administrators can use PHP's iconv:- http://php.net/manual/en/book.iconv.php functions, or some other mean, to convert text to UTF-8 before passing it to htmLawed.
+
+ * Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.
+
+
+-- 2.9 Examples of usage -------------------------------------------o
+
+
+ Safest, allowing only `safe` HTML markup --
+
+ $config = array('safe'=>1);
+ $out = htmLawed($in);
+
+ Simplest, allowing all valid HTML markup except 'javascript:' --
+
+ $out = htmLawed($in);
+
+ Allowing all valid HTML markup including 'javascript:' --
+
+ $config = array('schemes'=>'*:*');
+ $out = htmLawed($in, $config);
+
+ Allowing only 'safe' HTML and the elements 'a', 'em', and 'strong' --
+
+ $config = array('safe'=>1, 'elements'=>'a, em, strong');
+ $out = htmLawed($in, $config);
+
+ Not allowing elements 'script' and 'object' --
+
+ $config = array('elements'=>'* -script -object');
+ $out = htmLawed($in, $config);
+
+ Not allowing attributes 'id' and 'style' --
+
+ $config = array('deny_attribute'=>'id, style');
+ $out = htmLawed($in, $config);
+
+ Permitting only attributes 'title' and 'href' --
+
+ $config = array('deny_attribute'=>'* -title -href');
+ $out = htmLawed($in, $config);
+
+ Remove bad/disallowed tags altogether instead of converting them to entities --
+
+ $config = array('keep_bad'=>0);
+ $out = htmLawed($in, $config);
+
+ Allowing attribute 'title' only in 'a' and not allowing attributes 'id', 'style', or scriptable `on*` attributes like 'onclick' --
+
+ $config = array('deny_attribute'=>'title, id, style, on*');
+ $spec = 'a=title';
+ $out = htmLawed($in, $config, $spec);
+
+ Allowing a custom attribute, 'vFlag', in 'img' and permitting custom use of the standard attribute, 'rel', in 'input' --
+
+ $spec = 'img=vFlag; input=rel';
+ $out = htmLawed($in, $config, $spec);
+
+ Some case-studies are presented below.
+
+ *1.* A blog administrator wants to allow only 'a', 'em', 'strike', 'strong' and 'u' in comments, but needs 'strike' and 'u' transformed to 'span' for better XHTML 1-strict compliance, and, he wants the 'a' links to point only to 'http' or 'https' resources:
+
+ $processed = htmLawed($in, array('elements'=>'a, em, strike, strong, u', 'make_tag_strict'=>1, 'safe'=>1, 'schemes'=>'*:http, https'), 'a=href');
+
+ *2.* An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional `bad` characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:
+
+ $processed = htmLawed($in, array('css_expression'=>1, 'keep_bad'=>1, 'make_tag_strict'=>1, 'schemes'=>'*:*', 'valid_xhtml'=>1));
+
+ For the final submission process, 'keep_bad' is set to '6'. A value of '1' for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.
+
+ *3.* A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:
+
+ $processed = htmLawed($in, array('elements'=>'tr, td', 'tidy'=>-1), 'tr, td =');
+
+
+== 3 Details =====================================================oo
+
+
+-- 3.1 Invalid/dangerous characters --------------------------------
+
+
+ Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, '9', 'a', 'd', '20' to 'd7ff', and 'e000' to '10ffff', except 'fffe' and 'ffff' (decimally, '9', '10', '13', '32' to '55295', and '57344' to '1114111', except '65534' and '65535'). htmLawed removes the invalid characters '0' to '8', 'b', 'c', and 'e' to '1f'.
+
+ Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.
+
+ Characters that are discouraged (see section:- #5.1) but not invalid are not removed by htmLawed.
+
+ It (function 'hl_tag()') also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, 'ad', or decimally, '173') in attribute values with spaces. Where required, the characters '<', '>', '&', and '"' are converted to entities.
+
+ With '$config["clean_ms_char"]' set as '1' or '2', many of the discouraged characters (decimal code-points '127' to '159' except '133') that many Microsoft applications incorrectly use (as per the 'Windows 1252' ['Cp-1252'] or a similar encoding system), and the character for decimal code-point '133', are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in section:- #5.4. This can help avoid some display issues arising from copying-pasting of content.
+
+ With '$config["clean_ms_char"]' set as '2', characters for the hexadecimal code-points '82', '91', and '92' (for special single-quotes), and '84', '93', and '94' (for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.
+
+ The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.
+
+ The '$config["clean_ms_char"]' parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the 'Windows 1252' ('Cp-1252') or a similar encoding like 'Cp-1251' (otherwise, for example when UTF-8 encoding is in use, Japanese or Korean characters can get mangled). Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.
+
+
+-- 3.2 Character references/entities ------------------------------o
+
+
+ Valid character entities take the form '&*;' where '*' is '#x' followed by a hexadecimal number (hexadecimal numeric entity; like '&#xA0;' for non-breaking space), or alphanumeric like 'gt' (external or named entity; like '&nbsp;' for non-breaking space), or '#' followed by a number (decimal numeric entity; like '&#160;' for non-breaking space). Character entities referring to the soft-hyphen character (the '&shy;' or '\xad' character; hexadecimal code-point 'ad' [decimal '173']) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.
+
+ htmLawed (function 'hl_ent()'):
+
+ * Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)
+
+ * Lowercases the 'X' (for XML-compliance) and 'A-F' of hexadecimal numeric entities
+
+ * Neutralizes entities referring to characters that are HTML-invalid (see section:- #3.1)
+
+ * Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, '7f' to '84', '86' to '9f', and 'fdd0' to 'fddf', or decimally, '127' to '132', '134' to '159', and '64991' to '64976'). Entities referring to the remaining discouraged characters (see section:- #5.1 for a full list) are let through.
+
+ * Neutralizes named entities that are not in the specs.
+
+ * Optionally converts valid HTML-specific named entities except '&gt;', '&lt;', '&quot;', and '&amp;' to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is '2') for generic XML-compliance. For this, '$config["named_entity"]' should be '1'.
+
+ * Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, '$config["hexdec_entity"]' should be '0'.
+
+ * Optionally converts decimal numeric entities to the hexadecimal ones. For this, '$config["hexdec_entity"]' should be '2'.
+
+ `Neutralization` refers to the `entitification` of '&' to '&amp;'.
+
+ *Note*: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's 'html_entity_decode' function:- http://www.php.net/html_entity_decode for that.
+
+ *Note*: If '$config["and_mark"]' is set, and set to a value other than '0', then the '&' characters in the original input are replaced with the control character for the hexadecimal code-point '6' ('\x06'; '&' characters introduced by htmLawed, e.g., after converting '<' to '&lt;', are not affected). This allows one to distinguish, say, an '&gt;' introduced by htmLawed and an '&gt;' put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence 'o(><)o' to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the '\x06' can break documents. Before use in such documents, and preferably before any storage, any remaining '\x06' should be changed back to '&', e.g., with:
+
+ $final = str_replace("\x06", '&', $prelim);
+
+ Also, see section:- #3.9.
+
+
+-- 3.3 HTML elements ----------------------------------------------o
+
+
+ htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on '$config["keep_bad"]', are either `neutralized` (converted to plain text by entitification of '<' and '>') or removed.
+
+ E.g., with only 'em' permitted:
+
+ Input:
+
+ <em>My</em> website is <a href="http://a.com>a.com</a>.
+
+ Output, with '$config["keep_bad"] = 0':
+
+ <em>My</em> website is a.com.
+
+ Output, with '$config["keep_bad"]' not '0':
+
+ <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;.
+
+ See section:- #3.3.3 for differences between the various non-zero '$config["keep_bad"]' values.
+
+ htmLawed by default permits these 86 elements:
+
+ a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var
+
+ Except for 'embed' (included because of its wide-spread use) and the Ruby elements ('rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby'; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude 'center', 'dir', 'font', 'isindex', 'menu', 's', 'strike', and 'u'.
+
+ With '$config["safe"] = 1', the default set will exclude 'applet', 'embed', 'iframe', 'object' and 'script'; see section:- #3.6.
+
+ When '$config["elements"]', which specifies allowed elements, is `properly` defined, and neither empty nor set to '0' or '*', the default set is not used. To have elements added to or removed from the default set, a '+/-' notation is used. E.g., '*-script-object' implies that only 'script' and 'object' are disallowed, whereas '*+embed' means that 'noembed' is also allowed. Elements can also be specified as comma separated names. E.g., 'a, b, i' means only 'a', 'b' and 'i' are permitted. In this notation, '*', '+' and '-' have no significance and can actually cause a mis-reading.
+
+ Some more examples of '$config["elements"]' values indicating permitted elements (note that empty spaces are liberally allowed for clarity):
+
+ * 'a, blockquote, code, em, strong' -- only 'a', 'blockquote', 'code', 'em', and 'strong'
+ * '*-script' -- all excluding 'script'
+ * '* -center -dir -font -isindex -menu -s -strike -u' -- only XHTML-Strict elements
+ * '*+noembed-script' -- all including 'noembed' excluding 'script'
+
+ Some mis-usages (and the resulting permitted elements) that can be avoided:
+
+ * '-*' -- none; instead of htmLawed, one might just use, e.g., the 'htmlspecialchars()' PHP function
+ * '*, -script' -- all except 'script'; admin probably meant '*-script'
+ * '-*, a, em, strong' -- all; admin probably meant 'a, em, strong'
+ * '*' -- all; admin need not have set 'elements'
+ * '*-form+form' -- all; a '+' will always over-ride any '-'
+ * '*, noembed' -- only 'noembed'; admin probably meant '*+noembed'
+ * 'a, +b, i' -- only 'a' and 'i'; admin probably meant 'a, b, i'
+
+ Basically, when using the '+/-' notation, commas (',') should not be used, and vice versa, and '*' should be used with the former but not the latter.
+
+ *Note*: Even if an element that is not in the default set is allowed through '$config["elements"]', like 'noembed' in the last example, it will eventually be removed during tag balancing unless such balancing is turned off ('$config["balance"]' set to '0'). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function 'hl_bal()' to accommodate the element and its nesting properties.
+
+ *A possibly second way to specify allowed elements* is to set '$config["parent"]' to an element name that supposedly will hold the input, and to set '$config["balance"]' to '1'. During tag balancing (see section:- #3.3.3), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to 'div' if '$config["parent"]' is empty, 'body', or an element not in htmLawed's default set of 86 elements.
+
+ `Tag transformation` is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see section:- #3.3.2.
+
+
+.. 3.3.1 Handling of comments and CDATA sections ...................
+
+
+ 'CDATA' sections have the format '<![CDATA[...anything but not "]]>"...]]>', and HTML comments, '<!--...anything but not "-->"... -->'. Neither HTML comments nor 'CDATA' sections can reside inside tags. HTML comments can exist anywhere else, but 'CDATA' sections can exist only where plain text is allowed (e.g., immediately inside 'td' element content but not immediately inside 'tr' element content).
+
+ htmLawed (function 'hl_cmtcd()') handles HTML comments or 'CDATA' sections depending on the values of '$config["comment"]' or '$config["cdata"]'. If '0', such markup is not looked for and the text is processed like plain text. If '1', it is removed completely. If '2', it is preserved but any '<', '>' and '&' inside are changed to entities. If '3', they are left as such.
+
+ Note that for the last two cases, HTML comments and 'CDATA' sections will always be removed from tag content (function 'hl_tag()').
+
+ Examples:
+
+ Input:
+ <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
+ Output ('$config["comment"] = 0, $config["cdata"] = 2'):
+ &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
+ Output ('$config["comment"] = 1, $config["cdata"] = 2'):
+ <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
+ Output ('$config["comment"] = 2, $config["cdata"] = 2'):
+ <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
+ Output ('$config["comment"] = 2, $config["cdata"] = 1'):
+ <!-- home link --><a href="home.htm">Home</a>
+ Output ('$config["comment"] = 3, $config["cdata"] = 3'):
+ <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
+
+ For standard-compliance, comments are given the form '<!--comment -->', and any '--' in the content is made '-'.
+
+ When '$config["safe"] = 1', CDATA sections and comments are considered plain text unless '$config["comment"]' or '$config["cdata"]' is explicitly specified; see section:- #3.6.
+
+
+.. 3.3.2 Tag-transformation for better XHTML-Strict ................o
+
+
+ If '$config["make_tag_strict"]' is set and not '0', following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function 'hl_tag2()'):
+
+ * applet - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
+ * center - 'div style="text-align: center;"'
+ * dir - 'ul'
+ * embed - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
+ * font (face, size, color) - 'span style="font-family: ; font-size: ; color: ;"' (size transformation reference:- http://style.cleverchimp.com/font_size_intervals/altintervals.html)
+ * isindex - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
+ * menu - 'ul'
+ * s - 'span style="text-decoration: line-through;"'
+ * strike - 'span style="text-decoration: line-through;"'
+ * u - 'span style="text-decoration: underline;"'
+
+ For an element with a pre-existing 'style' attribute value, the extra style properties are appended.
+
+ Example input:
+
+ <center>
+ The PHP <s>software</s> script used for this <strike>web-page</strike> web-page is <font style="font-weight: bold " face=arial size='+3' color = "red ">htmLawedTest.php</font>, from <u style= 'color:green'>PHP Labware</u>.
+ </center>
+
+ The output:
+
+ <div style="text-align: center;">
+ The PHP <span style="text-decoration: line-through;">software</span> script used for this <span style="text-decoration: line-through;">web-page</span> web-page is <span style="font-weight: bold; font-family: arial; color: red; font-size: 200%;">htmLawedTest.php</span>, from <span style="color:green; text-decoration: underline;">PHP Labware</span>.
+ </div>
+
+
+-- 3.3.3 Tag balancing and proper nesting -------------------------o
+
+
+ If '$config["balance"]' is set to '1', htmLawed (function 'hl_bal()') checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).
+
+ Depending on the value of '$config["keep_bad"]' (see section:- #2.2 and section:- #3.3), illegal content may be removed or neutralized to plain text by converting < and > to entities:
+
+ '0' - remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see section:- #2.6)
+ '1' - neutralize tags and keep element content
+ '2' - remove tags but keep element content
+ '3' and '4' - like '1' and '2', but keep element content only if text ('pcdata') is valid in parent element as per specs
+ '5' and '6' - like '3' and '4', but line-breaks, tabs and spaces are left
+
+ Example input (disallowing the 'p' element):
+
+ <*> Pseudo-tags <*>
+ <xml>Non-HTML tag xml</xml>
+ <p>
+ Disallowed tag p
+ </p>
+ <ul>Bad<li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 1':
+
+ &lt;*&gt; Pseudo-tags &lt;*&gt;
+ &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
+ &lt;p&gt;
+ Disallowed tag p
+ &lt;/p&gt;
+ <ul>Bad<li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 3':
+
+ &lt;*&gt; Pseudo-tags &lt;*&gt;
+ &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
+ &lt;p&gt;
+ Disallowed tag p
+ &lt;/p&gt;
+ <ul><li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 6':
+
+ &lt;*&gt; Pseudo-tags &lt;*&gt;
+ Non-HTML tag xml
+
+ Disallowed tag p
+
+ <ul><li>OK</li></ul>
+
+ An option like '1' is useful, e.g., when a writer previews his submission, whereas one like '3' is useful before content is finalized and made available to all.
+
+ *Note:* In the example above, unlike '<*>', '<xml>' gets considered as a tag (even though there is no HTML element named 'xml'). Thus, the 'keep_bad' parameter's value affects '<xml>' but not '<*>'. In general, text matching the regular expression pattern '<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>' is considered a tag (phrase enclosed by the angled brackets '<' and '>', and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...), and is subjected to the 'keep_bad' value.
+
+ Nesting/content rules for each of the 86 elements in htmLawed's default set (see section:- #3.3) are defined in function 'hl_bal()'. This means that if a non-standard element besides 'embed' is being permitted through '$config["elements"]', the element's tag content will end up getting removed if '$config["balance"]' is set to '1'.
+
+ Plain text and/or certain elements nested inside 'blockquote', 'form', 'map' and 'noscript' need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as 'form', the input 'B:<input type="text" value="b" />C:<input type="text" value="c" />' is converted to '<div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div>'.
+
+
+-- 3.3.4 Elements requiring child elements ------------------------o
+
+
+ As per specs, the following elements require legal child elements nested inside them:
+
+ blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul
+
+ In some cases, the specs stipulate the number and/or the ordering of the child elements. A 'table' can have 0 or 1 'caption', 'tbody', 'tfoot', and 'thead', but they must be in this order: 'caption', 'thead', 'tfoot', 'tbody'.
+
+ htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.
+
+ With '$config["direct_list_nest"]' set to '1', htmLawed will allow direct nesting of an 'ol' or 'ul' list within another 'ol' or 'ul' without requiring the child list to be within an 'li' of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter '$config["direct_list_nest"]' has no effect if tag-balancing (section:- #3.3.3) is turned off.
+
+
+-- 3.3.5 Beautify or compact HTML ---------------------------------o
+
+
+ By default, htmLawed will neither `beautify` HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)
+
+ As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside 'pre' elements) are all considered equivalent, and referred to as `white-spaces`. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space `normalization` allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such `pretty` HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.
+
+ With the '$config' parameter 'tidy', htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides 'pre', the 'script' and 'textarea' elements, CDATA sections, and HTML comments are not subjected to the tidying process.
+
+ To `compact`, use '$config["tidy"] = -1'; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.
+
+ To `beautify`, '$config["tidy"]' is set as '1', or for customized tidying, as a string like '2s2n'. The 's' or 't' character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The 'r' and 'n' characters are used to specify line-break characters: 'n' for '\n' (Unix/Mac OS X line-breaks), 'rn' or 'nr' for '\r\n' (Windows/DOS line-breaks), or 'r' for '\r'.
+
+ The '$config["tidy"]' value of '1' is equivalent to '2s0n'. Other '$config["tidy"]' values are read loosely: a value of '4' is equivalent to '4s0n'; 't2', to '1t2n'; 's', to '2s0n'; '2TR', to '2t0r'; 'T1', to '1t1n'; 'nr3', to '3s0nr', and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.
+
+ Input formatting using '$config["tidy"]' is not recommended when input text has mixed markup (like HTML + PHP).
+
+
+-- 3.4 Attributes ------------------------------------------------oo
+
+
+ htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the 'embed' element (the non-standard 'embed' element is supported in htmLawed because of its widespread use), and the the 'xml:space' attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in section:- #5.2. Using the '$spec' argument, htmLawed can be forced to permit custom, non-standard attributes as well as custom rules for standard attributes (section:- #2.3).
+
+ When '$config["deny_attribute"]' is not set, or set to '0', or empty ('""'), all the 111 attributes are permitted. Otherwise, '$config["deny_attribute"]' can be set as a list of comma-separated names of the denied attributes. 'on*' can be used to refer to the group of potentially dangerous, script-accepting attributes: 'onblur', 'onchange', 'onclick', 'ondblclick', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onreset', 'onselect' and 'onsubmit'.
+
+ Note that attributes specified in '$config["deny_attribute"]' are denied globally, for all elements. To deny attributes for only specific elements, '$spec' (see section:- #2.3) can be used. '$spec' can also be used to element-specifically permit an attribute otherwise denied through '$config["deny_attribute"]'.
+
+ With '$config["safe"] = 1' (section:- #3.6), the 'on*' attributes are automatically disallowed.
+
+ *Note*: To deny all but a few attributes globally, a simpler way to specify '$config["deny_attribute"]' would be to use the notation '* -attribute1 -attribute2 ...'. Thus, a value of '* -title -href' implies that except 'href' and 'title' (where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter 'safe' (section:- #3.6) will have no effect on 'deny_attribute'.
+
+ htmLawed (function 'hl_tag()') also:
+
+ * Lower-cases attribute names
+ * Removes duplicate attributes (last one stays)
+ * Gives attributes the form 'name="value"' and single-spaces them, removing unnecessary white-spacing
+ * Provides `required` attributes (see section:- #3.4.1)
+ * Double-quotes values and escapes any '"' inside them
+ * Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point 'ad') in the values with spaces
+ * Allows custom function to additionally filter/modify attribute values (see section:- #3.4.9)
+
+
+.. 3.4.1 Auto-addition of XHTML-required attributes ................
+
+
+ If indicated attributes for the following elements are found missing, htmLawed (function 'hl_tag()') will add them (with values same as attribute names unless indicated otherwise below):
+
+ * area - alt ('area')
+ * area, img - src, alt ('image')
+ * bdo - dir ('ltr')
+ * form - action
+ * map - name
+ * optgroup - label
+ * param - name
+ * script - type ('text/javascript')
+ * textarea - rows ('10'), cols ('50')
+
+ Additionally, with '$config["xml:lang"]' set to '1' or '2', if the 'lang' but not the 'xml:lang' attribute is declared, then the latter is added too, with a value copied from that of 'lang'. This is for better standard-compliance. With '$config["xml:lang"]' set to '2', the 'lang' attribute is removed (XHTML 1.1 specs).
+
+ Note that the 'name' attribute for 'map', invalid in XHTML 1.1, is also transformed if required -- see section:- #3.4.6.
+
+
+.. 3.4.2 Duplicate/invalid 'id' values ............................o
+
+
+ If '$config["unique_ids"]' is '1', htmLawed (function 'hl_tag()') removes 'id' attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, ':', '.', '-' and '_') or duplicate. If '$config["unique_ids"]' is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, ':', '.', '_' and '-'.
+
+ Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of 'id' values as it uses a global variable ('$GLOBALS["hl_Ids"]' array). Further, an admin can restrict the use of certain 'id' values by presetting this variable before htmLawed is called into use. E.g.:
+
+ $GLOBALS['hl_Ids'] = array('top'=>1, 'bottom'=>1, 'myform'=>1); // id values not allowed in input
+ $processed = htmLawed($text); // filter input
+
+
+.. 3.4.3 URL schemes (protocols) and scripts in attribute values ............o
+
+
+ htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the 'afp' scheme is not permitted, then '<a href="afp://domain.org">' becomes '<a href="denied:afp://domain.org">', and if Javascript is not permitted '<a onclick="javascript:xss();">' becomes '<a onclick="denied:javascript:xss();">'.
+
+ By default htmLawed permits these schemes in URLs for the 'href' attribute:
+
+ aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet
+
+ Also, only 'file', 'http' and 'https' are permitted in attributes whose names start with 'o' (like 'onmouseover'), and in these attributes that accept URLs:
+
+ action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap
+
+ These default sets are used when '$config["schemes"]' is not set (see section:- #2.2). To over-ride the defaults, '$config["schemes"]' is defined as a string of semi-colon-separated sub-strings of type 'attribute: comma-separated schemes'. E.g., 'href: mailto, http, https; onclick: javascript; src: http, https'. For unspecified attributes, 'file', 'http' and 'https' are permitted. This can be changed by passing schemes for '*' in '$config["schemes"]'. E.g., 'href: mailto, http, https; *: https, https'.
+
+ '*' can be put in the list of schemes to permit all protocols. E.g., 'style: *; img: http, https' results in protocols not being checked in 'style' attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (section:- #3.4.4) is not done.
+
+ Thus, `to allow Javascript`, one can set '$config["schemes"]' as 'href: mailto, http, https; *: http, https, javascript', or 'href: mailto, http, https, javascript; *: http, https, javascript', or '*: *', and so on.
+
+ As a side-note, one may find 'style: *' useful as URLs in 'style' attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.
+
+ '!' can be put in the list of schemes to disallow all protocols as well as `local` URLs. Thus, with 'href: http, style: !', '<a href="http://cnn.com" style="background-image: url('local.jpg');">CNN</a>' will become '<a href="http://cnn.com" style="background-image: url('denied:local.jpg');">CNN</a>'.
+
+ *Note*: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string 'src' (e.g., 'dynsrc') or starts with 'o' (e.g., 'onbeforecopy').
+
+ With '$config["safe"] = 1', all URLs are disallowed in the 'style' attribute values.
+
+
+.. 3.4.4 Absolute & relative URLs in attribute values .............o
+
+
+ htmLawed can make absolute URLs in attributes like 'href' relative ('$config["abs_url"]' is '-1'), and vice versa ('$config["abs_url"]' is '1'). URLs in scripts are not considered for this, and so are URLs like '#section_6' (fragment), '?name=Tim#show' (starting with query string), and ';var=1?name=Tim#show' (starting with parameters). Further, this requires that '$config["base_url"]' be set properly, with the '://' and a trailing slash ('/'), with no query string, etc. E.g., 'file:///D:/page/', 'https://abc.com/x/y/', or 'http://localhost/demo/' are okay, but 'file:///D:/page/?help=1', 'abc.com/x/y/' and 'http://localhost/demo/index.htm' are not.
+
+ For making absolute URLs relative, only those URLs that have the '$config["base_url"]' string at the beginning are converted. E.g., with '$config["base_url"] = "https://abc.com/x/y/"', 'https://abc.com/x/y/a.gif' and 'https://abc.com/x/y/z/b.gif' become 'a.gif' and 'z/b.gif' respectively, while 'https://abc.com/x/c.gif' is not changed.
+
+ When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See section:- #5.5 for more about the URL specification as per RFC 1808:- http://www.ietf.org/rfc/rfc1808.txt.
+
+
+.. 3.4.5 Lower-cased, standard attribute values ....................o
+
+
+ Optionally, for standard-compliance, htmLawed (function 'hl_tag()') lower-cases standard attribute values to give, e.g., 'input type="password"' instead of 'input type="Password"', if '$config["lc_std_val"]' is '1'. Attribute values matching those listed below for any of the elements (plus those for the 'type' attribute of 'button' or 'input') are lower-cased:
+
+ all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top
+
+ a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml:space
+
+ The following `empty` (`minimized`) attributes are always assigned lower-cased values (same as the names):
+
+ checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected
+
+
+.. 3.4.6 Transformation of deprecated attributes ..................o
+
+
+ If '$config["no_deprecated_attr"]' is '0', then deprecated attributes (see appendix in section:- #5.2) are removed and, in most cases, their values are transformed to CSS style properties and added to the 'style' attributes (function 'hl_tag()'). Except for 'bordercolor' for 'table', 'tr' and 'td', the scores of proprietary attributes that were never part of any cross-browser standard are not supported.
+
+ *Note*: The attribute 'target' for 'a' is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.
+
+ * align - for 'img' with value of 'left' or 'right', becomes, e.g., 'float: left'; for 'div' and 'table' with value 'center', becomes 'margin: auto'; all others become, e.g., 'text-align: right'
+
+ * bgcolor - E.g., 'bgcolor="#ffffff"' becomes 'background-color: #ffffff'
+ * border - E.g., 'height= "10"' becomes 'height: 10px'
+ * bordercolor - E.g., 'bordercolor=#999999' becomes 'border-color: #999999;'
+ * compact - 'font-size: 85%'
+ * clear - E.g., 'clear="all" becomes 'clear: both'
+
+ * height - E.g., 'height= "10"' becomes 'height: 10px' and 'height="*"' becomes 'height: auto'
+
+ * hspace - E.g., 'hspace="10"' becomes 'margin-left: 10px; margin-right: 10px'
+ * language - 'language="VBScript"' becomes 'type="text/vbscript"'
+ * name - E.g., 'name="xx"' becomes 'id="xx"'
+ * noshade - 'border-style: none; border: 0; background-color: gray; color: gray'
+ * nowrap - 'white-space: nowrap'
+ * size - E.g., 'size="10"' becomes 'height: 10px'
+ * start - removed
+ * type - E.g., 'type="i"' becomes 'list-style-type: lower-roman'
+ * value - removed
+ * vspace - E.g., 'vspace="10"' becomes 'margin-top: 10px; margin-bottom: 10px'
+ * width - like 'height'
+
+ Example input:
+
+ <img src="j.gif" alt="image" name="dad's" /><img src="k.gif" alt="image" id="dad_off" name="dad" />
+ <br clear="left" />
+ <hr noshade size="1" />
+ <img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding:5px;" />
+ <table width="50em" align="center" bgcolor="red">
+ <tr>
+ <td width="20%">
+ <div align="center">
+ <h3 align="right">Section</h3>
+ <p align="right">Para</p>
+ <ol type="a" start="e"><li value="x">First item</li></ol>
+ </div>
+ </td>
+ <td width="*">
+ <ol type="1"><li>First item</li></ol>
+ </td>
+ </tr>
+ </table>
+ <br clear="all" />
+
+ And the output with '$config["no_deprecated_attr"] = 1':
+
+ <img src="j.gif" alt="image" /><img src="k.gif" alt="image" id="dad_off" />
+ <br style="clear: left;" />
+ <hr style="border-style: none; border: 0; background-color: gray; color: gray; size: 1px;" />
+ <img src="i.gif" alt="image" width="10em" height="20" style="padding:5px; float: left; margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px; border: 1px;" id="img" />
+ <table width="50em" style="margin: auto; background-color: red;">
+ <tr>
+ <td style="width: 20%;">
+ <div style="margin: auto;">
+ <h3 style="text-align: right;">Section</h3>
+ <p style="text-align: right;">Para</p>
+ <ol style="list-style-type: lower-latin;"><li>First item</li></ol>
+ </div>
+ </td>
+ <td style="width: auto;">
+ <ol style="list-style-type: decimal;"><li>First item</li></ol>
+ </td>
+ </tr>
+ </table>
+ <br style="clear: both;" />
+
+ For 'lang', deprecated in XHTML 1.1, transformation is taken care of through '$config["xml:lang"]'; see section:- #3.4.1.
+
+ The attribute 'name' is deprecated in 'form', 'iframe', and 'img', and is replaced with 'id' if an 'id' attribute doesn't exist and if the 'name' value is appropriate for 'id'. For such replacements for 'a' and 'map', for which the 'name' attribute is deprecated in XHTML 1.1, '$config["no_deprecated_attr"]' should be set to '2' (when set to '1', for these two elements, the 'name' attribute is retained).
+
+
+-- 3.4.7 Anti-spam & 'href' ---------------------------------------o
+
+
+ htmLawed (function 'hl_tag()') can check the 'href' attribute values (link addresses) as an anti-spam (email or link spam) measure.
+
+ If '$config["anti_mail_spam"]' is not '0', the '@' of email addresses in 'href' values like 'mailto:a@b.com' is replaced with text specified by '$config["anti_mail_spam"]'. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., '<remove_this_antispam>@' (makes the example address 'a<remove_this_antispam>@b.com').
+
+ For regular links, one can choose to have a 'rel' attribute with 'nofollow' in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the 'href' value altogether (disable the link).
+
+ For use of these options, '$config["anti_link_spam"]' should be set as an array with values 'regex1' and 'regex2', both or one of which can be empty (like 'array("", "regex2")') to indicate that that option is not to be used. Otherwise, 'regex1' or 'regex2' should be PHP- and PCRE-compatible regular expression patterns: 'href' values will be matched against them and those matching the pattern will accordingly be treated.
+
+ Note that the regular expressions should have `delimiters`, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.
+
+ An example, to have a 'rel' attribute with 'nofollow' for all links, and to disable links that do not point to domains 'abc.com' and 'xyz.org':
+
+ $config["anti_link_spam"] = array('`.`', '`://\W*(?!(abc\.com|xyz\.org))`');
+
+
+-- 3.4.8 Inline style properties ----------------------------------o
+
+
+ htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the 'style' attributes. (CSS properties like 'background-image' that accept URLs in their values are noted in section:- #5.3.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting '$config["css_expression"]' to '1' (default setting). Note that when '$config["css_expression"]' is set to '1', htmLawed will remove '/*' from the 'style' values.
+
+ *Note*: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the 'style' attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off ('$config["schemes"] = "...style:*..."', see section:- #3.4.3, and '$config["css_expression"] = 0'). Alternately, admins can use their own custom function for finer handling of 'style' values through the 'hook_tag' parameter (see section:- #3.4.9).
+
+ It is also possible to have htmLawed let through any 'style' value by setting '$config["style_pass"]' to '1'.
+
+ As such, it is better to set up a CSS file with class declarations, disallow the 'style' attribute, set a '$spec' rule (see section:- #2.3) for 'class' for the 'oneof' or 'match' parameter, and ask writers to make use of the 'class' attribute.
+
+
+-- 3.4.9 Hook function for tag content ----------------------------o
+
+
+ It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).
+
+ When '$config' parameter 'hook_tag' is set to the name of a function, htmLawed (function 'hl_tag()') will pass on the element name, and, in the case of an opening tag, the `finalized` attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like '<element_name attribute_1_name="attribute_1_value"...>' (for empty elements like 'img' and 'input', the element-closing slash '/' should also be included), etc.
+
+ Any 'hook_tag' function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as 'a' in the closing '</a>' tag of the element '<a href="http://cnn.com">CNN</a>'. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like '</a>').
+
+ This is a *powerful functionality* that can be exploited for various objectives: consolidate-and-convert inline 'style' attributes to 'class', convert 'embed' elements to 'object', permit only one 'caption' element in a 'table' element, disallow embedding of certain types of media, *inject HTML*, use CSSTidy:- http://csstidy.sourceforge.net to sanitize 'style' attribute values, etc.
+
+ As an example, the custom hook code below can be used to force a series of specifically ordered 'id' attributes on all elements, and a specific 'param' element inside all 'object' elements:
+
+ function my_tag_function($element, $attribute_array=0){
+
+ // If second argument is not received, it means a closing tag is being handled
+ if(is_numeric($attribute_array)){
+ return "</$element>";
+ }
+
+ static $id = 0;
+ // Remove any duplicate element
+ if($element == 'param' && isset($attribute_array['allowscriptaccess'])){
+ return '';
+ }
+
+ $new_element = '';
+
+ // Force a serialized ID number
+ $attribute_array['id'] = 'my_'. $id;
+ ++$id;
+
+ // Inject param for allowscriptaccess
+ if($element == 'object'){
+ $new_element = '<param id='my_'. $id; allowscriptaccess="never" />';
+ ++$id;
+ }
+
+ $string = '';
+ foreach($attribute_array as $k=>$v){
+ $string .= " {$k}=\"{$v}\"";
+ }
+
+ static $empty_elements = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);
+
+ return "<{$element}{$string}". (isset($in_array($element, $empty_elements) ? ' /' : ''). '>'. $new_element;
+ }
+
+ The 'hook_tag' parameter is different from the 'hook' parameter (section:- #3.7).
+
+ Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
+
+
+-- 3.5 Simple configuration directive for most valid XHTML -------oo
+
+
+ If '$config["valid_xhtml"]' is set to '1', some relevant '$config' parameters (indicated by '~' in section:- #2.2) are auto-adjusted. This allows one to pass the '$config' argument with a simpler value. If a value for a parameter auto-set through 'valid_xhtml' is still manually provided, then that value will over-ride the auto-set value.
+
+
+-- 3.6 Simple configuration directive for most `safe` HTML --------o
+
+
+ `Safe` HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as 'script' and 'object', and attributes such as 'onmouseover' and 'style' are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered 'safe' depends on the nature of the web application and the trust-level accorded to its users.
+
+ htmLawed allows an admin to use '$config["safe"]' to auto-adjust multiple '$config' parameters (such as 'elements' which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by '"' in section:- #2.2). Thus, one can pass the '$config' argument with a simpler value.
+
+ With the value of '1', htmLawed considers 'CDATA' sections and HTML comments as plain text, and prohibits the 'applet', 'embed', 'iframe', 'object' and 'script' elements, and the 'on*' attributes like 'onclick'. ( There are '$config' parameters like 'css_expression' that are not affected by the value set for 'safe' but whose default values still contribute towards a more `safe` output.) Further, URLs with schemes (see section:- #3.4.3) are neutralized so that, e.g., 'style="moz-binding:url(http://danger)"' becomes 'style="moz-binding:url(denied:http://danger)"'.
+
+ Admins, however, may still want to completely deny the 'style' attribute, e.g., with code like
+
+ $processed = htmLawed($text, array('safe'=>1, 'deny_attribute'=>'style'));
+
+ Permitting the 'style' attribute brings in risks of `click-jacking`, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check 'style' values. It does provide ways for the code-developer implementing htmLawed to do such checks through the '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.
+
+ If a value for a parameter auto-set through 'safe' is still manually provided, then that value can over-ride the auto-set value. E.g., with '$config["safe"] = 1' and '$config["elements"] = "*+script"', 'script', but not 'applet', is allowed.
+
+ A page illustrating the efficacy of htmLawed's anti-XSS abilities with 'safe' set to '1' against XSS vectors listed by RSnake:- http://ha.ckers.org/xss.html may be available here:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm.
+
+
+-- 3.7 Using a hook function --------------------------------------o
+
+
+ If '$config["hook"]' is not set to '0', then htmLawed will allow preliminarily processed input to be altered by a hook function named by '$config["hook"]' before starting the main work (but after handling of characters, entities, HTML comments and 'CDATA' sections -- see code for function 'htmLawed()').
+
+ The hook function also allows one to alter the `finalized` values of '$config' and '$spec'.
+
+ Note that the 'hook' parameter is different from the 'hook_tag' parameter (section:- #3.4.9).
+
+ Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
+
+
+-- 3.8 Obtaining `finalized` parameter values ---------------------o
+
+
+ htmLawed can assign the `finalized` '$config' and '$spec' values to a variable named by '$config["show_setting"]'. The variable, made global by htmLawed, is set as an array with three keys: 'config', with the '$config' value, 'spec', with the '$spec' value, and 'time', with a value that is the Unix time (the output of PHP's 'microtime()' function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.
+
+ The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.
+
+
+-- 3.9 Retaining non-HTML tags in input with mixed markup ---------o
+
+
+ htmLawed does not remove certain characters that, though invalid, are nevertheless `discouraged` in HTML documents as per the specifications (see section:- #5.1). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the '<', '>' and '&' characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).
+
+ To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the '<', '>' and '&' characters with some of the HTML-discouraged characters (see section:- #3.1.2). Post-htmLawed processing, the replacements are reverted.
+
+ An example (mixed HTML and PHP code in input text):
+
+ $text = preg_replace('`<\?php(.+?)\?>`sm', "\x83?php\\1?\x84", $text);
+ $processed = htmLawed($text);
+ $processed = preg_replace('`\x83\?php(.+?)\?\x84`sm', '<?php$1?>', $processed);
+
+ This code will not work if '$config["clean_ms_char"]' is set to '1' (section:- #3.1), in which case one should instead deploy a hook function (section:- #3.7). (htmLawed internally uses certain control characters, code-points '1' to '7', and use of these characters as markers in the logic of hook functions may cause issues.)
+
+ Admins may also be able to use '$config["and_mark"]' to deal with such mixed markup; see section:- #3.2.
+
+
+== 4 Other =======================================================oo
+
+
+-- 4.1 Support -----------------------------------------------------
+
+
+ A careful reading of this documentation may provide an answer.
+
+ Software updates and forum-based community-support may be found at http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at http://php.net.
+
+
+-- 4.2 Known issues -----------------------------------------------o
+
+
+ See section:- #2.8.
+
+
+-- 4.3 Change-log -------------------------------------------------o
+
+
+ (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the 'htmLawed.php' file, may be updated without a change-log entry if the secondary files, but not htmLawed per se, are revised.)
+
+ `Version number - Release date. Notes`
+
+ 1.1.16 - 29 August 2013. Fix for a potential security vulnerability arising from specialy encoded space characters in URL schemes/protocols
+
+ 1.1.15 - 11 August 2013. Improved tidying/prettifying functionality
+
+ 1.1.14 - 8 August 2012. Fix for possible segmental loss of incremental indentation during 'tidying' when 'balance' is disabled; fix for non-effectuation under some circumstances of a corrective behavior to preserve plain text within elements like 'blockquote'.
+
+ 1.1.13 - 22 July 2012. Added feature allowing use of custom, non-standard attributes or custom rules for standard attributes
+
+ 1.1.12 - 5 July 2012. Fix for a bug in identifying an unquoted value of the 'face' attribute
+
+ 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. '$config["hook_tag"]', if specified, now receives names of elements in closing tags.
+
+ 1.1.10 - 22 October 2011. Fix for a bug in the 'tidy' functionality that caused the entire input to be replaced with a single space; new parameter, '$config["direct_list_nest"]' to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)
+
+ 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of 'li' within 'dir'
+
+ 1.1.9.4 - 3 July 2010. Parameter 'schemes' now accepts '!' so any URL, even a local one, can be `denied`. An issue in which a second URL value in 'style' properties was not checked was fixed.
+
+ 1.1.9.3 - 17 May 2010. Checks for correct nesting of 'param'
+
+ 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes
+
+ 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for 'flashvars' attribute for 'embed'
+
+ 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values
+
+ 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice
+
+ 1.1.8 - 23 April 2009. Parameter 'deny_attribute' now accepts the wild-card '*', making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting '$spec'
+
+ 1.1.7 - 11-12 March 2009. Attributes globally denied through 'deny_attribute' can be allowed element-specifically through '$spec'; '$config["style_pass"]' allowing letting through any 'style' value introduced; altered logic to catch certain types of dynamic crafted CSS expressions
+
+ 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions
+
+ 1.1.2 - 22 January 2009. Fixed bug in parsing of 'font' attributes during tag transformation
+
+ 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent
+
+ 1.1 - 29 June 2008. '$config["hook_tag"]' and '$config["tidy"]' introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug
+
+ 1.0.9 - 11 June 2008. Fix for a bug in checks for invalid HTML code-point entities
+
+ 1.0.8 - 15 May 2008. Permit 'bordercolor' attribute for 'table', 'td' and 'tr'
+
+ 1.0.7 - 1 May 2008. Support for 'wmode' attribute for 'embed'; '$config["show_setting"]' introduced; improved '$config["elements"]' evaluation
+
+ 1.0.6 - 20 April 2008. '$config["and_mark"]' introduced
+
+ 1.0.5 - 12 March 2008. 'style' URL schemes essentially disallowed when $config 'safe' is on; improved regex for CSS expression search
+
+ 1.0.4 - 10 March 2008. Improved corrections for 'blockquote', 'form', 'map' and 'noscript'
+
+ 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); fix for a bug allowing 'td' directly inside 'table'; '$config["safe"]' introduced
+
+ 1.0.2 - 13 February 2008. Improved implementation of '$config["keep_bad"]'
+
+ 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions ('hl_tag()' and 'hl_prot()'); no error display with 'hl_regex()'
+
+ 1.0 - 2 November 2007. First release
+
+
+-- 4.4 Testing ----------------------------------------------------o
+
+
+ To test htmLawed using a form interface, a demo:- htmLawedTest.php web-page is provided with the htmLawed distribution ('htmLawed.php' and 'htmLawedTest.php' should be in the same directory on the web-server). A file with test-cases:- htmLawed_TESTCASE.txt is also provided.
+
+
+-- 4.5 Upgrade, & old versions ------------------------------------o
+
+
+ Upgrading is as simple as replacing the previous version of 'htmLawed.php' (assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.
+
+ *Important* The following upgrades may affect the functionality of a specific htmLawed installation:
+
+ (1) From version 1.1-1.1.10 to 1.1.11 (or later), if a 'hook_tag' function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a 'hook_tag' function receives only the element name. The 'hook_tag' function therefore may have to be edited. See section:- #3.4.9.
+
+ Old versions of htmLawed may be available online. E.g., for version 1.0, check http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.
+
+
+-- 4.6 Comparison with 'HTMLPurifier' -----------------------------o
+
+
+ The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of year 2010):
+
+ * does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)
+
+ * is 15-20 times bigger (scores of files totalling more than 750 kb)
+
+ * consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)
+
+ * is expectedly slower
+
+ * does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like 'script' illegal)
+
+ * lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)
+
+ * has poor documentation
+
+ However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier website:- http://htmlpurifier.org for updated information.
+
+
+-- 4.7 Use through application plug-ins/modules -------------------o
+
+
+ Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
+
+
+-- 4.8 Use in non-PHP applications --------------------------------o
+
+
+ Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
+
+
+-- 4.9 Donate -----------------------------------------------------o
+
+
+ A donation in any currency and amount to appreciate or support this software can be sent by PayPal:- http://paypal.com to this email address: drpatnaik at yahoo dot com.
+
+
+-- 4.10 Acknowledgements ------------------------------------------o
+
+
+ Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Dac Chartrand, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Harro Verton, Edward Yang, and many anonymous users.
+
+ Thank you!
+
+
+== 5 Appendices ==================================================oo
+
+
+-- 5.1 Characters discouraged in XHTML -----------------------------
+
+
+ Characters represented by the following hexadecimal code-points are `not` invalid, even though some validators may issue messages stating otherwise.
+
+ '7f' to '84', '86' to '9f', 'fdd0' to 'fddf', '1fffe', '1ffff', '2fffe', '2ffff', '3fffe', '3ffff', '4fffe', '4ffff', '5fffe', '5ffff', '6fffe', '6ffff', '7fffe', '7ffff', '8fffe', '8ffff', '9fffe', '9ffff', 'afffe', 'affff', 'bfffe', 'bffff', 'cfffe', 'cffff', 'dfffe', 'dffff', 'efffe', 'effff', 'ffffe', 'fffff', '10fffe' and '10ffff'
+
+
+-- 5.2 Valid attribute-element combinations -----------------------o
+
+
+ Valid attribute-element combinations as per W3C:- http://www.w3c.org specs.
+
+ * includes deprecated attributes (marked '^'), attributes for the non-standard 'embed' element (marked '*'), and the proprietary 'bordercolor' (marked '~')
+ * only non-frameset, HTML body elements
+ * 'name' for 'a' and 'map', and 'lang' are invalid in XHTML 1.1
+ * 'target' is valid for 'a' in XHTML 1.1 and higher
+ * 'xml:space' is only for XHTML 1.1
+
+ abbr - td, th
+ accept - form, input
+ accept-charset - form
+ accesskey - a, area, button, input, label, legend, textarea
+ action - form
+ align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr
+ alt - applet, area, img, input
+ archive - applet, object
+ axis - td, th
+ bgcolor - embed, table^, tr^, td^, th^
+ border - table, img^, object^
+ bordercolor~ - table, td, tr
+ cellpadding - table
+ cellspacing - table
+ char - col, colgroup, tbody, td, tfoot, th, thead, tr
+ charoff - col, colgroup, tbody, td, tfoot, th, thead, tr
+ charset - a, script
+ checked - input
+ cite - blockquote, q, del, ins
+ classid - object
+ clear - br^
+ code - applet
+ codebase - object, applet
+ codetype - object
+ color - font
+ cols - textarea
+ colspan - td, th
+ compact - dir, dl^, menu, ol^, ul^
+ coords - area, a
+ data - object
+ datetime - del, ins
+ declare - object
+ defer - script
+ dir - bdo
+ disabled - button, input, optgroup, option, select, textarea
+ enctype - form
+ face - font
+ flashvars* - embed
+ for - label
+ frame - table
+ frameborder - iframe
+ headers - td, th
+ height - embed, iframe, td^, th^, img, object, applet
+ href - a, area
+ hreflang - a
+ hspace - applet, img^, object^
+ ismap - img, input
+ label - option, optgroup
+ language - script^
+ longdesc - img, iframe
+ marginheight - iframe
+ marginwidth - iframe
+ maxlength - input
+ method - form
+ model* - embed
+ multiple - select
+ name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param
+ nohref - area
+ noshade - hr^
+ nowrap - td^, th^
+ object - applet
+ onblur - a, area, button, input, label, select, textarea
+ onchange - input, select, textarea
+ onfocus - a, area, button, input, label, select, textarea
+ onreset - form
+ onselect - input, textarea
+ onsubmit - form
+ pluginspage* - embed
+ pluginurl* - embed
+ prompt - isindex
+ readonly - textarea, input
+ rel - a
+ rev - a
+ rows - textarea
+ rowspan - td, th
+ rules - table
+ scope - td, th
+ scrolling - iframe
+ selected - option
+ shape - area, a
+ size - hr^, font, input, select
+ span - col, colgroup
+ src - embed, script, input, iframe, img
+ standby - object
+ start - ol^
+ summary - table
+ tabindex - a, area, button, input, object, select, textarea
+ target - a^, area, form
+ type - a, embed, object, param, script, input, li^, ol^, ul^, button
+ usemap - img, input, object
+ valign - col, colgroup, tbody, td, tfoot, th, thead, tr
+ value - input, option, param, button, li^
+ valuetype - param
+ vspace - applet, img^, object^
+ width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^
+ wmode - embed
+ xml:space - pre, script, style
+
+ These are allowed in all but the shown elements:
+
+ class - param, script
+ dir - applet, bdo, br, iframe, param, script
+ id - script
+ lang - applet, br, iframe, param, script
+ onclick - applet, bdo, br, font, iframe, isindex, param, script
+ ondblclick - applet, bdo, br, font, iframe, isindex, param, script
+ onkeydown - applet, bdo, br, font, iframe, isindex, param, script
+ onkeypress - applet, bdo, br, font, iframe, isindex, param, script
+ onkeyup - applet, bdo, br, font, iframe, isindex, param, script
+ onmousedown - applet, bdo, br, font, iframe, isindex, param, script
+ onmousemove - applet, bdo, br, font, iframe, isindex, param, script
+ onmouseout - applet, bdo, br, font, iframe, isindex, param, script
+ onmouseover - applet, bdo, br, font, iframe, isindex, param, script
+ onmouseup - applet, bdo, br, font, iframe, isindex, param, script
+ style - param, script
+ title - param, script
+ xml:lang - applet, br, iframe, param, script
+
+
+-- 5.3 CSS 2.1 properties accepting URLs ------------------------o
+
+
+ background
+ background-image
+ content
+ cue-after
+ cue-before
+ cursor
+ list-style
+ list-style-image
+ play-during
+
+
+-- 5.4 Microsoft Windows 1252 character replacements --------------o
+
+
+ Key: 'd' double, 'l' left, 'q' quote, 'r' right, 's.' single
+
+ Code-point (decimal) - hexadecimal value - replacement entity - represented character
+
+ 127 - 7f - (removed) - (not used)
+ 128 - 80 - &#8364; - euro
+ 129 - 81 - (removed) - (not used)
+ 130 - 82 - &#8218; - baseline s. q
+ 131 - 83 - &#402; - florin
+ 132 - 84 - &#8222; - baseline d q
+ 133 - 85 - &#8230; - ellipsis
+ 134 - 86 - &#8224; - dagger
+ 135 - 87 - &#8225; - d dagger
+ 136 - 88 - &#710; - circumflex accent
+ 137 - 89 - &#8240; - permile
+ 138 - 8a - &#352; - S Hacek
+ 139 - 8b - &#8249; - l s. guillemet
+ 140 - 8c - &#338; - OE ligature
+ 141 - 8d - (removed) - (not used)
+ 142 - 8e - &#381; - Z dieresis
+ 143 - 8f - (removed) - (not used)
+ 144 - 90 - (removed) - (not used)
+ 145 - 91 - &#8216; - l s. q
+ 146 - 92 - &#8217; - r s. q
+ 147 - 93 - &#8220; - l d q
+ 148 - 94 - &#8221; - r d q
+ 149 - 95 - &#8226; - bullet
+ 150 - 96 - &#8211; - en dash
+ 151 - 97 - &#8212; - em dash
+ 152 - 98 - &#732; - tilde accent
+ 153 - 99 - &#8482; - trademark
+ 154 - 9a - &#353; - s Hacek
+ 155 - 9b - &#8250; - r s. guillemet
+ 156 - 9c - &#339; - oe ligature
+ 157 - 9d - (removed) - (not used)
+ 158 - 9e - &#382; - z dieresis
+ 159 - 9f - &#376; - Y dieresis
+
+
+-- 5.5 URL format -------------------------------------------------o
+
+
+ An `absolute` URL has a 'protocol' or 'scheme', a 'network location' or 'hostname', and, optional 'path', 'parameters', 'query' and 'fragment' segments. Thus, an absolute URL has this generic structure:
+
+ (scheme) : (//network location) /(path) ;(parameters) ?(query) #(fragment)
+
+ The schemes can only contain letters, digits, '+', '.' and '-'. Hostname is the portion after the '//' and up to the first '/' (if any; else, up to the end) when ':' is followed by a '//' (e.g., 'abc.com' in 'ftp://abc.com/def'); otherwise, it consists of everything after the ':' (e.g., 'def@abc.com' in mailto:def@abc.com').
+
+ `Relative` URLs do not have explicit schemes and network locations; such values are inherited from a `base` URL.
+
+
+-- 5.6 Brief on htmLawed code -------------------------------------o
+
+
+ Much of the code's logic and reasoning can be understood from the documentation above.
+
+ The *output* of htmLawed is a text string containing the processed input. There is no custom error tracking.
+
+ *Function arguments* for htmLawed are:
+
+ * '$in' - first argument; a text string; the *input text* to be processed. Any extraneous slashes added by PHP when `magic quotes` are enabled should be removed beforehand using PHP's 'stripslashes()' function.
+
+ * '$config' - second argument; an associative array; optional; named '$C' within htmLawed code. The array has keys with names like 'balance' and 'keep_bad', and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the *configurable parameters* (indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through '$config'. `Finalized` '$config' is thus a filtered and possibly larger array.
+
+ * '$spec' - third argument; a text string; optional. The string has rules, written in an htmLawed-designated format, *specifying* element-specific attribute and attribute value restrictions. Function 'hl_spec()' is used to convert the string to an associative-array, named '$S' within htmLawed code, for internal use. `Finalized` '$spec' is thus an array.
+
+ `Finalized` '$config' and '$spec' are made *global variables* while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the `finalized` values, the 'show_settings' parameter of '$config' should be used). Depending on '$config', another global variable 'hl_Ids', to track 'id' attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.
+
+ Except for the main function 'htmLawed()' and the functions 'kses()' and 'kses_hook()', htmLawed's functions are *name-spaced* using the 'hl_' prefix. The *functions* and their roles are:
+
+ * 'hl_attrval' - checking attribute values against $spec
+ * 'hl_bal' - tag balancing
+ * 'hl_cmtcd' - handling CDATA sections and HTML comments
+ * 'hl_ent' - entity handling
+ * 'hl_prot' - checking a URL scheme/protocol
+ * 'hl_regex' - checking syntax of a regular expression
+ * 'hl_spec' - converting user-supplied $spec value to one used by htmLawed internally
+ * 'hl_tag' - handling tags
+ * 'hl_tag2' - transforming tags
+ * 'hl_tidy' - compact/beautify HTML
+ * 'hl_version' - reporting htmLawed version
+ * 'htmLawed' - main function
+ * 'kses' - main function of 'kses'
+ * 'kses_hook' - hook function of 'kses'
+
+ The last two are for compatibility with pre-existing code using the 'kses' script. htmLawed's 'kses()' basically passes on the filtering task to 'htmLawed()' function after deciphering '$config' and '$spec' from the argument values supplied to it. 'kses_hook()' is an empty function and is meant for being filled with custom code if the 'kses' script users were using one.
+
+ 'htmLawed()' finalizes '$spec' (with the help of 'hl_spec()') and '$config', and globalizes them. Finalization of '$config' involves setting default values if an inappropriate or invalid one is supplied. This includes calling 'hl_regex()' to check well-formedness of regular expression patterns if such expressions are user-supplied through '$config'. 'htmLawed()' then removes invalid characters like nulls and 'x01' and appropriately handles entities using 'hl_ent()'. HTML comments and CDATA sections are identified and treated as per '$config' with the help of 'hl_cmtcd()'. When retained, the '<' and '>' characters identifying them, and the '<', '>' and '&' characters inside them, are replaced with control characters (code-points '1' to '5') till any tag balancing is completed.
+
+ After this `initial processing` 'htmLawed()' identifies tags using regex and processes them with the help of 'hl_tag()' -- a large function that analyzes tag content, filtering it as per HTML standards, '$config' and '$spec'. Among other things, 'hl_tag()' transforms deprecated elements using 'hl_tag2()', removes attributes from closing tags, checks attribute values as per '$spec' rules using 'hl_attrval()', and checks URL protocols using 'hl_prot()'. 'htmLawed()' performs tag balancing and nesting checks with a call to 'hl_bal()', and optionally compacts/beautifies the output with proper white-spacing with a call to 'hl_tidy()'. The latter temporarily replaces white-space, and '<', '>' and '&' characters inside 'pre', 'script' and 'textarea' elements, and HTML comments and CDATA sections with control characters (code-points '1' to '5', and '7').
+
+ htmLawed permits the use of custom code or *hook functions* at two stages. The first, called inside 'htmLawed()', allows the input text as well as the finalized '$config' and '$spec' values to be altered right after the initial processing (see section:- #3.7). The second is called by 'hl_tag()' once the tag content is finalized (see section:- #3.4.9).
+
+ The functionality of htmLawed is dictated by the external HTML standard. It is thus coded for a clear-cut objective with not much concern for tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specifications will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid a quick grasp of the logic.
+
+___________________________________________________________________oo
+
+
+@@description: htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter
+@@encoding: utf-8
+@@keywords: htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements
+@@language: en
@@title: htmLawed documentation \ No newline at end of file
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt
index 793a5a6a7..c5cccaaba 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt
@@ -1,8 +1,8 @@
/*
-htmLawed_TESTCASE.txt, 22 October 2011
-htmLawed 1.1.11, 5 June 2012
+htmLawed_TESTCASE.txt, 27 August 2013
+htmLawed 1.1.16, 29 August 2013
Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
+Dual licensed with LGPL 3 and GPL 2+
A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
*/
@@ -27,6 +27,8 @@ character encoding to Unicode/UTF-8
<strong>Duplicated:</strong> <a id="id5" id="id6">a</a><br />
<strong>Deprecated:</strong> <a id="id7" target="self" name="n">a</a>, <hr noshade="noshade" /><br />
<strong>Casing:</strong> <a HREF=""></a><br />
+<strong>Custom:</strong> <img alt="image" my:data="portrait" /><br />
+<strong>Data-*:</strong> <a data-xml="x" data-xmnt="x" data-xmlnt="x" data-xmn:t="x" data-xmxm="x">a</a><br />
<strong>Admin-restricted?:</strong> <a href="x" onclick="alert();"></a>
<h6>Attribute values</h6>
@@ -46,6 +48,11 @@ character encoding to Unicode/UTF-8
<blockquote><div>abc</div>def</blockquote><br />
<blockquote>abc<div>def</div>ghi</blockquote><br />
abc<div>def</div>ghi<br />
+<blockquote>QQQ<div>x</div><!-- comment --></blockquote><br />
+<blockquote><div>x</div><!-- comment -->QQQ</blockquote><br />
+<blockquote><!-- comment --><div>x</div>QQQ<div>x</div></blockquote><br />
+<blockquote><div>x<!-- comment --></div>QQQ</blockquote><p>x</p><br />
+<br />
(try with blockquote parent)
<h6>CDATA sections</h6>
@@ -128,8 +135,9 @@ Disallowed tag p
<strong>Invalid:</strong> <image src="s" alt="a" /><br />
<strong>Empty:</strong> <img src="s" alt="a" />, <img src="s" alt="a"></img>, <img src="s" alt="a">text</img><br />
<strong>Content invalid:</strong> <a href="h">1<a>2</a></a><br />
-<strong>Content invalid?:</strong> <form></form><br /> (try setting 'form' as parent)
-<strong>Casing:</strong> <A href=""></a>
+<strong>Content invalid?:</strong> <form></form><br /> (try setting 'form' as parent)<br />
+<strong>Casing:</strong> <A href=""></a><br />
+<strong>Check for tidy:</strong> <br /><hr /></div><hr /></div><hr /></div><div>hi</div>
<h6>Entities</h6>
@@ -198,6 +206,13 @@ text <img src="none" alt="none" /> <b>t<em> e <strong> x </strong> t</em></b>
<strong>Malformed:</strong> <![cdata check ]]>, < ![CDATA check ]]>, < ![CDATA check ] ]><br />
Invalid:</strong> <em <!-- check -->>comment in tag content</em>, <!--check-->
+<h6>HTML5</h6>
+
+<strong>figure and figcaption:</strong> <figure><img src="picture.jpg" alt="picture"><figcaption>Caption for the awesome picture</figcaption></figure>
+<strong>article:</strong> <h1>A</h1><p>B</p><article><h2>C</h2></article><article><h2>E</h2><p>F</p><p>G</p></article>
+<strong>meter</strong>: <p>Heat <meter min="100" max="200" value="150">150</meter>.</p>
+<strong>datalist</strong>: <input list="b" /><datalist id="b"><option value="c"><option value="d"></datalist>
+
<h6>Ins-Del</h6>
(depending on context, these elements can be of either block or inline type)<br />
@@ -258,6 +273,10 @@ Invalid:</strong> <em <!-- check -->>comment in tag content</em>, <!--check-->
</form>
</li></ul>
</td></tr></table></li></ol>
+<strong>Menu</strong>: <menu type="toolbar"><li><menu label="File">
+ <button type="button" onclick="new()">New...</button>
+ </menu></li><li><menu label="Edit"><button type="button" onclick="cut()">Cut...</button></menu></li>
+ </menu>
<h6>Microdata</h6>
@@ -266,6 +285,16 @@ I am <span itemprop="name">X</span> but people call me <span itemprop="nickname"
Find me at <a href="http://www.xy.com" itemprop="url">www.xy.com</a>
</div>
+<h6>Microsoft Word</h6>
+
+<strong>Proprietary tag</strong>: <p class=3DMsoNormal><o:p>&nbsp;</o:p></p><br />
+<strong>XML declaration</strong>: <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><br />
+<strong>XML-invalid character code-point (may not replicate)</strong>: <p class=3DMsoNormal>“Where is he?” asked both Mary – the one so lovely – and Jane.</p>
+
+<h6>Nesting</h6>
+
+<strong>Block or inline a</strong>: <p><a href="link">text</a></p><a href="link"><div>hi</div></a><br />
+
<h6>Non-English text-1</h6>
Inscrieţi-vă acum la a Zecea Conferinţă Internaţională<br />
@@ -313,6 +342,7 @@ na Alemanha.
<rp>(</rp><rt>aaa</rt><rp>)</rp>
</ruby>
+
<h6>Tables</h6>
<strong>Omitted closing tags:</strong> <table>
@@ -339,6 +369,14 @@ na Alemanha.
<tr><td>r2c1<td>r2c2
</table><br />
+<h6>Tag transformation</h6>
+<strong>Font element intended as 'inline' element:</strong> <p><font color='red'>hi</font></p><br />
+<strong>Font element intended as 'block' element:</strong> <div><font color='red'><div>hi</div></font></div><br />
+<strong>Font element intended as 'block' element:</strong> <center><font color='red'><div>hi</div><div>QQQ</div></font></center><br />
+
+<h6>Tidy</h6>
+<strong>White-space handling:</strong> abc<em> def </em> ghi abc <em>def</em> ghi
+
<h6>URLs</h6>
<strong>Relative and absolute:</strong> <a href="mailto:x"></a>, <a href="http://a.com/b/c/d.f"></a>, <a href="./../d.f"></a>, <a href="./d.f"></a>, <a href="d.f"></a>, <a href="#s"></a>, <a href="./../../d.f#s"></a><br />
@@ -364,6 +402,7 @@ src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#1
<a style=";-moz-binding:url(http://lukasz.pilorz.net/xss/xss.xml#xss)" href="http://example.com">test</a><br />
<strong>Bad IE7:</strong> <a href="http://x&x=%22+style%3d%22background-image%3a+expression%28alert
%28%27xss%3f%29%29">x</a><br />
+<strong>Opera:</strong> <a href="\xE2\x80\x83javascript:alert(123)">link</a>
<strong>Bad IE7:</strong> <a style=color:expr/*comment*/ession(alert(document.domain))>xxx</a><br />
<strong>Bad IE7:</strong> <a href="xxx" style="background: exp&#x72;ession(alert('xss'));">xxx</a><br />
<strong>Bad IE7:</strong> <a href="xxx" style="background: &#101;xpression(alert('xss'));">xxx</a><br />
@@ -393,4 +432,19 @@ script:eval(document.all.mycode.expr)')">hi</a><br />
3 < 4 <br />
3 > 4 <br />
- > 3 <br /> \ No newline at end of file
+ > 3 <br />
+<._.> hi! <br />
+<<< ALERT >>> <br />
+<![if !vml]> some stuff <![endif]> <br />
+<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> <br />
+<uml:ns ns = "urn:www"> <br />
+<uml:ns ns = 'urn:www'> <br />
+if(13<age AND 21>age){say 'teen'} <br />
+age >51 and a smoking history of >51 pack-years <b>was</b> <br />
+age > 51 and a smoking history of >51 pack-years <b>was</b> <br />
+age <51 and a smoking history of <51 pack-years <b>was</b> <br />
+age < 51 and a smoking history of < 51 pack-years <b>was</b> <br />
+<b>age >51 and a smoking history of >51 pack-years</b> <br />
+<b>age > 51 and a smoking history of >51 pack-years</b> <br />
+<b>age <51 and a smoking history of <51 pack-years</b> <br />
+<b>age < 51 and a smoking history of < 51 pack-years</b> <br /> \ No newline at end of file
diff --git a/mod/logbrowser/views/default/forms/logbrowser/refine.php b/mod/logbrowser/views/default/forms/logbrowser/refine.php
index ebf7f10ed..3d081c9c2 100644
--- a/mod/logbrowser/views/default/forms/logbrowser/refine.php
+++ b/mod/logbrowser/views/default/forms/logbrowser/refine.php
@@ -9,12 +9,12 @@
* @uses $vars['timeupper']
*/
-if (isset($vars['timelower'])) {
+if (isset($vars['timelower']) && $vars['timelower']) {
$lowerval = date('r', $vars['timelower']);
} else {
$lowerval = "";
}
-if (isset($vars['timeupper'])) {
+if (isset($vars['timeupper']) && $vars['timeupper']) {
$upperval = date('r', $vars['timeupper']);
} else {
$upperval = "";
diff --git a/mod/logbrowser/views/default/logbrowser/refine.php b/mod/logbrowser/views/default/logbrowser/refine.php
index 86460c79e..b40f23fa3 100644
--- a/mod/logbrowser/views/default/logbrowser/refine.php
+++ b/mod/logbrowser/views/default/logbrowser/refine.php
@@ -19,7 +19,7 @@ $toggle_link = elgg_view('output/url', array(
));
$form_class = 'elgg-module elgg-module-inline';
-if (!isset($vars['user_guid'])) {
+if (!isset($vars['user_guid']) && !isset($vars['username'])) {
$form_class .= ' hidden';
}
diff --git a/mod/logbrowser/views/default/logbrowser/table.php b/mod/logbrowser/views/default/logbrowser/table.php
index 1223c1456..b08a0c428 100644
--- a/mod/logbrowser/views/default/logbrowser/table.php
+++ b/mod/logbrowser/views/default/logbrowser/table.php
@@ -35,7 +35,7 @@ $log_entries = $vars['log_entries'];
'is_trusted' => true,
));
$user_guid_link = elgg_view('output/url', array(
- 'href' => "admin/overview/logbrowser?user_guid=$user->guid",
+ 'href' => "admin/administer_utilities/logbrowser?user_guid={$user->guid}",
'text' => $user->getGUID(),
'is_trusted' => true,
));
diff --git a/mod/logrotate/languages/en.php b/mod/logrotate/languages/en.php
index 27731d732..d785ad50d 100644
--- a/mod/logrotate/languages/en.php
+++ b/mod/logrotate/languages/en.php
@@ -20,9 +20,10 @@ $english = array(
'logrotate:week' => 'week',
'logrotate:month' => 'month',
'logrotate:year' => 'year',
+ 'logrotate:never' => 'never',
'logrotate:logdeleted' => "Log deleted\n",
- 'logrotate:lognotdeleted' => "Error deleting log\n",
+ 'logrotate:lognotdeleted' => "No logs deleted\n",
);
add_translation("en", $english);
diff --git a/mod/logrotate/start.php b/mod/logrotate/start.php
index 28f14ad14..f67e419bc 100644
--- a/mod/logrotate/start.php
+++ b/mod/logrotate/start.php
@@ -21,8 +21,11 @@ function logrotate_init() {
// Register cron hook for archival of logs
elgg_register_plugin_hook_handler('cron', $period, 'logrotate_archive_cron');
- // Register cron hook for deletion of selected archived logs
- elgg_register_plugin_hook_handler('cron', $delete, 'logrotate_delete_cron');
+
+ if ($delete != 'never') {
+ // Register cron hook for deletion of selected archived logs
+ elgg_register_plugin_hook_handler('cron', $delete, 'logrotate_delete_cron');
+ }
}
/**
@@ -88,34 +91,32 @@ function logrotate_delete_cron($hook, $entity_type, $returnvalue, $params) {
/**
* This function deletes archived copies of the system logs that are older than specified.
*
- * @param int $time_of_delete An offset in seconds from now to delete (useful for log deletion)
+ * @param int $time_of_delete An offset in seconds from now to delete log tables
+ * @return bool Were any log tables deleted
*/
function log_browser_delete_log($time_of_delete) {
global $CONFIG;
- $offset = (int)$time_of_delete;
- $now = time();
-
- $ts = $now - $offset;
-
- $FLAG = 1;
- $result = mysql_query("SHOW TABLES like '{$CONFIG->dbprefix}system_log_%'");
- while ($showtablerow = mysql_fetch_array($result)) {
- //To obtain time of archival
- $log_time = explode("{$CONFIG->dbprefix}system_log_", $showtablerow[0]);
- if ($log_time < $ts) {
- //If the time of archival is before the required offset then delete
- if (!mysql_query("DROP TABLE $showtablerow[0]")) {
- $FLAG = 0;
- }
+ $cutoff = time() - (int)$time_of_delete;
+
+ $deleted_tables = false;
+ $results = get_data("SHOW TABLES like '{$CONFIG->dbprefix}system_log_%'");
+ if ($results) {
+ foreach ($results as $result) {
+ $data = (array)$result;
+ $table_name = array_shift($data);
+ // extract log table rotation time
+ $log_time = str_replace("{$CONFIG->dbprefix}system_log_", '', $table_name);
+ if ($log_time < $cutoff) {
+ if (delete_data("DROP TABLE $table_name") !== false) {
+ // delete_data returns 0 when dropping a table (false for failure)
+ $deleted_tables = true;
+ } else {
+ elgg_log("Failed to delete the log table $table_name", 'ERROR');
+ }
+ }
}
}
- //Check if the appropriate tables have been deleted and return true if yes
- if ($FLAG) {
- return true;
- } else {
- return false;
- }
-
+ return $deleted_tables;
}
diff --git a/mod/logrotate/views/default/plugins/logrotate/settings.php b/mod/logrotate/views/default/plugins/logrotate/settings.php
index bef8b308d..9fd3e08df 100644
--- a/mod/logrotate/views/default/plugins/logrotate/settings.php
+++ b/mod/logrotate/views/default/plugins/logrotate/settings.php
@@ -40,6 +40,7 @@ if (!$delete) {
'weekly' => elgg_echo('logrotate:week'),
'monthly' => elgg_echo('logrotate:month'),
'yearly' => elgg_echo('logrotate:year'),
+ 'never' => elgg_echo('logrotate:never'),
),
'value' => $delete,
));
diff --git a/mod/members/pages/members/search.php b/mod/members/pages/members/search.php
index 1f0444d67..5466a8246 100644
--- a/mod/members/pages/members/search.php
+++ b/mod/members/pages/members/search.php
@@ -7,7 +7,9 @@
if ($vars['search_type'] == 'tag') {
$tag = get_input('tag');
- $title = elgg_echo('members:title:searchtag', array($tag));
+ $display_query = _elgg_get_display_query($tag);
+
+ $title = elgg_echo('members:title:searchtag', array($display_query));
$options = array();
$options['query'] = $tag;
@@ -28,7 +30,9 @@ if ($vars['search_type'] == 'tag') {
} else {
$name = sanitize_string(get_input('name'));
- $title = elgg_echo('members:title:searchname', array($name));
+ $display_query = _elgg_get_display_query($name);
+
+ $title = elgg_echo('members:title:searchname', array($display_query));
$db_prefix = elgg_get_config('dbprefix');
$params = array(
diff --git a/mod/messageboard/pages/messageboard/owner.php b/mod/messageboard/pages/messageboard/owner.php
index 2c854d4f3..b3e9f45b0 100644
--- a/mod/messageboard/pages/messageboard/owner.php
+++ b/mod/messageboard/pages/messageboard/owner.php
@@ -16,7 +16,6 @@ elgg_push_breadcrumb($page_owner->name, $page_owner->getURL());
$options = array(
'annotations_name' => 'messageboard',
'guid' => $page_owner_guid,
- 'limit' => 10,
'reverse_order_by' => true,
);
diff --git a/mod/messages/start.php b/mod/messages/start.php
index 2bf0526da..5c2cb8d0d 100644
--- a/mod/messages/start.php
+++ b/mod/messages/start.php
@@ -51,6 +51,9 @@ function messages_init() {
elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'messages_notification_msg');
register_notification_object('object', 'messages', elgg_echo('messages:new'));
+ // delete messages sent by a user when user is deleted
+ elgg_register_event_handler('delete', 'user', 'messages_purge');
+
// ecml
elgg_register_plugin_hook_handler('get_views', 'ecml', 'messages_ecml_views_hook');
@@ -428,6 +431,39 @@ function messages_user_hover_menu($hook, $type, $return, $params) {
return $return;
}
+/**
+ * Delete messages from a user who is being deleted
+ *
+ * @param string $event Event name
+ * @param string $type Event type
+ * @param ElggUser $user User being deleted
+ */
+function messages_purge($event, $type, $user) {
+
+ if (!$user->getGUID()) {
+ return;
+ }
+
+ // make sure we delete them all
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'messages',
+ 'metadata_name' => 'fromId',
+ 'metadata_value' => $user->getGUID(),
+ 'limit' => 0,
+ );
+ $batch = new ElggBatch('elgg_get_entities_from_metadata', $options);
+ foreach ($batch as $e) {
+ $e->delete();
+ }
+
+ elgg_set_ignore_access($ia);
+ access_show_hidden_entities($entity_disable_override);
+}
/**
* Register messages with ECML.
diff --git a/mod/oauth_api/manifest.xml b/mod/oauth_api/manifest.xml
deleted file mode 100644
index 991be6a22..000000000
--- a/mod/oauth_api/manifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
- <name>OAuth API</name>
- <author>Core developers</author>
- <version>1.8</version>
- <description>Provides OAuth libraries and API support.</description>
- <category>bundled</category>
- <category>api</category>
- <website>http://www.elgg.org/</website>
- <copyright>See COPYRIGHT.txt</copyright>
- <license>GNU General Public License version 2</license>
- <requires>
- <type>elgg_release</type>
- <version>1.8</version>
- </requires>
-
- <conflicts>
- <type>plugin</type>
- <name>oauth_lib</name>
- </conflicts>
- <conflicts>
- <type>php_extension</type>
- <name>oauth</name>
- </conflicts>
-</plugin_manifest>
diff --git a/mod/oauth_api/start.php b/mod/oauth_api/start.php
deleted file mode 100644
index d087a13d1..000000000
--- a/mod/oauth_api/start.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-/**
- * OAuth libs
- *
- * @todo Pull these out into an elgg_oauth lib and use elgg_register_library().
- * @package oauth_api
- */
-
-// require all vendor libraries
-$plugin_path = dirname(__FILE__) . '/vendors/oauth/library';
-require_once "$plugin_path/OAuthDiscovery.php";
-require_once "$plugin_path/OAuthRequest.php";
-require_once "$plugin_path/OAuthRequester.php";
-require_once "$plugin_path/OAuthRequestVerifier.php";
-require_once "$plugin_path/OAuthServer.php";
-
-require_once "$plugin_path/body/OAuthBodyMultipartFormdata.php";
-
-require_once "$plugin_path/store/OAuthStoreAbstract.class.php";
-
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_HMAC_SHA1.php";
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_MD5.php";
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_PLAINTEXT.php";
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_RSA_SHA1.php";
diff --git a/mod/oauth_api/vendors/oauth/LICENSE b/mod/oauth_api/vendors/oauth/LICENSE
deleted file mode 100644
index f64bcd50f..000000000
--- a/mod/oauth_api/vendors/oauth/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) 2007-2008 Mediamatic Lab
-
-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. \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/INSTALL b/mod/oauth_api/vendors/oauth/example/server/INSTALL
deleted file mode 100644
index 249c85e9d..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/INSTALL
+++ /dev/null
@@ -1,53 +0,0 @@
-In this example I assume that oauth-php lives in /home/john/src/oauth-php
-
-
-1) Create a virtual host and set the DB_DSN VARIABLE to the DSN of your (mysql) database.
-
-Example
-<VirtualHost *>
- ServerAdmin admin@localhost
- ServerName hello.local
- DocumentRoot /home/john/src/oauth-php/example/server/www
-
- UseCanonicalName Off
- ServerSignature On
-
- SetEnv DB_DSN mysql://foo:bar@localhost/oauth_example_server_db
-
- <Directory "home/john/src/oauth-php/example/server/www">
- Options Indexes FollowSymLinks MultiViews
- AllowOverride None
- Order allow,deny
- Allow from all
-
- <IfModule mod_php5.c>
- php_value magic_quotes_gpc 0
- php_value register_globals 0
- php_value session.auto_start 0
- </IfModule>
-
- </Directory>
-</VirtualHost>
-
-
-2) Create the database structure for the server:
-
-# mysql -u foo -p bar -h localhost < /home/john/src/oauth-php/library/store/mysql/mysql.sql
-
-
-
-3) Download and install smarty into the smarty/core/smarty directory:
-
-# cd /home/john/src/oauth-php/example/server/core
-# wget 'http://www.smarty.net/do_download.php?download_file=Smarty-2.6.19.tar.gz'
-# tar zxf Smarty-2.6.19.tar.gz
-# mv Smarty-2.6.19 smarty
-
-
-4) That's it! Point your browser to
-
- http://hello.local/
-
-To get started.
-
-Arjan Scherpenisse <arjan@mediamatic.nl>, July 2008
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/init.php b/mod/oauth_api/vendors/oauth/example/server/core/init.php
deleted file mode 100644
index e5bb9de35..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/init.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * Global initialization file for the server, defines some helper
- * functions, required includes, and starts the session.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-
-/*
- * Simple 'user management'
- */
-define ('USERNAME', 'sysadmin');
-define ('PASSWORD', 'sysadmin');
-
-
-/*
- * Always announce XRDS OAuth discovery
- */
-header('X-XRDS-Location: http://' . $_SERVER['SERVER_NAME'] . '/services.xrds');
-
-
-/*
- * Initialize the database connection
- */
-$info = parse_url(getenv('DB_DSN'));
-($GLOBALS['db_conn'] = mysql_connect($info['host'], $info['user'], $info['pass'])) || die(mysql_error());
-mysql_select_db(basename($info['path']), $GLOBALS['db_conn']) || die(mysql_error());
-unset($info);
-
-
-require_once '../../../library/OAuthServer.php';
-
-/*
- * Initialize OAuth store
- */
-require_once '../../../library/OAuthStore.php';
-OAuthStore::instance('MySQL', array('conn' => $GLOBALS['db_conn']));
-
-
-/*
- * Session
- */
-session_start();
-
-
-/*
- * Template handling
- */
-require_once 'smarty/libs/Smarty.class.php';
-function session_smarty()
-{
- if (!isset($GLOBALS['smarty']))
- {
- $GLOBALS['smarty'] = new Smarty;
- $GLOBALS['smarty']->template_dir = dirname(__FILE__) . '/templates/';
- $GLOBALS['smarty']->compile_dir = dirname(__FILE__) . '/../cache/templates_c';
- }
-
- return $GLOBALS['smarty'];
-}
-
-function assert_logged_in()
-{
- if (empty($_SESSION['authorized']))
- {
- $uri = $_SERVER['REQUEST_URI'];
- header('Location: /logon?goto=' . urlencode($uri));
- }
-}
-
-function assert_request_vars()
-{
- foreach(func_get_args() as $a)
- {
- if (!isset($_REQUEST[$a]))
- {
- header('HTTP/1.1 400 Bad Request');
- echo 'Bad request.';
- exit;
- }
- }
-}
-
-function assert_request_vars_all()
-{
- foreach($_REQUEST as $row)
- {
- foreach(func_get_args() as $a)
- {
- if (!isset($row[$a]))
- {
- header('HTTP/1.1 400 Bad Request');
- echo 'Bad request.';
- exit;
- }
- }
- }
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl
deleted file mode 100644
index 308b1d01b..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl
+++ /dev/null
@@ -1,2 +0,0 @@
-</body>
-</html>
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl
deleted file mode 100644
index 5046f54b0..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl
+++ /dev/null
@@ -1,2 +0,0 @@
-<html>
- <body>
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl
deleted file mode 100644
index 7b065537d..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl
+++ /dev/null
@@ -1,13 +0,0 @@
-{include file='inc/header.tpl'}
-
-<h1>OAuth server</h1>
-Go to:
-
-<ul>
- <li><a href="/logon">Logon</a></li>
- <li><a href="/register">Register your consumer</a></li>
-</ul>
-
-Afterwards, make an OAuth test request to <strong>http://{$smarty.server.name}/hello</strong> to test your connection.</p>
-
-{include file='inc/footer.tpl'}
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl
deleted file mode 100644
index 5ccd432b5..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl
+++ /dev/null
@@ -1,21 +0,0 @@
-{include file='inc/header.tpl'}
-
-<h1>Login</h1>
-
-<form method="post">
- <input type="hidden" name="goto" value="{$smarty.request.goto}" />
-
- <label for="username">User name</label><br />
- <input type="text" name="username" id="username" />
-
- <br /><br />
-
- <label for="password">Password</label><br />
- <input type="text" name="password" id="password" />
-
- <br /><br />
-
- <input type="submit" value="Login" />
-</form>
-
-{include file='inc/footer.tpl'}
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl
deleted file mode 100644
index 0e28c1584..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl
+++ /dev/null
@@ -1,41 +0,0 @@
-{include file='inc/header.tpl'}
-
-<h1>Register server</h1>
-
-<p>Register a server which is gonna act as an identity client.</p>
-
-<form method="post">
-
- <fieldset>
- <legend>About You</legend>
-
- <p>
- <label for="requester_name">Your name</label><br/>
- <input class="text" id="requester_name" name="requester_name" type="text" value="{$consumer.requester_name|default:$smarty.request.requester_name|escape}" />
- </p>
-
- <p>
- <label for="requester_email">Your email address</label><br/>
- <input class="text" id="requester_email" name="requester_email" type="text" value="{$consumer.requester_email|default:$smarty.request.requester_email|escape}" />
- </p>
- </fieldset>
-
- <fieldset>
- <legend>Location Of Your Application Or Site</legend>
-
- <p>
- <label for="application_uri">URL of your application or site</label><br/>
- <input id="application_uri" class="text" name="application_uri" type="text" value="{$consumer.application_uri|default:$smarty.request.application_uri|escape}" />
- </p>
-
- <p>
- <label for="callback_uri">Callback URL</label><br/>
- <input id="callback_uri" class="text" name="callback_uri" type="text" value="{$consumer.callback_uri|default:$smarty.request.callback_uri|escape}" />
- </p>
- </fieldset>
-
- <br />
- <input type="submit" value="Register server" />
-</form>
-
-{include file='inc/footer.tpl'}
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/hello.php b/mod/oauth_api/vendors/oauth/example/server/www/hello.php
deleted file mode 100644
index 8cb94bb1e..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/hello.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * An example service, http://hostname/hello. You will only get the
- * 'Hello, world!' string back if you have signed your request with
- * oauth.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once '../core/init.php';
-
-$authorized = false;
-$server = new OAuthServer();
-try
-{
- if ($server->verifyIfSigned())
- {
- $authorized = true;
- }
-}
-catch (OAuthException $e)
-{
-}
-
-if (!$authorized)
-{
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: text/plain');
-
- echo "OAuth Verification Failed: " . $e->getMessage();
- die;
-}
-
-// From here on we are authenticated with OAuth.
-
-header('Content-type: text/plain');
-echo 'Hello, world!';
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/index.php b/mod/oauth_api/vendors/oauth/example/server/www/index.php
deleted file mode 100644
index f5cadbe61..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/index.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require '../core/init.php';
-
-$smarty = session_smarty();
-$smarty->display('index.tpl');
-
-?>
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/logon.php b/mod/oauth_api/vendors/oauth/example/server/www/logon.php
deleted file mode 100644
index 5c937b719..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/logon.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * Simple logon for consumer registration at this server.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once '../core/init.php';
-
-if (isset($_POST['username']) && isset($_POST['password']))
-{
- if ($_POST['username'] == USERNAME && $_POST['password'] == PASSWORD)
- {
- $_SESSION['authorized'] = true;
- if (!empty($_REQUEST['goto']))
- {
- header('Location: ' . $_REQUEST['goto']);
- die;
- }
-
- echo "Logon succesfull.";
- die;
- }
-}
-
-$smarty = session_smarty();
-$smarty->display('logon.tpl');
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/oauth.php b/mod/oauth_api/vendors/oauth/example/server/www/oauth.php
deleted file mode 100644
index e0badcc39..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/oauth.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * This file implements the OAuth server endpoints. The most basic
- * implementation of an OAuth server.
- *
- * Call with: /oauth/request_token, /oauth/authorize, /oauth/access_token
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once '../core/init.php';
-
-$server = new OAuthServer();
-
-switch($_SERVER['PATH_INFO'])
-{
-case '/request_token':
- $server->requestToken();
- exit;
-
-case '/access_token':
- $server->accessToken();
- exit;
-
-case '/authorize':
- # logon
-
- assert_logged_in();
-
- try
- {
- $server->authorizeVerify();
- $server->authorizeFinish(true, 1);
- }
- catch (OAuthException $e)
- {
- header('HTTP/1.1 400 Bad Request');
- header('Content-Type: text/plain');
-
- echo "Failed OAuth Request: " . $e->getMessage();
- }
- exit;
-
-
-default:
- header('HTTP/1.1 500 Internal Server Error');
- header('Content-Type: text/plain');
- echo "Unknown request";
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/register.php b/mod/oauth_api/vendors/oauth/example/server/www/register.php
deleted file mode 100644
index c5785c2c8..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/register.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-require_once '../core/init.php';
-
-assert_logged_in();
-
-if ($_SERVER['REQUEST_METHOD'] == 'POST')
-{
- try
- {
- $store = OAuthStore::instance();
- $key = $store->updateConsumer($_POST, 1, true);
-
- $c = $store->getConsumer($key);
- echo 'Your consumer key is: <strong>' . $c['consumer_key'] . '</strong><br />';
- echo 'Your consumer secret is: <strong>' . $c['consumer_secret'] . '</strong><br />';
- }
- catch (OAuthException $e)
- {
- echo '<strong>Error: ' . $e->getMessage() . '</strong><br />';
- }
-}
-
-
-$smarty = session_smarty();
-$smarty->display('register.tpl');
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php b/mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php
deleted file mode 100644
index 4c50aa12b..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * XRDS discovery for OAuth. This file helps the consumer program to
- * discover where the OAuth endpoints for this server are.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-header('Content-Type: application/xrds+xml');
-
-$server = $_SERVER['SERVER_NAME'];
-
-echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";
-
-?>
-<XRDS xmlns="xri://$xrds">
- <XRD xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0" version="2.0" xml:id="main">
- <Type>xri://$xrds*simple</Type>
- <Service>
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#main</URI>
- </Service>
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
- <URI>http://<?=$server?>/oauth/request_token</URI>
- </Service>
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <URI>http://<?=$server?>/oauth/authorize</URI>
- </Service>
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
- <URI>http://<?=$server?>/oauth/access_token</URI>
- </Service>
- </XRD>
-</XRDS>
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php b/mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php
deleted file mode 100644
index d097756dd..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php
+++ /dev/null
@@ -1,226 +0,0 @@
-<?php
-
-/**
- * Handle the discovery of OAuth service provider endpoints and static consumer identity.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 4, 2008 5:05:19 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__).'/discovery/xrds_parse.php';
-
-require_once dirname(__FILE__).'/OAuthException.php';
-require_once dirname(__FILE__).'/OAuthRequestLogger.php';
-
-
-class OAuthDiscovery
-{
- /**
- * Return a description how we can do a consumer allocation. Prefers static allocation if
- * possible. If static allocation is possible
- *
- * See also: http://oauth.net/discovery/#consumer_identity_types
- *
- * @param string uri
- * @return array provider description
- */
- static function discover ( $uri )
- {
- // See what kind of consumer allocations are available
- $xrds_file = self::discoverXRDS($uri);
- if (!empty($xrds_file))
- {
- $xrds = xrds_parse($xrds_file);
- if (empty($xrds))
- {
- throw new OAuthException('Could not discover OAuth information for '.$uri);
- }
- }
- else
- {
- throw new OAuthException('Could not discover XRDS file at '.$uri);
- }
-
- // Fill an OAuthServer record for the uri found
- $ps = parse_url($uri);
- $host = isset($ps['host']) ? $ps['host'] : 'localhost';
- $server_uri = $ps['scheme'].'://'.$host.'/';
-
- $p = array(
- 'user_id' => null,
- 'consumer_key' => '',
- 'consumer_secret' => '',
- 'signature_methods' => '',
- 'server_uri' => $server_uri,
- 'request_token_uri' => '',
- 'authorize_uri' => '',
- 'access_token_uri' => ''
- );
-
-
- // Consumer identity (out of bounds or static)
- if (isset($xrds['consumer_identity']))
- {
- // Try to find a static consumer allocation, we like those :)
- foreach ($xrds['consumer_identity'] as $ci)
- {
- if ($ci['method'] == 'static' && !empty($ci['consumer_key']))
- {
- $p['consumer_key'] = $ci['consumer_key'];
- $p['consumer_secret'] = '';
- }
- else if ($ci['method'] == 'oob' && !empty($ci['uri']))
- {
- // TODO: Keep this uri somewhere for the user?
- $p['consumer_oob_uri'] = $ci['uri'];
- }
- }
- }
-
- // The token uris
- if (isset($xrds['request'][0]['uri']))
- {
- $p['request_token_uri'] = $xrds['request'][0]['uri'];
- if (!empty($xrds['request'][0]['signature_method']))
- {
- $p['signature_methods'] = $xrds['request'][0]['signature_method'];
- }
- }
- if (isset($xrds['authorize'][0]['uri']))
- {
- $p['authorize_uri'] = $xrds['authorize'][0]['uri'];
- if (!empty($xrds['authorize'][0]['signature_method']))
- {
- $p['signature_methods'] = $xrds['authorize'][0]['signature_method'];
- }
- }
- if (isset($xrds['access'][0]['uri']))
- {
- $p['access_token_uri'] = $xrds['access'][0]['uri'];
- if (!empty($xrds['access'][0]['signature_method']))
- {
- $p['signature_methods'] = $xrds['access'][0]['signature_method'];
- }
- }
- return $p;
- }
-
-
- /**
- * Discover the XRDS file at the uri. This is a bit primitive, you should overrule
- * this function so that the XRDS file can be cached for later referral.
- *
- * @param string uri
- * @return string false when no XRDS file found
- */
- static protected function discoverXRDS ( $uri, $recur = 0 )
- {
- // Bail out when we are following redirects
- if ($recur > 10)
- {
- return false;
- }
-
- $data = self::curl($uri);
-
- // Check what we got back, could be:
- // 1. The XRDS discovery file itself (check content-type)
- // 2. The X-XRDS-Location header
-
- if (is_string($data) && !empty($data))
- {
- list($head,$body) = explode("\r\n\r\n", $data);
- $body = trim($body);
- $m = false;
-
- // See if we got the XRDS file itself or we have to follow a location header
- if ( preg_match('/^Content-Type:\s*application\/xrds+xml/im', $head)
- || preg_match('/^<\?xml[^>]*\?>\s*<xrds\s/i', $body)
- || preg_match('/^<xrds\s/i', $body)
- )
- {
- $xrds = $body;
- }
- else if ( preg_match('/^X-XRDS-Location:\s*(.*)$/im', $head, $m)
- || preg_match('/^Location:\s*(.*)$/im', $head, $m))
- {
- // Recurse to the given location
- if ($uri != $m[1])
- {
- $xrds = self::discoverXRDS($m[1], $recur+1);
- }
- else
- {
- // Referring to the same uri, bail out
- $xrds = false;
- }
- }
- else
- {
- // Not an XRDS file an nowhere else to check
- $xrds = false;
- }
- }
- else
- {
- $xrds = false;
- }
- return $xrds;
- }
-
-
- /**
- * Try to fetch an XRDS file at the given location. Sends an accept header preferring the xrds file.
- *
- * @param string uri
- * @return array (head,body), false on an error
- */
- static protected function curl ( $uri )
- {
- $ch = curl_init();
-
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*;q=0.1'));
- curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - (OAuth Discovery $LastChangedRevision: 45 $)');
- curl_setopt($ch, CURLOPT_URL, $uri);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HEADER, true);
-
- $txt = curl_exec($ch);
- curl_close($ch);
-
- // Tell the logger what we requested and what we received back
- $data = "GET $uri";
- OAuthRequestLogger::setSent($data, "");
- OAuthRequestLogger::setReceived($txt);
-
- return $txt;
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthException.php b/mod/oauth_api/vendors/oauth/library/OAuthException.php
deleted file mode 100644
index cadd1d032..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthException.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * Simple exception wrapper for OAuth
- *
- * @version $Id: OAuthException.php 49 2008-10-01 09:43:19Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 29, 2007 5:33:54 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-// TODO: something with the HTTP return code matching to the problem
-
-require_once dirname(__FILE__) . '/OAuthRequestLogger.php';
-
-class OAuthException extends Exception
-{
- function __construct ( $message )
- {
- Exception::__construct($message);
- OAuthRequestLogger::addNote('OAuthException: '.$message);
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequest.php b/mod/oauth_api/vendors/oauth/library/OAuthRequest.php
deleted file mode 100644
index c0d6ddbc7..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequest.php
+++ /dev/null
@@ -1,801 +0,0 @@
-<?php
-
-/**
- * Request wrapper class. Prepares a request for consumption by the OAuth routines
- *
- * @version $Id: OAuthRequest.php 50 2008-10-01 15:11:08Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 12:20:31 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-
-require_once dirname(__FILE__) . '/OAuthException.php';
-
-/**
- * Object to parse an incoming OAuth request or prepare an outgoing OAuth request
- */
-class OAuthRequest
-{
- /* the realm for this request */
- protected $realm;
-
- /* all the parameters, RFC3986 encoded name/value pairs */
- protected $param = array();
-
- /* the parsed request uri */
- protected $uri_parts;
-
- /* the raw request uri */
- protected $uri;
-
- /* the request headers */
- protected $headers;
-
- /* the request method */
- protected $method;
-
- /* the body of the OAuth request */
- protected $body;
-
-
- /**
- * Construct from the current request. Useful for checking the signature of a request.
- * When not supplied with any parameters this will use the current request.
- *
- * @param string uri might include parameters
- * @param string method GET, PUT, POST etc.
- * @param string parameters additional post parameters, urlencoded (RFC1738)
- * @param array headers headers for request
- * @param string body optional body of the OAuth request (POST or PUT)
- */
- function __construct ( $uri = null, $method = 'GET', $parameters = '', $headers = array(), $body = null )
- {
- if (empty($uri))
- {
- if (is_object($_SERVER))
- {
- // Tainted arrays - the normal stuff in anyMeta
- $method = $_SERVER->REQUEST_METHOD->getRawUnsafe();
- $uri = $_SERVER->REQUEST_URI->getRawUnsafe();
- }
- else
- {
- // non anyMeta systems
- $method = $_SERVER['REQUEST_METHOD'];
- $uri = $_SERVER['REQUEST_URI'];
- }
- $headers = getallheaders();
- $parameters = '';
- $this->method = strtoupper($method);
-
- // If this is a post then also check the posted variables
- if (strcasecmp($method, 'POST') == 0)
- {
- /*
- // TODO: what to do with 'multipart/form-data'?
- if ($this->getRequestContentType() == 'multipart/form-data')
- {
- throw new OAuthException('Unsupported POST content type, expected "application/x-www-form-urlencoded" got "'.@$_SERVER['CONTENT_TYPE'].'"');
- }
- */
- if ($this->getRequestContentType() == 'application/x-www-form-urlencoded')
- {
- // Get the posted body (when available)
- if (!isset($headers['X-OAuth-Test']))
- {
- $parameters .= $this->getRequestBody();
- }
- }
- else
- {
- $body = $this->getRequestBody();
- }
- }
- else if (strcasecmp($method, 'PUT') == 0)
- {
- $body = $this->getRequestBody();
- }
- }
-
- $this->method = strtoupper($method);
- $this->headers = $headers;
- // Store the values, prepare for oauth
- $this->uri = $uri;
- $this->body = $body;
- $this->parseUri($parameters);
- $this->parseHeaders();
- $this->transcodeParams();
- }
-
-
- /**
- * Return the signature base string.
- * Note that we can't use rawurlencode due to specified use of RFC3986.
- *
- * @return string
- */
- function signatureBaseString ()
- {
- $sig = array();
- $sig[] = $this->method;
- $sig[] = $this->getRequestUrl();
- $sig[] = $this->getNormalizedParams();
-
- return implode('&', array_map(array($this, 'urlencode'), $sig));
- }
-
-
- /**
- * Calculate the signature of the request, using the method in oauth_signature_method.
- * The signature is returned encoded in the form as used in the url. So the base64 and
- * urlencoding has been done.
- *
- * @param string consumer_secret
- * @param string token_secret
- * @exception when not all parts available
- * @return string
- */
- function calculateSignature ( $consumer_secret, $token_secret, $token_type = 'access' )
- {
- $required = array(
- 'oauth_consumer_key',
- 'oauth_signature_method',
- 'oauth_timestamp',
- 'oauth_nonce'
- );
-
- if ($token_type !== false)
- {
- $required[] = 'oauth_token';
- }
-
- foreach ($required as $req)
- {
- if (!isset($this->param[$req]))
- {
- throw new OAuthException('Can\'t sign request, missing parameter "'.$req.'"');
- }
- }
-
- $this->checks();
-
- $base = $this->signatureBaseString();
- $signature = $this->calculateDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method']);
- return $signature;
- }
-
-
- /**
- * Calculate the signature of a string.
- * Uses the signature method from the current parameters.
- *
- * @param string data
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature_method
- * @exception OAuthException thrown when the signature method is unknown
- * @return string signature
- */
- function calculateDataSignature ( $data, $consumer_secret, $token_secret, $signature_method )
- {
- if (is_null($data))
- {
- $data = '';
- }
-
- $sig = $this->getSignatureMethod($signature_method);
- return $sig->signature($this, $data, $consumer_secret, $token_secret);
- }
-
-
- /**
- * Select a signature method from the list of available methods.
- * We try to check the most secure methods first.
- *
- * @todo Let the signature method tell us how secure it is
- * @param array methods
- * @exception OAuthException when we don't support any method in the list
- * @return string
- */
- public function selectSignatureMethod ( $methods )
- {
- if (in_array('HMAC-SHA1', $methods))
- {
- $method = 'HMAC-SHA1';
- }
- else if (in_array('MD5', $methods))
- {
- $method = 'MD5';
- }
- else
- {
- $method = false;
- foreach ($methods as $m)
- {
- $m = strtoupper($m);
- $m = preg_replace('/[^A-Z0-9]/', '_', $m);
- if (file_exists(dirname(__FILE__).'/signature_method/OAuthSignatureMethod_'.$m.'.php'))
- {
- $method = $m;
- break;
- }
- }
-
- if (empty($method))
- {
- throw new OAuthException('None of the signing methods is supported.');
- }
- }
- return $method;
- }
-
-
- /**
- * Fetch the signature object used for calculating and checking the signature base string
- *
- * @param string method
- * @return OAuthSignatureMethod object
- */
- function getSignatureMethod ( $method )
- {
- $m = strtoupper($method);
- $m = preg_replace('/[^A-Z0-9]/', '_', $m);
- $class = 'OAuthSignatureMethod_'.$m;
-
- if (file_exists(dirname(__FILE__).'/signature_method/'.$class.'.php'))
- {
- require_once dirname(__FILE__).'/signature_method/'.$class.'.php';
- $sig = new $class();
- }
- else
- {
- throw new OAuthException('Unsupported signature method "'.$m.'".');
- }
- return $sig;
- }
-
-
- /**
- * Perform some sanity checks.
- *
- * @exception OAuthException thrown when sanity checks failed
- */
- function checks ()
- {
- if (isset($this->param['oauth_version']))
- {
- $version = $this->urldecode($this->param['oauth_version']);
- if ($version != '1.0')
- {
- throw new OAuthException('Expected OAuth version 1.0, got "'.$this->param['oauth_version'].'"');
- }
- }
- }
-
-
- /**
- * Return the request method
- *
- * @return string
- */
- function getMethod ()
- {
- return $this->method;
- }
-
- /**
- * Return the complete parameter string for the signature check.
- * All parameters are correctly urlencoded and sorted on name and value
- *
- * @return string
- */
- function getNormalizedParams ()
- {
- /*
- // sort by name, then by value
- // (needed when we start allowing multiple values with the same name)
- $keys = array_keys($this->param);
- $values = array_values($this->param);
- array_multisort($keys, SORT_ASC, $values, SORT_ASC);
- */
- $params = $this->param;
- $normalized = array();
-
- ksort($params);
- foreach ($params as $key => $value)
- {
- // all names and values are already urlencoded, exclude the oauth signature
- if ($key != 'oauth_signature')
- {
- if (is_array($value))
- {
- $value_sort = $value;
- sort($value_sort);
- foreach ($value_sort as $v)
- {
- $normalized[] = $key.'='.$v;
- }
- }
- else
- {
- $normalized[] = $key.'='.$value;
- }
- }
- }
- return implode('&', $normalized);
- }
-
-
- /**
- * Return the normalised url for signature checks
- */
- function getRequestUrl ()
- {
- $url = $this->uri_parts['scheme'] . '://'
- . $this->uri_parts['user'] . (!empty($this->uri_parts['pass']) ? ':' : '')
- . $this->uri_parts['pass'] . (!empty($this->uri_parts['user']) ? '@' : '')
- . $this->uri_parts['host'];
-
- if ( $this->uri_parts['port']
- && $this->uri_parts['port'] != $this->defaultPortForScheme($this->uri_parts['scheme']))
- {
- $url .= ':'.$this->uri_parts['port'];
- }
- if (!empty($this->uri_parts['path']))
- {
- $url .= $this->uri_parts['path'];
- }
- return $url;
- }
-
-
- /**
- * Get a parameter, value is always urlencoded
- *
- * @param string name
- * @param boolean urldecode set to true to decode the value upon return
- * @return string value false when not found
- */
- function getParam ( $name, $urldecode = false )
- {
- if (isset($this->param[$name]))
- {
- $s = $this->param[$name];
- }
- else if (isset($this->param[$this->urlencode($name)]))
- {
- $s = $this->param[$this->urlencode($name)];
- }
- else
- {
- $s = false;
- }
- if (!empty($s) && $urldecode)
- {
- if (is_array($s))
- {
- $s = array_map(array($this,'urldecode'), $s);
- }
- else
- {
- $s = $this->urldecode($s);
- }
- }
- return $s;
- }
-
- /**
- * Set a parameter
- *
- * @param string name
- * @param string value
- * @param boolean encoded set to true when the values are already encoded
- */
- function setParam ( $name, $value, $encoded = false )
- {
- if (!$encoded)
- {
- $name_encoded = $this->urlencode($name);
- if (is_array($value))
- {
- foreach ($value as $v)
- {
- $this->param[$name_encoded][] = $this->urlencode($v);
- }
- }
- else
- {
- $this->param[$name_encoded] = $this->urlencode($value);
- }
- }
- else
- {
- $this->param[$name] = $value;
- }
- }
-
-
- /**
- * Re-encode all parameters so that they are encoded using RFC3986.
- * Updates the $this->param attribute.
- */
- protected function transcodeParams ()
- {
- $params = $this->param;
- $this->param = array();
-
- foreach ($params as $name=>$value)
- {
- if (is_array($value))
- {
- $this->param[$this->urltranscode($name)] = array_map(array($this,'urltranscode'), $value);
- }
- else
- {
- $this->param[$this->urltranscode($name)] = $this->urltranscode($value);
- }
- }
- }
-
-
-
- /**
- * Return the body of the OAuth request.
- *
- * @return string null when no body
- */
- function getBody ()
- {
- return $this->body;
- }
-
-
- /**
- * Return the body of the OAuth request.
- *
- * @return string null when no body
- */
- function setBody ( $body )
- {
- $this->body = $body;
- }
-
-
- /**
- * Parse the uri into its parts. Fill in the missing parts.
- *
- * @todo check for the use of https, right now we default to http
- * @todo support for multiple occurences of parameters
- * @param string $parameters optional extra parameters (from eg the http post)
- */
- protected function parseUri ( $parameters )
- {
- $ps = parse_url($this->uri);
-
- // Get the current/requested method
- if (empty($ps['scheme']))
- {
- $ps['scheme'] = 'http';
- }
- else
- {
- $ps['scheme'] = strtolower($ps['scheme']);
- }
-
- // Get the current/requested host
- if (empty($ps['host']))
- {
- if (isset($_SERVER['HTTP_HOST']))
- {
- $ps['host'] = $_SERVER['HTTP_HOST'];
- }
- else
- {
- $ps['host'] = '';
- }
- }
- $ps['host'] = mb_strtolower($ps['host']);
- if (!preg_match('/^[a-z0-9\.\-]+$/', $ps['host']))
- {
- throw new OAuthException('Unsupported characters in host name');
- }
-
- // Get the port we are talking on
- if (empty($ps['port']))
- {
- $ps['port'] = $this->defaultPortForScheme($ps['scheme']);
- }
-
- if (empty($ps['user']))
- {
- $ps['user'] = '';
- }
- if (empty($ps['pass']))
- {
- $ps['pass'] = '';
- }
- if (empty($ps['path']))
- {
- $ps['path'] = '/';
- }
- if (empty($ps['query']))
- {
- $ps['query'] = '';
- }
- if (empty($ps['fragment']))
- {
- $ps['fragment'] = '';
- }
-
- // Now all is complete - parse all parameters
- foreach (array($ps['query'], $parameters) as $params)
- {
- if (strlen($params) > 0)
- {
- $params = explode('&', $params);
- foreach ($params as $p)
- {
- @list($name, $value) = explode('=', $p, 2);
- $this->param[$name] = $value;
- }
- }
- }
- $this->uri_parts = $ps;
- }
-
-
- /**
- * Return the default port for a scheme
- *
- * @param string scheme
- * @return int
- */
- protected function defaultPortForScheme ( $scheme )
- {
- switch ($scheme)
- {
- case 'http': return 80;
- case 'https': return 43;
- default:
- throw new OAuthException('Unsupported scheme type, expected http or https, got "'.$scheme.'"');
- break;
- }
- }
-
-
- /**
- * Encode a string according to the RFC3986
- *
- * @param string s
- * @return string
- */
- function urlencode ( $s )
- {
- if ($s === false)
- {
- return $s;
- }
- else
- {
- return str_replace('%7E', '~', rawurlencode($s));
- }
- }
-
- /**
- * Decode a string according to RFC3986.
- * Also correctly decodes RFC1738 urls.
- *
- * @param string s
- * @return string
- */
- function urldecode ( $s )
- {
- if ($s === false)
- {
- return $s;
- }
- else
- {
- return rawurldecode($s);
- }
- }
-
- /**
- * urltranscode - make sure that a value is encoded using RFC3986.
- * We use a basic urldecode() function so that any use of '+' as the
- * encoding of the space character is correctly handled.
- *
- * @param string s
- * @return string
- */
- function urltranscode ( $s )
- {
- if ($s === false)
- {
- return $s;
- }
- else
- {
- return $this->urlencode(urldecode($s));
- }
- }
-
-
- /**
- * Parse the oauth parameters from the request headers
- * Looks for something like:
- *
- * Authorization: OAuth realm="http://photos.example.net/authorize",
- * oauth_consumer_key="dpf43f3p2l4k3l03",
- * oauth_token="nnch734d00sl2jdk",
- * oauth_signature_method="HMAC-SHA1",
- * oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",
- * oauth_timestamp="1191242096",
- * oauth_nonce="kllo9940pd9333jh",
- * oauth_version="1.0"
- */
- private function parseHeaders ()
- {
-/*
- $this->headers['Authorization'] = 'OAuth realm="http://photos.example.net/authorize",
- oauth_consumer_key="dpf43f3p2l4k3l03",
- oauth_token="nnch734d00sl2jdk",
- oauth_signature_method="HMAC-SHA1",
- oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",
- oauth_timestamp="1191242096",
- oauth_nonce="kllo9940pd9333jh",
- oauth_version="1.0"';
-*/
- if (isset($this->headers['Authorization']))
- {
- $auth = trim($this->headers['Authorization']);
- if (strncasecmp($auth, 'OAuth', 4) == 0)
- {
- $vs = explode(',', substr($auth, 6));
- foreach ($vs as $v)
- {
- if (strpos($v, '='))
- {
- $v = trim($v);
- list($name,$value) = explode('=', $v, 2);
- if (!empty($value) && $value{0} == '"' && substr($value, -1) == '"')
- {
- $value = substr(substr($value, 1), 0, -1);
- }
-
- if (strcasecmp($name, 'realm') == 0)
- {
- $this->realm = $value;
- }
- else
- {
- $this->param[$name] = $value;
- }
- }
- }
- }
- }
- }
-
-
- /**
- * Fetch the content type of the current request
- *
- * @return string
- */
- private function getRequestContentType ()
- {
- $content_type = 'application/octet-stream';
- if (!empty($_SERVER) && array_key_exists('CONTENT_TYPE', $_SERVER))
- {
- list($content_type) = explode(';', $_SERVER['CONTENT_TYPE']);
- }
- return trim($content_type);
- }
-
-
- /**
- * Get the body of a POST or PUT.
- *
- * Used for fetching the post parameters and to calculate the body signature.
- *
- * @return string null when no body present (or wrong content type for body)
- */
- private function getRequestBody ()
- {
- $body = null;
- if ($this->method == 'POST' || $this->method == 'PUT')
- {
- $body = '';
- $fh = @fopen('php://input', 'r');
- if ($fh)
- {
- while (!feof($fh))
- {
- $s = fread($fh, 1024);
- if (is_string($s))
- {
- $body .= $s;
- }
- }
- fclose($fh);
- }
- }
- return $body;
- }
-
-
- /**
- * Simple function to perform a redirect (GET).
- * Redirects the User-Agent, does not return.
- *
- * @param string uri
- * @param array params parameters, urlencoded
- * @exception OAuthException when redirect uri is illegal
- */
- public function redirect ( $uri, $params )
- {
- if (!empty($params))
- {
- $q = array();
- foreach ($params as $name=>$value)
- {
- $q[] = $name.'='.$value;
- }
- $q_s = implode('&', $q);
-
- if (strpos($uri, '?'))
- {
- $uri .= '&'.$q_s;
- }
- else
- {
- $uri .= '?'.$q_s;
- }
- }
-
- // simple security - multiline location headers can inject all kinds of extras
- $uri = preg_replace('/\s/', '%20', $uri);
- if (strncasecmp($uri, 'http://', 7) && strncasecmp($uri, 'https://', 8))
- {
- if (strpos($uri, '://'))
- {
- throw new OAuthException('Illegal protocol in redirect uri '.$uri);
- }
- $uri = 'http://'.$uri;
- }
-
- header('HTTP/1.1 302 Found');
- header('Location: '.$uri);
- echo '';
- exit();
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php b/mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php
deleted file mode 100644
index 934c1c53c..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php
+++ /dev/null
@@ -1,274 +0,0 @@
-<?php
-
-/**
- * Log OAuth requests
- *
- * @version $Id: OAuthRequestLogger.php 55 2009-01-14 15:27:36Z scherpenisse $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Dec 7, 2007 12:22:43 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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 OAuthRequestLogger
-{
- static private $logging = 0;
- static private $enable_logging = null;
- static private $store_log = null;
- static private $note = '';
- static private $user_id = null;
- static private $request_object = null;
- static private $sent = null;
- static private $received = null;
- static private $log = array();
-
- /**
- * Start any logging, checks the system configuration if logging is needed.
- *
- * @param OAuthRequest $request_object
- */
- static function start ( $request_object = null )
- {
- if (defined('OAUTH_LOG_REQUEST'))
- {
- if (is_null(OAuthRequestLogger::$enable_logging))
- {
- OAuthRequestLogger::$enable_logging = true;
- }
- if (is_null(OAuthRequestLogger::$store_log))
- {
- OAuthRequestLogger::$store_log = true;
- }
- }
-
- if (OAuthRequestLogger::$enable_logging && !OAuthRequestLogger::$logging)
- {
- OAuthRequestLogger::$logging = true;
- OAuthRequestLogger::$request_object = $request_object;
- ob_start();
-
- // Make sure we flush our log entry when we stop the request (eg on an exception)
- register_shutdown_function(array('OAuthRequestLogger','flush'));
- }
- }
-
-
- /**
- * Force logging, needed for performing test connects independent from the debugging setting.
- *
- * @param boolean store_log (optional) true to store the log in the db
- */
- static function enableLogging ( $store_log = null )
- {
- OAuthRequestLogger::$enable_logging = true;
- if (!is_null($store_log))
- {
- OAuthRequestLogger::$store_log = $store_log;
- }
- }
-
-
- /**
- * Logs the request to the database, sends any cached output.
- * Also called on shutdown, to make sure we always log the request being handled.
- */
- static function flush ()
- {
- if (OAuthRequestLogger::$logging)
- {
- OAuthRequestLogger::$logging = false;
-
- if (is_null(OAuthRequestLogger::$sent))
- {
- // What has been sent to the user-agent?
- $data = ob_get_contents();
- if (strlen($data) > 0)
- {
- ob_end_flush();
- }
- elseif (ob_get_level())
- {
- ob_end_clean();
- }
- $hs = headers_list();
- $sent = implode("\n", $hs) . "\n\n" . $data;
- }
- else
- {
- // The request we sent
- $sent = OAuthRequestLogger::$sent;
- }
-
- if (is_null(OAuthRequestLogger::$received))
- {
- // Build the request we received
- $hs0 = getallheaders();
- $hs = array();
- foreach ($hs0 as $h => $v)
- {
- $hs[] = "$h: $v";
- }
-
- $data = '';
- $fh = @fopen('php://input', 'r');
- if ($fh)
- {
- while (!feof($fh))
- {
- $s = fread($fh, 1024);
- if (is_string($s))
- {
- $data .= $s;
- }
- }
- fclose($fh);
- }
- $received = implode("\n", $hs) . "\n\n" . $data;
- }
- else
- {
- // The answer we received
- $received = OAuthRequestLogger::$received;
- }
-
- // The request base string
- if (OAuthRequestLogger::$request_object)
- {
- $base_string = OAuthRequestLogger::$request_object->signatureBaseString();
- }
- else
- {
- $base_string = '';
- }
-
- // Figure out to what keys we want to log this request
- $keys = array();
- if (OAuthRequestLogger::$request_object)
- {
- $consumer_key = OAuthRequestLogger::$request_object->getParam('oauth_consumer_key', true);
- $token = OAuthRequestLogger::$request_object->getParam('oauth_token', true);
-
- switch (get_class(OAuthRequestLogger::$request_object))
- {
- // tokens are access/request tokens by a consumer
- case 'OAuthServer':
- case 'OAuthRequestVerifier':
- $keys['ocr_consumer_key'] = $consumer_key;
- $keys['oct_token'] = $token;
- break;
-
- // tokens are access/request tokens to a server
- case 'OAuthRequester':
- case 'OAuthRequestSigner':
- $keys['osr_consumer_key'] = $consumer_key;
- $keys['ost_token'] = $token;
- break;
- }
- }
-
- // Log the request
- if (OAuthRequestLogger::$store_log)
- {
- $store = OAuthStore::instance();
- $store->addLog($keys, $received, $sent, $base_string, OAuthRequestLogger::$note, OAuthRequestLogger::$user_id);
- }
-
- OAuthRequestLogger::$log[] = array(
- 'keys' => $keys,
- 'received' => $received,
- 'sent' => $sent,
- 'base_string' => $base_string,
- 'note' => OAuthRequestLogger::$note
- );
- }
- }
-
-
- /**
- * Add a note, used by the OAuthException to log all exceptions.
- *
- * @param string note
- */
- static function addNote ( $note )
- {
- OAuthRequestLogger::$note .= $note . "\n\n";
- }
-
- /**
- * Set the OAuth request object being used
- *
- * @param OAuthRequest request_object
- */
- static function setRequestObject ( $request_object )
- {
- OAuthRequestLogger::$request_object = $request_object;
- }
-
-
- /**
- * Set the relevant user (defaults to the current user)
- *
- * @param int user_id
- */
- static function setUser ( $user_id )
- {
- OAuthRequestLogger::$user_id = $user_id;
- }
-
-
- /**
- * Set the request we sent
- *
- * @param string request
- */
- static function setSent ( $request )
- {
- OAuthRequestLogger::$sent = $request;
- }
-
- /**
- * Set the reply we received
- *
- * @param string request
- */
- static function setReceived ( $reply )
- {
- OAuthRequestLogger::$received = $reply;
- }
-
-
- /**
- * Get the the log till now
- *
- * @return array
- */
- static function getLog ()
- {
- return OAuthRequestLogger::$log;
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php b/mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php
deleted file mode 100644
index 9f83f287f..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-
-/**
- * Sign requests before performing the request.
- *
- * @version $Id: OAuthRequestSigner.php 58 2009-02-23 01:47:23Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:02:49 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-
-require_once dirname(__FILE__) . '/OAuthStore.php';
-require_once dirname(__FILE__) . '/OAuthRequest.php';
-
-
-class OAuthRequestSigner extends OAuthRequest
-{
- protected $request;
- protected $store;
- protected $usr_id = 0;
- private $signed = false;
-
-
- /**
- * Construct the request to be signed. Parses or appends the parameters in the params url.
- * When you supply an params array, then the params should not be urlencoded.
- * When you supply a string, then it is assumed it is of the type application/x-www-form-urlencoded
- *
- * @param string request url
- * @param string method PUT, GET, POST etc.
- * @param mixed params string (for urlencoded data, or array with name/value pairs)
- * @param string body optional body for PUT and/or POST requests
- */
- function __construct ( $request, $method = 'GET', $params = null, $body = null )
- {
- $this->store = OAuthStore::instance();
-
- if (is_string($params))
- {
- parent::__construct($request, $method, $params);
- }
- else
- {
- parent::__construct($request, $method);
- if (is_array($params))
- {
- foreach ($params as $name => $value)
- {
- $this->setParam($name, $value);
- }
- }
- }
-
- // With put/ post we might have a body (not for application/x-www-form-urlencoded requests)
- if ($method == 'PUT' || $method == 'POST')
- {
- $this->setBody($body);
- }
- }
-
-
- /**
- * Reset the 'signed' flag, so that any changes in the parameters force a recalculation
- * of the signature.
- */
- function setUnsigned ()
- {
- $this->signed = false;
- }
-
-
- /**
- * Sign our message in the way the server understands.
- * Set the needed oauth_xxxx parameters.
- *
- * @param int usr_id (optional) user that wants to sign this request
- * @param array secrets secrets used for signing, when empty then secrets will be fetched from the token registry
- * @param string name name of the token to be used for signing
- * @exception OAuthException when there is no oauth relation with the server
- * @exception OAuthException when we don't support the signing methods of the server
- */
- function sign ( $usr_id = 0, $secrets = null, $name = '' )
- {
- $url = $this->getRequestUrl();
- if (empty($secrets))
- {
- // get the access tokens for the site (on an user by user basis)
- $secrets = $this->store->getSecretsForSignature($url, $usr_id, $name);
- }
- if (empty($secrets))
- {
- throw new OAuthException('No OAuth relation with the server for at "'.$url.'"');
- }
-
- $signature_method = $this->selectSignatureMethod($secrets['signature_methods']);
-
- $token = isset($secrets['token']) ? $secrets['token'] : '';
- $token_secret = isset($secrets['token_secret']) ? $secrets['token_secret'] : '';
-
- $this->setParam('oauth_signature_method',$signature_method);
- $this->setParam('oauth_signature', '');
- $this->setParam('oauth_nonce', !empty($secrets['nonce']) ? $secrets['nonce'] : uniqid(''));
- $this->setParam('oauth_timestamp', !empty($secrets['timestamp']) ? $secrets['timestamp'] : time());
- $this->setParam('oauth_token', $token);
- $this->setParam('oauth_consumer_key', $secrets['consumer_key']);
- $this->setParam('oauth_version', '1.0');
-
- $body = $this->getBody();
- if (!is_null($body))
- {
- // We also need to sign the body, use the default signature method
- $body_signature = $this->calculateDataSignature($body, $secrets['consumer_secret'], $token_secret, $signature_method);
- $this->setParam('xoauth_body_signature', $body_signature, true);
- }
-
- $signature = $this->calculateSignature($secrets['consumer_secret'], $token_secret);
- $this->setParam('oauth_signature', $signature, true);
-
- $this->signed = true;
- $this->usr_id = $usr_id;
- }
-
-
- /**
- * Builds the Authorization header for the request.
- * Adds all oauth_ and xoauth_ parameters to the Authorization header.
- *
- * @return string
- */
- function getAuthorizationHeader ()
- {
- if (!$this->signed)
- {
- $this->sign($this->usr_id);
- }
- $h = array();
- $h[] = 'Authorization: OAuth realm=""';
- foreach ($this->param as $name => $value)
- {
- if (strncmp($name, 'oauth_', 6) == 0 || strncmp($name, 'xoauth_', 7) == 0)
- {
- $h[] = $name.'="'.$value.'"';
- }
- }
- $hs = implode(', ', $h);
- return $hs;
- }
-
-
- /**
- * Builds the application/x-www-form-urlencoded parameter string. Can be appended as
- * the query part to a GET or inside the request body for a POST.
- *
- * @param boolean oauth_as_header (optional) set to false to include oauth parameters
- * @return string
- */
- function getQueryString ( $oauth_as_header = true )
- {
- $parms = array();
- foreach ($this->param as $name => $value)
- {
- if ( !$oauth_as_header
- || (strncmp($name, 'oauth_', 6) != 0 && strncmp($name, 'xoauth_', 7) != 0))
- {
- if (is_array($value))
- {
- foreach ($value as $v)
- {
- $parms[] = $name.'='.$v;
- }
- }
- else
- {
- $parms[] = $name.'='.$value;
- }
- }
- }
- return implode('&', $parms);
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php b/mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php
deleted file mode 100644
index 4b4db9685..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php
+++ /dev/null
@@ -1,262 +0,0 @@
-<?php
-
-/**
- * Verify the current request. Checks if signed and if the signature is correct.
- * When correct then also figures out on behalf of which user this request is being made.
- *
- * @version $Id: OAuthRequestVerifier.php 51 2008-10-15 15:15:47Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:35:03 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__) . '/OAuthStore.php';
-require_once dirname(__FILE__) . '/OAuthRequest.php';
-
-
-class OAuthRequestVerifier extends OAuthRequest
-{
- private $request;
- private $store;
-
- /**
- * Construct the request to be verified
- *
- * @param string request
- * @param string method
- */
- function __construct ( $uri = null, $method = 'GET' )
- {
- $this->store = OAuthStore::instance();
- parent::__construct($uri, $method);
-
- OAuthRequestLogger::start($this);
- }
-
-
- /**
- * See if the current request is signed with OAuth
- *
- * @return boolean
- */
- static public function requestIsSigned ()
- {
- if (isset($_REQUEST['oauth_signature']))
- {
- $signed = true;
- }
- else
- {
- $hs = getallheaders();
- if (isset($hs['Authorization']) && strpos($hs['Authorization'], 'oauth_signature') !== false)
- {
- $signed = true;
- }
- else
- {
- $signed = false;
- }
- }
- return $signed;
- }
-
-
- /**
- * Verify the request if it seemed to be signed.
- *
- * @param string token_type the kind of token needed, defaults to 'access'
- * @exception OAuthException thrown when the request did not verify
- * @return boolean true when signed, false when not signed
- */
- public function verifyIfSigned ( $token_type = 'access' )
- {
- if ($this->getParam('oauth_consumer_key'))
- {
- OAuthRequestLogger::start($this);
- $this->verify($token_type);
- $signed = true;
- OAuthRequestLogger::flush();
- }
- else
- {
- $signed = false;
- }
- return $signed;
- }
-
-
- /**
- * Verify the request
- *
- * @param string token_type the kind of token needed, defaults to 'access' (false, 'access', 'request')
- * @exception OAuthException thrown when the request did not verify
- * @return int user_id associated with token (false when no user associated)
- */
- public function verify ( $token_type = 'access' )
- {
- $consumer_key = $this->getParam('oauth_consumer_key');
- $token = $this->getParam('oauth_token');
- $user_id = false;
-
- if ($consumer_key && ($token_type === false || $token))
- {
- $secrets = $this->store->getSecretsForVerify( $this->urldecode($consumer_key),
- $this->urldecode($token),
- $token_type);
-
- $this->store->checkServerNonce( $this->urldecode($consumer_key),
- $this->urldecode($token),
- $this->getParam('oauth_timestamp', true),
- $this->getParam('oauth_nonce', true));
-
- $oauth_sig = $this->getParam('oauth_signature');
- if (empty($oauth_sig))
- {
- throw new OAuthException('Verification of signature failed (no oauth_signature in request).');
- }
-
- try
- {
- $this->verifySignature($secrets['consumer_secret'], $secrets['token_secret'], $token_type);
- }
- catch (OAuthException $e)
- {
- throw new OAuthException('Verification of signature failed (signature base string was "'.$this->signatureBaseString().'").');
- }
-
- // Check the optional body signature
- if ($this->getParam('xoauth_body_signature'))
- {
- $method = $this->getParam('xoauth_body_signature_method');
- if (empty($method))
- {
- $method = $this->getParam('oauth_signature_method');
- }
-
- try
- {
- $this->verifyDataSignature($this->getBody(), $secrets['consumer_secret'], $secrets['token_secret'], $method, $this->getParam('xoauth_body_signature'));
- }
- catch (OAuthException $e)
- {
- throw new OAuthException('Verification of body signature failed.');
- }
- }
-
- // All ok - fetch the user associated with this request
- if (isset($secrets['user_id']))
- {
- $user_id = $secrets['user_id'];
- }
-
- // Check if the consumer wants us to reset the ttl of this token
- $ttl = $this->getParam('xoauth_token_ttl', true);
- if (is_numeric($ttl))
- {
- $this->store->setConsumerAccessTokenTtl($this->urldecode($token), $ttl);
- }
- }
- else
- {
- throw new OAuthException('Can\'t verify request, missing oauth_consumer_key or oauth_token');
- }
- return $user_id;
- }
-
-
-
- /**
- * Verify the signature of the request, using the method in oauth_signature_method.
- * The signature is returned encoded in the form as used in the url. So the base64 and
- * urlencoding has been done.
- *
- * @param string consumer_secret
- * @param string token_secret
- * @exception OAuthException thrown when the signature method is unknown
- * @exception OAuthException when not all parts available
- * @exception OAuthException when signature does not match
- */
- public function verifySignature ( $consumer_secret, $token_secret, $token_type = 'access' )
- {
- $required = array(
- 'oauth_consumer_key',
- 'oauth_signature_method',
- 'oauth_timestamp',
- 'oauth_nonce',
- 'oauth_signature'
- );
-
- if ($token_type !== false)
- {
- $required[] = 'oauth_token';
- }
-
- foreach ($required as $req)
- {
- if (!isset($this->param[$req]))
- {
- throw new OAuthException('Can\'t verify request signature, missing parameter "'.$req.'"');
- }
- }
-
- $this->checks();
-
- $base = $this->signatureBaseString();
- $this->verifyDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method'], $this->param['oauth_signature']);
- }
-
-
-
- /**
- * Verify the signature of a string.
- *
- * @param string data
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature_method
- * @param string signature
- * @exception OAuthException thrown when the signature method is unknown
- * @exception OAuthException when signature does not match
- */
- public function verifyDataSignature ( $data, $consumer_secret, $token_secret, $signature_method, $signature )
- {
- if (is_null($data))
- {
- $data = '';
- }
-
- $sig = $this->getSignatureMethod($signature_method);
- if (!$sig->verify($this, $data, $consumer_secret, $token_secret, $signature))
- {
- throw new OAuthException('Signature verification failed ('.$signature_method.')');
- }
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequester.php b/mod/oauth_api/vendors/oauth/library/OAuthRequester.php
deleted file mode 100644
index 87f9586c0..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequester.php
+++ /dev/null
@@ -1,508 +0,0 @@
-<?php
-
-/**
- * Perform a signed OAuth request with a GET, POST, PUT or DELETE operation.
- *
- * @version $Id: OAuthRequester.php 63 2009-02-25 10:24:33Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 20, 2007 1:41:38 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__) . '/OAuthRequestSigner.php';
-require_once dirname(__FILE__) . '/body/OAuthBodyContentDisposition.php';
-
-
-class OAuthRequester extends OAuthRequestSigner
-{
- protected $files;
-
- /**
- * Construct a new request signer. Perform the request with the doRequest() method below.
- *
- * A request can have either one file or a body, not both.
- *
- * The files array consists of arrays:
- * - file the filename/path containing the data for the POST/PUT
- * - data data for the file, omit when you have a file
- * - mime content-type of the file
- * - filename filename for content disposition header
- *
- * When OAuth (and PHP) can support multipart/form-data then we can handle more than one file.
- * For now max one file, with all the params encoded in the query string.
- *
- * @param string request
- * @param string method http method. GET, PUT, POST etc.
- * @param array params name=>value array with request parameters
- * @param string body optional body to send
- * @param array files optional files to send (max 1 till OAuth support multipart/form-data posts)
- */
- function __construct ( $request, $method = 'GET', $params = null, $body = null, $files = null )
- {
- parent::__construct($request, $method, $params, $body);
-
- // When there are files, then we can construct a POST with a single file
- if (!empty($files))
- {
- $empty = true;
- foreach ($files as $f)
- {
- $empty = $empty && empty($f['file']) && !isset($f['data']);
- }
-
- if (!$empty)
- {
- if (!is_null($body))
- {
- throw new OAuthException('When sending files, you can\'t send a body as well.');
- }
- $this->files = $files;
- }
- }
- }
-
-
- /**
- * Perform the request, returns the response code, headers and body.
- *
- * @param int usr_id optional user id for which we make the request
- * @param array curl_options optional extra options for curl request
- * @param array options options like name and token_ttl
- * @exception OAuthException when authentication not accepted
- * @exception OAuthException when signing was not possible
- * @return array (code=>int, headers=>array(), body=>string)
- */
- function doRequest ( $usr_id = 0, $curl_options = array(), $options = array() )
- {
- $name = isset($options['name']) ? $options['name'] : '';
- if (isset($options['token_ttl']))
- {
- $this->setParam('xoauth_token_ttl', intval($options['token_ttl']));
- }
-
- if (!empty($this->files))
- {
- // At the moment OAuth does not support multipart/form-data, so try to encode
- // the supplied file (or data) as the request body and add a content-disposition header.
- list($extra_headers, $body) = OAuthBodyContentDisposition::encodeBody($this->files);
- $this->setBody($body);
- $curl_options = $this->prepareCurlOptions($curl_options, $extra_headers);
- }
- $this->sign($usr_id, null, $name);
- $text = $this->curl_raw($curl_options);
- $result = $this->curl_parse($text);
- if ($result['code'] >= 400)
- {
- throw new OAuthException('Request failed with code ' . $result['code'] . ': ' . $result['body']);
- }
-
- // Record the token time to live for this server access token, immediate delete iff ttl <= 0
- // Only done on a succesful request.
- $token_ttl = $this->getParam('xoauth_token_ttl', false);
- if (is_numeric($token_ttl))
- {
- $this->store->setServerTokenTtl($this->getParam('oauth_consumer_key',true), $this->getParam('oauth_token',true), $token_ttl);
- }
-
- return $result;
- }
-
-
- /**
- * Request a request token from the site belonging to consumer_key
- *
- * @param string consumer_key
- * @param int usr_id
- * @param array params (optional) extra arguments for when requesting the request token
- * @param string method (optional) change the method of the request, defaults to POST (as it should be)
- * @param array options (optional) options like name and token_ttl
- * @exception OAuthException when no key could be fetched
- * @exception OAuthException when no server with consumer_key registered
- * @return array (authorize_uri, token)
- */
- static function requestRequestToken ( $consumer_key, $usr_id, $params = null, $method = 'POST', $options = array() )
- {
- OAuthRequestLogger::start();
-
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $params['xoauth_token_ttl'] = intval($options['token_ttl']);
- }
-
- $store = OAuthStore::instance();
- $r = $store->getServer($consumer_key, $usr_id);
- $uri = $r['request_token_uri'];
-
- $oauth = new OAuthRequester($uri, $method, $params);
- $oauth->sign($usr_id, $r);
- $text = $oauth->curl_raw();
-
- if (empty($text))
- {
- throw new OAuthException('No answer from the server "'.$uri.'" while requesting a request token');
- }
- $data = $oauth->curl_parse($text);
- if ($data['code'] != 200)
- {
- throw new OAuthException('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token');
- }
- $token = array();
- $params = explode('&', $data['body']);
- foreach ($params as $p)
- {
- @list($name, $value) = explode('=', $p, 2);
- $token[$name] = $oauth->urldecode($value);
- }
-
- if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret']))
- {
- $opts = array();
- if (isset($options['name']))
- {
- $opts['name'] = $options['name'];
- }
- if (isset($token['xoauth_token_ttl']))
- {
- $opts['token_ttl'] = $token['xoauth_token_ttl'];
- }
- $store->addServerToken($consumer_key, 'request', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts);
- }
- else
- {
- throw new OAuthException('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret');
- }
-
- OAuthRequestLogger::flush();
-
- // Now we can direct a browser to the authorize_uri
- return array(
- 'authorize_uri' => $r['authorize_uri'],
- 'token' => $token['oauth_token']
- );
- }
-
-
- /**
- * Request an access token from the site belonging to consumer_key.
- * Before this we got an request token, now we want to exchange it for
- * an access token.
- *
- * @param string consumer_key
- * @param string token
- * @param int usr_id user requesting the access token
- * @param string method (optional) change the method of the request, defaults to POST (as it should be)
- * @param array options (optional) extra options for request, eg token_ttl
- * @exception OAuthException when no key could be fetched
- * @exception OAuthException when no server with consumer_key registered
- */
- static function requestAccessToken ( $consumer_key, $token, $usr_id, $method = 'POST', $options = array() )
- {
- OAuthRequestLogger::start();
-
- $store = OAuthStore::instance();
- $r = $store->getServerTokenSecrets($consumer_key, $token, 'request', $usr_id);
- $uri = $r['access_token_uri'];
- $token_name = $r['token_name'];
-
- // Delete the server request token, this one was for one use only
- $store->deleteServerToken($consumer_key, $r['token'], 0, true);
-
- // Try to exchange our request token for an access token
- $oauth = new OAuthRequester($uri, $method);
-
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $oauth->setParam('xoauth_token_ttl', intval($options['token_ttl']));
- }
-
- OAuthRequestLogger::setRequestObject($oauth);
-
- $oauth->sign($usr_id, $r);
- $text = $oauth->curl_raw();
- if (empty($text))
- {
- throw new OAuthException('No answer from the server "'.$uri.'" while requesting a request token');
- }
- $data = $oauth->curl_parse($text);
-
- if ($data['code'] != 200)
- {
- throw new OAuthException('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token');
- }
-
- $token = array();
- $params = explode('&', $data['body']);
- foreach ($params as $p)
- {
- @list($name, $value) = explode('=', $p, 2);
- $token[$oauth->urldecode($name)] = $oauth->urldecode($value);
- }
-
- if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret']))
- {
- $opts = array();
- $opts['name'] = $token_name;
- if (isset($token['xoauth_token_ttl']))
- {
- $opts['token_ttl'] = $token['xoauth_token_ttl'];
- }
- $store->addServerToken($consumer_key, 'access', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts);
- }
- else
- {
- throw new OAuthException('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret');
- }
-
- OAuthRequestLogger::flush();
- }
-
-
-
- /**
- * Open and close a curl session passing all the options to the curl libs
- *
- * @param string url the http address to fetch
- * @exception OAuthException when temporary file for PUT operation could not be created
- * @return string the result of the curl action
- */
- protected function curl_raw ( $opts = array() )
- {
- if (isset($opts[CURLOPT_HTTPHEADER]))
- {
- $header = $opts[CURLOPT_HTTPHEADER];
- }
- else
- {
- $header = array();
- }
-
- $ch = curl_init();
- $method = $this->getMethod();
- $url = $this->getRequestUrl();
- $header[] = $this->getAuthorizationHeader();
- $query = $this->getQueryString();
- $body = $this->getBody();
-
- $has_content_type = false;
- foreach ($header as $h)
- {
- if (strncasecmp($h, 'Content-Type:', 13) == 0)
- {
- $has_content_type = true;
- }
- }
-
- if (!is_null($body))
- {
- if ($method == 'TRACE')
- {
- throw new OAuthException('A body can not be sent with a TRACE operation');
- }
-
- // PUT and POST allow a request body
- if (!empty($query))
- {
- $url .= '?'.$query;
- }
-
- // Make sure that the content type of the request is ok
- if (!$has_content_type)
- {
- $header[] = 'Content-Type: application/octet-stream';
- $has_content_type = true;
- }
-
- // When PUTting, we need to use an intermediate file (because of the curl implementation)
- if ($method == 'PUT')
- {
- /*
- if (version_compare(phpversion(), '5.2.0') >= 0)
- {
- // Use the data wrapper to create the file expected by the put method
- $put_file = fopen('data://application/octet-stream;base64,'.base64_encode($body));
- }
- */
-
- $put_file = @tmpfile();
- if (!$put_file)
- {
- throw new OAuthException('Could not create tmpfile for PUT operation');
- }
- fwrite($put_file, $body);
- fseek($put_file, 0);
-
- curl_setopt($ch, CURLOPT_PUT, true);
- curl_setopt($ch, CURLOPT_INFILE, $put_file);
- curl_setopt($ch, CURLOPT_INFILESIZE, strlen($body));
- }
- else
- {
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
- }
- }
- else
- {
- // a 'normal' request, no body to be send
- if ($method == 'POST')
- {
- if (!$has_content_type)
- {
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $has_content_type = true;
- }
-
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
- }
- else
- {
- if (!empty($query))
- {
- $url .= '?'.$query;
- }
- if ($method != 'GET')
- {
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
- }
- }
- }
-
- curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - ($LastChangedRevision: 63 $)');
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HEADER, true);
-
- foreach ($opts as $k => $v)
- {
- if ($k != CURLOPT_HTTPHEADER)
- {
- curl_setopt($ch, $k, $v);
- }
- }
-
- $txt = curl_exec($ch);
- curl_close($ch);
-
- if (!empty($put_file))
- {
- fclose($put_file);
- }
-
- // Tell the logger what we requested and what we received back
- $data = $method . " $url\n".implode("\n",$header);
- if (is_string($body))
- {
- $data .= "\n\n".$body;
- }
- else if ($method == 'POST')
- {
- $data .= "\n\n".$query;
- }
-
- OAuthRequestLogger::setSent($data, $body);
- OAuthRequestLogger::setReceived($txt);
-
- return $txt;
- }
-
-
- /**
- * Parse an http response
- *
- * @param string response the http text to parse
- * @return array (code=>http-code, headers=>http-headers, body=>body)
- */
- protected function curl_parse ( $response )
- {
- if (empty($response))
- {
- return array();
- }
-
- @list($headers,$body) = explode("\r\n\r\n",$response,2);
- $lines = explode("\r\n",$headers);
-
- if (preg_match('@^HTTP/[0-9]\.[0-9] +100@', $lines[0]))
- {
- /* HTTP/1.x 100 Continue
- * the real data is on the next line
- */
- @list($headers,$body) = explode("\r\n\r\n",$body,2);
- $lines = explode("\r\n",$headers);
- }
-
- // first line of headers is the HTTP response code
- $http_line = array_shift($lines);
- if (preg_match('@^HTTP/[0-9]\.[0-9] +([0-9]{3})@', $http_line, $matches))
- {
- $code = $matches[1];
- }
-
- // put the rest of the headers in an array
- $headers = array();
- foreach ($lines as $l)
- {
- list($k, $v) = explode(': ', $l, 2);
- $headers[strtolower($k)] = $v;
- }
-
- return array( 'code' => $code, 'headers' => $headers, 'body' => $body);
- }
-
-
- /**
- * Mix the given headers into the headers that were given to curl
- *
- * @param array curl_options
- * @param array extra_headers
- * @return array new curl options
- */
- protected function prepareCurlOptions ( $curl_options, $extra_headers )
- {
- $hs = array();
- if (!empty($curl_options[CURLOPT_HTTPHEADER]) && is_array($curl_options[CURLOPT_HTTPHEADER]))
- {
- foreach ($curl_options[CURLOPT_HTTPHEADER] as $h)
- {
- list($opt, $val) = explode(':', $h, 2);
- $opt = str_replace(' ', '-', ucwords(str_replace('-', ' ', $opt)));
- $hs[$opt] = $val;
- }
- }
-
- $curl_options[CURLOPT_HTTPHEADER] = array();
- $hs = array_merge($hs, $extra_headers);
- foreach ($hs as $h => $v)
- {
- $curl_options[CURLOPT_HTTPHEADER][] = "$h: $v";
- }
- return $curl_options;
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthServer.php b/mod/oauth_api/vendors/oauth/library/OAuthServer.php
deleted file mode 100644
index c7f9097b3..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthServer.php
+++ /dev/null
@@ -1,232 +0,0 @@
-<?php
-
-/**
- * Server layer over the OAuthRequest handler
- *
- * @version $Id: OAuthServer.php 51 2008-10-15 15:15:47Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 27, 2007 12:36:38 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once 'OAuthRequestVerifier.php';
-
-class OAuthServer extends OAuthRequestVerifier
-{
- /**
- * Handle the request_token request.
- * Returns the new request token and request token secret.
- *
- * TODO: add correct result code to exception
- *
- * @return string returned request token, false on an error
- */
- public function requestToken ()
- {
- OAuthRequestLogger::start($this);
- try
- {
- $this->verify(false);
-
- $options = array();
- $ttl = $this->getParam('xoauth_token_ttl', false);
- if ($ttl)
- {
- $options['token_ttl'] = $ttl;
- }
-
- // Create a request token
- $store = OAuthStore::instance();
- $token = $store->addConsumerRequestToken($this->getParam('oauth_consumer_key', true), $options);
- $result = 'oauth_token='.$this->urlencode($token['token'])
- .'&oauth_token_secret='.$this->urlencode($token['token_secret']);
-
- if (!empty($token['token_ttl']))
- {
- $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']);
- }
-
- $request_token = $token['token'];
-
- header('HTTP/1.1 200 OK');
- header('Content-Length: '.strlen($result));
- header('Content-Type: application/x-www-form-urlencoded');
-
- echo $result;
- }
- catch (OAuthException $e)
- {
- $request_token = false;
-
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: text/plain');
-
- echo "OAuth Verification Failed: " . $e->getMessage();
- }
-
- OAuthRequestLogger::flush();
- return $request_token;
- }
-
-
- /**
- * Verify the start of an authorization request. Verifies if the request token is valid.
- * Next step is the method authorizeFinish()
- *
- * Nota bene: this stores the current token, consumer key and callback in the _SESSION
- *
- * @exception OAuthException thrown when not a valid request
- * @return array token description
- */
- public function authorizeVerify ( )
- {
- OAuthRequestLogger::start($this);
-
- $store = OAuthStore::instance();
- $token = $this->getParam('oauth_token', true);
- $rs = $store->getConsumerRequestToken($token);
- if (empty($rs))
- {
- throw new OAuthException('Unknown request token "'.$token.'"');
- }
-
- // We need to remember the callback
- if ( empty($_SESSION['verify_oauth_token'])
- || strcmp($_SESSION['verify_oauth_token'], $rs['token']))
- {
- $_SESSION['verify_oauth_token'] = $rs['token'];
- $_SESSION['verify_oauth_consumer_key'] = $rs['consumer_key'];
- $_SESSION['verify_oauth_callback'] = $this->getParam('oauth_callback', true);
- }
- OAuthRequestLogger::flush();
- return $rs;
- }
-
-
- /**
- * Overrule this method when you want to display a nice page when
- * the authorization is finished. This function does not know if the authorization was
- * succesfull, you need to check the token in the database.
- *
- * @param boolean authorized if the current token (oauth_token param) is authorized or not
- * @param int user_id user for which the token was authorized (or denied)
- */
- public function authorizeFinish ( $authorized, $user_id )
- {
- OAuthRequestLogger::start($this);
-
- $token = $this->getParam('oauth_token', true);
- if ( isset($_SESSION['verify_oauth_token'])
- && $_SESSION['verify_oauth_token'] == $token)
- {
- // Flag the token as authorized, or remove the token when not authorized
- $store = OAuthStore::instance();
-
- // Fetch the referrer host from the oauth callback parameter
- $referrer_host = '';
- $oauth_callback = false;
- if (!empty($_SESSION['verify_oauth_callback']))
- {
- $oauth_callback = $_SESSION['verify_oauth_callback'];
- $ps = parse_url($oauth_callback);
- if (isset($ps['host']))
- {
- $referrer_host = $ps['host'];
- }
- }
-
- if ($authorized)
- {
- OAuthRequestLogger::addNote('Authorized token "'.$token.'" for user '.$user_id.' with referrer "'.$referrer_host.'"');
- $store->authorizeConsumerRequestToken($token, $user_id, $referrer_host);
- }
- else
- {
- OAuthRequestLogger::addNote('Authorization rejected for token "'.$token.'" for user '.$user_id."\nToken has been deleted");
- $store->deleteConsumerRequestToken($token);
- }
-
- if (!empty($oauth_callback))
- {
- $this->redirect($oauth_callback, array('oauth_token'=>rawurlencode($token)));
- }
- }
- OAuthRequestLogger::flush();
- }
-
-
- /**
- * Exchange a request token for an access token.
- * The exchange is only succesful iff the request token has been authorized.
- *
- * Never returns, calls exit() when token is exchanged or when error is returned.
- */
- public function accessToken ()
- {
- OAuthRequestLogger::start($this);
-
- try
- {
- $this->verify('request');
-
- $options = array();
- $ttl = $this->getParam('xoauth_token_ttl', false);
- if ($ttl)
- {
- $options['token_ttl'] = $ttl;
- }
-
- $store = OAuthStore::instance();
- $token = $store->exchangeConsumerRequestForAccessToken($this->getParam('oauth_token', true), $options);
- $result = 'oauth_token='.$this->urlencode($token['token'])
- .'&oauth_token_secret='.$this->urlencode($token['token_secret']);
-
- if (!empty($token['token_ttl']))
- {
- $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']);
- }
-
- header('HTTP/1.1 200 OK');
- header('Content-Length: '.strlen($result));
- header('Content-Type: application/x-www-form-urlencoded');
-
- echo $result;
- }
- catch (OAuthException $e)
- {
- header('HTTP/1.1 401 Access Denied');
- header('Content-Type: text/plain');
-
- echo "OAuth Verification Failed: " . $e->getMessage();
- }
-
- OAuthRequestLogger::flush();
- exit();
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthStore.php b/mod/oauth_api/vendors/oauth/library/OAuthStore.php
deleted file mode 100644
index 1841ab5fa..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthStore.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * Storage container for the oauth credentials, both server and consumer side.
- * This is the factory to select the store you want to use
- *
- * @version $Id: OAuthStore.php 49 2008-10-01 09:43:19Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:03:30 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__) . '/OAuthException.php';
-
-class OAuthStore
-{
- static private $instance = false;
-
- /**
- * Request an instance of the OAuthStore
- */
- public static function instance ( $store = 'MySQL', $options = array() )
- {
- if (!OAuthStore::$instance)
- {
- // Select the store you want to use
- if (strpos($store, '/') === false)
- {
- $class = 'OAuthStore'.$store;
- $file = dirname(__FILE__) . '/store/'.$class.'.php';
- }
- else
- {
- $file = $store;
- $store = basename($file, '.php');
- $class = $store;
- }
-
- if (is_file($file))
- {
- require_once $file;
-
- if (class_exists($class))
- {
- OAuthStore::$instance = new $class($options);
- }
- else
- {
- throw new OAuthException('Could not find class '.$class.' in file '.$file);
- }
- }
- else
- {
- throw new OAuthException('No OAuthStore for '.$store.' (file '.$file.')');
- }
- }
- return OAuthStore::$instance;
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php b/mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php
deleted file mode 100644
index 84123b6d0..000000000
--- a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-/**
- * Add the extra headers for a PUT or POST request with a file.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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 OAuthBodyContentDisposition
-{
- /**
- * Builds the request string.
- *
- * The files array can be a combination of the following (either data or file):
- *
- * file => "path/to/file", filename=, mime=, data=
- *
- * @param array files (name => filedesc) (not urlencoded)
- * @return array (headers, body)
- */
- static function encodeBody ( $files )
- {
- $headers = array();
- $body = null;
-
- // 1. Add all the files to the post
- if (!empty($files))
- {
- foreach ($files as $name => $f)
- {
- $data = false;
- $filename = false;
-
- if (isset($f['filename']))
- {
- $filename = $f['filename'];
- }
-
- if (!empty($f['file']))
- {
- $data = @file_get_contents($f['file']);
- if ($data === false)
- {
- throw new OAuthException(sprintf('Could not read the file "%s" for request body', $f['file']));
- }
- if (empty($filename))
- {
- $filename = basename($f['file']);
- }
- }
- else if (isset($f['data']))
- {
- $data = $f['data'];
- }
-
- // When there is data, add it as a request body, otherwise silently skip the upload
- if ($data !== false)
- {
- if (isset($headers['Content-Disposition']))
- {
- throw new OAuthException('Only a single file (or data) allowed in a signed PUT/POST request body.');
- }
-
- if (empty($filename))
- {
- $filename = 'untitled';
- }
- $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream';
-
- $headers['Content-Disposition'] = 'attachment; filename="'.OAuthBodyContentDisposition::encodeParameterName($filename).'"';
- $headers['Content-Type'] = $mime;
-
- $body = $data;
- }
-
- }
-
- // When we have a body, add the content-length
- if (!is_null($body))
- {
- $headers['Content-Length'] = strlen($body);
- }
- }
- return array($headers, $body);
- }
-
-
- /**
- * Encode a parameter's name for use in a multipart header.
- * For now we do a simple filter that removes some unwanted characters.
- * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522
- *
- * @param string name
- * @return string
- */
- static function encodeParameterName ( $name )
- {
- return preg_replace('/[^\x20-\x7f]|"/', '-', $name);
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php b/mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php
deleted file mode 100644
index 048fdeb63..000000000
--- a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/**
- * Create the body for a multipart/form-data message.
- *
- * @version $Id: OAuthMultipartFormdata.php 6 2008-02-13 12:35:09Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Jan 31, 2008 12:50:05 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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 OAuthBodyMultipartFormdata
-{
- /**
- * Builds the request string.
- *
- * The files array can be a combination of the following (either data or file):
- *
- * file => "path/to/file", filename=, mime=, data=
- *
- * @param array params (name => value) (all names and values should be urlencoded)
- * @param array files (name => filedesc) (not urlencoded)
- * @return array (headers, body)
- */
- static function encodeBody ( $params, $files )
- {
- $headers = array();
- $body = '';
- $boundary = 'OAuthRequester_'.md5(uniqid('multipart') . microtime());
- $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
-
-
- // 1. Add the parameters to the post
- if (!empty($params))
- {
- foreach ($params as $name => $value)
- {
- $body .= '--'.$boundary."\r\n";
- $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName(rawurldecode($name)).'"';
- $body .= "\r\n\r\n";
- $body .= urldecode($value);
- $body .= "\r\n";
- }
- }
-
- // 2. Add all the files to the post
- if (!empty($files))
- {
- $untitled = 1;
-
- foreach ($files as $name => $f)
- {
- $data = false;
- $filename = false;
-
- if (isset($f['filename']))
- {
- $filename = $f['filename'];
- }
-
- if (!empty($f['file']))
- {
- $data = @file_get_contents($f['file']);
- if ($data === false)
- {
- throw new OAuthException(sprintf('Could not read the file "%s" for form-data part', $f['file']));
- }
- if (empty($filename))
- {
- $filename = basename($f['file']);
- }
- }
- else if (isset($f['data']))
- {
- $data = $f['data'];
- }
-
- // When there is data, add it as a form-data part, otherwise silently skip the upload
- if ($data !== false)
- {
- if (empty($filename))
- {
- $filename = sprintf('untitled-%d', $untitled++);
- }
- $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream';
- $body .= '--'.$boundary."\r\n";
- $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName($name).'"; filename="'.OAuthBodyMultipartFormdata::encodeParameterName($filename).'"'."\r\n";
- $body .= 'Content-Type: '.$mime;
- $body .= "\r\n\r\n";
- $body .= $data;
- $body .= "\r\n";
- }
-
- }
- }
- $body .= '--'.$boundary."--\r\n";
-
- $headers['Content-Length'] = strlen($body);
- return array($headers, $body);
- }
-
-
- /**
- * Encode a parameter's name for use in a multipart header.
- * For now we do a simple filter that removes some unwanted characters.
- * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522
- *
- * @param string name
- * @return string
- */
- static function encodeParameterName ( $name )
- {
- return preg_replace('/[^\x20-\x7f]|"/', '-', $name);
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php b/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php
deleted file mode 100644
index c9cf94997..000000000
--- a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php
+++ /dev/null
@@ -1,304 +0,0 @@
-<?php
-
-/**
- * Parse a XRDS discovery description to a simple array format.
- *
- * For now a simple parse of the document. Better error checking
- * in a later version.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-/* example of use:
-
-header('content-type: text/plain');
-$file = file_get_contents('../../test/discovery/xrds-magnolia.xrds');
-$xrds = xrds_parse($file);
-print_r($xrds);
-
- */
-
-/**
- * Parse the xrds file in the argument. The xrds description must have been
- * fetched via curl or something else.
- *
- * TODO: more robust checking, support for more service documents
- * TODO: support for URIs to definition instead of local xml:id
- *
- * @param string data contents of xrds file
- * @exception Exception when the file is in an unknown format
- * @return array
- */
-function xrds_parse ( $data )
-{
- $oauth = array();
- $doc = @DOMDocument::loadXML($data);
- if ($doc === false)
- {
- throw new Exception('Error in XML, can\'t load XRDS document');
- }
-
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('xrds', 'xri://$xrds');
- $xpath->registerNamespace('xrd', 'xri://$XRD*($v*2.0)');
- $xpath->registerNamespace('simple', 'http://xrds-simple.net/core/1.0');
-
- // Yahoo! uses this namespace, with lowercase xrd in it
- $xpath->registerNamespace('xrd2', 'xri://$xrd*($v*2.0)');
-
- $uris = xrds_oauth_service_uris($xpath);
-
- foreach ($uris as $uri)
- {
- // TODO: support uris referring to service documents outside this one
- if ($uri{0} == '#')
- {
- $id = substr($uri, 1);
- $oauth = xrds_xrd_oauth($xpath, $id);
- if (is_array($oauth) && !empty($oauth))
- {
- return $oauth;
- }
- }
- }
-
- return false;
-}
-
-
-/**
- * Parse a XRD definition for OAuth and return the uris etc.
- *
- * @param XPath xpath
- * @param string id
- * @return array
- */
-function xrds_xrd_oauth ( $xpath, $id )
-{
- $oauth = array();
- $xrd = $xpath->query('//xrds:XRDS/xrd:XRD[@xml:id="'.$id.'"]');
- if ($xrd->length == 0)
- {
- // Yahoo! uses another namespace
- $xrd = $xpath->query('//xrds:XRDS/xrd2:XRD[@xml:id="'.$id.'"]');
- }
-
- if ($xrd->length >= 1)
- {
- $x = $xrd->item(0);
- $services = array();
- foreach ($x->childNodes as $n)
- {
- switch ($n->nodeName)
- {
- case 'Type':
- if ($n->nodeValue != 'xri://$xrds*simple')
- {
- // Not a simple XRDS document
- return false;
- }
- break;
- case 'Expires':
- $oauth['expires'] = $n->nodeValue;
- break;
- case 'Service':
- list($type,$service) = xrds_xrd_oauth_service($n);
- if ($type)
- {
- $services[$type][xrds_priority($n)][] = $service;
- }
- break;
- }
- }
-
- // Flatten the services on priority
- foreach ($services as $type => $service)
- {
- $oauth[$type] = xrds_priority_flatten($service);
- }
- }
- else
- {
- $oauth = false;
- }
- return $oauth;
-}
-
-
-/**
- * Parse a service definition for OAuth in a simple xrd element
- *
- * @param DOMElement n
- * @return array (type, service desc)
- */
-function xrds_xrd_oauth_service ( $n )
-{
- $service = array(
- 'uri' => '',
- 'signature_method' => array(),
- 'parameters' => array()
- );
-
- $type = false;
- foreach ($n->childNodes as $c)
- {
- $name = $c->nodeName;
- $value = $c->nodeValue;
-
- if ($name == 'URI')
- {
- $service['uri'] = $value;
- }
- else if ($name == 'Type')
- {
- if (strncmp($value, 'http://oauth.net/core/1.0/endpoint/', 35) == 0)
- {
- $type = basename($value);
- }
- else if (strncmp($value, 'http://oauth.net/core/1.0/signature/', 36) == 0)
- {
- $service['signature_method'][] = basename($value);
- }
- else if (strncmp($value, 'http://oauth.net/core/1.0/parameters/', 37) == 0)
- {
- $service['parameters'][] = basename($value);
- }
- else if (strncmp($value, 'http://oauth.net/discovery/1.0/consumer-identity/', 49) == 0)
- {
- $type = 'consumer_identity';
- $service['method'] = basename($value);
- unset($service['signature_method']);
- unset($service['parameters']);
- }
- else
- {
- $service['unknown'][] = $value;
- }
- }
- else if ($name == 'LocalID')
- {
- $service['consumer_key'] = $value;
- }
- else if ($name{0} != '#')
- {
- $service[strtolower($name)] = $value;
- }
- }
- return array($type, $service);
-}
-
-
-/**
- * Return the OAuth service uris in order of the priority.
- *
- * @param XPath xpath
- * @return array
- */
-function xrds_oauth_service_uris ( $xpath )
-{
- $uris = array();
- $xrd_oauth = $xpath->query('//xrds:XRDS/xrd:XRD/xrd:Service/xrd:Type[.=\'http://oauth.net/discovery/1.0\']');
- if ($xrd_oauth->length > 0)
- {
- $service = array();
- foreach ($xrd_oauth as $xo)
- {
- // Find the URI of the service definition
- $cs = $xo->parentNode->childNodes;
- foreach ($cs as $c)
- {
- if ($c->nodeName == 'URI')
- {
- $prio = xrds_priority($xo);
- $service[$prio][] = $c->nodeValue;
- }
- }
- }
- $uris = xrds_priority_flatten($service);
- }
- return $uris;
-}
-
-
-
-/**
- * Flatten an array according to the priority
- *
- * @param array ps buckets per prio
- * @return array one dimensional array
- */
-function xrds_priority_flatten ( $ps )
-{
- $prio = array();
- $null = array();
- ksort($ps);
- foreach ($ps as $idx => $bucket)
- {
- if (!empty($bucket))
- {
- if ($idx == 'null')
- {
- $null = $bucket;
- }
- else
- {
- $prio = array_merge($prio, $bucket);
- }
- }
- }
- $prio = array_merge($prio, $bucket);
- return $prio;
-}
-
-
-/**
- * Fetch the priority of a element
- *
- * @param DOMElement elt
- * @return mixed 'null' or int
- */
-function xrds_priority ( $elt )
-{
- if ($elt->hasAttribute('priority'))
- {
- $prio = $elt->getAttribute('priority');
- if (is_numeric($prio))
- {
- $prio = intval($prio);
- }
- }
- else
- {
- $prio = 'null';
- }
- return $prio;
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt b/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt
deleted file mode 100644
index fd867ea9f..000000000
--- a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt
+++ /dev/null
@@ -1,101 +0,0 @@
-The xrds_parse.php script contains the function:
-
- function xrds_parse ( $data. )
-
-$data Contains the contents of a XRDS XML file.
-When the data is invalid XML then this will throw an exception.
-
-After parsing a XRDS definition it will return a datastructure much like the one below.
-
-Array
-(
- [expires] => 2008-04-13T07:34:58Z
- [request] => Array
- (
- [0] => Array
- (
- [uri] => https://ma.gnolia.com/oauth/get_request_token
- [signature_method] => Array
- (
- [0] => HMAC-SHA1
- [1] => RSA-SHA1
- [2] => PLAINTEXT
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => post-body
- [2] => uri-query
- )
- )
- )
-
- [authorize] => Array
- (
- [0] => Array
- (
- [uri] => http://ma.gnolia.com/oauth/authorize
- [signature_method] => Array
- (
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => uri-query
- )
- )
- )
-
- [access] => Array
- (
- [0] => Array
- (
- [uri] => https://ma.gnolia.com/oauth/get_access_token
- [signature_method] => Array
- (
- [0] => HMAC-SHA1
- [1] => RSA-SHA1
- [2] => PLAINTEXT
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => post-body
- [2] => uri-query
- )
- )
- )
-
- [resource] => Array
- (
- [0] => Array
- (
- [uri] =>
- [signature_method] => Array
- (
- [0] => HMAC-SHA1
- [1] => RSA-SHA1
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => post-body
- [2] => uri-query
- )
- )
- )
-
- [consumer_identity] => Array
- (
- [0] => Array
- (
- [uri] => http://ma.gnolia.com/applications/new
- [method] => oob
- )
- )
-)
-
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php
deleted file mode 100644
index 34ccb428c..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/**
- * Interface for OAuth signature methods
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:04:35 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-abstract class OAuthSignatureMethod
-{
- /**
- * Return the name of this signature
- *
- * @return string
- */
- abstract public function name();
-
- /**
- * Return the signature for the given request
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- abstract public function signature ( $request, $base_string, $consumer_secret, $token_secret );
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- abstract public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature );
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php
deleted file mode 100644
index 4bc949c10..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using HMAC-SHA1
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:21:19 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-
-require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php';
-
-
-class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod
-{
- public function name ()
- {
- return 'HMAC-SHA1';
- }
-
-
- /**
- * Calculate the signature using HMAC-SHA1
- * This function is copyright Andy Smith, 2007.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- $key = $request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret);
- if (function_exists('hash_hmac'))
- {
- $signature = base64_encode(hash_hmac("sha1", $base_string, $key, true));
- }
- else
- {
- $blocksize = 64;
- $hashfunc = 'sha1';
- if (strlen($key) > $blocksize)
- {
- $key = pack('H*', $hashfunc($key));
- }
- $key = str_pad($key,$blocksize,chr(0x00));
- $ipad = str_repeat(chr(0x36),$blocksize);
- $opad = str_repeat(chr(0x5c),$blocksize);
- $hmac = pack(
- 'H*',$hashfunc(
- ($key^$opad).pack(
- 'H*',$hashfunc(
- ($key^$ipad).$base_string
- )
- )
- )
- );
- $signature = base64_encode($hmac);
- }
- return $request->urlencode($signature);
- }
-
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $a = $request->urldecode($signature);
- $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret));
-
- // We have to compare the decoded values
- $valA = base64_decode($a);
- $valB = base64_decode($b);
-
- // Crude binary comparison
- return rawurlencode($a) == rawurlencode($b);
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php
deleted file mode 100644
index 6f593a47f..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using MD5
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:09:43 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php';
-
-
-class OAuthSignatureMethod_MD5 extends OAuthSignatureMethod
-{
- public function name ()
- {
- return 'MD5';
- }
-
-
- /**
- * Calculate the signature using MD5
- * Binary md5 digest, as distinct from PHP's built-in hexdigest.
- * This function is copyright Andy Smith, 2007.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- $s .= '&'.$request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret);
- $md5 = md5($base_string);
- $bin = '';
-
- for ($i = 0; $i < strlen($md5); $i += 2)
- {
- $bin .= chr(hexdec($md5{$i+1}) + hexdec($md5{$i}) * 16);
- }
- return $request->urlencode(base64_encode($bin));
- }
-
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $a = $request->urldecode($signature);
- $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret));
-
- // We have to compare the decoded values
- $valA = base64_decode($a);
- $valB = base64_decode($b);
-
- // Crude binary comparison
- return rawurlencode($a) == rawurlencode($b);
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php
deleted file mode 100644
index 92ef30867..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using PLAINTEXT
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:09:43 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php';
-
-
-class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod
-{
- public function name ()
- {
- return 'PLAINTEXT';
- }
-
-
- /**
- * Calculate the signature using PLAINTEXT
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- return $request->urlencode($request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret));
- }
-
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $a = $request->urldecode($signature);
- $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret));
-
- return $request->urldecode($a) == $request->urldecode($b);
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php
deleted file mode 100644
index 3bbde7d90..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using PLAINTEXT
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:00:14 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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 OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod
-{
- public function name()
- {
- return 'RSA-SHA1';
- }
-
-
- /**
- * Fetch the public CERT key for the signature
- *
- * @param OAuthRequest request
- * @return string public key
- */
- protected function fetch_public_cert ( $request )
- {
- // not implemented yet, ideas are:
- // (1) do a lookup in a table of trusted certs keyed off of consumer
- // (2) fetch via http using a url provided by the requester
- // (3) some sort of specific discovery code based on request
- //
- // either way should return a string representation of the certificate
- throw OAuthException("OAuthSignatureMethod_RSA_SHA1::fetch_public_cert not implemented");
- }
-
-
- /**
- * Fetch the private CERT key for the signature
- *
- * @param OAuthRequest request
- * @return string private key
- */
- protected function fetch_private_cert ( $request )
- {
- // not implemented yet, ideas are:
- // (1) do a lookup in a table of trusted certs keyed off of consumer
- //
- // either way should return a string representation of the certificate
- throw OAuthException("OAuthSignatureMethod_RSA_SHA1::fetch_private_cert not implemented");
- }
-
-
- /**
- * Calculate the signature using RSA-SHA1
- * This function is copyright Andy Smith, 2008.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- public function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- // Fetch the private key cert based on the request
- $cert = $this->fetch_private_cert($request);
-
- // Pull the private key ID from the certificate
- $privatekeyid = openssl_get_privatekey($cert);
-
- // Sign using the key
- $sig = false;
- $ok = openssl_sign($base_string, $sig, $privatekeyid);
-
- // Release the key resource
- openssl_free_key($privatekeyid);
-
- return $request->urlencode(base64_encode($sig));
- }
-
-
- /**
- * Check if the request signature is the same as the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $decoded_sig = base64_decode($request->urldecode($signature));
-
- // Fetch the public key cert based on the request
- $cert = $this->fetch_public_cert($request);
-
- // Pull the public key ID from the certificate
- $publickeyid = openssl_get_publickey($cert);
-
- // Check the computed signature against the one passed in the query
- $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
-
- // Release the key resource
- openssl_free_key($publickeyid);
- return $ok == 1;
- }
-
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php b/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php
deleted file mode 100644
index e7cca981a..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-
-/**
- * Abstract base class for OAuthStore implementations
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-abstract class OAuthStoreAbstract
-{
- abstract public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' );
- abstract public function getSecretsForSignature ( $uri, $user_id );
- abstract public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' );
- abstract public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() );
-
- abstract public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getServer( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getServerForUri ( $uri, $user_id );
- abstract public function listServerTokens ( $user_id );
- abstract public function countServerTokens ( $consumer_key );
- abstract public function getServerToken ( $consumer_key, $token, $user_id );
- abstract public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false );
- abstract public function listServers ( $q = '', $user_id );
- abstract public function updateServer ( $server, $user_id, $user_is_admin = false );
-
- abstract public function updateConsumer ( $consumer, $user_id, $user_is_admin = false );
- abstract public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getConsumerStatic ();
-
- abstract public function addConsumerRequestToken ( $consumer_key, $options = array() );
- abstract public function getConsumerRequestToken ( $token );
- abstract public function deleteConsumerRequestToken ( $token );
- abstract public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' );
- abstract public function countConsumerAccessTokens ( $consumer_key );
- abstract public function exchangeConsumerRequestForAccessToken ( $token, $options = array() );
- abstract public function getConsumerAccessToken ( $token, $user_id );
- abstract public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false );
- abstract public function setConsumerAccessTokenTtl ( $token, $ttl );
-
- abstract public function listConsumers ( $user_id );
- abstract public function listConsumerTokens ( $user_id );
-
- abstract public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce );
-
- abstract public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null );
- abstract public function listLog ( $options, $user_id );
-
- abstract public function install ();
-
- /**
- * Fetch the current static consumer key for this site, create it when it was not found.
- * The consumer secret for the consumer key is always empty.
- *
- * @return string consumer key
- */
-
-
- /* ** Some handy utility functions ** */
-
- /**
- * Generate a unique key
- *
- * @param boolean unique force the key to be unique
- * @return string
- */
- public function generateKey ( $unique = false )
- {
- $key = md5(uniqid(rand(), true));
- if ($unique)
- {
- list($usec,$sec) = explode(' ',microtime());
- $key .= dechex($usec).dechex($sec);
- }
- return $key;
- }
-
- /**
- * Check to see if a string is valid utf8
- *
- * @param string $s
- * @return boolean
- */
- protected function isUTF8 ( $s )
- {
- return preg_match('%(?:
- [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
- |\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
- |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
- |\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
- |\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
- |[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
- |\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
- )+%xs', $s);
- }
-
-
- /**
- * Make a string utf8, replacing all non-utf8 chars with a '.'
- *
- * @param string
- * @return string
- */
- protected function makeUTF8 ( $s )
- {
- if (function_exists('iconv'))
- {
- do
- {
- $ok = true;
- $text = @iconv('UTF-8', 'UTF-8//TRANSLIT', $s);
- if (strlen($text) != strlen($s))
- {
- // Remove the offending character...
- $s = $text . '.' . substr($s, strlen($text) + 1);
- $ok = false;
- }
- }
- while (!$ok);
- }
- return $s;
- }
-
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php b/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php
deleted file mode 100644
index 9c971733f..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php
-
-/**
- * Storage container for the oauth credentials, both server and consumer side.
- * This file can only be used in conjunction with anyMeta.
- *
- * @version $Id: OAuthStoreAnyMeta.php 49 2008-10-01 09:43:19Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:03:30 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__) . '/../../../../core/inc/any_database.inc.php';
-require_once dirname(__FILE__) . '/OAuthStoreMySQL.php';
-
-
-class OAuthStoreAnymeta extends OAuthStoreMySQL
-{
- /**
- * Construct the OAuthStoreAnymeta
- *
- * @param array options
- */
- function __construct ( $options = array() )
- {
- parent::__construct(array('conn' => any_db_conn()));
- }
-
-
- /**
- * Add an entry to the log table
- *
- * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token)
- * @param string received
- * @param string sent
- * @param string base_string
- * @param string notes
- * @param int (optional) user_id
- */
- public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null )
- {
- if (is_null($user_id) && isset($GLOBALS['any_auth']))
- {
- $user_id = $GLOBALS['any_auth']->getUserId();
- }
- parent::addLog($keys, $received, $sent, $base_string, $notes, $user_id);
- }
-
-
- /**
- * Get a page of entries from the log. Returns the last 100 records
- * matching the options given.
- *
- * @param array options
- * @param int user_id current user
- * @return array log records
- */
- public function listLog ( $options, $user_id )
- {
- $where = array();
- $args = array();
- if (empty($options))
- {
- $where[] = 'olg_usa_id_ref = %d';
- $args[] = $user_id;
- }
- else
- {
- foreach ($options as $option => $value)
- {
- if (strlen($value) > 0)
- {
- switch ($option)
- {
- case 'osr_consumer_key':
- case 'ocr_consumer_key':
- case 'ost_token':
- case 'oct_token':
- $where[] = 'olg_'.$option.' = \'%s\'';
- $args[] = $value;
- break;
- }
- }
- }
-
- $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)';
- $args[] = $user_id;
- }
-
- $rs = any_db_query_all_assoc('
- SELECT olg_id,
- olg_osr_consumer_key AS osr_consumer_key,
- olg_ost_token AS ost_token,
- olg_ocr_consumer_key AS ocr_consumer_key,
- olg_oct_token AS oct_token,
- olg_usa_id_ref AS user_id,
- olg_received AS received,
- olg_sent AS sent,
- olg_base_string AS base_string,
- olg_notes AS notes,
- olg_timestamp AS timestamp,
- INET_NTOA(olg_remote_ip) AS remote_ip
- FROM oauth_log
- WHERE '.implode(' AND ', $where).'
- ORDER BY olg_id DESC
- LIMIT 0,100', $args);
-
- return $rs;
- }
-
-
-
- /**
- * Initialise the database
- */
- public function install ()
- {
- parent::install();
-
- any_db_query("ALTER TABLE oauth_consumer_registry MODIFY ocr_usa_id_ref int(11) unsigned");
- any_db_query("ALTER TABLE oauth_consumer_token MODIFY oct_usa_id_ref int(11) unsigned not null");
- any_db_query("ALTER TABLE oauth_server_registry MODIFY osr_usa_id_ref int(11) unsigned");
- any_db_query("ALTER TABLE oauth_server_token MODIFY ost_usa_id_ref int(11) unsigned not null");
- any_db_query("ALTER TABLE oauth_log MODIFY olg_usa_id_ref int(11) unsigned");
-
- any_db_alter_add_fk('oauth_consumer_registry', 'ocr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null');
- any_db_alter_add_fk('oauth_consumer_token', 'oct_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade');
- any_db_alter_add_fk('oauth_server_registry', 'osr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null');
- any_db_alter_add_fk('oauth_server_token', 'ost_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade');
- any_db_alter_add_fk('oauth_log', 'olg_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade');
- }
-
-
-
- /** Some simple helper functions for querying the mysql db **/
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- */
- protected function query ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- any_db_query($sql, $args);
- }
-
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_all_assoc ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_all_assoc($sql, $args);
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row_assoc ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_row_assoc($sql, $args);
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_row($sql, $args);
- }
-
-
- /**
- * Perform a query, return the first column of the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return mixed
- */
- protected function query_one ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_one($sql, $args);
- }
-
-
- /**
- * Return the number of rows affected in the last query
- *
- * @return int
- */
- protected function query_affected_rows ()
- {
- return any_db_affected_rows();
- }
-
-
- /**
- * Return the id of the last inserted row
- *
- * @return int
- */
- protected function query_insert_id ()
- {
- return any_db_insert_id();
- }
-
-
- private function sql_args ( $args )
- {
- $sql = array_shift($args);
- if (count($args) == 1 && is_array($args[0]))
- {
- $args = $args[0];
- }
- return array($sql, $args);
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php b/mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php
deleted file mode 100644
index a1b04c5c8..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php
+++ /dev/null
@@ -1,1879 +0,0 @@
-<?php
-
-/**
- * Storage container for the oauth credentials, both server and consumer side.
- * Based on MySQL
- *
- * @version $Id: OAuthStoreMySQL.php 64 2009-08-16 19:37:00Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:03:30 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-
-require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php';
-
-
-class OAuthStoreMySQL extends OAuthStoreAbstract
-{
- /**
- * The MySQL connection
- */
- protected $conn;
-
- /**
- * Maximum delta a timestamp may be off from a previous timestamp.
- * Allows multiple consumers with some clock skew to work with the same token.
- * Unit is seconds, default max skew is 10 minutes.
- */
- protected $max_timestamp_skew = 600;
-
- /**
- * Default ttl for request tokens
- */
- protected $max_request_token_ttl = 3600;
-
-
- /**
- * Construct the OAuthStoreMySQL.
- * In the options you have to supply either:
- * - server, username, password and database (for a mysql_connect)
- * - conn (for the connection to be used)
- *
- * @param array options
- */
- function __construct ( $options = array() )
- {
- if (isset($options['conn']))
- {
- $this->conn = $options['conn'];
- }
- else
- {
- if (isset($options['server']))
- {
- $server = $options['server'];
- $username = $options['username'];
-
- if (isset($options['password']))
- {
- $this->conn = mysql_connect($server, $username, $options['password']);
- }
- else
- {
- $this->conn = mysql_connect($server, $username);
- }
- }
- else
- {
- // Try the default mysql connect
- $this->conn = mysql_connect();
- }
-
- if ($this->conn === false)
- {
- throw new OAuthException('Could not connect to MySQL database: ' . mysql_error());
- }
-
- if (isset($options['database']))
- {
- if (!mysql_select_db($options['database'], $this->conn))
- {
- $this->sql_errcheck();
- }
- }
- $this->query('set character set utf8');
- }
- }
-
-
- /**
- * Find stored credentials for the consumer key and token. Used by an OAuth server
- * when verifying an OAuth request.
- *
- * @param string consumer_key
- * @param string token
- * @param string token_type false, 'request' or 'access'
- * @exception OAuthException when no secrets where found
- * @return array assoc (consumer_secret, token_secret, osr_id, ost_id, user_id)
- */
- public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' )
- {
- if ($token_type === false)
- {
- $rs = $this->query_row_assoc('
- SELECT osr_id,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret
- FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND osr_enabled = 1
- ',
- $consumer_key);
-
- if ($rs)
- {
- $rs['token'] = false;
- $rs['token_secret'] = false;
- $rs['user_id'] = false;
- $rs['ost_id'] = false;
- }
- }
- else
- {
- $rs = $this->query_row_assoc('
- SELECT osr_id,
- ost_id,
- ost_usa_id_ref as user_id,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- ost_token as token,
- ost_token_secret as token_secret
- FROM oauth_server_registry
- JOIN oauth_server_token
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'%s\'
- AND osr_consumer_key = \'%s\'
- AND ost_token = \'%s\'
- AND osr_enabled = 1
- AND ost_token_ttl >= NOW()
- ',
- $token_type, $consumer_key, $token);
- }
-
- if (empty($rs))
- {
- throw new OAuthException('The consumer_key "'.$consumer_key.'" token "'.$token.'" combination does not exist or is not enabled.');
- }
- return $rs;
- }
-
-
- /**
- * Find the server details for signing a request, always looks for an access token.
- * The returned credentials depend on which local user is making the request.
- *
- * The consumer_key must belong to the user or be public (user id is null)
- *
- * For signing we need all of the following:
- *
- * consumer_key consumer key associated with the server
- * consumer_secret consumer secret associated with this server
- * token access token associated with this server
- * token_secret secret for the access token
- * signature_methods signing methods supported by the server (array)
- *
- * @todo filter on token type (we should know how and with what to sign this request, and there might be old access tokens)
- * @param string uri uri of the server
- * @param int user_id id of the logged on user
- * @param string name (optional) name of the token (case sensitive)
- * @exception OAuthException when no credentials found
- * @return array
- */
- public function getSecretsForSignature ( $uri, $user_id, $name = '' )
- {
- // Find a consumer key and token for the given uri
- $ps = parse_url($uri);
- $host = isset($ps['host']) ? $ps['host'] : 'localhost';
- $path = isset($ps['path']) ? $ps['path'] : '';
-
- if (empty($path) || substr($path, -1) != '/')
- {
- $path .= '/';
- }
-
- // The owner of the consumer_key is either the user or nobody (public consumer key)
- $secrets = $this->query_row_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_token as token,
- oct_token_secret as token_secret,
- ocr_signature_methods as signature_methods
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token ON oct_ocr_id_ref = ocr_id
- WHERE ocr_server_uri_host = \'%s\'
- AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path))
- AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL)
- AND oct_usa_id_ref = %d
- AND oct_token_type = \'access\'
- AND oct_name = \'%s\'
- AND oct_token_ttl >= NOW()
- ORDER BY ocr_usa_id_ref DESC, ocr_consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC
- LIMIT 0,1
- ', $host, $path, $user_id, $user_id, $name
- );
-
- if (empty($secrets))
- {
- throw new OAuthException('No server tokens available for '.$uri);
- }
- $secrets['signature_methods'] = explode(',', $secrets['signature_methods']);
- return $secrets;
- }
-
-
- /**
- * Get the token and token secret we obtained from a server.
- *
- * @param string consumer_key
- * @param string token
- * @param string token_type
- * @param int user_id the user owning the token
- * @param string name optional name for a named token
- * @exception OAuthException when no credentials found
- * @return array
- */
- public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' )
- {
- if ($token_type != 'request' && $token_type != 'access')
- {
- throw new OAuthException('Unkown token type "'.$token_type.'", must be either "request" or "access"');
- }
-
- // Take the most recent token of the given type
- $r = $this->query_row_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_token as token,
- oct_token_secret as token_secret,
- oct_name as token_name,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri,
- IF(oct_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(oct_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_token_type = \'%s\'
- AND oct_token = \'%s\'
- AND oct_usa_id_ref = %d
- AND oct_token_ttl >= NOW()
- ', $consumer_key, $token_type, $token, $user_id
- );
-
- if (empty($r))
- {
- throw new OAuthException('Could not find a "'.$token_type.'" token for consumer "'.$consumer_key.'" and user '.$user_id);
- }
- if (isset($r['signature_methods']) && !empty($r['signature_methods']))
- {
- $r['signature_methods'] = explode(',',$r['signature_methods']);
- }
- else
- {
- $r['signature_methods'] = array();
- }
- return $r;
- }
-
-
- /**
- * Add a request token we obtained from a server.
- *
- * @todo remove old tokens for this user and this ocr_id
- * @param string consumer_key key of the server in the consumer registry
- * @param string token_type one of 'request' or 'access'
- * @param string token
- * @param string token_secret
- * @param int user_id the user owning the token
- * @param array options extra options, name and token_ttl
- * @exception OAuthException when server is not known
- * @exception OAuthException when we received a duplicate token
- */
- public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() )
- {
- if ($token_type != 'request' && $token_type != 'access')
- {
- throw new OAuthException('Unknown token type "'.$token_type.'", must be either "request" or "access"');
- }
-
- // Maximum time to live for this token
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $ttl = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)';
- }
- else if ($token == 'request')
- {
- $ttl = 'DATE_ADD(NOW(), INTERVAL '.$this->max_request_token_ttl.' SECOND)';
- }
- else
- {
- $ttl = "'9999-12-31'";
- }
-
- $ocr_id = $this->query_one('
- SELECT ocr_id
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- ', $consumer_key);
-
- if (empty($ocr_id))
- {
- throw new OAuthException('No server associated with consumer_key "'.$consumer_key.'"');
- }
-
- // Named tokens, unique per user/consumer key
- if (isset($options['name']) && $options['name'] != '')
- {
- $name = $options['name'];
- }
- else
- {
- $name = '';
- }
-
- // Delete any old tokens with the same type and name for this user/server combination
- $this->query('
- DELETE FROM oauth_consumer_token
- WHERE oct_ocr_id_ref = %d
- AND oct_usa_id_ref = %d
- AND oct_token_type = LOWER(\'%s\')
- AND oct_name = \'%s\'
- ',
- $ocr_id,
- $user_id,
- $token_type,
- $name);
-
- // Insert the new token
- $this->query('
- INSERT IGNORE INTO oauth_consumer_token
- SET oct_ocr_id_ref = %d,
- oct_usa_id_ref = %d,
- oct_name = \'%s\',
- oct_token = \'%s\',
- oct_token_secret= \'%s\',
- oct_token_type = LOWER(\'%s\'),
- oct_timestamp = NOW(),
- oct_token_ttl = '.$ttl.'
- ',
- $ocr_id,
- $user_id,
- $name,
- $token,
- $token_secret,
- $token_type);
-
- if (!$this->query_affected_rows())
- {
- throw new OAuthException('Received duplicate token "'.$token.'" for the same consumer_key "'.$consumer_key.'"');
- }
- }
-
-
- /**
- * Delete a server key. This removes access to that site.
- *
- * @param string consumer_key
- * @param int user_id user registering this server
- * @param boolean user_is_admin
- */
- public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $consumer_key, $user_id);
- }
- else
- {
- $this->query('
- DELETE FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND ocr_usa_id_ref = %d
- ', $consumer_key, $user_id);
- }
- }
-
-
- /**
- * Get a server from the consumer registry using the consumer key
- *
- * @param string consumer_key
- * @param int user_id
- * @param boolean user_is_admin (optional)
- * @exception OAuthException when server is not found
- * @return array
- */
- public function getServer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- $r = $this->query_row_assoc('
- SELECT ocr_id as id,
- ocr_usa_id_ref as user_id,
- ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $consumer_key, $user_id);
-
- if (empty($r))
- {
- throw new OAuthException('No server with consumer_key "'.$consumer_key.'" has been registered (for this user)');
- }
-
- if (isset($r['signature_methods']) && !empty($r['signature_methods']))
- {
- $r['signature_methods'] = explode(',',$r['signature_methods']);
- }
- else
- {
- $r['signature_methods'] = array();
- }
- return $r;
- }
-
-
-
- /**
- * Find the server details that might be used for a request
- *
- * The consumer_key must belong to the user or be public (user id is null)
- *
- * @param string uri uri of the server
- * @param int user_id id of the logged on user
- * @exception OAuthException when no credentials found
- * @return array
- */
- public function getServerForUri ( $uri, $user_id )
- {
- // Find a consumer key and token for the given uri
- $ps = parse_url($uri);
- $host = isset($ps['host']) ? $ps['host'] : 'localhost';
- $path = isset($ps['path']) ? $ps['path'] : '';
-
- if (empty($path) || substr($path, -1) != '/')
- {
- $path .= '/';
- }
-
- // The owner of the consumer_key is either the user or nobody (public consumer key)
- $server = $this->query_row_assoc('
- SELECT ocr_id as id,
- ocr_usa_id_ref as user_id,
- ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri
- FROM oauth_consumer_registry
- WHERE ocr_server_uri_host = \'%s\'
- AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path))
- AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL)
- ORDER BY ocr_usa_id_ref DESC, consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC
- LIMIT 0,1
- ', $host, $path, $user_id
- );
-
- if (empty($server))
- {
- throw new OAuthException('No server available for '.$uri);
- }
- $server['signature_methods'] = explode(',', $server['signature_methods']);
- return $server;
- }
-
-
- /**
- * Get a list of all server token this user has access to.
- *
- * @param int usr_id
- * @return array
- */
- public function listServerTokens ( $user_id )
- {
- $ts = $this->query_all_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_id as token_id,
- oct_token as token,
- oct_token_secret as token_secret,
- oct_usa_id_ref as user_id,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_server_uri_host as server_uri_host,
- ocr_server_uri_path as server_uri_path,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri,
- oct_timestamp as timestamp
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token
- ON oct_ocr_id_ref = ocr_id
- WHERE oct_usa_id_ref = %d
- AND oct_token_type = \'access\'
- AND oct_token_ttl >= NOW()
- ORDER BY ocr_server_uri_host, ocr_server_uri_path
- ', $user_id);
- return $ts;
- }
-
-
- /**
- * Count how many tokens we have for the given server
- *
- * @param string consumer_key
- * @return int
- */
- public function countServerTokens ( $consumer_key )
- {
- $count = $this->query_one('
- SELECT COUNT(oct_id)
- FROM oauth_consumer_token
- JOIN oauth_consumer_registry
- ON oct_ocr_id_ref = ocr_id
- WHERE oct_token_type = \'access\'
- AND ocr_consumer_key = \'%s\'
- AND oct_token_ttl >= NOW()
- ', $consumer_key);
-
- return $count;
- }
-
-
- /**
- * Get a specific server token for the given user
- *
- * @param string consumer_key
- * @param string token
- * @param int user_id
- * @exception OAuthException when no such token found
- * @return array
- */
- public function getServerToken ( $consumer_key, $token, $user_id )
- {
- $ts = $this->query_row_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_token as token,
- oct_token_secret as token_secret,
- oct_usa_id_ref as usr_id,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_server_uri_host as server_uri_host,
- ocr_server_uri_path as server_uri_path,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri,
- oct_timestamp as timestamp
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_usa_id_ref = %d
- AND oct_token_type = \'access\'
- AND oct_token = \'%s\'
- AND oct_token_ttl >= NOW()
- ', $consumer_key, $user_id, $token);
-
- if (empty($ts))
- {
- throw new OAuthException('No such consumer key ('.$consumer_key.') and token ('.$token.') combination for user "'.$user_id.'"');
- }
- return $ts;
- }
-
-
- /**
- * Delete a token we obtained from a server.
- *
- * @param string consumer_key
- * @param string token
- * @param int user_id
- * @param boolean user_is_admin
- */
- public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE oauth_consumer_token
- FROM oauth_consumer_token
- JOIN oauth_consumer_registry
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_token = \'%s\'
- ', $consumer_key, $token);
- }
- else
- {
- $this->query('
- DELETE oauth_consumer_token
- FROM oauth_consumer_token
- JOIN oauth_consumer_registry
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_token = \'%s\'
- AND oct_usa_id_ref = %d
- ', $consumer_key, $token, $user_id);
- }
- }
-
-
- /**
- * Set the ttl of a server access token. This is done when the
- * server receives a valid request with a xoauth_token_ttl parameter in it.
- *
- * @param string consumer_key
- * @param string token
- * @param int token_ttl
- */
- public function setServerTokenTtl ( $consumer_key, $token, $token_ttl )
- {
- if ($token_ttl <= 0)
- {
- // Immediate delete when the token is past its ttl
- $this->deleteServerToken($consumer_key, $token, 0, true);
- }
- else
- {
- // Set maximum time to live for this token
- $this->query('
- UPDATE oauth_consumer_token, oauth_consumer_registry
- SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND)
- WHERE ocr_consumer_key = \'%s\'
- AND oct_ocr_id_ref = ocr_id
- AND oct_token = \'%s\'
- ', $token_ttl, $consumer_key, $token);
- }
- }
-
-
- /**
- * Get a list of all consumers from the consumer registry.
- * The consumer keys belong to the user or are public (user id is null)
- *
- * @param string q query term
- * @param int user_id
- * @return array
- */
- public function listServers ( $q = '', $user_id )
- {
- $q = trim(str_replace('%', '', $q));
- $args = array();
-
- if (!empty($q))
- {
- $where = ' WHERE ( ocr_consumer_key like \'%%%s%%\'
- OR ocr_server_uri like \'%%%s%%\'
- OR ocr_server_uri_host like \'%%%s%%\'
- OR ocr_server_uri_path like \'%%%s%%\')
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ';
-
- $args[] = $q;
- $args[] = $q;
- $args[] = $q;
- $args[] = $q;
- $args[] = $user_id;
- }
- else
- {
- $where = ' WHERE ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL';
- $args[] = $user_id;
- }
-
- $servers = $this->query_all_assoc('
- SELECT ocr_id as id,
- ocr_usa_id_ref as user_id,
- ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_server_uri_host as server_uri_host,
- ocr_server_uri_path as server_uri_path,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri
- FROM oauth_consumer_registry
- '.$where.'
- ORDER BY ocr_server_uri_host, ocr_server_uri_path
- ', $args);
- return $servers;
- }
-
-
- /**
- * Register or update a server for our site (we will be the consumer)
- *
- * (This is the registry at the consumers, registering servers ;-) )
- *
- * @param array server
- * @param int user_id user registering this server
- * @param boolean user_is_admin
- * @exception OAuthException when fields are missing or on duplicate consumer_key
- * @return consumer_key
- */
- public function updateServer ( $server, $user_id, $user_is_admin = false )
- {
- foreach (array('consumer_key', 'server_uri') as $f)
- {
- if (empty($server[$f]))
- {
- throw new OAuthException('The field "'.$f.'" must be set and non empty');
- }
- }
-
- if (!empty($server['id']))
- {
- $exists = $this->query_one('
- SELECT ocr_id
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND ocr_id <> %d
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $server['consumer_key'], $server['id'], $user_id);
- }
- else
- {
- $exists = $this->query_one('
- SELECT ocr_id
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $server['consumer_key'], $user_id);
- }
-
- if ($exists)
- {
- throw new OAuthException('The server with key "'.$server['consumer_key'].'" has already been registered');
- }
-
- $parts = parse_url($server['server_uri']);
- $host = (isset($parts['host']) ? $parts['host'] : 'localhost');
- $path = (isset($parts['path']) ? $parts['path'] : '/');
-
- if (isset($server['signature_methods']))
- {
- if (is_array($server['signature_methods']))
- {
- $server['signature_methods'] = strtoupper(implode(',', $server['signature_methods']));
- }
- }
- else
- {
- $server['signature_methods'] = '';
- }
-
- // When the user is an admin, then the user can update the user_id of this record
- if ($user_is_admin && array_key_exists('user_id', $server))
- {
- if (is_null($server['user_id']))
- {
- $update_user = ', ocr_usa_id_ref = NULL';
- }
- else
- {
- $update_user = ', ocr_usa_id_ref = '.intval($server['user_id']);
- }
- }
- else
- {
- $update_user = '';
- }
-
- if (!empty($server['id']))
- {
- // Check if the current user can update this server definition
- if (!$user_is_admin)
- {
- $ocr_usa_id_ref = $this->query_one('
- SELECT ocr_usa_id_ref
- FROM oauth_consumer_registry
- WHERE ocr_id = %d
- ', $server['id']);
-
- if ($ocr_usa_id_ref != $user_id)
- {
- throw new OAuthException('The user "'.$user_id.'" is not allowed to update this server');
- }
- }
-
- // Update the consumer registration
- $this->query('
- UPDATE oauth_consumer_registry
- SET ocr_consumer_key = \'%s\',
- ocr_consumer_secret = \'%s\',
- ocr_server_uri = \'%s\',
- ocr_server_uri_host = \'%s\',
- ocr_server_uri_path = \'%s\',
- ocr_timestamp = NOW(),
- ocr_request_token_uri = \'%s\',
- ocr_authorize_uri = \'%s\',
- ocr_access_token_uri = \'%s\',
- ocr_signature_methods = \'%s\'
- '.$update_user.'
- WHERE ocr_id = %d
- ',
- $server['consumer_key'],
- $server['consumer_secret'],
- $server['server_uri'],
- strtolower($host),
- $path,
- isset($server['request_token_uri']) ? $server['request_token_uri'] : '',
- isset($server['authorize_uri']) ? $server['authorize_uri'] : '',
- isset($server['access_token_uri']) ? $server['access_token_uri'] : '',
- $server['signature_methods'],
- $server['id']
- );
- }
- else
- {
- if (empty($update_user))
- {
- // Per default the user owning the key is the user registering the key
- $update_user = ', ocr_usa_id_ref = '.intval($user_id);
- }
-
- $this->query('
- INSERT INTO oauth_consumer_registry
- SET ocr_consumer_key = \'%s\',
- ocr_consumer_secret = \'%s\',
- ocr_server_uri = \'%s\',
- ocr_server_uri_host = \'%s\',
- ocr_server_uri_path = \'%s\',
- ocr_timestamp = NOW(),
- ocr_request_token_uri = \'%s\',
- ocr_authorize_uri = \'%s\',
- ocr_access_token_uri = \'%s\',
- ocr_signature_methods = \'%s\'
- '.$update_user,
- $server['consumer_key'],
- $server['consumer_secret'],
- $server['server_uri'],
- strtolower($host),
- $path,
- isset($server['request_token_uri']) ? $server['request_token_uri'] : '',
- isset($server['authorize_uri']) ? $server['authorize_uri'] : '',
- isset($server['access_token_uri']) ? $server['access_token_uri'] : '',
- $server['signature_methods']
- );
-
- $ocr_id = $this->query_insert_id();
- }
- return $server['consumer_key'];
- }
-
-
- /**
- * Insert/update a new consumer with this server (we will be the server)
- * When this is a new consumer, then also generate the consumer key and secret.
- * Never updates the consumer key and secret.
- * When the id is set, then the key and secret must correspond to the entry
- * being updated.
- *
- * (This is the registry at the server, registering consumers ;-) )
- *
- * @param array consumer
- * @param int user_id user registering this consumer
- * @param boolean user_is_admin
- * @return string consumer key
- */
- public function updateConsumer ( $consumer, $user_id, $user_is_admin = false )
- {
- if (!$user_is_admin)
- {
- foreach (array('requester_name', 'requester_email') as $f)
- {
- if (empty($consumer[$f]))
- {
- throw new OAuthException('The field "'.$f.'" must be set and non empty');
- }
- }
- }
-
- if (!empty($consumer['id']))
- {
- if (empty($consumer['consumer_key']))
- {
- throw new OAuthException('The field "consumer_key" must be set and non empty');
- }
- if (!$user_is_admin && empty($consumer['consumer_secret']))
- {
- throw new OAuthException('The field "consumer_secret" must be set and non empty');
- }
-
- // Check if the current user can update this server definition
- if (!$user_is_admin)
- {
- $osr_usa_id_ref = $this->query_one('
- SELECT osr_usa_id_ref
- FROM oauth_server_registry
- WHERE osr_id = %d
- ', $consumer['id']);
-
- if ($osr_usa_id_ref != $user_id)
- {
- throw new OAuthException('The user "'.$user_id.'" is not allowed to update this consumer');
- }
- }
- else
- {
- // User is an admin, allow a key owner to be changed or key to be shared
- if (array_key_exists('user_id',$consumer))
- {
- if (is_null($consumer['user_id']))
- {
- $this->query('
- UPDATE oauth_server_registry
- SET osr_usa_id_ref = NULL
- WHERE osr_id = %d
- ', $consumer['id']);
- }
- else
- {
- $this->query('
- UPDATE oauth_server_registry
- SET osr_usa_id_ref = %d
- WHERE osr_id = %d
- ', $consumer['user_id'], $consumer['id']);
- }
- }
- }
-
- $this->query('
- UPDATE oauth_server_registry
- SET osr_requester_name = \'%s\',
- osr_requester_email = \'%s\',
- osr_callback_uri = \'%s\',
- osr_application_uri = \'%s\',
- osr_application_title = \'%s\',
- osr_application_descr = \'%s\',
- osr_application_notes = \'%s\',
- osr_application_type = \'%s\',
- osr_application_commercial = IF(%d,1,0),
- osr_timestamp = NOW()
- WHERE osr_id = %d
- AND osr_consumer_key = \'%s\'
- AND osr_consumer_secret = \'%s\'
- ',
- $consumer['requester_name'],
- $consumer['requester_email'],
- isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '',
- isset($consumer['application_uri']) ? $consumer['application_uri'] : '',
- isset($consumer['application_title']) ? $consumer['application_title'] : '',
- isset($consumer['application_descr']) ? $consumer['application_descr'] : '',
- isset($consumer['application_notes']) ? $consumer['application_notes'] : '',
- isset($consumer['application_type']) ? $consumer['application_type'] : '',
- isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0,
- $consumer['id'],
- $consumer['consumer_key'],
- $consumer['consumer_secret']
- );
-
-
- $consumer_key = $consumer['consumer_key'];
- }
- else
- {
- $consumer_key = $this->generateKey(true);
- $consumer_secret= $this->generateKey();
-
- // When the user is an admin, then the user can be forced to something else that the user
- if ($user_is_admin && array_key_exists('user_id',$consumer))
- {
- if (is_null($consumer['user_id']))
- {
- $owner_id = 'NULL';
- }
- else
- {
- $owner_id = intval($consumer['user_id']);
- }
- }
- else
- {
- // No admin, take the user id as the owner id.
- $owner_id = intval($user_id);
- }
-
- $this->query('
- INSERT INTO oauth_server_registry
- SET osr_enabled = 1,
- osr_status = \'active\',
- osr_usa_id_ref = %s,
- osr_consumer_key = \'%s\',
- osr_consumer_secret = \'%s\',
- osr_requester_name = \'%s\',
- osr_requester_email = \'%s\',
- osr_callback_uri = \'%s\',
- osr_application_uri = \'%s\',
- osr_application_title = \'%s\',
- osr_application_descr = \'%s\',
- osr_application_notes = \'%s\',
- osr_application_type = \'%s\',
- osr_application_commercial = IF(%d,1,0),
- osr_timestamp = NOW(),
- osr_issue_date = NOW()
- ',
- $owner_id,
- $consumer_key,
- $consumer_secret,
- $consumer['requester_name'],
- $consumer['requester_email'],
- isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '',
- isset($consumer['application_uri']) ? $consumer['application_uri'] : '',
- isset($consumer['application_title']) ? $consumer['application_title'] : '',
- isset($consumer['application_descr']) ? $consumer['application_descr'] : '',
- isset($consumer['application_notes']) ? $consumer['application_notes'] : '',
- isset($consumer['application_type']) ? $consumer['application_type'] : '',
- isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0
- );
- }
- return $consumer_key;
-
- }
-
-
-
- /**
- * Delete a consumer key. This removes access to our site for all applications using this key.
- *
- * @param string consumer_key
- * @param int user_id user registering this server
- * @param boolean user_is_admin
- */
- public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL)
- ', $consumer_key, $user_id);
- }
- else
- {
- $this->query('
- DELETE FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND osr_usa_id_ref = %d
- ', $consumer_key, $user_id);
- }
- }
-
-
-
- /**
- * Fetch a consumer of this server, by consumer_key.
- *
- * @param string consumer_key
- * @param int user_id
- * @param boolean user_is_admin (optional)
- * @exception OAuthException when consumer not found
- * @return array
- */
- public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- $consumer = $this->query_row_assoc('
- SELECT *
- FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- ', $consumer_key);
-
- if (!is_array($consumer))
- {
- throw new OAuthException('No consumer with consumer_key "'.$consumer_key.'"');
- }
-
- $c = array();
- foreach ($consumer as $key => $value)
- {
- $c[substr($key, 4)] = $value;
- }
- $c['user_id'] = $c['usa_id_ref'];
-
- if (!$user_is_admin && !empty($c['user_id']) && $c['user_id'] != $user_id)
- {
- throw new OAuthException('No access to the consumer information for consumer_key "'.$consumer_key.'"');
- }
- return $c;
- }
-
-
- /**
- * Fetch the static consumer key for this provider. The user for the static consumer
- * key is NULL (no user, shared key). If the key did not exist then the key is created.
- *
- * @return string
- */
- public function getConsumerStatic ()
- {
- $consumer = $this->query_one('
- SELECT osr_consumer_key
- FROM oauth_server_registry
- WHERE osr_consumer_key LIKE \'sc-%%\'
- AND osr_usa_id_ref IS NULL
- ');
-
- if (empty($consumer))
- {
- $consumer_key = 'sc-'.$this->generateKey(true);
- $this->query('
- INSERT INTO oauth_server_registry
- SET osr_enabled = 1,
- osr_status = \'active\',
- osr_usa_id_ref = NULL,
- osr_consumer_key = \'%s\',
- osr_consumer_secret = \'\',
- osr_requester_name = \'\',
- osr_requester_email = \'\',
- osr_callback_uri = \'\',
- osr_application_uri = \'\',
- osr_application_title = \'Static shared consumer key\',
- osr_application_descr = \'\',
- osr_application_notes = \'Static shared consumer key\',
- osr_application_type = \'\',
- osr_application_commercial = 0,
- osr_timestamp = NOW(),
- osr_issue_date = NOW()
- ',
- $consumer_key
- );
-
- // Just make sure that if the consumer key is truncated that we get the truncated string
- $consumer = $this->getConsumerStatic();
- }
- return $consumer;
- }
-
-
- /**
- * Add an unautorized request token to our server.
- *
- * @param string consumer_key
- * @param array options (eg. token_ttl)
- * @return array (token, token_secret)
- */
- public function addConsumerRequestToken ( $consumer_key, $options = array() )
- {
- $token = $this->generateKey(true);
- $secret = $this->generateKey();
- $osr_id = $this->query_one('
- SELECT osr_id
- FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND osr_enabled = 1
- ', $consumer_key);
-
- if (!$osr_id)
- {
- throw new OAuthException('No server with consumer_key "'.$consumer_key.'" or consumer_key is disabled');
- }
-
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $ttl = intval($options['token_ttl']);
- }
- else
- {
- $ttl = $this->max_request_token_ttl;
- }
-
- $this->query('
- INSERT INTO oauth_server_token
- SET ost_osr_id_ref = %d,
- ost_usa_id_ref = 1,
- ost_token = \'%s\',
- ost_token_secret = \'%s\',
- ost_token_type = \'request\',
- ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND)
- ON DUPLICATE KEY UPDATE
- ost_osr_id_ref = VALUES(ost_osr_id_ref),
- ost_usa_id_ref = VALUES(ost_usa_id_ref),
- ost_token = VALUES(ost_token),
- ost_token_secret = VALUES(ost_token_secret),
- ost_token_type = VALUES(ost_token_type),
- ost_token_ttl = VALUES(ost_token_ttl),
- ost_timestamp = NOW()
- ', $osr_id, $token, $secret, $ttl);
-
- return array('token'=>$token, 'token_secret'=>$secret, 'token_ttl'=>$ttl);
- }
-
-
- /**
- * Fetch the consumer request token, by request token.
- *
- * @param string token
- * @return array token and consumer details
- */
- public function getConsumerRequestToken ( $token )
- {
- $rs = $this->query_row_assoc('
- SELECT ost_token as token,
- ost_token_secret as token_secret,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- ost_token_type as token_type
- FROM oauth_server_token
- JOIN oauth_server_registry
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'request\'
- AND ost_token = \'%s\'
- AND ost_token_ttl >= NOW()
- ', $token);
-
- return $rs;
- }
-
-
- /**
- * Delete a consumer token. The token must be a request or authorized token.
- *
- * @param string token
- */
- public function deleteConsumerRequestToken ( $token )
- {
- $this->query('
- DELETE FROM oauth_server_token
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'request\'
- ', $token);
- }
-
-
- /**
- * Upgrade a request token to be an authorized request token.
- *
- * @param string token
- * @param int user_id user authorizing the token
- * @param string referrer_host used to set the referrer host for this token, for user feedback
- */
- public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' )
- {
- $this->query('
- UPDATE oauth_server_token
- SET ost_authorized = 1,
- ost_usa_id_ref = %d,
- ost_timestamp = NOW(),
- ost_referrer_host = \'%s\'
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'request\'
- ', $user_id, $referrer_host, $token);
- }
-
-
- /**
- * Count the consumer access tokens for the given consumer.
- *
- * @param string consumer_key
- * @return int
- */
- public function countConsumerAccessTokens ( $consumer_key )
- {
- $count = $this->query_one('
- SELECT COUNT(ost_id)
- FROM oauth_server_token
- JOIN oauth_server_registry
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'access\'
- AND osr_consumer_key = \'%s\'
- AND ost_token_ttl >= NOW()
- ', $consumer_key);
-
- return $count;
- }
-
-
- /**
- * Exchange an authorized request token for new access token.
- *
- * @param string token
- * @param array options options for the token, token_ttl
- * @exception OAuthException when token could not be exchanged
- * @return array (token, token_secret)
- */
- public function exchangeConsumerRequestForAccessToken ( $token, $options = array() )
- {
- $new_token = $this->generateKey(true);
- $new_secret = $this->generateKey();
-
- // Maximum time to live for this token
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $ttl_sql = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)';
- }
- else
- {
- $ttl_sql = "'9999-12-31'";
- }
-
- $this->query('
- UPDATE oauth_server_token
- SET ost_token = \'%s\',
- ost_token_secret = \'%s\',
- ost_token_type = \'access\',
- ost_timestamp = NOW(),
- ost_token_ttl = '.$ttl_sql.'
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'request\'
- AND ost_authorized = 1
- AND ost_token_ttl >= NOW()
- ', $new_token, $new_secret, $token);
-
- if ($this->query_affected_rows() != 1)
- {
- throw new OAuthException('Can\'t exchange request token "'.$token.'" for access token. No such token or not authorized');
- }
-
- $ret = array('token' => $new_token, 'token_secret' => $new_secret);
- $ttl = $this->query_one('
- SELECT IF(ost_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(ost_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl
- FROM oauth_server_token
- WHERE ost_token = \'%s\'', $new_token);
-
- if (is_numeric($ttl))
- {
- $ret['token_ttl'] = intval($ttl);
- }
- return $ret;
- }
-
-
- /**
- * Fetch the consumer access token, by access token.
- *
- * @param string token
- * @param int user_id
- * @exception OAuthException when token is not found
- * @return array token and consumer details
- */
- public function getConsumerAccessToken ( $token, $user_id )
- {
- $rs = $this->query_row_assoc('
- SELECT ost_token as token,
- ost_token_secret as token_secret,
- ost_referrer_host as token_referrer_host,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- osr_application_uri as application_uri,
- osr_application_title as application_title,
- osr_application_descr as application_descr
- FROM oauth_server_token
- JOIN oauth_server_registry
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'access\'
- AND ost_token = \'%s\'
- AND ost_usa_id_ref = %d
- AND ost_token_ttl >= NOW()
- ', $token, $user_id);
-
- if (empty($rs))
- {
- throw new OAuthException('No server_token "'.$token.'" for user "'.$user_id.'"');
- }
- return $rs;
- }
-
-
- /**
- * Delete a consumer access token.
- *
- * @param string token
- * @param int user_id
- * @param boolean user_is_admin
- */
- public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE FROM oauth_server_token
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'access\'
- ', $token);
- }
- else
- {
- $this->query('
- DELETE FROM oauth_server_token
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'access\'
- AND ost_usa_id_ref = %d
- ', $token, $user_id);
- }
- }
-
-
- /**
- * Set the ttl of a consumer access token. This is done when the
- * server receives a valid request with a xoauth_token_ttl parameter in it.
- *
- * @param string token
- * @param int ttl
- */
- public function setConsumerAccessTokenTtl ( $token, $token_ttl )
- {
- if ($token_ttl <= 0)
- {
- // Immediate delete when the token is past its ttl
- $this->deleteConsumerAccessToken($token, 0, true);
- }
- else
- {
- // Set maximum time to live for this token
- $this->query('
- UPDATE oauth_server_token
- SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND)
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'access\'
- ', $token_ttl, $token);
- }
- }
-
-
- /**
- * Fetch a list of all consumer keys, secrets etc.
- * Returns the public (user_id is null) and the keys owned by the user
- *
- * @param int user_id
- * @return array
- */
- public function listConsumers ( $user_id )
- {
- $rs = $this->query_all_assoc('
- SELECT osr_id as id,
- osr_usa_id_ref as user_id,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- osr_enabled as enabled,
- osr_status as status,
- osr_issue_date as issue_date,
- osr_application_uri as application_uri,
- osr_application_title as application_title,
- osr_application_descr as application_descr,
- osr_requester_name as requester_name,
- osr_requester_email as requester_email
- FROM oauth_server_registry
- WHERE (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL)
- ORDER BY osr_application_title
- ', $user_id);
- return $rs;
- }
-
-
- /**
- * Fetch a list of all consumer tokens accessing the account of the given user.
- *
- * @param int user_id
- * @return array
- */
- public function listConsumerTokens ( $user_id )
- {
- $rs = $this->query_all_assoc('
- SELECT osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- osr_enabled as enabled,
- osr_status as status,
- osr_application_uri as application_uri,
- osr_application_title as application_title,
- osr_application_descr as application_descr,
- ost_timestamp as timestamp,
- ost_token as token,
- ost_token_secret as token_secret,
- ost_referrer_host as token_referrer_host
- FROM oauth_server_registry
- JOIN oauth_server_token
- ON ost_osr_id_ref = osr_id
- WHERE ost_usa_id_ref = %d
- AND ost_token_type = \'access\'
- AND ost_token_ttl >= NOW()
- ORDER BY osr_application_title
- ', $user_id);
- return $rs;
- }
-
-
- /**
- * Check an nonce/timestamp combination. Clears any nonce combinations
- * that are older than the one received.
- *
- * @param string consumer_key
- * @param string token
- * @param int timestamp
- * @param string nonce
- * @exception OAuthException thrown when the timestamp is not in sequence or nonce is not unique
- */
- public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce )
- {
- $r = $this->query_row('
- SELECT MAX(osn_timestamp), MAX(osn_timestamp) > %d + %d
- FROM oauth_server_nonce
- WHERE osn_consumer_key = \'%s\'
- AND osn_token = \'%s\'
- ', $timestamp, $this->max_timestamp_skew, $consumer_key, $token);
-
- if (!empty($r) && $r[1])
- {
- throw new OAuthException('Timestamp is out of sequence. Request rejected. Got '.$timestamp.' last max is '.$r[0].' allowed skew is '.$this->max_timestamp_skew);
- }
-
- // Insert the new combination
- $this->query('
- INSERT IGNORE INTO oauth_server_nonce
- SET osn_consumer_key = \'%s\',
- osn_token = \'%s\',
- osn_timestamp = %d,
- osn_nonce = \'%s\'
- ', $consumer_key, $token, $timestamp, $nonce);
-
- if ($this->query_affected_rows() == 0)
- {
- throw new OAuthException('Duplicate timestamp/nonce combination, possible replay attack. Request rejected.');
- }
-
- // Clean up all timestamps older than the one we just received
- $this->query('
- DELETE FROM oauth_server_nonce
- WHERE osn_consumer_key = \'%s\'
- AND osn_token = \'%s\'
- AND osn_timestamp < %d - %d
- ', $consumer_key, $token, $timestamp, $this->max_timestamp_skew);
- }
-
-
- /**
- * Add an entry to the log table
- *
- * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token)
- * @param string received
- * @param string sent
- * @param string base_string
- * @param string notes
- * @param int (optional) user_id
- */
- public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null )
- {
- $args = array();
- $ps = array();
- foreach ($keys as $key => $value)
- {
- $args[] = $value;
- $ps[] = "olg_$key = '%s'";
- }
-
- if (!empty($_SERVER['REMOTE_ADDR']))
- {
- $remote_ip = $_SERVER['REMOTE_ADDR'];
- }
- else if (!empty($_SERVER['REMOTE_IP']))
- {
- $remote_ip = $_SERVER['REMOTE_IP'];
- }
- else
- {
- $remote_ip = '0.0.0.0';
- }
-
- // Build the SQL
- $ps[] = "olg_received = '%s'"; $args[] = $this->makeUTF8($received);
- $ps[] = "olg_sent = '%s'"; $args[] = $this->makeUTF8($sent);
- $ps[] = "olg_base_string= '%s'"; $args[] = $base_string;
- $ps[] = "olg_notes = '%s'"; $args[] = $this->makeUTF8($notes);
- $ps[] = "olg_usa_id_ref = NULLIF(%d,0)"; $args[] = $user_id;
- $ps[] = "olg_remote_ip = IFNULL(INET_ATON('%s'),0)"; $args[] = $remote_ip;
-
- $this->query('INSERT INTO oauth_log SET '.implode(',', $ps), $args);
- }
-
-
- /**
- * Get a page of entries from the log. Returns the last 100 records
- * matching the options given.
- *
- * @param array options
- * @param int user_id current user
- * @return array log records
- */
- public function listLog ( $options, $user_id )
- {
- $where = array();
- $args = array();
- if (empty($options))
- {
- $where[] = 'olg_usa_id_ref = %d';
- $args[] = $user_id;
- }
- else
- {
- foreach ($options as $option => $value)
- {
- if (strlen($value) > 0)
- {
- switch ($option)
- {
- case 'osr_consumer_key':
- case 'ocr_consumer_key':
- case 'ost_token':
- case 'oct_token':
- $where[] = 'olg_'.$option.' = \'%s\'';
- $args[] = $value;
- break;
- }
- }
- }
-
- $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)';
- $args[] = $user_id;
- }
-
- $rs = $this->query_all_assoc('
- SELECT olg_id,
- olg_osr_consumer_key AS osr_consumer_key,
- olg_ost_token AS ost_token,
- olg_ocr_consumer_key AS ocr_consumer_key,
- olg_oct_token AS oct_token,
- olg_usa_id_ref AS user_id,
- olg_received AS received,
- olg_sent AS sent,
- olg_base_string AS base_string,
- olg_notes AS notes,
- olg_timestamp AS timestamp,
- INET_NTOA(olg_remote_ip) AS remote_ip
- FROM oauth_log
- WHERE '.implode(' AND ', $where).'
- ORDER BY olg_id DESC
- LIMIT 0,100', $args);
-
- return $rs;
- }
-
-
-
- /**
- * Initialise the database
- */
- public function install ()
- {
- require_once dirname(__FILE__) . '/mysql/install.php';
- }
-
-
- /* ** Some simple helper functions for querying the mysql db ** */
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- */
- protected function query ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- if (is_resource($res))
- {
- mysql_free_result($res);
- }
- }
-
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_all_assoc ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- $rs = array();
- while ($row = mysql_fetch_assoc($res))
- {
- $rs[] = $row;
- }
- mysql_free_result($res);
- return $rs;
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row_assoc ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- if ($row = mysql_fetch_assoc($res))
- {
- $rs = $row;
- }
- else
- {
- $rs = false;
- }
- mysql_free_result($res);
- return $rs;
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- if ($row = mysql_fetch_array($res))
- {
- $rs = $row;
- }
- else
- {
- $rs = false;
- }
- mysql_free_result($res);
- return $rs;
- }
-
-
- /**
- * Perform a query, return the first column of the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return mixed
- */
- protected function query_one ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- $val = @mysql_result($res, 0, 0);
- mysql_free_result($res);
- return $val;
- }
-
-
- /**
- * Return the number of rows affected in the last query
- */
- protected function query_affected_rows ()
- {
- return mysql_affected_rows($this->conn);
- }
-
-
- /**
- * Return the id of the last inserted row
- *
- * @return int
- */
- protected function query_insert_id ()
- {
- return mysql_insert_id($this->conn);
- }
-
-
- protected function sql_printf ( $args )
- {
- $sql = array_shift($args);
- if (count($args) == 1 && is_array($args[0]))
- {
- $args = $args[0];
- }
- $args = array_map(array($this, 'sql_escape_string'), $args);
- return vsprintf($sql, $args);
- }
-
-
- protected function sql_escape_string ( $s )
- {
- if (is_string($s))
- {
- return mysql_real_escape_string($s, $this->conn);
- }
- else if (is_null($s))
- {
- return NULL;
- }
- else if (is_bool($s))
- {
- return intval($s);
- }
- else if (is_int($s) || is_float($s))
- {
- return $s;
- }
- else
- {
- return mysql_real_escape_string(strval($s), $this->conn);
- }
- }
-
-
- protected function sql_errcheck ( $sql )
- {
- if (mysql_errno($this->conn))
- {
- $msg = "SQL Error in OAuthStoreMySQL: ".mysql_error($this->conn)."\n\n" . $sql;
- throw new OAuthException($msg);
- }
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/mysql/install.php b/mod/oauth_api/vendors/oauth/library/store/mysql/install.php
deleted file mode 100644
index 0015da5e3..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/mysql/install.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Installs all tables in the mysql.sql file, using the default mysql connection
- */
-
-/* Change and uncomment this when you need to: */
-
-/*
-mysql_connect('localhost', 'root');
-if (mysql_errno())
-{
- die(' Error '.mysql_errno().': '.mysql_error());
-}
-mysql_select_db('test');
-*/
-
-$sql = file_get_contents(dirname(__FILE__) . '/mysql.sql');
-$ps = explode('#--SPLIT--', $sql);
-
-foreach ($ps as $p)
-{
- $p = preg_replace('/^\s*#.*$/m', '', $p);
-
- mysql_query($p);
- if (mysql_errno())
- {
- die(' Error '.mysql_errno().': '.mysql_error());
- }
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql b/mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql
deleted file mode 100644
index d652a1c99..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql
+++ /dev/null
@@ -1,219 +0,0 @@
-# Datamodel for OAuthStoreMySQL
-#
-# You need to add the foreign key constraints for the user ids your are using.
-# I have commented the constraints out, just look for 'usa_id_ref' to enable them.
-#
-# The --SPLIT-- markers are used by the install.php script
-#
-# @version $Id: mysql.sql 51 2008-10-15 15:15:47Z marcw@pobox.com $
-# @author Marc Worrell
-#
-
-# Changes:
-#
-# 2008-10-15 (on r48) Added ttl to consumer and server tokens, added named server tokens
-#
-# ALTER TABLE oauth_server_token
-# ADD ost_token_ttl datetime not null default '9999-12-31',
-# ADD KEY (ost_token_ttl);
-#
-# ALTER TABLE oauth_consumer_token
-# ADD oct_name varchar(64) binary not null default '',
-# ADD oct_token_ttl datetime not null default '9999-12-31',
-# DROP KEY oct_usa_id_ref,
-# ADD UNIQUE KEY (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name),
-# ADD KEY (oct_token_ttl);
-#
-# 2008-09-09 (on r5) Added referrer host to server access token
-#
-# ALTER TABLE oauth_server_token ADD ost_referrer_host VARCHAR(128) NOT NULL;
-#
-
-
-#
-# Log table to hold all OAuth request when you enabled logging
-#
-
-CREATE TABLE IF NOT EXISTS oauth_log (
- olg_id int(11) not null auto_increment,
- olg_osr_consumer_key varchar(64) binary,
- olg_ost_token varchar(64) binary,
- olg_ocr_consumer_key varchar(64) binary,
- olg_oct_token varchar(64) binary,
- olg_usa_id_ref int(11),
- olg_received text not null,
- olg_sent text not null,
- olg_base_string text not null,
- olg_notes text not null,
- olg_timestamp timestamp not null default current_timestamp,
- olg_remote_ip bigint not null,
-
- primary key (olg_id),
- key (olg_osr_consumer_key, olg_id),
- key (olg_ost_token, olg_id),
- key (olg_ocr_consumer_key, olg_id),
- key (olg_oct_token, olg_id),
- key (olg_usa_id_ref, olg_id)
-
-# , foreign key (olg_usa_id_ref) references any_user_auth (usa_id_ref)
-# on update cascade
-# on delete cascade
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-#
-# /////////////////// CONSUMER SIDE ///////////////////
-#
-
-# This is a registry of all consumer codes we got from other servers
-# The consumer_key/secret is obtained from the server
-# We also register the server uri, so that we can find the consumer key and secret
-# for a certain server. From that server we can check if we have a token for a
-# particular user.
-
-CREATE TABLE IF NOT EXISTS oauth_consumer_registry (
- ocr_id int(11) not null auto_increment,
- ocr_usa_id_ref int(11),
- ocr_consumer_key varchar(64) binary not null,
- ocr_consumer_secret varchar(64) binary not null,
- ocr_signature_methods varchar(255) not null default 'HMAC-SHA1,PLAINTEXT',
- ocr_server_uri varchar(255) not null,
- ocr_server_uri_host varchar(128) not null,
- ocr_server_uri_path varchar(128) binary not null,
-
- ocr_request_token_uri varchar(255) not null,
- ocr_authorize_uri varchar(255) not null,
- ocr_access_token_uri varchar(255) not null,
- ocr_timestamp timestamp not null default current_timestamp,
-
- primary key (ocr_id),
- unique key (ocr_consumer_key, ocr_usa_id_ref),
- key (ocr_server_uri),
- key (ocr_server_uri_host, ocr_server_uri_path),
- key (ocr_usa_id_ref)
-
-# , foreign key (ocr_usa_id_ref) references any_user_auth(usa_id_ref)
-# on update cascade
-# on delete set null
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-# Table used to sign requests for sending to a server by the consumer
-# The key is defined for a particular user. Only one single named
-# key is allowed per user/server combination
-
-CREATE TABLE IF NOT EXISTS oauth_consumer_token (
- oct_id int(11) not null auto_increment,
- oct_ocr_id_ref int(11) not null,
- oct_usa_id_ref int(11) not null,
- oct_name varchar(64) binary not null default '',
- oct_token varchar(64) binary not null,
- oct_token_secret varchar(64) binary not null,
- oct_token_type enum('request','authorized','access'),
- oct_token_ttl datetime not null default '9999-12-31',
- oct_timestamp timestamp not null default current_timestamp,
-
- primary key (oct_id),
- unique key (oct_ocr_id_ref, oct_token),
- unique key (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name),
- key (oct_token_ttl),
-
- foreign key (oct_ocr_id_ref) references oauth_consumer_registry (ocr_id)
- on update cascade
- on delete cascade
-
-# , foreign key (oct_usa_id_ref) references any_user_auth (usa_id_ref)
-# on update cascade
-# on delete cascade
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-
-#
-# ////////////////// SERVER SIDE /////////////////
-#
-
-# Table holding consumer key/secret combos an user issued to consumers.
-# Used for verification of incoming requests.
-
-CREATE TABLE IF NOT EXISTS oauth_server_registry (
- osr_id int(11) not null auto_increment,
- osr_usa_id_ref int(11),
- osr_consumer_key varchar(64) binary not null,
- osr_consumer_secret varchar(64) binary not null,
- osr_enabled tinyint(1) not null default '1',
- osr_status varchar(16) not null,
- osr_requester_name varchar(64) not null,
- osr_requester_email varchar(64) not null,
- osr_callback_uri varchar(255) not null,
- osr_application_uri varchar(255) not null,
- osr_application_title varchar(80) not null,
- osr_application_descr text not null,
- osr_application_notes text not null,
- osr_application_type varchar(20) not null,
- osr_application_commercial tinyint(1) not null default '0',
- osr_issue_date datetime not null,
- osr_timestamp timestamp not null default current_timestamp,
-
- primary key (osr_id),
- unique key (osr_consumer_key),
- key (osr_usa_id_ref)
-
-# , foreign key (osr_usa_id_ref) references any_user_auth(usa_id_ref)
-# on update cascade
-# on delete set null
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-# Nonce used by a certain consumer, every used nonce should be unique, this prevents
-# replaying attacks. We need to store all timestamp/nonce combinations for the
-# maximum timestamp received.
-
-CREATE TABLE IF NOT EXISTS oauth_server_nonce (
- osn_id int(11) not null auto_increment,
- osn_consumer_key varchar(64) binary not null,
- osn_token varchar(64) binary not null,
- osn_timestamp bigint not null,
- osn_nonce varchar(80) binary not null,
-
- primary key (osn_id),
- unique key (osn_consumer_key, osn_token, osn_timestamp, osn_nonce)
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-# Table used to verify signed requests sent to a server by the consumer
-# When the verification is succesful then the associated user id is returned.
-
-CREATE TABLE IF NOT EXISTS oauth_server_token (
- ost_id int(11) not null auto_increment,
- ost_osr_id_ref int(11) not null,
- ost_usa_id_ref int(11) not null,
- ost_token varchar(64) binary not null,
- ost_token_secret varchar(64) binary not null,
- ost_token_type enum('request','access'),
- ost_authorized tinyint(1) not null default '0',
- ost_referrer_host varchar(128) not null,
- ost_token_ttl datetime not null default '9999-12-31',
- ost_timestamp timestamp not null default current_timestamp,
-
- primary key (ost_id),
- unique key (ost_token),
- key (ost_osr_id_ref),
- key (ost_token_ttl),
-
- foreign key (ost_osr_id_ref) references oauth_server_registry (osr_id)
- on update cascade
- on delete cascade
-
-# , foreign key (ost_usa_id_ref) references any_user_auth (usa_id_ref)
-# on update cascade
-# on delete cascade
-) engine=InnoDB default charset=utf8;
-
-
-
diff --git a/mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds b/mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds
deleted file mode 100644
index 0f5eba222..000000000
--- a/mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds">
-
- <!-- FireEagle User-Centric OAuth Configuration -->
- <XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0">
-
- <Type>xri://$xrds*simple</Type>
- <Expires>2008-04-15T00:25:30-07:00</Expires>
-
- <!-- Request Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>https://fireeagle.yahooapis.com/oauth/request_token</URI>
- </Service>
-
- <!-- User Authorization -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
-
- <URI>https://fireeagle.yahooapis.com/oauth/access_token</URI>
- </Service>
-
- <!-- Access Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>http://fireeagle.yahoo.net/oauth/authorize</URI>
- </Service>
-
- <!-- Protected Resources -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/resource</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
- </Service>
-
- <!-- Consumer Identity -->
-
- <!-- Manual Consumer Identity Allocation -->
- <Service>
- <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type>
- <URI>https://fireeagle.yahoo.net/developer/create</URI>
- </Service>
- </XRD>
-
- <!-- Global Resource Definition -->
-
- <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
-
- <!-- OAuth Endpoints Definition -->
- <Service>
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#oauth</URI>
- </Service>
- </XRD>
-
-</XRDS> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds b/mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds
deleted file mode 100644
index ab94b5bea..000000000
--- a/mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds">
-
- <XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
- <Expires>2008-04-30T23:59:59Z</Expires>
-
- <!-- Request Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
-
- <URI>http://getsatisfaction.com/api/request_token</URI>
- </Service>
-
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
-
- <URI>http://getsatisfaction.com/api/authorize</URI>
- </Service>
-
- <!-- Access Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
-
- <URI>http://getsatisfaction.com/api/access_token</URI>
- </Service>
-
- <!-- Protected Resources -->
- <!--
-
- To test successful access token grant, make a request against
-
- http://api.getsatisfaction.com/me
-
- The API should respond with hCard of the user who authorized the token
- -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/resource</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
-
- </Service>
-
- <!-- Consumer Identity -->
-
- <Service>
- <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type>
- <URI>http://getsatisfaction.com/me/extensions/new</URI>
- </Service>
- </XRD>
-
- <!-- Global Resource Definition -->
-
- <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
-
- <!-- OAuth Endpoints Definition -->
- <Service priority="10">
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#oauth</URI>
- </Service>
- </XRD>
-
-</XRDS> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds b/mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds
deleted file mode 100644
index 361b5c9a1..000000000
--- a/mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds">
-
- <!-- Ma.gnolia OAuth Configuration -->
- <XRD xml:id="oauth" xmlns="xri://$XRD*($v*2.0)" version="2.0">
-
- <Type>xri://$xrds*simple</Type>
- <Expires>2008-04-13T07:34:58Z</Expires>
-
- <!-- Request Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>https://ma.gnolia.com/oauth/get_request_token</URI>
- </Service>
-
- <!-- User Authorization (HTTPS Prefered) -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
-
- <URI priority="10">https://ma.gnolia.com/oauth/authorize</URI>
- <URI priority="20">http://ma.gnolia.com/oauth/authorize</URI>
- </Service>
-
- <!-- Access Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>https://ma.gnolia.com/oauth/get_access_token</URI>
- </Service>
-
- <!-- Protected Resources -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/resource</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type>
- </Service>
-
- <!-- Consumer Identity -->
-
- <!-- Manual Consumer Identity Allocation -->
- <Service>
- <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type>
- <URI>http://ma.gnolia.com/applications/new</URI>
- </Service>
- </XRD>
-
- <!-- Global Resource Definition -->
-
- <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
-
- <!-- OAuth Endpoints Definition -->
- <Service priority="10">
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#oauth</URI>
- </Service>
- </XRD>
-
-</XRDS> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/test/oauth_test.php b/mod/oauth_api/vendors/oauth/test/oauth_test.php
deleted file mode 100644
index 0c0504c70..000000000
--- a/mod/oauth_api/vendors/oauth/test/oauth_test.php
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-
-/**
- * Tests of OAuth implementation.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 29, 2007 3:46:56 PM
- * @see http://wiki.oauth.net/TestCases
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * 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.
- */
-
-require_once dirname(__FILE__) . '/../library/OAuthRequest.php';
-require_once dirname(__FILE__) . '/../library/OAuthRequester.php';
-require_once dirname(__FILE__) . '/../library/OAuthRequestSigner.php';
-require_once dirname(__FILE__) . '/../library/OAuthRequestVerifier.php';
-
-if (!function_exists('getallheaders'))
-{
- function getallheaders()
- {
- return array();
- }
-}
-
-
-oauth_test();
-
-function oauth_test ()
-{
- error_reporting(E_ALL);
-
- header('Content-Type: text/plain; charset=utf-8');
-
- echo "Performing OAuth module tests.\n\n";
- echo "See also: http://wiki.oauth.net/TestCases\n\n";
-
- assert_options(ASSERT_CALLBACK, 'oauth_assert_handler');
- assert_options(ASSERT_WARNING, 0);
-
- $req = new OAuthRequest('http://www.example.com', 'GET');
-
- echo "***** Parameter Encoding *****\n\n";
-
- assert('$req->urlencode(\'abcABC123\') == \'abcABC123\'');
- assert('$req->urlencode(\'-._~\') == \'-._~\'');
- assert('$req->urlencode(\'%\') == \'%25\'');
- assert('$req->urlencode(\'&=*\') == \'%26%3D%2A\'');
- assert('$req->urlencode(\'&=*\') == \'%26%3D%2A\'');
- assert('$req->urlencode("\n") == \'%0A\'');
- assert('$req->urlencode(" ") == \'%20\'');
- assert('$req->urlencode("\x7f") == \'%7F\'');
-
-
- echo "***** Normalize Request Parameters *****\n\n";
-
- $req = new OAuthRequest('http://example.com/?name', 'GET');
- assert('$req->getNormalizedParams() == \'name=\'');
-
- $req = new OAuthRequest('http://example.com/?a=b', 'GET');
- assert('$req->getNormalizedParams() == \'a=b\'');
-
- $req = new OAuthRequest('http://example.com/?a=b&c=d', 'GET');
- assert('$req->getNormalizedParams() == \'a=b&c=d\'');
-
- // At this moment we don't support two parameters with the same name
- // so I changed this test case to "a=" and "b=" and not "a=" and "a="
- $req = new OAuthRequest('http://example.com/?b=x!y&a=x+y', 'GET');
- assert('$req->getNormalizedParams() == \'a=x%20y&b=x%21y\'');
-
- $req = new OAuthRequest('http://example.com/?x!y=a&x=a', 'GET');
- assert('$req->getNormalizedParams() == \'x=a&x%21y=a\'');
-
-
- echo "***** Base String *****\n\n";
-
- $req = new OAuthRequest('http://example.com/?n=v', 'GET');
- assert('$req->signatureBaseString() == \'GET&http%3A%2F%2Fexample.com%2F&n%3Dv\'');
-
- $req = new OAuthRequest(
- 'https://photos.example.net/request_token',
- 'POST',
- 'oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_timestamp=1191242090&oauth_nonce=hsu94j3884jdopsl&oauth_signature_method=PLAINTEXT&oauth_signature=ignored',
- array('X-OAuth-Test' => true));
- assert('$req->signatureBaseString() == \'POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884jdopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestamp%3D1191242090%26oauth_version%3D1.0\'');
-
- $req = new OAuthRequest(
- 'http://photos.example.net/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1',
- 'GET');
- assert('$req->signatureBaseString() == \'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal\'');
-
-
- echo "***** HMAC-SHA1 *****\nRequest signing\n";
-
- OAuthStore::instance('MySQL', array('conn'=>false));
- $req = new OAuthRequestSigner('http://photos.example.net/photos?file=vacation.jpg&size=original', 'GET');
-
- assert('$req->urldecode($req->calculateDataSignature(\'bs\', \'cs\', \'\', \'HMAC-SHA1\')) == \'egQqG5AJep5sJ7anhXju1unge2I=\'');
- assert('$req->urldecode($req->calculateDataSignature(\'bs\', \'cs\', \'ts\', \'HMAC-SHA1\')) == \'VZVjXceV7JgPq/dOTnNmEfO0Fv8=\'');
-
- $secrets = array(
- 'consumer_key' => 'dpf43f3p2l4k3l03',
- 'consumer_secret' => 'kd94hf93k423kf44',
- 'token' => 'nnch734d00sl2jdk',
- 'token_secret' => 'pfkkdhi9sl3r4s00',
- 'signature_methods' => array('HMAC-SHA1'),
- 'nonce' => 'kllo9940pd9333jh',
- 'timestamp' => '1191242096'
- );
- $req->sign(0, $secrets);
- assert('$req->getParam(\'oauth_signature\', true) == \'tR3+Ty81lMeYAr/Fid0kMTYa/WM=\'');
-
- echo "***** HMAC-SHA1 *****\nRequest verification\n";
-
- $req = new OAuthRequestVerifier(
- 'http://photos.example.net/photos?file=vacation.jpg&size=original'
- .'&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk'
- .'&oauth_signature_method=HMAC-SHA1&oauth_nonce=kllo9940pd9333jh'
- .'&oauth_timestamp=1191242096&oauth_version=1.0'
- .'&oauth_signature='.rawurlencode('tR3+Ty81lMeYAr/Fid0kMTYa/WM=')
- , 'GET');
-
- $req->verifySignature('kd94hf93k423kf44', 'pfkkdhi9sl3r4s00');
-
- echo "\n";
- echo "***** Yahoo! test case ******\n\n";
-
- OAuthStore::instance('MySQL', array('conn'=>false));
- $req = new OAuthRequestSigner('http://example.com:80/photo', 'GET');
-
- $req->setParam('title', 'taken with a 30% orange filter');
- $req->setParam('file', 'mountain & water view');
- $req->setParam('format', 'jpeg');
- $req->setParam('include', array('date','aperture'));
-
- $secrets = array(
- 'consumer_key' => '1234=asdf=4567',
- 'consumer_secret' => 'erks823*43=asd&123ls%23',
- 'token' => 'asdf-4354=asew-5698',
- 'token_secret' => 'dis9$#$Js009%==',
- 'signature_methods' => array('HMAC-SHA1'),
- 'nonce' => '3jd834jd9',
- 'timestamp' => '12303202302'
- );
- $req->sign(0, $secrets);
-
- // echo "Basestring:\n",$req->signatureBaseString(), "\n\n";
-
- //echo "queryString:\n",$req->getQueryString(), "\n\n";
- assert('$req->getQueryString() == \'title=taken%20with%20a%2030%25%20orange%20filter&file=mountain%20%26%20water%20view&format=jpeg&include=date&include=aperture\'');
-
- //echo "oauth_signature:\n",$req->getParam('oauth_signature', true),"\n\n";
- assert('$req->getParam(\'oauth_signature\', true) == \'jMdUSR1vOr3SzNv3gZ5DDDuGirA=\'');
-
- echo "\n\nFinished.\n";
-}
-
-
-function oauth_assert_handler ( $file, $line, $code )
-{
- echo "\nAssertion failed in $file:$line
- $code\n\n";
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/openid_client/start.php b/mod/openid_client/start.php
index 9da701b6b..a21832230 100644
--- a/mod/openid_client/start.php
+++ b/mod/openid_client/start.php
@@ -30,10 +30,6 @@ function openid_client_init() {
'Anillosur' => 'https://anillosur.net/mod/openid_server/server.php',
'Saravea' => 'https://saravea.net/mod/openid_server/server.php',
'Enekenbat' => 'https://enekenbat.cc/mod/openid_server/server.php',
-<<<<<<< HEAD
-=======
-
->>>>>>> 84be7867eac3e0b69cca4d84c184f6e8e4d2e43b
// ...
));
diff --git a/mod/pages/actions/annotations/page/delete.php b/mod/pages/actions/annotations/page/delete.php
new file mode 100644
index 000000000..156b516d2
--- /dev/null
+++ b/mod/pages/actions/annotations/page/delete.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Remove a page (revision) annotation
+ *
+ * @package ElggPages
+ */
+
+// Make sure we can get the annotations and entity in question
+$annotation_id = (int) get_input('annotation_id');
+$annotation = elgg_get_annotation_from_id($annotation_id);
+$entity = get_entity($annotation->entity_guid);
+
+if ($annotation && $entity->canEdit() && $annotation->canEdit()) {
+ $annotation->delete();
+ system_message(elgg_echo("pages:revision:delete:success"));
+} else {
+ register_error(elgg_echo("pages:revision:delete:failure"));
+}
+
+forward("pages/history/{$annotation->entity_guid}"); \ No newline at end of file
diff --git a/mod/pages/actions/pages/delete.php b/mod/pages/actions/pages/delete.php
index 7a314a280..fd5791e4d 100644
--- a/mod/pages/actions/pages/delete.php
+++ b/mod/pages/actions/pages/delete.php
@@ -21,11 +21,33 @@ if (elgg_instanceof($page, 'object', 'page') || elgg_instanceof($page, 'object',
'metadata_value' => $page->getGUID()
));
if ($children) {
+ $db_prefix = elgg_get_config('dbprefix');
+ $subtype_id = (int)get_subtype_id('object', 'page_top');
+ $newentity_cache = is_memcache_available() ? new ElggMemcache('new_entity_cache') : null;
+
foreach ($children as $child) {
- $child->parent_guid = $parent;
+ if ($parent) {
+ $child->parent_guid = $parent;
+ } else {
+ // If no parent, we need to transform $child to a page_top
+ $child_guid = (int)$child->guid;
+
+ update_data("UPDATE {$db_prefix}entities
+ SET subtype = $subtype_id WHERE guid = $child_guid");
+
+ elgg_delete_metadata(array(
+ 'guid' => $child_guid,
+ 'metadata_name' => 'parent_guid',
+ ));
+
+ _elgg_invalidate_cache_for_entity($child_guid);
+ if ($newentity_cache) {
+ $newentity_cache->delete($child_guid);
+ }
+ }
}
}
-
+
if ($page->delete()) {
system_message(elgg_echo('pages:delete:success'));
if ($parent) {
diff --git a/mod/pages/languages/en.php b/mod/pages/languages/en.php
index 930676b3e..c204c1901 100644
--- a/mod/pages/languages/en.php
+++ b/mod/pages/languages/en.php
@@ -15,7 +15,7 @@ $english = array(
'pages:owner' => "%s's pages",
'pages:friends' => "Friends' pages",
'pages:all' => "All site pages",
- 'pages:add' => "Add page",
+ 'pages:add' => "Add a page",
'pages:group' => "Group pages",
'groups:enablepages' => 'Enable group pages',
@@ -25,6 +25,8 @@ $english = array(
'pages:history' => "History",
'pages:view' => "View page",
'pages:revision' => "Revision",
+ 'pages:current_revision' => "Current Revision",
+ 'pages:revert' => "Revert",
'pages:navigation' => "Navigation",
'pages:new' => "A new page",
@@ -75,6 +77,9 @@ View and comment on the new page:
'pages:error:no_title' => 'You must specify a title for this page.',
'pages:delete:success' => 'The page was successfully deleted.',
'pages:delete:failure' => 'The page could not be deleted.',
+ 'pages:revision:delete:success' => 'The page revision was successfully deleted.',
+ 'pages:revision:delete:failure' => 'The page revision could not be deleted.',
+ 'pages:revision:not_found' => 'Cannot find this revision.',
/**
* Page
diff --git a/mod/pages/lib/pages.php b/mod/pages/lib/pages.php
index afe42b68f..7f90d53d8 100644
--- a/mod/pages/lib/pages.php
+++ b/mod/pages/lib/pages.php
@@ -9,7 +9,7 @@
* @param ElggObject $page
* @return array
*/
-function pages_prepare_form_vars($page = null, $parent_guid = 0) {
+function pages_prepare_form_vars($page = null, $parent_guid = 0, $revision = null) {
// input names => defaults
$values = array(
@@ -41,6 +41,11 @@ function pages_prepare_form_vars($page = null, $parent_guid = 0) {
elgg_clear_sticky_form('page');
+ // load the revision annotation if requested
+ if ($revision instanceof ElggAnnotation && $revision->entity_guid == $page->getGUID()) {
+ $values['description'] = $revision->value;
+ }
+
return $values;
}
diff --git a/mod/pages/pages/pages/edit.php b/mod/pages/pages/pages/edit.php
index 1f411b94d..a925cdc55 100644
--- a/mod/pages/pages/pages/edit.php
+++ b/mod/pages/pages/pages/edit.php
@@ -8,6 +8,7 @@
gatekeeper();
$page_guid = (int)get_input('guid');
+$revision = (int)get_input('annotation_id');
$page = get_entity($page_guid);
if (!$page) {
register_error(elgg_echo('noaccess'));
@@ -28,7 +29,17 @@ elgg_push_breadcrumb(elgg_echo('edit'));
$title = elgg_echo("pages:edit");
if ($page->canEdit()) {
- $vars = pages_prepare_form_vars($page);
+
+ if ($revision) {
+ $revision = elgg_get_annotation_from_id($revision);
+ if (!$revision || !($revision->entity_guid == $page_guid)) {
+ register_error(elgg_echo('pages:revision:not_found'));
+ forward(REFERER);
+ }
+ }
+
+ $vars = pages_prepare_form_vars($page, $page->parent_guid, $revision);
+
$content = elgg_view_form('pages/edit', array(), $vars);
} else {
$content = elgg_echo("pages:noaccess");
diff --git a/mod/pages/pages/pages/friends.php b/mod/pages/pages/pages/friends.php
index 87ac631c2..cecc4053b 100644
--- a/mod/pages/pages/pages/friends.php
+++ b/mod/pages/pages/pages/friends.php
@@ -7,7 +7,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('pages/all');
+ forward('', '404');
}
elgg_push_breadcrumb($owner->name, "pages/owner/$owner->username");
diff --git a/mod/pages/pages/pages/history.php b/mod/pages/pages/pages/history.php
index 872596179..7f5fa4f4f 100644
--- a/mod/pages/pages/pages/history.php
+++ b/mod/pages/pages/pages/history.php
@@ -9,12 +9,12 @@ $page_guid = get_input('guid');
$page = get_entity($page_guid);
if (!$page) {
-
+ forward('', '404');
}
$container = $page->getContainerEntity();
if (!$container) {
-
+ forward('', '404');
}
elgg_set_page_owner_guid($container->getGUID());
diff --git a/mod/pages/pages/pages/owner.php b/mod/pages/pages/pages/owner.php
index 48199368c..7de74a3b4 100644
--- a/mod/pages/pages/pages/owner.php
+++ b/mod/pages/pages/pages/owner.php
@@ -7,7 +7,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('pages/all');
+ forward('', '404');
}
// access check for closed groups
diff --git a/mod/pages/start.php b/mod/pages/start.php
index 6d974f122..f9c34cd85 100644
--- a/mod/pages/start.php
+++ b/mod/pages/start.php
@@ -28,9 +28,10 @@ function pages_init() {
elgg_register_annotation_url_handler('page', 'pages_revision_url');
// Register some actions
- $action_base = elgg_get_plugins_path() . 'pages/actions/pages';
- elgg_register_action("pages/edit", "$action_base/edit.php");
- elgg_register_action("pages/delete", "$action_base/delete.php");
+ $action_base = elgg_get_plugins_path() . 'pages/actions';
+ elgg_register_action("pages/edit", "$action_base/pages/edit.php");
+ elgg_register_action("pages/delete", "$action_base/pages/delete.php");
+ elgg_register_action("annotations/page/delete", "$action_base/annotations/page/delete.php");
// Extend the main css view
elgg_extend_view('css/elgg', 'pages/css');
@@ -80,8 +81,13 @@ function pages_init() {
// entity menu
elgg_register_plugin_hook_handler('register', 'menu:entity', 'pages_entity_menu_setup');
+ // hook into annotation menu
+ elgg_register_plugin_hook_handler('register', 'menu:annotation', 'pages_annotation_menu_setup');
+
// register ecml views to parse
elgg_register_plugin_hook_handler('get_views', 'ecml', 'pages_ecml_views_hook');
+
+ elgg_register_event_handler('upgrade', 'system', 'pages_run_upgrades');
}
/**
@@ -281,25 +287,37 @@ function page_notify_message($hook, $entity_type, $returnvalue, $params) {
/**
* Extend permissions checking to extend can-edit for write users.
*
- * @param unknown_type $hook
- * @param unknown_type $entity_type
- * @param unknown_type $returnvalue
- * @param unknown_type $params
+ * @param string $hook
+ * @param string $entity_type
+ * @param bool $returnvalue
+ * @param array $params
*/
-function pages_write_permission_check($hook, $entity_type, $returnvalue, $params)
-{
+function pages_write_permission_check($hook, $entity_type, $returnvalue, $params) {
if ($params['entity']->getSubtype() == 'page'
|| $params['entity']->getSubtype() == 'page_top') {
$write_permission = $params['entity']->write_access_id;
$user = $params['user'];
- if (($write_permission) && ($user)) {
- // $list = get_write_access_array($user->guid);
- $list = get_access_array($user->guid); // get_access_list($user->guid);
-
- if (($write_permission!=0) && (in_array($write_permission,$list))) {
- return true;
+ if ($write_permission && $user) {
+ switch ($write_permission) {
+ case ACCESS_PRIVATE:
+ // Elgg's default decision is what we want
+ return;
+ break;
+ case ACCESS_FRIENDS:
+ $owner = $params['entity']->getOwnerEntity();
+ if ($owner && $owner->isFriendsWith($user->guid)) {
+ return true;
+ }
+ break;
+ default:
+ $list = get_access_array($user->guid);
+ if (in_array($write_permission, $list)) {
+ // user in the access collection
+ return true;
+ }
+ break;
}
}
}
diff --git a/mod/pages/upgrades/2012061800.php b/mod/pages/upgrades/2012061800.php
new file mode 100644
index 000000000..c21ccae3b
--- /dev/null
+++ b/mod/pages/upgrades/2012061800.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Restore disappeared subpages. This is caused by its parent page being deleted
+ * when the parent page is a top level page. We take advantage of the fact that
+ * the parent_guid was deleted for the subpages.
+ *
+ * This upgrade script will no longer work once we have converted all pages to
+ * have the same entity subtype.
+ */
+
+
+/**
+ * Update subtype
+ *
+ * @param ElggObject $page
+ */
+function pages_2012061800($page) {
+ $dbprefix = elgg_get_config('dbprefix');
+ $subtype_id = (int)get_subtype_id('object', 'page_top');
+ $page_guid = (int)$page->guid;
+ update_data("UPDATE {$dbprefix}entities
+ SET subtype = $subtype_id WHERE guid = $page_guid");
+ error_log("called");
+ return true;
+}
+
+$previous_access = elgg_set_ignore_access(true);
+
+$dbprefix = elgg_get_config('dbprefix');
+$name_metastring_id = get_metastring_id('parent_guid');
+if (!$name_metastring_id) {
+ return;
+}
+
+// Looking for pages without metadata
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'page',
+ 'wheres' => "NOT EXISTS (
+ SELECT 1 FROM {$dbprefix}metadata md
+ WHERE md.entity_guid = e.guid
+ AND md.name_id = $name_metastring_id)"
+);
+$batch = new ElggBatch('elgg_get_entities_from_metadata', $options, 'pages_2012061800', 50, false);
+elgg_set_ignore_access($previous_access);
+
+if ($batch->callbackResult) {
+ error_log("Elgg Pages upgrade (2012061800) succeeded");
+}
diff --git a/mod/pages/views/default/annotation/page.php b/mod/pages/views/default/annotation/page.php
index a621b9281..ecb289092 100644
--- a/mod/pages/views/default/annotation/page.php
+++ b/mod/pages/views/default/annotation/page.php
@@ -39,4 +39,22 @@ $body = <<< HTML
<p class="elgg-subtext">$subtitle</p>
HTML;
+if (!elgg_in_context('widgets')) {
+ $menu = elgg_view_menu('annotation', array(
+ 'annotation' => $annotation,
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz float-alt',
+ ));
+}
+
+$body = <<<HTML
+<div class="mbn">
+ $menu
+ <h3>$title_link</h3>
+ <span class="elgg-subtext">
+ $subtitle
+ </span>
+</div>
+HTML;
+
echo elgg_view_image_block($icon, $body); \ No newline at end of file
diff --git a/mod/pages/views/default/object/page_top.php b/mod/pages/views/default/object/page_top.php
index 945a22eed..f35202993 100644
--- a/mod/pages/views/default/object/page_top.php
+++ b/mod/pages/views/default/object/page_top.php
@@ -60,18 +60,26 @@ if ($comments_count != 0 && !$revision) {
$comments_link = '';
}
-$metadata = elgg_view_menu('entity', array(
- 'entity' => $vars['entity'],
- 'handler' => 'pages',
- 'sort_by' => 'priority',
- 'class' => 'elgg-menu-hz',
-));
-
$subtitle = "$editor_text $comments_link $categories";
// do not show the metadata and controls in widget view
-if (elgg_in_context('widgets') || $revision) {
- $metadata = '';
+if (!elgg_in_context('widgets')) {
+ // If we're looking at a revision, display annotation menu
+ if ($revision) {
+ $metadata = elgg_view_menu('annotation', array(
+ 'annotation' => $annotation,
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz float-alt',
+ ));
+ } else {
+ // Regular entity menu
+ $metadata = elgg_view_menu('entity', array(
+ 'entity' => $vars['entity'],
+ 'handler' => 'pages',
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz',
+ ));
+ }
}
if ($full) {
diff --git a/mod/pages/views/default/pages/sidebar/history.php b/mod/pages/views/default/pages/sidebar/history.php
index 7077edb9a..e0e8ed11a 100644
--- a/mod/pages/views/default/pages/sidebar/history.php
+++ b/mod/pages/views/default/pages/sidebar/history.php
@@ -14,6 +14,7 @@ if ($vars['page']) {
'limit' => 20,
'reverse_order_by' => true
);
+ elgg_push_context('widgets');
$content = elgg_list_annotations($options);
}
diff --git a/mod/profile/icondirect.php b/mod/profile/icondirect.php
index dbab5d31f..5f1599e0d 100644
--- a/mod/profile/icondirect.php
+++ b/mod/profile/icondirect.php
@@ -55,13 +55,13 @@ if ($mysql_dblink) {
$user_path = date('Y/m/d/', $join_date) . $guid;
$filename = "$data_root$user_path/profile/{$guid}{$size}.jpg";
- $size = @filesize($filename);
- if ($size) {
+ $filesize = @filesize($filename);
+ if ($filesize) {
header("Content-type: image/jpeg");
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true);
header("Pragma: public");
header("Cache-Control: public");
- header("Content-Length: $size");
+ header("Content-Length: $filesize");
header("ETag: \"$etag\"");
readfile($filename);
exit;
diff --git a/mod/profile/views/default/profile/details.php b/mod/profile/views/default/profile/details.php
index 167f995ae..da4e95690 100644
--- a/mod/profile/views/default/profile/details.php
+++ b/mod/profile/views/default/profile/details.php
@@ -22,13 +22,21 @@ if (is_array($profile_fields) && sizeof($profile_fields) > 0) {
}
$value = $user->$shortname;
- // validate urls
- if ($valtype == 'url' && !preg_match('~^https?\://~i', $value)) {
- $value = "http://$value";
- }
-
if (!empty($value)) {
- //This function controls the alternating class
+
+ // fix profile URLs populated by https://github.com/Elgg/Elgg/issues/5232
+ // @todo Replace with upgrade script, only need to alter users with last_update after 1.8.13
+ if ($valtype == 'url' && $value == 'http://') {
+ $user->$shortname = '';
+ continue;
+ }
+
+ // validate urls
+ if ($valtype == 'url' && !preg_match('~^https?\://~i', $value)) {
+ $value = "http://$value";
+ }
+
+ // this controls the alternating class
$even_odd = ( 'odd' != $even_odd ) ? 'odd' : 'even';
?>
<div class="<?php echo $even_odd; ?>">
diff --git a/mod/reportedcontent/views/default/object/reported_content.php b/mod/reportedcontent/views/default/object/reported_content.php
index 0e733e154..cc33f54fb 100644
--- a/mod/reportedcontent/views/default/object/reported_content.php
+++ b/mod/reportedcontent/views/default/object/reported_content.php
@@ -57,16 +57,6 @@ if ($report->state == 'archived') {
<p>
<b><?php echo elgg_echo('reportedcontent:objecttitle'); ?>:</b>
<?php echo $report->title; ?>
- <br />
- <?php echo elgg_view('output/url', array(
- 'href' => "#report-$report->guid",
- 'text' => elgg_echo('reportedcontent:moreinfo'),
- 'rel' => "toggle",
- ));
- ?>
- </p>
- </div>
- <div class="report-details hidden" id="report-<?php echo $report->getGUID();?>">
<p>
<b><?php echo elgg_echo('reportedcontent:objecturl'); ?>:</b>
<?php echo elgg_view('output/url', array(
@@ -77,6 +67,16 @@ if ($report->state == 'archived') {
?>
</p>
<p>
+ <?php echo elgg_view('output/url', array(
+ 'href' => "#report-$report->guid",
+ 'text' => elgg_echo('reportedcontent:moreinfo'),
+ 'rel' => "toggle",
+ ));
+ ?>
+ </p>
+ </div>
+ <div class="report-details hidden" id="report-<?php echo $report->getGUID();?>">
+ <p>
<b><?php echo elgg_echo('reportedcontent:reason'); ?>:</b>
<?php echo $report->description; ?>
</p>
diff --git a/mod/search/README.txt b/mod/search/README.txt
index 98a002dd5..ac5930e5f 100644
--- a/mod/search/README.txt
+++ b/mod/search/README.txt
@@ -273,4 +273,4 @@ MySQL's fulltext engine returns *ZERO* rows if more than 50% of
the rows searched match.
The default search hooks for users and groups ignore subtypes.
-See [trac ticket 1499](http://trac.elgg.org/elgg/ticket/1499)
+See [GitHub issue 1499](https://github.com/elgg/elgg/issues/1499)
diff --git a/mod/search/pages/search/index.php b/mod/search/pages/search/index.php
index ede09329b..9542e0751 100644
--- a/mod/search/pages/search/index.php
+++ b/mod/search/pages/search/index.php
@@ -17,15 +17,7 @@ $search_type = get_input('search_type', 'all');
// XSS protection is more important that searching for HTML.
$query = stripslashes(get_input('q', get_input('tag', '')));
-// @todo - create function for sanitization of strings for display in 1.8
-// encode <,>,&, quotes and characters above 127
-if (function_exists('mb_convert_encoding')) {
- $display_query = mb_convert_encoding($query, 'HTML-ENTITIES', 'UTF-8');
-} else {
- // if no mbstring extension, we just strip characters
- $display_query = preg_replace("/[^\x01-\x7F]/", "", $query);
-}
-$display_query = htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false);
+$display_query = _elgg_get_display_query($query);
// check that we have an actual query
if (!$query) {
diff --git a/mod/search/search_hooks.php b/mod/search/search_hooks.php
index 47351fb8a..923cf0aa8 100644
--- a/mod/search/search_hooks.php
+++ b/mod/search/search_hooks.php
@@ -3,17 +3,17 @@
* Elgg core search.
*
* @package Elgg
- * @subpackage Core
+ * @subpackage Search
*/
/**
- * Return default results for searches on objects.
+ * Get objects that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_objects_hook($hook, $type, $value, $params) {
@@ -23,7 +23,7 @@ function search_objects_hook($hook, $type, $value, $params) {
$params['joins'] = array($join);
$fields = array('title', 'description');
- $where = search_get_where_sql('oe', $fields, $params, FALSE);
+ $where = search_get_where_sql('oe', $fields, $params);
$params['wheres'] = array($where);
$params['count'] = TRUE;
@@ -54,13 +54,13 @@ function search_objects_hook($hook, $type, $value, $params) {
}
/**
- * Return default results for searches on groups.
+ * Get groups that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_groups_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -69,12 +69,9 @@ function search_groups_hook($hook, $type, $value, $params) {
$join = "JOIN {$db_prefix}groups_entity ge ON e.guid = ge.guid";
$params['joins'] = array($join);
-
$fields = array('name', 'description');
- // force into boolean mode because we've having problems with the
- // "if > 50% match 0 sets are returns" problem.
- $where = search_get_where_sql('ge', $fields, $params, FALSE);
+ $where = search_get_where_sql('ge', $fields, $params);
$params['wheres'] = array($where);
@@ -109,15 +106,15 @@ function search_groups_hook($hook, $type, $value, $params) {
}
/**
- * Return default results for searches on users.
- *
- * @todo add profile field MD searching
+ * Get users that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * Searches on username, display name, and profile fields
+ *
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_users_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -178,11 +175,20 @@ function search_users_hook($hook, $type, $value, $params) {
$entity->setVolatileData('search_matched_title', $title);
$matched = '';
- foreach ($profile_fields as $md) {
- $text = $entity->$md;
- if (stristr($text, $query)) {
- $matched .= elgg_echo("profile:{$md}") . ': '
- . search_get_highlighted_relevant_substrings($text, $query);
+ foreach ($profile_fields as $md_name) {
+ $metadata = $entity->$md_name;
+ if (is_array($metadata)) {
+ foreach ($metadata as $text) {
+ if (stristr($text, $query)) {
+ $matched .= elgg_echo("profile:{$md_name}") . ': '
+ . search_get_highlighted_relevant_substrings($text, $query);
+ }
+ }
+ } else {
+ if (stristr($metadata, $query)) {
+ $matched .= elgg_echo("profile:{$md_name}") . ': '
+ . search_get_highlighted_relevant_substrings($metadata, $query);
+ }
}
}
@@ -196,13 +202,13 @@ function search_users_hook($hook, $type, $value, $params) {
}
/**
- * Return default results for searches on tags.
+ * Get entities with tags that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_tags_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -331,11 +337,11 @@ function search_tags_hook($hook, $type, $value, $params) {
/**
* Register tags as a custom search type.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Array of custom search types
+ * @param array $params Search parameters
+ * @return array
*/
function search_custom_types_tags_hook($hook, $type, $value, $params) {
$value[] = 'tags';
@@ -344,13 +350,13 @@ function search_custom_types_tags_hook($hook, $type, $value, $params) {
/**
- * Return default results for searches on comments.
+ * Get comments that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_comments_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -399,14 +405,19 @@ function search_comments_hook($hook, $type, $value, $params) {
// don't continue if nothing there...
if (!$count) {
- return array ('entities' => array(), 'count' => 0);
+ return array('entities' => array(), 'count' => 0);
}
-
- $order_by = search_get_order_by_sql('e', null, $params['sort'], $params['order']);
+
+ // no full text index on metastrings table
+ if ($params['sort'] == 'relevance') {
+ $params['sort'] = 'created';
+ }
+
+ $order_by = search_get_order_by_sql('a', null, $params['sort'], $params['order']);
if ($order_by) {
$order_by = "ORDER BY $order_by";
}
-
+
$q = "SELECT DISTINCT a.*, msv.string as comment FROM {$db_prefix}annotations a
JOIN {$db_prefix}metastrings msn ON a.name_id = msn.id
JOIN {$db_prefix}metastrings msv ON a.value_id = msv.id
@@ -444,10 +455,17 @@ function search_comments_hook($hook, $type, $value, $params) {
}
$comment_str = search_get_highlighted_relevant_substrings($comment->comment, $query);
- $entity->setVolatileData('search_match_annotation_id', $comment->id);
- $entity->setVolatileData('search_matched_comment', $comment_str);
- $entity->setVolatileData('search_matched_comment_owner_guid', $comment->owner_guid);
- $entity->setVolatileData('search_matched_comment_time_created', $comment->time_created);
+ $comments_data = $entity->getVolatileData('search_comments_data');
+ if (!$comments_data) {
+ $comments_data = array();
+ }
+ $comments_data[] = array(
+ 'annotation_id' => $comment->id,
+ 'text' => $comment_str,
+ 'owner_guid' => $comment->owner_guid,
+ 'time_created' => $comment->time_created,
+ );
+ $entity->setVolatileData('search_comments_data', $comments_data);
$entities[] = $entity;
}
@@ -460,11 +478,11 @@ function search_comments_hook($hook, $type, $value, $params) {
/**
* Register comments as a custom search type.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Array of custom search types
+ * @param array $params Search parameters
+ * @return array
*/
function search_custom_types_comments_hook($hook, $type, $value, $params) {
$value[] = 'comments';
diff --git a/mod/search/views/default/search/comments/entity.php b/mod/search/views/default/search/comments/entity.php
index 005bb270c..77e950843 100644
--- a/mod/search/views/default/search/comments/entity.php
+++ b/mod/search/views/default/search/comments/entity.php
@@ -6,8 +6,11 @@
*/
$entity = $vars['entity'];
+$comments_data = $entity->getVolatileData('search_comments_data');
+$comment_data = array_shift($comments_data);
+$entity->setVolatileData('search_comments_data', $comments_data);
-$owner = get_entity($entity->getVolatileData('search_matched_comment_owner_guid'));
+$owner = get_entity($comment_data['owner_guid']);
if ($owner instanceof ElggUser) {
$icon = elgg_view_entity_icon($owner, 'tiny');
@@ -38,12 +41,12 @@ if ($entity->getVolatileData('search_unavailable_entity')) {
$title = elgg_echo('search:comment_on', array($title));
// @todo this should use something like $comment->getURL()
- $url = $entity->getURL() . '#comment_' . $entity->getVolatileData('search_match_annotation_id');
+ $url = $entity->getURL() . '#comment_' . $comment_data['annotation_id'];
$title = "<a href=\"$url\">$title</a>";
}
-$description = $entity->getVolatileData('search_matched_comment');
-$tc = $entity->getVolatileData('search_matched_comment_time_created');;
+$description = $comment_data['text'];
+$tc = $comment_data['time_created'];
$time = elgg_view_friendly_time($tc);
$body = "<p class=\"mbn\">$title</p>$description";
diff --git a/mod/search/views/default/search/list.php b/mod/search/views/default/search/list.php
index 1ed40be1b..90aa28989 100644
--- a/mod/search/views/default/search/list.php
+++ b/mod/search/views/default/search/list.php
@@ -36,16 +36,21 @@ $query = http_build_query(
$url = elgg_get_site_url() . "search?$query";
+$more_items = $vars['results']['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
+
// get pagination
if (array_key_exists('pagination', $vars['params']) && $vars['params']['pagination']) {
- $nav = elgg_view('navigation/pagination',array(
+ $nav = elgg_view('navigation/pagination', array(
'base_url' => $url,
'offset' => $vars['params']['offset'],
'count' => $vars['results']['count'],
'limit' => $vars['params']['limit'],
));
+ $show_more = false;
} else {
+ // faceted search page so no pagination
$nav = '';
+ $show_more = $more_items > 0;
}
// figure out what we're dealing with.
@@ -75,12 +80,7 @@ if (array_key_exists('search_type', $vars['params'])
$type_str = $search_type_str;
}
-// get any more links.
-$more_check = $vars['results']['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
-$more = ($more_check > 0) ? $more_check : 0;
-
-if ($more) {
- $title_key = ($more == 1) ? 'comment' : 'comments';
+if ($show_more) {
$more_str = elgg_echo('search:more', array($count, $type_str));
$more_url = elgg_http_remove_url_query_element($url, 'limit');
$more_link = "<li class='elgg-item'><a href=\"$more_url\">$more_str</a></li>";
diff --git a/mod/search/views/rss/search/comments/entity.php b/mod/search/views/rss/search/comments/entity.php
index 869779f35..e47afec4a 100644
--- a/mod/search/views/rss/search/comments/entity.php
+++ b/mod/search/views/rss/search/comments/entity.php
@@ -6,9 +6,12 @@
*/
$entity = $vars['entity'];
+$comments_data = $entity->getVolatileData('search_comments_data');
+$comment_data = array_shift($comments_data);
+$entity->setVolatileData('search_comments_data', $comments_data);
$author_name = '';
-$comment_author_guid = $entity->getVolatileData('search_matched_comment_owner_guid');
+$comment_author_guid = $comment_data['owner_guid'];
$author = get_user($comment_author_guid);
if ($author) {
$author_name = $author->name;
@@ -34,11 +37,11 @@ if ($entity->getVolatileData('search_unavailable_entity')) {
$title = elgg_echo('search:comment_on', array($title));
$title .= ' ' . elgg_echo('search:comment_by') . ' ' . $author_name;
- $url = $entity->getURL() . '#annotation-' . $entity->getVolatileData('search_match_annotation_id');
+ $url = $entity->getURL() . '#annotation-' . $comment_data['annotation_id'];
}
-$description = $entity->getVolatileData('search_matched_comment');
-$tc = $entity->getVolatileData('search_matched_comment_time_created');;
+$description = $comment_data['text'];
+$tc = $comment_data['time_created'];
?>
diff --git a/mod/simplepie/README b/mod/simplepie/README
index 24591c3e1..29a9f38aa 100644
--- a/mod/simplepie/README
+++ b/mod/simplepie/README
@@ -1,11 +1,11 @@
Widget Title
----------------------------
+===========
To change the widget title, edit the language file (en.php) and change the
string 'simplepie:widget' from 'RSS Feed' to whatever you desire.
Proxy Server
-----------------------------
+===========
If your site is going through a proxy server to get to the feeds, you may
want to increase the timeout on the feeds (though this is unlikely as the
default timeout is 10 seconds). You can do this by editing
@@ -14,6 +14,6 @@ uncomment the line $feed->set_timeout(20);
HTML tags in feeds
---------------------------
+=================
The widget allows the following tags: `<a><p><br><b><i><em><del><pre><strong><ul><ol><li><img>`.
-Other tags are stripped to avoid problems with the display of your site. \ No newline at end of file
+Other tags are stripped to avoid problems with the display of your site.
diff --git a/mod/simplepie/actions/simplepie/save_group_feed.php b/mod/simplepie/actions/simplepie/save_group_feed.php
new file mode 100644
index 000000000..8dc2ffd78
--- /dev/null
+++ b/mod/simplepie/actions/simplepie/save_group_feed.php
@@ -0,0 +1,14 @@
+<?php
+
+$group = get_entity((int)get_input('group_guid'));
+$feed_url = get_input('feed_url');
+
+if (!simplepie_is_url($feed_url)) {
+ register_error (elgg_echo("simplepie:invalid_url"));
+ forward(REFERER);
+}
+
+if (!elgg_instanceof($group, 'group') || !$group->canEdit()) {
+ forward(REFERER);
+}
+$group->feed_url = $feed_url;
diff --git a/mod/simplepie/languages/en.php b/mod/simplepie/languages/en.php
index 07ff02905..a22316863 100644
--- a/mod/simplepie/languages/en.php
+++ b/mod/simplepie/languages/en.php
@@ -1,16 +1,14 @@
<?php
-
$english = array(
'simplepie:widget' => 'RSS Feed',
'simplepie:description' => 'Add an external blog to your profile',
'simplepie:notset' => 'Feed url is not set',
- 'simplepie:notfind' => 'Cannot find feed. Check the feed url.',
+ 'simplepie:notfound' => 'Cannot find feed. Check the feed url.',
'simplepie:feed_url' => 'Feed URL',
'simplepie:num_items' => 'Number of items',
'simplepie:excerpt' => 'Include excerpt',
'simplepie:post_date' => 'Include post date',
'simplepie:postedon' => 'Posted on',
+ 'simplepie:invalid_url' => 'Invalid url, copy it from the navigation bar please',
);
-
add_translation("en", $english);
-
diff --git a/mod/simplepie/languages/es.php b/mod/simplepie/languages/es.php
new file mode 100644
index 000000000..d9bd5d2d6
--- /dev/null
+++ b/mod/simplepie/languages/es.php
@@ -0,0 +1,14 @@
+<?php
+$spanish = array(
+ 'simplepie:widget' => 'Enlace RSS',
+ 'simplepie:description' => 'Agregar un blog externo',
+ 'simplepie:notset' => 'Enlace RSS no configurado',
+ 'simplepie:notfound' => 'no se encontro el feed. Revisa el feed url.',
+ 'simplepie:feed_url' => 'Feed URL',
+ 'simplepie:num_items' => 'Numero de items',
+ 'simplepie:excerpt' => 'Incluir contenido',
+ 'simplepie:post_date' => 'Incluir fecha del post',
+ 'simplepie:postedon' => 'Posted on',
+ 'simplepie:invalid_url' => 'Url invalida, copiela desde la barra del navegador por favor',
+);
+add_translation("en", $english);
diff --git a/mod/simplepie/manifest.xml b/mod/simplepie/manifest.xml
index 94b8a4b42..eff9681d2 100644
--- a/mod/simplepie/manifest.xml
+++ b/mod/simplepie/manifest.xml
@@ -2,7 +2,7 @@
<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
<name>SimplePie RSS Feed Integration</name>
<author>Cash Costello</author>
- <version>1.8.0</version>
+ <version>1.8.1.1</version>
<category>widget</category>
<description>Provides a widget that pulls in RSS feeds</description>
<website>http://www.cashcostello.com/</website>
diff --git a/mod/simplepie/start.php b/mod/simplepie/start.php
index 20adc7545..34a7ced3f 100644
--- a/mod/simplepie/start.php
+++ b/mod/simplepie/start.php
@@ -20,4 +20,14 @@ function simplepie_init() {
$lib = elgg_get_plugins_path() . 'simplepie/vendors/simplepie.inc';
elgg_register_library('simplepie', $lib);
+
+ // Add group option
+ add_group_tool_option('rss', elgg_echo('simplepie:enablerss'), false);
+ elgg_extend_view('groups/tool_latest', 'simplepie/group_module');
+
+ elgg_register_action('simplepie/save_group_feed', elgg_get_plugins_path() . 'simplepie/actions/simplepie/save_group_feed.php');
+}
+
+function simplepie_is_url($url) {
+ return preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $url);
}
diff --git a/mod/simplepie/views/default/forms/simplepie/save_group_feed.php b/mod/simplepie/views/default/forms/simplepie/save_group_feed.php
new file mode 100644
index 000000000..a3f771586
--- /dev/null
+++ b/mod/simplepie/views/default/forms/simplepie/save_group_feed.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Simplepie feed reader widget settings
+ */
+
+// set default value
+
+$url_label = elgg_echo("simplepie:feed_url");
+$url_textbox = elgg_view('input/text', array(
+ 'name' => 'feed_url',
+ 'id'=> 'feed_url',
+ 'value' => $vars['entity']->feed_url,
+));
+
+$group_field = elgg_view('input/hidden', array(
+ 'name' => 'group_guid',
+ 'value' => $vars['entity']->guid,
+));
+
+$save_button = elgg_view('input/submit', array(
+ 'value' => elgg_echo('save'),
+));
+
+
+
+
+echo <<<HTML
+<div>
+ <label for="feed_url">$url_label</label>
+ $url_textbox
+ $group_field
+</div>
+<div>
+ $save_button
+</div>
+HTML;
+
+
+
diff --git a/mod/simplepie/views/default/simplepie/group_module.php b/mod/simplepie/views/default/simplepie/group_module.php
new file mode 100644
index 000000000..90c017a86
--- /dev/null
+++ b/mod/simplepie/views/default/simplepie/group_module.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Group simplepie:rss module
+ */
+elgg_load_library('simplepie');
+
+$group = elgg_get_page_owner_entity();
+
+if ($group->rss_enable != "yes") {
+ return true;
+}
+
+elgg_push_context('widgets');
+
+$allowed_tags = '<a><p><br><b><i><em><del><pre><strong><ul><ol><li><img>';
+$feed_url = $group->feed_url;
+$content = '';
+
+if ($group->canEdit()) {
+ $content .= elgg_view_form("simplepie/save_group_feed", array(
+ 'id' => 'simplepie-form',
+ 'class' => $feed_url ? 'hidden' : '',
+ ), $vars);
+}
+
+if ($feed_url) {
+
+// get widget settings
+ $excerpt = true;
+ $post_date = true;
+ $num_items = 7;
+ $cache_location = elgg_get_data_path() . '/simplepie_cache/';
+ if (!file_exists($cache_location)) {
+ mkdir($cache_location, 0777);
+ }
+ $feed = new SimplePie($feed_url, $cache_location);
+
+ // doubles timeout if going through a proxy
+ //$feed->set_timeout(20);
+ // only display errors to profile owner
+
+ $num_posts_in_feed = $feed->get_item_quantity();
+ if (!$num_posts_in_feed) {
+ if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) {
+ $content .= '<p>' . elgg_echo('simplepie:notfound') . '</p>';
+ }
+ }
+
+ // don't display more feed items than user requested
+ if ($num_items > $num_posts_in_feed) {
+ $num_items = $num_posts_in_feed;
+}
+
+ $feed_link = elgg_view('output/url', array(
+ 'href' => $feed->get_permalink(),
+ 'text' => $feed->get_title(),
+ ));
+
+ // need to center
+ $content .= "<h2 class=\"simplepie-heading\">$feed_link</h2>";
+ $content .= '<ul class="simplepie-list">';
+ foreach ($feed->get_items(0, $num_items) as $item) {
+ $item_link = elgg_view('output/url', array(
+ 'href' => $item->get_permalink(),
+ 'text' => $item->get_title(),
+ ));
+
+ if ($excerpt) {
+ $text = strip_tags($item->get_description(true), $allowed_tags);
+ $excerpt = elgg_get_excerpt($text);
+ }
+
+ if ($post_date) {
+ $item_date_label = elgg_echo('simplepie:postedon');
+ $item_date = $item->get_date('j F Y | g:i a');
+ $post_date = "$item_date_label $item_date";
+ }
+
+ $content .= <<<HTML
+<li class="mbm elgg-item">
+ <h4 class="mbs">$item_link</h4>
+ <p class="elgg-subtext">$post_date</p>
+ <div class="elgg-content">$excerpt</div>
+</li>
+HTML;
+ }
+ $content .= "</ul>";
+}
+
+elgg_pop_context();
+
+if (!$content) {
+ $content = '<p>' . elgg_echo('simplepie:none') . '</p>';
+}
+if ($group->canEdit()) {
+ $edit = elgg_view('output/url', array(
+ 'href' => '#simplepie-form',
+ 'text' => elgg_echo('edit'),
+ 'rel' => 'toggle'
+ ));
+} else {
+ $edit = false;
+}
+
+echo elgg_view('groups/profile/module', array(
+ 'title' => elgg_echo('RSS Group'),
+ 'content' => $content,
+ 'all_link' => $edit,
+));
diff --git a/mod/thewire/pages/thewire/everyone.php b/mod/thewire/pages/thewire/everyone.php
index 909f0caf2..c7438747e 100644
--- a/mod/thewire/pages/thewire/everyone.php
+++ b/mod/thewire/pages/thewire/everyone.php
@@ -18,7 +18,7 @@ if (elgg_is_logged_in()) {
$content .= elgg_list_entities(array(
'type' => 'object',
'subtype' => 'thewire',
- 'limit' => 15,
+ 'limit' => get_input('limit', 15),
));
$body = elgg_view_layout('content', array(
diff --git a/mod/thewire/pages/thewire/friends.php b/mod/thewire/pages/thewire/friends.php
index e7f5eed59..efa7e7a56 100644
--- a/mod/thewire/pages/thewire/friends.php
+++ b/mod/thewire/pages/thewire/friends.php
@@ -5,7 +5,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('thewire/all');
+ forward('', '404');
}
$title = elgg_echo('thewire:friends');
diff --git a/mod/thewire/pages/thewire/owner.php b/mod/thewire/pages/thewire/owner.php
index 6246c1770..dc25940e1 100644
--- a/mod/thewire/pages/thewire/owner.php
+++ b/mod/thewire/pages/thewire/owner.php
@@ -6,7 +6,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('thewire/all');
+ forward('', '404');
}
$title = elgg_echo('thewire:user', array($owner->name));
@@ -26,7 +26,7 @@ $content .= elgg_list_entities(array(
'type' => 'object',
'subtype' => 'thewire',
'owner_guid' => $owner->guid,
- 'limit' => 15,
+ 'limit' => get_input('limit', 15),
));
$body = elgg_view_layout('content', array(
diff --git a/mod/tinymce/views/default/js/tinymce.php b/mod/tinymce/views/default/js/tinymce.php
index b4db43cee..344d71b14 100644
--- a/mod/tinymce/views/default/js/tinymce.php
+++ b/mod/tinymce/views/default/js/tinymce.php
@@ -66,6 +66,18 @@ elgg.tinymce.init = function() {
var text = elgg.echo('tinymce:word_count') + strip.split(' ').length + ' ';
tinymce.DOM.setHTML(tinymce.DOM.get(tinyMCE.activeEditor.id + '_path_row'), text);
});
+
+ ed.onInit.add(function(ed) {
+ // prevent Firefox from dragging/dropping files into editor
+ if (tinymce.isGecko) {
+ tinymce.dom.Event.add(ed.getBody().parentNode, "drop", function(e) {
+ if (e.dataTransfer.files.length > 0) {
+ e.preventDefault();
+ }
+ });
+ }
+ });
+
},
content_css: elgg.config.wwwroot + 'mod/tinymce/css/elgg_tinymce.css'
});
diff --git a/mod/twitter/graphics/thewire_speech_bubble.gif b/mod/twitter/graphics/thewire_speech_bubble.gif
deleted file mode 100644
index d0e8606a1..000000000
--- a/mod/twitter/graphics/thewire_speech_bubble.gif
+++ /dev/null
Binary files differ
diff --git a/mod/twitter/graphics/twitter16px.png b/mod/twitter/graphics/twitter16px.png
deleted file mode 100644
index de51c6953..000000000
--- a/mod/twitter/graphics/twitter16px.png
+++ /dev/null
Binary files differ
diff --git a/mod/twitter/languages/en.php b/mod/twitter/languages/en.php
deleted file mode 100644
index 11e745ba1..000000000
--- a/mod/twitter/languages/en.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * Twitter widget language file
- */
-
-$english = array(
- 'twitter:title' => 'Twitter',
- 'twitter:info' => 'Display your latest tweets',
- 'twitter:username' => 'Your twitter username',
- 'twitter:num' => 'Number of tweets to show*',
- 'twitter:visit' => 'visit my twitter',
- 'twitter:notset' => 'This widget needs to be configured. To display your latest tweets, click the customize icon and fill in your Twitter username.',
- 'twitter:invalid' => 'This widget is configured with an invalid Twitter username. Click the customize icon to correct it.',
- 'twitter:apibug' => "*Due to a bug in the Twitter 1.0 API, you may see fewer tweets than you ask for.",
-);
-
-add_translation("en", $english);
diff --git a/mod/twitter/manifest.xml b/mod/twitter/manifest.xml
deleted file mode 100644
index 18fa8c957..000000000
--- a/mod/twitter/manifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
- <name>Twitter Widget</name>
- <author>Core developers</author>
- <version>1.7</version>
- <category>bundled</category>
- <category>widget</category>
- <description>Elgg simple twitter widget</description>
- <website>http://www.elgg.org/</website>
- <copyright>See COPYRIGHT.txt</copyright>
- <license>GNU General Public License version 2</license>
- <requires>
- <type>elgg_release</type>
- <version>1.8</version>
- </requires>
-</plugin_manifest>
diff --git a/mod/twitter/start.php b/mod/twitter/start.php
deleted file mode 100644
index b793eadf0..000000000
--- a/mod/twitter/start.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-/**
- * Elgg twitter widget
- * This plugin allows users to pull in their twitter feed to display on their profile
- *
- * @package ElggTwitter
- */
-
-elgg_register_event_handler('init', 'system', 'twitter_init');
-
-function twitter_init() {
- elgg_extend_view('css/elgg', 'twitter/css');
- elgg_register_widget_type('twitter', elgg_echo('twitter:title'), elgg_echo('twitter:info'));
-}
diff --git a/mod/twitter/views/default/twitter/css.php b/mod/twitter/views/default/twitter/css.php
deleted file mode 100644
index eb0cda98a..000000000
--- a/mod/twitter/views/default/twitter/css.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/**
- * Elgg Twitter CSS
- *
- * @package ElggTwitter
- */
-?>
-
-#twitter_widget {
- margin:0 10px 0 10px;
-}
-#twitter_widget ul {
- margin:0;
- padding:0;
-}
-#twitter_widget li {
- list-style-image:none;
- list-style-position:outside;
- list-style-type:none;
- margin:0 0 5px 0;
- padding:0;
- overflow-x: hidden;
- border: 2px solid #dedede;
- -webkit-border-radius: 8px;
- -moz-border-radius: 8px;
- border-radius: 8px;
-}
-#twitter_widget li span {
- color:#666666;
- background:white;
-
- -webkit-border-radius: 8px;
- -moz-border-radius: 8px;
- border-radius: 8px;
-
- padding:5px;
- display:block;
-}
-p.visit_twitter a {
- background:url(<?php echo elgg_get_site_url(); ?>mod/twitter/graphics/twitter16px.png) left no-repeat;
- padding:0 0 0 20px;
- margin:0;
-}
-p.twitter_username .input-text {
- width:200px;
-}
-.visit_twitter {
- background:white;
-
- -webkit-border-radius: 8px;
- -moz-border-radius: 8px;
- border-radius: 8px;
-
- padding:2px;
- margin:0 0 5px 0;
-}
-#twitter_widget li > a {
- display:block;
- margin:0 0 0 4px;
-}
-#twitter_widget li span a {
- display:inline !important;
-} \ No newline at end of file
diff --git a/mod/twitter/views/default/widgets/twitter/content.php b/mod/twitter/views/default/widgets/twitter/content.php
deleted file mode 100644
index caefd369a..000000000
--- a/mod/twitter/views/default/widgets/twitter/content.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * Elgg twitter view page
- *
- * @package ElggTwitter
- */
-
-$username = $vars['entity']->twitter_username;
-
-if (empty($username)) {
- echo "<p>" . elgg_echo("twitter:notset") . "</p>";
- return;
-}
-
-$username_is_valid = preg_match('~^[a-zA-Z0-9_]{1,20}$~', $username);
-if (!$username_is_valid) {
- echo "<p>" . elgg_echo("twitter:invalid") . "</p>";
- return;
-}
-
-
-$num = $vars['entity']->twitter_num;
-if (empty($num)) {
- $num = 5;
-}
-
-// @todo upgrade to 1.1 API https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
-$script_url = "https://api.twitter.com/1/statuses/user_timeline/" . urlencode($username) . ".json"
- . "?callback=twitterCallback2&count=" . (int) $num;
-
-?>
-<div id="twitter_widget">
- <ul id="twitter_update_list"></ul>
- <p class="visit_twitter"><?php echo elgg_view('output/url', array(
- 'text' => elgg_echo("twitter:visit"),
- 'href' => 'http://twitter.com/' . urlencode($username),
- 'is_trusted' => true,
- )) ?></p>
- <script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
- <script type="text/javascript" src="<?php echo htmlspecialchars($script_url, ENT_QUOTES, 'UTF-8') ?>"></script>
-</div>
diff --git a/mod/twitter/views/default/widgets/twitter/edit.php b/mod/twitter/views/default/widgets/twitter/edit.php
deleted file mode 100644
index c3fc6f0d5..000000000
--- a/mod/twitter/views/default/widgets/twitter/edit.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * Elgg twitter edit page
- *
- * @package ElggTwitter
- */
-
-?>
-<div>
- <?php echo elgg_echo("twitter:username"); ?>
- <?php echo elgg_view('input/text', array(
- 'name' => 'params[twitter_username]',
- 'value' => $vars['entity']->twitter_username,
- )) ?>
-</div>
-<div>
- <?php echo elgg_echo("twitter:num"); ?>
- <?php echo elgg_view('input/text', array(
- 'name' => 'params[twitter_num]',
- 'value' => $vars['entity']->twitter_num,
- )) ?>
- <span class="elgg-text-help"><?php echo elgg_echo("twitter:apibug"); ?></span>
-</div> \ No newline at end of file
diff --git a/mod/twitter_api/languages/en.php b/mod/twitter_api/languages/en.php
index f4b3c7f94..a6f4b40a5 100644
--- a/mod/twitter_api/languages/en.php
+++ b/mod/twitter_api/languages/en.php
@@ -25,7 +25,9 @@ $english = array(
'twitter_api:revoke:success' => 'Twitter access has been revoked.',
- 'twitter_api:login' => 'Allow existing users who have connected their Twitter account to sign in with Twitter?',
+ 'twitter_api:post_to_twitter' => "Send users' wire posts to Twitter?",
+
+ 'twitter_api:login' => 'Allow users to sign in with Twitter?',
'twitter_api:new_users' => 'Allow new users to sign up using their Twitter account even if user registration is disabled?',
'twitter_api:login:success' => 'You have been logged in.',
'twitter_api:login:error' => 'Unable to login with Twitter.',
diff --git a/mod/twitter_api/lib/twitter_api.php b/mod/twitter_api/lib/twitter_api.php
index e163d2b3e..a7b971876 100644
--- a/mod/twitter_api/lib/twitter_api.php
+++ b/mod/twitter_api/lib/twitter_api.php
@@ -6,6 +6,27 @@
*/
/**
+ * Get the API wrapper object
+ *
+ * @param string $oauth_token User's OAuth token
+ * @param string $oauth_token_secret User's OAuth secret
+ * @return TwitterOAuth|null
+ */
+function twitter_api_get_api_object($oauth_token = null, $oauth_token_secret = null) {
+ $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
+ $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
+ if (!($consumer_key && $consumer_secret)) {
+ return null;
+ }
+
+ $api = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret);
+ if ($api) {
+ $api->host = "https://api.twitter.com/1.1/";
+ }
+ return $api;
+}
+
+/**
* Tests if the system admin has enabled Sign-On-With-Twitter
*
* @param void
@@ -94,7 +115,7 @@ function twitter_api_login() {
$forward = $login_metadata['forward'];
}
- if (!isset($token['oauth_token']) or !isset($token['oauth_token_secret'])) {
+ if (!isset($token['oauth_token']) || !isset($token['oauth_token_secret'])) {
register_error(elgg_echo('twitter_api:login:error'));
forward();
}
@@ -121,9 +142,7 @@ function twitter_api_login() {
forward();
}
} else {
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $token['oauth_token'], $token['oauth_token_secret']);
+ $api = twitter_api_get_api_object($token['oauth_token'], $token['oauth_token_secret']);
$twitter = $api->get('account/verify_credentials');
// backward compatibility for deprecated Twitter Login plugin
@@ -255,7 +274,7 @@ function twitter_api_update_user_avatar($user, $file_location) {
* to establish session request tokens.
*/
function twitter_api_authorize() {
- $token = twitter_api_get_access_token();
+ $token = twitter_api_get_access_token(get_input('oauth_verifier'));
if (!isset($token['oauth_token']) || !isset($token['oauth_token_secret'])) {
register_error(elgg_echo('twitter_api:authorize:error'));
forward('settings/plugins', 'twitter_api');
@@ -314,11 +333,8 @@ function twitter_api_revoke() {
function twitter_api_get_authorize_url($callback = NULL, $login = true) {
global $SESSION;
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
-
// request tokens from Twitter
- $twitter = new TwitterOAuth($consumer_key, $consumer_secret);
+ $twitter = twitter_api_get_api_object();
$token = $twitter->getRequestToken($callback);
// save token in session for use after authorization
@@ -340,16 +356,13 @@ function twitter_api_get_access_token($oauth_verifier = FALSE) {
/* @var ElggSession $SESSION */
global $SESSION;
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
-
// retrieve stored tokens
$oauth_token = $SESSION['twitter_api']['oauth_token'];
$oauth_token_secret = $SESSION['twitter_api']['oauth_token_secret'];
unset($SESSION['twitter_api']);
// fetch an access token
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret);
+ $api = twitter_api_get_api_object($oauth_token, $oauth_token_secret);
return $api->getAccessToken($oauth_verifier);
}
@@ -367,4 +380,4 @@ function twitter_api_allow_new_users_with_twitter() {
}
return false;
-} \ No newline at end of file
+}
diff --git a/mod/twitter_api/manifest.xml b/mod/twitter_api/manifest.xml
index 86bba4b50..3af866bba 100644
--- a/mod/twitter_api/manifest.xml
+++ b/mod/twitter_api/manifest.xml
@@ -2,7 +2,7 @@
<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
<name>Twitter API</name>
<author>Core developers</author>
- <version>1.8</version>
+ <version>1.8.15</version>
<description>Allows users to authenticate their Elgg account with Twitter.</description>
<category>api</category>
<category>bundled</category>
@@ -14,16 +14,16 @@
<version>1.8</version>
</requires>
<requires>
- <type>plugin</type>
- <name>oauth_api</name>
- </requires>
- <requires>
<type>php_extension</type>
<name>curl</name>
</requires>
<conflicts>
<type>plugin</type>
+ <name>oauth_api</name>
+ </conflicts>
+ <conflicts>
+ <type>plugin</type>
<name>twitterservice</name>
</conflicts>
</plugin_manifest>
diff --git a/mod/twitter_api/pages/twitter_api/interstitial.php b/mod/twitter_api/pages/twitter_api/interstitial.php
index d1f1ac20c..23b5069cb 100644
--- a/mod/twitter_api/pages/twitter_api/interstitial.php
+++ b/mod/twitter_api/pages/twitter_api/interstitial.php
@@ -8,9 +8,7 @@
$title = elgg_echo('twitter_api:interstitial:settings');
-$site = get_config('site');
-$content = elgg_echo('twitter_api:interstitial:description', array($site->name));
-$content .= elgg_view_form('twitter_api/interstitial_settings');
+$content = elgg_view_form('twitter_api/interstitial_settings');
$params = array(
'content' => $content,
diff --git a/mod/twitter_api/start.php b/mod/twitter_api/start.php
index e6221de6b..7318ac55d 100644
--- a/mod/twitter_api/start.php
+++ b/mod/twitter_api/start.php
@@ -35,8 +35,10 @@ function twitter_api_init() {
// register Walled Garden public pages
elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'twitter_api_public_pages');
- // push status messages to twitter
- elgg_register_plugin_hook_handler('status', 'user', 'twitter_api_tweet');
+ // push wire post messages to twitter
+ if (elgg_get_plugin_setting('wire_posts', 'twitter_api') == 'yes') {
+ elgg_register_plugin_hook_handler('status', 'user', 'twitter_api_tweet');
+ }
$actions = dirname(__FILE__) . '/actions/twitter_api';
elgg_register_action('twitter_api/interstitial_settings', "$actions/interstitial_settings.php", 'logged_in');
@@ -115,13 +117,6 @@ function twitter_api_tweet($hook, $type, $returnvalue, $params) {
// @todo - allow admin to select origins?
- // check admin settings
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
- if (!($consumer_key && $consumer_secret)) {
- return;
- }
-
// check user settings
$user_id = $params['user']->getGUID();
$access_key = elgg_get_plugin_user_setting('access_key', $user_id, 'twitter_api');
@@ -130,8 +125,11 @@ function twitter_api_tweet($hook, $type, $returnvalue, $params) {
return;
}
- // send tweet
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $access_key, $access_secret);
+ $api = twitter_api_get_api_object($access_key, $access_secret);
+ if (!$api) {
+ return;
+ }
+
$api->post('statuses/update', array('status' => $params['message']));
}
@@ -143,12 +141,6 @@ function twitter_api_tweet($hook, $type, $returnvalue, $params) {
* @return array
*/
function twitter_api_fetch_tweets($user_guid, $options = array()) {
- // check admin settings
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
- if (!($consumer_key && $consumer_secret)) {
- return FALSE;
- }
// check user settings
$access_key = elgg_get_plugin_user_setting('access_key', $user_guid, 'twitter_api');
@@ -157,8 +149,11 @@ function twitter_api_fetch_tweets($user_guid, $options = array()) {
return FALSE;
}
- // fetch tweets
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $access_key, $access_secret);
+ $api = twitter_api_get_api_object($access_key, $access_secret);
+ if (!$api) {
+ return FALSE;
+ }
+
return $api->get('statuses/user_timeline', $options);
}
diff --git a/mod/twitter_api/vendors/twitteroauth/OAuth.php b/mod/twitter_api/vendors/twitteroauth/OAuth.php
index e132a5bc8..e76304146 100644
--- a/mod/twitter_api/vendors/twitteroauth/OAuth.php
+++ b/mod/twitter_api/vendors/twitteroauth/OAuth.php
@@ -1,6 +1,12 @@
<?php
// vim: foldmethod=marker
+/* Generic exception class
+ */
+class OAuthException extends Exception {
+ // pass
+}
+
class OAuthConsumer {
public $key;
public $secret;
@@ -46,12 +52,56 @@ class OAuthToken {
}
}
-class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SHA1 {/*{{{*/
- function get_name() {/*{{{*/
+/**
+ * A class for implementing a Signature Method
+ * See section 9 ("Signing Requests") in the spec
+ */
+abstract class OAuthSignatureMethod {
+ /**
+ * Needs to return the name of the Signature Method (ie HMAC-SHA1)
+ * @return string
+ */
+ abstract public function get_name();
+
+ /**
+ * Build up the signature
+ * NOTE: The output of this function MUST NOT be urlencoded.
+ * the encoding is handled in OAuthRequest when the final
+ * request is serialized
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @return string
+ */
+ abstract public function build_signature($request, $consumer, $token);
+
+ /**
+ * Verifies that a given signature is correct
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @param string $signature
+ * @return bool
+ */
+ public function check_signature($request, $consumer, $token, $signature) {
+ $built = $this->build_signature($request, $consumer, $token);
+ return $built == $signature;
+ }
+}
+
+/**
+ * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
+ * where the Signature Base String is the text and the key is the concatenated values (each first
+ * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
+ * character (ASCII code 38) even if empty.
+ * - Chapter 9.2 ("HMAC-SHA1")
+ */
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
+ function get_name() {
return "HMAC-SHA1";
- }/*}}}*/
+ }
- public function build_signature($request, $consumer, $token) {/*{{{*/
+ public function build_signature($request, $consumer, $token) {
$base_string = $request->get_signature_base_string();
$request->base_string = $base_string;
@@ -63,16 +113,111 @@ class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SH
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key = implode('&', $key_parts);
- return base64_encode( hash_hmac('sha1', $base_string, $key, true));
- }/*}}}*/
+ return base64_encode(hash_hmac('sha1', $base_string, $key, true));
+ }
+}
- public function check_signature(&$request, $consumer, $token, $signature) {
- $built = $this->build_signature($request, $consumer, $token);
- return $built == $signature;
+/**
+ * The PLAINTEXT method does not provide any security protection and SHOULD only be used
+ * over a secure channel such as HTTPS. It does not use the Signature Base String.
+ * - Chapter 9.4 ("PLAINTEXT")
+ */
+class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
+ public function get_name() {
+ return "PLAINTEXT";
+ }
+
+ /**
+ * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
+ * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
+ * empty. The result MUST be encoded again.
+ * - Chapter 9.4.1 ("Generating Signatures")
+ *
+ * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
+ * OAuthRequest handles this!
+ */
+ public function build_signature($request, $consumer, $token) {
+ $key_parts = array(
+ $consumer->secret,
+ ($token) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
+ $key = implode('&', $key_parts);
+ $request->base_string = $key;
+
+ return $key;
}
-}/*}}}*/
+}
+
+/**
+ * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
+ * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
+ * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
+ * verified way to the Service Provider, in a manner which is beyond the scope of this
+ * specification.
+ * - Chapter 9.3 ("RSA-SHA1")
+ */
+abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
+ public function get_name() {
+ return "RSA-SHA1";
+ }
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
+ // (2) fetch via http using a url provided by the requester
+ // (3) some sort of specific discovery code based on request
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_public_cert(&$request);
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_private_cert(&$request);
+
+ public function build_signature($request, $consumer, $token) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ // Fetch the private key cert based on the request
+ $cert = $this->fetch_private_cert($request);
+
+ // Pull the private key ID from the certificate
+ $privatekeyid = openssl_get_privatekey($cert);
+
+ // Sign using the key
+ $ok = openssl_sign($base_string, $signature, $privatekeyid);
+
+ // Release the key resource
+ openssl_free_key($privatekeyid);
+
+ return base64_encode($signature);
+ }
+
+ public function check_signature($request, $consumer, $token, $signature) {
+ $decoded_sig = base64_decode($signature);
+
+ $base_string = $request->get_signature_base_string();
+
+ // Fetch the public key cert based on the request
+ $cert = $this->fetch_public_cert($request);
+
+ // Pull the public key ID from the certificate
+ $publickeyid = openssl_get_publickey($cert);
-class twitterOAuthRequest extends OAuthRequest {
+ // Check the computed signature against the one passed in the query
+ $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
+
+ // Release the key resource
+ openssl_free_key($publickeyid);
+
+ return $ok == 1;
+ }
+}
+
+class OAuthRequest {
private $parameters;
private $http_method;
private $http_url;
@@ -138,7 +283,7 @@ class twitterOAuthRequest extends OAuthRequest {
}
- return new twitterOAuthRequest($http_method, $http_url, $parameters);
+ return new OAuthRequest($http_method, $http_url, $parameters);
}
/**
@@ -146,16 +291,16 @@ class twitterOAuthRequest extends OAuthRequest {
*/
public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
@$parameters or $parameters = array();
- $defaults = array("oauth_version" => twitterOAuthRequest::$version,
- "oauth_nonce" => twitterOAuthRequest::generate_nonce(),
- "oauth_timestamp" => twitterOAuthRequest::generate_timestamp(),
+ $defaults = array("oauth_version" => OAuthRequest::$version,
+ "oauth_nonce" => OAuthRequest::generate_nonce(),
+ "oauth_timestamp" => OAuthRequest::generate_timestamp(),
"oauth_consumer_key" => $consumer->key);
if ($token)
$defaults['oauth_token'] = $token->key;
$parameters = array_merge($defaults, $parameters);
- return new twitterOAuthRequest($http_method, $http_url, $parameters);
+ return new OAuthRequest($http_method, $http_url, $parameters);
}
public function set_parameter($name, $value, $allow_duplicates = true) {
@@ -333,6 +478,217 @@ class twitterOAuthRequest extends OAuthRequest {
}
}
+class OAuthServer {
+ protected $timestamp_threshold = 300; // in seconds, five minutes
+ protected $version = '1.0'; // hi blaine
+ protected $signature_methods = array();
+
+ protected $data_store;
+
+ function __construct($data_store) {
+ $this->data_store = $data_store;
+ }
+
+ public function add_signature_method($signature_method) {
+ $this->signature_methods[$signature_method->get_name()] =
+ $signature_method;
+ }
+
+ // high level functions
+
+ /**
+ * process a request_token request
+ * returns the request token on success
+ */
+ public function fetch_request_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // no token required for the initial token request
+ $token = NULL;
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $callback = $request->get_parameter('oauth_callback');
+ $new_token = $this->data_store->new_request_token($consumer, $callback);
+
+ return $new_token;
+ }
+
+ /**
+ * process an access_token request
+ * returns the access token on success
+ */
+ public function fetch_access_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // requires authorized request token
+ $token = $this->get_token($request, $consumer, "request");
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $verifier = $request->get_parameter('oauth_verifier');
+ $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
+
+ return $new_token;
+ }
+
+ /**
+ * verify an api call, checks all the parameters
+ */
+ public function verify_request(&$request) {
+ $this->get_version($request);
+ $consumer = $this->get_consumer($request);
+ $token = $this->get_token($request, $consumer, "access");
+ $this->check_signature($request, $consumer, $token);
+ return array($consumer, $token);
+ }
+
+ // Internals from here
+ /**
+ * version 1
+ */
+ private function get_version(&$request) {
+ $version = $request->get_parameter("oauth_version");
+ if (!$version) {
+ // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
+ // Chapter 7.0 ("Accessing Protected Ressources")
+ $version = '1.0';
+ }
+ if ($version !== $this->version) {
+ throw new OAuthException("OAuth version '$version' not supported");
+ }
+ return $version;
+ }
+
+ /**
+ * figure out the signature with some defaults
+ */
+ private function get_signature_method(&$request) {
+ $signature_method =
+ @$request->get_parameter("oauth_signature_method");
+
+ if (!$signature_method) {
+ // According to chapter 7 ("Accessing Protected Ressources") the signature-method
+ // parameter is required, and we can't just fallback to PLAINTEXT
+ throw new OAuthException('No signature method parameter. This parameter is required');
+ }
+
+ if (!in_array($signature_method,
+ array_keys($this->signature_methods))) {
+ throw new OAuthException(
+ "Signature method '$signature_method' not supported " .
+ "try one of the following: " .
+ implode(", ", array_keys($this->signature_methods))
+ );
+ }
+ return $this->signature_methods[$signature_method];
+ }
+
+ /**
+ * try to find the consumer for the provided request's consumer key
+ */
+ private function get_consumer(&$request) {
+ $consumer_key = @$request->get_parameter("oauth_consumer_key");
+ if (!$consumer_key) {
+ throw new OAuthException("Invalid consumer key");
+ }
+
+ $consumer = $this->data_store->lookup_consumer($consumer_key);
+ if (!$consumer) {
+ throw new OAuthException("Invalid consumer");
+ }
+
+ return $consumer;
+ }
+
+ /**
+ * try to find the token for the provided request's token key
+ */
+ private function get_token(&$request, $consumer, $token_type="access") {
+ $token_field = @$request->get_parameter('oauth_token');
+ $token = $this->data_store->lookup_token(
+ $consumer, $token_type, $token_field
+ );
+ if (!$token) {
+ throw new OAuthException("Invalid $token_type token: $token_field");
+ }
+ return $token;
+ }
+
+ /**
+ * all-in-one function to check the signature on a request
+ * should guess the signature method appropriately
+ */
+ private function check_signature(&$request, $consumer, $token) {
+ // this should probably be in a different method
+ $timestamp = @$request->get_parameter('oauth_timestamp');
+ $nonce = @$request->get_parameter('oauth_nonce');
+
+ $this->check_timestamp($timestamp);
+ $this->check_nonce($consumer, $token, $nonce, $timestamp);
+
+ $signature_method = $this->get_signature_method($request);
+
+ $signature = $request->get_parameter('oauth_signature');
+ $valid_sig = $signature_method->check_signature(
+ $request,
+ $consumer,
+ $token,
+ $signature
+ );
+
+ if (!$valid_sig) {
+ throw new OAuthException("Invalid signature");
+ }
+ }
+
+ /**
+ * check that the timestamp is new enough
+ */
+ private function check_timestamp($timestamp) {
+ if( ! $timestamp )
+ throw new OAuthException(
+ 'Missing timestamp parameter. The parameter is required'
+ );
+
+ // verify that timestamp is recentish
+ $now = time();
+ if (abs($now - $timestamp) > $this->timestamp_threshold) {
+ throw new OAuthException(
+ "Expired timestamp, yours $timestamp, ours $now"
+ );
+ }
+ }
+
+ /**
+ * check that the nonce is not repeated
+ */
+ private function check_nonce($consumer, $token, $nonce, $timestamp) {
+ if( ! $nonce )
+ throw new OAuthException(
+ 'Missing nonce parameter. The parameter is required'
+ );
+
+ // verify that the nonce is uniqueish
+ $found = $this->data_store->lookup_nonce(
+ $consumer,
+ $token,
+ $nonce,
+ $timestamp
+ );
+ if ($found) {
+ throw new OAuthException("Nonce already used: $nonce");
+ }
+ }
+
+}
+
class OAuthDataStore {
function lookup_consumer($consumer_key) {
// implement me
@@ -514,5 +870,3 @@ class OAuthUtil {
return implode('&', $pairs);
}
}
-
-?>
diff --git a/mod/twitter_api/vendors/twitteroauth/README b/mod/twitter_api/vendors/twitteroauth/README
index 33cb91f21..c9a17ce4b 100644
--- a/mod/twitter_api/vendors/twitteroauth/README
+++ b/mod/twitter_api/vendors/twitteroauth/README
@@ -1,7 +1,114 @@
-Abraham Williams | abraham@poseurte.ch | http://abrah.am | @abraham
+TwitterOAuth
+------------
-The first PHP library for working with Twitter's OAuth API.
+PHP library for working with Twitter's OAuth API.
-Documentation: http://wiki.github.com/abraham/twitteroauth/documentation
-Source: http://github.com/abraham/twitteroauth
-Twitter: http://apiwiki.twitter.com
+Flow Overview
+=============
+
+1. Build TwitterOAuth object using client credentials.
+2. Request temporary credentials from Twitter.
+3. Build authorize URL for Twitter.
+4. Redirect user to authorize URL.
+5. User authorizes access and returns from Twitter.
+6. Rebuild TwitterOAuth object with client credentials and temporary credentials.
+7. Get token credentials from Twitter.
+8. Rebuild TwitterOAuth object with client credentials and token credentials.
+9. Query Twitter API.
+
+Terminology
+===========
+
+The terminology has changed since 0.1.x to better match the draft-hammer-oauth IETF
+RFC. You can read that at http://tools.ietf.org/html/draft-hammer-oauth. Some of the
+terms will differ from those Twitter uses as well.
+
+client credentials - Consumer key/secret you get when registering an app with Twitter.
+temporary credentials - Previously known as the request token.
+token credentials - Previously known as the access token.
+
+Parameters
+==========
+
+There are a number of parameters you can modify after creating a TwitterOAuth object.
+
+Switch an existing TwitterOAuth install to use version 1.1 of the API.
+
+ $connection->$host = "https://api.twitter.com/1.1/";
+
+Custom useragent.
+
+ $connection->useragent = 'Custom useragent string';
+
+Verify Twitters SSL certificate.
+
+ $connection->ssl_verifypeer = TRUE;
+
+There are several more you can find in TwitterOAuth.php.
+
+Extended flow using example code
+================================
+
+To use TwitterOAuth with the Twitter API you need *TwitterOAuth.php*, *OAuth.php* and
+client credentials. You can get client credentials by registering your application at
+[dev.twitter.com/apps](https://dev.twitter.com/apps).
+
+Users start out on connect.php which displays the "Sign in with Twitter" image hyperlinked
+to redirect.php. This button should be displayed on your homepage in your login section. The
+client credentials are saved in config.php as `CONSUMER_KEY` and `CONSUMER_SECRET`. You can
+save a static callback URL in the app settings page, in the config file or use a dynamic
+callback URL later in step 2. In example use https://example.com/callback.php.
+
+1) When a user lands on redirect.php we build a new TwitterOAuth object using the client credentials.
+If you have your own configuration method feel free to use it instead of config.php.
+
+ $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET); // Use config.php client credentials
+ $connection = new TwitterOAuth('abc890', '123xyz');
+
+2) Using the built $connection object you will ask Twitter for temporary credentials. The `oauth_callback` value is required.
+
+ $temporary_credentials = $connection->getRequestToken(OAUTH_CALLBACK); // Use config.php callback URL.
+
+3) Now that we have temporary credentials the user has to go to Twitter and authorize the app
+to access and updates their data. You can also pass a second parameter of FALSE to not use [Sign
+in with Twitter](https://dev.twitter.com/docs/auth/sign-twitter).
+
+ $redirect_url = $connection->getAuthorizeURL($temporary_credentials); // Use Sign in with Twitter
+ $redirect_url = $connection->getAuthorizeURL($temporary_credentials, FALSE);
+
+4) You will now have a Twitter URL that you must send the user to.
+
+ https://api.twitter.com/oauth/authenticate?oauth_token=xyz123
+
+5) The user is now on twitter.com and may have to login. Once authenticated with Twitter they will
+will either have to click on allow/deny, or will be automatically redirected back to the callback.
+
+6) Now that the user has returned to callback.php and allowed access we need to build a new
+TwitterOAuth object using the temporary credentials.
+
+ $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_SESSION['oauth_token'],
+ $_SESSION['oauth_token_secret']);
+
+7) Now we ask Twitter for long lasting token credentials. These are specific to the application
+and user and will act like password to make future requests. Normally the token credentials would
+get saved in your database but for this example we are just using sessions.
+
+ $token_credentials = $connection->getAccessToken($_REQUEST['oauth_verifier']);
+
+8) With the token credentials we build a new TwitterOAuth object.
+
+ $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $token_credentials['oauth_token'],
+ $token_credentials['oauth_token_secret']);
+
+9) And finally we can make requests authenticated as the user. You can GET, POST, and DELETE API
+methods. Directly copy the path from the API documentation and add an array of any parameter
+you wish to include for the API method such as curser or in_reply_to_status_id.
+
+ $account = $connection->get('account/verify_credentials');
+ $status = $connection->post('statuses/update', array('status' => 'Text of status here', 'in_reply_to_status_id' => 123456));
+ $status = $connection->delete('statuses/destroy/12345');
+
+Contributors
+============
+
+* [Abraham Williams](https://twitter.com/abraham) - Main developer, current maintainer.
diff --git a/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php b/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php
index f36e6158d..4c2447c46 100644
--- a/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php
+++ b/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php
@@ -57,7 +57,7 @@ class TwitterOAuth {
* construct TwitterOAuth object
*/
function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {
- $this->sha1_method = new twitterOAuthSignatureMethod_HMAC_SHA1();
+ $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
$this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
if (!empty($oauth_token) && !empty($oauth_token_secret)) {
$this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
@@ -72,11 +72,9 @@ class TwitterOAuth {
*
* @returns a key/value array containing oauth_token and oauth_token_secret
*/
- function getRequestToken($oauth_callback = NULL) {
+ function getRequestToken($oauth_callback) {
$parameters = array();
- if (!empty($oauth_callback)) {
- $parameters['oauth_callback'] = $oauth_callback;
- }
+ $parameters['oauth_callback'] = $oauth_callback;
$request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
@@ -108,11 +106,9 @@ class TwitterOAuth {
* "user_id" => "9436992",
* "screen_name" => "abraham")
*/
- function getAccessToken($oauth_verifier = FALSE) {
+ function getAccessToken($oauth_verifier) {
$parameters = array();
- if (!empty($oauth_verifier)) {
- $parameters['oauth_verifier'] = $oauth_verifier;
- }
+ $parameters['oauth_verifier'] = $oauth_verifier;
$request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
@@ -179,7 +175,7 @@ class TwitterOAuth {
if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
$url = "{$this->host}{$url}.{$this->format}";
}
- $request = twitterOAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
+ $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
$request->sign_request($this->sha1_method, $this->consumer, $this->token);
switch ($method) {
case 'GET':
diff --git a/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php b/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php
index cad2be345..b4882bb7f 100644
--- a/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php
+++ b/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php
@@ -3,6 +3,11 @@
* Make the user set up some alternative ways to login.
*/
+echo '<div>';
+$site = get_config('site');
+echo elgg_echo('twitter_api:interstitial:description', array($site->name));
+echo '</div>';
+
$user = elgg_get_logged_in_user_entity();
if (elgg_is_sticky_form('twitter_api_interstitial')) {
@@ -51,7 +56,7 @@ echo elgg_view_module('info', $title, $body);
// buttons
echo elgg_view('input/submit', array(
- 'text' => elgg_echo('save')
+ 'value' => elgg_echo('save')
));
echo elgg_view('output/url', array(
diff --git a/mod/twitter_api/views/default/plugins/twitter_api/settings.php b/mod/twitter_api/views/default/plugins/twitter_api/settings.php
index 0b9afd4cf..3a3ec93a2 100644
--- a/mod/twitter_api/views/default/plugins/twitter_api/settings.php
+++ b/mod/twitter_api/views/default/plugins/twitter_api/settings.php
@@ -39,12 +39,27 @@ $new_users_with_twitter_view = elgg_view('input/dropdown', array(
'value' => $vars['entity']->new_users ? $vars['entity']->new_users : 'no',
));
+$post_to_twitter = '';
+if (elgg_is_active_plugin('thewire')) {
+ $post_to_twitter_string = elgg_echo('twitter_api:post_to_twitter');
+ $post_to_twitter_view = elgg_view('input/dropdown', array(
+ 'name' => 'params[wire_posts]',
+ 'options_values' => array(
+ 'yes' => elgg_echo('option:yes'),
+ 'no' => elgg_echo('option:no'),
+ ),
+ 'value' => $vars['entity']->wire_posts ? $vars['entity']->wire_posts : 'no',
+ ));
+ $post_to_twitter = "<div>$post_to_twitter_string $post_to_twitter_view</div>";
+}
+
$settings = <<<__HTML
<div class="elgg-content-thin mtm"><p>$instructions</p></div>
<div><label>$consumer_key_string</label><br /> $consumer_key_view</div>
<div><label>$consumer_secret_string</label><br /> $consumer_secret_view</div>
<div>$sign_on_with_twitter_string $sign_on_with_twitter_view</div>
<div>$new_users_with_twitter $new_users_with_twitter_view</div>
+$post_to_twitter
__HTML;
echo $settings;
diff --git a/pages/account/forgotten_password.php b/pages/account/forgotten_password.php
index bf6ef87e0..f464f98c9 100644
--- a/pages/account/forgotten_password.php
+++ b/pages/account/forgotten_password.php
@@ -17,6 +17,11 @@ $content .= elgg_view_form('user/requestnewpassword', array(
'class' => 'elgg-form-account',
));
-$body = elgg_view_layout("one_column", array('content' => $content));
-
-echo elgg_view_page($title, $body);
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/account/login.php b/pages/account/login.php
index 14f65cc3f..6aa3752d0 100644
--- a/pages/account/login.php
+++ b/pages/account/login.php
@@ -15,6 +15,14 @@ if (elgg_is_logged_in()) {
forward('');
}
-$login_box = elgg_view('core/account/login_box');
-$content = elgg_view_layout('one_column', array('content' => $login_box));
-echo elgg_view_page(elgg_echo('login'), $content);
+$title = elgg_echo('login');
+$content = elgg_view('core/account/login_box');
+
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/account/register.php b/pages/account/register.php
index cf18a635b..2fe8b74c0 100644
--- a/pages/account/register.php
+++ b/pages/account/register.php
@@ -48,6 +48,11 @@ $content .= elgg_view_form('register', $form_params, $body_params);
$content .= elgg_view('help/register');
-$body = elgg_view_layout("one_column", array('content' => $content));
-
-echo elgg_view_page($title, $body);
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/account/reset_password.php b/pages/account/reset_password.php
index 6515bfc5d..3ab8ccf3e 100644
--- a/pages/account/reset_password.php
+++ b/pages/account/reset_password.php
@@ -30,6 +30,11 @@ $form = elgg_view_form('user/passwordreset', array('class' => 'elgg-form-account
$title = elgg_echo('resetpassword');
$content = elgg_view_title(elgg_echo('resetpassword')) . $form;
-$body = elgg_view_layout('one_column', array('content' => $content));
-
-echo elgg_view_page($title, $body);
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/avatar/edit.php b/pages/avatar/edit.php
index c71633b8b..56aede887 100644
--- a/pages/avatar/edit.php
+++ b/pages/avatar/edit.php
@@ -11,6 +11,11 @@ elgg_set_context('profile_edit');
$title = elgg_echo('avatar:edit');
$entity = elgg_get_page_owner_entity();
+if (!elgg_instanceof($entity, 'user') || !$entity->canEdit()) {
+ register_error(elgg_echo('avatar:noaccess'));
+ forward(REFERER);
+}
+
$content = elgg_view('core/avatar/upload', array('entity' => $entity));
// only offer the crop view if an avatar has been uploaded
diff --git a/pages/river.php b/pages/river.php
index 0e1511334..801d9f664 100644
--- a/pages/river.php
+++ b/pages/river.php
@@ -49,6 +49,7 @@ $content = elgg_view('core/river/filter', array('selector' => $selector));
$sidebar = elgg_view('core/river/sidebar');
$params = array(
+ 'title' => $title,
'content' => $content . $activity,
'sidebar' => $sidebar,
'filter_context' => $page_filter,
diff --git a/upgrade.php b/upgrade.php
index c5f158c61..d07b2a1da 100644
--- a/upgrade.php
+++ b/upgrade.php
@@ -46,7 +46,7 @@ if (get_input('upgrade') == 'upgrade') {
} else {
// if upgrading from < 1.8.0, check for the core view 'welcome' and bail if it's found.
- // see http://trac.elgg.org/ticket/3064
+ // see https://github.com/elgg/elgg/issues/3064
// we're not checking the view itself because it's likely themes will override this view.
// we're only concerned with core files.
$welcome = dirname(__FILE__) . '/views/default/welcome.php';
diff --git a/version.php b/version.php
index b5822b371..a7a4776a4 100644
--- a/version.php
+++ b/version.php
@@ -11,7 +11,7 @@
// YYYYMMDD = Elgg Date
// XX = Interim incrementer
-$version = 2013030600;
+$version = 2014110100;
// Human-friendly version name
-$release = '1.8.14';
+$release = '1.8.18';
diff --git a/views/default/admin/settings/advanced/site_secret.php b/views/default/admin/settings/advanced/site_secret.php
new file mode 100644
index 000000000..e70ac7ab6
--- /dev/null
+++ b/views/default/admin/settings/advanced/site_secret.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Elgg administration site secret settings
+ *
+ * @package Elgg
+ * @subpackage Core
+ */
+
+echo elgg_view_form('admin/site/regenerate_secret', array(), array(
+ 'strength' => _elgg_get_site_secret_strength(),
+));
diff --git a/views/default/css/admin.php b/views/default/css/admin.php
index ceeac71a2..c435621b2 100644
--- a/views/default/css/admin.php
+++ b/views/default/css/admin.php
@@ -446,7 +446,8 @@ input {
.elgg-input-text,
.elgg-input-tags,
.elgg-input-url,
-.elgg-input-plaintext {
+.elgg-input-plaintext,
+.elgg-input-longtext {
width: 98%;
}
textarea {
@@ -1003,7 +1004,7 @@ a.elgg-button {
ENTITY MENU
*************************************** */
<?php // height depends on line height/font size ?>
-.elgg-menu-entity, elgg-menu-annotation {
+.elgg-menu-entity, .elgg-menu-annotation {
float: right;
margin-left: 15px;
font-size: 90%;
@@ -1543,6 +1544,26 @@ table.mceLayout {
}
/* ***************************************
+ SITE SECRET
+*************************************** */
+.elgg-form-admin-site-regenerate-secret table {
+ width: 60%;
+ margin: 1em auto;
+}
+td.elgg-strength-strong,
+td.elgg-strength-strong h4 {
+ background: #DFF0D8; color: #468847;
+}
+td.elgg-strength-moderate,
+td.elgg-strength-moderate h4 {
+ background: #FCF8E3; color: #C09853;
+}
+td.elgg-strength-weak,
+td.elgg-strength-weak h4 {
+ background: #F2DEDE; color: #B94A48;
+}
+
+/* ***************************************
HELPERS
*************************************** */
.hidden {
diff --git a/views/default/css/elements/forms.php b/views/default/css/elements/forms.php
index f55e57fb4..068cc8fd6 100644
--- a/views/default/css/elements/forms.php
+++ b/views/default/css/elements/forms.php
@@ -69,6 +69,7 @@ input[type="radio"] {
margin:0 3px 0 0;
padding:0;
border:none;
+ border-radius:0;
width:auto;
}
.elgg-input-checkboxes.elgg-horizontal li,
diff --git a/views/default/css/elements/navigation.php b/views/default/css/elements/navigation.php
index 62f370069..6b29e4c19 100644
--- a/views/default/css/elements/navigation.php
+++ b/views/default/css/elements/navigation.php
@@ -16,7 +16,7 @@
text-align: center;
}
.elgg-pagination li {
- display: inline;
+ display: inline-block;
margin: 0 6px 0 0;
text-align: center;
}
@@ -24,7 +24,8 @@
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-
+
+ display: block;
padding: 2px 6px;
color: #4690d6;
border: 1px solid #4690d6;
@@ -449,7 +450,7 @@
ENTITY AND ANNOTATION
*************************************** */
<?php // height depends on line height/font size ?>
-.elgg-menu-entity, elgg-menu-annotation {
+.elgg-menu-entity, .elgg-menu-annotation {
float: right;
margin-left: 15px;
font-size: 90%;
diff --git a/views/default/css/ie.php b/views/default/css/ie.php
index 4bddd4d55..34ececa89 100644
--- a/views/default/css/ie.php
+++ b/views/default/css/ie.php
@@ -6,3 +6,11 @@
.elgg-avatar {
display: block;
}
+
+/* ie8 adds space to the top of .elgg-gallery which causes jumpiness if this is display: block; */
+.elgg-gallery .elgg-avatar > a > img {
+ display: inline-block;
+}
+.elgg-gallery .elgg-avatar > .elgg-icon-hover-menu {
+ bottom: 4px;
+}
diff --git a/views/default/css/ie7.php b/views/default/css/ie7.php
index 229df8431..90274797d 100644
--- a/views/default/css/ie7.php
+++ b/views/default/css/ie7.php
@@ -24,6 +24,7 @@
.elgg-menu-footer > li > a,
.elgg-menu-footer li,
.elgg-menu-general > li > a,
+.elgg-pagination li,
.elgg-menu-general li {
display: inline;
}
diff --git a/views/default/forms/admin/site/regenerate_secret.php b/views/default/forms/admin/site/regenerate_secret.php
new file mode 100644
index 000000000..af269b801
--- /dev/null
+++ b/views/default/forms/admin/site/regenerate_secret.php
@@ -0,0 +1,24 @@
+<?php
+
+$strength = $vars['strength'];
+
+?>
+<p><?php echo elgg_echo('admin:site:secret:intro'); ?></p>
+
+<table class="elgg-table">
+ <tr>
+ <th><?php echo elgg_echo('site_secret:current_strength'); ?></th>
+ <td class="elgg-strength-<?php echo $strength; ?>">
+ <h4><?php echo elgg_echo("site_secret:strength:$strength"); ?></h4>
+ <div><?php echo elgg_echo("site_secret:strength_msg:$strength"); ?></div>
+ </td>
+ </tr>
+</table>
+
+<div class="elgg-foot">
+ <?php echo elgg_view('input/submit', array(
+ 'value' => elgg_echo('admin:site:secret:regenerate'),
+ 'class' => 'elgg-requires-confirmation elgg-button elgg-button-submit',
+ )); ?>
+ <p class="elgg-text-help mts"><?php echo elgg_echo('admin:site:secret:regenerate:help'); ?></p>
+</div>
diff --git a/views/default/forms/plugins/settings/save.php b/views/default/forms/plugins/settings/save.php
index dc7b2fef7..116529905 100644
--- a/views/default/forms/plugins/settings/save.php
+++ b/views/default/forms/plugins/settings/save.php
@@ -17,11 +17,11 @@ if ($type != 'user') {
$type = '';
}
-if (elgg_view_exists("{$type}settings/$plugin_id/edit")) {
+if (elgg_view_exists("plugins/$plugin_id/{$type}settings")) {
+ echo elgg_view("plugins/$plugin_id/{$type}settings", $vars);
+} elseif (elgg_view_exists("{$type}settings/$plugin_id/edit")) {
elgg_deprecated_notice("{$type}settings/$plugin_id/edit was deprecated in favor of plugins/$plugin_id/{$type}settings", 1.8);
echo elgg_view("{$type}settings/$plugin_id/edit", $vars);
-} else {
- echo elgg_view("plugins/$plugin_id/{$type}settings", $vars);
}
echo '<div class="elgg-foot">';
diff --git a/views/default/forms/profile/edit.php b/views/default/forms/profile/edit.php
index 9538b779e..cb0a37ca4 100644
--- a/views/default/forms/profile/edit.php
+++ b/views/default/forms/profile/edit.php
@@ -13,6 +13,8 @@
</div>
<?php
+$sticky_values = elgg_get_sticky_values('profile:edit');
+
$profile_fields = elgg_get_config('profile_fields');
if (is_array($profile_fields) && count($profile_fields) > 0) {
foreach ($profile_fields as $shortname => $valtype) {
@@ -40,6 +42,14 @@ if (is_array($profile_fields) && count($profile_fields) > 0) {
$access_id = ACCESS_DEFAULT;
}
+ // sticky form values take precedence over saved ones
+ if (isset($sticky_values[$shortname])) {
+ $value = $sticky_values[$shortname];
+ }
+ if (isset($sticky_values['accesslevel'][$shortname])) {
+ $access_id = $sticky_values['accesslevel'][$shortname];
+ }
+
?>
<div>
<label><?php echo elgg_echo("profile:{$shortname}") ?></label>
@@ -59,6 +69,9 @@ if (is_array($profile_fields) && count($profile_fields) > 0) {
<?php
}
}
+
+elgg_clear_sticky_form('profile:edit');
+
?>
<div class="elgg-foot">
<?php
diff --git a/views/default/input/userpicker.php b/views/default/input/userpicker.php
index 91a397e37..8b64d7df5 100644
--- a/views/default/input/userpicker.php
+++ b/views/default/input/userpicker.php
@@ -63,11 +63,13 @@ foreach ($vars['value'] as $user_id) {
?>
<div class="elgg-user-picker">
<input type="text" class="elgg-input-user-picker" size="30"/>
- <input type="checkbox" name="match_on" value="true" />
- <label><?php echo elgg_echo('userpicker:only_friends'); ?></label>
+ <label>
+ <input type="checkbox" name="match_on" value="true" />
+ <?php echo elgg_echo('userpicker:only_friends'); ?>
+ </label>
<ul class="elgg-user-picker-list"><?php echo $user_list; ?></ul>
</div>
<script type="text/javascript">
// @todo grab the values in the init function rather than using inline JS
elgg.userpicker.userList = <?php echo $json_values ?>;
-</script> \ No newline at end of file
+</script>
diff --git a/views/default/js/elgg.php b/views/default/js/elgg.php
index 6fe03484d..c3b56e398 100644
--- a/views/default/js/elgg.php
+++ b/views/default/js/elgg.php
@@ -43,7 +43,7 @@ $libs = array(
foreach ($libs as $file) {
include("{$CONFIG->path}js/lib/$file.js");
- // putting a new line between the files to address http://trac.elgg.org/ticket/3081
+ // putting a new line between the files to address https://github.com/elgg/elgg/issues/3081
echo "\n";
}
diff --git a/views/default/js/languages.php b/views/default/js/languages.php
index c51d7bcb2..fcf903d4b 100644
--- a/views/default/js/languages.php
+++ b/views/default/js/languages.php
@@ -1,15 +1,33 @@
<?php
/**
* @uses $vars['language']
+ * @uses $vars['lc'] if present, client will be sent long expires headers
*/
-global $CONFIG;
$language = $vars['language'];
+$lastcache = elgg_extract('lc', $vars, 0);
-$translations = $CONFIG->translations['en'];
+// @todo add server-side caching
+if ($lastcache) {
+ // we're relying on lastcache changes to predict language changes
+ $etag = '"' . md5("$language|$lastcache") . '"';
+
+ header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true);
+ header("Pragma: public", true);
+ header("Cache-Control: public", true);
+ header("ETag: $etag");
+
+ if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) {
+ header("HTTP/1.1 304 Not Modified");
+ exit;
+ }
+}
+
+$all_translations = elgg_get_config('translations');
+$translations = $all_translations['en'];
if ($language != 'en') {
- $translations = array_merge($translations, $CONFIG->translations[$language]);
+ $translations = array_merge($translations, $all_translations[$language]);
}
echo json_encode($translations); \ No newline at end of file
diff --git a/views/default/js/walled_garden.php b/views/default/js/walled_garden.php
index 7a482fe23..e228df507 100644
--- a/views/default/js/walled_garden.php
+++ b/views/default/js/walled_garden.php
@@ -5,12 +5,11 @@
* @since 1.8
*/
-// note that this assumes the button view is not using single quotes
$cancel_button = elgg_view('input/button', array(
'value' => elgg_echo('cancel'),
'class' => 'elgg-button-cancel mlm',
));
-$cancel_button = trim($cancel_button);
+$cancel_button = json_encode($cancel_button);
if (0) { ?><script><?php }
?>
@@ -23,10 +22,11 @@ elgg.walled_garden.init = function () {
$('.registration_link').click(elgg.walled_garden.load('register'));
$('input.elgg-button-cancel').live('click', function(event) {
- if ($('.elgg-walledgarden-single').is(':visible')) {
+ var $wgs = $('.elgg-walledgarden-single');
+ if ($wgs.is(':visible')) {
$('.elgg-walledgarden-double').fadeToggle();
- $('.elgg-walledgarden-single').fadeToggle();
- $('.elgg-walledgarden-single').remove();
+ $wgs.fadeToggle();
+ $wgs.remove();
}
event.preventDefault();
});
@@ -42,12 +42,22 @@ elgg.walled_garden.load = function(view) {
return function(event) {
var id = '#elgg-walledgarden-' + view;
id = id.replace('_', '-');
+ //@todo display some visual element that indicates that loading of content is running
elgg.get('walled_garden/' + view, {
'success' : function(data) {
- $('.elgg-body-walledgarden').append(data);
- $(id).find('input.elgg-button-submit').after('<?php echo $cancel_button; ?>');
- $('#elgg-walledgarden-login').fadeToggle();
- $(id).fadeToggle();
+ var $wg = $('.elgg-body-walledgarden');
+ $wg.append(data);
+ $(id).find('input.elgg-button-submit').after(<?php echo $cancel_button; ?>);
+
+ if (view == 'register' && $wg.hasClass('hidden')) {
+ // this was a failed register, display the register form ASAP
+ $('#elgg-walledgarden-login').toggle(false);
+ $(id).toggle();
+ $wg.removeClass('hidden');
+ } else {
+ $('#elgg-walledgarden-login').fadeToggle();
+ $(id).fadeToggle();
+ }
}
});
event.preventDefault();
diff --git a/views/default/object/default.php b/views/default/object/default.php
index 110648304..70e098742 100644
--- a/views/default/object/default.php
+++ b/views/default/object/default.php
@@ -41,7 +41,6 @@ $params = array(
'title' => $title,
'metadata' => $metadata,
'subtitle' => $subtitle,
- 'tags' => $vars['entity']->tags,
);
$params = $params + $vars;
$body = elgg_view('object/elements/summary', $params);
diff --git a/views/default/object/elements/full.php b/views/default/object/elements/full.php
index 9b89f9706..b4634fe7e 100644
--- a/views/default/object/elements/full.php
+++ b/views/default/object/elements/full.php
@@ -22,9 +22,9 @@ $summary = elgg_extract('summary', $vars);
$body = elgg_extract('body', $vars);
$class = elgg_extract('class', $vars);
if ($class) {
- $class = "elgg-content $class";
+ $class = "elgg-content clearfix $class";
} else {
- $class = "elgg-content";
+ $class = "elgg-content clearfix";
}
$header = elgg_view_image_block($icon, $summary);
diff --git a/views/default/object/elements/summary.php b/views/default/object/elements/summary.php
index c0f3ad340..63ab8f816 100644
--- a/views/default/object/elements/summary.php
+++ b/views/default/object/elements/summary.php
@@ -27,7 +27,7 @@ if ($title_link === '') {
$text = $entity->name;
}
$params = array(
- 'text' => $text,
+ 'text' => elgg_get_excerpt($text, 100),
'href' => $entity->getURL(),
'is_trusted' => true,
);
diff --git a/views/default/output/access.php b/views/default/output/access.php
index 91c5c721e..5c8d62c4d 100644
--- a/views/default/output/access.php
+++ b/views/default/output/access.php
@@ -11,7 +11,7 @@ if (isset($vars['entity']) && elgg_instanceof($vars['entity'])) {
$access_id = $vars['entity']->access_id;
$access_class = 'elgg-access';
$access_id_string = get_readable_access_level($access_id);
- $access_id_string = htmlentities($access_id_string, ENT_QUOTES, 'UTF-8');
+ $access_id_string = htmlspecialchars($access_id_string, ENT_QUOTES, 'UTF-8', false);
// if within a group or shared access collection display group name and open/closed membership status
// @todo have a better way to do this instead of checking against subtype / class.
diff --git a/views/default/output/tag.php b/views/default/output/tag.php
index 3e1f1c320..6bd9a72a7 100644
--- a/views/default/output/tag.php
+++ b/views/default/output/tag.php
@@ -8,25 +8,25 @@
*
*/
+if (!empty($vars['type'])) {
+ $type = "&type=" . rawurlencode($vars['type']);
+} else {
+ $type = "";
+}
if (!empty($vars['subtype'])) {
- $subtype = "&subtype=" . urlencode($vars['subtype']);
+ $subtype = "&subtype=" . rawurlencode($vars['subtype']);
} else {
$subtype = "";
}
if (!empty($vars['object'])) {
- $object = "&object=" . urlencode($vars['object']);
+ $object = "&object=" . rawurlencode($vars['object']);
} else {
$object = "";
}
if (isset($vars['value'])) {
+ $url = elgg_get_site_url() . 'search?q=' . rawurlencode($vars['value']) . "&search_type=tags{$type}{$subtype}{$object}";
$vars['value'] = htmlspecialchars($vars['value'], ENT_QUOTES, 'UTF-8', false);
- if (!empty($vars['type'])) {
- $type = "&type={$vars['type']}";
- } else {
- $type = "";
- }
- $url = elgg_get_site_url() . 'search?q=' . urlencode($vars['value']) . "&search_type=tags{$type}{$subtype}{$object}";
echo elgg_view('output/url', array(
'href' => $url,
'text' => $vars['value'],
diff --git a/views/default/output/tags.php b/views/default/output/tags.php
index 41fd5f168..db096a3be 100644
--- a/views/default/output/tags.php
+++ b/views/default/output/tags.php
@@ -17,13 +17,18 @@ if (isset($vars['entity'])) {
unset($vars['entity']);
}
+if (!empty($vars['type'])) {
+ $type = "&type=" . rawurlencode($vars['type']);
+} else {
+ $type = "";
+}
if (!empty($vars['subtype'])) {
- $subtype = "&subtype=" . urlencode($vars['subtype']);
+ $subtype = "&subtype=" . rawurlencode($vars['subtype']);
} else {
$subtype = "";
}
if (!empty($vars['object'])) {
- $object = "&object=" . urlencode($vars['object']);
+ $object = "&object=" . rawurlencode($vars['object']);
} else {
$object = "";
}
@@ -53,16 +58,11 @@ if (!empty($vars['tags'])) {
$icon_class = elgg_extract('icon_class', $vars);
$list_items = '<li>' . elgg_view_icon('tag', $icon_class) . '</li>';
-
+
foreach($vars['tags'] as $tag) {
- $tag = htmlspecialchars($tag, ENT_QUOTES, 'UTF-8', false);
- if (!empty($vars['type'])) {
- $type = "&type={$vars['type']}";
- } else {
- $type = "";
- }
- $url = elgg_get_site_url() . 'search?q=' . urlencode($tag) . "&search_type=tags{$type}{$subtype}{$object}";
+ $url = elgg_get_site_url() . 'search?q=' . rawurlencode($tag) . "&search_type=tags{$type}{$subtype}{$object}";
if (is_string($tag)) {
+ $tag = htmlspecialchars($tag, ENT_QUOTES, 'UTF-8', false);
$list_items .= "<li class=\"$item_class\">";
$list_items .= elgg_view('output/url', array('href' => $url, 'text' => $tag, 'rel' => 'tag'));
$list_items .= '</li>';
diff --git a/views/default/page/walled_garden.php b/views/default/page/walled_garden.php
index ff8e317c7..b280cf6b2 100644
--- a/views/default/page/walled_garden.php
+++ b/views/default/page/walled_garden.php
@@ -5,6 +5,12 @@
* Used for the walled garden index page
*/
+$is_sticky_register = elgg_is_sticky_form('register');
+$wg_body_class = 'elgg-body-walledgarden';
+if ($is_sticky_register) {
+ $wg_body_class .= ' hidden';
+}
+
// Set the content type
header("Content-type: text/html; charset=UTF-8");
?>
@@ -18,10 +24,17 @@ header("Content-type: text/html; charset=UTF-8");
<div class="elgg-page-messages">
<?php echo elgg_view('page/elements/messages', array('object' => $vars['sysmessages'])); ?>
</div>
- <div class="elgg-body-walledgarden">
+ <div class="<?php echo $wg_body_class; ?>">
<?php echo $vars['body']; ?>
</div>
</div>
+<?php if ($is_sticky_register): ?>
+<script type="text/javascript">
+elgg.register_hook_handler('init', 'system', function() {
+ $('.registration_link').trigger('click');
+});
+</script>
+<?php endif; ?>
<?php echo elgg_view('page/elements/foot'); ?>
</body>
</html> \ No newline at end of file
diff --git a/views/default/river/elements/summary.php b/views/default/river/elements/summary.php
index 416bc708b..d7bde51dd 100644
--- a/views/default/river/elements/summary.php
+++ b/views/default/river/elements/summary.php
@@ -18,9 +18,10 @@ $subject_link = elgg_view('output/url', array(
'is_trusted' => true,
));
+$object_text = $object->title ? $object->title : $object->name;
$object_link = elgg_view('output/url', array(
'href' => $object->getURL(),
- 'text' => $object->title ? $object->title : $object->name,
+ 'text' => elgg_get_excerpt($object_text, 100),
'class' => 'elgg-river-object',
'is_trusted' => true,
));