Commit 75ebbee4 authored by Kirill Smelkov's avatar Kirill Smelkov

Merge branch 'master' into master+ZODB4-wc2

* master: (1429 commits)
  ERP5: fix handling of repozo restoration failure
  software/simpleran: Fix fluentbit ingestion not to barf on large lines
  stack/slapos: version up slapos.cookbook 1.0.373
  Release slapos.cookbook (1.0.373)
  random: fix password recipe when using storage-path and passwd
  stack/slapos.cfg: version up netaddr 1.3.0 (on py3)
  software/erp5/test/benchmarks: fix running pt-query-digest on py3
  software/mosquitto/test: fix flaky test
  stack/erp5: use repozo --kill-old-on-full when producing backups
  stack/erp5: fix ZEO repozo backups not produced on python3
  software/grafana: version up, split servers and agent
  component/chrpath: add support for newer architecture (e.g. riscv)
  software/wendelin: Add required software.cfg.json
  software/kvm: Drop default image and replace it with boot-image-url-select
  component/qemu-kvm, component/vm-img: Clean up qemu-kvm of ISOs
  software/kvm: Provide description for boot-image-url-select
  software/kvm: Test nbd-host and nbd-port
  software/kvm: Drop obsoleted nbd functionality
  stack/erp5: remove outdated comment in repozo restore script
  stack/erp5: replace a collective.recipe.template with slapos.recipe.template
  ...
