summaryrefslogtreecommitdiffstats
path: root/store
diff options
context:
space:
mode:
Diffstat (limited to 'store')
-rw-r--r--store/admin.py43
-rw-r--r--store/api.py43
-rw-r--r--store/authdecorators.py4
-rw-r--r--store/management/commands/expire-downloads.py4
-rw-r--r--store/migrations/0001_initial.py9
-rw-r--r--store/migrations/0002_alter_category_order.py18
-rw-r--r--store/models.py14
-rw-r--r--store/tags.py20
-rw-r--r--store/utilities.py77
9 files changed, 118 insertions, 114 deletions
diff --git a/store/admin.py b/store/admin.py
index 910f3fb..389bb87 100644
--- a/store/admin.py
+++ b/store/admin.py
@@ -30,15 +30,14 @@
##
#############################################################################
-import StringIO
+import io
from PIL import Image, ImageChops
from django import forms
from django.contrib import admin
-from django.utils.translation import ugettext as _
-from django.utils.translation import ugettext_lazy
from django.core.files.uploadedfile import InMemoryUploadedFile
from ordered_model.admin import OrderedModelAdmin
+from django.utils.safestring import mark_safe
from store.models import *
from store.utilities import parseAndValidatePackageMetadata, writeTempIcon, makeTagList
@@ -68,10 +67,11 @@ class CategoryAdminForm(forms.ModelForm):
im = Image.open(cleaned_data['icon'])
size = (settings.ICON_SIZE_X, settings.ICON_SIZE_Y)
im.thumbnail(size, Image.ANTIALIAS)
- imagefile = StringIO.StringIO()
+ imagefile = io.BytesIO()
im.save(imagefile, format='png')
+ length = imagefile.tell()
imagefile.seek(0)
- cleaned_data['icon'] = InMemoryUploadedFile(imagefile, 'icon', "icon.png", 'image/png', imagefile.len, None)
+ cleaned_data['icon'] = InMemoryUploadedFile(imagefile, 'icon', "icon.png", 'image/png', length, None)
return cleaned_data
class CategoryAdmin(OrderedModelAdmin):
@@ -85,16 +85,15 @@ class CategoryAdmin(OrderedModelAdmin):
def name(self, obj):
# just to forbid sorting by name
return obj.name
- name.short_description = ugettext_lazy('Item caption')
+ name.short_description = u'Item caption'
def icon_image(self, obj):
prefix = settings.URL_PREFIX
image_request = prefix + "/category/icon?id=%s" % (obj.id)
- html = u'<img width=%s height=%s src="%s" />' % (settings.ICON_SIZE_X, settings.ICON_SIZE_Y, image_request)
- return html
+ html = '<img width=%s height=%s src="%s" />' % (settings.ICON_SIZE_X, settings.ICON_SIZE_Y, image_request)
+ return mark_safe(html)
- icon_image.allow_tags = True
- icon_image.short_description = ugettext_lazy('Category icon')
+ icon_image.short_description = u'Category icon'
class AppAdminForm(forms.ModelForm):
@@ -113,7 +112,7 @@ class AppAdminForm(forms.ModelForm):
try:
pkgdata = parseAndValidatePackageMetadata(package_file)
except Exception as error:
- raise forms.ValidationError(_('Validation error: %s' % str(error)))
+ raise forms.ValidationError('Validation error: %s' % str(error))
self.appId = pkgdata['info']['id']
self.name = pkgdata['storeName']
@@ -123,27 +122,27 @@ class AppAdminForm(forms.ModelForm):
# check if this really is an update
if hasattr(self, 'instance') and self.instance.appid:
if self.appId != self.instance.appid:
- raise forms.ValidationError(_('Validation error: an update cannot change the '
- 'application id, tried to change from %s to %s' %
- (self.instance.appid, self.appId)))
+ raise forms.ValidationError('Validation error: an update cannot change the '
+ 'application id, tried to change from %s to %s' %
+ (self.instance.appid, self.appId))
elif self.architecture != self.instance.architecture:
- raise forms.ValidationError(_('Validation error: an update cannot change the '
- 'application architecture from %s to %s' %
- (self.instance.architecture, self.architecture)))
+ raise forms.ValidationError('Validation error: an update cannot change the '
+ 'application architecture from %s to %s' %
+ (self.instance.architecture, self.architecture))
else:
try:
if App.objects.get(appid__exact=self.appId, architecture__exact=self.architecture, tags_hash__exact=self.tags_hash):
- raise forms.ValidationError(_('Validation error: another application with id'
- ' %s , tags %s and architecture %s already '
- 'exists' % (str(self.appId), str(self.tags_hash),
- str(self.architecture))))
+ raise forms.ValidationError('Validation error: another application with id'
+ ' %s , tags %s and architecture %s already '
+ 'exists' % (str(self.appId), str(self.tags_hash),
+ str(self.architecture)))
except App.DoesNotExist:
pass
# write icon into file to serve statically
success, error = writeTempIcon(self.appId, self.architecture, self.tags_hash, pkgdata['icon'])
if not success:
- raise forms.ValidationError(_(error))
+ raise forms.ValidationError(error)
return cleaned_data
diff --git a/store/api.py b/store/api.py
index 9444b0b..b617e34 100644
--- a/store/api.py
+++ b/store/api.py
@@ -33,6 +33,7 @@
import os
import shutil
import hashlib
+import logging
from django.conf import settings
from django.db.models import Q, Count
@@ -89,7 +90,6 @@ def hello(request):
else:
request.session['architecture'] = ''
- request.session['pkgversions'] = range(1, version + 1)
return JsonResponse({'status': status})
@@ -120,7 +120,7 @@ def login(request):
def logout(request):
status = 'ok'
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
status = 'failed'
logout(request)
@@ -201,12 +201,7 @@ def appList(request):
if 'architecture' in request.session:
archlist.append(request.session['architecture'])
- versionlist = [1]
- if 'pkgversions' in request.session:
- versionlist = request.session['pkgversions']
-
apps = apps.filter(architecture__in=archlist)
- apps = apps.filter(pkgformat__in=versionlist)
#Tag filtering
#There is no search by version distance yet - this must be fixed
@@ -261,13 +256,9 @@ def appDescription(request):
archlist = ['All', ]
if 'architecture' in request.session:
archlist.append(request.session['architecture'])
- versionlist = [1]
- if 'pkgversions' in request.session:
- versionlist = request.session['pkgversions']
appId = getRequestDictionary(request)['id']
try:
app = App.objects.filter(appid__exact = appId, architecture__in = archlist).order_by('architecture','tags_hash')
- app = app.filter(pkgformat__in=versionlist)
#Tag filtering
#There is no search by version distance yet - this must be fixed
if 'tag' in request.session:
@@ -285,7 +276,7 @@ def appIconNew(request, path):
path=path.replace('/', '_').replace('\\', '_').replace(':', 'x3A').replace(',', 'x2C') + '.png'
try:
response = HttpResponse(content_type='image/png')
- with open(iconPath() + path, 'rb') as pkg:
+ with open(os.path.join(settings.MEDIA_ROOT, iconPath(), path), 'rb') as pkg:
response.write(pkg.read())
response['Content-Length'] = pkg.tell()
return response
@@ -299,22 +290,20 @@ def appIcon(request):
archlist.append(normalizeArch(dictionary['architecture']))
elif 'architecture' in request.session:
archlist.append(request.session['architecture'])
- versionlist = [1]
- if 'pkgversions' in request.session:
- versionlist = request.session['pkgversions']
appId = dictionary['id']
try:
- app = App.objects.filter(appid__exact = appId, architecture__in = archlist).order_by('architecture','tags_hash')
- app = app.filter(pkgformat__in=versionlist)
+ apps = App.objects.filter(appid__exact = appId, architecture__in = archlist).order_by('architecture','tags_hash')
#Tag filtering
#There is no search by version distance yet - this must be fixed
if 'tag' in request.session:
tags = SoftwareTagList()
tags.parse(request.session['tag'])
- app_ids = [x.id for x in app if x.is_tagmatching(tags.list())]
- app = App.objects.filter(id__in=app_ids)
- app = app.last()
- with open(iconPath(app.appid, app.architecture, app.tags_hash), 'rb') as iconPng:
+ app_ids = [x.id for x in apps if x.is_tagmatching(tags.list())]
+ apps = App.objects.filter(id__in=app_ids)
+ app = apps.last()
+ path = iconPath(app.appid, app.architecture, app.tags_hash)
+ path = os.path.join(settings.MEDIA_ROOT, path)
+ with open(path, 'rb') as iconPng:
response = HttpResponse(content_type='image/png')
response.write(iconPng.read())
return response
@@ -323,14 +312,11 @@ def appIcon(request):
def appPurchase(request):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return HttpResponseForbidden('no login')
archlist = ['All', ]
if 'architecture' in request.session:
archlist.append(request.session['architecture'])
- versionlist = [1]
- if 'pkgversions' in request.session:
- versionlist = request.session['pkgversions']
try:
deviceId = str(getRequestDictionary(request).get("device_id", ""))
@@ -346,7 +332,6 @@ def appPurchase(request):
app = App.objects.filter(id__exact = getRequestDictionary(request)['purchaseId'], architecture__in=archlist).order_by('architecture','tags_hash')
else:
raise ValidationError('id or purchaseId parameter required')
- app = app.filter(pkgformat__in=versionlist)
#Tag filtering
#There is no search by version distance yet - this must be fixed
if 'tag' in request.session:
@@ -356,7 +341,7 @@ def appPurchase(request):
app = App.objects.filter(id__in=app_ids)
app = app.last()
- fromFilePath = packagePath(app.appid, app.architecture, app.tags_hash)
+ fromFilePath = os.path.join(settings.MEDIA_ROOT, packagePath(app.appid, app.architecture, app.tags_hash))
# we should not use obvious names here, but just hash the string.
# this would be a nightmare to debug though and this is a development server :)
@@ -423,7 +408,7 @@ def categoryIcon(request):
try:
if categoryId != '-1':
category = Category.objects.filter(id__exact = categoryId)[0]
- filename = iconPath() + "category_" + str(category.id) + ".png"
+ filename = os.path.join(settings.MEDIA_ROOT, iconPath(), "category_" + str(category.id) + ".png")
else:
from django.contrib.staticfiles import finders
filename = finders.find('img/category_All.png')
@@ -439,7 +424,7 @@ def categoryIcon(request):
# |Error|
# | |
# +-----+
- emptyPng = "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x01\x03\x00\x00\x00I\xb4\xe8\xb7\x00\x00\x00\x06PLTE\x00\x00\x00\x00\x00\x00\xa5\x67\xb9\xcf\x00\x00\x00\x01tRNS\x00@\xe6\xd8f\x00\x00\x00\x33IDAT\x08\xd7\x63\xf8\x0f\x04\x0c\x0d\x0c\x0c\x8c\x44\x13\x7f\x40\xc4\x01\x10\x71\xb0\xf4\x5c\x2c\xc3\xcf\x36\xc1\x44\x86\x83\x2c\x82\x8e\x48\xc4\x5f\x16\x3e\x47\xd2\x0c\xc5\x46\x80\x9c\x06\x00\xa4\xe5\x1d\xb4\x8e\xae\xe8\x43\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"
+ emptyPng = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x01\x03\x00\x00\x00I\xb4\xe8\xb7\x00\x00\x00\x06PLTE\x00\x00\x00\x00\x00\x00\xa5\x67\xb9\xcf\x00\x00\x00\x01tRNS\x00@\xe6\xd8f\x00\x00\x00\x33IDAT\x08\xd7\x63\xf8\x0f\x04\x0c\x0d\x0c\x0c\x8c\x44\x13\x7f\x40\xc4\x01\x10\x71\xb0\xf4\x5c\x2c\xc3\xcf\x36\xc1\x44\x86\x83\x2c\x82\x8e\x48\xc4\x5f\x16\x3e\x47\xd2\x0c\xc5\x46\x80\x9c\x06\x00\xa4\xe5\x1d\xb4\x8e\xae\xe8\x43\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"
response.write(emptyPng)
return response
diff --git a/store/authdecorators.py b/store/authdecorators.py
index 2a4119c..a4307ff 100644
--- a/store/authdecorators.py
+++ b/store/authdecorators.py
@@ -60,7 +60,7 @@ def view_or_basicauth(view, request, test_func, realm="", *args, **kwargs):
# NOTE: We are only support basic authentication for now.
#
if auth[0].lower() == "basic":
- uname, passwd = base64.b64decode(auth[1]).split(':')
+ uname, passwd = base64.b64decode(auth[1].encode('utf-8')).decode('utf-8').split(':')
user = authenticate(username=uname, password=passwd)
if user is not None:
if user.is_active:
@@ -114,7 +114,7 @@ def logged_in_or_basicauth(realm=""):
def view_decorator(func):
def wrapper(request, *args, **kwargs):
return view_or_basicauth(func, request,
- lambda u: u.is_authenticated(),
+ lambda u: u.is_authenticated,
realm, *args, **kwargs)
return wrapper
diff --git a/store/management/commands/expire-downloads.py b/store/management/commands/expire-downloads.py
index 94b0d24..012182e 100644
--- a/store/management/commands/expire-downloads.py
+++ b/store/management/commands/expire-downloads.py
@@ -39,7 +39,7 @@ from django.conf import settings
from store.utilities import downloadPath
class Command(BaseCommand):
- help = 'Expires all downloads that are older than 10 minutes'
+ help = 'Expires all downloads that are older than APPSTORE_DOWNLOAD_EXPIRY minutes'
def handle(self, *args, **options):
self.stdout.write('Removing expired download packages')
@@ -50,7 +50,7 @@ class Command(BaseCommand):
for pkg in os.listdir(pkgPath):
t = os.path.getmtime(pkgPath + pkg)
age = time.time() - t
- if age > (10 * 60):
+ if age > (int(settings.APPSTORE_DOWNLOAD_EXPIRY) * 60):
os.remove(pkgPath + pkg)
self.stdout.write(' -> %s (age: %s seconds)' % (pkg, int(age)))
diff --git a/store/migrations/0001_initial.py b/store/migrations/0001_initial.py
index 80d154c..2bb3457 100644
--- a/store/migrations/0001_initial.py
+++ b/store/migrations/0001_initial.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
-# Generated by Django 1.11.27 on 2020-08-14 17:24
#############################################################################
##
## Copyright (C) 2020 Luxoft Sweden AB
@@ -32,7 +31,7 @@
##
#############################################################################
-# Generated by Django 1.11.27 on 2020-07-22 16:39
+# Generated by Django 1.11.27 on 2020-10-30 13:46
from __future__ import unicode_literals
from django.conf import settings
@@ -62,9 +61,9 @@ class Migration(migrations.Migration):
('description', models.TextField()),
('dateAdded', models.DateField(auto_now_add=True)),
('dateModified', models.DateField(auto_now=True)),
- ('tags_hash', models.CharField(default=b'', max_length=4096)),
- ('architecture', models.CharField(default=b'All', max_length=20)),
- ('version', models.CharField(default=b'0.0.0', max_length=20)),
+ ('tags_hash', models.CharField(default='', max_length=4096)),
+ ('architecture', models.CharField(default='All', max_length=20)),
+ ('version', models.CharField(default='0.0.0', max_length=20)),
('pkgformat', models.IntegerField()),
],
),
diff --git a/store/migrations/0002_alter_category_order.py b/store/migrations/0002_alter_category_order.py
new file mode 100644
index 0000000..e13175c
--- /dev/null
+++ b/store/migrations/0002_alter_category_order.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.0.6 on 2023-06-26 19:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('store', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='category',
+ name='order',
+ field=models.PositiveIntegerField(db_index=True, editable=False, verbose_name='order'),
+ ),
+ ]
diff --git a/store/models.py b/store/models.py
index 7e8751a..ff8ab0c 100644
--- a/store/models.py
+++ b/store/models.py
@@ -45,7 +45,7 @@ from store.tags import SoftwareTag
def category_file_name(instance, filename):
# filename parameter is unused. See django documentation for details:
# https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.FileField.upload_to
- return settings.MEDIA_ROOT + "icons/category_" + str(instance.id) + ".png"
+ return "icons/category_" + str(instance.id) + ".png"
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
@@ -63,6 +63,9 @@ class Category(OrderedModel):
def __unicode__(self):
return self.name
+ def __str__(self):
+ return self.name
+
def save(self, *args, **kwargs):
if self.id is None:
# This is a django hack. When category icon is saved and then later accessed,
@@ -77,13 +80,16 @@ class Category(OrderedModel):
super(Category, self).save(*args, **kwargs)
class Vendor(models.Model):
- user = models.ForeignKey(User, primary_key = False)
+ user = models.ForeignKey(User, primary_key = False, on_delete = models.CASCADE)
name = models.CharField(max_length = 200)
certificate = models.TextField(max_length = 8000)
def __unicode__(self):
return self.name
+ def __str__(self):
+ return self.name
+
class Tag(models.Model):
negative = models.BooleanField(default=False)
name = models.CharField(max_length=200)
@@ -106,8 +112,8 @@ class App(models.Model):
appid = models.CharField(max_length=200)
name = models.CharField(max_length=200)
file = models.FileField(upload_to=content_file_name, storage=OverwriteStorage())
- vendor = models.ForeignKey(Vendor)
- category = models.ForeignKey(Category)
+ vendor = models.ForeignKey(Vendor, on_delete = models.CASCADE)
+ category = models.ForeignKey(Category, on_delete = models.CASCADE)
briefDescription = models.TextField()
description = models.TextField()
dateAdded = models.DateField(auto_now_add=True)
diff --git a/store/tags.py b/store/tags.py
index e906684..da09803 100644
--- a/store/tags.py
+++ b/store/tags.py
@@ -79,7 +79,7 @@ class SoftwareTag:
""" Takes tag and parses it. If it can't parse - raises exception of invalid value
:type tag: str
"""
- if not isinstance(tag, (str, unicode)):
+ if not isinstance(tag, str):
raise BaseException("Invalid input data-type")
if not validateTag(tag):
raise BaseException("Malformed tag")
@@ -121,7 +121,7 @@ class SoftwareTagList:
def __str__(self):
lst = list()
- for _, value in self.taglist.items():
+ for _, value in list(self.taglist.items()):
lst += [str(i) for i in value]
lst.sort()
return ",".join(lst)
@@ -168,7 +168,7 @@ class SoftwareTagList:
def list(self):
lst = list()
- for _, value in self.taglist.items():
+ for _, value in list(self.taglist.items()):
for i in value:
lst.append(i)
return lst
@@ -188,13 +188,13 @@ class TestSoftwareTagMethods(unittest.TestCase):
self.assertTrue(tag.has_version())
tag = SoftwareTag('Qt')
self.assertFalse(tag.has_version())
- with self.assertRaisesRegexp(BaseException, "Malformed tag"):
+ with self.assertRaisesRegex(BaseException, "Malformed tag"):
SoftwareTag('фыва')
- with self.assertRaisesRegexp(BaseException, "Malformed tag"):
+ with self.assertRaisesRegex(BaseException, "Malformed tag"):
SoftwareTag('фыва:5.1')
- with self.assertRaisesRegexp(BaseException, "Malformed tag"):
+ with self.assertRaisesRegex(BaseException, "Malformed tag"):
SoftwareTag('qt.1:5.1')
- with self.assertRaisesRegexp(BaseException, "Invalid input data-type"):
+ with self.assertRaisesRegex(BaseException, "Invalid input data-type"):
SoftwareTag(1)
def test_tag_match(self):
@@ -227,11 +227,11 @@ class TestSoftwareTagListMethods(unittest.TestCase):
def test_append_invalid(self):
lst = SoftwareTagList()
- with self.assertRaisesRegexp(BaseException, "Malformed tag"):
+ with self.assertRaisesRegex(BaseException, "Malformed tag"):
self.assertFalse(lst.append(SoftwareTag('qt:1:1'))) # Invalid version
- with self.assertRaisesRegexp(BaseException, "Malformed tag"):
+ with self.assertRaisesRegex(BaseException, "Malformed tag"):
self.assertFalse(lst.append(SoftwareTag('фыва'))) # Non-ascii
- with self.assertRaisesRegexp(BaseException, "Malformed tag"):
+ with self.assertRaisesRegex(BaseException, "Malformed tag"):
self.assertFalse(lst.append(SoftwareTag(''))) # empty tag is not valid
def test_append_valid(self):
diff --git a/store/utilities.py b/store/utilities.py
index 06151bb..1a283e5 100644
--- a/store/utilities.py
+++ b/store/utilities.py
@@ -70,28 +70,29 @@ def getRequestDictionary(request):
return request.GET
def packagePath(appId=None, architecture=None, tags=None):
- path = settings.MEDIA_ROOT + 'packages/'
+ path = "packages" ## os.path.join(settings.MEDIA_ROOT, 'packages/')
if tags is None:
tags = ""
if (appId is not None) and (architecture is not None):
- path = path + '_'.join([appId, architecture, tags]).replace('/', '_').\
- replace('\\', '_').replace(':', 'x3A').replace(',', 'x2C')
+ path = os.path.join(path, '_'.join([appId, architecture, tags]).replace('/', '_').\
+ replace('\\', '_').replace(':', 'x3A').replace(',', 'x2C'))
return path
def iconPath(appId=None, architecture=None, tags=None):
- path = settings.MEDIA_ROOT + 'icons/'
+ path = "icons" ## os.path.join(settings.MEDIA_ROOT, 'icons/')
if tags is None:
tags = ""
if (appId is not None) and (architecture is not None):
- return path + '_'.join([appId, architecture, tags]).replace('/', '_').\
- replace('\\', '_').replace(':', 'x3A').replace(',', 'x2C') + '.png'
+ return os.path.join(path, '_'.join([appId, architecture, tags]).replace('/', '_').\
+ replace('\\', '_').replace(':', 'x3A').replace(',', 'x2C') + '.png')
return path
def writeTempIcon(appId, architecture, tags, icon):
try:
- if not os.path.exists(iconPath()):
- os.makedirs(iconPath())
- tempicon = open(iconPath(appId, architecture, tags), 'w')
+ path = os.path.join(settings.MEDIA_ROOT, iconPath())
+ if not os.path.exists(path):
+ os.makedirs(path)
+ tempicon = open(os.path.join(settings.MEDIA_ROOT, iconPath(appId, architecture, tags)), 'wb')
tempicon.write(icon)
tempicon.flush()
tempicon.close()
@@ -101,39 +102,35 @@ def writeTempIcon(appId, architecture, tags, icon):
str(error)
def downloadPath():
- return settings.MEDIA_ROOT + 'downloads/'
+ return os.path.join(settings.MEDIA_ROOT, 'downloads/')
-def isValidDnsName(dnsName, errorList):
- # see also in AM: src/common-lib/utilities.cpp / isValidDnsName()
+def isValidFilesystemName(name, errorList):
+ # see also in AM: src/common-lib/utilities.cpp / validateForFilesystemUsage
try:
- # this is not based on any RFC, but we want to make sure that this id is usable as filesystem
- # name. So in order to support FAT (SD-Cards), we need to keep the path < 256 characters
+ # we need to make sure that we can use the name as directory in a filesystem and inode names
+ # are limited to 255 characters in Linux. We need to subtract a safety margin for prefixes
+ # or suffixes though:
- if len(dnsName) > 200:
- raise Exception('too long - the maximum length is 200 characters')
+ if not name:
+ raise Exception('must not be empty')
- # we require at least 3 parts: tld.company-name.application-name
- # this make it easier for humans to identify apps by id.
+ if len(name) > 150:
+ raise Exception('the maximum length is 150 characters')
- labels = dnsName.split('.')
- if len(labels) < 3:
- raise Exception('wrong format - needs to be in reverse-DNS notation and consist of at least three parts separated by .')
+ # all characters need to be ASCII minus any filesystem special characters:
+ spaceOnly = True
+ forbiddenChars = '<>:"/\\|?*'
+ for i, c in enumerate(name):
+ if (ord(c) < 0x20) or (ord(c) > 0x7f) or (c in forbiddenChars):
+ raise Exception(f'must consist of printable ASCII characters only, except any of \'{forbiddenChars}\'')
- # standard domain name requirements from the RFCs 1035 and 1123
+ if spaceOnly:
+ spaceOnly = (c == ' ')
- for label in labels:
- if 0 >= len(label) > 63:
- raise Exception('wrong format - each part of the name needs to at least 1 and at most 63 characters')
-
- for i, c in enumerate(label):
- isAlpha = (c >= '0' and c <= '9') or (c >= 'a' and c <= 'z');
- isDash = (c == '-');
- isInMiddle = (i > 0) and (i < (len(label) - 1));
-
- if not (isAlpha or (isDash and isInMiddle)):
- raise Exception('invalid characters - only [a-z0-9-] are allowed (and '-' cannot be the first or last character)')
+ if spaceOnly:
+ raise Exception('must not consist of only white-space characters')
return True
@@ -141,7 +138,6 @@ def isValidDnsName(dnsName, errorList):
errorList[0] = str(error)
return False
-
def verifySignature(signaturePkcs7, hash, chainOfTrust):
# see also in AM: src/crypto-lib/signature.cpp / Signature::verify()
@@ -256,7 +252,7 @@ def parsePackageMetadata(packageFile):
raise Exception('the first file in the package is not --PACKAGE-HEADER--, but %s' % entry.name)
if entry.name.startswith('--PACKAGE-FOOTER--'):
- footerContents += contents
+ footerContents += contents.decode('utf-8')
foundFooter = True
elif foundFooter:
@@ -264,10 +260,11 @@ def parsePackageMetadata(packageFile):
if not entry.name.startswith('--PACKAGE-'):
addToDigest1 = '%s/%s/' % ('D' if entry.isdir() else 'F', 0 if entry.isdir() else entry.size)
+ addToDigest1 = addToDigest1.encode('utf-8')
entryName = entry.name
if entry.isdir() and entryName.endswith('/'):
entryName = entryName[:-1]
- addToDigest2 = unicode(entryName, 'utf-8').encode('utf-8')
+ addToDigest2 = str(entryName).encode('utf-8')
if entry.isfile():
digest.update(contents)
@@ -380,7 +377,7 @@ def parseAndValidatePackageMetadata(packageFile, certificates = []):
'icon': [],
'digest': [] }
- for part in partFields.keys():
+ for part in list(partFields.keys()):
if not part in pkgdata:
raise Exception('package metadata is missing the %s part' % part)
data = pkgdata[part]
@@ -393,7 +390,7 @@ def parseAndValidatePackageMetadata(packageFile, certificates = []):
raise Exception('the id fields in --PACKAGE-HEADER-- and info.yaml are different: %s vs. %s' % (pkgdata['header'][packageIdKey], pkgdata['info']['id']))
error = ['']
- if not isValidDnsName(pkgdata['info']['id'], error):
+ if not isValidFilesystemName(pkgdata['info']['id'], error):
raise Exception('invalid id: %s' % error[0])
if pkgdata['header']['diskSpaceUsed'] <= 0:
@@ -408,7 +405,7 @@ def parseAndValidatePackageMetadata(packageFile, certificates = []):
elif 'en_US' in pkgdata['info']['name']:
name = pkgdata['info']['name']['en_US']
elif len(pkgdata['info']['name']) > 0:
- name = pkgdata['info']['name'].values()[0]
+ name = list(pkgdata['info']['name'].values())[0]
if not name:
raise Exception('could not deduce a suitable package name from the info part')
@@ -456,7 +453,7 @@ def addFileToPackage(sourcePackageFile, destinationPackageFile, fileName, fileCo
entry = dst.gettarinfo(fileobj = tmp, arcname = fileName)
entry.uid = entry.gid = 0
entry.uname = entry.gname = ''
- entry.mode = 0400
+ entry.mode = 0o400
dst.addfile(entry, fileobj = tmp)
dst.close()