diff options
Diffstat (limited to 'AptUrl/Parser.py')
-rw-r--r-- | AptUrl/Parser.py | 153 |
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 |