--- /dev/null
+#!/bin/bash
+
+# Copyright 2025 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+# This is a generator for test data. It produces a preseeded confetti
+# DATA directory with random vcard files.
+
+export REQUEST_METHOD=SHELL
+. ${0%/*}/cgilite/cgilite.sh
+. ${0%/*}/cgilite/session.sh 2>/dev/null
+
+# https://en.wikipedia.org/wiki/List_of_most_popular_given_names
+# Germany, France, Spain, Poland, Italy, Wales, Israel (Muslim), Vietnam, Tahiti, Egypt (Coptic), Panama, Jamaica, Denmark
+names_m="Noah Matteo Elias Luca Leon Theo Finn Paul Emil Henry Gabriel Raphaël Louis Léo Noah Arthur Adam Jules Maël Léon Hugo Mateo Martín Leo Lucas Manuel Pablo Alejandro Enzo Daniel Nikodem Jan Aleksander Antoni Franciszek Leon Ignacy Jakub Stanisław Mikołaj Leonardo Edoardo Tommaso Francesco Alessandro Mattia Lorenzo Gabriele Riccardo Andrea Noah Luca Oliver Arthur Archie Theo Leo Oscar Arlo Theodore Muhammad Ahmad Adam Yusef Omer Ali Abd Amir Ibrahim Mahmoud Tuân Hùng Nam Huy Long Hai Hieu Son Hoang Duy Hiro Teiki Moana Manua Marama Teiva Teva Maui Tehei Tamatoa Ioane Tapuarii Peter Pierre George John Mina Beshoi Kirollos Mark Fadi Habib Aminadab Thiago Aibar David Eider Endrick Enoc Gael Ian Jatniel Aiden Liam Malachi Amir Nathaniel Joshua Josiah Zahir Nathan Oscar Carl William Noah August Aksel Emil Oliver Alfred Theo"
+names_f="Emilia Sophia Emma Hannah Mia Lina Ella Lia Leni Mila Louise Jade Ambre Alba Emma Alma Romy Rose Alice Anna Lucía Sofía Martina María Julia Valeria Olivia Emma Paula Vega Maja Zofia Zuzanna Hanna Laura Julia Oliwia Pola Alicja Maria Sofia Aurora Ginevra Vittoria Giulia Beatrice Ludovica Alice Emma Matilde Olivia Amelia Isla Poppy Freya Ivy Bonnie Lottie Harper Lily Elsie Millie Maryam Shams Lyn Malek Jori Lian Mila Aline Nur Marya Anh Linh Hà Trang Phương Thảo Ngọc Thùy Hoa Nguyên Tiare Hinano Poema Maeva Hina Vaea Titaua Moea Moeata Tarita Titaina Teura Heikapu Mareva Mary Marie Mariam Marina Irene Malak Habiba Hana Farah Marwa Nada Salma Zoe Alaia Alahia Sofía Ana Cailín Elianys Hannah Isabella Ivanna Miranda Amelia Arianna Gianna Kyra Azora Kiara Azariah Aria Ariana Azaria Emma Ella Luna Alma Frida Olivia Ida Agnes Karla Nora"
+
+# Last names are derived from street names in the Berlin street atlas
+names_l="Späthswald Schöppinger Cornelius Klützer Fuchs Schönwald Baluschek Sadow Schöppinger Holtz Oberbaum Dürer Wiesenbrücke Marchand Andersen Seegefelder Kraatz Sperling Möbius Zeuthener Breitunger Dillenburger Kunze Immergrün Trinius Georg Berblinger Buschkrug Preuschen Grüber Haack Schilfsänger Wilsberg Meier Sonntag Gutzkow Canova Grüneberg Schlieper Franzensbader Treue Grimm Löwenburg Mettlacher Donizetti Hadubrand Geisenheimer Fischer Steinbrech Werfel Horchheimer Friesdorfer Holzminden Trollblum Itten Roedenbeck Blanchard Nahariya Röttenbacher Pfirsich Kloeden Handjery Pöthig Hollaender Kötztinger Gladauer Birkenknick Matthes Haveldün Breite Grebenhain Boxberger Lauterbach Hollaberg Weyl Rufacher Nieber Freytag Blumenthal Steeger Steinhöfel Schwabe Laube Herkomer Aalemann Iffland Farman Colditz Spinnpfad Poelchau Katzengrab Reisfink Preysing Salmbach Arnold Pfennig Siewert Habicht Haynauer Meisenwink Heideweg Lerchenweg Wells Hapig Kunz Buntschuh Frank Langdorfer Schwarzbach Weinert Pansfeld Feldtmann Dachstein Schlegel Markgraf Diestelmeyer Rackwitz Katzbach Kottmeier Furka Philipp Grunwald Koppen Wiclef Höhn Dierhagen Kattfuß Mertens Jansen Ibsen"
+
+# Street names are excerpts form the Berlin street atlas
+streets="Ringbahnstraße
+Klaushager Weg
+Fuchsbau
+Pariser Straße
+Eisenbahnstraße
+Klützer Straße
+Corneliusbrücke
+Sadowastraße
+Schöppinger Straße
+Karl-Holtz-Straße
+Magirusstraße
+Oberbaumbrücke
+Heiligenstadter Straße
+Möllendorffstraße
+Rehkitzsteig
+Dianaplatz
+Dürergärten
+Wiesenbrücke
+Marchandstraße
+Grenzpfad
+Humboldthöhe
+Andersenweg
+Fischadlerweg
+Berliner Straße
+Seegefelder Straße
+Am Kraatz-Tränke-Graben
+Paulsternstraße
+Wolfgang-Heinz-Straße
+Privatstraße 1
+Sperlingsgasse
+Zeuthener See
+Breitunger Weg
+Argenauer Straße
+Dillenburger Straße
+Horst-Kunze-Weg
+Johann-Georg-Straße
+Franzensbader Straße
+Porthanweg
+Eislebener Straße
+Straße 164
+Gartenstraße
+Legiendamm
+Teupitzer Straße
+Mozartstraße
+Krampnitzer Weg
+Heilkräuterweg
+Friedelstraße
+Grimmstraße
+Dahlemer Weg
+Klaustaler Straße
+Löwenburgweg
+Graunstraße
+Tiefenbronner Weg
+Mettlacher Straße
+Händelplatz
+Donizettistraße
+Küstriner Straße
+Lutherbrücke
+Hadubrandstraße
+Holenbrunner Weg
+Thomasiusstraße
+Am Waldkiefernweg
+Herthastraße
+Mindener Straße
+Geisenheimer Straße
+Steinbrechgang
+Am Wäldchen
+Werfelstraße
+Horchheimer Weg
+Eichborndamm
+Friesdorfer Pfad
+Holzmindener Straße
+Ziegenorter Pfad
+Trollblumenweg
+Rixdorfer Höhe
+Heinersdorfer Straße
+Widenhof
+Am Meisenwinkel
+Heideweg
+Marienfelder Chaussee
+Heiligegeistgasse
+Lerchenweg
+Spreewaldplatz
+Scharfenberg
+Bleibtreustraße
+Roggensteig
+Hefnersteig
+Lloyd-G.-Wells-Straße
+Marianne-Hapig-Weg
+Kunz-Buntschuh-Straße
+Nostitzstraße
+Frankenstraße
+Sibeliusweg
+Lissaer Straße
+Langdorfer Straße
+Lindenstraße
+Schwarzbacher Straße
+Rietzestraße
+Goldzeisigweg
+Hessische Straße
+Ernst-Haeckel-Straße
+Lunagang
+Wasserstraße
+Greifenhagener Straße
+Marie-Elisabeth-von-Humboldt-Weg
+Kuckuckssteig
+Malvenweg
+Erpetaler Weg
+Wandalenallee
+Erich-Weinert-Straße
+Gryphiusstraße
+Pansfelder Weg
+Am Oberhafen
+Wangerooger Steig
+Herzfelder Steig
+"
+
+name_f(){
+ local num=$(printf %s\\n "$names_f" |wc -w)
+ printf %s "$names_f" |cut -d" " -f$((RANDOM % num + 1))
+}
+name_m(){
+ local num=$(printf %s\\n "$names_m" |wc -w)
+ printf %s "$names_m" |cut -d" " -f$((RANDOM % num + 1))
+}
+name_l(){
+ local num=$(printf %s\\n "$names_l" |wc -w)
+ printf %s "$names_l" |cut -d" " -f$((RANDOM % num + 1))
+}
+street(){
+ local num=$(printf %s "$streets" |wc -l)
+ printf %s "$streets" |sed -n $((RANDOM % num + 1))p
+}
+
+# 1st given name is always gender based (although gendering may be ambiguous)
+# there is 1/4th chance for each additional given name
+# 1/5th chance that a middle name will be of the opposite gender
+# generated persons will be strictly binary, male or female
+# as this is mostly supposed to be prepubescent children
+ln="$(name_l)" mn=""
+if [ $((RANDOM % 2)) -eq 0 ]; then
+ gender=female
+ fn="$(name_f)"
+ while [ $((RANDOM % 4)) -eq 0 ]; do
+ [ $((RANDOM % 5)) -eq 0 ] && mn="${mn}${mn:+ }$(name_m)" || mn="${mn}${mn:+ }$(name_f)"
+ done
+else
+ gender=male
+ fn="$(name_m)"
+ while [ $((RANDOM % 4)) -eq 0 ]; do
+ [ $((RANDOM % 5)) -eq 0 ] && mn="${mn}${mn:+ }$(name_f)" || mn="${mn}${mn:+ }$(name_m)"
+ done
+fi
+
+# 1/3 chance for the house number to feature a letter a-f
+if [ $((RANDOM % 3)) -eq 0 ]; then
+ AdrNo="$(printf %i%1x $((RANDOM % 99 + 1)) $((RANDOM % 6 + 10)))"
+else
+ AdrNo="$((RANDOM % 99 + 1))"
+fi
+# Roughly a Berlin ZIP Code
+zip="$(printf 1%04i $((RANDOM % 2500 + 101)))"
+# printf 'ADR:%s %s\\, %i Berlin\n' "$(street)" "$AdrNo" "$zip"
+
+uid="$(timeid)$(randomid)" # 32 Octets UID, starting with timestamp
+
+vcf_escape(){
+ for each in "$@"; do
+ printf %s\\n "$each" \
+ | sed -E ':X;$!{N;bX}; s;\r\n;\n;g; s;([;,\\]);\\\1;g; s;\n;\\n;g;'
+ done \
+ | sed -E ':X;$!{N;bX}; s;\n;\;;g'
+}
+
+rdate() {
+ yy="$(date +%Y)" yy="$((yy - (RANDOM % 10 + 5)))"
+ mm="$((RANDOM % 12 + 1))"
+ case $mm in
+ 1|3|5|7|8|10|12) dd=$((RANDOM % 31 + 1));;
+ 4|6|9|11) dd=$((RANDOM % 30 + 1));;
+ 2) dd=$((RANDOM % 28 + 1));;
+ esac
+ printf '%04i-%02i-%02i' $yy $mm $dd
+}
+
+cat <<-EOF
+ BEGIN:VCARD
+ VERSION:4.0
+ N:$(vcf_escape "$ln" "$fn" "$mn" "" "")
+ FN:$(vcf_escape "${fn}${mn:+ }${mn} ${ln}")
+ GENDER:${gender}
+ BDAY:$(rdate)
+ ADR:$(printf 'ADR:%s %s\\, %i Berlin' "$(street)" "$AdrNo" "$zip")
+ UID:${uid}
+ END:VCARD
+ EOF
+ # TEL:$(vcf_escape "$tel")
+ # TEL;TYPE=CELL:$(vcf_escape "$tcell")
+ # EMAIL:$(vcf_escape "$email")
+ # X-ZACK-JOINDATE:$(parse_date "$date")
+ # NOTE:$(vcf_escape "$note")