aboutsummaryrefslogtreecommitdiff
path: root/AptUrl/Parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'AptUrl/Parser.py')
-rw-r--r--AptUrl/Parser.py153
1 files changed, 153 insertions, 0 deletions
diff --git a/AptUrl/Parser.py b/AptUrl/Parser.py
new file mode 100644
index 0000000..0543d07
--- /dev/null
+++ b/AptUrl/Parser.py
@@ -0,0 +1,153 @@
+# Copyright (c) 2007-2008 Canonical
+#
+# AUTHOR:
+# Michael Vogt <mvo@ubuntu.com>
+# With contributions by Siegfried-A. Gevatter <rainct@ubuntu.com>
+#
+# This file is part of AptUrl
+#
+# AptUrl is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# AptUrl is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with AptUrl; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os
+import string
+from string import Template
+from .Helpers import get_dist
+from .Helpers import _
+
+class InvalidUrlException(Exception):
+ def __init__(self, url, msg=""):
+ self.url = url
+ self.message = msg
+ def __str__(self):
+ return self.message
+
+MAX_URL_LEN=255
+
+# substituion mapping
+apturl_substitution_mapping = {
+ "distro" : get_dist(),
+ "kernel" : os.uname()[2]
+}
+
+# whitelist for the uri
+whitelist = []
+whitelist.extend(string.ascii_letters)
+whitelist.extend(string.digits)
+whitelist.extend(['_',':','?','/','+','.','~','=','<','>','-',',','$','&'])
+
+
+class AptUrl(object):
+ " a class that contains the parsed data from an apt url "
+ def __init__(self):
+ self.package = None
+ self.schema = None
+ self.minver = None
+ self.refresh = None
+ # for added repos
+ self.keyfile = None
+ self.repo_url = None
+ self.dist = '/'
+ # for known sections
+ self.section = []
+ # for known channels
+ self.channel = None
+
+def is_format_package_name(string):
+ " return True if string would be an acceptable name for a Debian package "
+
+ return (string.replace("+", "").replace("-", "").replace(".", "").replace(":", "").isalnum()
+ and string.islower() and string[0].isalnum() and len(string) > 1)
+
+def do_apt_url_substitution(apt_url, mapping):
+ " substitute known templates against the field package and channel "
+ for field in ["package","channel"]:
+ if getattr(apt_url, field):
+ s=Template(getattr(apt_url, field))
+ setattr(apt_url, field, s.substitute(mapping))
+
+def match_against_whitelist(raw_url):
+ " test if the url matches the internal whitelist "
+ for char in raw_url:
+ if not char in whitelist:
+ raise InvalidUrlException(
+ raw_url, _("Non whitelist char in the uri"))
+ return True
+
+def set_value(apt_url, s):
+ " set a key,value pair from string s to AptUrl object "
+ (key, value) = s.split("=")
+ try:
+ if ' ' in value:
+ raise InvalidUrlException(apt_url, _("Whitespace in key=value"))
+ if type(getattr(apt_url, key)) == type([]):
+ getattr(apt_url, key).append(value)
+ else:
+ setattr(apt_url, key, value)
+ except Exception as e:
+ raise InvalidUrlException(apt_url, _("Exception '%s'") % e)
+
+
+def parse(full_url, mapping=apturl_substitution_mapping):
+ " parse an apt url and return a list of AptUrl objects "
+ # apt:pkg1?k11=v11?k12=v12,pkg2?k21=v21?k22=v22,...
+ res = []
+
+ if len(full_url) > MAX_URL_LEN:
+ url = "%s ..." % full_url[0:(MAX_URL_LEN // 10)]
+ raise InvalidUrlException(url, _("Url string '%s' too long") % url)
+
+ # check against whitelist
+ match_against_whitelist(full_url)
+ for url in full_url.split(";"):
+ if not ":" in url:
+ raise InvalidUrlException(url, _("No ':' in the uri"))
+
+ # now parse it
+
+ (schema, packages) = url.split(":", 1)
+ packages = packages.split(",")
+
+ for package in packages:
+ apt_url = AptUrl()
+ apt_url.schema = schema
+ # check for schemas of the form: apt+http://
+ if schema.startswith("apt+"):
+ apt_url.repo_url = schema[len("apt+"):] + ":" + package.split("?",1)[0]
+ else:
+ if "?" in package:
+ apt_url.package = package.split("?")[0].lstrip("/")
+ else:
+ apt_url.package = package.lstrip("/")
+
+ # now parse the ?... bits
+ if "?" in package:
+ key_value_pairs = package.split("?")[1:]
+ for s in key_value_pairs:
+ if "&" in s:
+ and_key_value_pairs = s.split("&")
+ for s in and_key_value_pairs:
+ set_value(apt_url, s)
+ else:
+ set_value(apt_url, s)
+
+ # do substitution (if needed)
+ do_apt_url_substitution(apt_url, mapping)
+
+ # check if the package name is valid
+ if not is_format_package_name(apt_url.package):
+ raise InvalidUrlException(url, "Invalid package name '%s'" % apt_url.package)
+
+ res.append(apt_url)
+ return res