parents 4bf51569 6cf1769d

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -48,6 +48,7 @@ recipe = zc.recipe.egg:develop
setup = ${ZEO4-repository:location}
egg = ZEO
egg-versions =
ZEO = 4.3.1
[ZEO4-repository]
recipe = slapos.recipe.build:gitclone
......@@ -58,14 +59,18 @@ location = ${buildout:parts-directory}/ZEO4
git-executable = ${git:location}/bin/git
# ZEO5 is plain upstream egg
[ZEO5]
# ZEO6 and ZEO5 are plain upstream eggs
[ZEO6]
recipe = zc.recipe.egg:eggs
egg = ZEO
eggs = ${:egg}
egg-versions =
ZEO = 6.0.0
[ZEO5]
<= ZEO6
egg-versions =
ZEO = 5.2.3
ZEO = 5.4.0
trollius = 2.2.1
futures = 3.3.0
......
......@@ -9,7 +9,7 @@ extends =
parts = ZODB/scripts
# ZODB allows to use either ZODB4, ZODB4-wc2 or ZODB5.
# ZODB allows to use either ZODB4, ZODB4-wc2, ZODB5 or ZODB6.
# To select which version to use users should do:
#
# [ZODB]
......@@ -36,13 +36,12 @@ init =
import zc.buildout.easy_install
zc.buildout.easy_install.default_versions(versions)
# ZODB/scripts installs scripts from ZODB
[ZODB/scripts]
recipe = zc.recipe.egg:scripts
eggs = ${ZODB:egg}
# ZODB4 and ZODB5 are plain upstream eggs
# ZODB4, ZODB5 and ZODB6 are plain upstream eggs
[_ZODB]
recipe = zc.recipe.egg:eggs
egg = ZODB
......@@ -58,9 +57,20 @@ egg-versions =
[ZODB5]
<= _ZODB
egg-versions =
ZODB = 5.6.0
transaction = 2.4.0
ZODB = 5.8.1
transaction = 4.0.0
[ZODB5:python2]
<= _ZODB
egg-versions =
ZODB = 5.8.1
transaction = 3.0.1
[ZODB6]
<= _ZODB
egg-versions =
ZODB = 6.0.0
transaction = 4.0.0
# ZODB4-wc2 is ZODB4 version with patches for wendelin.core 2 to work correctly.
# The main change is backport of the way MVCC is handled by always calling
......@@ -92,13 +102,18 @@ egg = ${:_buildout_section_name_}
setup-eggs = ${python-cffi:egg}
# eggs that are common to ZODB4 and ZODB5.
# eggs that are common to ZODB4, ZODB5 and ZODB6.
[versions]
BTrees = 4.5.1
persistent = 4.6.4
zodbpickle = 2.0.0
BTrees = 5.1.0
persistent = 5.1.0
zodbpickle = 3.3.0
# Provide ZODB3 for those eggs that still care about ZODB3 compatibility -
# for example wendelin.core. ZODB3 3.11 is just a dependency egg on _latest_
# ZODB, persistent, BTrees and ZEO.
ZODB3 = 3.11.0
[versions:python2]
BTrees = 4.11.3
persistent = 4.9.3
zodbpickle = 2.6.0
......@@ -6,7 +6,6 @@ extends =
../../stack/nxdtest.cfg
../pygolang/buildout.cfg
../python-manuel/buildout.cfg
buildout.cfg
../ZEO/buildout.cfg
......@@ -26,6 +25,9 @@ revision =
[python]
part = python2.7
[openssl]
<= openssl-1.1
# we need persistent to be a git checkout because persistent tests want to
# discover in-tree files that are not present in persistent egg when it is
# installed in non-development mode:
......@@ -46,12 +48,6 @@ branch = master
revision = 4.6.4-0-g7ed95cf
git-executable = ${git:location}/bin/git
# test-dependent eggs that must come through in-tree recipes.
[ZODB]
depends += ${manuel:egg}
# bin/python is preinstalled with sys.path to ZODB & friends.
[ZODB-python]
<= python-interpreter
......@@ -110,9 +106,3 @@ inline =
env.sh = ${ZODB-env.sh:output}
workdir = ${buildout:directory}
[versions]
random2 = 1.0.1
zope.testing = 4.7
zope.testrunner = 5.2
zope.exceptions = 4.4
......@@ -6,12 +6,12 @@ parts =
# Contains libasound
recipe = slapos.recipe.cmmi
shared = true
url = ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.3.tar.bz2
md5sum = eefe5992567ba00d6110a540657aaf5c
url = https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.10.tar.bz2
md5sum = aced5acdb6161ed86e5ca3bb10618ca1
configure-options =
--disable-static
--disable-aload
--disable-rawmidi
--enable-rawmidi
--disable-ucm
--disable-alisp
--disable-old-symbols
......
......@@ -24,11 +24,15 @@ extends =
../icu/buildout.cfg
../openssl/buildout.cfg
../libnsl/buildout.cfg
../libsodium/buildout.cfg
../sqlite3/buildout.cfg
../oniguruma/buildout.cfg
../xz-utils/buildout.cfg
[php-redis]
recipe = slapos.recipe.cmmi
url = https://github.com/phpredis/phpredis/archive/5.0.0.tar.gz
md5sum = 4f11e0567a10c29394aae52a4fa8bb40
url = https://github.com/phpredis/phpredis/archive/refs/tags/6.0.2.tar.gz
md5sum = 3eaabf5871b50d1bbf9d00f64f33e7c2
configure-command =
phpize && ./configure
environment =
......@@ -36,12 +40,10 @@ environment =
[php-imagick]
recipe = slapos.recipe.cmmi
url = https://github.com/Imagick/imagick/archive/3.4.4.tar.gz
md5sum = ef6cbadd834eb306bd91874a8f5dea03
url = https://github.com/Imagick/imagick/archive/refs/tags/3.7.0.tar.gz
md5sum = 4dbe07a2ef15d3c9e4c54d5685bdfbc9
configure-command =
phpize && ./configure
configure-options =
--prefix=${buildout:parts-directory}/${:_buildout_section_name_}
environment =
PKG_CONFIG_PATH=${imagemagick:location}/lib/pkgconfig
PATH=${pkgconfig:location}/bin:${imagemagick:location}/bin:${autoconf:location}/bin:${automake:location}/bin:${m4:location}/bin:${apache-php:location}/bin:%(PATH)s
......@@ -49,8 +51,8 @@ environment =
[php-apcu]
recipe = slapos.recipe.cmmi
url = https://github.com/krakjoe/apcu/archive/v5.1.17.tar.gz
md5sum = f64b6cd5108aea63df2d5cc301c58b2b
url = https://github.com/krakjoe/apcu/archive/refs/tags/v5.1.24.tar.gz
md5sum = cf4b56ae18e5b031acbc486880dd880b
configure-command =
phpize && ./configure
configure-options =
......@@ -65,65 +67,47 @@ shared = false
[apache-php]
recipe = slapos.recipe.cmmi
url = https://www.php.net/distributions/php-7.3.6.tar.bz2
md5sum = bde9a912fb311182cd460dad1abbc247
url = https://www.php.net/distributions/php-8.2.24.tar.xz
md5sum = fff29ce84f5b4ddfc2063f7b2021fce2
configure-options =
--prefix=${buildout:parts-directory}/${:_buildout_section_name_}
--disable-static
--disable-zend-test
--enable-exif
--enable-ftp
--enable-gd
--enable-intl
--enable-mbstring
--enable-pcntl
--enable-session
--enable-sysvsem
--with-apxs2=${apache:location}/bin/apxs
--with-libxml-dir=${libxml2:location}
--with-mysql=${mariadb:location}
--with-zlib-dir=${zlib:location}
--with-bz2=${bzip2:location}
--with-mcrypt=${libmcrypt:location}
--with-gd
--with-jpeg-dir=${libjpeg:location}
--with-png-dir=${libpng:location}
--enable-gd-native-ttf
--with-freetype-dir=${freetype:location}
--with-pdo-mysql=mysqlnd
--with-mysqli=mysqlnd
--with-curl=${curl:location}
--with-imap=${cclient:location}
--with-iconv-dir=${libiconv:location}
--with-curl
--with-freetype
--with-jpeg
--with-gettext=${gettext:location}
--with-ldap=${openldap:location}
--with-imap-ssl
--with-imap=${cclient:location}
--with-ldap=${openldap:location}
--with-mysqli=mysqlnd
--with-openssl=${openssl:location}
--with-libzip=${libzip:location}
--with-icu-dir=${icu:location}
--with-password-argon2=${argon2:location}
--enable-apcu-bc
--enable-intl
--enable-libxml
--enable-json
--enable-mbstring
--enable-pcntl
--enable-session
--enable-exif
--enable-ftp
--enable-zip
--disable-zend-test
--disable-static
--with-pdo-mysql=mysqlnd
--with-sodium=${libsodium:location}
--with-zip
--with-zlib
# Changing TMPDIR is required for PEAR installation.
# It will create a pear/temp directory under the SR instead of a shared /tmp/pear/temp.
# XXX we could mkdir tmp there
environment =
PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig
PATH=${pkgconfig:location}/bin:${bzip2:location}/bin:${libxml2:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig:${sqlite3:location}/lib/pkgconfig:${curl:location}/lib/pkgconfig:${icu:location}/lib/pkgconfig:${oniguruma:location}/lib/pkgconfig:${argon2:location}/lib/pkgconfig:${zlib:location}/lib/pkgconfig:${mariadb:location}/lib/pkgconfig:${libjpeg:location}/lib/pkgconfig:${libpng:location}/lib/pkgconfig:${freetype:location}/lib/pkgconfig:${libiconv:location}/lib/pkgconfig:${libzip:location}/lib/pkgconfig:${libsodium:location}/lib/pkgconfig
PATH=${pkgconfig:location}/bin:${bzip2:location}/bin:${libxml2:location}/bin:${xz-utils:location}/bin:%(PATH)s
CPPFLAGS=-I${libzip:location}/include
LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath -Wl,${bzip2:location}/lib -Wl,-rpath -Wl,${curl:location}/lib -L${libtool:location}/lib -Wl,-rpath -Wl,${libtool:location}/lib -L${mariadb:location}/lib -Wl,-rpath -Wl,${mariadb:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -L${libmcrypt:location}/lib -Wl,-rpath -Wl,${libmcrypt:location}/libblkid -L${libzip:location}/lib -Wl,-rpath -Wl,${libzip:location}/lib -L${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${zstd:location}/lib -L${libnsl:location}/lib -Wl,-rpath -Wl,${libnsl:location}/lib
TMPDIR=${buildout:parts-directory}/${:_buildout_section_name_}
LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath -Wl,${bzip2:location}/lib -Wl,-rpath -Wl,${curl:location}/lib -L${libtool:location}/lib -Wl,-rpath -Wl,${libtool:location}/lib -L${mariadb:location}/lib -Wl,-rpath -Wl,${mariadb:location}/lib -L${zlib:location}/lib -Wl,-rpath -Wl,${zlib:location}/lib -L${libzip:location}/lib -Wl,-rpath -Wl,${libzip:location}/lib -L${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${argon2:location}/lib/x86_64-linux-gnu -Wl,-rpath -Wl,${zstd:location}/lib -L${libnsl:location}/lib -Wl,-rpath -Wl,${libnsl:location}/lib -L${sqlite3:location}/lib -Wl,-rpath -Wl,${sqlite3:location}/lib
TMPDIR=@@LOCATION@@
HOME=${apache:location}
[libmcrypt]
recipe = slapos.recipe.cmmi
url = http://downloads.sourceforge.net/project/mcrypt/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.bz2
md5sum = c4f491dd411a09e9de3b8702ea6f73eb
[xml-rpc]
recipe = slapos.recipe.cmmi
url = http://downloads.sourceforge.net/project/phpxmlrpc/phpxmlrpc/2.2.2/xmlrpc-2.2.2.tar.gz
url = https://github.com/gggeek/phpxmlrpc/releases/download/2.2.2/xmlrpc-2.2.2.tar.gz
md5sum = 59a644c636c6d98267d0c99b406ae9e8
......@@ -22,26 +22,31 @@ extends =
[apr]
recipe = slapos.recipe.cmmi
shared = true
version = 1.7.0
md5sum = 7a14a83d664e87599ea25ff4432e48a7
version = 1.7.4
md5sum = f8a62f3984898ba0ea8b6f26b851cb99
url = https://archive.apache.org/dist/apr/apr-${:version}.tar.bz2
environment =
LDFLAGS=-Wl,-rpath=${libuuid:location}/lib
[apr-util]
recipe = slapos.recipe.cmmi
shared = true
version = 1.6.1
version = 1.6.3
url = https://archive.apache.org/dist/apr/apr-util-${:version}.tar.bz2
md5sum = 8ff5dc36fa39a2a3db1df196d3ed6086
md5sum = b6e8c9b31d938fe5797ceb0d1ff2eb69
configure-options =
--with-apr=${apr:location}
--with-expat=${libexpat:location}
--without-sqlite3
environment =
LDFLAGS=-Wl,-rpath=${libuuid:location}/lib
[apache]
recipe = slapos.recipe.cmmi
shared = true
version = 2.4.54
version = 2.4.62
url = https://archive.apache.org/dist/httpd/httpd-${:version}.tar.bz2
md5sum = 861b43073ab416d689f1fc4dfa087711
md5sum = cded7afa23c13c4854008d95a69ce016
configure-options = --disable-static
--enable-authn-alias
--enable-bucketeer
......@@ -97,8 +102,8 @@ configure-options = --disable-static
environment =
PATH=${perl:location}/bin:${pkgconfig:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${openssl:location}/lib/pkgconfig
CPPFLAGS =-I${libuuid:location}/include -I${openssl:location}/include -I${apr:location}/include -I${apr-util:location}/include
LDFLAGS =-Wl,-rpath=${zlib:location}/lib -Wl,-rpath=${openssl:location}/lib -L${libuuid:location}/lib -Wl,-rpath=${libuuid:location}/lib -L${libxml2:location}/lib -Wl,-rpath=${libxml2:location}/lib -Wl,-rpath=${pcre:location}/lib -Wl,-rpath=${sqlite3:location}/lib -Wl,-rpath=${gdbm:location}/lib -L${apr:location}/lib -Wl,-rpath=${apr:location}/lib -L${apr-util:location}/lib -Wl,-rpath=${apr-util:location}/lib -L${libexpat:location}/lib -Wl,-rpath=${libexpat:location}/lib
CPPFLAGS=-I${libuuid:location}/include -I${openssl:location}/include -I${apr:location}/include -I${apr-util:location}/include
LDFLAGS=-Wl,-rpath=${zlib:location}/lib -Wl,-rpath=${openssl:location}/lib -L${libuuid:location}/lib -Wl,-rpath=${libuuid:location}/lib -L${libxml2:location}/lib -Wl,-rpath=${libxml2:location}/lib -Wl,-rpath=${pcre:location}/lib -Wl,-rpath=${sqlite3:location}/lib -Wl,-rpath=${gdbm:location}/lib -L${apr:location}/lib -Wl,-rpath=${apr:location}/lib -L${apr-util:location}/lib -Wl,-rpath=${apr-util:location}/lib -L${libexpat:location}/lib -Wl,-rpath=${libexpat:location}/lib
[apache-antiloris]
# Note: This component tries to write in a [apache] parts, which is now
......
......@@ -31,8 +31,10 @@ post-make-hook = ${:_profile_base_location_}/${aspell-create-wrapper:filename}#$
[aspell]
recipe = slapos.recipe.cmmi
shared = true
url = https://ftp.gnu.org/gnu/aspell/aspell-0.60.7.tar.gz
md5sum = 8ef2252609c511cd2bb26f3a3932ef28
url = https://ftp.gnu.org/gnu/aspell/aspell-0.60.8.1.tar.gz
md5sum = 187bd142f522ada555c7aa6b9cbf56e6
configure-options =
--enable-curses="-lncursesw -ltinfow"
environment =
PATH=${patch:location}/bin:${perl:location}/bin:%(PATH)s
CPPFLAGS=-I${ncurses:location}/include
......
[buildout]
extends = ../gnu-config/buildout.cfg
parts = attr
[attr]
......@@ -7,6 +7,7 @@ recipe = slapos.recipe.cmmi
shared = true
url = http://download.savannah.gnu.org/releases/attr/attr-2.4.47.src.tar.gz
md5sum = 84f58dec00b60f2dc8fd1c9709291cc7
pre-configure = cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess .
configure-options =
--enable-shared=yes
--enable-gettext=no
......
# Avahi - Service Discovery for Linux using mDNS/DNS-SD -- compatible with Bonjour
[buildout]
extends =
../dbus/buildout.cfg
../glib/buildout.cfg
../pkgconfig/buildout.cfg
parts =
avahi
[avahi]
recipe = slapos.recipe.cmmi
shared = true
url = https://github.com/lathiat/avahi/releases/download/v0.8/avahi-0.8.tar.gz
md5sum = 229c6aa30674fc43c202b22c5f8c2be7
configure-options =
--disable-static
--disable-libevent
--disable-mono
--disable-monodoc
--disable-python
--disable-qt3
--disable-qt4
--disable-qt5
--disable-gtk
--disable-gtk3
--disable-libdaemon
--disable-core-docs
--with-distro=none
--with-systemdsystemunitdir=no
environment =
PATH=${pkgconfig:location}/bin:${glib:location}/bin:%(PATH)s
CFLAGS=-I${gdbm:location}/include -I${libexpat:location}/include
LDFLAGS=-L${gettext:location}/lib -lintl -Wl,-rpath=${gettext:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib -L${glib:location}/lib -Wl,-rpath=${glib:location}/lib -L${gdbm:location}/lib -Wl,-rpath=${gdbm:location}/lib -L${libexpat:location}/lib -Wl,-rpath=${libexpat:location}/lib
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${glib:pkg_config_depends}:${dbus:location}/lib/pkgconfig:${libexpat:location}/lib/pkgconfig
[buildout]
extends =
../git/buildout.cfg
parts = babeld
[babeld-repository]
......@@ -6,14 +9,17 @@ recipe = slapos.recipe.build:gitclone
repository = https://lab.nexedi.com/nexedi/babeld.git
branch = master
git-executable = ${git:location}/bin/git
revision = hmac-nxd3
revision = v1.12.1-nxd3
[babeld]
recipe = slapos.recipe.cmmi
path = ${babeld-repository:location}
make-options = CC='gcc -std=gnu99'
# Fedora's redhat-hardened-ld forces us to use either -fPIC or -fPIE
make-options = CC='gcc -std=gnu99 -fPIE'
configure-command =
echo "No configure.."
environment =
PATH=${git:location}/bin:%(PATH)s
make-targets =
install PREFIX=${buildout:parts-directory}/${:_buildout_section_name_}
......@@ -27,8 +27,6 @@ patches =
${:patch-prefix}-010#d78ad19986c0355a8d67c9a0e82ad4aa
${:patch-prefix}-011#2416386b5ee94e499ccbf71f6fd4aebd
${:patch-prefix}-012#879b2d8a03162faebb7234c4cd57c5cd
configure-options =
--with-curses
environment =
CPPFLAGS=-I${ncurses:location}/include
LDFLAGS=-L${ncurses:location}/lib -Wl,-rpath=${ncurses:location}/lib
......
[buildout]
extends =
../bison/buildout.cfg
../gettext/buildout.cfg
../gmp/buildout.cfg
../gnu-config/buildout.cfg
../m4/buildout.cfg
../texinfo/buildout.cfg
parts = binutils
......@@ -13,7 +18,7 @@ configure-options =
--with-gmp=${gmp:location}
--disable-static
environment =
PATH=${xz-utils:location}/bin:%(PATH)s
PATH=${xz-utils:location}/bin:${bison:location}/bin:%(PATH)s
LDFLAGS=-Wl,-rpath=${gmp:location}/lib
[mpc]
......@@ -21,6 +26,8 @@ recipe = slapos.recipe.cmmi
shared = true
url = http://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz
md5sum = 4125404e41e482ec68282a2e687f6c73
pre-configure =
cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess .
configure-options =
--with-gmp=${gmp:location}
--with-mpfr=${mpfr:location}
......@@ -33,6 +40,8 @@ recipe = slapos.recipe.cmmi
shared = true
url = ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2
md5sum = 11436d6b205e516635b666090b94ab32
pre-configure =
cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess .
configure-options =
--with-gmp-prefix=${gmp:location}
--disable-static
......@@ -42,11 +51,15 @@ environment =
[binutils]
recipe = slapos.recipe.cmmi
shared = true
url = http://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.bz2
md5sum = 64f8ea283e571200f8b2b7f66fe8a0d6
url = https://ftp.gnu.org/gnu/binutils/binutils-2.42.tar.xz
md5sum = a075178a9646551379bfb64040487715
configure-options =
--disable-bootstrap
--with-mpc=${mpc:location}
--with-mpfr=${mpfr:location}
--with-gmp=${gmp:location}
--with-isl=${isl:location}
environment =
LDFLAGS=-L${gettext:location}/lib -lintl -Wl,-rpath=${gettext:location}/lib -Wl,-rpath=${zlib:location}/lib
PATH=${texinfo7:location}/bin:${bison:location}/bin:${m4:location}/bin:%(PATH)s
BISON_PKGDATADIR=${bison:location}/share/bison
......@@ -9,8 +9,8 @@ extends =
[boost-lib]
recipe = slapos.recipe.cmmi
shared = true
url = https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2
md5sum = db0112a3a37a3742326471d20f1a186a
url = https://boostorg.jfrog.io/artifactory/main/release/1.85.0/source/boost_1_85_0.tar.bz2
md5sum = 429d451cb9197143cc77962c5ff272ef
location = @@LOCATION@@
configure-command = ./bootstrap.sh --prefix=${:location} --without-icu $${PYTHON:+--with-python=$PYTHON}
make-binary =
......
......@@ -2,6 +2,7 @@
extends =
../autoconf/buildout.cfg
../automake/buildout.cfg
../xz-utils/buildout.cfg
parts =
brctl
......@@ -12,7 +13,7 @@ recipe = slapos.recipe.cmmi
url = https://mirrors.edge.kernel.org/pub/linux/utils/net/bridge-utils/bridge-utils-1.7.1.tar.xz
md5sum = 3e1fee4dc22cac5457c2f6ffb990a518
environment =
PATH=${autoconf:location}/bin:${automake:location}/bin:%(PATH)s
PATH=${autoconf:location}/bin:${automake:location}/bin:${xz-utils:location}/bin:%(PATH)s
pre-configure =
aclocal &&
autoconf
[buildout]
extends =
../patch/buildout.cfg
parts = busybox
[busybox]
recipe = slapos.recipe.build
url = http://git.busybox.net/busybox/snapshot/busybox-1_20_2.tar.gz
md5sum = 025acebb48040ef62dd635d416d317e8
patches =
${:_profile_base_location_}/busybox-1_20_2.patch#292498db86c46e101bb14bf2c74c36f0
install =
env = self.environ
extract_dir = self.extract(self.download(options['url'], options['md5sum']))
workdir = guessworkdir(extract_dir)
self.applyPatchList(options.get('patches'), '-p1', cwd=workdir)
self.logger.info("Creating default configuration")
call(['make', 'defconfig'], cwd=workdir, env=env)
self.logger.info("Building")
call(['make'], cwd=workdir, env=env)
self.logger.info("Installing")
call(['make', 'CONFIG_PREFIX=' + location, 'install'], cwd=workdir, env=env)
self.logger.info("Installation finished")
environment =
PATH=${patch:location}/bin:%(PATH)s
diff --git a/include/libbb.h b/include/libbb.h
index f12800f..e7806c2 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -40,6 +40,7 @@
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
......@@ -11,4 +11,4 @@ configure-command = true
make-options =
PREFIX=@@LOCATION@@
CFLAGS="-fpic -fPIC -Wall -Winline -O2 -g -D_FILE_OFFSET_BITS=64"
post-make-hook = ${:_profile_base_location_}/bzip2-hooks.py#785148a77f1a8456d914aa02ddf9d410:post_make_hook
post-make-hook = ${:_profile_base_location_}/bzip2-hooks.py#a02da93bc8ff337a1d590f265e220528:post_make_hook
......@@ -13,6 +13,7 @@ def post_make_hook(options, buildout):
os.unlink(f)
shutil.copyfile(os.path.join(os.curdir, original), os.path.join(destination,
original))
os.chmod(os.path.join(destination, original), 0o555)
for link in link_list:
os.symlink(original, os.path.join(destination,
......
......@@ -12,14 +12,17 @@ parts =
[ca-certificates]
recipe = slapos.recipe.cmmi
shared = true
url = http://deb.debian.org/debian/pool/main/c/ca-certificates/ca-certificates_20210119.tar.xz
md5sum = c02582bf9ae338e558617291897615eb
url = https://deb.debian.org/debian/pool/main/c/ca-certificates/ca-certificates_20230311.tar.xz
md5sum = fc1c3ec0067385f0be8ac7f6e670a0f8
patch-binary = ${patch:location}/bin/patch
patches =
${:_profile_base_location_}/ca-certificates-any-python.patch#c13b44dfc3157dda13a9a2ff97a9d501
${:_profile_base_location_}/ca-certificates-sbin-dir.patch#0b4e7d82ce768823c01954ee41ef177b
${:_profile_base_location_}/ca-certificates-any-python.patch#56ecfeb8f23ae00726191a611d08894e
${:_profile_base_location_}/ca-certificates-mkdir-p.patch#02ed8a6d60c39c4b088657888af345ef
${:_profile_base_location_}/ca-certificates-no-cryptography.patch#14ad1308623b0d15420906ae3d9b4867
patch-options = -p0
configure-command = true
make-targets = install DESTDIR=@@LOCATION@@ CERTSDIR=certs SBINDIR=sbin
make-targets = install DESTDIR=@@LOCATION@@ CERTSDIR=certs SBINDIR=/sbin
environment =
PATH=${xz-utils:location}/bin:%(PATH)s
pre-make-hook =
${:_profile_base_location_}/ca-certificates-pre-make-hook.py#89d64d612e143638b7b264be4b129798:pre_make_hook
......@@ -9,15 +9,3 @@
in_multiline = False
continue
if line.startswith('CKA_CLASS'):
--- mozilla/Makefile 2015-12-20 10:49:23.000000000 +0100
+++ mozilla/Makefile 2016-01-05 20:19:11.006874271 +0100
@@ -3,7 +3,8 @@
#
all:
- python3 certdata2pem.py
+ for x in 3 '' 2; do type python$$x && break; done >/dev/null \
+ && python$$x certdata2pem.py
clean:
-rm -f *.crt
--- Makefile.orig 2011-12-11 20:54:02.000000000 +0100
+++ Makefile 2012-01-09 17:36:55.059392824 +0100
@@ -17,7 +17,7 @@
install:
for dir in $(SUBDIRS); do \
- mkdir $(DESTDIR)/$(CERTSDIR)/$$dir; \
+ mkdir -p $(DESTDIR)/$(CERTSDIR)/$$dir; \
$(MAKE) -C $$dir install CERTSDIR=$(DESTDIR)/$(CERTSDIR)/$$dir; \
done
for dir in sbin; do \
Don't depend on cryptography
Revert https://salsa.debian.org/debian/ca-certificates/-/commit/8033d52259172b4bddc0f8bbcb6f6566b348db72
we don't need this here.
--- mozilla/certdata2pem.py 2023-01-14 22:58:27.000000000 +0900
+++ mozilla/certdata2pem.py 2023-10-02 22:13:31.355540545 +0900
@@ -21,15 +21,12 @@
# USA.
import base64
-import datetime
import os.path
import re
import sys
import textwrap
import io
-from cryptography import x509
-
objects = []
@@ -122,12 +119,6 @@
if not obj['CKA_LABEL'] in trust or not trust[obj['CKA_LABEL']]:
continue
- cert = x509.load_der_x509_certificate(bytes(obj['CKA_VALUE']))
- if cert.not_valid_after < datetime.datetime.utcnow():
- print('!'*74)
- print('Trusted but expired certificate found: %s' % obj['CKA_LABEL'])
- print('!'*74)
-
bname = obj['CKA_LABEL'][1:-1].replace('/', '_')\
.replace(' ', '_')\
.replace('(', '=')\
import sys
# Because ca-certificate is used very early in the bootstrap process,
# even before python is built, we can not use the software release python
# yet, because it would loop forever in slapos.rebootstrap.
# By using sys.executable in a hook like this, we can use python without
# buildout recording a dependency to python in the part options.
def pre_make_hook(options, buildout, environ):
with open('mozilla/Makefile') as f:
makefile = f.read()
makefile = makefile.replace('python3 certdata2pem.py', '%s certdata2pem.py' % sys.executable)
with open('mozilla/Makefile', 'w') as f:
f.write(makefile)
--- Makefile.orig 2011-12-11 20:54:02.000000000 +0100
+++ Makefile 2012-01-09 17:36:55.059392824 +0100
@@ -17,7 +17,7 @@
install:
for dir in $(SUBDIRS); do \
- mkdir $(DESTDIR)/$(CERTSDIR)/$$dir; \
+ mkdir -p $(DESTDIR)/$(CERTSDIR)/$$dir; \
$(MAKE) -C $$dir install CERTSDIR=$(DESTDIR)/$(CERTSDIR)/$$dir; \
done
for dir in sbin; do \
--- sbin/Makefile.orig 2011-12-11 20:54:02.000000000 +0100
+++ sbin/Makefile 2012-01-09 17:31:45.207387898 +0100
@@ -3,9 +3,12 @@
#
#
+SBINDIR=/usr/sbin
+
all:
clean:
install:
- install -m755 update-ca-certificates $(DESTDIR)/usr/sbin/
+ mkdir -p $(DESTDIR)/$(SBINDIR)
+ install -m755 update-ca-certificates $(DESTDIR)/$(SBINDIR)
[buildout]
extends =
../../component/golang/buildout.cfg
parts =
gowork
caddy
[caddy-get]
<= go-git-package
go.importpath = github.com/caddyserver/caddy
repository = https://lab.nexedi.com/nexedi/caddy.git
revision = nxd-v1.0.3-1-03fba31bf
[gowork]
golang = ${golang1.17:location}
install =
${caddy-get:location}:./...
[caddy]
recipe = plone.recipe.command
command = exit 0
update-command = ${:command}
output = ${gowork:bin}/caddy
location = ${:output}
......@@ -35,6 +35,11 @@ recipe = slapos.recipe.build
shared = true
pyyaml = ${pyyaml-download:target}
pyaml = ${pyaml-download:target}
init =
# add the python executable in the options dict so that
# buildout signature changes if python executable changes
import sys
options['python-executable'] = sys.executable
install =
import os, sys
......@@ -50,8 +55,8 @@ install =
# CEPH only for librbd
recipe = slapos.recipe.cmmi
shared = true
url = https://download.ceph.com/tarballs/ceph_17.1.0.orig.tar.gz
md5sum = 1f2ed3f17f76ea71d555428f26644610
url = https://download.ceph.com/tarballs/ceph-17.2.6.tar.gz
md5sum = aba03b7caacc590295b09829e0d4f088
# Note: SlapOSifying whole ceph, including it's managers, is an enormous effort
# so only "librbd" is provided with header files and this requires
......
......@@ -5,21 +5,24 @@
[buildout]
extends =
../chromium/buildout.cfg
../nss/buildout.cfg
../glib/buildout.cfg
../nspr/buildout.cfg
../nss/buildout.cfg
../pcre2/buildout.cfg
../xorg/buildout.cfg
parts =
chromedriver-wrapper
[chromedriver-wrapper-120]
<= chromedriver-wrapper
part = chromedriver-120
[chromedriver-wrapper-91]
<= chromedriver-wrapper
part = chromedriver-91
[chromedriver-wrapper-2.41]
<= chromedriver-wrapper
part = chromedriver-2.41
[chromedriver-wrapper]
# generate a wrapper named ${:wrapper-name} setting $LD_LIBRARY_PATH
......@@ -42,30 +45,36 @@ install =
[chromedriver]
<= chromedriver-91
<= chromedriver-120
[chromedriver-2.41]
[chromedriver-120]
<= chromedriver-download
version = 2.41
# Supports Chrome v67-69
md5sum-x86_64 = fbd8b9561575054e0e7e9cc53b680a70
version = 120.0.6099.109
revision-x86_64 = 1217362
generation-x86_64 = 1698717838856458
md5sum-x86_64 = 5cb8d386f01052cfc58c80ec63477db0
[chromedriver-91]
<= chromedriver-download
url = https://chromedriver.storage.googleapis.com/${:version}/chromedriver_linux64.zip
version = 91.0.4472.101
# Supports Chrome v91
md5sum-x86_64 = cc43ba0babbfff7f22b48165ec8e8c81
[chromedriver-download]
# Installs chromedriver ${version}.
# This chromedriver is not usable directly, it needs a wrapper.
recipe = slapos.recipe.build:download-unpacked
url = https://chromedriver.storage.googleapis.com/${:version}/chromedriver_${:_url}.zip
library =
${nss:location}/lib
${nspr:location}/lib
${glib:location}/lib
${libX11:location}/lib
${libXau:location}/lib
${libxcb:location}/lib
${libXdmcp:location}/lib
${nspr:location}/lib
${nss:location}/lib
${pcre2:location}/lib
[chromedriver-download:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu']
_url = linux64
[chromedriver-download:linux and platform.machine() == "x86_64"]
url = https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${:revision-x86_64}%2Fchromedriver_linux64.zip?generation=${:generation-x86_64}&alt=media
md5sum = ${:md5sum-x86_64}
......@@ -17,8 +17,7 @@ extends =
../fontconfig/buildout.cfg
../gettext/buildout.cfg
../glib/buildout.cfg
../gtk-2/buildout.cfg
../gtk-3/buildout.cfg
../gtk/buildout.cfg
../libexpat/buildout.cfg
../libffi/buildout.cfg
../libpng/buildout.cfg
......@@ -65,36 +64,35 @@ install =
))
os.fchmod(f.fileno(), 0o755)
[chromium-wrapper-120]
<= chromium-wrapper
part = chromium-120
[chromium-wrapper-91]
<= chromium-wrapper
part = chromium-91
[chromium-wrapper-69]
<= chromium-wrapper
part = chromium-69
[chromium]
<= chromium-120
[chromium-120]
<= chromium-download
version = 120.0.6099.109
revision-x86_64 = 1217362
md5sum-x86_64 = 86719e40f3d33f1b421d073bb4a71f41
generation-x86_64 = 1698717835110888
[chromium]
<= chromium-91
[chromium-91]
<= chromium-download
version = 91.0.4472.114
revision_x86-64 = 870763
revision-x86_64 = 870763
md5sum-x86_64 = 74eab41580469c2b8117cf396db823cb
generation-x86_64 = 1617926496067901
[chromium-69]
<= chromium-download
version = 69.0.3497.0
revision_x86-64 = 576753
md5sum-x86_64 = 08ac27fd40ace4ca8dfbd1db403deccb
generation-x86_64 = 1532051976706023
[chromium-download]
# macro to download a binary build of chromium and generate a
# wrapper as chrome-slapos in the part directory
......@@ -106,7 +104,6 @@ library =
${alsa:location}/lib
${cairo:location}/lib
${cups:location}/lib
${cups:location}/lib64
${dbus:location}/lib/
${dbus-glib:location}/lib/
${fontconfig:location}/lib/
......@@ -147,6 +144,6 @@ library =
path =
${fontconfig:location}/bin
[chromium-download:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu']
url = https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${:revision_x86-64}%2Fchrome-linux.zip?generation=${:generation-x86_64}&alt=media
[chromium-download:linux and platform.machine() == "x86_64"]
url = https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${:revision-x86_64}%2Fchrome-linux.zip?generation=${:generation-x86_64}&alt=media
md5sum = ${:md5sum-x86_64}
[buildout]
extends =
../gnu-config/buildout.cfg
parts =
chrpath
......@@ -6,3 +8,4 @@ parts =
recipe = slapos.recipe.cmmi
url = http://http.debian.net/debian/pool/main/c/chrpath/chrpath_0.16.orig.tar.gz
md5sum = 2bf8d1d1ee345fc8a7915576f5649982
pre-configure = cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess .
Disable clamdtop as it requires ncurses5, but ncurses5 doesn't build without an extra old gcc.
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f890da..5e51ca1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,6 +106,7 @@ endif()
set(ENABLE_DOXYGEN_DEFAULT OFF)
set(ENABLE_UNRAR_DEFAULT ON)
set(ENABLE_SYSTEMD_DEFAULT ON)
+set(ENABLE_CLAMDTOP_DEFAULT ON)
# See CMakeOptions.cmake for additional options.
include(CMakeOptions.cmake)
@@ -564,7 +565,9 @@ if(NOT ENABLE_LIBCLAMAV_ONLY)
endif()
if(ENABLE_APP)
- find_package(CURSES REQUIRED)
+ if(ENABLE_CLAMDTOP)
+ find_package(CURSES REQUIRED)
+ endif()
if(NOT WIN32 AND ENABLE_MILTER)
find_package(Milter REQUIRED)
@@ -1086,7 +1089,9 @@ if(NOT ENABLE_LIBCLAMAV_ONLY)
add_subdirectory( freshclam )
- add_subdirectory( clamdtop )
+ if(ENABLE_CLAMDTOP)
+ add_subdirectory( clamdtop )
+ endif()
if(WIN32)
add_subdirectory( win32/conf_examples )
diff --git a/CMakeOptions.cmake b/CMakeOptions.cmake
index e6e9b11..00b9db6 100644
--- a/CMakeOptions.cmake
+++ b/CMakeOptions.cmake
@@ -116,6 +116,10 @@ option(ENABLE_SYSTEMD
"Install systemd service files if systemd is found."
${ENABLE_SYSTEMD_DEFAULT})
+option(ENABLE_CLAMDTOP
+ "Compile clamdtop."
+ ${ENABLE_CLAMDTOP_DEFAULT})
+
# For reference determining target platform:
# Rust Targets: https://doc.rust-lang.org/nightly/rustc/platform-support.html
option(RUST_COMPILER_TARGET
[buildout]
extends =
../bzip2/buildout.cfg
../curl/buildout.cfg
../cmake/buildout.cfg
../json-c/buildout.cfg
../libxml2/buildout.cfg
../openssl/buildout.cfg
../patch/buildout.cfg
../pcre2/buildout.cfg
../rust/buildout.cfg
../zlib/buildout.cfg
parts =
clamav
[clamav]
recipe = slapos.recipe.cmmi
shared = true
url = https://www.clamav.net/downloads/production/clamav-1.3.1.tar.gz
md5sum = a2617a04a69433f9f7c86ede5dcc82c6
location = @@LOCATION@@
patch-options = -p1
patches =
${:_profile_base_location_}/add_cmake_enable_clamdtop_option.patch#79dad34211b89a37860bdf8388790b6e
configure-command = ${cmake:location}/bin/cmake
configure-options =
-DCMAKE_INSTALL_PREFIX=${:location}
-DCMAKE_C_FLAGS="${:CMAKE_CFLAGS}"
-DCMAKE_CXX_FLAGS="${:CMAKE_CFLAGS}"
-DCMAKE_INSTALL_RPATH=${:CMAKE_LIBRARY_PATH}
-DENABLE_MILTER=OFF
-DENABLE_CLAMONACC=OFF
-DENABLE_CLAMDTOP=OFF
-DENABLE_TESTS=OFF
environment=
CMAKE_PROGRAM_PATH=${cmake:location}/bin
CMAKE_INCLUDE_PATH=${openssl:location}/include:${bzip2:location}/include:${zlib:location}/include:${libxml2:location}/include:${pcre2:location}/include:${json-c:location}/include:${curl:location}/include
CMAKE_LIBRARY_PATH=${:CMAKE_LIBRARY_PATH}
PATH=${rustc:location}/bin:${patch:location}/bin:%(PATH)s
CMAKE_CFLAGS = -I${openssl:location}/include -I${bzip2:location}/include -I${zlib:location}/include -I${libxml2:location}/include -I${pcre2:location}/include -I${json-c:location}/include -I${curl:location}/include
CMAKE_LIBRARY_PATH = ${:location}/lib:${openssl:location}/lib:${bzip2:location}/lib:${zlib:location}/lib:${libxml2:location}/lib:${pcre2:location}/lib:${json-c:location}/lib:${curl:location}/lib
[buildout]
extends =
../git/buildout.cfg
../golang/buildout.cfg
parts =
clammit
[clammit-repository]
<= go-git-package
go.importpath = github.com/ifad/clammit
repository = https://github.com/ifad/clammit.git
revision = v0.8.1
[gowork]
install =
# We need a repository here, instead of a URL
# (github.com/ifad/clammit@v0.8.1), as the module
# definition in the go.mod is wrong (see
# https://github.com/ifad/clammit/pull/38)
${clammit-repository:location}:./...
[buildout]
extends =
../git/buildout.cfg
../lxml-python/buildout.cfg
parts =
......@@ -12,9 +13,8 @@ setup = ${cloudooo-repository:location}
[cloudooo-repository]
recipe = slapos.recipe.build:gitclone
repository = https://lab.nexedi.com/nexedi/cloudooo.git
branch = master
git-executable = ${git:location}/bin/git
revision = 78d15e091df750952998a6a23f49300a5a0faa53
revision = 2536160661791c7b4704f57a42fb181cb6633d7a
[cloudooo]
recipe = zc.recipe.egg
......
......@@ -9,8 +9,8 @@ parts =
[cmake]
recipe = slapos.recipe.cmmi
shared = true
url = https://cmake.org/files/v3.23/cmake-3.23.2.tar.gz
md5sum = ab3c9ed9578cdb496a252c6733989d78
url = https://cmake.org/files/v3.29/cmake-3.29.3.tar.gz
md5sum = cadecde55a73dcc9cf0c7e8c09dcdfff
environment =
CMAKE_INCLUDE_PATH=${ncurses:location}/include:${openssl:location}/include
CMAKE_LIBRARY_PATH=${ncurses:location}/lib:${openssl:location}/lib
......@@ -8,10 +8,10 @@ shared = true
version = 0.8.3
url = https://releases.hashicorp.com/consul/${:version}/consul_${:version}_${:_url}.zip
[consul:getattr(sys,'_multiarch',None)=='i386-linux-gnu']
[consul:linux and platform.machine() == "i386"]
_url = linux_386
md5sum = dfdc0eedd79baab7e6bc56c1582fd02e
[consul:getattr(sys,'_multiarch',None)=='x86_64-linux-gnu']
[consul:linux and platform.machine() == "x86_64"]
_url = linux_amd64
md5sum = d6bc0898ea37ae2198370a9e1978d1bb
[buildout]
extends =
../gmp/buildout.cfg
../patch/buildout.cfg
../perl/buildout.cfg
../xz-utils/buildout.cfg
parts =
......@@ -9,14 +10,23 @@ parts =
[coreutils]
recipe = slapos.recipe.cmmi
shared = true
url = https://ftp.gnu.org/gnu/coreutils/coreutils-9.0.tar.xz
md5sum = 0d79ae8a6124546e3b94171375e5e5d0
url = https://ftp.gnu.org/gnu/coreutils/coreutils-9.4.tar.xz
md5sum = 459e9546074db2834eefe5421f250025
configure-options =
--disable-libcap
--without-selinux
--prefix=@@LOCATION@@
--with-openssl=no
environment =
PATH=${perl:location}/bin:${xz-utils:location}/bin:%(PATH)s
PATH=${patch:location}/bin:${perl:location}/bin:${xz-utils:location}/bin:%(PATH)s
LDFLAGS=-Wl,--as-needed -L${gmp:location}/lib -Wl,-rpath=${gmp:location}/lib
patches =
https://github.com/coreutils/coreutils/commit/c4c5ed8f4e9cd55a12966d4f520e3a13101637d9.patch?full_index=1#5fc691542117b167b456daf222d2a6e5
patch-options = -p1
# disable year 2038 problem ONLY for 32 bit architectures
[coreutils:bits32]
configure-options += --disable-year2038
[coreutils-output]
# Shared binary location to ease migration
......
[buildout]
extends =
nss.cfg
parts =
corocosync
[corosync]
recipe = slapos.recipe.cmmi
url = ftp://ftp:downloads@corosync.org/downloads/corosync-1.3.1/corosync-1.3.1.tar.gz
md5sum = c58459a009a3a9d0b9c00e276a190d90
environment =
CPPFLAGS=-I${nspr:location}/include/nspr -I${nss:location}/include/nss
PKG_CONFIG_PATH=${nss:location}/lib/pkgconfig:${nspr:location}/lib/pkgconfig
LDFLAGS =-L${nspr:location}/lib -Wl,-rpath=${nspr:location}/lib -L${nss:location}/lib -Wl,-rpath=${nss:location}/lib -Wl,-rpath=${buildout:parts-directory}/${:_buildout_section_name_}/lib
......@@ -6,6 +6,7 @@ extends =
../pkgconfig/buildout.cfg
../json-c/buildout.cfg
../openssl/buildout.cfg
../xz-utils/buildout.cfg
[cryptsetup]
recipe = slapos.recipe.cmmi
......@@ -25,7 +26,7 @@ configure-options =
--disable-kernel_crypto
--disable-blkid
environment =
PATH=${pkgconfig:location}/bin:%(PATH)s
PATH=${pkgconfig:location}/bin:${xz-utils:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${json-c:location}/lib/pkgconfig:${openssl:location}/lib/pkgconfig
CFLAGS=-I${libuuid:location}/include -I${lvm2:location}/include -I${popt:location}/include -I${json-c:location}/include -I${openssl:location}/include
LDFLAGS=-L${libuuid:location}/lib -Wl,-rpath=${libuuid:location}/lib -L${lvm2:location}/lib -Wl,-rpath=${lvm2:location}/lib -L${popt:location}/lib -Wl,-rpath=${popt:location}/lib -L${json-c:location}/lib -Wl,-rpath=${json-c:location}/lib -L${openssl:location}/lib -Wl,-rpath=${openssl:location}/lib
......@@ -10,8 +10,9 @@ extends =
[cups]
recipe = slapos.recipe.cmmi
shared = true
url = http://www.cups.org/software/1.7.4/cups-1.7.4-source.tar.bz2
url = https://github.com/apple/cups/releases/download/release-1.7.4/cups-1.7.4-source.tar.bz2
md5sum = 1a2295c2b2d2f422db2e50f40ed2fb99
location = @@LOCATION@@
configure-options =
--disable-static
--disable-dbus
......@@ -25,6 +26,7 @@ configure-options =
--disable-default-shared
--disable-raw-printing
--disable-webif
--libdir=${:location}/lib
make-options =
libs
make-targets =
......
......@@ -9,7 +9,10 @@ extends =
../xz-utils/buildout.cfg
../zstd/buildout.cfg
../zlib/buildout.cfg
../libidn/buildout.cfg
../nghttp2/buildout.cfg
../ngtcp2/buildout.cfg
../nghttp3/buildout.cfg
../ca-certificates/buildout.cfg
parts =
curl
......@@ -17,8 +20,8 @@ parts =
[curl]
recipe = slapos.recipe.cmmi
shared = true
url = https://curl.se/download/curl-7.86.0.tar.xz
md5sum = 19a2165f37941a6f412afc924e750568
url = https://curl.se/download/curl-8.6.0.tar.xz
md5sum = 8f28f7e08c91cc679a45fccf66184fbc
configure-options =
--disable-static
--disable-ech
......@@ -36,10 +39,10 @@ configure-options =
--disable-manual
--enable-ipv6
--disable-sspi
--disable-alt-svc
${:ALT-SVC}
--with-zlib=${zlib:location}
--with-ssl=${openssl:location}
--with-ca-path=${openssl:location}/etc/ssl/certs
--with-ssl=${:OPENSSL}
--with-ca-path=${:OPENSSL}/etc/ssl/certs
--without-gnutls
--without-polarssl
--without-mbedtls
......@@ -50,18 +53,37 @@ configure-options =
--without-libssh2
--without-libssh
--without-librtmp
--without-libidn2
--with-libidn2=${libidn2:location}
--with-nghttp2=${nghttp2:location}
--without-ngtcp2
--without-nghttp3
--without-openssl-quic
--without-quiche
--without-zsh-functions-dir
--without-fish-functions-dir
--without-brotli
--with-zstd=${zstd:location}
--without-gssapi
${:WITH}
ALT-SVC = --disable-alt-svc
LDFLAGS =
OPENSSL = ${openssl:location}
PKG_CONFIG_PATH =
WITH =
environment =
PATH=${perl:location}/bin:${pkgconfig:location}/bin:${xz-utils:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${openssl:location}/lib/pkgconfig:${nghttp2:location}/lib/pkgconfig
LDFLAGS=-Wl,-rpath=${zlib:location}/lib -Wl,-rpath=${openssl:location}/lib -Wl,-rpath=${nghttp2:location}/lib -Wl,-rpath=${zstd:location}/lib
PKG_CONFIG_PATH=${:OPENSSL}/lib/pkgconfig:${nghttp2:location}/lib/pkgconfig:${libidn2:location}/lib/pkgconfig${:PKG_CONFIG_PATH}
LDFLAGS=-Wl,-rpath=${libidn2:location}/lib -Wl,-rpath=${zlib:location}/lib -Wl,-rpath=${:OPENSSL}/lib -Wl,-rpath=${nghttp2:location}/lib -Wl,-rpath=${zstd:location}/lib ${:LDFLAGS}
[curl-http3]
<= curl
ALT-SVC = --enable-alt-svc
LDFLAGS = -Wl,-rpath=${nghttp3:location}/lib -Wl,-rpath=${ngtcp2:location}/lib
OPENSSL = ${openssl-quictls:location}
PKG_CONFIG_PATH = :${nghttp3:location}/lib/pkgconfig:${ngtcp2:location}/lib/pkgconfig
WITH =
--with-nghttp3=${nghttp3:location}
--with-ngtcp2=${ngtcp2:location}
......@@ -6,4 +6,4 @@ recipe = zc.recipe.egg:custom
egg = cython
[versions]
cython = 0.29.24
cython = 0.29.36
......@@ -7,8 +7,8 @@ parts = dash-output
[dash]
recipe = slapos.recipe.cmmi
shared = true
url = http://gondor.apana.org.au/~herbert/dash/files/dash-0.5.11.tar.gz
md5sum = 027236e48b9202607b1418fee42c473e
url = http://gondor.apana.org.au/~herbert/dash/files/dash-0.5.12.tar.gz
md5sum = 57222b768b84003ea4b801e5d5e0e52b
configure-options =
--disable-static
--disable-fnmatch
......
[buildout]
extends =
../libxml2/buildout.cfg
../pcre2/buildout.cfg
../pkgconfig/buildout.cfg
../libexpat/buildout.cfg
../zlib/buildout.cfg
......@@ -27,13 +28,13 @@ environment =
[dbus-glib]
recipe = slapos.recipe.cmmi
shared = true
url = https://dbus.freedesktop.org/releases/dbus-glib/dbus-glib-0.110.tar.gz
md5sum = d7cebf1d69445cbd28b4983392145192
url = https://dbus.freedesktop.org/releases/dbus-glib/dbus-glib-0.112.tar.gz
md5sum = 021e6c8a288df02c227e4aafbf7e7527
configure-options =
--disable-static
--disable-gtk-doc-html
environment =
PATH=${pkgconfig:location}/bin:${glib:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${dbus:location}/lib/pkgconfig:${glib:location}/lib/pkgconfig:${pcre:location}/lib/pkgconfig
CPPFLAGS=-I${libexpat:location}/include
LDFLAGS=-L${libexpat:location}/lib -L${gettext:location}/lib -Wl,-rpath=${zlib:location}/lib
PKG_CONFIG_PATH=${dbus:location}/lib/pkgconfig:${glib:location}/lib/pkgconfig:${glib:pkg_config_depends}
CPPFLAGS=-I${libexpat:location}/include -I${gettext:location}/include
LDFLAGS=-L${libexpat:location}/lib -L${gettext:location}/lib -Wl,-rpath=${gettext:location}/lib -Wl,-rpath=${zlib:location}/lib -Wl,-rpath=${libffi:location}/lib -Wl,-rpath=${pcre2:location}/lib -Wl,-rpath=${glib:location}/lib
......@@ -8,12 +8,13 @@ parts = dcron-output
[dcron]
recipe = slapos.recipe.cmmi
shared = false
url = http://www.jimpryor.net/linux/releases/dcron-4.5.tar.gz
md5sum = 078833f3281f96944fc30392b1888326
url = https://github.com/ptchinster/dcron/archive/refs/tags/v4.6.zip
md5sum = d5bd12863547daf499187bc9b4e863a8
configure-command = true
patch-binary = ${patch:location}/bin/patch
patches =
${:_profile_base_location_}/noroot-no-globals.patch#623379916f48bd8292a28057c8bd30f7
${:_profile_base_location_}/noroot-no-globals.patch#cd8d0f6d1d2ee133d6341d1515832c0d
${:_profile_base_location_}/support_old_gcc.patch#4dc8c81b1d94474c3c15e0db7b5b60de
patch-options = -p1
make-options =
PREFIX=${buildout:parts-directory}/${:_buildout_section_name_}
......
--- dcron-4.5/chuser.c
+++ dcron-4.5/chuser.c
@@ -14,6 +14,7 @@
diff --git a/chuser.c b/chuser.c
index 0d79dbc..ccc3acc 100644
--- a/chuser.c
+++ b/chuser.c
@@ -11,6 +11,7 @@
Prototype int ChangeUser(const char *user, char *dochdir);
+#if 0
int
ChangeUser(const char *user, char *dochdir)
{
+#if 0
struct passwd *pas;
/*
@@ -57,5 +58,8 @@
@@ -57,5 +58,7 @@ ChangeUser(const char *user, char *dochdir)
}
}
return(pas->pw_uid);
+#else
+ return getpwnam(user);
+#endif
}
+#endif
--- dcron-4.5/crontab.c
+++ dcron-4.5/crontab.c
@@ -88,7 +88,7 @@
diff --git a/crontab.c b/crontab.c
index b29e0d2..06df70f 100644
--- a/crontab.c
+++ b/crontab.c
@@ -88,7 +88,7 @@ main(int ac, char **av)
break;
case 'c':
/* getopt guarantees optarg != 0 here */
......@@ -27,8 +30,8 @@
+ if (*optarg != 0) {
CDir = optarg;
} else {
printlogf(0, "-c option: superuser only");
@@ -316,9 +316,6 @@
printlogf(0, "-c option: superuser only\n");
@@ -318,9 +318,6 @@ GetReplaceStream(const char *user, const char *file)
close(filedes[0]);
......@@ -38,7 +41,7 @@
fd = open(file, O_RDONLY);
if (fd < 0) {
printlogf(0, "unable to open %s: %s", file, strerror(errno));
@@ -344,8 +341,6 @@
@@ -346,8 +343,6 @@ EditFile(const char *user, const char *file)
const char *ptr;
char visual[SMALL_BUFFER];
......@@ -47,9 +50,11 @@
if ((ptr = getenv("EDITOR")) == NULL || strlen(ptr) >= sizeof(visual))
if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) >= sizeof(visual))
ptr = PATH_VI;
--- dcron-4.5/job.c
+++ dcron-4.5/job.c
@@ -62,14 +62,6 @@
diff --git a/job.c b/job.c
index b5e02de..79650c2 100644
--- a/job.c
+++ b/job.c
@@ -63,14 +63,6 @@ RunJob(CronFile *file, CronLine *line)
* Change running state to the user in question
*/
......@@ -64,7 +69,7 @@
/* from this point we are unpriviledged */
if (DebugOpt)
@@ -295,14 +287,6 @@
@@ -296,14 +288,6 @@ EndJob(CronFile *file, CronLine *line, int exit_status)
* by the mailing and we already verified the mail file.
*/
......@@ -79,9 +84,11 @@
/* from this point we are unpriviledged */
/*
--- dcron-4.5/Makefile
+++ dcron-4.5/Makefile
@@ -3,7 +3,6 @@
diff --git a/Makefile b/Makefile
index 547febc..13829e6 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,6 @@ VERSION = 4.5
# these variables can be configured by e.g. `make SCRONTABS=/different/path`
PREFIX = /usr/local
......@@ -89,7 +96,7 @@
SCRONTABS = /etc/cron.d
CRONTABS = /var/spool/cron/crontabs
CRONSTAMPS = /var/spool/cron/cronstamps
@@ -20,10 +19,10 @@
@@ -20,10 +19,10 @@ MANDIR = $(PREFIX)/share/man
SHELL = /bin/sh
......@@ -101,9 +108,9 @@
+INSTALL_DATA = $(INSTALL) -D -m0644
+INSTALL_DIR = $(INSTALL) -d -m0755
CFLAGS ?= -O2
CFLAGS += -Wall -Wstrict-prototypes -Wno-missing-field-initializers
SRCS = main.c subs.c database.c job.c concat.c chuser.c
@@ -45,7 +44,6 @@
CFLAGS += -Wall -Wextra -Wstrict-prototypes -Wno-missing-field-initializers -Wfloat-equal -fstack-protector-all -Wformat-security -Wformat=2 -fPIE
CFLAGS += -Wl,-z,nodump -Wl,-z,noexecstack -Wl,-z,noexecheap -Wl,-z,relro -Wl,-z,now -Wl,-z,nodlopen -Wl,-z,-pie
@@ -47,7 +46,6 @@ all: $(PROTOS) crond crontab ;
echo "SBINDIR = $(SBINDIR)" >> config
echo "BINDIR = $(BINDIR)" >> config
echo "MANDIR = $(MANDIR)" >> config
......@@ -111,7 +118,7 @@
echo "SCRONTABS = $(SCRONTABS)" >> config
echo "CRONTABS = $(CRONTABS)" >> config
echo "CRONSTAMPS = $(CRONSTAMPS)" >> config
@@ -63,13 +61,10 @@
@@ -65,13 +63,10 @@ crontab: $(TABOBJS)
$(CC) $(CFLAGS) $(CPPFLAGS) -c $(DEFS) $< -o $@
install:
......
diff --git a/main.c b/main.c
diff --git a/main.c b/main.c
index dcf3f23..3eb6332 100644
--- a/main.c
+++ b/main.c
@@ -130,7 +130,7 @@ main(int ac, char **av)
case 'd':
DebugOpt = 1;
LogLevel = LOG_DEBUG;
- [[fallthrough]]; //fall through to include f too
+ __attribute__((fallthrough)); //fall through to include f too
case 'f':
ForegroundOpt = 1;
break;
......@@ -17,6 +17,7 @@ init =
python = self.buildout[options['part']]
for x in 'location', 'executable', 'version':
options[x] = python[x]
options.barrier()
update =
import os
path, os.environ['PYTHON'] = os.path.split(options['executable'])
......@@ -34,7 +35,7 @@ depends =
${patch:recipe}
recipe = slapos.recipe.build
# Latest version provided by SlapOS.
part = gcc-8.5
part = gcc-10.5
# Minimum version for all components that might be required for
# slapos.rebootstrap (see https://bugs.python.org/issue34112 about Python 3.7+).
min_version = 5.4
......@@ -65,7 +66,6 @@ init =
break
else:
options['prefix'] = self.buildout[options['part']]['location']
options.barrier()
update =
if 'part' in options:
import os
......
......@@ -7,5 +7,5 @@ parts =
[depot_tools]
recipe = slapos.recipe.build:gitclone
repository = https://chromium.googlesource.com/chromium/tools/depot_tools.git
revsion = e023d4482012d89690f6a483e877eceb47c4501e
revision = eb48a6ac0fa5835353ddd137ac35f44eee011716
git-executable = ${git:location}/bin/git
# Dnsmasq provides network infrastructure for small networks: DNS, DHCP, router advertisement and network boot.
# https://dnsmasq.org/
[buildout]
parts =
dnsmasq
[dnsmasq]
recipe = slapos.recipe.cmmi
url = https://thekelleys.org.uk/dnsmasq/dnsmasq-2.89.tar.gz
md5sum = f94ca2dad3d002a4f3e41cb8ddd6c260
configure-command = true
make-options =
PREFIX=%(location)s
[buildout]
parts =
double-conversion
extends =
../cmake/buildout.cfg
[double-conversion]
recipe = slapos.recipe.cmmi
shared = true
url = https://github.com/google/double-conversion/archive/refs/tags/v3.3.0.tar.gz
md5sum = b344abb64084a4a1d98a43e67752989b
location = @@LOCATION@@
configure-command = cmake
configure-options =
-DBUILD_SHARED_LIBS=ON
-DCMAKE_INSTALL_PREFIX=@@LOCATION@@
environment =
PATH=${cmake:location}/bin:%(PATH)s
# Dovecot
# https://doc.dovecot.org/
[buildout]
extends =
../lua/buildout.cfg
../openssl/buildout.cfg
../zlib/buildout.cfg
[dovecot]
recipe = slapos.recipe.cmmi
url = https://dovecot.org/releases/2.3/dovecot-2.3.20.tar.gz
md5sum = b8add62d0311dcc95ac25b379e8ba043
location = @@LOCATION@@
configure-options =
--enable-maintainer-mode
--prefix=${:location}
--exec-prefix=${:location}
--with-systemd=no
--with-lua=yes
--with-libcrypto=${openssl:location}
environment =
PATH=${pkgconfig:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${lua:location}/lib/pkgconfig
LUA_LIBS=-L${lua:location}/lib -Wl,-rpath=${lua:location}/lib -llua -lm
LUA_CFLAGS=-I${lua:location}/include
LDFLAGS=-L${openssl:location}/lib -Wl,-rpath=${openssl:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
make-targets = install
post-install = cp -r ${:location}/share/doc/dovecot/example-config/* ${:location}/etc/dovecot/
......@@ -9,6 +9,7 @@ extends =
../zlib/buildout.cfg
../coreutils/buildout.cfg
../patch/buildout.cfg
../gnu-config/buildout.cfg
parts =
dropbear-output
......@@ -20,6 +21,8 @@ md5sum = 0284ea239083f04c8b874e08e1aca243
# in order have all patches working.
url = https://matt.ucc.asn.au/dropbear/releases/dropbear-0.53.1.tar.bz2
pre-configure = cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess .
configure-options =
--with-zlib=${zlib:location}
CFLAGS="-DENABLE_SINGLEUSER -D__DIRTY_NO_SHELL_CHECKING"
......
From 27d88c40e251b370f4dd2fcc7ae03c2967c68e4c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Mon, 2 Sep 2024 04:41:13 +0000
Subject: [PATCH] checkPermission: align behavior with objects raising in
__getattr__
The observed problem was a behavior different between C and python
implementation on python 3, happening with Zope python script. When the
context can not be accessed by the current user, Zope binds a
`Shared.DC.Scripts.Bindings.UnauthorizedBinding`, a class that raises an
Unauthorized error when the context is actually accessed, in order to
postpone the Unauthorized if something is actually accessed. This class
does implements this by raising Unauthorized in __getattr__.
The python implementation of `checkPermission` uses `hasattr` and
`hasattr` has changed between python2 and python3, on python2 it was
ignoring all exceptions, including potential Unauthorized errors and
just returning False, but on python3 these errors are raised.
This change of behavior of python causes checkPermission to behave
differently: when using python implementation on python2 or when using
C implementation, such Unauthorized errors were gracefully handled and
caused checkPermission to return False, but on python3 checkPermission
raises.
This change make this scenario behave the same between python2, python3
and C implementation: Unauthorized errors raised in __getattr__ are
supported. The code is also micro-simplified by doing only one getattr
instead of hasattr and then getattr.
---
src/AccessControl/ImplPython.py | 6 +++++-
src/AccessControl/cAccessControl.c | 7 +++++--
src/AccessControl/tests/testZopeSecurityPolicy.py | 15 +++++++++++++++
4 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/src/AccessControl/ImplPython.py b/src/AccessControl/ImplPython.py
index 1a7788b..0a9326b 100644
--- a/src/AccessControl/ImplPython.py
+++ b/src/AccessControl/ImplPython.py
@@ -31,6 +31,7 @@
from Acquisition import aq_parent
from ExtensionClass import Base
from zope.interface import implementer
+from zExceptions import Unauthorized as zExceptions_Unauthorized
PURE_PYTHON = int(os.environ.get('PURE_PYTHON', '0'))
if PURE_PYTHON:
@@ -71,8 +72,11 @@ def rolesForPermissionOn(perm, object, default=_default_roles, n=None):
r = None
while True:
- if hasattr(object, n):
+ try:
roles = getattr(object, n)
+ except (AttributeError, zExceptions_Unauthorized):
+ pass
+ else:
if roles is None:
if _embed_permission_in_roles:
return (('Anonymous',), n)
diff --git a/src/AccessControl/cAccessControl.c b/src/AccessControl/cAccessControl.c
index 403ed67..1a109fa 100644
--- a/src/AccessControl/cAccessControl.c
+++ b/src/AccessControl/cAccessControl.c
@@ -1847,13 +1847,16 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object,
Py_INCREF(r);
/*
- while 1:
+ while True:
*/
while (1)
{
/*
- if hasattr(object, n):
+ try:
roles = getattr(object, n)
+ except (AttributeError, zExceptions_Unauthorized):
+ pass
+ else:
*/
PyObject *roles = PyObject_GetAttr(object, n);
if (roles != NULL)
diff --git a/src/AccessControl/tests/testZopeSecurityPolicy.py b/src/AccessControl/tests/testZopeSecurityPolicy.py
index 9b12a0f..ee74bad 100644
--- a/src/AccessControl/tests/testZopeSecurityPolicy.py
+++ b/src/AccessControl/tests/testZopeSecurityPolicy.py
@@ -157,6 +157,15 @@ class PartlyProtectedSimpleItem3 (PartlyProtectedSimpleItem1):
__roles__ = sysadmin_roles
+class DynamicallyUnauthorized(SimpleItemish):
+ # This class raises an Unauthorized on attribute access,
+ # similar to Zope's Shared.DC.Scripts.Bindings.UnauthorizedBinding
+ __ac_local_roles__ = {}
+
+ def __getattr__(self, name):
+ raise Unauthorized('Not authorized to access: %s' % name)
+
+
class SimpleClass:
attr = 1
@@ -173,6 +182,7 @@ def setUp(self):
a.item1 = PartlyProtectedSimpleItem1()
a.item2 = PartlyProtectedSimpleItem2()
a.item3 = PartlyProtectedSimpleItem3()
+ a.d_item = DynamicallyUnauthorized()
uf = UserFolder()
a.acl_users = uf
self.uf = a.acl_users
@@ -351,6 +361,11 @@ def test_checkPermission_proxy_role_scope(self):
r_subitem,
context))
+ def test_checkPermission_dynamically_unauthorized(self):
+ d_item = self.a.d_item
+ context = self.context
+ self.assertFalse(self.policy.checkPermission('View', d_item, context))
+
def testUnicodeRolesForPermission(self):
r_item = self.a.r_item
context = self.context
diff -Naur Acquisition-4.7.orig/src/Acquisition/_Acquisition.c Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition/_Acquisition.c
--- Acquisition-4.7.orig/src/Acquisition/_Acquisition.c 2021-03-17 16:22:28.266539592 +0100
+++ Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition/_Acquisition.c 2021-03-17 16:28:59.609842948 +0100
@@ -543,6 +543,64 @@
}
static PyObject *
+Wrapper_GetAttr(PyObject *self, PyObject *attr_name, PyObject *orig)
+{
+ /* This function retrieves an attribute from an object by PyObject_GetAttr.
+
+ The main difference between Wrapper_GetAttr and PyObject_GetAttr is that
+ Wrapper_GetAttr calls _aq_dynamic to generate an attribute dynamically, if
+ the attribute is not found.
+ */
+ PyObject *r, *v, *tb;
+ PyObject *d, *m;
+ PyObject *o;
+
+ if (isWrapper (self))
+ o = WRAPPER(self)->obj;
+ else
+ o = self;
+
+ /* Try to get an attribute in the normal way first. */
+ r = PyObject_GetAttr(o, attr_name);
+ if (r)
+ return r;
+
+ /* If an unexpected error happens, return immediately. */
+ PyErr_Fetch(&r,&v,&tb);
+ if (r != PyExc_AttributeError)
+ {
+ PyErr_Restore(r,v,tb);
+ return NULL;
+ }
+
+ /* Try to get _aq_dynamic. */
+ m = PyObject_GetAttrString(o, "_aq_dynamic");
+ if (! m) {
+ PyErr_Restore(r,v,tb);
+ return NULL;
+ }
+
+ /* Call _aq_dynamic in the context of the original acquisition wrapper. */
+ if (PyECMethod_Check(m) && PyECMethod_Self(m)==o)
+ ASSIGN(m,PyECMethod_New(m,OBJECT(self)));
+ else if (has__of__(m)) ASSIGN(m,__of__(m,OBJECT(self)));
+ d = PyObject_CallFunction(m, "O", attr_name);
+ Py_DECREF(m);
+
+ /* In the case of None, assume that the attribute is not found. */
+ if (d == Py_None) {
+ Py_DECREF(d);
+ PyErr_Restore(r,v,tb);
+ return NULL;
+ }
+
+ Py_XDECREF(r);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+ return d;
+}
+
+static PyObject *
Wrapper_acquire(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
int explicit, int containment);
@@ -677,8 +735,8 @@
return NULL;
}
- /* normal attribute lookup */
- else if ((r = PyObject_GetAttr(self->obj, oname))) {
+ /* Give _aq_dynamic a chance, then normal attribute lookup */
+ else if ((r = Wrapper_GetAttr(OBJECT(self), oname, orig))) {
if (r == Acquired) {
Py_DECREF(r);
return Wrapper_acquire(
@@ -806,7 +864,7 @@
return NULL;
}
- if ((r = PyObject_GetAttr(self->container, oname)) == NULL) {
+ if ((r = Wrapper_GetAttr(self->container, oname, orig)) == NULL) {
/* May be AttributeError or some other kind of error */
return NULL;
}
@@ -830,7 +888,7 @@
static PyObject *
Wrapper_getattro(Wrapper *self, PyObject *oname)
{
- return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 1, 0, 0);
+ return Wrapper_findattr(self, oname, NULL, NULL, OBJECT(self), 1, 1, 0, 0);
}
static PyObject *
@@ -846,7 +904,7 @@
if (STR_EQ(PyBytes_AS_STRING(tmp), "acquire")) {
result = Py_FindAttr(OBJECT(self), oname);
} else {
- result = Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 0, 0, 0);
+ result = Wrapper_findattr(self, oname, NULL, NULL, OBJECT(self), 1, 0, 0, 0);
}
Py_DECREF(tmp);
diff -Naur Acquisition-4.7.orig/src/Acquisition/test_dynamic_acquisition.py Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition/test_dynamic_acquisition.py
--- Acquisition-4.7.orig/src/Acquisition/test_dynamic_acquisition.py 1970-01-01 01:00:00.000000000 +0100
+++ Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition/test_dynamic_acquisition.py 2021-03-17 16:30:07.082413986 +0100
@@ -0,0 +1,160 @@
+##############################################################################
+#
+# Copyright (c) 1996-2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+import Acquisition
+
+def checkContext(self, o):
+ # Python equivalent to aq_inContextOf
+ from Acquisition import aq_base, aq_parent, aq_inner
+ subob = self
+ o = aq_base(o)
+ while 1:
+ if aq_base(subob) is o:
+ return True
+ self = aq_inner(subob)
+ if self is None: break
+ subob = aq_parent(self)
+ if subob is None: break
+ return False
+
+class B(Acquisition.Implicit):
+ color='red'
+
+ def __init__(self, name='b'):
+ self.name = name
+
+ def _aq_dynamic(self, attr):
+ if attr == 'bonjour': return None
+
+ def dynmethod():
+ chain = ' <- '.join(repr(obj) for obj in Acquisition.aq_chain(self))
+ print repr(self) + '.' + attr
+ print 'chain:', chain
+
+ return dynmethod
+
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, self.name)
+
+class A(Acquisition.Implicit):
+
+ def __init__(self, name='a'):
+ self.name = name
+
+ def hi(self):
+ print self, self.color
+
+ def _aq_dynamic(self, attr):
+ return None
+
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, self.name)
+
+def test_dynamic():
+ r'''
+ The _aq_dynamic functionality allows an object to dynamically provide an
+ attribute.
+
+ If an object doesn't have an attribute, Acquisition checks to see if the
+ object has a _aq_dynamic method, which is then called. It is functionally
+ equivalent to __getattr__, but _aq_dynamic is called with 'self' as the
+ acquisition wrapped object where as __getattr__ is called with self as the
+ unwrapped object.
+
+ Let's see how this works. In the examples below, the A class defines
+ '_aq_dynamic', but returns 'None' for all attempts, which means that no new
+ attributes should be generated dynamically. It also doesn't define 'color'
+ attribute, even though it uses it in the 'hi' method.
+
+ >>> A().hi()
+ Traceback (most recent call last):
+ ...
+ AttributeError: color
+
+ The class B, on the other hand, generates all attributes dynamically,
+ except if it is called 'bonjour'.
+
+ First we need to check that, even if an object provides '_aq_dynamic',
+ "regular" Aquisition attribute access should still work:
+
+ >>> b=B()
+ >>> b.a=A()
+ >>> b.a.hi()
+ A('a') red
+ >>> b.a.color='green'
+ >>> b.a.hi()
+ A('a') green
+
+ Now, let's see some dynamically generated action. B does not define a
+ 'salut' method, but remember that it dynamically generates a method for
+ every attribute access:
+
+ >>> b.a.salut()
+ B('b').salut
+ chain: B('b')
+
+ >>> a=A('a1')
+ >>> a.b=B('b1')
+ >>> a.b.salut()
+ B('b1').salut
+ chain: B('b1') <- A('a1')
+
+ >>> b.a.bonjour()
+ Traceback (most recent call last):
+ ...
+ AttributeError: bonjour
+
+ >>> a.b.bonjour()
+ Traceback (most recent call last):
+ ...
+ AttributeError: bonjour
+
+ '''
+
+def test_wrapper_comparissons():
+ r'''
+
+ Test wrapper comparisons in presence of _aq_dynamic
+
+ >>> b=B()
+ >>> b.a=A()
+ >>> foo = b.a
+ >>> bar = b.a
+ >>> assert( foo == bar )
+ >>> c = A('c')
+ >>> b.c = c
+ >>> b.c.d = c
+ >>> b.c.d == c
+ True
+ >>> b.c.d == b.c
+ True
+ >>> b.c == c
+ True
+
+ Test contextuality in presence of _aq_dynamic
+
+ >>> checkContext(b.c, b)
+ True
+ >>> checkContext(b.c, b.a)
+ False
+
+ >>> assert b.a.aq_inContextOf(b)
+ >>> assert b.c.aq_inContextOf(b)
+ >>> assert b.c.d.aq_inContextOf(b)
+ >>> assert b.c.d.aq_inContextOf(c)
+ >>> assert b.c.d.aq_inContextOf(b.c)
+ >>> assert not b.c.aq_inContextOf(foo)
+ >>> assert not b.c.aq_inContextOf(b.a)
+ >>> assert not b.a.aq_inContextOf('somestring')
+'''
+
diff -Naur Acquisition-4.7.orig/src/Acquisition/tests.py Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition/tests.py
--- Acquisition-4.7.orig/src/Acquisition/tests.py 2021-03-17 16:22:28.266539592 +0100
+++ Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition/tests.py 2021-03-17 16:31:31.971132854 +0100
@@ -3366,6 +3366,7 @@
suites = [
DocTestSuite(),
+ DocTestSuite('Acquisition.test_dynamic_acquisition'),
unittest.defaultTestLoader.loadTestsFromName(__name__),
]
diff -Naur Acquisition-4.7.orig/src/Acquisition.egg-info/SOURCES.txt Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition.egg-info/SOURCES.txt
--- Acquisition-4.7.orig/src/Acquisition.egg-info/SOURCES.txt 2021-03-17 16:22:28.262539558 +0100
+++ Acquisition-4.7-py2.7-linux-x86_64.egg/src/Acquisition.egg-info/SOURCES.txt 2021-03-17 16:32:31.619638229 +0100
@@ -15,9 +15,10 @@
src/Acquisition/__init__.py
src/Acquisition/interfaces.py
src/Acquisition/tests.py
+src/Acquisition/test_dynamic_acquisition.py
src/Acquisition.egg-info/PKG-INFO
src/Acquisition.egg-info/SOURCES.txt
src/Acquisition.egg-info/dependency_links.txt
src/Acquisition.egg-info/not-zip-safe
src/Acquisition.egg-info/requires.txt
-src/Acquisition.egg-info/top_level.txt
\ Pas de fin de ligne à la fin du fichier
+src/Acquisition.egg-info/top_level.txt
From a02c80e17f794dc5eaea1c4edd3a2a3277a13638 Mon Sep 17 00:00:00 2001
From: Kazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Date: Tue, 18 Jul 2023 10:26:54 +0200
Subject: [PATCH 1/4] Cast int to float in compare methods.
---
src/DateTime/DateTime.py | 30 ++++++++++-------------------
src/DateTime/tests/test_datetime.py | 17 ++++++++++++++++
2 files changed, 27 insertions(+), 20 deletions(-)
diff --git a/src/DateTime/DateTime.py b/src/DateTime/DateTime.py
index 2d2d97f..c141306 100644
--- a/src/DateTime/DateTime.py
+++ b/src/DateTime/DateTime.py
@@ -1256,12 +1256,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros > long(t * 1000000)
- try:
+ else:
return self._micros > t._micros
- except AttributeError:
- return self._micros > t
__gt__ = greaterThan
@@ -1279,12 +1277,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros >= long(t * 1000000)
- try:
+ else:
return self._micros >= t._micros
- except AttributeError:
- return self._micros >= t
__ge__ = greaterThanEqualTo
@@ -1301,12 +1297,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros == long(t * 1000000)
- try:
+ else:
return self._micros == t._micros
- except AttributeError:
- return self._micros == t
def notEqualTo(self, t):
"""Compare this DateTime object to another DateTime object
@@ -1348,12 +1342,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros < long(t * 1000000)
- try:
+ else:
return self._micros < t._micros
- except AttributeError:
- return self._micros < t
__lt__ = lessThan
@@ -1370,12 +1362,10 @@ class DateTime(object):
"""
if t is None:
t = 0
- if isinstance(t, float):
+ if isinstance(t, (float, int)):
return self._micros <= long(t * 1000000)
- try:
+ else:
return self._micros <= t._micros
- except AttributeError:
- return self._micros <= t
__le__ = lessThanEqualTo
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index 249e79a..e6b3f93 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -228,6 +228,23 @@ class DateTimeTests(unittest.TestCase):
self.assertTrue(dt.lessThanEqualTo(dt1))
self.assertTrue(dt.notEqualTo(dt1))
self.assertFalse(dt.equalTo(dt1))
+ # Compare a date to float
+ dt = DateTime(1.0)
+ self.assertFalse(dt.greaterThan(1.0))
+ self.assertTrue(dt.greaterThanEqualTo(1.0))
+ self.assertFalse(dt.lessThan(1.0))
+ self.assertTrue(dt.lessThanEqualTo(1.0))
+ self.assertFalse(dt.notEqualTo(1.0))
+ self.assertTrue(dt.equalTo(1.0))
+ # Compare a date to int
+ dt = DateTime(1)
+ self.assertEqual(dt, DateTime(1.0))
+ self.assertFalse(dt.greaterThan(1))
+ self.assertTrue(dt.greaterThanEqualTo(1))
+ self.assertFalse(dt.lessThan(1))
+ self.assertTrue(dt.lessThanEqualTo(1))
+ self.assertFalse(dt.notEqualTo(1))
+ self.assertTrue(dt.equalTo(1))
def test_compare_methods_none(self):
# Compare a date to None
--
2.40.1
From 892e66132025ab8c213e4be57dd10f0f8eec7e60 Mon Sep 17 00:00:00 2001
From: Kazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Date: Fri, 14 Jul 2023 21:10:06 +0200
Subject: [PATCH 2/4] Fix compare methods between DateTime(0) and None (fix
#52).
This is a fixup commit of 'further py3 work' that changed the behaviour of compare methods between DateTime(0) and None.
Now None is less than any DateTime instance including DateTime(0), just same as DateTime 2.
---
src/DateTime/DateTime.py | 10 +++++-----
src/DateTime/tests/test_datetime.py | 14 +++++++-------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/DateTime/DateTime.py b/src/DateTime/DateTime.py
index c141306..caf67e1 100644
--- a/src/DateTime/DateTime.py
+++ b/src/DateTime/DateTime.py
@@ -1255,7 +1255,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return True
if isinstance(t, (float, int)):
return self._micros > long(t * 1000000)
else:
@@ -1276,7 +1276,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return True
if isinstance(t, (float, int)):
return self._micros >= long(t * 1000000)
else:
@@ -1296,7 +1296,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return False
if isinstance(t, (float, int)):
return self._micros == long(t * 1000000)
else:
@@ -1341,7 +1341,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return False
if isinstance(t, (float, int)):
return self._micros < long(t * 1000000)
else:
@@ -1361,7 +1361,7 @@ class DateTime(object):
long integer microseconds.
"""
if t is None:
- t = 0
+ return False
if isinstance(t, (float, int)):
return self._micros <= long(t * 1000000)
else:
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index e6b3f93..1dd6c32 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -248,13 +248,13 @@ class DateTimeTests(unittest.TestCase):
def test_compare_methods_none(self):
# Compare a date to None
- dt = DateTime('1997/1/1')
- self.assertTrue(dt.greaterThan(None))
- self.assertTrue(dt.greaterThanEqualTo(None))
- self.assertFalse(dt.lessThan(None))
- self.assertFalse(dt.lessThanEqualTo(None))
- self.assertTrue(dt.notEqualTo(None))
- self.assertFalse(dt.equalTo(None))
+ for dt in (DateTime('1997/1/1'), DateTime(0)):
+ self.assertTrue(dt.greaterThan(None))
+ self.assertTrue(dt.greaterThanEqualTo(None))
+ self.assertFalse(dt.lessThan(None))
+ self.assertFalse(dt.lessThanEqualTo(None))
+ self.assertTrue(dt.notEqualTo(None))
+ self.assertFalse(dt.equalTo(None))
def test_pickle(self):
dt = DateTime()
--
2.40.1
From 4a9798072c87d2fe53b2e1e15b004ff982f9686a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Thu, 30 Nov 2023 06:19:54 +0100
Subject: [PATCH 3/4] Make it possible to pickle datetimes returned by
asdatetime
Fixes #58
---
src/DateTime/pytz_support.py | 5 +++++
src/DateTime/tests/test_datetime.py | 7 +++++++
2 files changed, 12 insertions(+)
diff --git a/src/DateTime/pytz_support.py b/src/DateTime/pytz_support.py
index 9ebf3db..e0746ea 100644
--- a/src/DateTime/pytz_support.py
+++ b/src/DateTime/pytz_support.py
@@ -199,9 +199,14 @@ for hour in range(0, 13):
_old_zmap['+%s00' % fhour] = 'GMT+%i' % hour
+def _p(zone):
+ return _numeric_timezones[zone]
+
+
def _static_timezone_factory(data):
zone = data[0]
cls = type(zone, (StaticTzInfo,), dict(
+ __reduce__=lambda _: (_p, (zone, )),
zone=zone,
_utcoffset=memorized_timedelta(data[5][0][0]),
_tzname=data[6][:-1])) # strip the trailing null
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index 1dd6c32..b9eeea9 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -270,6 +270,13 @@ class DateTimeTests(unittest.TestCase):
for key in DateTime.__slots__:
self.assertEqual(getattr(dt, key), getattr(new, key))
+ def test_pickle_asdatetime_with_tz(self):
+ dt = DateTime('2002/5/2 8:00am GMT+8')
+ data = pickle.dumps(dt.asdatetime(), 1)
+ new = DateTime(pickle.loads(data))
+ for key in DateTime.__slots__:
+ self.assertEqual(getattr(dt, key), getattr(new, key))
+
def test_pickle_with_numerical_tz(self):
for dt_str in ('2007/01/02 12:34:56.789 +0300',
'2007/01/02 12:34:56.789 +0430',
--
2.40.1
From 6ac321746ab86374871623ddaf414b7948325d22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Sun, 3 Dec 2023 15:57:01 +0100
Subject: [PATCH 4/4] Repair equality comparison between DateTime instances and
other types
Fixes #60
---
src/DateTime/DateTime.py | 20 +++++++++++++++-----
src/DateTime/tests/test_datetime.py | 17 +++++++++++++++++
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/src/DateTime/DateTime.py b/src/DateTime/DateTime.py
index caf67e1..84570b9 100644
--- a/src/DateTime/DateTime.py
+++ b/src/DateTime/DateTime.py
@@ -1258,8 +1258,10 @@ class DateTime(object):
return True
if isinstance(t, (float, int)):
return self._micros > long(t * 1000000)
- else:
+ try:
return self._micros > t._micros
+ except AttributeError:
+ return self._micros > t
__gt__ = greaterThan
@@ -1279,8 +1281,10 @@ class DateTime(object):
return True
if isinstance(t, (float, int)):
return self._micros >= long(t * 1000000)
- else:
+ try:
return self._micros >= t._micros
+ except AttributeError:
+ return self._micros >= t
__ge__ = greaterThanEqualTo
@@ -1299,8 +1303,10 @@ class DateTime(object):
return False
if isinstance(t, (float, int)):
return self._micros == long(t * 1000000)
- else:
+ try:
return self._micros == t._micros
+ except AttributeError:
+ return self._micros == t
def notEqualTo(self, t):
"""Compare this DateTime object to another DateTime object
@@ -1344,8 +1350,10 @@ class DateTime(object):
return False
if isinstance(t, (float, int)):
return self._micros < long(t * 1000000)
- else:
+ try:
return self._micros < t._micros
+ except AttributeError:
+ return self._micros < t
__lt__ = lessThan
@@ -1364,8 +1372,10 @@ class DateTime(object):
return False
if isinstance(t, (float, int)):
return self._micros <= long(t * 1000000)
- else:
+ try:
return self._micros <= t._micros
+ except AttributeError:
+ return self._micros <= t
__le__ = lessThanEqualTo
diff --git a/src/DateTime/tests/test_datetime.py b/src/DateTime/tests/test_datetime.py
index b9eeea9..970a072 100644
--- a/src/DateTime/tests/test_datetime.py
+++ b/src/DateTime/tests/test_datetime.py
@@ -230,6 +230,8 @@ class DateTimeTests(unittest.TestCase):
self.assertFalse(dt.equalTo(dt1))
# Compare a date to float
dt = DateTime(1.0)
+ self.assertTrue(dt == DateTime(1.0)) # testing __eq__
+ self.assertFalse(dt != DateTime(1.0)) # testing __ne__
self.assertFalse(dt.greaterThan(1.0))
self.assertTrue(dt.greaterThanEqualTo(1.0))
self.assertFalse(dt.lessThan(1.0))
@@ -239,12 +241,27 @@ class DateTimeTests(unittest.TestCase):
# Compare a date to int
dt = DateTime(1)
self.assertEqual(dt, DateTime(1.0))
+ self.assertTrue(dt == DateTime(1)) # testing __eq__
+ self.assertFalse(dt != DateTime(1)) # testing __ne__
self.assertFalse(dt.greaterThan(1))
self.assertTrue(dt.greaterThanEqualTo(1))
self.assertFalse(dt.lessThan(1))
self.assertTrue(dt.lessThanEqualTo(1))
self.assertFalse(dt.notEqualTo(1))
self.assertTrue(dt.equalTo(1))
+ # Compare a date to string; there is no implicit type conversion
+ # but behavior if consistent as when comparing, for example, an int
+ # and a string.
+ dt = DateTime("2023")
+ self.assertFalse(dt == "2023") # testing __eq__
+ self.assertTrue(dt != "2023") # testing __ne__
+ if sys.version_info > (3, ):
+ self.assertRaises(TypeError, dt.greaterThan, "2023")
+ self.assertRaises(TypeError, dt.greaterThanEqualTo, "2023")
+ self.assertRaises(TypeError, dt.lessThan, "2023")
+ self.assertRaises(TypeError, dt.lessThanEqualTo, "2023")
+ self.assertTrue(dt.notEqualTo("2023"))
+ self.assertFalse(dt.equalTo("2023"))
def test_compare_methods_none(self):
# Compare a date to None
--
2.40.1
From 77f86b50f097dcf364e0d140e45593bf001d46bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Fri, 1 Mar 2024 09:49:17 +0900
Subject: [PATCH] set metadata in setup.py for compatibility with old slapos
buildout
---
setup.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/setup.py b/setup.py
index 1bf0bcff5..a93fe7b22 100755
--- a/setup.py
+++ b/setup.py
@@ -987,6 +987,11 @@ ext_modules = [
try:
setup(
+ name='pillow',
+ version='10.2.0',
+ packages=["PIL"],
+ include_package_data=True,
+ package_dir={"": "src"},
cmdclass={"build_ext": pil_build_ext},
ext_modules=ext_modules,
zip_safe=not (debug_build() or PLATFORM_MINGW),
--
2.42.0
From cfdd177131b8f453c024ad3008da4951aab16a50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 2 May 2023 13:53:46 +0900
Subject: [PATCH] Add a confirmation prompt on ``Delete All Objects`` button
(#19)
Co-authored-by: Michael Howitz <icemac@gmx.net>
patch amended to remove the changelog entry
---
src/Products/BTreeFolder2/contents.dtml | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/Products/BTreeFolder2/contents.dtml b/src/Products/BTreeFolder2/contents.dtml
index d00e04a..9629700 100644
--- a/src/Products/BTreeFolder2/contents.dtml
+++ b/src/Products/BTreeFolder2/contents.dtml
@@ -82,7 +82,7 @@ function toggleSelect() {
<input class="form-element" type="submit" name="manage_delObjects:method"
value="Delete" />
<input class="form-element" type="submit" name="manage_delAllObjects:method"
- value="Delete All Objects" />
+ value="Delete All Objects" onClick="return confirm('Really delete all objects?')"/>
</dtml-if>
<dtml-if "_.SecurityCheckPermission('Import/Export objects', this())">
<input class="form-element" type="submit"
--
2.39.1
From c233d7278ae7089ba2ad32b8a178a3793273a47d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Sat, 1 Jun 2024 14:58:23 +0900
Subject: [PATCH] import from zope.lifecycleevent.interfaces to prevent
DeprecationWarnings
partial backport from https://github.com/zopefoundation/Products.CMFCore/pull/125
---
src/Products/CMFCore/CMFCatalogAware.py | 4 ++--
src/Products/CMFCore/CachingPolicyManager.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/Products/CMFCore/CMFCatalogAware.py b/src/Products/CMFCore/CMFCatalogAware.py
index a574660..502161f 100644
--- a/src/Products/CMFCore/CMFCatalogAware.py
+++ b/src/Products/CMFCore/CMFCatalogAware.py
@@ -25,11 +25,11 @@ from OFS.interfaces import IObjectClonedEvent
from OFS.interfaces import IObjectWillBeMovedEvent
from zope.component import queryUtility
from zope.component import subscribers
-from zope.container.interfaces import IObjectAddedEvent
-from zope.container.interfaces import IObjectMovedEvent
from zope.interface import implementer
+from zope.lifecycleevent.interfaces import IObjectAddedEvent
from zope.lifecycleevent.interfaces import IObjectCopiedEvent
from zope.lifecycleevent.interfaces import IObjectCreatedEvent
+from zope.lifecycleevent.interfaces import IObjectMovedEvent
from .interfaces import ICallableOpaqueItem
from .interfaces import ICatalogAware
diff --git a/src/Products/CMFCore/CachingPolicyManager.py b/src/Products/CMFCore/CachingPolicyManager.py
index 3722b7f..65a079d 100644
--- a/src/Products/CMFCore/CachingPolicyManager.py
+++ b/src/Products/CMFCore/CachingPolicyManager.py
@@ -27,9 +27,9 @@ from Persistence import PersistentMapping
from Products.PageTemplates.Expressions import SecureModuleImporter
from Products.PageTemplates.Expressions import getEngine
from zope.component import getUtility
-from zope.container.interfaces import IObjectMovedEvent
from zope.datetime import rfc1123_date
from zope.interface import implementer
+from zope.lifecycleevent.interfaces import IObjectMovedEvent
from .Expression import Expression
from .interfaces import ICachingPolicy
--
2.42.0
From 2a248ffc584082122776f3b51f5fdaf90c5e2905 Mon Sep 17 00:00:00 2001
From: Maurits van Rees <maurits@vanrees.org>
Date: Tue, 23 Apr 2024 22:22:53 +0200
Subject: [PATCH] Use the new resources keyword of registerClass if available.
This avoids a deprecation warning for using a non callable constructor in Zope higher than 5.9.
See report in https://github.com/zopefoundation/Zope/issues/1202
---
CHANGES.rst | 6 ++++-
src/Products/CMFCore/utils.py | 51 ++++++++++++++++++++++++-----------
2 files changed, 40 insertions(+), 17 deletions(-)
diff --git a/src/Products/CMFCore/utils.py b/src/Products/CMFCore/utils.py
index 2f99d6d..565914d 100644
--- a/src/Products/CMFCore/utils.py
+++ b/src/Products/CMFCore/utils.py
@@ -599,13 +599,20 @@ def initialize(self, context):
# Add only one meta type to the folder add list.
productObject = context._ProductContext__prod
self.product_name = productObject.id
- context.registerClass(
- meta_type=self.meta_type,
- # This is a little sneaky: we add self to the
- # FactoryDispatcher under the name "toolinit".
- # manage_addTool() can then grab it.
- constructors=(manage_addToolForm, manage_addTool, self),
- icon=self.icon)
+ # We add self to the FactoryDispatcher under the name 'toolinit'.
+ # manage_addContentType() can then grab it.
+ try:
+ context.registerClass(
+ meta_type=self.meta_type,
+ constructors=(manage_addToolForm, manage_addTool),
+ resources=(self, ),
+ icon=self.icon)
+ except TypeError:
+ # The 'resources' keyword was only introduced after Zope 5.9.
+ context.registerClass(
+ meta_type=self.meta_type,
+ constructors=(manage_addToolForm, manage_addTool, self),
+ icon=self.icon)
if self.icon:
icon = os_path.split(self.icon)[1]
@@ -680,15 +687,27 @@ def __init__(self, meta_type, content_types, permission=None,
def initialize(self, context):
# Add only one meta type to the folder add list.
- context.registerClass(
- meta_type=self.meta_type,
- # This is a little sneaky: we add self to the
- # FactoryDispatcher under the name "contentinit".
- # manage_addContentType() can then grab it.
- constructors=(manage_addContentForm, manage_addContent,
- self) + self.extra_constructors,
- permission=self.permission,
- visibility=self.visibility)
+ # We add self to the FactoryDispatcher under the name 'contentinit'.
+ # manage_addContentType() can then grab it.
+ try:
+ context.registerClass(
+ meta_type=self.meta_type,
+ constructors=(
+ manage_addContentForm,
+ manage_addContent,
+ ) + self.extra_constructors,
+ resources=(self, ),
+ permission=self.permission,
+ visibility=self.visibility)
+ except TypeError:
+ # The 'resources' keyword was only introduced after Zope 5.9.
+ context.registerClass(
+ meta_type=self.meta_type,
+ constructors=(
+ manage_addContentForm, manage_addContent, self,
+ ) + self.extra_constructors,
+ permission=self.permission,
+ visibility=self.visibility)
for ct in self.content_types:
ct.__factory_meta_type__ = self.meta_type
diff -Naur Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/DCWorkflow.py Products.DCWorkflow-2.4.1/Products/DCWorkflow/DCWorkflow.py
--- Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/DCWorkflow.py 2020-03-09 22:05:43.000000000 +0100
+++ Products.DCWorkflow-2.4.1/Products/DCWorkflow/DCWorkflow.py 2021-03-18 15:43:47.791236880 +0100
@@ -38,6 +38,7 @@
from Products.DCWorkflow.interfaces import IDCWorkflowDefinition
from Products.DCWorkflow.Transitions import TRIGGER_AUTOMATIC
from Products.DCWorkflow.Transitions import TRIGGER_USER_ACTION
+from Products.DCWorkflow.Transitions import TRIGGER_WORKFLOW_METHOD
from Products.DCWorkflow.utils import Message as _
from Products.DCWorkflow.utils import modifyRolesForGroup
from Products.DCWorkflow.utils import modifyRolesForPermission
@@ -279,6 +280,52 @@
self._changeStateOf(ob, tdef, kw)
@security.private
+ def isWorkflowMethodSupported(self, ob, method_id):
+ '''
+ Returns a true value if the given workflow method
+ is supported in the current state.
+ '''
+ sdef = self._getWorkflowStateOf(ob)
+ if sdef is None:
+ return 0
+ if method_id in sdef.transitions:
+ tdef = self.transitions.get(method_id, None)
+ if (tdef is not None and
+ tdef.trigger_type == TRIGGER_WORKFLOW_METHOD and
+ self._checkTransitionGuard(tdef, ob)):
+ return 1
+ return 0
+
+ @security.private
+ def wrapWorkflowMethod(self, ob, method_id, func, args, kw):
+ '''
+ Allows the user to request a workflow action. This method
+ must perform its own security checks.
+ '''
+ sdef = self._getWorkflowStateOf(ob)
+ if sdef is None:
+ raise WorkflowException('Object is in an undefined state')
+ if method_id not in sdef.transitions:
+ raise Unauthorized(method_id)
+ tdef = self.transitions.get(method_id, None)
+ if tdef is None or tdef.trigger_type != TRIGGER_WORKFLOW_METHOD:
+ raise WorkflowException(
+ 'Transition %s is not triggered by a workflow method'
+ % method_id)
+ if not self._checkTransitionGuard(tdef, ob):
+ raise Unauthorized(method_id)
+ res = func(*args, **kw)
+ try:
+ self._changeStateOf(ob, tdef)
+ except ObjectDeleted:
+ # Re-raise with a different result.
+ raise ObjectDeleted(res)
+ except ObjectMoved as ex:
+ # Re-raise with a different result.
+ raise ObjectMoved(ex.getNewObject(), res)
+ return res
+
+ @security.private
def isInfoSupported(self, ob, name):
'''
Returns a true value if the given info name is supported.
diff -Naur Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/dtml/transition_properties.dtml Products.DCWorkflow-2.4.1/Products/DCWorkflow/dtml/transition_properties.dtml
--- Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/dtml/transition_properties.dtml 2020-03-09 22:05:43.000000000 +0100
+++ Products.DCWorkflow-2.4.1/Products/DCWorkflow/dtml/transition_properties.dtml 2021-03-18 15:37:55.144028451 +0100
@@ -56,6 +56,16 @@
</tr>
<tr>
+<th></th>
+<td>
+<dtml-let checked="trigger_type==2 and 'checked' or ' '">
+<input type="radio" name="trigger_type" value="2" &dtml-checked; />
+Initiated by WorkflowMethod
+</dtml-let>
+</td>
+</tr>
+
+<tr>
<th align="left">Script (before)</th>
<td>
<select name="script_name">
diff -Naur Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/dtml/transitions.dtml Products.DCWorkflow-2.4.1/Products/DCWorkflow/dtml/transitions.dtml
--- Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/dtml/transitions.dtml 2020-03-09 22:05:43.000000000 +0100
+++ Products.DCWorkflow-2.4.1/Products/DCWorkflow/dtml/transitions.dtml 2021-03-18 15:37:55.144028451 +0100
@@ -17,7 +17,8 @@
<td>
Destination state: <code><dtml-if new_state_id>&dtml-new_state_id;<dtml-else>(Remain in state)</dtml-if></code> <br />
Trigger: <dtml-var expr="(trigger_type == 0 and 'Automatic') or
- (trigger_type == 1 and 'User action')">
+ (trigger_type == 1 and 'User action') or
+ (trigger_type == 2 and 'WorkflowMethod')">
<br />
<dtml-if script_name>
Script (before): &dtml-script_name;
diff -Naur Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/exportimport.py Products.DCWorkflow-2.4.1/Products/DCWorkflow/exportimport.py
--- Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/exportimport.py 2020-03-09 22:05:43.000000000 +0100
+++ Products.DCWorkflow-2.4.1/Products/DCWorkflow/exportimport.py 2021-03-18 15:44:34.903667147 +0100
@@ -40,7 +40,7 @@
from Products.DCWorkflow.utils import _xmldir
-TRIGGER_TYPES = ('AUTOMATIC', 'USER')
+TRIGGER_TYPES = ('AUTOMATIC', 'USER', 'METHOD' )
_FILENAME = 'workflows.xml'
diff -Naur Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/Transitions.py Products.DCWorkflow-2.4.1/Products/DCWorkflow/Transitions.py
--- Products.DCWorkflow-2.4.1.orig/Products/DCWorkflow/Transitions.py 2020-03-09 22:05:43.000000000 +0100
+++ Products.DCWorkflow-2.4.1/Products/DCWorkflow/Transitions.py 2021-03-18 15:37:55.148028486 +0100
@@ -31,6 +31,7 @@
TRIGGER_AUTOMATIC = 0
TRIGGER_USER_ACTION = 1
+TRIGGER_WORKFLOW_METHOD = 2
class TransitionDefinition(SimpleItem):
From 3c6b815bbb2a9300984a7b50cb5ec5375bf4588e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 2 Apr 2024 21:54:07 +0900
Subject: [PATCH] Revive TRIGGER_WORKFLOW_METHOD support, ERP5 uses it
---
src/Products/DCWorkflow/DCWorkflow.py | 47 +++++++++++++++++++
src/Products/DCWorkflow/Transitions.py | 1 +
.../dtml/transition_properties.dtml | 10 ++++
src/Products/DCWorkflow/dtml/transitions.dtml | 3 +-
src/Products/DCWorkflow/exportimport.py | 2 +-
5 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/src/Products/DCWorkflow/DCWorkflow.py b/src/Products/DCWorkflow/DCWorkflow.py
index 9adf05c..d0306dc 100644
--- a/src/Products/DCWorkflow/DCWorkflow.py
+++ b/src/Products/DCWorkflow/DCWorkflow.py
@@ -38,6 +38,7 @@ from .Expression import createExprContext
from .interfaces import IDCWorkflowDefinition
from .Transitions import TRIGGER_AUTOMATIC
from .Transitions import TRIGGER_USER_ACTION
+from .Transitions import TRIGGER_WORKFLOW_METHOD
from .utils import Message as _
from .utils import modifyRolesForGroup
from .utils import modifyRolesForPermission
@@ -278,6 +279,52 @@ class DCWorkflowDefinition(WorkflowUIMixin, Folder):
raise Unauthorized(action)
self._changeStateOf(ob, tdef, kw)
+ @security.private
+ def isWorkflowMethodSupported(self, ob, method_id):
+ '''
+ Returns a true value if the given workflow method
+ is supported in the current state.
+ '''
+ sdef = self._getWorkflowStateOf(ob)
+ if sdef is None:
+ return 0
+ if method_id in sdef.transitions:
+ tdef = self.transitions.get(method_id, None)
+ if (tdef is not None and
+ tdef.trigger_type == TRIGGER_WORKFLOW_METHOD and
+ self._checkTransitionGuard(tdef, ob)):
+ return 1
+ return 0
+
+ @security.private
+ def wrapWorkflowMethod(self, ob, method_id, func, args, kw):
+ '''
+ Allows the user to request a workflow action. This method
+ must perform its own security checks.
+ '''
+ sdef = self._getWorkflowStateOf(ob)
+ if sdef is None:
+ raise WorkflowException('Object is in an undefined state')
+ if method_id not in sdef.transitions:
+ raise Unauthorized(method_id)
+ tdef = self.transitions.get(method_id, None)
+ if tdef is None or tdef.trigger_type != TRIGGER_WORKFLOW_METHOD:
+ raise WorkflowException(
+ 'Transition %s is not triggered by a workflow method'
+ % method_id)
+ if not self._checkTransitionGuard(tdef, ob):
+ raise Unauthorized(method_id)
+ res = func(*args, **kw)
+ try:
+ self._changeStateOf(ob, tdef)
+ except ObjectDeleted:
+ # Re-raise with a different result.
+ raise ObjectDeleted(res)
+ except ObjectMoved as ex:
+ # Re-raise with a different result.
+ raise ObjectMoved(ex.getNewObject(), res)
+ return res
+
@security.private
def isInfoSupported(self, ob, name):
'''
diff --git a/src/Products/DCWorkflow/Transitions.py b/src/Products/DCWorkflow/Transitions.py
index a6e1e6f..b4e012c 100644
--- a/src/Products/DCWorkflow/Transitions.py
+++ b/src/Products/DCWorkflow/Transitions.py
@@ -31,6 +31,7 @@ from .utils import _dtmldir
TRIGGER_AUTOMATIC = 0
TRIGGER_USER_ACTION = 1
+TRIGGER_WORKFLOW_METHOD = 2
class TransitionDefinition(SimpleItem):
diff --git a/src/Products/DCWorkflow/dtml/transition_properties.dtml b/src/Products/DCWorkflow/dtml/transition_properties.dtml
index d6b8a74..6a0803e 100644
--- a/src/Products/DCWorkflow/dtml/transition_properties.dtml
+++ b/src/Products/DCWorkflow/dtml/transition_properties.dtml
@@ -55,6 +55,16 @@ Initiated by user action
</td>
</tr>
+<tr>
+<th></th>
+<td>
+<dtml-let checked="trigger_type==2 and 'checked' or ' '">
+<input type="radio" name="trigger_type" value="2" &dtml-checked; />
+Initiated by WorkflowMethod
+</dtml-let>
+</td>
+</tr>
+
<tr>
<th align="left">Script (before)</th>
<td>
diff --git a/src/Products/DCWorkflow/dtml/transitions.dtml b/src/Products/DCWorkflow/dtml/transitions.dtml
index 4cdd3d3..37e949c 100644
--- a/src/Products/DCWorkflow/dtml/transitions.dtml
+++ b/src/Products/DCWorkflow/dtml/transitions.dtml
@@ -17,7 +17,8 @@
<td>
Destination state: <code><dtml-if new_state_id>&dtml-new_state_id;<dtml-else>(Remain in state)</dtml-if></code> <br />
Trigger: <dtml-var expr="(trigger_type == 0 and 'Automatic') or
- (trigger_type == 1 and 'User action')">
+ (trigger_type == 1 and 'User action') or
+ (trigger_type == 2 and 'WorkflowMethod')">
<br />
<dtml-if script_name>
Script (before): &dtml-script_name;
diff --git a/src/Products/DCWorkflow/exportimport.py b/src/Products/DCWorkflow/exportimport.py
index f17264d..2374b6e 100644
--- a/src/Products/DCWorkflow/exportimport.py
+++ b/src/Products/DCWorkflow/exportimport.py
@@ -37,7 +37,7 @@ from .interfaces import IDCWorkflowDefinition
from .utils import _xmldir
-TRIGGER_TYPES = ('AUTOMATIC', 'USER')
+TRIGGER_TYPES = ('AUTOMATIC', 'USER', 'METHOD' )
_FILENAME = 'workflows.xml'
--
2.42.0
From 94785bb0ef91fd69b802b904b6037bc027f9b5b4 Mon Sep 17 00:00:00 2001
From: Kazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Date: Mon, 16 Sep 2024 09:48:40 +0200
Subject: [PATCH] Fix classify() for magic classifier and binary data.
---
Products/MimetypesRegistry/MimeTypesRegistry.py | 3 ++-
Products/MimetypesRegistry/tests/test_mimetypes.py | 8 ++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/Products/MimetypesRegistry/MimeTypesRegistry.py b/Products/MimetypesRegistry/MimeTypesRegistry.py
index 1f5c4b5..ad480c3 100644
--- a/Products/MimetypesRegistry/MimeTypesRegistry.py
+++ b/Products/MimetypesRegistry/MimeTypesRegistry.py
@@ -303,6 +303,8 @@ def classify(self, data, mimetype=None, filename=None):
if mt is None:
mt = self.globFilename(filename)
if data and not mt:
+ if isinstance(data, str):
+ data = data.encode()
for c in self._classifiers():
if c.classify(data):
mt = c
@@ -322,7 +324,6 @@ def classify(self, data, mimetype=None, filename=None):
failed = "text/x-unknown-content-type"
filename = filename or ""
data = data or ""
- data = data.encode()
ct, enc = guess_content_type(filename, data, None)
if ct == failed:
ct = "text/plain"
diff --git a/Products/MimetypesRegistry/tests/test_mimetypes.py b/Products/MimetypesRegistry/tests/test_mimetypes.py
index 1f55056..1fb75a7 100644
--- a/Products/MimetypesRegistry/tests/test_mimetypes.py
+++ b/Products/MimetypesRegistry/tests/test_mimetypes.py
@@ -58,6 +58,10 @@ def testClassify(self):
mt = reg.classify("<?xml ?>")
self.assertTrue(isinstance(mt, text_xml), str(mt))
+ # test magic classifiers
+ mt = reg.classify("BEGIN:VCARD\n")
+ self.assertEqual(str(mt), "text/vcard")
+
# test no data return default
mt = reg.classify("")
self.assertTrue(isinstance(mt, text_plain), str(mt))
@@ -73,6 +77,10 @@ def testClassify(self):
mt = reg.classify("baz", filename="xxx")
self.assertTrue(isinstance(mt, application_octet_stream), str(mt))
+ # test unclassifiable binary data
+ mt = reg.classify(b"\x01")
+ self.assertTrue(isinstance(mt, application_octet_stream), str(mt))
+
def testExtension(self):
reg = self.registry
data = "<foo>bar</foo>"
From a037f2a2e2090dcd63b83af9b06427dd8c7e9536 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Wed, 22 May 2024 23:58:45 +0900
Subject: [PATCH] Show Python Script source code in tracebacks
Expose a __loader__ in globals so that linecache module is able to use
it to display the source code.
This requires changing the "filename" used when compiling function,
because linecache uses code.co_filename as a cache key, so it's
necessary that each python script use a different filename.
WIP from https://github.com/zopefoundation/Products.PythonScripts/pull/65
---
CHANGES.rst | 2 +
src/Products/PythonScripts/PythonScript.py | 19 ++++++-
.../PythonScripts/tests/testPythonScript.py | 50 ++++++++++++++++++-
3 files changed, 67 insertions(+), 4 deletions(-)
diff --git a/src/Products/PythonScripts/PythonScript.py b/src/Products/PythonScripts/PythonScript.py
index fe4223a..5cb7f37 100644
--- a/src/Products/PythonScripts/PythonScript.py
+++ b/src/Products/PythonScripts/PythonScript.py
@@ -16,7 +16,9 @@
Python code.
"""
+import importlib.abc
import importlib.util
+import linecache
import marshal
import os
import re
@@ -56,7 +58,7 @@
Python_magic = importlib.util.MAGIC_NUMBER
# This should only be incremented to force recompilation.
-Script_magic = 4
+Script_magic = 5
_log_complaint = (
'Some of your Scripts have stale code cached. Since Zope cannot'
' use this code, startup will be slightly slower until these Scripts'
@@ -97,6 +99,16 @@ def manage_addPythonScript(self, id, title='', file=None, REQUEST=None,
return ''
+class PythonScriptLoader(importlib.abc.Loader):
+ """PEP302 loader to display source code in tracebacks
+ """
+ def __init__(self, source):
+ self._source = source
+
+ def get_source(self, name):
+ return self._source
+
+
class PythonScript(Script, Historical, Cacheable):
"""Web-callable scripts written in a safe subset of Python.
@@ -234,7 +246,7 @@ def _compile(self):
self._params,
body=self._body or 'pass',
name=self.id,
- filename=self.meta_type,
+ filename=getattr(self, '_filepath', None) or self.get_filepath(),
globalize=bind_names)
code = compile_result.code
@@ -261,6 +273,7 @@ def _compile(self):
fc.co_argcount)
self.Python_magic = Python_magic
self.Script_magic = Script_magic
+ linecache.clearcache()
self._v_change = 0
def _newfun(self, code):
@@ -331,6 +344,8 @@ def _exec(self, bound_names, args, kw):
PythonScriptTracebackSupplement, self, -1)
safe_globals['__file__'] = getattr(
self, '_filepath', None) or self.get_filepath()
+ safe_globals['__loader__'] = PythonScriptLoader(self._body)
+
function = types.FunctionType(
function_code, safe_globals, None, function_argument_definitions)
diff --git a/src/Products/PythonScripts/tests/testPythonScript.py b/src/Products/PythonScripts/tests/testPythonScript.py
index 60ef6c3..7cd2266 100644
--- a/src/Products/PythonScripts/tests/testPythonScript.py
+++ b/src/Products/PythonScripts/tests/testPythonScript.py
@@ -15,6 +15,7 @@
import io
import os
import sys
+import traceback
import unittest
import warnings
from urllib.error import HTTPError
@@ -241,7 +242,8 @@ def test_manage_DAVget(self):
self.assertEqual(ps.read(), ps.manage_DAVget())
def test_PUT_native_string(self):
- ps = makerequest(self._filePS('complete'))
+ container = DummyFolder('container')
+ ps = makerequest(self._filePS('complete').__of__(container))
self.assertEqual(ps.title, 'This is a title')
self.assertEqual(ps.body(), 'print(foo+bar+baz)\nreturn printed\n')
self.assertEqual(ps.params(), 'foo, bar, baz=1')
@@ -265,7 +267,8 @@ def test_PUT_native_string(self):
self.assertEqual(ps.params(), 'oops')
def test_PUT_bytes(self):
- ps = makerequest(self._filePS('complete'))
+ container = DummyFolder('container')
+ ps = makerequest(self._filePS('complete').__of__(container))
self.assertEqual(ps.title, 'This is a title')
self.assertEqual(ps.body(), 'print(foo+bar+baz)\nreturn printed\n')
self.assertEqual(ps.params(), 'foo, bar, baz=1')
@@ -588,3 +591,46 @@ def test_PythonScript_proxyroles_nonmanager(self):
# Cleanup
noSecurityManager()
+
+
+class TestTraceback(FunctionalTestCase, PythonScriptTestBase):
+
+ def _format_exception(self):
+ return "".join(traceback.format_exception(*sys.exc_info()))
+
+ def test_source_code_in_traceback(self):
+ ps = self._newPS("1 / 0")
+ try:
+ ps()
+ except ZeroDivisionError:
+ formatted_exception = self._format_exception()
+ self.assertIn("1 / 0", formatted_exception)
+
+ ps.write("2 / 0")
+ try:
+ ps()
+ except ZeroDivisionError:
+ formatted_exception = self._format_exception()
+ self.assertIn("2 / 0", formatted_exception)
+
+ def test_multiple_scripts_in_traceback(self):
+ from Products.PythonScripts.PythonScript import manage_addPythonScript
+
+ script1_body = "container.script2()"
+ manage_addPythonScript(
+ self.folder,
+ "script1",
+ file=script1_body,
+ )
+ script2_body = "1 / 0"
+ manage_addPythonScript(
+ self.folder,
+ "script2",
+ file=script2_body,
+ )
+ try:
+ self.folder.script1()
+ except ZeroDivisionError:
+ formatted_exception = self._format_exception()
+ self.assertIn(script1_body, formatted_exception)
+ self.assertIn(script2_body, formatted_exception)
From f0913cee83b897a58c150911ab9e8ed8c605c472 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Wed, 1 Nov 2023 09:44:23 +0100
Subject: [PATCH] fix pdf reader getting stuck when trying to read large files
wihhout xref marker #808
Backport of a PyPDF commit
---
PyPDF2/pdf.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/PyPDF2/pdf.py b/PyPDF2/pdf.py
index 7bba1c6..0034c90 100644
--- a/PyPDF2/pdf.py
+++ b/PyPDF2/pdf.py
@@ -1919,7 +1919,7 @@ class PdfFileReader(object):
def readNextEndLine(self, stream):
debug = False
if debug: print(">>readNextEndLine")
- line = b_("")
+ line_parts = []
while True:
# Prevent infinite loops in malformed PDFs
if stream.tell() == 0:
@@ -1946,10 +1946,11 @@ class PdfFileReader(object):
break
else:
if debug: print(" x is neither")
- line = x + line
+ line_parts.append(x)
if debug: print((" RNEL line:", line))
if debug: print("leaving RNEL")
- return line
+ line_parts.reverse()
+ return b"".join(line_parts)
def decrypt(self, password):
"""
--
2.39.2
From 4c132f622f33575aca8da1d0450caa3a33b8c0a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Thu, 4 Jan 2024 00:13:08 +0900
Subject: [PATCH] compile: implicitly enable __future__.print_function when
compiling functions
---
src/RestrictedPython/compile.py | 5 +++--
tests/test_compile_restricted_function.py | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/RestrictedPython/compile.py b/src/RestrictedPython/compile.py
index 3fc2881..98f3026 100644
--- a/src/RestrictedPython/compile.py
+++ b/src/RestrictedPython/compile.py
@@ -3,6 +3,7 @@ from RestrictedPython._compat import IS_CPYTHON
from RestrictedPython._compat import IS_PY2
from RestrictedPython.transformer import RestrictingNodeTransformer
+import __future__
import ast
import warnings
@@ -134,7 +135,7 @@ def compile_restricted_function(
name,
filename='<string>',
globalize=None, # List of globals (e.g. ['here', 'context', ...])
- flags=0,
+ flags=__future__.print_function.compiler_flag,
dont_inherit=False,
policy=RestrictingNodeTransformer):
"""Compile a restricted code object for a function.
@@ -144,7 +145,7 @@ def compile_restricted_function(
"""
# Parse the parameters and body, then combine them.
try:
- body_ast = ast.parse(body, '<func code>', 'exec')
+ body_ast = compile(body, '<func code>', 'exec', ast.PyCF_ONLY_AST | flags, dont_inherit)
except SyntaxError as v:
error = syntax_error_template.format(
lineno=v.lineno,
diff --git a/tests/test_compile_restricted_function.py b/tests/test_compile_restricted_function.py
index 5c81f86..a49e475 100644
--- a/tests/test_compile_restricted_function.py
+++ b/tests/test_compile_restricted_function.py
@@ -9,7 +9,7 @@ from types import FunctionType
def test_compile_restricted_function():
p = ''
body = """
-print("Hello World!")
+print("Hello", "World!")
return printed
"""
name = "hello_world"
--
2.42.0
From 21a91db138cca3ada0e4dff475b061066362410c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Sat, 17 Feb 2024 23:25:43 +0900
Subject: [PATCH] backport changes from 0.52.29
We can not use 0.52.29 directly because it does not have a setup.py
and our buildout / setuptools tooling is too old.
---
src/SOAPpy/Client.py | 3 ++-
src/SOAPpy/Types.py | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/SOAPpy/Client.py b/src/SOAPpy/Client.py
index e86c5ec..d2bbefb 100644
--- a/src/SOAPpy/Client.py
+++ b/src/SOAPpy/Client.py
@@ -45,6 +45,7 @@
ident = '$Id: Client.py 1496 2010-03-04 23:46:17Z pooryorick $'
from .version import __version__
+from io import StringIO
#import xml.sax
import urllib.request, urllib.parse, urllib.error
@@ -152,7 +153,7 @@ class HTTP:
return -1, e.line, None
self.headers = response.msg
- self.file = response.fp
+ self.file = StringIO(response.fp.read().decode('utf-8'))
return response.status, response.reason, response.msg
def close(self):
diff --git a/src/SOAPpy/Types.py b/src/SOAPpy/Types.py
index de9dcac..cf08d17 100644
--- a/src/SOAPpy/Types.py
+++ b/src/SOAPpy/Types.py
@@ -1451,6 +1451,8 @@ class arrayType(collections.UserList, compoundType):
def __getitem__(self, item):
try:
return self.data[int(item)]
+ except TypeError:
+ return self.data[item]
except ValueError:
return getattr(self, item)
--
2.42.0
From 8e7c9a6a86104e306aee2224ff5e517ee201b28f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 9 Jan 2024 17:15:11 +0900
Subject: [PATCH] Fix redirections to URLS with host given as IP-litteral
(#1192)
When redirecting to an URL with an IPv6 host with surrounding brackets,
we should not escape the surrounding brackets.
The patch updates referenced RFC from 2396 to 3986, which obsoletes it
and change the safe characters for the netloc part to allow [ and ].
The RFC specifies that [ and ] are only allowed when they are the first
and last characters, but we don't need to be more specific here, because
using [ or ] in other places of the host is rejected by urlparse above.
Fixes #1191
---
src/ZPublisher/HTTPResponse.py | 14 +++++++-------
src/ZPublisher/tests/testHTTPResponse.py | 8 ++++++--
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/src/ZPublisher/HTTPResponse.py b/src/ZPublisher/HTTPResponse.py
index b0b4ca2b1..b1a824151 100644
--- a/src/ZPublisher/HTTPResponse.py
+++ b/src/ZPublisher/HTTPResponse.py
@@ -230,24 +230,24 @@ class HTTPBaseResponse(BaseResponse):
# To be entirely correct, we must make sure that all non-ASCII
# characters are quoted correctly.
parsed = list(urlparse(location))
- rfc2396_unreserved = "-_.!~*'()" # RFC 2396 section 2.3
+ rfc3986_unreserved = "-_.!~*'()" # RFC 3986 section 2.3
for idx, idx_safe in (
# authority
- (1, ";:@?/&=+$,"), # RFC 2396 section 3.2, 3.2.1, 3.2.3
+ (1, "[];:@?/&=+$,"), # RFC 3986 section 3.2, 3.2.1, 3.2.3
# path
- (2, "/;:@&=+$,"), # RFC 2396 section 3.3
+ (2, "/;:@&=+$,"), # RFC 3986 section 3.3
# params - actually part of path; empty in Python 3
- (3, "/;:@&=+$,"), # RFC 2396 section 3.3
+ (3, "/;:@&=+$,"), # RFC 3986 section 3.3
# query
- (4, ";/?:@&=+,$"), # RFC 2396 section 3.4
+ (4, ";/?:@&=+,$"), # RFC 3986 section 3.4
# fragment
- (5, ";/?:@&=+$,"), # RFC 2396 section 4
+ (5, ";/?:@&=+$,"), # RFC 3986 section 4
):
# Make a hacky guess whether the component is already
# URL-encoded by checking for %. If it is, we don't touch it.
if '%' not in parsed[idx]:
parsed[idx] = quote(parsed[idx],
- safe=rfc2396_unreserved + idx_safe)
+ safe=rfc3986_unreserved + idx_safe)
location = urlunparse(parsed)
self.setStatus(status, lock=lock)
diff --git a/src/ZPublisher/tests/testHTTPResponse.py b/src/ZPublisher/tests/testHTTPResponse.py
index a7f816c04..08a1674ba 100644
--- a/src/ZPublisher/tests/testHTTPResponse.py
+++ b/src/ZPublisher/tests/testHTTPResponse.py
@@ -767,15 +767,19 @@ class HTTPResponseTests(unittest.TestCase):
self._redirectURLCheck(ENC_URL)
def test_redirect_unreserved_chars(self):
- # RFC 2396 section 2.3, characters that should not be encoded
+ # RFC 3986 section 2.3, characters that should not be encoded
url = "http://example.com/-_.!~*'()"
self._redirectURLCheck(url)
def test_redirect_reserved_chars(self):
- # RFC 2396 section 3.3, characters with reserved meaning in a path
+ # RFC 3986 section 3.3, characters with reserved meaning in a path
url = 'http://example.com/+/$/;/,/=/?/&/@@index.html'
self._redirectURLCheck(url)
+ def test_redirect_ipv6(self):
+ url = "http://[fe80::1ff:fe23:4567:890a]:1234"
+ self._redirectURLCheck(url)
+
def test__encode_unicode_no_content_type_uses_default_encoding(self):
UNICODE = u'<h1>Tr\u0039s Bien</h1>'
response = self._makeOne()
--
2.42.0
From 1f0be881320f440cec05eb838fa42c5ddf56a57c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 19 Sep 2023 15:19:51 +0900
Subject: [PATCH] WSGIPublisher: set REMOTE_USER even in case of error (#1156)
This fixes a problem that user name was empty in access log for error pages.
Fixes #1155
---------
Co-authored-by: Michael Howitz <icemac@gmx.net>
---
src/ZPublisher/WSGIPublisher.py | 13 +++++++------
src/ZPublisher/tests/test_WSGIPublisher.py | 9 +++++++++
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/src/ZPublisher/WSGIPublisher.py b/src/ZPublisher/WSGIPublisher.py
index bd0ae1d17..09b2c44c9 100644
--- a/src/ZPublisher/WSGIPublisher.py
+++ b/src/ZPublisher/WSGIPublisher.py
@@ -382,12 +382,13 @@ def publish_module(environ, start_response,
try:
with load_app(module_info) as new_mod_info:
with transaction_pubevents(request, response):
- response = _publish(request, new_mod_info)
-
- user = getSecurityManager().getUser()
- if user is not None and \
- user.getUserName() != 'Anonymous User':
- environ['REMOTE_USER'] = user.getUserName()
+ try:
+ response = _publish(request, new_mod_info)
+ finally:
+ user = getSecurityManager().getUser()
+ if user is not None and \
+ user.getUserName() != 'Anonymous User':
+ environ['REMOTE_USER'] = user.getUserName()
break
except TransientError:
if request.supports_retry():
diff --git a/src/ZPublisher/tests/test_WSGIPublisher.py b/src/ZPublisher/tests/test_WSGIPublisher.py
index 989970f24..38e402ab3 100644
--- a/src/ZPublisher/tests/test_WSGIPublisher.py
+++ b/src/ZPublisher/tests/test_WSGIPublisher.py
@@ -822,6 +822,15 @@ class TestPublishModule(ZopeTestCase):
self._callFUT(environ, start_response, _publish)
self.assertFalse('REMOTE_USER' in environ)
+ def test_set_REMOTE_USER_environ_error(self):
+ environ = self._makeEnviron()
+ start_response = DummyCallable()
+ _publish = DummyCallable()
+ _publish._raise = ValueError()
+ with self.assertRaises(ValueError):
+ self._callFUT(environ, start_response, _publish)
+ self.assertEqual(environ['REMOTE_USER'], user_name)
+
def test_webdav_source_port(self):
from ZPublisher import WSGIPublisher
old_webdav_source_port = WSGIPublisher._WEBDAV_SOURCE_PORT
--
2.39.2
From c56146829ab065183c709229a9daa682cc445212 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Fri, 26 Apr 2024 15:09:39 +0900
Subject: [PATCH] fix loading font for ean13
use same technique as for code128
---
hubarcode/ean13/renderer.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/hubarcode/ean13/renderer.py b/hubarcode/ean13/renderer.py
index 654501e..ff5f518 100644
--- a/hubarcode/ean13/renderer.py
+++ b/hubarcode/ean13/renderer.py
@@ -78,8 +78,10 @@ class EAN13Renderer:
# Draw the text
font_size = font_sizes.get(bar_width, 24)
- # Use relative name, PIL will do searching for us
- fontfile = os.path.join("fonts", "courR%02d.pil" % font_size)
+ # Locate and load the font file relative to the module
+ ean13dir, _ = os.path.split(__file__)
+ rootdir, _ = os.path.split(ean13dir)
+ fontfile = os.path.join(rootdir, "fonts", "courR%02d.pil" % font_size)
font = ImageFont.load_path(fontfile)
draw = ImageDraw.Draw(img)
--
2.42.0
This diff is collapsed.
diff -ur urlnorm-1.1.4.orig/setup.py urlnorm-1.1.4/setup.py
--- urlnorm-1.1.4.orig/setup.py 2016-08-05 20:07:24.000000000 +0200
+++ urlnorm-1.1.4/setup.py 2022-10-21 09:32:35.377477901 +0200
@@ -9,8 +9,15 @@
description="Normalize a URL to a standard unicode encoding",
py_modules=['urlnorm'],
license='MIT License',
+ install_requires=['six'],
author='Jehiah Czebotar',
author_email='jehiah@gmail.com',
url='http://github.com/jehiah/urlnorm',
download_url="http://github.com/downloads/jehiah/urlnorm/urlnorm-%s.tar.gz" % version,
+ classifiers=[
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.6',
+ ],
)
diff -ur urlnorm-1.1.4.orig/urlnorm.py urlnorm-1.1.4/urlnorm.py
--- urlnorm-1.1.4.orig/urlnorm.py 2016-08-05 20:06:32.000000000 +0200
+++ urlnorm-1.1.4/urlnorm.py 2022-10-21 09:32:35.377477901 +0200
@@ -41,6 +41,10 @@
- more fine-grained authority parsing and normalisation
"""
+from __future__ import absolute_import
+from six import unichr
+import six
+from six.moves import range
__license__ = """
Copyright (c) 1999-2002 Mark Nottingham <mnot@pobox.com>
Copyright (c) 2010 Jehiah Czebotar <jehiah@gmail.com>
@@ -67,8 +71,7 @@
# also update in setup.py
__version__ = "1.1.4"
-from urlparse import urlparse, urlunparse
-from string import lower
+from six.moves.urllib.parse import urlparse, urlunparse, unquote
import re
@@ -108,8 +111,8 @@
qs_unsafe_list = set('?&=+%#')
fragment_unsafe_list = set('+%#')
path_unsafe_list = set('/?;%+#')
-_hextochr = dict(('%02x' % i, chr(i)) for i in range(256))
-_hextochr.update(('%02X' % i, chr(i)) for i in range(256))
+_hextochr = dict((b'%02x' % i, six.int2byte(i)) for i in range(256))
+_hextochr.update((b'%02X' % i, six.int2byte(i)) for i in range(256))
def unquote_path(s):
@@ -132,22 +135,23 @@
"""unquote percent escaped string except for percent escape sequences that are in unsafe_list"""
# note: this build utf8 raw strings ,then does a .decode('utf8') at the end.
# as a result it's doing .encode('utf8') on each block of the string as it's processed.
- res = _utf8(s).split('%')
- for i in xrange(1, len(res)):
+ unsafe_list = [_utf8(i) for i in unsafe_list]
+ res = _utf8(s).split(b'%')
+ for i in range(1, len(res)):
item = res[i]
try:
raw_chr = _hextochr[item[:2]]
if raw_chr in unsafe_list or ord(raw_chr) < 20:
# leave it unescaped (but uppercase the percent escape)
- res[i] = '%' + item[:2].upper() + item[2:]
+ res[i] = b'%' + item[:2].upper() + item[2:]
else:
res[i] = raw_chr + item[2:]
except KeyError:
- res[i] = '%' + item
+ res[i] = b'%' + item
except UnicodeDecodeError:
# note: i'm not sure what this does
res[i] = unichr(int(item[:2], 16)) + item[2:]
- o = "".join(res)
+ o = b"".join(res)
return _unicode(o)
@@ -160,7 +164,7 @@
def norm_tuple(scheme, authority, path, parameters, query, fragment):
"""given individual url components, return its normalized form"""
- scheme = lower(scheme)
+ scheme = scheme.lower()
if not scheme:
raise InvalidUrl('missing URL scheme')
authority = norm_netloc(scheme, authority)
@@ -203,7 +207,7 @@
return '/'
return path
-MAX_IP = 0xffffffffL
+MAX_IP = 0xffffffff
def int2ip(ipnum):
@@ -238,7 +242,7 @@
if '.' not in host and not (host[0] == '[' and host[-1] == ']'):
raise InvalidUrl('host %r is not valid' % host)
- authority = lower(host)
+ authority = host.lower()
if 'xn--' in authority:
subdomains = [_idn(subdomain) for subdomain in authority.split('.')]
authority = '.'.join(subdomains)
@@ -260,14 +264,14 @@
def _utf8(value):
- if isinstance(value, unicode):
+ if isinstance(value, six.text_type):
return value.encode("utf-8")
assert isinstance(value, str)
return value
def _unicode(value):
- if isinstance(value, str):
+ if isinstance(value, six.binary_type):
return value.decode("utf-8")
- assert isinstance(value, unicode)
+ assert isinstance(value, six.text_type)
return value
From: Bert JW Regeer <bertjw@regeer.org>
Date: Sat, 12 Mar 2022 18:30:30 -0700
Subject: Add new regular expressions for Chunked Encoding
This also moves some regular expressions for QUOTED_PAIR/QUOTED_STRING
into this module from utilities so that they may be reused.
Part of CVE-2022-24761
---
src/waitress/rfc7230.py | 27 ++++++++++++++++++++++++++-
src/waitress/utilities.py | 28 +++-------------------------
2 files changed, 29 insertions(+), 26 deletions(-)
diff --git a/src/waitress/rfc7230.py b/src/waitress/rfc7230.py
index cd33c90..0b76a38 100644
--- a/src/waitress/rfc7230.py
+++ b/src/waitress/rfc7230.py
@@ -7,6 +7,9 @@ import re
from .compat import tobytes
+HEXDIG = "[0-9a-fA-F]"
+DIGIT = "[0-9]"
+
WS = "[ \t]"
OWS = WS + "{0,}?"
RWS = WS + "{1,}?"
@@ -27,6 +30,12 @@ TOKEN = TCHAR + "{1,}"
# ; visible (printing) characters
VCHAR = r"\x21-\x7e"
+# The '\\' between \x5b and \x5d is needed to escape \x5d (']')
+QDTEXT = "[\t \x21\x23-\x5b\\\x5d-\x7e" + OBS_TEXT + "]"
+
+QUOTED_PAIR = r"\\" + "([\t " + VCHAR + OBS_TEXT + "])"
+QUOTED_STRING = '"(?:(?:' + QDTEXT + ")|(?:" + QUOTED_PAIR + '))*"'
+
# header-field = field-name ":" OWS field-value OWS
# field-name = token
# field-value = *( field-content / obs-fold )
@@ -45,8 +54,24 @@ FIELD_CONTENT = FIELD_VCHAR + "+(?:[ \t]+" + FIELD_VCHAR + "+)*"
# Which allows the field value here to just see if there is even a value in the first place
FIELD_VALUE = "(?:" + FIELD_CONTENT + ")?"
-HEADER_FIELD = re.compile(
+# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+# chunk-ext-name = token
+# chunk-ext-val = token / quoted-string
+
+CHUNK_EXT_NAME = TOKEN
+CHUNK_EXT_VAL = "(?:" + TOKEN + ")|(?:" + QUOTED_STRING + ")"
+CHUNK_EXT = (
+ "(?:;(?P<extension>" + CHUNK_EXT_NAME + ")(?:=(?P<value>" + CHUNK_EXT_VAL + "))?)*"
+)
+
+# Pre-compiled regular expressions for use elsewhere
+ONLY_HEXDIG_RE = re.compile(("^" + HEXDIG + "+$").encode("latin-1"))
+ONLY_DIGIT_RE = re.compile(("^" + DIGIT + "+$").encode("latin-1"))
+HEADER_FIELD_RE = re.compile(
tobytes(
"^(?P<name>" + TOKEN + "):" + OWS + "(?P<value>" + FIELD_VALUE + ")" + OWS + "$"
)
)
+QUOTED_PAIR_RE = re.compile(QUOTED_PAIR)
+QUOTED_STRING_RE = re.compile(QUOTED_STRING)
+CHUNK_EXT_RE = re.compile(("^" + CHUNK_EXT + "$").encode("latin-1"))
diff --git a/src/waitress/utilities.py b/src/waitress/utilities.py
index 556bed2..fa59657 100644
--- a/src/waitress/utilities.py
+++ b/src/waitress/utilities.py
@@ -22,7 +22,7 @@ import re
import stat
import time
-from .rfc7230 import OBS_TEXT, VCHAR
+from .rfc7230 import QUOTED_PAIR_RE, QUOTED_STRING_RE
logger = logging.getLogger("waitress")
queue_logger = logging.getLogger("waitress.queue")
@@ -216,32 +216,10 @@ def parse_http_date(d):
return retval
-# RFC 5234 Appendix B.1 "Core Rules":
-# VCHAR = %x21-7E
-# ; visible (printing) characters
-vchar_re = VCHAR
-
-# RFC 7230 Section 3.2.6 "Field Value Components":
-# quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
-# qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
-# obs-text = %x80-FF
-# quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
-obs_text_re = OBS_TEXT
-
-# The '\\' between \x5b and \x5d is needed to escape \x5d (']')
-qdtext_re = "[\t \x21\x23-\x5b\\\x5d-\x7e" + obs_text_re + "]"
-
-quoted_pair_re = r"\\" + "([\t " + vchar_re + obs_text_re + "])"
-quoted_string_re = '"(?:(?:' + qdtext_re + ")|(?:" + quoted_pair_re + '))*"'
-
-quoted_string = re.compile(quoted_string_re)
-quoted_pair = re.compile(quoted_pair_re)
-
-
def undquote(value):
if value.startswith('"') and value.endswith('"'):
# So it claims to be DQUOTE'ed, let's validate that
- matches = quoted_string.match(value)
+ matches = QUOTED_STRING_RE.match(value)
if matches and matches.end() == len(value):
# Remove the DQUOTE's from the value
@@ -249,7 +227,7 @@ def undquote(value):
# Remove all backslashes that are followed by a valid vchar or
# obs-text
- value = quoted_pair.sub(r"\1", value)
+ value = QUOTED_PAIR_RE.sub(r"\1", value)
return value
elif not value.startswith('"') and not value.endswith('"'):
From: Bert JW Regeer <bertjw@regeer.org>
Date: Sat, 12 Mar 2022 18:32:24 -0700
Subject: Be more strict in parsing Content-Length
Validate that we are only parsing digits and nothing else. RFC7230 is
explicit in that the Content-Length can only exist of 1*DIGIT and may
not include any additional sign information.
The Python int() function parses `+10` as `10` which means we were more
lenient than the standard intended.
Part of CVE-2022-24761
---
src/waitress/parser.py | 12 ++++++------
tests/test_parser.py | 24 ++++++++++++++++++++++++
2 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/src/waitress/parser.py b/src/waitress/parser.py
index 765fe59..acaf494 100644
--- a/src/waitress/parser.py
+++ b/src/waitress/parser.py
@@ -22,6 +22,7 @@ from io import BytesIO
from waitress.buffers import OverflowableBuffer
from waitress.compat import tostr, unquote_bytes_to_wsgi, urlparse
from waitress.receiver import ChunkedReceiver, FixedStreamReceiver
+from waitress.rfc7230 import HEADER_FIELD_RE, ONLY_DIGIT_RE
from waitress.utilities import (
BadRequest,
RequestEntityTooLarge,
@@ -29,8 +30,6 @@ from waitress.utilities import (
ServerNotImplemented,
find_double_newline,
)
-from .rfc7230 import HEADER_FIELD
-
class ParsingError(Exception):
pass
@@ -209,7 +208,7 @@ class HTTPRequestParser(object):
headers = self.headers
for line in lines:
- header = HEADER_FIELD.match(line)
+ header = HEADER_FIELD_RE.match(line)
if not header:
raise ParsingError("Invalid header")
@@ -299,11 +298,12 @@ class HTTPRequestParser(object):
self.connection_close = True
if not self.chunked:
- try:
- cl = int(headers.get("CONTENT_LENGTH", 0))
- except ValueError:
+ cl = headers.get("CONTENT_LENGTH", "0")
+
+ if not ONLY_DIGIT_RE.match(cl.encode("latin-1")):
raise ParsingError("Content-Length is invalid")
+ cl = int(cl)
self.content_length = cl
if cl > 0:
buf = OverflowableBuffer(self.adj.inbuf_overflow)
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 91837c7..eabf353 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -194,6 +194,30 @@ class TestHTTPRequestParser(unittest.TestCase):
else: # pragma: nocover
self.assertTrue(False)
+ def test_parse_header_bad_content_length_plus(self):
+ from waitress.parser import ParsingError
+
+ data = b"GET /foobar HTTP/8.4\r\ncontent-length: +10\r\n"
+
+ try:
+ self.parser.parse_header(data)
+ except ParsingError as e:
+ self.assertIn("Content-Length is invalid", e.args[0])
+ else: # pragma: nocover
+ self.assertTrue(False)
+
+ def test_parse_header_bad_content_length_minus(self):
+ from waitress.parser import ParsingError
+
+ data = b"GET /foobar HTTP/8.4\r\ncontent-length: -10\r\n"
+
+ try:
+ self.parser.parse_header(data)
+ except ParsingError as e:
+ self.assertIn("Content-Length is invalid", e.args[0])
+ else: # pragma: nocover
+ self.assertTrue(False)
+
def test_parse_header_multiple_content_length(self):
from waitress.parser import ParsingError
From: Bert JW Regeer <bertjw@regeer.org>
Date: Sat, 12 Mar 2022 18:35:01 -0700
Subject: Update tests to remove invalid chunked encoding chunk-size
RFC7230 states the following:
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
chunk-size = 1*HEXDIG
Where chunk-ext is:
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
Only if there is a chunk-ext should there be a `;` after the 1*HEXDIG.
And a chunk-ext that is empty is invalid.
Part of CVE-2022-24761
---
tests/test_functional.py | 6 +++---
tests/test_parser.py | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/test_functional.py b/tests/test_functional.py
index e894497..7a54b22 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -302,7 +302,7 @@ class EchoTests(object):
self.assertFalse("transfer-encoding" in headers)
def test_chunking_request_with_content(self):
- control_line = b"20;\r\n" # 20 hex = 32 dec
+ control_line = b"20\r\n" # 20 hex = 32 dec
s = b"This string has 32 characters.\r\n"
expected = s * 12
header = tobytes("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n")
@@ -321,7 +321,7 @@ class EchoTests(object):
self.assertFalse("transfer-encoding" in headers)
def test_broken_chunked_encoding(self):
- control_line = "20;\r\n" # 20 hex = 32 dec
+ control_line = "20\r\n" # 20 hex = 32 dec
s = "This string has 32 characters.\r\n"
to_send = "GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
to_send += control_line + s + "\r\n"
@@ -346,7 +346,7 @@ class EchoTests(object):
self.assertRaises(ConnectionClosed, read_http, fp)
def test_broken_chunked_encoding_missing_chunk_end(self):
- control_line = "20;\r\n" # 20 hex = 32 dec
+ control_line = "20\r\n" # 20 hex = 32 dec
s = "This string has 32 characters.\r\n"
to_send = "GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
to_send += control_line + s
diff --git a/tests/test_parser.py b/tests/test_parser.py
index eabf353..420f280 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -152,7 +152,7 @@ class TestHTTPRequestParser(unittest.TestCase):
b"Transfer-Encoding: chunked\r\n"
b"X-Foo: 1\r\n"
b"\r\n"
- b"1d;\r\n"
+ b"1d\r\n"
b"This string has 29 characters\r\n"
b"0\r\n\r\n"
)
From: Bert JW Regeer <bertjw@regeer.org>
Date: Sat, 12 Mar 2022 18:42:51 -0700
Subject: Error when receiving back Chunk Extension
Waitress discards chunked extensions and does no further processing on
them, however it failed to validate that the chunked encoding extension
did not contain invalid data.
We now validate that if there are any chunked extensions that they are
well-formed, if they are not and contain invalid characters, then
Waitress will now correctly return a Bad Request and stop any further
processing of the request.
Part of CVE-2022-24761
---
src/waitress/receiver.py | 11 ++++++++++-
tests/test_functional.py | 22 ++++++++++++++++++++++
tests/test_receiver.py | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/src/waitress/receiver.py b/src/waitress/receiver.py
index 5d1568d..106dbc7 100644
--- a/src/waitress/receiver.py
+++ b/src/waitress/receiver.py
@@ -14,6 +14,7 @@
"""Data Chunk Receiver
"""
+from waitress.rfc7230 import CHUNK_EXT_RE, ONLY_HEXDIG_RE
from waitress.utilities import BadRequest, find_double_newline
@@ -110,6 +111,7 @@ class ChunkedReceiver(object):
s = b""
else:
self.chunk_end = b""
+
if pos == 0:
# Chop off the terminating CR LF from the chunk
s = s[2:]
@@ -140,7 +142,14 @@ class ChunkedReceiver(object):
semi = line.find(b";")
if semi >= 0:
- # discard extension info.
+ extinfo = line[semi:]
+ valid_ext_info = CHUNK_EXT_RE.match(extinfo)
+
+ if not valid_ext_info:
+ self.error = BadRequest("Invalid chunk extension")
+ self.all_chunks_received = True
+
+ break
line = line[:semi]
try:
sz = int(line.strip(), 16) # hexadecimal
diff --git a/tests/test_functional.py b/tests/test_functional.py
index 7a54b22..853942c 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -345,6 +345,28 @@ class EchoTests(object):
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
+ def test_broken_chunked_encoding_invalid_extension(self):
+ control_line = b"20;invalid=\r\n" # 20 hex = 32 dec
+ s = b"This string has 32 characters.\r\n"
+ to_send = b"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
+ to_send += control_line + s + b"\r\n"
+ self.connect()
+ self.sock.send(to_send)
+ with self.sock.makefile("rb", 0) as fp:
+ line, headers, response_body = read_http(fp)
+ self.assertline(line, "400", "Bad Request", "HTTP/1.1")
+ cl = int(headers["content-length"])
+ self.assertEqual(cl, len(response_body))
+ self.assertIn(b"Invalid chunk extension", response_body)
+ self.assertEqual(
+ sorted(headers.keys()),
+ ["connection", "content-length", "content-type", "date", "server"],
+ )
+ self.assertEqual(headers["content-type"], "text/plain")
+ # connection has been closed
+ self.send_check_error(to_send)
+ self.assertRaises(ConnectionClosed, read_http, fp)
+
def test_broken_chunked_encoding_missing_chunk_end(self):
control_line = "20\r\n" # 20 hex = 32 dec
s = "This string has 32 characters.\r\n"
diff --git a/tests/test_receiver.py b/tests/test_receiver.py
index b4910bb..a6261ea 100644
--- a/tests/test_receiver.py
+++ b/tests/test_receiver.py
@@ -1,5 +1,7 @@
import unittest
+import pytest
+
class TestFixedStreamReceiver(unittest.TestCase):
def _makeOne(self, cl, buf):
@@ -226,6 +228,41 @@ class TestChunkedReceiver(unittest.TestCase):
self.assertEqual(inst.error, None)
+class TestChunkedReceiverParametrized:
+ def _makeOne(self, buf):
+ from waitress.receiver import ChunkedReceiver
+
+ return ChunkedReceiver(buf)
+
+ @pytest.mark.parametrize(
+ "invalid_extension", [b"\n", b"invalid=", b"\r", b"invalid = true"]
+ )
+ def test_received_invalid_extensions(self, invalid_extension):
+ from waitress.utilities import BadRequest
+
+ buf = DummyBuffer()
+ inst = self._makeOne(buf)
+ data = b"4;" + invalid_extension + b"\r\ntest\r\n"
+ result = inst.received(data)
+ assert result == len(data)
+ assert inst.error.__class__ == BadRequest
+ assert inst.error.body == "Invalid chunk extension"
+
+ @pytest.mark.parametrize(
+ "valid_extension", [b"test", b"valid=true", b"valid=true;other=true"]
+ )
+ def test_received_valid_extensions(self, valid_extension):
+ # While waitress may ignore extensions in Chunked Encoding, we do want
+ # to make sure that we don't fail when we do encounter one that is
+ # valid
+ buf = DummyBuffer()
+ inst = self._makeOne(buf)
+ data = b"4;" + valid_extension + b"\r\ntest\r\n"
+ result = inst.received(data)
+ assert result == len(data)
+ assert inst.error == None
+
+
class DummyBuffer(object):
def __init__(self, data=None):
if data is None:
From: Bert JW Regeer <bertjw@regeer.org>
Date: Sat, 12 Mar 2022 18:48:26 -0700
Subject: Validate chunk size in Chunked Encoding are HEXDIG
RFC7230 states that a chunk-size should be 1*HEXDIG, this is now
validated before passing the resulting string to int() which would also
parse other formats for hex, such as: `0x01` as `1` and `+0x01` as `1`.
This would lead to a potential for a frontend proxy server and waitress
to disagree on where a chunk started and ended, thereby potentially
leading to request smuggling.
With the increased validation if the size is not just hex digits,
Waitress now returns a Bad Request and stops processing the request.
Part of CVE-2022-24761
---
src/waitress/receiver.py | 19 ++++++++++++++-----
tests/test_functional.py | 22 ++++++++++++++++++++++
tests/test_receiver.py | 12 ++++++++++++
3 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/src/waitress/receiver.py b/src/waitress/receiver.py
index 106dbc7..9e4bffe 100644
--- a/src/waitress/receiver.py
+++ b/src/waitress/receiver.py
@@ -150,12 +150,21 @@ class ChunkedReceiver(object):
self.all_chunks_received = True
break
+
line = line[:semi]
- try:
- sz = int(line.strip(), 16) # hexadecimal
- except ValueError: # garbage in input
- self.error = BadRequest("garbage in chunked encoding input")
- sz = 0
+
+ # Remove any whitespace
+ line = line.strip()
+
+ if not ONLY_HEXDIG_RE.match(line):
+ self.error = BadRequest("Invalid chunk size")
+ self.all_chunks_received = True
+
+ break
+
+ # Can not fail due to matching against the regular
+ # expression above
+ sz = int(line.strip(), 16) # hexadecimal
if sz > 0:
# Start a new chunk.
diff --git a/tests/test_functional.py b/tests/test_functional.py
index 853942c..448e0c0 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -345,6 +345,28 @@ class EchoTests(object):
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
+ def test_broken_chunked_encoding_invalid_hex(self):
+ control_line = b"0x20\r\n" # 20 hex = 32 dec
+ s = b"This string has 32 characters.\r\n"
+ to_send = b"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
+ to_send += control_line + s + b"\r\n"
+ self.connect()
+ self.sock.send(to_send)
+ with self.sock.makefile("rb", 0) as fp:
+ line, headers, response_body = read_http(fp)
+ self.assertline(line, "400", "Bad Request", "HTTP/1.1")
+ cl = int(headers["content-length"])
+ self.assertEqual(cl, len(response_body))
+ self.assertIn(b"Invalid chunk size", response_body)
+ self.assertEqual(
+ sorted(headers.keys()),
+ ["connection", "content-length", "content-type", "date", "server"],
+ )
+ self.assertEqual(headers["content-type"], "text/plain")
+ # connection has been closed
+ self.send_check_error(to_send)
+ self.assertRaises(ConnectionClosed, read_http, fp)
+
def test_broken_chunked_encoding_invalid_extension(self):
control_line = b"20;invalid=\r\n" # 20 hex = 32 dec
s = b"This string has 32 characters.\r\n"
diff --git a/tests/test_receiver.py b/tests/test_receiver.py
index a6261ea..17328d4 100644
--- a/tests/test_receiver.py
+++ b/tests/test_receiver.py
@@ -262,6 +262,18 @@ class TestChunkedReceiverParametrized:
assert result == len(data)
assert inst.error == None
+ @pytest.mark.parametrize("invalid_size", [b"0x04", b"+0x04", b"x04", b"+04"])
+ def test_received_invalid_size(self, invalid_size):
+ from waitress.utilities import BadRequest
+
+ buf = DummyBuffer()
+ inst = self._makeOne(buf)
+ data = invalid_size + b"\r\ntest\r\n"
+ result = inst.received(data)
+ assert result == len(data)
+ assert inst.error.__class__ == BadRequest
+ assert inst.error.body == "Invalid chunk size"
+
class DummyBuffer(object):
def __init__(self, data=None):
From: Bert JW Regeer <bertjw@regeer.org>
Date: Sat, 12 Mar 2022 19:16:23 -0700
Subject: Remove extraneous calls to .strip() in Chunked Encoding
To be valid chunked encoding we should not be removing any whitespace as
the standard does not allow for optional whitespace.
If whitespace is encountered in the wrong place, it should lead to a 400
Bad Request instead.
Part of CVE-2022-24761
---
src/waitress/receiver.py | 6 +-----
tests/test_receiver.py | 4 +++-
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/waitress/receiver.py b/src/waitress/receiver.py
index 9e4bffe..806ff87 100644
--- a/src/waitress/receiver.py
+++ b/src/waitress/receiver.py
@@ -135,7 +135,6 @@ class ChunkedReceiver(object):
line = s[:pos]
s = s[pos + 2 :]
self.control_line = b""
- line = line.strip()
if line:
# Begin a new chunk.
@@ -153,9 +152,6 @@ class ChunkedReceiver(object):
line = line[:semi]
- # Remove any whitespace
- line = line.strip()
-
if not ONLY_HEXDIG_RE.match(line):
self.error = BadRequest("Invalid chunk size")
self.all_chunks_received = True
@@ -164,7 +160,7 @@ class ChunkedReceiver(object):
# Can not fail due to matching against the regular
# expression above
- sz = int(line.strip(), 16) # hexadecimal
+ sz = int(line, 16) # hexadecimal
if sz > 0:
# Start a new chunk.
diff --git a/tests/test_receiver.py b/tests/test_receiver.py
index 17328d4..014f785 100644
--- a/tests/test_receiver.py
+++ b/tests/test_receiver.py
@@ -262,7 +262,9 @@ class TestChunkedReceiverParametrized:
assert result == len(data)
assert inst.error == None
- @pytest.mark.parametrize("invalid_size", [b"0x04", b"+0x04", b"x04", b"+04"])
+ @pytest.mark.parametrize(
+ "invalid_size", [b"0x04", b"+0x04", b"x04", b"+04", b" 04", b" 0x04"]
+ )
def test_received_invalid_size(self, invalid_size):
from waitress.utilities import BadRequest
[buildout]
parts = embulk
[embulk]
recipe = slapos.recipe.build:download
url = https://dl.bintray.com/embulk/maven/embulk-0.9.7.jar
md5sum = 05f41d3750ec359fc10c1dc3e30f238e
filename = embulk.jar
# ethtool - query or control network driver and hardware settings
# https://www.kernel.org/pub/software/network/ethtool
[buildout]
extends =
../xz-utils/buildout.cfg
parts = ethtool
[ethtool]
recipe = slapos.recipe.cmmi
url = https://www.kernel.org/pub/software/network/ethtool/ethtool-4.11.tar.xz
md5sum = 16d38f4ebe23e44f96f7d8b38ed3652c
environment =
PATH=${xz-utils:location}/bin:%(PATH)s
......@@ -3,6 +3,7 @@ extends =
../bzip2/buildout.cfg
../libpng/buildout.cfg
../patch/buildout.cfg
../ncurses/buildout.cfg
../pkgconfig/buildout.cfg
../zlib/buildout.cfg
../xorg/buildout.cfg
......@@ -86,6 +87,10 @@ md5sum = 8e9866ad6b570c6c95c8cba48060473f
configure-options =
--disable-static
--disable-gtktest
environment =
PKG_CONFIG=${pkgconfig:location}/bin/pkgconfig
PKG_CONFIG_PATH=${ncurses:location}/lib/pkgconfig
LDFLAGS=-Wl,-rpath=${ncurses:location}/lib
[opencore-amr]
recipe = slapos.recipe.cmmi
......@@ -98,6 +103,8 @@ configure-options =
recipe = slapos.recipe.cmmi
url = https://ffmpeg.org/releases/ffmpeg-4.1.4.tar.bz2
md5sum = 611d171e4aee749b85e04d17e2aee71d
patches = https://git.ffmpeg.org/gitweb/ffmpeg.git/commitdiff_plain/effadce6c756247ea8bae32dc13bb3e6f464f0eb#adf42203d59a3c98a863216113946661
patch-options = -p1
location = @@LOCATION@@
pkg_config_depends = ${libxcb:location}/lib/pkgconfig:${libxcb:pkg_config_depends}:${libtheora:location}/lib/pkgconfig:${libtheora:pkg_config_depends}:${libvpx:location}/lib/pkgconfig:${libx264:location}/lib/pkgconfig:${opencore-amr:location}/lib/pkgconfig:${zlib:location}/lib/pkgconfig
configure-options =
......@@ -123,4 +130,4 @@ environment =
PKG_CONFIG_PATH=${:pkg_config_depends}
CPPFLAGS=-I${bzip2:location}/include -I${libogg:location}/include -I${libtheora:location}/include -I${opencore-amr:location}/include -I${lame:location}/include
LDFLAGS=-Wl,-rpath=${:location}/lib -L${bzip2:location}/lib -Wl,-rpath=${bzip2:location}/lib -Wl,-rpath=${libxcb:location}/lib -L${libogg:location}/lib -L${libvorbis:location}/lib -Wl,-rpath=${libvorbis:location}/lib -L${libtheora:location}/lib -Wl,-rpath=${libtheora:location}/lib -L${libvpx:location}/lib -Wl,-rpath=${libvpx:location}/lib -L${libx264:location}/lib -Wl,-rpath=${libx264:location}/lib -L${lame:location}/lib -Wl,-rpath=${lame:location}/lib -L${opencore-amr:location}/lib -Wl,-rpath=${opencore-amr:location}/lib -Wl,-rpath=${zlib:location}/lib
PATH=${pkgconfig:location}/bin:${yasm:location}/bin:%(PATH)s
PATH=${patch:location}/bin:${pkgconfig:location}/bin:${yasm:location}/bin:%(PATH)s
......@@ -7,18 +7,20 @@ extends =
../bzip2/buildout.cfg
../xz-utils/buildout.cfg
../zlib/buildout.cfg
../zstd/buildout.cfg
[file]
recipe = slapos.recipe.cmmi
shared = true
url = http://ftp.icm.edu.pl/packages/file/file-5.41.tar.gz
md5sum = 18233bb0a0089dfdc7dfbc93b96f231b
url = http://ftp.astron.com/pub/file/file-5.45.tar.gz
md5sum = 26b2a96d4e3a8938827a1e572afd527a
configure-options =
--disable-static
--disable-libseccomp
--enable-zlib
--enable-bzlib
--enable-xzlib
--enable-zstdlib
environment =
CPPFLAGS=-I${bzip2:location}/include -I${xz-utils:location}/include -I${zlib:location}/include
LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath=${bzip2:location}/lib -L${xz-utils:location}/lib -Wl,-rpath=${xz-utils:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
CPPFLAGS=-I${bzip2:location}/include -I${xz-utils:location}/include -I${zlib:location}/include -I${zstd:location}/include
LDFLAGS=-L${bzip2:location}/lib -Wl,-rpath=${bzip2:location}/lib -L${xz-utils:location}/lib -Wl,-rpath=${xz-utils:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib -L${zstd:location}/lib -Wl,-rpath=${zstd:location}/lib
[buildout]
extends =
../coreutils/buildout.cfg
../xz-utils/buildout.cfg
parts =
findutils-output
[findutils]
recipe = slapos.recipe.cmmi
shared = true
url = http://ftp.debian.org/debian/pool/main/f/findutils/findutils_4.8.0.orig.tar.xz
md5sum = eeefe2e6380931a77dfa6d9350b43186
url = http://ftp.debian.org/debian/pool/main/f/findutils/findutils_4.9.0.orig.tar.xz
md5sum = 4a4a547e888a944b2f3af31d789a1137
environment =
PATH=${xz-utils:location}/bin:%(PATH)s
[findutils-output]
# Shared binary location to ease migration
......
This diff is collapsed.
......@@ -4,13 +4,14 @@ parts =
extends =
../defaults.cfg
../libtool/buildout.cfg
../intltool/buildout.cfg
../glib/buildout.cfg
../python-slip/buildout.cfg
../dbus/buildout.cfg
../flex/buildout.cfg
../glib/buildout.cfg
../gnu-config/buildout.cfg
../intltool/buildout.cfg
../libtool/buildout.cfg
../nftables/buildout.cfg
../python-slip/buildout.cfg
[firewalld]
recipe = slapos.recipe.cmmi
......@@ -38,7 +39,7 @@ configure-options =
--with-ipset=/bin/false
environment =
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${pcre:location}/lib/pkgconfig
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${glib:pkg_config_depends}
PATH=${autoconf:location}/bin:${automake:location}/bin:${pkgconfig:location}/bin:${libtool:location}/bin:${intltool:location}/bin:${gettext:location}/bin:${glib:location}/bin:${perl:location}/bin:%(PATH)s
CPPFLAGS=-I${gettext:location}/include
M4=${m4:location}/bin/m4
......@@ -53,7 +54,7 @@ url = http://dbus.freedesktop.org/releases/dbus-python/dbus-python-${:version}.t
md5sum = 51a45c973d82bedff033a4b57d69d5d8
python-egg = ${buildout:parts-directory}/${:_buildout_section_name_}/lib/python${python:version}/site-packages
environment =
PKG_CONFIG_PATH=${dbus:location}/lib/pkgconfig:${dbus-glib:location}/lib/pkgconfig:${glib:location}/lib/pkgconfig:${pcre:location}/lib/pkgconfig
PKG_CONFIG_PATH=${dbus:location}/lib/pkgconfig:${dbus-glib:location}/lib/pkgconfig:${glib:location}/lib/pkgconfig:${glib:pkg_config_depends}
PATH=${pkgconfig:location}/bin:%(PATH)s
DBUS_CFLAGS=-I${dbus:location}/include/dbus-1.0 -I${dbus:location}/lib/dbus-1.0/include
DBUS_LIBS=-L${dbus:location}/lib -ldbus-1
......@@ -67,16 +68,17 @@ environment =
recipe = slapos.recipe.cmmi
url = http://ftp.gnome.org/pub/gnome/core/3.22/3.22.2/sources/gobject-introspection-1.50.0.tar.xz
md5sum = 5af8d724f25d0c9cfbe6df41b77e5dc0
pre-configure = cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess build-aux/
configure-options =
--disable-static
environment =
PATH=${pkgconfig:location}/bin:${gettext:location}/bin:${glib:location}/bin:${xz-utils:location}/bin:${flex:location}/bin:${bison:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${pcre:location}/lib/pkgconfig
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${glib:pkg_config_depends}
CPPFLAGS=-I${glib:location}/include/glib-2.0 -I${glib:location}/lib/glib-2.0/include
LDFLAGS=-L${glib:location}/lib -Wl,-rpath=${glib:location}/lib -L${libffi:location}/lib -Wl,-rpath=${libffi:location}/lib -lffi -L${zlib:location}/lib/ -Wl,-rpath=${zlib:location}/lib/
GLIB_CFLAGS=-I${glib:location}/include/glib-2.0 -I${glib:location}/lib/glib-2.0/include
GLIB_LIBS=-L${glib:location}/lib -lglib-2.0 -lintl -lgobject-2.0
GLIB_LIBS=-L${glib:location}/lib -lglib-2.0 -lgobject-2.0
FFI_CFLAGS=-I${libffi:location}/include
FFI_LIBS=-L${libffi:location}/lib -Wl,-rpath=${libffi:location}/lib -lffi
GIR_DIR=${buildout:parts-directory}/${:_buildout_section_name_}/share/gir-1.0
......@@ -88,6 +90,7 @@ url = http://ftp.gnome.org/pub/gnome/core/3.22/3.22.2/sources/pygobject-3.22.0.t
python-egg = ${buildout:parts-directory}/${:_buildout_section_name_}/lib/python${python:version}/site-packages
md5sum = ed4117ed5d554d25fd7718807fbf819f
pre-configure =
cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess .
sed -i 's#/usr/local#${gobject-introspection:location}#g' ${gobject-introspection:location}/lib/pkgconfig/gobject-introspection-1.0.pc
configure-options =
--disable-static
......@@ -95,7 +98,7 @@ configure-options =
environment =
PATH=${pkgconfig:location}/bin:${libtool:location}/bin:${glib:location}/bin:${xz-utils:location}/bin:%(PATH)s
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${gobject-introspection:location}/lib/pkgconfig:${pcre:location}/lib/pkgconfig
PKG_CONFIG_PATH=${glib:location}/lib/pkgconfig:${glib:pkg_config_depends}:${gobject-introspection:location}/lib/pkgconfig
FFI_CFLAGS=-I${libffi:location}/include
FFI_LIBS=-L${libffi:location}/lib -Wl,-rpath=${libffi:location}/lib -lffi
CPPFLAGS=-I${glib:location}/include/glib-2.0 -I${glib:location}/lib/glib-2.0/include -I${gettext:location}/include -I${libffi:location}/include
......@@ -117,6 +120,7 @@ python = ${buildout:bin-directory}/${firewalld-eggs:interpreter}
stop-on-error = True
command =
set -e
set -x
sed -i 's#python3#${:python}#' ${firewalld:location}/bin/firewall-applet
sed -i 's#python3#${:python}#' ${firewalld:location}/bin/firewall-cmd
sed -i 's#python3#${:python}#' ${firewalld:location}/bin/firewall-config
......@@ -127,19 +131,23 @@ command =
sed -i 's#DefaultZone=public#DefaultZone=trusted#' ${firewalld:etc-dir}/firewalld.conf
sed -i 's#FirewallBackend=nftables#FirewallBackend=iptables#' ${firewalld:etc-dir}/firewalld.conf
sed -i 's#IPv6_rpfilter=yes#IPv6_rpfilter=no#' ${firewalld:etc-dir}/firewalld.conf
sed -i "s#'/etc/firewalld'#'${firewalld:etc-dir}'#" ${firewalld:python-egg}/firewall/config/__init__.py
sed -i "s#'/usr/lib/firewalld'#'${firewalld:location}/lib/firewalld'#" ${firewalld:python-egg}/firewall/config/__init__.py
sed -i "s#'/usr/share/'#'${firewalld:location}/share'#" ${firewalld:python-egg}/firewall/config/__init__.py
sed -i "s#import sys#import sys, os\n\nos.environ['GI_TYPELIB_PATH'] = '${gobject-introspection:location}/lib/girepository-1.0/'#" ${:python}
sed -i 's#<user>messagebus</user>#<user>slapsoft</user>#' ${dbus:location}/share/dbus-1/system.conf
cp -f ${firewalld:location}/lib/firewalld/zones/trusted.xml ${firewalld:etc-dir}/zones/
cp -f ${firewalld:location}/share/dbus-1/system.d/FirewallD.conf ${dbus:location}/share/dbus-1/system.d/
mkdir -p ${firewalld:location}/sbin
echo -n '#!/bin/sh\nLD_LIBRARY_PATH=${nftables:location}/lib exec ${firewalld:location}/${firewalld:sbin-dir}/firewalld "$@"' > ${firewalld:location}/sbin/firewalld
chmod a+x ${firewalld:location}/sbin/firewalld
# the following 2 commands may fail in a Theia environment because we don't have write permission on a shared folder
sed -i 's#<user>messagebus</user>#<user>slapsoft</user>#' ${dbus:location}/share/dbus-1/system.conf || true
cp -f ${firewalld:location}/share/dbus-1/system.d/FirewallD.conf ${dbus:location}/share/dbus-1/system.d/ || true
update-command = ${:command}
stop-on-error = true
......
This diff is collapsed.
[buildout]
extends =
../bison/buildout.cfg
../gnu-config/buildout.cfg
../m4/buildout.cfg
../xz-utils/buildout.cfg
parts =
......@@ -9,8 +10,12 @@ parts =
[flex]
recipe = slapos.recipe.cmmi
shared = true
url = http://downloads.sourceforge.net/project/flex/flex-2.6.0.tar.xz
md5sum = 3cbbfa1554d0b75fad9f8100732454de
url = https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz
md5sum = 2882e3179748cc9f9c23ec593d6adc8d
pre-configure = cp -f ${gnu-config:location}/config.sub ${gnu-config:location}/config.guess build-aux/
environment =
M4=${m4:location}/bin/m4
PATH=${bison:location}/bin:${xz-utils:location}/bin:%(PATH)s
# CFLAGS to workaround https://github.com/westes/flex/issues/442
# -fpic is needed to fix compilation on Fedora for slapos-node package
CFLAGS=-g -O2 -D_GNU_SOURCE -fpic
This diff is collapsed.
......@@ -18,8 +18,8 @@ parts =
fluentbit-plugin-wendelin
[fluentbit-plugin-wendelin]
url = https://lab.nexedi.com/nexedi/fluentbit-plugin-wendelin/-/archive/0.3.1/fluentbit-plugin-wendelin-0.3.1.tar.gz
md5sum = 7bafdcbeb2bf9634e041fde95b63b51f
url = https://lab.nexedi.com/nexedi/fluentbit-plugin-wendelin/-/archive/0.3.3/fluentbit-plugin-wendelin-0.3.3.tar.gz
md5sum = 982b76021a30384e7ecfc05d55e3419a
[golang1.17]
# Using "./make.bash" instead of "./all.bash" disables golang tests. Some of these tests attempt to use the network, which fails on OBS' VM.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment