diff --git a/.gitignore b/.gitignore index db3c0fa..0b49190 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ tramp # Pycharm .idea +virtualenv_3_7/include/ diff --git a/AQL-Examples/artifacts-created-earlier-than.json b/AQL-Examples/artifacts-created-earlier-than.json deleted file mode 100644 index 9b18c1c..0000000 --- a/AQL-Examples/artifacts-created-earlier-than.json +++ /dev/null @@ -1,7 +0,0 @@ -items.find({ - -"$and": -[ -{"created" : {"$gte":"2015-05-14"}} -] -}).include("name","repo","modified").limit(100) \ No newline at end of file diff --git a/AQL-Examples/artifacts-modified-within-last.json b/AQL-Examples/artifacts-modified-within-last.json deleted file mode 100644 index 8de7b4b..0000000 --- a/AQL-Examples/artifacts-modified-within-last.json +++ /dev/null @@ -1,2 +0,0 @@ -items.find({"modified" : {"$last" : "60minutes"}}) - /* hello */ \ No newline at end of file diff --git a/AQL-Examples/artifacts-with-property.json b/AQL-Examples/artifacts-with-property.json deleted file mode 100644 index 8543a38..0000000 --- a/AQL-Examples/artifacts-with-property.json +++ /dev/null @@ -1 +0,0 @@ -items.find({"@property": {"$eq" : "value"}}) \ No newline at end of file diff --git a/AQL-Examples/cleanup.py b/AQL-Examples/cleanup.py new file mode 100644 index 0000000..58153c3 --- /dev/null +++ b/AQL-Examples/cleanup.py @@ -0,0 +1,34 @@ +import requests + +def deep_clean(): + + base_url = 'http://localhost:8081/artifactory/' + base_name_to_search = "Test-SNAPSHOT-*.ear" + + headers = {'content-type': 'text/plain'} + data = 'items.find({"name":{"$match":"%s"}})' % base_name_to_search + limit_number = 4 + response = requests.post(base_url+'api/search/aql', auth=('admin', 'NewPassword'), headers=headers, data=data) + + + for result in eval(response.text)["results"]: + + name = result['name'] + path = result['path'] + '/' + repo = result['repo'] + if path == './': + path = '' + + full_path = repo + '/' + path + name + + artifact_url = base_url + full_path + version = name.split('-')[-1].split('.')[0] + + if int(version) < limit_number: + print(full_path) + print(artifact_url) + requests.delete(artifact_url, auth=('admin', 'NewPassword')) + +if __name__ == '__main__': + deep_clean() + diff --git a/ArtifactoryLogParser/README.md b/ArtifactoryLogParser/README.md index ca27104..80af8d9 100644 --- a/ArtifactoryLogParser/README.md +++ b/ArtifactoryLogParser/README.md @@ -1,8 +1,28 @@ # Artifactory Log Parser -Uses regex to parse Artifactory logs +A script that allows to parse Artifactory logs and output them in a +readable and organized way. + + +## Installation and Configuration +No external dependencies are necessary.
+Code has been tested and optimized for Pyhton 2.7 or Python 3.6 running +on Unix systems. Coloring might not work on Windows machines. + + +## Usage +To run the script you must simply pass the file path along with options +and search filters. The filters\options available are the following:
+`WARN`, `ERROR`, `INFO`, `nocolor`
+ Example commands: -python artifactory.py $artifactory.log -python access.py $access.log -python request.py $request.log +`python artifactory.py /path/to/artifactory.log WARN`
+`python artifactory.py /path/to/artifactory.log ERROR WARN`
+`python artifactory.py /path/to/artifactory.log ERROR nocolor`
+ +Under construction (only functional with Python 2.7. +No advanced options): + +`python access.py /path/to/access.log`
+`python request.py /path/to/request.log`
diff --git a/ArtifactoryLogParser/artifactory.py b/ArtifactoryLogParser/artifactory.py
index 6b8ff27..8b6b44d 100644
--- a/ArtifactoryLogParser/artifactory.py
+++ b/ArtifactoryLogParser/artifactory.py
@@ -1,50 +1,103 @@
#!/usr/bin/python
import re
import sys
-import colorama
-from colorama import Fore, Back, Style
-
-if len(sys.argv) !=2:
- print('Usage: python artifactory.py artifactory.log')
- sys.exit(0)
-
-
-readfile = sys.argv[1]
-i = 0
-with open(readfile) as f:
- for line in f:
- if "WARN" in line:
- try:
- regex = '^([-0-9]+ [:0-9]+,[0-9]+) \[([-a-zA-Z0-9]+)\] \[([A-Z]+) *\] \(([.a-zA-Z0-9]+):([0-9]+)\) - (.*)$'
- match = re.search(regex,line.encode('ascii'),flags = 0)
- print Fore.RED + "WARNING:" + (match.group(6))
-
- except Exception:
- pass
-
-with open(readfile) as f:
- for line in f:
- if "ERROR" in line:
- try:
- regex = '^([-0-9]+ [:0-9]+,[0-9]+) \[([-a-zA-Z0-9]+)\] \[([A-Z]+) *\] \(([.a-zA-Z0-9]+):([0-9]+)\) - (.*)$'
- match = re.search(regex,line.encode('ascii'),flags = 0)
- print Fore.YELLOW + "ERROR:" + (match.group(6))
-
- except Exception:
- pass
-
-with open(readfile) as f:
- for line in f:
- if "INFO" in line:
- try:
- regex = '^([-0-9]+ [:0-9]+,[0-9]+) \[([-a-zA-Z0-9]+)\] \[([A-Z]+) *\] \(([.a-zA-Z0-9]+):([0-9]+)\) - (.*)$'
- match = re.search(regex,line.encode('ascii'),flags = 0)
- print Fore.GREEN + "INFO:" + (match.group(6))
-
- except Exception:
- pass
-
-print (Style.RESET_ALL)
-exit()
-
-
\ No newline at end of file
+
+
+class Colors:
+
+ def __init__(self):
+ self.HEADER = '\033[95m'
+ self.OKBLUE = '\033[94m'
+ self.OKGREEN = '\033[92m'
+ self.WARNING = '\033[93m'
+ self.FAIL = '\033[91m'
+ self.ENDC = '\033[0m'
+
+ def disable(self):
+ self.HEADER = ''
+ self.OKBLUE = ''
+ self.OKGREEN = ''
+ self.WARNING = ''
+ self.FAIL = ''
+
+ def clear(self):
+ print(self.ENDC)
+
+
+class Logger:
+
+ default_sf = ["WARN", "ERROR", "INFO"]
+
+ def __init__(self, readfile, regex):
+ self.colors = Colors()
+ self.data = {
+ 'WARN':
+ {'results': [],
+ 'color': self.colors.WARNING,
+ 'message': 'WARNING:'},
+ 'ERROR':
+ {'results': [],
+ 'color': self.colors.FAIL,
+ 'message': 'ERROR:'},
+ 'INFO':
+ {'results': [],
+ 'color': self.colors.OKBLUE,
+ 'message': 'INFO:'
+ }
+ }
+ self.readfile = readfile
+ self.regex = regex
+
+ def parse(self):
+ with open(self.readfile) as f:
+ for line in f.readlines():
+ match = re.search(self.regex, line, flags=0)
+ if match:
+ e_type = [x for x in self.default_sf if x in line][0]
+ entry = ' '.join([self.data[e_type]['color'], self.data[e_type]['message'], (match.group(6))])
+ self.data[e_type]['results'].append(entry)
+
+ def output(self, sf):
+ for entry_type in self.data:
+ if entry_type in sf:
+ for result in self.data[entry_type]["results"]:
+ print(result)
+
+ def disable_color(self):
+ for entry_type in self.data:
+ self.data[entry_type]["color"] = ''
+
+
+def main():
+
+ # Gather input values
+ readfile = sys.argv[1]
+ regex = '^([-0-9]+ [:0-9]+,[0-9]+) \[([-a-zA-Z0-9]+)\] \[([A-Z]+) *\] \(([.a-zA-Z0-9]+):([0-9]+)\) - (.*)$'
+ logger = Logger(readfile, regex)
+ n_args = len(sys.argv)
+
+ if n_args < 2:
+ print('Usage: python artifactory.py /path/to/artifactory.log [filters]')
+ sys.exit(0)
+
+ elif n_args == 2:
+ search_filter = logger.default_sf
+ else:
+ search_filter = sys.argv[2:]
+
+ if "nocolor" in search_filter:
+ logger.disable_color()
+
+ logger.parse()
+ logger.output(sf=search_filter)
+ logger.colors.clear()
+ exit()
+
+
+if __name__ == '__main__':
+ main()
+
+
+
+
+
diff --git a/ArtifactoryLogParser/request.py b/ArtifactoryLogParser/request.py
index 28a3cc7..2501c57 100644
--- a/ArtifactoryLogParser/request.py
+++ b/ArtifactoryLogParser/request.py
@@ -13,9 +13,9 @@
with open(readfile) as f:
for line in f:
try:
- p = re.compile(ur'(\d*)\|(\d*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)')
+ p = re.compile(r'(\d*)\|(\d*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)\|(.*)')
match = re.match(p,line)
- print match.group(4) + " - " + match.group(6) + " - " + match.group(9)
+ print(match.group(4) + " - " + match.group(6) + " - " + match.group(9))
except Exception:
pass
-exit()
\ No newline at end of file
+exit()
diff --git a/DownloadUsage/DownloadUsage.py b/DownloadUsage/DownloadUsage.py
index e27fa2e..cead21b 100644
--- a/DownloadUsage/DownloadUsage.py
+++ b/DownloadUsage/DownloadUsage.py
@@ -39,4 +39,4 @@
print Fore.RED + "Could not find",(notfound),"artifacts(most likely deleted)"
print Fore.GREEN + "Total download usage:",(totalbytes/1000000),"MB"
print (Style.RESET_ALL)
-exit()
\ No newline at end of file
+exit()
diff --git a/REST-API-Examples/node-get.js b/REST-API-Examples/NodeExamples/node-get.js
similarity index 100%
rename from REST-API-Examples/node-get.js
rename to REST-API-Examples/NodeExamples/node-get.js
diff --git a/REST-API-Examples/node-post.js b/REST-API-Examples/NodeExamples/node-post.js
similarity index 100%
rename from REST-API-Examples/node-post.js
rename to REST-API-Examples/NodeExamples/node-post.js
diff --git a/REST-API-Examples/php-get.php b/REST-API-Examples/NodeExamples/php-get.php
similarity index 100%
rename from REST-API-Examples/php-get.php
rename to REST-API-Examples/NodeExamples/php-get.php
diff --git a/REST-API-Examples/python-get.py b/REST-API-Examples/PythonExamples/python-get.py
similarity index 57%
rename from REST-API-Examples/python-get.py
rename to REST-API-Examples/PythonExamples/python-get.py
index c001cf8..667bc80 100644
--- a/REST-API-Examples/python-get.py
+++ b/REST-API-Examples/PythonExamples/python-get.py
@@ -5,17 +5,17 @@
username = "admin"
password = "password"
artifactory = "http://127.0.0.1:8081/artifactory/" #artifactory URL
-api = "api/security/users/admin" #you can change this API URL to any API method you'd like to use
+api = "api/security/users/admin" # you can change this API URL to any API method you'd like to use
url = artifactory + api
r = requests.get(url, auth = (username, password)) #this script is only for API methods that use GET
if r.status_code == 200:
- print r.content
+ print(r.content)
else:
- print "Fail"
+ print("Fail")
response = json.loads(r.content)
- print response["errors"]
+ print(response["errors"])
- print "x-request-id : " + r.headers['x-request-id']
- print "Status Code : " + r.status_code
\ No newline at end of file
+ print("x-request-id : " + r.headers['x-request-id'])
+ print("Status Code : " + r.status_code)
diff --git a/REST-API-Examples/python-post.py b/REST-API-Examples/PythonExamples/python-post.py
similarity index 100%
rename from REST-API-Examples/python-post.py
rename to REST-API-Examples/PythonExamples/python-post.py
diff --git a/REST-API-Examples/PythonRestClient/README.md b/REST-API-Examples/PythonRestClient/README.md
new file mode 100644
index 0000000..7002f39
--- /dev/null
+++ b/REST-API-Examples/PythonRestClient/README.md
@@ -0,0 +1,37 @@
+artifactopy - Artiactory's Python REST Client
+=============================================
+
+Artifactopy allows for quick use of the Artifactory REST API for the automation
+of get, put, post, delete, and patch operations.
+
+The library has been tested on and is compatible with Python 3.6 and above.
+
+Structure
+---------
+
+The library has a structure that reflects each operation's usage and request type.
+- `artifactopy.api`: (All API requests as classes)
+ - `artifactopy.api.get`: (all GET requests)
+ - `artifactopy.api.put`: (all PUT requests)
+ - `artifactopy.api.post`: (all POST requests)
+ - `artifactopy.api.patch`: (all PATCH requests)
+ - `artifactopy.api.delete`: (all DELETE requests)
+- `artifactopy.auth`: (Authentication classes)
+- `artifactopy.models`: (JSON classes)
+
+Examples
+--------------------------------------------------------------------------------------
+
+`test test`
+
+*The -download_missingfiles flag can take `yes or no` as a value and when you pass `yes` the script will download all the files that are present in Source repository and missing from the Target repository to a folder 'replication_downloads' in the current working directory*
+
+**If you don't want to provide parameters for the script, then you could just run the script without any of the above options and it will prompt you for all the details of the source Artifactory instance and target Artifactory instance.**
+
+**NOTE:**
+----------
+*After a successful run of the script it will provide you with the count of files sorted according to the file extension that are present in the source repository and are missing in the target repository.*
+
+*In the current working directory you will find a text file named **"filepaths_uri.txt"** which contains all the artifacts including metadata files that are present in the source repository and missing in the target repository. It will include the entire URL of the source Artifactory and the path to the artifact.*
+
+*You will also find another text file named **"filepaths_nometadatafiles.txt"** which contains only the artifacts and not metadata files that are present in the source repository and missing in the target repository. Since metadata files are not necessary as the target repository is responsible for calculating metadata, we have filtered them to only provide the missing artifacts.*
diff --git a/REST-API-Examples/PythonRestClient/artifactopy/Artifactory/__init__.py b/REST-API-Examples/PythonRestClient/artifactopy/Artifactory/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/REST-API-Examples/PythonRestClient/artifactopy/__init__.py b/REST-API-Examples/PythonRestClient/artifactopy/__init__.py
new file mode 100644
index 0000000..77b24a9
--- /dev/null
+++ b/REST-API-Examples/PythonRestClient/artifactopy/__init__.py
@@ -0,0 +1,13 @@
+# Add the b directory to your sys.path
+
+import sys, os
+parent_dir = os.getcwd() # find the path to module a
+# Then go up one level to the common parent directory
+path = os.path.dirname(parent_dir)
+# Add the parent to sys.pah
+sys.path.append(parent_dir)
+
+# Now you can import anything from b.s
+import api
+
+print(api.get)
diff --git a/REST-API-Examples/PythonRestClient/artifactopy/__pycache__/__init__.cpython-37.pyc b/REST-API-Examples/PythonRestClient/artifactopy/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..837458e
Binary files /dev/null and b/REST-API-Examples/PythonRestClient/artifactopy/__pycache__/__init__.cpython-37.pyc differ
diff --git a/REST-API-Examples/PythonRestClient/artifactopy/api/DONE b/REST-API-Examples/PythonRestClient/artifactopy/api/DONE
new file mode 100644
index 0000000..0fa120c
--- /dev/null
+++ b/REST-API-Examples/PythonRestClient/artifactopy/api/DONE
@@ -0,0 +1,70 @@
+===== GET =====
+GET /api/build
+GET /api/build/{buildName}
+GET /api/build/{buildName}/{buildNumber}
+GET /api/build/{buildName}/{buildNumber}?diff={OlderbuildNumber}
+-----
+GET /api/storage/{repoKey}/{folder-path}
+GET /api/storage/{repoKey}/{filePath}
+GET /api/storageinfo
+GET /api/storage/{repoKey}/{item-path}?lastModified
+GET /api/storage/{repoKey}/{item-path}?stats
+GET /api/storage/{repoKey}/{itemPath}?properties[=x[,y]]
+-----
+GET /api/replications/{repoKey}
+GET /api/replication/{repoKey}
+GET /api/replications/channels/{repo}
+-----
+GET /api/security/users
+GET /api/security/users/{userName}
+GET /api/security/encryptedPassword
+GET /api/security/configuration/passwordExpirationPolicy
+GET /api/security/userLockPolicy
+GET /api/security/lockedUsers
+GET /api/security/apiKey
+GET /api/security/groups
+GET /api/security/groups/{groupName}
+GET /api/security/permissions
+GET /api/security/permissions/{permissionTargetName}
+GET /api/security/token
+-----
+GET /api/system/security
+GET /api/system/service_id
+GET /api/system/security/certificates
+GET /api/system
+GET /api/system/ping
+GET /api/system/configuration
+GET /api/system/licenses
+GET /api/system/version
+GET /api/system/configuration/webServer
+GET /api/system/configuration/reverseProxy/nginx
+GET /api/system/replications
+-----
+GET /api/search/artifact?name=name[&repos=x[,y]]
+GET /api/search/archive?name=[archiveEntryName][&repos=x[,y]]
+GET /api/search/gavc?[g=groupId][&a=artifactId][&v=version][&c=classifier][&repos=x[,y]]
+GET /api/search/prop?[p1=v1,v2][&p2=v3][&repos=x[,y]]
+GET /api/search/checksum?md5=md5sum?sha1=sha1sum?sha256=sha256sum[&repos=x[,y]]
+GET /api/search/badChecksum?type=md5|sha1[&repos=x[,y]]
+GET /api/search/usage?notUsedSince=javaEpochMillis[&createdBefore=javaEpochMillis][&repos=x[,y]]
+GET /api/search/creation?from=javaEpochMillis[&to=javaEpochMillis][&repos=x[,y]]
+GET /api/search/pattern?pattern=repo-key:this/is/a/*pattern*.war
+GET /api/search/dependency?sha1=sha1Checksum
+GET /api/search/license[?unapproved=1][&unknown=1][¬found=0][&neutral=0][&approved=0][&autofind=0][&repos=x[,y]]
+GET /api/search/versions?[g=groupId][&a=artifactId][&v=version][&remote=0/1][&repos=x[,y]]
+GET /api/search/latestVersion?[g=groupId][&a=artifactId][&v=version][&remote=1][&repos=x[,y]]
+GET /api/search/dates?[from=fromVal][&to=toVal][&repos=x[,y]][&dateFields=c[,d]]
+
+==========
+
+===== PATCH =====
+==========
+
+===== DELETE =====
+==========
+
+===== DELETE =====
+==========
+
+===== DELETE =====
+==========
diff --git a/REST-API-Examples/PythonRestClient/artifactopy/api/TODO b/REST-API-Examples/PythonRestClient/artifactopy/api/TODO
new file mode 100644
index 0000000..3fe76a6
--- /dev/null
+++ b/REST-API-Examples/PythonRestClient/artifactopy/api/TODO
@@ -0,0 +1,194 @@
+DOWNLOADS
+------
+GET /repo-key/path/to/artifact.ext
+GET /repo-key/path/to/artifact.ext?trace
+GET /repo-key/path/to/artifact.jar!/path/to/archived/resource (NOTE! the '!' should be in between the archive file name and the archive entry path, and will not work without the '/' after the '!'.)
+---
+GET /api/archive/download/{repoKey}/{path}?archiveType={archiveType}[&includeChecksumFiles=true]
+GET /api/download/{repoKey}/{filePath}[?content=none/progress][&mark=numOfBytesToPrintANewProgressMark]
+-----
+GET /api/storage/{repoKey}/{folder-path}?list[&deep=0/1][&depth=n][&listFolders=0/1][&mdTimestamps=0/1][&includeRootPath=0/1]
+GET /api/storage/{repoKey}/{itemPath}?permissions
+==========
+OTHER GET
+-----
+GET /api/compliance/{repoKey}/{item-path}
+GET /api/sync/{remoteRepositoryKey}/{folderPath}[?progress=showProgress][&mark=numOfBytesToPrintANewProgressMark][&delete=deleteExistingFiles][&overwrite=never/force][&timeout=fileListTimeoutInMillis]
+GET /api/tasks
+GET /api/versions/{repo}/{path}?[listFiles=0/1]&[ This is a doc This is a docoptgroup
+ # element, or if there is no more content in the parent
+ # element.
+ if type == "StartTag":
+ return next["name"] in ('option', 'optgroup')
+ else:
+ return type == "EndTag" or type is None
+ elif tagname in ('rt', 'rp'):
+ # An rt element's end tag may be omitted if the rt element is
+ # immediately followed by an rt or rp element, or if there is
+ # no more content in the parent element.
+ # An rp element's end tag may be omitted if the rp element is
+ # immediately followed by an rt or rp element, or if there is
+ # no more content in the parent element.
+ if type == "StartTag":
+ return next["name"] in ('rt', 'rp')
+ else:
+ return type == "EndTag" or type is None
+ elif tagname == 'colgroup':
+ # A colgroup element's end tag may be omitted if the colgroup
+ # element is not immediately followed by a space character or
+ # a comment.
+ if type in ("Comment", "SpaceCharacters"):
+ return False
+ elif type == "StartTag":
+ # XXX: we also look for an immediately following colgroup
+ # element. See is_optional_start.
+ return next["name"] != 'colgroup'
+ else:
+ return True
+ elif tagname in ('thead', 'tbody'):
+ # A thead element's end tag may be omitted if the thead element
+ # is immediately followed by a tbody or tfoot element.
+ # A tbody element's end tag may be omitted if the tbody element
+ # is immediately followed by a tbody or tfoot element, or if
+ # there is no more content in the parent element.
+ # A tfoot element's end tag may be omitted if the tfoot element
+ # is immediately followed by a tbody element, or if there is no
+ # more content in the parent element.
+ # XXX: we never omit the end tag when the following element is
+ # a tbody. See is_optional_start.
+ if type == "StartTag":
+ return next["name"] in ['tbody', 'tfoot']
+ elif tagname == 'tbody':
+ return type == "EndTag" or type is None
+ else:
+ return False
+ elif tagname == 'tfoot':
+ # A tfoot element's end tag may be omitted if the tfoot element
+ # is immediately followed by a tbody element, or if there is no
+ # more content in the parent element.
+ # XXX: we never omit the end tag when the following element is
+ # a tbody. See is_optional_start.
+ if type == "StartTag":
+ return next["name"] == 'tbody'
+ else:
+ return type == "EndTag" or type is None
+ elif tagname in ('td', 'th'):
+ # A td element's end tag may be omitted if the td element is
+ # immediately followed by a td or th element, or if there is
+ # no more content in the parent element.
+ # A th element's end tag may be omitted if the th element is
+ # immediately followed by a td or th element, or if there is
+ # no more content in the parent element.
+ if type == "StartTag":
+ return next["name"] in ('td', 'th')
+ else:
+ return type == "EndTag" or type is None
+ return False
diff --git a/virtualenv_3_7/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/html5lib/filters/sanitizer.py b/virtualenv_3_7/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/html5lib/filters/sanitizer.py
new file mode 100644
index 0000000..c3199a5
--- /dev/null
+++ b/virtualenv_3_7/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/html5lib/filters/sanitizer.py
@@ -0,0 +1,896 @@
+from __future__ import absolute_import, division, unicode_literals
+
+import re
+from xml.sax.saxutils import escape, unescape
+
+from pip._vendor.six.moves import urllib_parse as urlparse
+
+from . import base
+from ..constants import namespaces, prefixes
+
+__all__ = ["Filter"]
+
+
+allowed_elements = frozenset((
+ (namespaces['html'], 'a'),
+ (namespaces['html'], 'abbr'),
+ (namespaces['html'], 'acronym'),
+ (namespaces['html'], 'address'),
+ (namespaces['html'], 'area'),
+ (namespaces['html'], 'article'),
+ (namespaces['html'], 'aside'),
+ (namespaces['html'], 'audio'),
+ (namespaces['html'], 'b'),
+ (namespaces['html'], 'big'),
+ (namespaces['html'], 'blockquote'),
+ (namespaces['html'], 'br'),
+ (namespaces['html'], 'button'),
+ (namespaces['html'], 'canvas'),
+ (namespaces['html'], 'caption'),
+ (namespaces['html'], 'center'),
+ (namespaces['html'], 'cite'),
+ (namespaces['html'], 'code'),
+ (namespaces['html'], 'col'),
+ (namespaces['html'], 'colgroup'),
+ (namespaces['html'], 'command'),
+ (namespaces['html'], 'datagrid'),
+ (namespaces['html'], 'datalist'),
+ (namespaces['html'], 'dd'),
+ (namespaces['html'], 'del'),
+ (namespaces['html'], 'details'),
+ (namespaces['html'], 'dfn'),
+ (namespaces['html'], 'dialog'),
+ (namespaces['html'], 'dir'),
+ (namespaces['html'], 'div'),
+ (namespaces['html'], 'dl'),
+ (namespaces['html'], 'dt'),
+ (namespaces['html'], 'em'),
+ (namespaces['html'], 'event-source'),
+ (namespaces['html'], 'fieldset'),
+ (namespaces['html'], 'figcaption'),
+ (namespaces['html'], 'figure'),
+ (namespaces['html'], 'footer'),
+ (namespaces['html'], 'font'),
+ (namespaces['html'], 'form'),
+ (namespaces['html'], 'header'),
+ (namespaces['html'], 'h1'),
+ (namespaces['html'], 'h2'),
+ (namespaces['html'], 'h3'),
+ (namespaces['html'], 'h4'),
+ (namespaces['html'], 'h5'),
+ (namespaces['html'], 'h6'),
+ (namespaces['html'], 'hr'),
+ (namespaces['html'], 'i'),
+ (namespaces['html'], 'img'),
+ (namespaces['html'], 'input'),
+ (namespaces['html'], 'ins'),
+ (namespaces['html'], 'keygen'),
+ (namespaces['html'], 'kbd'),
+ (namespaces['html'], 'label'),
+ (namespaces['html'], 'legend'),
+ (namespaces['html'], 'li'),
+ (namespaces['html'], 'm'),
+ (namespaces['html'], 'map'),
+ (namespaces['html'], 'menu'),
+ (namespaces['html'], 'meter'),
+ (namespaces['html'], 'multicol'),
+ (namespaces['html'], 'nav'),
+ (namespaces['html'], 'nextid'),
+ (namespaces['html'], 'ol'),
+ (namespaces['html'], 'output'),
+ (namespaces['html'], 'optgroup'),
+ (namespaces['html'], 'option'),
+ (namespaces['html'], 'p'),
+ (namespaces['html'], 'pre'),
+ (namespaces['html'], 'progress'),
+ (namespaces['html'], 'q'),
+ (namespaces['html'], 's'),
+ (namespaces['html'], 'samp'),
+ (namespaces['html'], 'section'),
+ (namespaces['html'], 'select'),
+ (namespaces['html'], 'small'),
+ (namespaces['html'], 'sound'),
+ (namespaces['html'], 'source'),
+ (namespaces['html'], 'spacer'),
+ (namespaces['html'], 'span'),
+ (namespaces['html'], 'strike'),
+ (namespaces['html'], 'strong'),
+ (namespaces['html'], 'sub'),
+ (namespaces['html'], 'sup'),
+ (namespaces['html'], 'table'),
+ (namespaces['html'], 'tbody'),
+ (namespaces['html'], 'td'),
+ (namespaces['html'], 'textarea'),
+ (namespaces['html'], 'time'),
+ (namespaces['html'], 'tfoot'),
+ (namespaces['html'], 'th'),
+ (namespaces['html'], 'thead'),
+ (namespaces['html'], 'tr'),
+ (namespaces['html'], 'tt'),
+ (namespaces['html'], 'u'),
+ (namespaces['html'], 'ul'),
+ (namespaces['html'], 'var'),
+ (namespaces['html'], 'video'),
+ (namespaces['mathml'], 'maction'),
+ (namespaces['mathml'], 'math'),
+ (namespaces['mathml'], 'merror'),
+ (namespaces['mathml'], 'mfrac'),
+ (namespaces['mathml'], 'mi'),
+ (namespaces['mathml'], 'mmultiscripts'),
+ (namespaces['mathml'], 'mn'),
+ (namespaces['mathml'], 'mo'),
+ (namespaces['mathml'], 'mover'),
+ (namespaces['mathml'], 'mpadded'),
+ (namespaces['mathml'], 'mphantom'),
+ (namespaces['mathml'], 'mprescripts'),
+ (namespaces['mathml'], 'mroot'),
+ (namespaces['mathml'], 'mrow'),
+ (namespaces['mathml'], 'mspace'),
+ (namespaces['mathml'], 'msqrt'),
+ (namespaces['mathml'], 'mstyle'),
+ (namespaces['mathml'], 'msub'),
+ (namespaces['mathml'], 'msubsup'),
+ (namespaces['mathml'], 'msup'),
+ (namespaces['mathml'], 'mtable'),
+ (namespaces['mathml'], 'mtd'),
+ (namespaces['mathml'], 'mtext'),
+ (namespaces['mathml'], 'mtr'),
+ (namespaces['mathml'], 'munder'),
+ (namespaces['mathml'], 'munderover'),
+ (namespaces['mathml'], 'none'),
+ (namespaces['svg'], 'a'),
+ (namespaces['svg'], 'animate'),
+ (namespaces['svg'], 'animateColor'),
+ (namespaces['svg'], 'animateMotion'),
+ (namespaces['svg'], 'animateTransform'),
+ (namespaces['svg'], 'clipPath'),
+ (namespaces['svg'], 'circle'),
+ (namespaces['svg'], 'defs'),
+ (namespaces['svg'], 'desc'),
+ (namespaces['svg'], 'ellipse'),
+ (namespaces['svg'], 'font-face'),
+ (namespaces['svg'], 'font-face-name'),
+ (namespaces['svg'], 'font-face-src'),
+ (namespaces['svg'], 'g'),
+ (namespaces['svg'], 'glyph'),
+ (namespaces['svg'], 'hkern'),
+ (namespaces['svg'], 'linearGradient'),
+ (namespaces['svg'], 'line'),
+ (namespaces['svg'], 'marker'),
+ (namespaces['svg'], 'metadata'),
+ (namespaces['svg'], 'missing-glyph'),
+ (namespaces['svg'], 'mpath'),
+ (namespaces['svg'], 'path'),
+ (namespaces['svg'], 'polygon'),
+ (namespaces['svg'], 'polyline'),
+ (namespaces['svg'], 'radialGradient'),
+ (namespaces['svg'], 'rect'),
+ (namespaces['svg'], 'set'),
+ (namespaces['svg'], 'stop'),
+ (namespaces['svg'], 'svg'),
+ (namespaces['svg'], 'switch'),
+ (namespaces['svg'], 'text'),
+ (namespaces['svg'], 'title'),
+ (namespaces['svg'], 'tspan'),
+ (namespaces['svg'], 'use'),
+))
+
+allowed_attributes = frozenset((
+ # HTML attributes
+ (None, 'abbr'),
+ (None, 'accept'),
+ (None, 'accept-charset'),
+ (None, 'accesskey'),
+ (None, 'action'),
+ (None, 'align'),
+ (None, 'alt'),
+ (None, 'autocomplete'),
+ (None, 'autofocus'),
+ (None, 'axis'),
+ (None, 'background'),
+ (None, 'balance'),
+ (None, 'bgcolor'),
+ (None, 'bgproperties'),
+ (None, 'border'),
+ (None, 'bordercolor'),
+ (None, 'bordercolordark'),
+ (None, 'bordercolorlight'),
+ (None, 'bottompadding'),
+ (None, 'cellpadding'),
+ (None, 'cellspacing'),
+ (None, 'ch'),
+ (None, 'challenge'),
+ (None, 'char'),
+ (None, 'charoff'),
+ (None, 'choff'),
+ (None, 'charset'),
+ (None, 'checked'),
+ (None, 'cite'),
+ (None, 'class'),
+ (None, 'clear'),
+ (None, 'color'),
+ (None, 'cols'),
+ (None, 'colspan'),
+ (None, 'compact'),
+ (None, 'contenteditable'),
+ (None, 'controls'),
+ (None, 'coords'),
+ (None, 'data'),
+ (None, 'datafld'),
+ (None, 'datapagesize'),
+ (None, 'datasrc'),
+ (None, 'datetime'),
+ (None, 'default'),
+ (None, 'delay'),
+ (None, 'dir'),
+ (None, 'disabled'),
+ (None, 'draggable'),
+ (None, 'dynsrc'),
+ (None, 'enctype'),
+ (None, 'end'),
+ (None, 'face'),
+ (None, 'for'),
+ (None, 'form'),
+ (None, 'frame'),
+ (None, 'galleryimg'),
+ (None, 'gutter'),
+ (None, 'headers'),
+ (None, 'height'),
+ (None, 'hidefocus'),
+ (None, 'hidden'),
+ (None, 'high'),
+ (None, 'href'),
+ (None, 'hreflang'),
+ (None, 'hspace'),
+ (None, 'icon'),
+ (None, 'id'),
+ (None, 'inputmode'),
+ (None, 'ismap'),
+ (None, 'keytype'),
+ (None, 'label'),
+ (None, 'leftspacing'),
+ (None, 'lang'),
+ (None, 'list'),
+ (None, 'longdesc'),
+ (None, 'loop'),
+ (None, 'loopcount'),
+ (None, 'loopend'),
+ (None, 'loopstart'),
+ (None, 'low'),
+ (None, 'lowsrc'),
+ (None, 'max'),
+ (None, 'maxlength'),
+ (None, 'media'),
+ (None, 'method'),
+ (None, 'min'),
+ (None, 'multiple'),
+ (None, 'name'),
+ (None, 'nohref'),
+ (None, 'noshade'),
+ (None, 'nowrap'),
+ (None, 'open'),
+ (None, 'optimum'),
+ (None, 'pattern'),
+ (None, 'ping'),
+ (None, 'point-size'),
+ (None, 'poster'),
+ (None, 'pqg'),
+ (None, 'preload'),
+ (None, 'prompt'),
+ (None, 'radiogroup'),
+ (None, 'readonly'),
+ (None, 'rel'),
+ (None, 'repeat-max'),
+ (None, 'repeat-min'),
+ (None, 'replace'),
+ (None, 'required'),
+ (None, 'rev'),
+ (None, 'rightspacing'),
+ (None, 'rows'),
+ (None, 'rowspan'),
+ (None, 'rules'),
+ (None, 'scope'),
+ (None, 'selected'),
+ (None, 'shape'),
+ (None, 'size'),
+ (None, 'span'),
+ (None, 'src'),
+ (None, 'start'),
+ (None, 'step'),
+ (None, 'style'),
+ (None, 'summary'),
+ (None, 'suppress'),
+ (None, 'tabindex'),
+ (None, 'target'),
+ (None, 'template'),
+ (None, 'title'),
+ (None, 'toppadding'),
+ (None, 'type'),
+ (None, 'unselectable'),
+ (None, 'usemap'),
+ (None, 'urn'),
+ (None, 'valign'),
+ (None, 'value'),
+ (None, 'variable'),
+ (None, 'volume'),
+ (None, 'vspace'),
+ (None, 'vrml'),
+ (None, 'width'),
+ (None, 'wrap'),
+ (namespaces['xml'], 'lang'),
+ # MathML attributes
+ (None, 'actiontype'),
+ (None, 'align'),
+ (None, 'columnalign'),
+ (None, 'columnalign'),
+ (None, 'columnalign'),
+ (None, 'columnlines'),
+ (None, 'columnspacing'),
+ (None, 'columnspan'),
+ (None, 'depth'),
+ (None, 'display'),
+ (None, 'displaystyle'),
+ (None, 'equalcolumns'),
+ (None, 'equalrows'),
+ (None, 'fence'),
+ (None, 'fontstyle'),
+ (None, 'fontweight'),
+ (None, 'frame'),
+ (None, 'height'),
+ (None, 'linethickness'),
+ (None, 'lspace'),
+ (None, 'mathbackground'),
+ (None, 'mathcolor'),
+ (None, 'mathvariant'),
+ (None, 'mathvariant'),
+ (None, 'maxsize'),
+ (None, 'minsize'),
+ (None, 'other'),
+ (None, 'rowalign'),
+ (None, 'rowalign'),
+ (None, 'rowalign'),
+ (None, 'rowlines'),
+ (None, 'rowspacing'),
+ (None, 'rowspan'),
+ (None, 'rspace'),
+ (None, 'scriptlevel'),
+ (None, 'selection'),
+ (None, 'separator'),
+ (None, 'stretchy'),
+ (None, 'width'),
+ (None, 'width'),
+ (namespaces['xlink'], 'href'),
+ (namespaces['xlink'], 'show'),
+ (namespaces['xlink'], 'type'),
+ # SVG attributes
+ (None, 'accent-height'),
+ (None, 'accumulate'),
+ (None, 'additive'),
+ (None, 'alphabetic'),
+ (None, 'arabic-form'),
+ (None, 'ascent'),
+ (None, 'attributeName'),
+ (None, 'attributeType'),
+ (None, 'baseProfile'),
+ (None, 'bbox'),
+ (None, 'begin'),
+ (None, 'by'),
+ (None, 'calcMode'),
+ (None, 'cap-height'),
+ (None, 'class'),
+ (None, 'clip-path'),
+ (None, 'color'),
+ (None, 'color-rendering'),
+ (None, 'content'),
+ (None, 'cx'),
+ (None, 'cy'),
+ (None, 'd'),
+ (None, 'dx'),
+ (None, 'dy'),
+ (None, 'descent'),
+ (None, 'display'),
+ (None, 'dur'),
+ (None, 'end'),
+ (None, 'fill'),
+ (None, 'fill-opacity'),
+ (None, 'fill-rule'),
+ (None, 'font-family'),
+ (None, 'font-size'),
+ (None, 'font-stretch'),
+ (None, 'font-style'),
+ (None, 'font-variant'),
+ (None, 'font-weight'),
+ (None, 'from'),
+ (None, 'fx'),
+ (None, 'fy'),
+ (None, 'g1'),
+ (None, 'g2'),
+ (None, 'glyph-name'),
+ (None, 'gradientUnits'),
+ (None, 'hanging'),
+ (None, 'height'),
+ (None, 'horiz-adv-x'),
+ (None, 'horiz-origin-x'),
+ (None, 'id'),
+ (None, 'ideographic'),
+ (None, 'k'),
+ (None, 'keyPoints'),
+ (None, 'keySplines'),
+ (None, 'keyTimes'),
+ (None, 'lang'),
+ (None, 'marker-end'),
+ (None, 'marker-mid'),
+ (None, 'marker-start'),
+ (None, 'markerHeight'),
+ (None, 'markerUnits'),
+ (None, 'markerWidth'),
+ (None, 'mathematical'),
+ (None, 'max'),
+ (None, 'min'),
+ (None, 'name'),
+ (None, 'offset'),
+ (None, 'opacity'),
+ (None, 'orient'),
+ (None, 'origin'),
+ (None, 'overline-position'),
+ (None, 'overline-thickness'),
+ (None, 'panose-1'),
+ (None, 'path'),
+ (None, 'pathLength'),
+ (None, 'points'),
+ (None, 'preserveAspectRatio'),
+ (None, 'r'),
+ (None, 'refX'),
+ (None, 'refY'),
+ (None, 'repeatCount'),
+ (None, 'repeatDur'),
+ (None, 'requiredExtensions'),
+ (None, 'requiredFeatures'),
+ (None, 'restart'),
+ (None, 'rotate'),
+ (None, 'rx'),
+ (None, 'ry'),
+ (None, 'slope'),
+ (None, 'stemh'),
+ (None, 'stemv'),
+ (None, 'stop-color'),
+ (None, 'stop-opacity'),
+ (None, 'strikethrough-position'),
+ (None, 'strikethrough-thickness'),
+ (None, 'stroke'),
+ (None, 'stroke-dasharray'),
+ (None, 'stroke-dashoffset'),
+ (None, 'stroke-linecap'),
+ (None, 'stroke-linejoin'),
+ (None, 'stroke-miterlimit'),
+ (None, 'stroke-opacity'),
+ (None, 'stroke-width'),
+ (None, 'systemLanguage'),
+ (None, 'target'),
+ (None, 'text-anchor'),
+ (None, 'to'),
+ (None, 'transform'),
+ (None, 'type'),
+ (None, 'u1'),
+ (None, 'u2'),
+ (None, 'underline-position'),
+ (None, 'underline-thickness'),
+ (None, 'unicode'),
+ (None, 'unicode-range'),
+ (None, 'units-per-em'),
+ (None, 'values'),
+ (None, 'version'),
+ (None, 'viewBox'),
+ (None, 'visibility'),
+ (None, 'width'),
+ (None, 'widths'),
+ (None, 'x'),
+ (None, 'x-height'),
+ (None, 'x1'),
+ (None, 'x2'),
+ (namespaces['xlink'], 'actuate'),
+ (namespaces['xlink'], 'arcrole'),
+ (namespaces['xlink'], 'href'),
+ (namespaces['xlink'], 'role'),
+ (namespaces['xlink'], 'show'),
+ (namespaces['xlink'], 'title'),
+ (namespaces['xlink'], 'type'),
+ (namespaces['xml'], 'base'),
+ (namespaces['xml'], 'lang'),
+ (namespaces['xml'], 'space'),
+ (None, 'y'),
+ (None, 'y1'),
+ (None, 'y2'),
+ (None, 'zoomAndPan'),
+))
+
+attr_val_is_uri = frozenset((
+ (None, 'href'),
+ (None, 'src'),
+ (None, 'cite'),
+ (None, 'action'),
+ (None, 'longdesc'),
+ (None, 'poster'),
+ (None, 'background'),
+ (None, 'datasrc'),
+ (None, 'dynsrc'),
+ (None, 'lowsrc'),
+ (None, 'ping'),
+ (namespaces['xlink'], 'href'),
+ (namespaces['xml'], 'base'),
+))
+
+svg_attr_val_allows_ref = frozenset((
+ (None, 'clip-path'),
+ (None, 'color-profile'),
+ (None, 'cursor'),
+ (None, 'fill'),
+ (None, 'filter'),
+ (None, 'marker'),
+ (None, 'marker-start'),
+ (None, 'marker-mid'),
+ (None, 'marker-end'),
+ (None, 'mask'),
+ (None, 'stroke'),
+))
+
+svg_allow_local_href = frozenset((
+ (None, 'altGlyph'),
+ (None, 'animate'),
+ (None, 'animateColor'),
+ (None, 'animateMotion'),
+ (None, 'animateTransform'),
+ (None, 'cursor'),
+ (None, 'feImage'),
+ (None, 'filter'),
+ (None, 'linearGradient'),
+ (None, 'pattern'),
+ (None, 'radialGradient'),
+ (None, 'textpath'),
+ (None, 'tref'),
+ (None, 'set'),
+ (None, 'use')
+))
+
+allowed_css_properties = frozenset((
+ 'azimuth',
+ 'background-color',
+ 'border-bottom-color',
+ 'border-collapse',
+ 'border-color',
+ 'border-left-color',
+ 'border-right-color',
+ 'border-top-color',
+ 'clear',
+ 'color',
+ 'cursor',
+ 'direction',
+ 'display',
+ 'elevation',
+ 'float',
+ 'font',
+ 'font-family',
+ 'font-size',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'height',
+ 'letter-spacing',
+ 'line-height',
+ 'overflow',
+ 'pause',
+ 'pause-after',
+ 'pause-before',
+ 'pitch',
+ 'pitch-range',
+ 'richness',
+ 'speak',
+ 'speak-header',
+ 'speak-numeral',
+ 'speak-punctuation',
+ 'speech-rate',
+ 'stress',
+ 'text-align',
+ 'text-decoration',
+ 'text-indent',
+ 'unicode-bidi',
+ 'vertical-align',
+ 'voice-family',
+ 'volume',
+ 'white-space',
+ 'width',
+))
+
+allowed_css_keywords = frozenset((
+ 'auto',
+ 'aqua',
+ 'black',
+ 'block',
+ 'blue',
+ 'bold',
+ 'both',
+ 'bottom',
+ 'brown',
+ 'center',
+ 'collapse',
+ 'dashed',
+ 'dotted',
+ 'fuchsia',
+ 'gray',
+ 'green',
+ '!important',
+ 'italic',
+ 'left',
+ 'lime',
+ 'maroon',
+ 'medium',
+ 'none',
+ 'navy',
+ 'normal',
+ 'nowrap',
+ 'olive',
+ 'pointer',
+ 'purple',
+ 'red',
+ 'right',
+ 'solid',
+ 'silver',
+ 'teal',
+ 'top',
+ 'transparent',
+ 'underline',
+ 'white',
+ 'yellow',
+))
+
+allowed_svg_properties = frozenset((
+ 'fill',
+ 'fill-opacity',
+ 'fill-rule',
+ 'stroke',
+ 'stroke-width',
+ 'stroke-linecap',
+ 'stroke-linejoin',
+ 'stroke-opacity',
+))
+
+allowed_protocols = frozenset((
+ 'ed2k',
+ 'ftp',
+ 'http',
+ 'https',
+ 'irc',
+ 'mailto',
+ 'news',
+ 'gopher',
+ 'nntp',
+ 'telnet',
+ 'webcal',
+ 'xmpp',
+ 'callto',
+ 'feed',
+ 'urn',
+ 'aim',
+ 'rsync',
+ 'tag',
+ 'ssh',
+ 'sftp',
+ 'rtsp',
+ 'afs',
+ 'data',
+))
+
+allowed_content_types = frozenset((
+ 'image/png',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/webp',
+ 'image/bmp',
+ 'text/plain',
+))
+
+
+data_content_type = re.compile(r'''
+ ^
+ # Match a content type ,