🏠 🔑
Command - diff
diff --git a/.exec b/.exec
deleted file mode 100644
index ffc5783..0000000
--- a/.exec
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/ksh
-
-if [[ ! -z "$1" ]]; then
-	case "$1" in
-		cgit.cgi) exec ./e/cgit.cgi;;
-		poem) _see_other /poem/$poem_id;;
-		*) NotAllowed;;
-	esac
-fi
-
-NotAllowed
diff --git a/components/menu.html b/components/menu.html
index 8448fad..3174a34 100644
--- a/components/menu.html
+++ b/components/menu.html
@@ -1,4 +1,4 @@
-<div class="_ f fic mkn">
-	<a class="$RB" href="/e/index">🏠</a>
+<div class="h f fic mt0">
+	<a class="$RB" href="/"><span role="img" aria-label="home">🏠</span></a>
 	$USER_ICON
 </div>
diff --git a/e/cart b/e/cart
deleted file mode 100755
index 148118f..0000000
--- a/e/cart
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-. $ROOT/lib/shop.sh
-
-EmptyContents() {
-	_EMPTY="`_ "Empty"`"
-	echo "<div class=\"tsxl tac\">$_EMPTY</div>"
-}
-
-Contents() {
-	TOTAL_CART_EXP="`process_cart $CART_PATH`"
-	TOTAL="`echo "$TOTAL_CART_EXP" | bc -l`"
-	_SUBMIT="`_ Submit`"
-	PRODUCTS="`ProductsFromCart -rcart $CART_PATH`"
-	PRODUCTS="`Wrap "$PRODUCTS"`"
-
-	cat <<!
-<div class="v">
-	$PRODUCTS
-</div>
-<div class="tcv fic v">
-	<div class="tsxl">$TOTAL€</div>
-	<form action="/e/order" method="POST">
-		<input type="hidden" name="shop_id" value="$shop_id"></input>
-		<button>$_SUBMIT</Button>
-	</form>
-</div>
-!
-}
-
-if [[ -z "$shop_id" ]] || [[ ! -d $SHOP_PATH ]]; then
-	Fatal 404 Shop not found
-fi
-
-case "$REQUEST_METHOD" in
-	POST)
-		PRODUCT_PATH=$SHOP_PATH/$product_id
-
-		if [[ -z "$product_id" ]] || [[ ! -d "$PRODUCT_PATH" ]]; then
-			Fatal 404 Product not found
-		fi
-
-		if [[ $quantity -lt 0 ]]; then
-			Fatal 400 Invalid quantity
-		fi
-
-		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-		fmkdir $USER_SHOP_PATH
-		STOCK="`cat $PRODUCT_PATH/stock`"
-		if [[ -f $CART_PATH ]] && cat $CART_PATH | grep -q $product_id; then
-			OLD_QUANTITY="`cat $CART_PATH | grep $product_id | awk '{ print $2 }'`"
-		else
-			OLD_QUANTITY=0
-		fi
-		AVAILABLE_EXP="$STOCK - ($quantity - $OLD_QUANTITY) >= 0"
-		AVAILABLE="`echo $AVAILABLE_EXP | bc`"
-
-		[[ "$AVAILABLE" != "0" ]] || Fatal 400 Not enough stock
-
-		if [[ "$quantity" == "0" ]]; then
-			sed -i "/^$product_id /d" $CART_PATH
-		else
-			fappend $CART_PATH echo $product_id $quantity
-			cat $CART_PATH | awk \
-				'{a[$1]=$2} END{for (i in a) print i FS a[i]}' \
-				> $ROOT/tmp/cart
-			mv $ROOT/tmp/cart $CART_PATH
-		fi
-
-		case "$return" in
-			cart)
-				see_other cart ?shop_id=$shop_id
-				;;
-			shop)
-				see_other shop ?shop_id=$shop_id
-				;;
-			product)
-				see_other product ?shop_id=$shop_id\&product_id=$product_id
-				;;
-			*)
-				Fatal 400 "Invalid return"
-				;;
-		esac
-
-		;;
-
-	GET)
-		if [[ -f "$CART_PATH" ]] && [[ ! -z `cat $CART_PATH` ]]; then
-			CONTENTS="`Contents`"
-		else
-			CONTENTS="`EmptyContents`"
-		fi
-		export CONTENTS
-
-		export _TITLE="`_ $shop_id` - `_ Cart`"
-
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/cgit.cgi b/e/cgit.cgi
deleted file mode 100755
index 96ea1c3..0000000
Binary files a/e/cgit.cgi and /dev/null differ
diff --git a/e/class b/e/class
new file mode 100755
index 0000000..3d6b3f3
--- /dev/null
+++ b/e/class
@@ -0,0 +1,70 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+Courses() {
+	for_each_in "$SCHOOL_PATH/courses" classes "$class_id" \
+		| SmallButtons course \&school_id=$school_id | cond || {
+		echo "<h2>`_ Courses`</h2>"
+		cat $contents | fw 8
+	}
+}
+
+Teacher() {
+	_TEACHER="`_ Teacher`"
+	LabeledIDEdit "$_TEACHER" teacher $teacher_id class $class_id \&school_id=$school_id
+}
+
+AssignGrades() {
+	if im $SCHOOL_OWNER $teacher_id; then
+		ls $SCHOOL_PATH/students | while read student_id; do
+			STUDENT_PATH=$SCHOOL_PATH/students/$student_id
+
+			if grep -q "^$class_id " $STUDENT_PATH/grades; then
+				continue
+			fi
+
+			cat $STUDENT_PATH/courses | while read course_id; do
+				COURSE_PATH=$SCHOOL_PATH/courses/$course_id
+
+				if grep -q $class_id $COURSE_PATH/classes; then
+					echo $student_id
+				fi
+			done
+		done | sort -u | while read student_id; do
+			cat <<!
+<a class="btn c0 ps rs" href="/e/class-grading?school_id=$school_id&class_id=$class_id&student_id=$student_id">
+	$student_id
+</a>
+!
+		done | cond || {
+			echo "<h2>`_ "Assign grades"`</h2>"
+			cat $contents | fw 8
+		}
+	fi
+}
+
+case "$REQUEST_METHOD" in
+	GET)
+		export class_id
+		CLASS_PATH=$SCHOOL_PATH/classes/$class_id
+		teacher_id="`cat $CLASS_PATH/.teacher`"
+		export class_title="`cat $CLASS_PATH/title`"
+		export _SEMESTER="`_ Semester`"
+		export COURSES="`Courses`"
+		export TEACHER="`Teacher`"
+		export ASSIGN_GRADES="`AssignGrades`"
+		export class_semester="`cat $CLASS_PATH/semester`"
+
+		NormalCat ?school_id=$school_id\&class_id=$class_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
+
+
diff --git a/e/class-add b/e/class-add
new file mode 100755
index 0000000..be6f2f4
--- /dev/null
+++ b/e/class-add
@@ -0,0 +1,39 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_id $class_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		CLASS_PATH="$SCHOOL_PATH/classes/$class_id"
+
+		fmkdir $CLASS_PATH
+		urldecode $teacher | fwrite $CLASS_PATH/.teacher
+		urldecode $class_title | fwrite $CLASS_PATH/title
+		urldecode $semester | fwrite $CLASS_PATH/semester
+
+		see_other class ?school_id=$school_id\&class_id=$class_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add class to school"`"
+		export _CLASS_ID="`_ "Class ID"`"
+		export _CLASS_TITLE="`_ "Class Title"`"
+		export _TEACHER="`_ "Teacher"`"
+		export _SEMESTER="`_ "Semester"`"
+		export _SUBMIT="`_ Submit`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/class-grading b/e/class-grading
new file mode 100755
index 0000000..7b6bb99
--- /dev/null
+++ b/e/class-grading
@@ -0,0 +1,34 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+CLASS_PATH="$SCHOOL_PATH/classes/$class_id"
+teacher_id="`cat $CLASS_PATH/.teacher`"
+
+im $SCHOOL_OWNER $teacher_id || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		STUDENT_PATH="$SCHOOL_PATH/students/$student_id"
+
+		echo $class_id `urldecode $grade` | fappend $STUDENT_PATH/grades
+
+		see_other class ?school_id=$school_id\&class_id=$class_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Assign grade to student"`"
+		export _SUBMIT="`_ Submit`"
+		export class_id
+		export student_id
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
+
diff --git a/e/class-teacher-associate b/e/class-teacher-associate
new file mode 100755
index 0000000..56268b3
--- /dev/null
+++ b/e/class-teacher-associate
@@ -0,0 +1,35 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		CLASS_PATH="$SCHOOL_PATH/classes/$class_id"
+
+		if invalid_s teachers $teacher_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		echo $teacher_id | fwrite $CLASS_PATH/.teacher
+
+		see_other class ?school_id=$school_id\&class_id=$class_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Assign teacher to class"`"
+		export _TEACHER_ID="`_ "Teacher ID"`"
+		export _CLASS_ID="`_ "Class ID"`"
+		export _SUBMIT="`_ Submit`"
+		export class_id
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/classes b/e/classes
new file mode 100755
index 0000000..2ef8983
--- /dev/null
+++ b/e/classes
@@ -0,0 +1,22 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+case "$REQUEST_METHOD" in
+	GET)
+		export _TITLE="`_ Classes`"
+
+		if im $SCHOOL_OWNER; then
+			export CLASS_ADD="<a class=\"$RB\" href=\"/e/class-add?school_id=$school_id\">+</a>"
+		fi
+
+		export CLASSES="`ls $SCHOOL_PATH/classes | BigButtons class \&school_id=$school_id`"
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/course b/e/course
new file mode 100755
index 0000000..c1c6374
--- /dev/null
+++ b/e/course
@@ -0,0 +1,50 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+COURSE_PATH=$SCHOOL_PATH/courses/$course_id
+
+Classes() {
+	{
+		cat $COURSE_PATH/classes | SmallButtons class \&school_id=$school_id
+		if im $SCHOOL_OWNER; then
+			echo "<a class=\"$RBS\" href=\"/e/course-class-associate?school_id=$school_id&course_id=$course_id\">+</a>"
+		fi
+	} | cond || {
+		echo "<h2>`_ Classes`</h2>"
+		cat $contents | fw 8
+	}
+}
+
+Students() {
+	for_each_in "$SCHOOL_PATH/students" courses "$course_id" \
+		| SmallButtons student \&school_id=$school_id | cond || {
+		echo "<h2>`_ Students`</h2>"
+		cat $contents | fw 8
+	}
+}
+
+Regent() {
+	teacher_id="`cat $COURSE_PATH/.teacher`"
+	_REGENT="`_ Regent`"
+	LabeledIDEdit "$_REGENT" teacher $teacher_id course $course_id \&school_id=$school_id
+}
+
+case "$REQUEST_METHOD" in
+	GET)
+		export course_id
+		export course_title="`cat $COURSE_PATH/title`"
+		export CLASSES="`Classes`"
+		export STUDENTS="`Students`"
+		export REGENT="`Regent`"
+
+		NormalCat ?school_id=$school_id\&course_id=$course_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/course-add b/e/course-add
new file mode 100755
index 0000000..d793bd1
--- /dev/null
+++ b/e/course-add
@@ -0,0 +1,37 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_id $course_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		COURSE_PATH="$SCHOOL_PATH/courses/$course_id"
+
+		fmkdir $COURSE_PATH
+		echo $teacher_id | fwrite $COURSE_PATH/.teacher
+		urldecode $course_title | fwrite $COURSE_PATH/title
+
+		see_other course ?school_id=$school_id\&course_id=$course_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add course to school"`"
+		export _COURSE_ID="`_ "Course ID"`"
+		export _COURSE_TITLE="`_ "Course Title"`"
+		export _REGENT="`_ Regent`"
+		export _SUBMIT="`_ Submit`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/course-class-associate b/e/course-class-associate
new file mode 100755
index 0000000..7c8039a
--- /dev/null
+++ b/e/course-class-associate
@@ -0,0 +1,40 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+COURSE_PATH="$SCHOOL_PATH/courses/$course_id"
+COURSE_REGENT="`cat $COURSE_PATH/.teacher`"
+
+im $SCHOOL_OWNER $COURSE_REGENT || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_s classes $class_id; then
+			Fatal 400 That class does not exist
+		fi
+
+		if grep -q "$class_id" $COURSE_PATH/classes; then
+			Fatal 400 That class is already assigned to this course
+		fi
+
+		echo $class_id | fappend $COURSE_PATH/classes
+
+		see_other course ?school_id=$school_id\&course_id=$course_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Assign class to course"`"
+		export _COURSE_ID="`_ "Course ID"`"
+		export _CLASS_ID="`_ "Class ID"`"
+		export _SUBMIT="`_ Submit`"
+		export course_id
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/course-teacher-associate b/e/course-teacher-associate
new file mode 100755
index 0000000..4847bc5
--- /dev/null
+++ b/e/course-teacher-associate
@@ -0,0 +1,34 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		COURSE_PATH="$SCHOOL_PATH/courses/$course_id"
+
+		if invalid_s teachers $teacher_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		echo $teacher_id | fwrite $COURSE_PATH/.teacher
+
+		see_other course ?school_id=$school_id\&course_id=$course_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Assign teacher to course"`"
+		export _TEACHER_ID="`_ "Teacher ID"`"
+		export _SUBMIT="`_ Submit`"
+		export course_id
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/courses b/e/courses
new file mode 100755
index 0000000..15dc46d
--- /dev/null
+++ b/e/courses
@@ -0,0 +1,23 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+case "$REQUEST_METHOD" in
+	GET)
+		export _TITLE="`_ Courses`"
+
+		if im $SCHOOL_OWNER; then
+			export COURSE_ADD="<a class=\"$RB\" href=\"/e/course-add?school_id=$school_id\">+</a>"
+		fi
+
+		export COURSES="`ls $SCHOOL_PATH/courses | BigButtons course \&school_id=$school_id`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/image-add b/e/image-add
index 436e46d..b66b9f8 100755
--- a/e/image-add
+++ b/e/image-add
@@ -12,6 +12,8 @@ case "$REQUEST_METHOD" in
 
 		fmkdir $USER_IMAGES_PATH
 
+		[[ ! -f $ROOT/tmp/images ]] || rm $ROOT/tmp/images
+
 		file_count="`cat $ROOT/tmp/mpfd/file-count`"
 		for i in `seq 0 $file_count`; do
 			FILE_PATH=$ROOT/tmp/mpfd/file$i
@@ -19,8 +21,20 @@ case "$REQUEST_METHOD" in
 			IMAGE_ID="`counter_inc $ROOT/public/img-counter`"
 			ext="`file -i $FILE_PATH | awk '{print $2}' | tr '/' ' ' | awk '{print $2}'`"
 			mv $FILE_PATH $USER_IMAGES_PATH/$IMAGE_ID.$ext
+			cd $USER_IMAGES_PATH
+			convert -resize x128 $IMAGE_ID.$ext small-$IMAGE_ID.$ext 2>&1
+			cd - > $ROOT/tmp/null
+			echo /img/$REMOTE_USER/$IMAGE_ID.$ext >> $ROOT/tmp/images
 		done
 
+		if [[ "$HTTP_ACCEPT" == "text/plain" ]]; then
+			RESP_CONTENT_TYPE="text/plain"
+			NormalHead 200
+			echo
+			cat $ROOT/tmp/images
+			exit
+		fi
+
 		see_other images
 
 		;;
diff --git a/e/images b/e/images
index 4b51bc1..3fbfd97 100755
--- a/e/images
+++ b/e/images
@@ -5,7 +5,7 @@
 
 Image() {
 	cat <<!
-<img height="128" class="ofc s_k256 b0" src="http://$HTTP_HOST/img/$1" />
+<img height="128" class="ofc s_k256 b0" src="http://$HTTP_HOST/img/$REMOTE_USER/small-$1" />
 !
 }
 
@@ -15,16 +15,14 @@ Images() {
 	done
 }
 
-find_images() {
-	find $1 -type f | sed "s|$1/||"
+ls_images() {
+	ls $ROOT/htdocs/img/$REMOTE_USER | sed '/^small-.*$/d'
 }
 
 case "$REQUEST_METHOD" in
 	GET)
 		export _TITLE="`_ Images`"
-		IMAGES="`find_images $ROOT/htdocs/img | Images`"
-		IMAGES="`Wrap $IMAGES`"
-		export IMAGES
+		export IMAGES="`ls_images | Images | fw`"
 		NormalCat
 		;;
 	*)
diff --git a/e/images-edit b/e/images-edit
new file mode 100755
index 0000000..98ec1fe
--- /dev/null
+++ b/e/images-edit
@@ -0,0 +1,31 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+
+ls_images() {
+	ls $ROOT/htdocs/img/$REMOTE_USER | sed '/^small-.*$/d'
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		USER_IMAGES_PATH=$ROOT/htdocs/img/$REMOTE_USER
+
+		ls_images | while read noext; do
+			local qname=delete_$noext
+			eval echo $noext \$$qname
+		done | while read noext state; do
+			[[ "$state" != "on" ]] || rm $USER_IMAGES_PATH/$noext.*
+		done
+
+		RESP_CONTENT_TYPE="text/plain"
+		NormalHead 200
+		echo
+		exit
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/index b/e/index
index 5c02e7d..717355f 100755
--- a/e/index
+++ b/e/index
@@ -8,9 +8,12 @@ case "$REQUEST_METHOD" in
 		export _TITLE="tty.pt"
 		export _POEMS="`_ Poems`"
 		export _NEVERDARK="`_ "Never Dark"`"
+		export _SCHOOLS="`_ "Schools"`"
 		export _SHOPS="`_ "Shops"`"
 		export _SEM="`_ "Shared Expenses Manager"`"
+		export _SOURCE_CODE="`_ "Source Code"`"
 		export _TERMINAL="`_ "Terminal"`"
+		export _COMMANDS="`_ "Commands"`"
 		NormalCat
 		;;
 	*)
diff --git a/e/login b/e/login
deleted file mode 100755
index 4bb5914..0000000
--- a/e/login
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/optional-auth.sh
-. $ROOT/lib/common.sh
-
-error() {
-	echo 'Status: 303 See Other'
-	echo "Location: login?error=$1"
-	echo
-	exit
-}
-
-case "$REQUEST_METHOD" in
-	POST)
-		username="`urldecode $username`"
-		password="`urldecode $password`"
-
-		hash="`grep "^$username:" $ROOT/.htpasswd | awk 'BEGIN{FS=":"} {print $2}'`"
-		[[ -z "$REMOTE_USER" ]] || rm $ROOT/sessions/$cookie
-		[[ ! -z "$hash" ]] || Fatal 400 No such user
-
-		if crypt_checkpass "$password" "$hash"; then
-			Unauthorized
-		fi
-
-		[[ ! -f $ROOT/users/$username/rcode ]] \
-			|| Fatal 400 The account was not activated
-
-		TOKEN="`rand_str_1`"
-		#[[ -d $ROOT/sessions ]] || mkdir $ROOT/sessions
-		echo $username > $ROOT/sessions/$TOKEN
-		echo 'Status: 303 See Other'
-		echo "Set-Cookie: QSESSION=$TOKEN; SameSite=Lax"
-		echo "Location: /e/user"
-		#echo "Location: http://$username:$password@$HTTP_HOST/e/user"
-		echo
-		;;
-	GET)
-		export _TITLE="`_ Login`"
-		export _REGISTER="`_ "Register"`"
-		export _USERNAME="`_ "Username"`"
-		export _PASSWORD="`_ "Password"`"
-		export _SUBMIT="`_ "Submit"`"
-
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/logout b/e/logout
deleted file mode 100755
index d3b844b..0000000
--- a/e/logout
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-
-case "$REQUEST_METHOD" in
-	GET)
-		rm $ROOT/sessions/$cookie
-
-		echo 'Status: 303 See Other'
-		echo "Set-Cookie: QSESSION=; SameSite=Lax; expires=Thu, 01 Jan 1970 00:00:00 GMT"
-		echo "Location: /e/index"
-		echo
-		exit
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
diff --git a/e/order b/e/order
deleted file mode 100755
index e14a7d3..0000000
--- a/e/order
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-. $ROOT/lib/shop.sh
-. $ROOT/lib/order.sh
-
-AccountInfo() {
-	ACCOUNT_IBAN="`zcat $ROOT/users/$SHOP_OWNER/iban`"
-
-	if [[ -z "$ACCOUNT_IBAN" ]]; then
-		return
-	fi
-
-	cat <<!
-<div>
-	<div>`_ "Account IBAN"`</div>
-	<div>$ACCOUNT_IBAN</div>
-</div>
-!
-}
-
-MBWayInfo() {
-	PHONE_NUMBER="`zcat $ROOT/users/$SHOP_OWNER/phone_number`"
-
-	if [[ -z $PHONE_NUMBER ]]; then
-		return
-	fi
-
-	cat <<!
-<div>
-	<span>`_ "Phone number"` (MBWay): </span>
-	<a href="tel:$PHONE_NUMBER">$PHONE_NUMBER</a>
-</div>
-!
-}
-
-TransferData() {
-	OWNER_PATH=$ROOT/users/$SHOP_OWNER
-	OWNER_EMAIL="`cat $OWNER_PATH/email`"
-	cat <<!
-<div>`_ "Please refer to the order number in the credit description."` (#$order_id)</div>
-<div>`_ "You can send an e-mail to"` <a href="mailto:$OWNER_EMAIL">$OWNER_EMAIL</a> `_ "to speed up shipment."`</div>
-`AccountInfo`
-`MBWayInfo`
-!
-}
-
-AddressInfo() {
-	if [[ "$ORDER_STATE_TEXT" != "Pending_shipment" ]] \
-		|| [[ "$REMOTE_USER" != "$SHOP_OWNER" ]]; then
-		return;
-	fi
-
-	cat <<!
-<pre>
-`cat $ORDER_PATH/address_line_1`
-`cat $ORDER_PATH/address_line_2`
-`cat $ORDER_PATH/zip`
-</pre>
-
-!
-}
-
-if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
-	Fatal 404 Shop not found
-fi
-
-SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-DF_USER=$SHOP_OWNER
-
-case "$REQUEST_METHOD" in
-	POST)
-		if [[ -z "$order_id" ]]; then
-			if [[ -z "`zcat $CART_PATH`" ]]; then
-				Fatal 404 Cart not found
-			fi
-
-			if [[ -z "`zcat $USER_PATH/address_line_1`" ]] \
-				|| [[ -z "`zcat $USER_PATH/address_line_2`" ]] \
-				|| [[ -z "`zcat $USER_PATH/zip`" ]]; then
-				Fatal 400 Invalid shipping address 
-			fi
-
-			ORDERS_PATH=$SHOP_PATH/.orders
-			ORDER_ID_PATH=$SHOP_PATH/.orders/.count
-			ORDER_ID="`counter_inc $ORDER_ID_PATH`"
-			ORDER_PATH=$SHOP_PATH/.orders/$ORDER_ID
-
-			fmkdir $ORDERS_PATH
-			fmkdir $ORDER_PATH
-			fwrite $ORDER_PATH/raw cat $CART_PATH
-			fwrite $ORDER_PATH/owner echo $REMOTE_USER
-			fwrite $ORDER_PATH/state echo Pending_payment
-			fwrite $ORDER_PATH/address_line_1 cat $USER_PATH/address_line_1
-			fwrite $ORDER_PATH/address_line_2 cat $USER_PATH/address_line_2
-			fwrite $ORDER_PATH/zip cat $USER_PATH/zip
-
-			cat $ORDER_PATH/raw | while read product_id quantity; do
-				counter_dec $SHOP_PATH/$product_id/stock $quantity
-			done >/tmp/null
-
-			rm $CART_PATH # TODO also remove unneeded directories?
-
-			see_other order ?shop_id=$shop_id\&order_id=$ORDER_ID
-		else
-			ORDER_PATH=$SHOP_PATH/.orders/$order_id
-
-			if [[ "$SHOP_OWNER" != "$REMOTE_USER" ]]; then
-				Fatal 401 "You can not do that"
-			fi
-
-			ORDER_STATE_TEXT="`cat $ORDER_PATH/state`"
-			case "$ORDER_STATE_TEXT" in
-				Pending_payment)
-					ORDER_STATE_TEXT=Pending_shipment
-					;;
-				Pending_shipment)
-					ORDER_STATE_TEXT=Shipped
-					;;
-				Shipped)
-					ORDER_STATE_TEXT=Delivered
-					;;
-				Delivered)
-					rm -rf $ORDER_PATH
-					see_other orders ?shop_id=$shop_id
-					exit
-					;;
-			esac
-
-			fwrite $ORDER_PATH/state echo $ORDER_STATE_TEXT
-
-			case "$return" in
-				order)
-					see_other order ?shop_id=$shop_id\&order_id=$order_id
-					;;
-				orders)
-					see_other orders ?shop_id=$shop_id
-					;;
-			esac
-		fi
-
-		;;
-
-	GET)
-		ORDER_PATH=$SHOP_PATH/.orders/$order_id
-		if [[ -z "$order_id" ]] || [[ ! -d "$ORDER_PATH" ]]; then
-			Fatal 404 Order not found
-		fi
-
-		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-		ORDER_OWNER="`cat $ORDER_PATH/owner`"
-
-		if [[ "$REMOTE_USER" != "$ORDER_OWNER" ]] \
-			&& [[ "$REMOTE_USER" != "$SHOP_OWNER" ]]; then
-
-			Fatal 401 "You can not do that"
-		fi
-
-		export order_id
-
-		export _TITLE="`_ $shop_id` - `_ Order` #$order_id"
-
-		TOTAL_EXP="`process_cart $ORDER_PATH/raw`"
-		export TOTAL="`echo "$TOTAL_EXP" | bc -l`"
-		export PRODUCTS="`ProductsFromCart  $ORDER_PATH/raw`"
-		ORDER_STATE_TEXT="`cat $ORDER_PATH/state`"
-		export ADDRESS_INFO="`AddressInfo`"
-		export ORDER_STATE="`OrderState -rorder "$ORDER_STATE_TEXT" $order_id`"
-		if [[ "$REMOTE_USER" == "$ORDER_OWNER" ]]; then
-			case "$ORDER_STATE_TEXT" in
-				Pending_payment)
-					TRANSFER_DATA=`TransferData`
-					export TRANSFER_DATA
-					;;
-			esac
-		fi
-		NormalCat ?shop_id=$shop_id
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
-
diff --git a/e/orders b/e/orders
deleted file mode 100755
index ec689d2..0000000
--- a/e/orders
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-. $ROOT/lib/shop.sh
-. $ROOT/lib/order.sh
-
-Order() {
-	ORDER_PATH=$SHOP_PATH/.orders/$1
-	ORDER_OWNER="`cat $ORDER_PATH/owner`"
-	TOTAL_EXP="`process_cart $ORDER_PATH/raw`"
-	TOTAL="`echo "$TOTAL_EXP" | bc -l`"
-	ORDER_STATE_TEXT="`cat $ORDER_PATH/state`"
-	ORDER_STATE="`OrderState -rorders "$ORDER_STATE_TEXT" $1`"
-
-	cat <<!
-<a href="/e/order?shop_id=$shop_id&order_id=$1" class="b0 p v">
-	<span>
-		$_ORDER #$1 - $ORDER_OWNER $TOTAL€
-	</span>
-
-	$ORDER_STATE
-</a>
-!
-}
-
-VendorOrders() {
-	ls $SHOP_PATH/.orders | while read line; do
-		Order $line
-	done
-}
-
-UserOrders() {
-	ls $SHOP_PATH/.orders | while read line; do
-		ORDER_PATH=$SHOP_PATH/.orders/$line
-		ORDER_OWNER="`cat $ORDER_PATH/owner`"
-		[[ "$ORDER_OWNER" == "$REMOTE_USER" ]] && Order $line || true
-	done
-}
-
-if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
-	Fatal 404 Shop not found
-fi
-
-case "$REQUEST_METHOD" in
-	GET)
-		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-
-		ORDERS_PATH=$SHOP_PATH/.orders
-
-		export _TITLE="`_ $shop_id` - `_ Orders`"
-
-		_ORDER="`_ Order`"
-
-		if [[ "$REMOTE_USER" == "$SHOP_OWNER" ]]; then
-			ORDERS="`VendorOrders`"
-		else
-			ORDERS="`UserOrders`"
-		fi
-
-		ORDERS="`Wrap $ORDERS`"
-
-		export ORDERS
-		NormalCat ?shop_id=$shop_id
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
-
-
diff --git a/e/poem b/e/poem
index eb36e1b..d515eff 100755
--- a/e/poem
+++ b/e/poem
@@ -3,17 +3,13 @@
 . $ROOT/lib/optional-auth.sh
 . $ROOT/lib/common.sh
 
-noslash() {
-	sed -e 's:/:\\/:g' $1
-}
-
 POEM_PATH=$ROOT/poems/$poem_id
 COMMENTS_PATH="$POEM_PATH/comments.txt"
 
 case "$REQUEST_METHOD" in
 	POST)
 		[[ ! -z "$REMOTE_USER" ]] || Unauthorized
-		fappend $COMMENTS_PATH echo $REMOTE_USER: "`urldecode "$comment"`" 2>&1
+		echo $REMOTE_USER: "`urldecode "$comment"`" | fappend $COMMENTS_PATH
 		see_other poem ?poem_id=$poem_id
 		;;
 	GET)
diff --git a/e/poem-add b/e/poem-add
index 0ec79f3..341c423 100755
--- a/e/poem-add
+++ b/e/poem-add
@@ -7,26 +7,30 @@ case "$REQUEST_METHOD" in
 	POST)
 		poem_id="`cat $ROOT/tmp/mpfd/poem_id`"
 
-		if not_valid_id $poem_id; then
+		if invalid_id $poem_id; then
 			Fatal 400 Not a valid ID
 		fi
 
 		POEM_PATH="$ROOT/poems/$poem_id"
 
 		fmkdir $POEM_PATH
-		fwrite $POEM_PATH/.owner echo $REMOTE_USER
-		file_count="`cat $ROOT/tmp/mpfd/file-count`"
-		for i in `seq 0 $file_count`; do
-			FILE_PATH=$ROOT/tmp/mpfd/file$i
-			fbytes $FILE_PATH
-			filename="`cat $FILE_PATH-name`"
+		echo $REMOTE_USER | fwrite $POEM_PATH/.owner
+
+		mpfd-ls | while read FILE_PATH filename ; do
 			file_lang="`echo $filename | sed 's/\.[^.]*$//'`"
-			if not_valid_lang "$file_lang"; then
+			if invalid_lang "$file_lang"; then
 				Fatal 400 Not a valid locale
 			fi
-			mv $FILE_PATH $POEM_PATH/$file_lang.html
+			fbytes $FILE_PATH
+			cat $FILE_PATH | cslash > $POEM_PATH/$file_lang.html
+			rm $FILE_PATH
 		done
 
+		if [[ ! -f $POEM_PATH/$lang.html ]]; then
+			rm -rf $POEM_PATH
+			Fatal 400 You must submit a poem in your language
+		fi
+
 		see_other poem ?poem_id=$poem_id
 		;;
 
diff --git a/e/product b/e/product
deleted file mode 100755
index ae264db..0000000
--- a/e/product
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/optional-auth.sh
-. $ROOT/lib/common.sh
-. $ROOT/lib/shop.sh
-. $ROOT/lib/order.sh
-
-if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
-	Fatal 404 Shop not found
-fi
-
-PRODUCT_PATH=$SHOP_PATH/$product_id
-if [[ -z "$product_id" ]] || [[ ! -d "$PRODUCT_PATH" ]]; then
-	Fatal 404 Product not found
-fi
-
-case "$REQUEST_METHOD" in
-	GET)
-
-		export _TITLE="`_ $shop_id` - `_ Product` #$product_id"
-
-		export PRODUCT="`Product -rproduct $CART_PATH $product_id`"
-		NormalCat ?shop_id=$shop_id\&product_id=$product_id
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
-
-
-
diff --git a/e/product-add b/e/product-add
deleted file mode 100755
index 1a335a3..0000000
--- a/e/product-add
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-. $ROOT/lib/shop.sh
-
-if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
-	fatal 404 Shop not found
-fi
-
-case "$REQUEST_METHOD" in
-	POST)
-		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-
-		if [[ "$REMOTE_USER" != "$SHOP_OWNER" ]]; then
-			Fatal 401 "You cannot do that"
-		fi
-
-		PRODUCT_ID_PATH=$SHOP_PATH/.count
-		PRODUCT_ID="`counter_inc $PRODUCT_ID_PATH`"
-		PRODUCT_PATH=$SHOP_PATH/$PRODUCT_ID
-		DF_USER=$SHOP_OWNER
-
-		fmkdir $PRODUCT_PATH
-		fwrite $PRODUCT_PATH/title urldecode $title
-		fwrite $PRODUCT_PATH/description urldecode $description
-		fwrite $PRODUCT_PATH/images urldecode $images
-		fwrite $PRODUCT_PATH/price echo $price
-		fwrite $PRODUCT_PATH/stock echo $stock
-
-		see_other shop ?shop_id=$shop_id
-		;;
-
-	GET)
-		export _TITLE="`_ $shop_id` - `_ "Add product"`"
-		export __TITLE="`_ Title`"
-		export _DESCRIPTION="`_ Description`"
-		export _IMAGES="`_ Images`"
-		export _STOCK="`_ Stock`"
-		export _PRICE="`_ Price`"
-		export _SUBMIT="`_ Submit`"
-
-		NormalCat ?shop_id=$shop_id
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/register b/e/register
deleted file mode 100755
index ad1e0e3..0000000
--- a/e/register
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/common.sh
-
-error() {
-	see_other register ?error=$1
-	exit
-}
-
-case "$REQUEST_METHOD" in
-	POST)
-		username="`urldecode $username`"
-		password="`urldecode $password`"
-		password2="`urldecode $password2`"
-		email="`urldecode $email`"
-
-		if not_valid_password $password; then
-			Fatal 400 Not a valid password
-		fi
-
-		if [[ "$password" != "$password2" ]]; then
-			Fatal 400 The passwords don\'t match
-		fi
-
-		if grep -q "^$username:" $ROOT/.htpasswd; then
-			Fatal 400 User already exists
-		fi
-
-		USER_DIR=$ROOT/users/$username
-		DF_USER=$username
-		fmkdir $USER_DIR
-		fwrite $USER_DIR/email echo $email
-		echo $username:$password | htpasswd -I $ROOT/.htpasswd
-		rand_str="`rand_str_1`"
-		fwrite $USER_DIR/rcode echo "$rand_str"
-		femail -f noreply@tty.pt $email <<!
-Subject: `_ "Registration on tty.pt"`
-
-`_ "Welcome to tty.pt!"`
-
-`_ "To confirm that this e-mail address belongs to you, go to the page at:"`
-https://tty.pt/e/registration-confirm?username=$username&rcode=$rand_str
-
-`_ "You will then be able to use your account."`
-
-`_ "Thank you!"`
-!
-		see_other registration-complete
-		;;
-	GET)
-		export _REGISTER="`_ "Register"`"
-		export _USERNAME="`_ "Username"`"
-		export _PASSWORD="`_ "Password"`"
-		export _REPEAT_PASSWORD="`_ "Repeat password"`"
-		export _EMAIL="`_ "Email"`"
-		export _SUBMIT="`_ "Submit"`"
-		export _LOGIN="`_ Login`"
-		export _COOKIE_POLICY="`_ "Cookie policy"`"
-
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/registration-complete b/e/registration-complete
deleted file mode 100755
index 5d9ef6b..0000000
--- a/e/registration-complete
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/common.sh
-
-case "$REQUEST_METHOD" in
-	GET)
-		export _REGISTRATION_COMPLETE="`_ "Registration complete"`"
-		export _ACCOUNT_CREATED="`_ "Please click the link sent to your e-mail to activate your account."`"
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
diff --git a/e/registration-confirm b/e/registration-confirm
deleted file mode 100755
index cf7f57c..0000000
--- a/e/registration-confirm
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/common.sh
-
-case "$REQUEST_METHOD" in
-	GET)
-		USER_PATH=$ROOT/users/$username
-		USER_RCODE="`cat $USER_PATH/rcode`"
-
-		if [[ "$rcode" != "$USER_RCODE" ]]; then
-			Fatal 401 "You can not do that"
-		fi
-
-		rm $USER_PATH/rcode
-		see_other login
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
diff --git a/e/school b/e/school
new file mode 100755
index 0000000..f163b4b
--- /dev/null
+++ b/e/school
@@ -0,0 +1,24 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+case "$REQUEST_METHOD" in
+	GET)
+		# export SCHOOL_MENU="`SchoolMenu`"
+		export _TEACHERS="`_ Teachers`"
+		export _STUDENTS="`_ Students`"
+		export _COURSES="`_ Courses`"
+		export _CLASSES="`_ Classes`"
+		export school_title="`cat $SCHOOL_PATH/title`"
+
+		NormalCat ?school_id=$school_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
+
diff --git a/e/school-add b/e/school-add
new file mode 100755
index 0000000..220ad79
--- /dev/null
+++ b/e/school-add
@@ -0,0 +1,35 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+
+case "$REQUEST_METHOD" in
+	POST)
+		school_id="`urldecode $school_id`"
+
+		if invalid_id $school_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		SCHOOL_PATH="$ROOT/schools/$school_id"
+
+		fmkdir $SCHOOL_PATH
+		echo $REMOTE_USER | fwrite $SCHOOL_PATH/.owner
+		echo $school_title | fwrite $SCHOOL_PATH/title
+
+		see_other school ?school_id=$school_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add school"`"
+		export _SCHOOL_ID="`_ "School ID"`"
+		export _SCHOOL_TITLE="`_ "School Title"`"
+		export _SUBMIT="`_ Submit`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/poems b/e/schools
similarity index 58%
rename from e/poems
rename to e/schools
index 56a60d6..a33d4f3 100755
--- a/e/poems
+++ b/e/schools
@@ -5,13 +5,13 @@
 
 case "$REQUEST_METHOD" in
 	GET)
-		export _TITLE="`_ Poems`"
+		export _TITLE="`_ Schools`"
 
 		if [[ ! -z "$REMOTE_USER" ]]; then
-			export POEM_ADD="<a class=\"$RB\" href=\"/e/poem-add\">+</p>"
+			export SCHOOL_ADD="<a class=\"$RB\" href=\"/e/school-add\">+</a>"
 		fi
 
-		export POEMS="`ls $ROOT/poems | BigButtons poem`"
+		export SCHOOLS="`ls $ROOT/schools | BigButtons school`"
 		NormalCat
 		;;
 	*)
@@ -19,3 +19,5 @@ case "$REQUEST_METHOD" in
 		echo
 		;;
 esac
+
+
diff --git a/e/sem b/e/sem
deleted file mode 100755
index aa8ae6f..0000000
--- a/e/sem
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/ksh
-
-case "$REQUEST_METHOD" in
-	POST)
-		echo 'Status: 200 OK'
-		echo 'Content-Type: text/plain; charset=utf-8'
-		echo
-		(
-			read && read && read && read &&
-			read line1 &&
-			read line2 &&
-			while read nextline ; do
-				echo "$line1"
-				line1="$line2"
-				line2="$nextline"
-			done
-		) | sem
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-esac
diff --git a/e/shop b/e/shop
deleted file mode 100755
index 34a0eca..0000000
--- a/e/shop
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/optional-auth.sh
-. $ROOT/lib/common.sh
-. $ROOT/lib/shop.sh
-
-lsshown() {
-	 find $1 -type d -mindepth 1 -maxdepth 1 -name "[!.]*" | sed "s|$1||"
-}
-
-Category() {
-	CATEGORY_ID="$1"
-	CATEGORY_NAME="`_ $CATEGORY_ID`"
-	cat <<!
-<option value="$CATEGORY_ID">$CATEGORY_NAME</option>
-!
-}
-
-Products() {
-	while read line; do
-		if [[ "`cat $SHOP_PATH/$line/stock`" -gt 0 ]]; then
-			Product -rshop $CART_PATH $line;
-		fi
-	done
-}
-
-ShopButtons() {
-	[[ ! -z "$REMOTE_USER" ]] || return
-
-	cat <<!
-<a class="$RB" href="/e/orders?shop_id=$shop_id">🚚</a>
-<a class="$RB" href="/e/cart?shop_id=$shop_id">🛒</a>
-!
-}
-
-if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
-	Fatal 404 Shop not found
-fi
-
-case "$REQUEST_METHOD" in
-	POST)
-		case "$action" in
-			delete)
-				PRODUCT_PATH=$SHOP_PATH/$product_id
-				if [[ -z "$product_id" ]] || [[ ! -d "$PRODUCT_PATH" ]]; then
-					Fatal 404 Product not found
-				fi
-
-				SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-
-				[[ "$SHOP_OWNER" != "$REMOTE_USER" ]] \
-					&& Fatal 401 "You can not do that"
-
-				rm -rf $SHOP_PATH/$product_id
-
-				see_other shop ?shop_id=$shop_id
-				;;
-			*)
-				Fatal 400 Invalid action
-				;;
-		esac
-
-		;;
-	GET)
-		export _TITLE="`_ $shop_id`"
-
-		export SHOP_CATEGORIES="`lsshown $SHOP_PATH/.categories/ | while read line; do Category $line; done`"
-		PRODUCTS="`lsshown $SHOP_PATH/ | Products`"
-		export PRODUCTS="`Wrap $PRODUCTS`"
-		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-		export SHOP_BUTTONS="`ShopButtons`"
-		if [[ "$REMOTE_USER" == "$SHOP_OWNER" ]]; then
-			ADD_PRODUCT_BUTTON="<div class=\"tar\"><a class=\"tsxl round ps btn\" href=\"/e/product-add?shop_id=$shop_id\">+</a></div>"
-		fi
-		export ADD_PRODUCT_BUTTON
-
-		NormalCat ?shop_id=$shop_id
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/shop-add b/e/shop-add
deleted file mode 100755
index e1c3372..0000000
--- a/e/shop-add
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-
-case "$REQUEST_METHOD" in
-	POST)
-		shop_id="`urldecode $shop_id`"
-
-		if not_valid_id $shop_id; then
-			Fatal 400 Not a valid ID
-		fi
-
-		SHOP_PATH="$ROOT/shops/$shop_id"
-
-		fmkdir $SHOP_PATH
-		fwrite $SHOP_PATH/.owner echo $REMOTE_USER
-		fmkdir $SHOP_PATH/.orders
-
-		see_other shop ?shop_id=$shop_id
-		;;
-
-	GET)
-		export _TITLE="`_ "Add shop"`"
-		export _SHOP_ID="`_ "Shop ID"`"
-		export _SUBMIT="`_ Submit`"
-
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
-
diff --git a/e/shop-edit b/e/shop-edit
new file mode 100755
index 0000000..9321000
--- /dev/null
+++ b/e/shop-edit
@@ -0,0 +1,65 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/shop.sh
+
+lsshown() {
+	 find $1 -type d -mindepth 1 -maxdepth 1 -name "[!.]*" | sed "s|$1||" | sed "s/\/$//"
+}
+
+Products() {
+	local product_id
+	while read product_id; do
+		if [[ "`cat $SHOP_PATH/$product_id/stock`" -gt 0 ]]; then
+			product_env $product_id
+			cat <<!
+<label class="card f v8 b0 fic p">
+	`PImage $product_image_path`
+	<div class="tsl">$product_price€</div>
+	<a class="tsxl" href="/e/product?shop_id=$shop_id&product_id=$product_id">$product_title</a>
+	`echo "$product_description" | csurround p`
+	<input name="delete_$product_id" type="checkbox"></input>
+</label>
+!
+		fi
+	done
+}
+
+if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
+	Fatal 404 Shop not found
+fi
+
+case "$REQUEST_METHOD" in
+	POST)
+		im $SHOP_OWNER || Forbidden
+
+		lsshown $SHOP_PATH/ | while read product_id; do
+			local qname=delete_$product_id
+			eval echo $product_id \$$qname
+		done | while read product_id state; do
+			[[ "$state" != "on" ]] || product_rm $product_id
+		done
+
+		see_other shop ?shop_id=$shop_id
+
+		;;
+	GET)
+		export _TITLE="`_ $shop_id` - `_ "Edit shop"`"
+		export _DELETE_PRODUCTS="`_ "Delete products"`"
+
+		export PRODUCTS="`lsshown $SHOP_PATH/ | Products | fw`"
+		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
+		export SHOP_BUTTONS="`ShopButtons`"
+		if im $SHOP_OWNER; then
+			export ADD_PRODUCT_BUTTON="<div class=\"tar\"><a class=\"tsxl round p8 btn\" href=\"/e/product-add?shop_id=$shop_id\">+</a></div>"
+			export EDIT_BTN="`EditBtn "/e/shop-edit?shop_id=$shop_id"`"
+		fi
+
+		NormalCat ?shop_id=$shop_id
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/shops b/e/shops
deleted file mode 100755
index 1b2ad77..0000000
--- a/e/shops
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/optional-auth.sh
-. $ROOT/lib/common.sh
-
-case "$REQUEST_METHOD" in
-	GET)
-		export _TITLE="`_ Shops`"
-		if [[ ! -z "$REMOTE_USER" ]]; then
-			export SHOP_ADD="<a class=\"$RB\" href=\"/e/shop-add\">+</a>"
-		fi
-
-		export SHOPS="`ls $ROOT/shops | BigButtons shop`"
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/student b/e/student
new file mode 100755
index 0000000..91ed993
--- /dev/null
+++ b/e/student
@@ -0,0 +1,70 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+invalid_s students $student_id && Fatal 400 Invalid student
+
+STUDENT_PATH=$SCHOOL_PATH/students/$student_id
+
+Courses() {
+	{
+		cat $STUDENT_PATH/courses | SmallButtons course \&school_id=$school_id
+		if im $SCHOOL_OWNER $student_id; then
+			echo "<a class=\"$RBS\" href=\"/e/student-course-associate?school_id=$school_id&student_id=$student_id\">+</a>"
+		fi
+	} | cond || {
+		echo "<h2>`_ Courses`</h2>"
+		cat $contents | fw 8
+	}
+}
+
+Grades() {
+	cat $STUDENT_PATH/grades | while read class_id grade; do
+		cat <<!
+<div class="ps c0 _">
+	<a href="/e/class?school_id=$school_id&class_id=$class_id">$class_id</a>
+	<span>$grade</span>
+</div>
+!
+	done | cond || {
+		echo "<h2>`_ Grades`</h2>"
+		cat $contents | fw 8
+	}
+}
+
+RNField() {
+	Field "`_ "Registration Number"`" "`cat $STUDENT_PATH/required_number`"
+}
+
+NameField() {
+	Field "`_ Name`" "`cat $STUDENT_PATH/name`"
+}
+
+DobField() {
+	Field "`_ "Date of birth"`" "`cat $STUDENT_PATH/dob`"
+}
+
+case "$REQUEST_METHOD" in
+	GET)
+		export student_id
+		export _STUDENT="`_ Student`"
+		export COURSES="`Courses`"
+		export RN="`RNField`"
+		export NAME="`NameField`"
+		export DOB="`DobField`"
+		export GRADES="`Grades`"
+
+		if im $SCHOOL_OWNER $student_id; then
+			export EDIT_BTN="`EditBtn "/e/student-edit?school_id=$school_id&student_id=$student_id"`"
+		fi
+
+		NormalCat ?school_id=$school_id\&student_id=$student_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/student-add b/e/student-add
new file mode 100755
index 0000000..851767a
--- /dev/null
+++ b/e/student-add
@@ -0,0 +1,38 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_id $student_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		STUDENT_PATH="$SCHOOL_PATH/students/$student_id"
+
+		fmkdir $STUDENT_PATH
+		urldecode $name | fwrite $STUDENT_PATH/name
+		urldecode $dob | fwrite $STUDENT_PATH/dob
+		counter_inc $SCHOOL_PATH/students/.count | fwrite $STUDENT_PATH/required_number
+
+		see_other student ?school_id=$school_id\&student_id=$student_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add student to school"`"
+		export _STUDENT_ID="`_ "Student ID"`"
+		export _NAME="`_ Name`"
+		export _DOB="`_ "Date of birth"`"
+		export _SUBMIT="`_ Submit`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/student-course-associate b/e/student-course-associate
new file mode 100755
index 0000000..a3a72a7
--- /dev/null
+++ b/e/student-course-associate
@@ -0,0 +1,38 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER $student_id || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_s courses $course_id; then
+			Fatal 400 That course does not exist
+		fi
+
+		STUDENT_PATH="$SCHOOL_PATH/students/$student_id"
+
+		if grep -q "$course_id_id" "$STUDENT_PATH/courses"; then
+			Fatal 400 That course is already assigned to this student
+		fi
+
+		echo $course_id | fappend "$STUDENT_PATH/courses"
+
+		see_other student ?school_id=$school_id\&student_id=$student_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Assign course to student"`"
+		export _CLASS_ID="`_ "Course ID"`"
+		export _SUBMIT="`_ Submit`"
+		export student_id
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/student-edit b/e/student-edit
new file mode 100755
index 0000000..26def23
--- /dev/null
+++ b/e/student-edit
@@ -0,0 +1,44 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER $student_id || Forbidden
+
+STUDENT_PATH=$SCHOOL_PATH/students/$student_id
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_id $student_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		urldecode $name | fwrite $STUDENT_PATH/name
+		urldecode $dob | fwrite $STUDENT_PATH/dob
+
+		see_other student ?school_id=$school_id\&student_id=$student_id
+		;;
+
+	GET)
+		export student_id
+		export _EDIT="`_ Edit`"
+		export _STUDENT="`_ Student`"
+		export _NAME="`_ Name`"
+		export name="`cat $STUDENT_PATH/name`"
+		export _DOB="`_ "Date of birth"`"
+		export dob="`cat $STUDENT_PATH/dob`"
+		export _SUBMIT="`_ Submit`"
+
+		if im $SCHOOL_OWNER $student_id; then
+			export EDIT_BTN="`EditBtn "/e/student-edit?school_id=$school_id&student_id=$student_id"`"
+		fi
+
+		NormalCat ?school_id=$school_id\&student_id=$student_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/students b/e/students
new file mode 100755
index 0000000..4efdc77
--- /dev/null
+++ b/e/students
@@ -0,0 +1,22 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+case "$REQUEST_METHOD" in
+	GET)
+		export _TITLE="`_ Students`"
+
+		if im $SCHOOL_OWNER; then
+			export STUDENT_ADD="<a class=\"$RB\" href=\"/e/student-add?school_id=$school_id\">+</a>"
+		fi
+
+		export STUDENTS="`ls $SCHOOL_PATH/students | BigButtons student \&school_id=$school_id`"
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/teacher b/e/teacher
new file mode 100755
index 0000000..492cdd9
--- /dev/null
+++ b/e/teacher
@@ -0,0 +1,75 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+teacher_things() {
+	for_each_in "$SCHOOL_PATH/$1" .teacher "$teacher_id"
+}
+
+Classes() {
+	teacher_things classes | SmallButtons class \&school_id=$school_id | cond || {
+		echo "<h2>`_ Classes`</h2>"
+		cat $contents | fw 8
+	}
+}
+
+Courses() {
+	teacher_things courses | SmallButtons course \&school_id=$school_id | cond || {
+		echo "<h2>`_ Courses` (`_ Regent`)</h2>"
+		cat $contents | fw 8
+	}
+}
+
+salary_exp() {
+	pay_per_class=750
+	pay_per_course=2500
+
+	teacher_things classes | while read ignored; do
+		echo $pay_per_class
+	done | sum_lines_exp
+
+	echo -n " + "
+
+	teacher_things courses | while read ignored; do
+		echo $pay_per_course
+	done | sum_lines_exp
+}
+
+NameField() {
+	Field "`_ Name`" "`cat $TEACHER_PATH/name`"
+}
+
+DobField() {
+	Field "`_ "Date of birth"`" "`cat $TEACHER_PATH/dob`"
+}
+
+SalaryField() {
+	salary_exp="`salary_exp`"
+	Field "`_ Salary`" "`echo "$salary_exp" | bc`€"
+}
+
+case "$REQUEST_METHOD" in
+	GET)
+		export teacher_id
+		export _TEACHER="`_ Teacher`"
+		export CLASSES="`Classes`"
+		export COURSES="`Courses`"
+		TEACHER_PATH=$SCHOOL_PATH/teachers/$teacher_id
+		export NAME="`NameField`"
+		export DOB="`DobField`"
+		export SALARY="`SalaryField`"
+
+		if im $SCHOOL_OWNER $teacher_id; then
+			export EDIT_BTN="`EditBtn "/e/teacher-edit?school_id=$school_id&teacher_id=$teacher_id"`"
+		fi
+
+		NormalCat ?school_id=$school_id\&teacher_id=$teacher_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/teacher-add b/e/teacher-add
new file mode 100755
index 0000000..06896e6
--- /dev/null
+++ b/e/teacher-add
@@ -0,0 +1,37 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_id $teacher_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		TEACHER_PATH="$SCHOOL_PATH/students/$teacher_id"
+
+		fmkdir $TEACHER_PATH
+		urldecode $name | fwrite $TEACHER_PATH/name
+		urldecode $dob | fwrite $TEACHER_PATH/dob
+
+		see_other student ?school_id=$school_id\&student_id=$student_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add teacher to school"`"
+		export _TEACHER_ID="`_ "Teacher ID"`"
+		export _NAME="`_ Name`"
+		export _DOB="`_ "Date of birth"`"
+		export _SUBMIT="`_ Submit`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/teacher-edit b/e/teacher-edit
new file mode 100755
index 0000000..93d9018
--- /dev/null
+++ b/e/teacher-edit
@@ -0,0 +1,46 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+im $SCHOOL_OWNER $teacher_id || Forbidden
+
+TEACHER_PATH=$SCHOOL_PATH/teachers/$teacher_id
+
+case "$REQUEST_METHOD" in
+	POST)
+		if invalid_id $teacher_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		urldecode $name | fwrite $TEACHER_PATH/name
+		urldecode $dob | fwrite $TEACHER_PATH/dob
+
+		see_other teacher ?school_id=$school_id\&teacher_id=$teacher_id
+		;;
+
+	GET)
+		TEACHER_PATH=$SCHOOL_PATH/teachers/$teacher_id
+
+		export teacher_id
+		export _EDIT="`_ Edit`"
+		export _TEACHER="`_ Teacher`"
+		export _NAME="`_ Name`"
+		export name="`cat $TEACHER_PATH/name`"
+		export _DOB="`_ "Date of birth"`"
+		export dob="`cat $TEACHER_PATH/dob`"
+		export _SUBMIT="`_ Submit`"
+
+		if im $SCHOOL_OWNER $teacher_id; then
+			export EDIT_BTN="`EditBtn "/e/teacher-edit?school_id=$school_id&teacher_id=$teacher_id"`"
+		fi
+
+		NormalCat ?school_id=$school_id\&teacher_id=$teacher_id
+
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/teachers b/e/teachers
new file mode 100755
index 0000000..f868490
--- /dev/null
+++ b/e/teachers
@@ -0,0 +1,22 @@
+#!/bin/ksh
+
+. $ROOT/lib/optional-auth.sh
+. $ROOT/lib/common.sh
+. $ROOT/lib/school.sh
+
+case "$REQUEST_METHOD" in
+	GET)
+		export _TITLE="`_ Teachers`"
+
+		if im $SCHOOL_OWNER; then
+			export TEACHER_ADD="<a class=\"$RB\" href=\"/e/teacher-add?school_id=$school_id\">+</a>"
+		fi
+
+		export TEACHERS="`ls $SCHOOL_PATH/teachers | BigButtons teacher \&school_id=$school_id`"
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/e/tty b/e/tty
deleted file mode 100755
index 36e5eac..0000000
--- a/e/tty
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-
-USER_PATH="$ROOT/users/$REMOTE_USER"
-WHISPER_PATH="$USER_PATH/.whisper"
-
-case "$REQUEST_METHOD" in
-	POST)
-		cmd="`urldecode $cmd`"
-		echo "$REMOTE_USER$ $cmd" >> $WHISPER_PATH
-
-		case "$cmd" in
-			help) 
-				echo Welcome to the terminal
-				echo Beware - the values might not be correct
-				echo commands: df quota whisper
-				;;
-			df)
-				df
-				;;
-			quota)
-				echo "`df_total`/`free_space`"
-				;;
-			whisper*)
-				set -- $cmd
-				shift
-				username=$1
-				shift
-				message=$@
-
-				[[ -d $ROOT/users/$username ]] || Fatal 400 No such user
-
-				DF_USER=$username
-				fappend $ROOT/users/$username/.whisper \
-					echo "$REMOTE_USER": "$message"
-				echo "Sent whisper."
-				;;
-			*)
-				;;
-		esac >> $WHISPER_PATH
-
-		;;
-	GET)
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		exit
-		;;
-esac
-
-export _TITLE="`_ "Terminal"`"
-
-NormalCat
diff --git a/e/user b/e/user
deleted file mode 100755
index ea688cd..0000000
--- a/e/user
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/ksh
-
-. $ROOT/lib/auth.sh
-. $ROOT/lib/common.sh
-
-USER_PATH=$ROOT/users/$REMOTE_USER
-
-case "$REQUEST_METHOD" in
-	POST)
-		case "$action" in
-			personal_data)
-				address_line_1="`urldecode $address_line_1`"
-				address_line_2="`urldecode $address_line_2`"
-				zip="`urldecode $zip`"
-				phone_number="`urldecode $phone_number`"
-				fwrite $USER_PATH/address_line_1 echo $address_line_1
-				fwrite $USER_PATH/address_line_2 echo $address_line_2
-				fwrite $USER_PATH/zip echo $zip
-				fwrite $USER_PATH/phone_number echo $phone_number
-				;;
-			vendor_data)
-				iban="`urldecode $iban`"
-				bicswift="`urldecode $bicswift`"
-				fwrite $USER_PATH/iban echo $iban
-				fwrite $USER_PATH/bicswift echo $bicswift
-				;;
-			*)
-				Fatal 400 Invalid action
-		esac
-		see_other user
-		;;
-	GET)
-		export _TITLE="`_ User` - $REMOTE_USER"
-		export _WELCOME="`_ Welcome`"
-		export _LOGOUT="`_ Logout`"
-		export _CHANGE_PERSONAL_DATA="`_ change_personal_data`"
-		export _ADDRESS_LINE_1="`_ "Address line 1"`"
-		export _ADDRESS_LINE_2="`_ "Address line 2"`"
-		export _ZIP_CODE="`_ "Zip code"`"
-		export _PHONE_NUMBER="`_ "Phone number"`"
-		export _CHANGE_VENDOR_DATA="`_ change_vendor_data`"
-		export _ACCOUNT_IBAN="`_ "Account IBAN"`"
-		export _ACCOUNT_BICSWIFT="`_ "Account BICSWIFT"`"
-		export _SUBMIT="`_ Submit`"
-
-		export address_line_1="`zcat $USER_PATH/address_line_1`"
-		export address_line_2="`zcat $USER_PATH/address_line_2`"
-		export zip="`zcat $USER_PATH/zip`"
-		export phone_number="`zcat $USER_PATH/phone_number`"
-
-		export iban="`zcat $USER_PATH/iban`"
-		export bicswift="`zcat $USER_PATH/bicswift`"
-
-		NormalCat
-		;;
-	*)
-		echo "Status: 405 Method Not Allowed"
-		echo
-		;;
-esac
diff --git a/e/user-edit b/e/user-edit
new file mode 100755
index 0000000..e1badf0
--- /dev/null
+++ b/e/user-edit
@@ -0,0 +1,45 @@
+#!/bin/ksh
+
+. $ROOT/lib/auth.sh
+. $ROOT/lib/common.sh
+
+USER_PATH=$ROOT/users/$REMOTE_USER
+
+case "$REQUEST_METHOD" in
+	POST)
+		urldecode $name | fwrite $USER_PATH/name
+		urldecode $address_line_1 | fwrite $USER_PATH/address_line_1
+		urldecode $address_line_2 | fwrite $USER_PATH/address_line_2
+		urldecode $zip | fwrite $USER_PATH/zip
+		urldecode $phone_number | fwrite $USER_PATH/phone_number
+		urldecode $iban | fwrite $USER_PATH/iban
+		urldecode $bicswift | fwrite $USER_PATH/bicswift
+
+		see_other user
+		;;
+	GET)
+		export _TITLE="`_ "Edit user"` - $REMOTE_USER"
+		export _NAME="`_ Name`"
+		export _ADDRESS_LINE_1="`_ "Address line 1"`"
+		export _ADDRESS_LINE_2="`_ "Address line 2"`"
+		export _ZIP_CODE="`_ "Zip code"`"
+		export _PHONE_NUMBER="`_ "Phone number"`"
+		export _ACCOUNT_IBAN="`_ "Account IBAN"`"
+		export _ACCOUNT_BICSWIFT="`_ "Account BICSWIFT"`"
+		export _SUBMIT="`_ Submit`"
+
+		export name="`zcat $USER_PATH/name`"
+		export address_line_1="`zcat $USER_PATH/address_line_1`"
+		export address_line_2="`zcat $USER_PATH/address_line_2`"
+		export zip="`zcat $USER_PATH/zip`"
+		export phone_number="`zcat $USER_PATH/phone_number`"
+		export iban="`zcat $USER_PATH/iban`"
+		export bicswift="`zcat $USER_PATH/bicswift`"
+
+		NormalCat
+		;;
+	*)
+		echo "Status: 405 Method Not Allowed"
+		echo
+		;;
+esac
diff --git a/htdocs/vss/color.h b/htdocs/vss/color.h
new file mode 100644
index 0000000..e242ce3
--- /dev/null
+++ b/htdocs/vss/color.h
@@ -0,0 +1,20 @@
+#ifndef COLORS_N
+#define COLORS_N
+#define VAL_COLOR_0 #2c2c2c
+#define VAL_COLOR_1 #bd4a6a
+#define VAL_COLOR_2 #3a5e46
+#define VAL_COLOR_3 #a3975a
+#define VAL_COLOR_4 #7389bd
+#define VAL_COLOR_5 #8a77ac
+#define VAL_COLOR_6 #415c5f
+#define VAL_COLOR_7 #8b85a5
+#define VAL_COLOR_8 #b4aac1
+#define VAL_COLOR_9 #bd4a87
+#define VAL_COLOR_10 #53d38a
+#define VAL_COLOR_11 #b8cd2e
+#define VAL_COLOR_12 #739cbd
+#define VAL_COLOR_13 #9589c5
+#define VAL_COLOR_14 #16d0ba
+#define VAL_COLOR_15 #f5f5f5
+#define ALL_COLORS 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+#endif
diff --git a/htdocs/vss/c.txt b/htdocs/vss/color.txt
similarity index 100%
rename from htdocs/vss/c.txt
rename to htdocs/vss/color.txt
diff --git a/htdocs/vss/text-size.h b/htdocs/vss/text-size.h
new file mode 100644
index 0000000..ea0c090
--- /dev/null
+++ b/htdocs/vss/text-size.h
@@ -0,0 +1,18 @@
+/* x = i * 0.880; f(x) = 0.112 * x ^ 3 + -0.610 * x ^ 2 + 3.900 * x + 6.100 */
+#ifndef TEXT_SIZES_H
+#define TEXT_SIZES_H
+#define VAL_TEXT_SIZE_9 9px
+#define VAL_TEXT_SIZE_11 11px
+#define VAL_TEXT_SIZE_14 14px
+#define VAL_TEXT_SIZE_17 17px
+#define VAL_TEXT_SIZE_20 20px
+#define VAL_TEXT_SIZE_26 26px
+#define VAL_TEXT_SIZE_33 33px
+#define VAL_TEXT_SIZE_42 42px
+#define VAL_TEXT_SIZE_54 54px
+#define VAL_TEXT_SIZE_69 69px
+#define VAL_TEXT_SIZE_88 88px
+#define VAL_TEXT_SIZE_111 111px
+#define VAL_TEXT_SIZE_138 138px
+#define ALL_TEXT_SIZES 9, 11, 14, 17, 20, 26, 33, 42, 54, 69, 88, 111, 138
+#endif
diff --git a/htdocs/vss/vss.config.h b/htdocs/vss/vss.config.h
index 50c5ef4..bb33263 100644
--- a/htdocs/vss/vss.config.h
+++ b/htdocs/vss/vss.config.h
@@ -2,26 +2,31 @@
 #define CONFIG_ROUND
 #define CONFIG_VARS
 #include "vss/vss.h"
+#define CF #c1c3da
+#define CM VAL(COLOR, 13)
 
-CALL(TEXT_SIZE, TS)
-CALL(BG_COLOR, CS)
-CALL(COLOR, CS)
-CALL(BO_COLOR, CS)
+ROUND
+ALL_FLEX
+CALL(TEXT_SIZE, ALL_TEXT_SIZES)
+CALL(BACKGROUND_COLOR, ALL_COLORS)
+CALL(COLOR, ALL_COLORS)
+CALL(BORDER, ALL_COLORS)
 CALL(SIZE, SS)
-CALL(PADDING, SS)
-CALL(DIR_PADDING, SS)
-CALL(ABS_PADDING, SS)
-CALL(CENTER_ABS_V, SS)
-CALL(HORIZONTAL, SS)
-CALL(VERTICAL, SS)
-CALL(MARGIN, SS)
-CALL(FLEX_VERTICAL, SS)
-CALL(ROUND_T, TS)
-CALL(ROUND_EDGE, SS)
-ROUND_PADDING( , l)
-ROUND_PADDING( s, l)
-ROUND_PADDING( s, xl)
+CALL(SIZE, ALL_SIZES)
+CALL(PADDING, ALL_SIZES)
+CALL(AXIS_horizontal, ALL_SIZES)
+CALL(AXIS_vertical, ALL_SIZES)
+AXIS_0
+FULL_SIZE
+/* CALL(FLEX_VERTICAL, SS) */
+CALL(ROUND_T, ALL_TEXT_SIZES)
+CALL(ROUND_EDGE, ALL_SIZES)
+ROUND_PADDING( 4, 14)
+ROUND_PADDING( 8, 17)
+ROUND_PADDING( 8, 20)
+ROUND_PADDING( 8, 26)
 
+.dn { display: none; }
 .cf { background: #3c403c; }
 .cb { color: #c1c3da; }
 body {
@@ -42,6 +47,8 @@ input:focus {
         border: solid thin #9589c5;
         outline: #9589c5;
 }
+.abs { position: absolute; }
+.rel { position: relative; }
 a { color: #c1c3da; }
 style { display: none !important; }
 .oav { overflow: auto; }
@@ -57,14 +64,20 @@ pre { font-family: monospace; }
 .wn { white-space: nowrap; }
 button, .btn {
 	font-size: inherit;
-	padding: var(--S);
-	background-color: var(--C0);
+	padding: VAL(SIZE, );
+	background-color: VAL(COLOR, 0);
 	color: var(--C15);
 	border: none;
 	//border: solid thin black;
 	box-shadow: 0 3px 3px rgba(0, 0, 0, 0.5);
 	text-decoration: none;
 }
+
+button > a, .btn > a, a.btn {
+	display: block;
+	text-decoration: none;
+}
+
 button:hover, .btn:hover, .card:hover {
 	box-shadow: 0 5px 5px rgba(0, 0, 0, 0.5);
 }
@@ -87,3 +100,23 @@ pre {
 .card a {
 	//color: var(--C0);
 }
+
+.menu:not(.js) > div { display: none; }
+.menu input[type="checkbox"] { display: none; }
+.menu :checked + div { display: block; }
+.menu a { text-decoration: none; }
+.menu a:hover { color: white; }
+
+.pn { padding: 0; }
+.fix { position: fixed; }
+
+.j { bottom: var(--S); }
+.l { right: var(--S); }
+.cp { cursor: pointer; }
+.ttc { text-transform: capitalize; }
+
+input.c0 { border: solid thin VAL(COLOR, 0); color: CF; }
+input.c0:focus {
+        border: solid thin CM;
+        outline: CM;
+}
diff --git a/init.sh b/init.sh
deleted file mode 100644
index e039b0f..0000000
--- a/init.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/ksh
-mkdir dev 2>/dev/null || true
-cd dev
-mknod zero c 13 12
-mknod null c 13 2
-mknod tty c 1 0
-chmod 666 zero null tty
-cd -
-# copy www and root users to etc/master.passwd
-# and run the following inside the chroot
-# pwd_mkdb /etc/master.passwd
diff --git a/js/menu.js b/js/menu.js
new file mode 100644
index 0000000..c4364ea
--- /dev/null
+++ b/js/menu.js
@@ -0,0 +1,14 @@
+function menu(cont_el) {
+	let menu_el = cont_el.querySelector('div');
+	cont_el.classList.add("js");
+	cont_el.removeChild(cont_el.querySelector("input"));
+	menu_el.classList.add("dn");
+	let sem_menu_visible = false;
+	cont_el.onclick = function (ev) {
+		sem_menu_visible = !sem_menu_visible;
+		if (sem_menu_visible)
+			menu_el.classList.remove("dn");
+		else
+			menu_el.classList.add("dn");
+	};
+}
diff --git a/js/nfiles.js b/js/nfiles.js
new file mode 100644
index 0000000..82e1dab
--- /dev/null
+++ b/js/nfiles.js
@@ -0,0 +1,148 @@
+function basename(str) {
+	return str.substr(str.lastIndexOf('/') + 1);
+}
+
+function nfiles(nfiles_name, nfiles_el, submit_el, submit_label, file_name, add_ep, edit_ep) {
+	let loading = 0;
+
+	function load_start() {
+		submit_el.innerText = i_wait;
+		submit_el.disabled = true;
+		loading++;
+	}
+
+	function load_end() {
+		loading--;
+		if (!loading) {
+			submit_el.innerText = _submit;
+			submit_el.disabled = false;
+		}
+	}
+
+	let search = nfiles_el.querySelector('a');
+	let textarea = nfiles_el.querySelector('textarea');
+
+	nfiles_el.removeChild(search);
+	nfiles_el.removeChild(textarea);
+
+	let nfiles_cont = document.createElement('div');
+	nfiles_cont.classList.add('fw', 'f', 'h8', 'v8', 'fic');
+	nfiles_el.appendChild(nfiles_cont);
+
+	let upload_image = document.createElement('button');
+	upload_image.innerText = '📁';
+	upload_image.classList.add('btn', 'round', 'p8', 'tsxl');
+	upload_image.type = "button";
+	nfiles_el.appendChild(upload_image);
+
+	let file_input = document.createElement('input');
+	file_input.type = 'file';
+	file_input.name = file_name;
+	file_input.classList.add('dn');
+	file_input.multiple = true;
+	nfiles_el.appendChild(file_input);
+
+	let hidden_input = document.createElement('input');
+	hidden_input.type = 'hidden';
+	hidden_input.name = nfiles_name;
+	nfiles_el.appendChild(hidden_input);
+
+	let nfiles_map = {};
+
+	const body = document.querySelector('body');
+
+	function nfiles_insert(url) {
+		let image = document.createElement('div');
+		image.classList.add('tss', 'p4', 'c0', 'rxs', 'h8', 'f', 'fic');
+
+		let image_link = document.createElement('a');
+		const bname = basename(url);
+		image_link.innerText = bname;
+		image_link.href = url;
+		image.appendChild(image_link);
+
+		nfiles_map[url] = true;
+
+		let btn = document.createElement('button');
+		btn.classList.add('btn', 'round', 'p4', 'tss', 'c15', 'cf0');
+		btn.innerText = '×';
+		btn.type = 'button';
+		btn.onclick = function (ev) {
+			const formData = new FormData();
+			const noext = bname.substr(0, bname.indexOf('.'));
+
+			formData.set('delete_' + noext, 'on');
+
+			nfiles_cont.removeChild(image);
+			delete nfiles_map[bname];
+
+			fetch(edit_ep, {
+				method: 'POST',
+				data: formData,
+			});
+		};
+		image.appendChild(btn);
+
+		nfiles_cont.appendChild(image);
+	}
+
+	textarea.value.split('\n').filter(url => !!url).map(nfiles_insert);
+
+	let main_form = document.querySelector('form');
+
+	main_form.onsubmit = function() {
+		file_input.disabled = true;
+		hidden_input.value = Object.keys(nfiles_map).join('\n');
+		console.log('final value', hidden_input.value);
+	}
+
+	upload_image.onclick = function (ev) {
+		file_input.click(ev);
+	};
+
+	const _submit = submit_label;
+	const i_wait = "⏱";
+
+	file_input.onchange = function (ev) {
+		let files = file_input.files;
+		let formData = new FormData();
+
+		for (const file of files)
+			formData.append(file_input.name, file, file.name);
+
+		load_start();
+
+		fetch(add_ep, {
+			method: 'POST',
+			headers: {
+				'Accept': 'text/plain',
+			},
+			body: formData,
+		})
+			.then(response => {
+				return response.text().then(data => {
+					const san = data.replaceAll('\r', '');
+					if (response.ok) {
+						console.log('received', data);
+						// TODO might have multiple images
+						san.split('\n')
+							.filter(url => !!url)
+							.map(nfiles_insert);
+						load_end();
+					} else {
+						load_end();
+						const notif = document.createElement('div');
+						notif.classList.add('tsxs', 'cf9', 'cp');
+						notif.innerText = san;
+						notif.onclick = function (ev) {
+							body.removeChild(notif);
+
+						};
+						body.prepend(notif);
+					}
+				});
+			});
+
+		return false;
+	};
+}
diff --git a/lib/auth.sh b/lib/auth.sh
index e73b090..beb2069 100644
--- a/lib/auth.sh
+++ b/lib/auth.sh
@@ -1,10 +1,10 @@
 #!/bin/ksh
 
-. $ROOT/lib/very-common.sh
+. $DOCUMENT_ROOT/lib/very-common.sh
 
-if [[ -z "$cookie" ]] || [[ ! -f "$ROOT/sessions/$cookie" ]]; then
+if [[ -z "$cookie" ]] || [[ ! -f "$DOCUMENT_ROOT/sessions/$cookie" ]]; then
 	Unauthorized
 fi
 
-user="`cat $ROOT/sessions/$cookie`"
+user="`cat $DOCUMENT_ROOT/sessions/$cookie`"
 REMOTE_USER=$user
diff --git a/lib/command.sh b/lib/command.sh
new file mode 100644
index 0000000..30d446b
--- /dev/null
+++ b/lib/command.sh
@@ -0,0 +1,26 @@
+. $DOCUMENT_ROOT/lib/common.sh
+
+if [[ "$REQUEST_METHOD" != "GET" ]]; then
+	echo "Status: 405 Method Not Allowed"
+	echo
+	exit
+fi
+
+Command() {
+	local args="$@"
+	if [[ -z "$args" ]]; then
+		args=$SCRIPT
+	fi
+
+	if [[ "$HTTP_ACCEPT" == "text/plain" ]]; then
+		NormalHead 200
+		echo
+	else
+		Normal 200
+		echo
+		export _TITLE="`_ Command` - $args"
+		export MENU="`Menu`"
+		export OUTPUT="`literal`"
+		Cat command
+	fi
+}
diff --git a/lib/common.sh b/lib/common.sh
index 4f4b663..0905283 100644
--- a/lib/common.sh
+++ b/lib/common.sh
@@ -1,10 +1,42 @@
 #!/bin/ksh
 
-. $ROOT/lib/very-common.sh
+. $DOCUMENT_ROOT/lib/very-common.sh
 
 Forbidden() {
 	NormalHead 403
 	_TITLE="`_ Forbidden`"
+	if [[ $# -ge 1 ]]; then
+		_TITLE="$_TITLE - $@"
+	fi
+	export _TITLE
+	echo
+	Head
+	export MENU="`Menu`"
+	Cat fatal
+	exit
+}
+
+NotAllowed() {
+	NormalHead 405 Method Not Allowed
+	_TITLE="`_ Method Not Allowed`"
+	if [[ $# -ge 1 ]]; then
+		_TITLE="$_TITLE - $@"
+	fi
+	export _TITLE
+	echo
+	Head
+	export MENU="`Menu`"
+	Cat fatal
+	exit
+}
+
+NotFound() {
+	NormalHead 404 Not Found
+	_TITLE="`_ Not Found`"
+	if [[ $# -ge 1 ]]; then
+		_TITLE="$_TITLE - $@"
+	fi
+	export _TITLE
 	echo
 	Head
 	export MENU="`Menu`"
@@ -15,11 +47,15 @@ Forbidden() {
 bc() {
 	read exp
 	#echo "BC='$exp'" >&2
-	echo "$exp" | $ROOT/usr/bin/bc "$@"
+	echo "$exp" | $DOCUMENT_ROOT/usr/bin/bc "$@"
+}
+
+_urldecode() {
+	sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf "%b"
 }
 
 urldecode() {
-	echo $@ | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf "%b"
+	echo $@ | _urldecode
 }
 
 url2vars() {
@@ -29,16 +65,13 @@ url2vars() {
 	eval "$exp"
 }
 
-zcat() {
-	[[ -f "$@" ]] && cat $@ || true
-}
-
 case "$REQUEST_METHOD" in
 	POST)
 		case "$CONTENT_TYPE" in
 			multipart/form-data*)
+				grep -q "^$REMOTE_USER$" $DOCUMENT_ROOT/.uploaders || Forbidden "`_ "You don't have upload permissions"`"
 				boundary="`echo $CONTENT_TYPE | sed 's/.*=//'`"
-				mpfd "$boundary" 2>&1
+				$DOCUMENT_ROOT/usr/bin/mpfd "$boundary" 2>&1
 				;;
 			application/x-www-form-urlencoded*)
 				read line1 || true
@@ -83,7 +116,7 @@ counter_dec() {
 }
 
 sum_lines_exp() {
-	echo -n '('
+	echo -n '(0'
 	sed 's/$/ +/' | tr '\n' ' ' | sed 's/ + $//'
 	echo -n ')'
 }
@@ -92,10 +125,18 @@ revlines() {
 	rev | tr '\n' '~' | rev | tr '~' '\n'
 }
 
+_see_other() {
+	echo 'Status: 303 See Other'
+	echo "Location: $1"
+	echo
+	exit
+}
+
 see_other() {
 	echo 'Status: 303 See Other'
 	echo "Location: /e/$1$2"
 	echo
+	exit
 }
 
 no_html() {
@@ -108,17 +149,17 @@ format_df() {
 }
 
 df_dir() {
-	if [[ ! -d $ROOT/$1 ]]; then
+	if [[ ! -d $DOCUMENT_ROOT/$1 ]]; then
 		return
 	fi
-	du_user="`du -c $ROOT/$1 | tail -1 | awk '{print $1}'`"
+	du_user="`du -c $DOCUMENT_ROOT/$1 | tail -1 | awk '{print $1}'`"
 	format_df $1 `calcround "$du_user * 1024"`
 }
 
 dir_df() {
-	ls $ROOT/$1 | \
+	ls $DOCUMENT_ROOT/$1 | \
 		while read line; do
-			path=$ROOT/$1/$line
+			path=$DOCUMENT_ROOT/$1/$line
 			OWNER="`cat $path/.owner`"
 			[[ "$OWNER" != "$DF_USER" ]] || df_dir $1/$line
 		done
@@ -129,6 +170,8 @@ df() {
 	df_dir htdocs/img/$DF_USER
 	dir_df shops
 	dir_df poems
+	dir_df sems
+	dir_df schools
 }
 
 df_total_exp() {
@@ -147,21 +190,23 @@ calcround() {
 }
 
 free_space() {
-	N_USERS="`cat $ROOT/.htpasswd | wc -l | sed 's/ //g'`"
+	N_USERS="`cat $DOCUMENT_ROOT/.htpasswd | wc -l | sed 's/ //g'`"
 	FREE_SPACE_EXP="(20000000000 / $N_USERS)"
 	calcround "$FREE_SPACE_EXP"
 }
 
 FREE_SPACE="`free_space`"
 
-_fbytes() {
+__fbytes() {
 	[[ -z "$DF_USER" ]] && Fatal 400 Checking bytes of unknown user
-	# echo exp="`df_total_exp`" >&2
 	OCCUPIED_SPACE="`df_total`"
 	CAN_EXP="($FREE_SPACE - $OCCUPIED_SPACE) >= $1"
-	# echo CAN_EXP="$CAN_EXP" >&2
 	CAN="`echo $CAN_EXP | bc -l`"
-	if [[ "$CAN" == "0" ]]; then
+	[[ "$CAN" == "0" ]]
+}
+
+_fbytes() {
+	if __fbytes $1; then
 		Fatal 400 No available space
 	fi
 }
@@ -173,37 +218,36 @@ fbytes() {
 
 fmkdir() {
 	if [[ ! -d "$1" ]]; then
-		fbytes /empty
+		fbytes $DOCUMENT_ROOT/empty
 		mkdir -p "$1"
 	fi
 }
 
 fwrite() {
-	TARGET=$1
-	shift
-	count="`$@ | wc | awk '{print $3}'`"
-	_fbytes $count
-	$@ > $TARGET
+	cat - > $1
+	local count="`cat $1 | wc | awk '{print $3}'`"
+	if __fbytes $count; then
+		rm $1
+		Fatal 400 No available space
+	fi
 }
 
 fappend() {
-	TARGET=$1
-	shift
-	count="`$@ | wc | awk '{print $3}'`"
-	_fbytes $count
-	$@ >> $TARGET
-}
-
-rand_str_1() {
-	# TODO maybe remove lowercase conversion
-	xxd -l32 -ps $ROOT/dev/urandom | xxd -r -ps | openssl base64 \
-		    | tr -d = | tr + - | tr / _ | tr '[A-Z]' '[a-z]'
+	cat - > $DOCUMENT_ROOT/tmp/append
+	local count="`cat $DOCUMENT_ROOT/tmp/append | wc | awk '{print $3}'`"
+	if __fbytes $count; then
+		rm $DOCUMENT_ROOT/tmp/append
+		Fatal 400 No available space
+	else
+		cat $DOCUMENT_ROOT/tmp/append >> $1
+		rm $DOCUMENT_ROOT/tmp/append
+	fi
 }
 
 ## COMPONENTS
 
 Whisper() {
-	WHISPER_PATH=$ROOT/users/$REMOTE_USER/.whisper
+	WHISPER_PATH=$DOCUMENT_ROOT/users/$REMOTE_USER/.whisper
 	WHISPER="`zcat $WHISPER_PATH | no_html`"
 	if [[ -z "$WHISPER" ]]; then
 		return
@@ -213,16 +257,34 @@ Whisper() {
 	rm $WHISPER_PATH
 }
 
+NotNormal() {
+	NormalHead "$1"
+	shift
+	echo "Link: <http://$HTTP_HOST$@>; rel=\"alternate\"; hreflang=\"x-default\""
+	echo
+	Head
+	Whisper
+	export MENU="`Menu`"
+}
+
 Normal() {
 	NormalHead "$1"
 	echo "Link: <http://$HTTP_HOST/e/$2$3>; rel=\"alternate\"; hreflang=\"x-default\""
 	echo
 	Head
-
 	Whisper
 	export MENU="`Menu`"
 }
 
+UserNormal() {
+	NormalHead "$1"
+	echo "Link: <http://$HTTP_HOST/e/$2$3>; rel=\"alternate\"; hreflang=\"x-default\""
+	echo
+	export HEAD="`Head`"
+	export WHISPER="`Whisper`"
+	export MENU="`Menu`"
+}
+
 NormalCat() {
 	Normal 200 $SCRIPT $1
 	Cat $SCRIPT
@@ -233,46 +295,197 @@ Fatal() {
 	shift
 	allargs="$@"
 	export _TITLE="`_ "$allargs"`"
-	export _HEAD_TITLE="tty.pt - $SC - $_TITLE"
-	Normal $SC
-	Cat fatal
-	exit 1
+
+	if [[ "$HTTP_ACCEPT" == "text/plain" ]]; then
+		NormalHead $SC
+		echo
+		echo $_TITLE
+		exit 1
+	else
+		export _HEAD_TITLE="tty.pt - $SC - $_TITLE"
+		Normal $SC
+		Cat fatal
+		exit 1
+	fi
 }
 
 DF_USER=$REMOTE_USER
-SCRIPT="`basename $SCRIPT_NAME | cut -f1 -d'.'`"
-Wrap() {
-	if [[ ! -z "$@" ]]; then
-		echo "<div class=\"_ v f fw fcc fic\">$@</div>"
-	fi
+SCRIPT="`echo $DOCUMENT_URI | awk -F '/' '{print $2}'`"
+ARG="`echo $DOCUMENT_URI | awk -F '/' '{print $3}'`"
+set -- `echo $DOCUMENT_URI | tr '/' ' '`
+if [[ "$SCRIPT" == "e" ]]; then
+	SCRIPT="`echo $DOCUMENT_URI | awk -F '/' '{print $3}'`"
+	ARG="`echo $DOCUMENT_URI | awk -F '/' '{print $4}'`"
+	e_mode=1
+fi
+
+fw() {
+	csurround div "class=\"h$1 v$1 f fw fcc fic\""
 }
 
-not_valid_id() {
+invalid_id() {
 	valid="`echo $@ | tr -cd '[a-zA-Z0-9]_'`"
-	[[ "$valid" != "$@" ]]
+	count="`echo $@ | wc -c`"
+	[[ "$valid" != "$@" ]] || [[ "$count" -le 0 ]]
 }
 
-not_valid_password() {
+invalid_password() {
 	count="`echo $@ | wc -c`"
 	[[ "$count" -le 8 ]]
 }
 
-not_valid_lang() {
+invalid_lang() {
 	lang="$@"
-	! grep -q "$lang" $ROOT/locale/langs
+	! grep -q "$lang" $DOCUMENT_ROOT/locale/langs
 }
 
-BigButton() {
-	ID="$1"
-	_TITLE="`_ $1`"
-	WHERE="$2"
-	cat <<!
-<a class="btn tsxl" href="/e/$WHERE?${WHERE}_id=$ID">
+Buttons() {
+	if [[ $e_mode == 1 ]]; then
+		while read id; do
+			_TITLE="`_ $id`"
+			where="$2"
+			extra="$3"
+			cat <<!
+<div><a class="btn $1" href="/e/$where?${where}_id=$id$extra">
 	$_TITLE
-</a>
+</a></div>
 !
+		done
+	else
+		while read id; do
+			_TITLE="`_ $id`"
+			where="$2"
+			extra="$3"
+			cat <<!
+<div><a class="btn $1" href="/$where/$id$extra">
+	$_TITLE
+</a></div>
+!
+		done
+	fi
 }
 
 BigButtons() {
-	while read line; do BigButton $line $1; done
+	Buttons "tsxl" "$1" "$2"
+}
+
+SmallButtons() {
+	Buttons "c0 ps rs" "$1" "$2"
+}
+
+for_each_in() {
+	path="$1"
+	target="$2"
+	find_id="$3"
+
+	ls $path | while read id; do
+		if grep -q "$find_id" "$path/$id/$target"; then
+			echo $id
+		fi
+	done
+}
+
+im() {
+	ret=""
+	while [[ $# -ge 1 ]]; do
+		if [[ "$REMOTE_USER" == "$1" ]]; then
+			ret="1"
+			break;
+		fi
+		shift
+	done
+
+	[[ "$ret" == "1" ]]
+}
+
+contents=$DOCUMENT_ROOT/tmp/contents
+
+cond() {
+	# tee $contents$1
+	cat - > $contents$1
+	[[ -z "`cat $contents$1`" ]]
+}
+
+surround() {
+	echo "<$@>"
+	cat -
+	echo "</$1>"
+}
+
+csurround() {
+	local contents="`cat -`"
+
+	[[ -z "$contents" ]] || {
+		echo $contents | surround $@
+	}
+
+}
+
+Field() {
+	cat <<!
+<div class="_">
+	<span class="tsxs">$1</span>
+	<span>$2</span>
+</div>
+!
+}
+
+EditBtn() {
+	local i_edit="✎"
+	echo $i_edit | surround a "href=\"$1\"" "class=\"$RB\""
+}
+
+nfiles() {
+	urldecode "$@" | sed '/^$/d' | tr -d '\r'
+}
+
+a2l() {
+	while [[ $# -ge 1 ]]; do
+		echo $1
+		shift;
+	done
+}
+
+noslash() {
+	sed -e 's:/:\\/:g' $1
+}
+
+cslash() {
+	if grep -q "^$REMOTE_USER$" $DOCUMENT_ROOT/.slash; then
+		cat -
+	else
+		noslash -
+	fi
+}
+
+mpfd-ls() {
+	local file_count="`cat $DOCUMENT_ROOT/tmp/mpfd/file-count`"
+	for i in `seq 0 $file_count`; do
+		local FILE_PATH=$DOCUMENT_ROOT/tmp/mpfd/file$i
+		filename="`cat $FILE_PATH-name`"
+		echo $FILE_PATH $filename
+	done
+}
+
+literal() {
+	sed 's/</\<\;/g'
+}
+
+ls_shown() {
+	ls $1 | while read line; do
+		if [[ ! -f "$1/$line/.hidden" ]]; then
+			echo $line
+		fi
+	done
+}
+
+export GIT_HTTP_EXPORT_ALL=1
+export REQUEST_METHOD
+
+git_backend() {
+	export GIT_PROJECT_ROOT="/"
+	export PATH_INFO="`echo $DOCUMENT_URI | sed 's|^/~|/home/|'`"
+	# echo PATH_INFO$PATH_INFO
+	$DOCUMENT_ROOT/usr/local/libexec/git/git-http-backend 2>&1
+	exit
 }
diff --git a/lib/optional-auth.sh b/lib/optional-auth.sh
index 34c5e0c..84081dd 100644
--- a/lib/optional-auth.sh
+++ b/lib/optional-auth.sh
@@ -1,8 +1,14 @@
 #!/bin/ksh
 
-. $ROOT/lib/very-common.sh
+. $DOCUMENT_ROOT/lib/very-common.sh
 
-if [[ -f "$ROOT/sessions/$cookie" ]]; then
-	user="`cat $ROOT/sessions/$cookie`"
+if [[ -f "$DOCUMENT_ROOT/sessions/$cookie" ]]; then
+	user="`cat $DOCUMENT_ROOT/sessions/$cookie`"
 	REMOTE_USER=$user
+elif [[ ! -z "$HTTP_AUTHORIZATION" ]]; then
+	AUTH="`echo $HTTP_AUTHORIZATION | awk '{print $2}' | openssl base64 -d | tr ':' ' '`"
+	username="`echo $AUTH | awk '{print $1}'`"
+	password="`echo $AUTH | awk '{print $2}'`"
+	auth $username $password 2>&1
+	REMOTE_USER=$username
 fi
diff --git a/lib/order.sh b/lib/order.sh
index bb063fc..5732f62 100644
--- a/lib/order.sh
+++ b/lib/order.sh
@@ -1,28 +1,12 @@
 #!/bin/ksh
 
 OrderStateVendor() {
-	TEMP="`getopt r: $*`"
-	if [ $? -ne 0 ]; then
-		exit 1;
-	fi
-	set -- $TEMP
-	while [ $# -ne 0 ]; do
-		case "$1" in
-			-r)
-				return_str="<input name=\"return\" type=\"hidden\" value=\"$2\"></input>"
-				shift 2
-				;;
-			--)
-				shift
-				break;
-				;;
-		esac
-	done
+	return_str="<input name=\"return\" type=\"hidden\" value=\"$1\"></input>"
 
 	cat <<!
-<form action="/e/order" method="POST" class="tac">
+<form action="/shop/$shop_id/order/$2" method="POST" class="tac">
 	<input type="hidden" name="shop_id" value="$shop_id"></input>
-	<input type="hidden" name="order_id" value="$1"></input>
+	<input type="hidden" name="order_id" value="$2"></input>
 	$return_str
 	<button class="dib ps c$ORDER_STATE_COLOR">
 		$_ORDER_STATE
@@ -59,29 +43,11 @@ order_state_color() {
 }
 
 OrderState() {
-	TEMP="`getopt r: $*`"
-	if [ $? -ne 0 ]; then
-		exit 1;
-	fi
-	set -- $TEMP
-	while [ $# -ne 0 ]; do
-		case "$1" in
-			-r)
-				ret=$1$2
-				shift 2
-				;;
-			--)
-				shift
-				break;
-				;;
-		esac
-	done
-
-	_ORDER_STATE="`_ "$1"`"
-	ORDER_STATE_COLOR="`order_state_color "$1"`"
+	_ORDER_STATE="`_ "$2"`"
+	ORDER_STATE_COLOR="`order_state_color "$2"`"
 
-	if [[ "$REMOTE_USER" == "$SHOP_OWNER" ]]; then
-		OrderStateVendor $ret $2
+	if im $SHOP_OWNER; then
+		OrderStateVendor $1 $3
 	else
 		OrderStateUser
 	fi
diff --git a/lib/school.sh b/lib/school.sh
new file mode 100644
index 0000000..fffbe53
--- /dev/null
+++ b/lib/school.sh
@@ -0,0 +1,34 @@
+SCHOOL_PATH="$DOCUMENT_ROOT/schools/$school_id"
+
+if [[ -z "$school_id" ]] || [[ ! -d "$SCHOOL_PATH" ]]; then
+	Fatal 404 School not found
+fi
+
+SCHOOL_OWNER="`cat $SCHOOL_PATH/.owner`"
+SCHOOL_BTN="<a class=\"$RB\" href=\"/e/school?school_id=$school_id\">🏫</a>"
+DF_USER="$SCHOOL_OWNER"
+
+export school_id
+export SCHOOL_BTN
+
+LabeledIDEdit() {
+	label=$1
+	typ=$2
+	typ_id=$3
+	assoc=$4
+	assoc_id=$5
+	extra=$6
+	cat <<!
+<div class="c0 ps rs _s">
+	<a class="tdn _s" href="/e/$typ?${typ}_id=$typ_id$extra">
+		<small>$label</small>
+		<span>$typ_id</span>
+	</a>
+	<a class="$RBXS cf0 c15" href="/e/$assoc-${typ}-associate?${assoc}_id=$assoc_id$extra">🖉</a>
+</div>
+!
+}
+
+invalid_s() {
+	invalid_id $2 || [[ ! -d "$SCHOOL_PATH/$1/$2" ]]
+}
diff --git a/lib/sem.sh b/lib/sem.sh
new file mode 100644
index 0000000..4e45d2e
--- /dev/null
+++ b/lib/sem.sh
@@ -0,0 +1,155 @@
+SemMenuOption() {
+	if [[ $e_mode == 1 ]]; then
+		echo "<div><a href=\"/e/sem-$1?sem_id=$sem_id\">`_ "$1"`</a></div>"
+	else
+		echo "<div><a href=\"/sem/$sem_id/$1\">`_ "$1"`</a></div>"
+	fi
+}
+
+js() {
+	cat <<!
+`cat $DOCUMENT_ROOT/js/menu.js`
+menu(document.getElementById("sem_menu_cont"));
+!
+}
+
+access() {
+	$SEM -p < $SEM_FILE
+}
+
+export JS="`js`"
+
+SemMenu() {
+	current="$1"
+	options=""
+	present="`echo $PRESENT | awk '{ print $1 }'`"
+
+	if im $SEM_OWNER; then
+		if [[ "$current" != "start" ]]; then
+			options="$options`SemMenuOption start`"
+		fi
+
+		if [[ "$current" != "pause" ]] && $SEM -p < $SEM_FILE | grep -q '^P '; then
+			options="$options`SemMenuOption pause`"
+		fi
+
+		if [[ "$current" != "resume" ]] && $SEM -p < $SEM_FILE | grep -q '^A '; then
+			options="$options`SemMenuOption resume`"
+		fi
+	else
+		case "$present" in
+			P)
+				if [[ "$current" != "pause" ]]; then
+					options="$options`SemMenuOption pause`"
+				fi
+
+				if [[ "$current" != "stop" ]]; then
+					options="$options`SemMenuOption stop`"
+				fi
+
+				;;
+			A)
+				if [[ "$current" != "resume" ]]; then
+					options="$options`SemMenuOption resume`"
+				fi
+
+				if [[ "$current" != "stop" ]]; then
+					options="$options`SemMenuOption stop`"
+				fi
+
+				;;
+		esac
+	fi
+
+	if [[ "$current" != "pay" ]]; then
+		options="$options`SemMenuOption pay`"
+	fi
+
+	if [[ "$current" != "transfer" ]]; then
+		options="$options`SemMenuOption transfer`"
+	fi
+
+	if [[ "$current" != "buy" ]]; then
+		options="$options`SemMenuOption buy`"
+	fi
+
+	cat <<!
+<label id="sem_menu_cont" class="$RB rel c15 cf0 menu">
+	+
+	<input type="checkbox" />
+	<div class="abs v0 f ah ak p c0 ts btn ttc">
+		$options
+	</div>
+</label>
+!
+}
+
+IdOptions() {
+	while read value sel; do
+		if test "$sel" == "y"; then
+			echo "<option selected value=\"$value\">$value</option>"
+		else
+			echo "<option value=\"$value\">$value</option>"
+		fi
+	done
+}
+
+SourceIdOptions() {
+	if im $SEM_OWNER; then
+		cat $SEM_PATH/.owner $SEM_PATH/.members
+	else
+		echo $REMOTE_USER
+	fi | IdOptions
+}
+
+sem_op() {
+	$DOCUMENT_ROOT/usr/bin/sem-echo "`echo $@`" < $SEM_FILE > $DOCUMENT_ROOT/tmp/data.txt
+
+	if $SEM -q 2>&1 < $DOCUMENT_ROOT/tmp/data.txt; then
+		DF_USER=$SEM_OWNER
+		cat $DOCUMENT_ROOT/tmp/data.txt | fwrite $SEM_FILE
+		rm $DOCUMENT_ROOT/tmp/data.txt
+		git -C $DOCUMENT_ROOT/sem/$sem_id add data.txt
+		git -C $DOCUMENT_ROOT/sem/$sem_id commit -m "$@"
+		if [[ $e_mode == 1 ]]; then
+			_see_other e/sem?sem_id=$sem_id
+		else
+			_see_other .
+		fi
+	else
+		rm $DOCUMENT_ROOT/tmp/data.txt
+		Fatal 409 We can not have that
+	fi
+}
+
+sem_source() {
+	if [[ $e_mode == 1 ]]; then
+		SEM_PATH="$DOCUMENT_ROOT/sems/$sem_id"
+	else
+		SEM_PATH="$DOCUMENT_ROOT/sem/$sem_id"
+	fi
+	if [[ -z "$sem_id" ]]; then
+		sem_id="`echo $SCRIPT_NAME | awk -F'/' '{print $3}'`"
+		SEM_PATH="$DOCUMENT_ROOT/sem/$sem_id"
+		sem_script=1
+	fi
+	SEM_FILE="$SEM_PATH/data.txt"
+
+	if [[ -z "$sem_id" ]] ; then
+		Fatal 404 Sem not found
+	fi
+
+	SEM_OWNER="`cat $SEM_PATH/.owner`"
+	SEM="$DOCUMENT_ROOT/usr/bin/sem"
+
+	if [[ ! -d "$SEM_PATH" ]]; then
+		Fatal 404 Sem not found
+	fi
+
+	PRESENT="`access | grep $REMOTE_USER || echo`"
+	if [[ -z "$PRESENT" ]] && ! test $REMOTE_USER == quirinpa; then
+		Unauthorized
+	fi
+
+	export sem_id
+}
diff --git a/lib/shop.sh b/lib/shop.sh
index cf06e21..526cfc1 100644
--- a/lib/shop.sh
+++ b/lib/shop.sh
@@ -1,178 +1,55 @@
 get_product_path() {
-	echo $ROOT/shops/$shop_id/$1
+	echo $DOCUMENT_ROOT/shop/$shop_id/$1
 }
 
-ProductSummary() {
-	if [[ ! -z "$REMOTE_USER" ]]; then
-		qntstr=" x $quantity"
-	fi
-	cat <<!
-<div class="tsl">$product_price€$qntstr</div>
-!
-}
-
-ProductForm() {
-	PRODUCT_STOCK="`cat $PRODUCT_PATH/stock`"
-	cat <<!
-<div class="tsl">$product_price€</div>
-<div class="_ f fic">
-	<form action="./cart" method="post" class="_ f fic wn">
-		<input name="product_id" type="hidden" value="$PRODUCT_ID"></input>
-		<input name="shop_id" type="hidden" value="$shop_id"></input>
-		<input name="quantity" type="number" min="0" max="$PRODUCT_STOCK" value="$quantity" class="s_4_5"></input>
-		$return_str
-		<button class="$SRB">🛒</button>
-	</form>
-	$delete_form
-</div>
-!
-}
-
-DeleteProductForm() {
-	cat <<!
-<form action="./shop" method="post">
-	<input name="action" type="hidden" value="delete"></input>
-	<input name="product_id" type="hidden" value="$PRODUCT_ID"></input>
-	<input name="shop_id" type="hidden" value="$shop_id"></input>
-	<button class="$SRB">×</button>
-</form>
-!
+product_env() {
+	local product_id=$1
+	product_path="`get_product_path $product_id`"
+	product_images="`zcat $product_path/images`"
+	[[ ! -z "$product_images" ]] || product_images=/img/no-image.png
+	product_image_path="`echo "$product_images" | head -n 1`"
+	product_title="`cat $product_path/title`"
+	product_description="`cat $product_path/description`"
+	product_price="`cat $product_path/price`"
+	product_stock="`cat $product_path/stock`"
 }
 
-ProductImage() {
-	cat <<!
-<a href="$1"><img height="128" class="ofc" src="$1" /></a>
-!
-}
-
-Product() {
-	TEMP="`getopt r: $*`"
-	if [ $? -ne 0 ]; then
-		exit 1;
-	fi
-	set -- $TEMP
-	while [ $# -ne 0 ]; do
-		case "$1" in
-			-r)
-				if [[ -z "$REMOTE_USER" ]]; then
-					shift 2	
-					continue
-				fi
-				return_str="<input name=\"return\" type=\"hidden\" value=\"$2\"></input>"
-				SHOP_OWNER="`cat $SHOP_PATH/.owner`"
-				if [[ "$2" == "shop" ]] && [[ "$SHOP_OWNER" == "$REMOTE_USER" ]]; then
-					delete_form=y
-				else
-					if [[ "$2" == "product" ]]; then
-						multiple_images=y
-					fi
-				fi
-
-				shift 2
-				;;
-			--)
-				shift
-				break;
-				;;
-		esac
-	done
-
-	CART_PATH=$1
-	PRODUCT_ID=$2
-
-	if [[ ! -z "$delete_form" ]]; then
-		delete_form="`DeleteProductForm`"
-	fi
-
-	PRODUCT_PATH="`get_product_path $PRODUCT_ID`"
-
-	PRODUCT_IMAGES_CONTENT="`cat $PRODUCT_PATH/images`"
-	if [[ -z "$PRODUCT_IMAGES_CONTENT" ]]; then
-		PRODUCT_IMAGES_CONTENT=/img/no-image.png
-	fi
-
-	if [[ -z "$multiple_images" ]]; then
-		PRODUCT_IMAGES="`echo "$PRODUCT_IMAGES_CONTENT" | head -n 1 | while read image_path; do ProductImage $image_path; done`"
-	else
-		PRODUCT_IMAGES="`echo "$PRODUCT_IMAGES_CONTENT" | while read image_path; do ProductImage $image_path; done`"
-		if [[ ! -z $PRODUCT_IMAGES ]]; then
-			PRODUCT_IMAGES="<div class=\"f fw _ v fic fcc\">$PRODUCT_IMAGES</div>"
-		fi
-	fi
-
-	PRODUCT_TITLE="`cat $PRODUCT_PATH/title`"
-	PRODUCT_DESCRIPTION="`cat $PRODUCT_PATH/description`"
-	if [[ ! -z "$PRODUCT_DESCRIPTION" ]]; then
-		PRODUCT_DESCRIPTION="<p>$PRODUCT_DESCRIPTION</p>"
-	fi
-
-	product_price="`cat $PRODUCT_PATH/price`"
-
-	if [[ -f $CART_PATH ]]; then
-		quantity="`cat $CART_PATH | grep $PRODUCT_ID | awk '{print $2}'`"
-		if [[ -z "$quantity" ]]; then
-			quantity=0
-		fi
-	else
-		quantity=0
-	fi
-
-
-	if [[ -z "$return_str" ]]; then
-		summary="`ProductSummary`"
-	else
-		summary="`ProductForm`"
-	fi
-
+PImage() {
 	cat <<!
-<div class="card f v b0 fic p">
-	$PRODUCT_IMAGES
-	<a class="tsxl" href="/e/product?shop_id=$shop_id&product_id=$PRODUCT_ID">
-		$PRODUCT_TITLE
-	</a>
-	$PRODUCT_DESCRIPTION
-	$summary
-</div>
+<img height="128" class="ofc" src="`dirname $1`/small-`basename $1`" />
 !
 }
 
 process_cart() {
+	local product_id
+	local quantity
 	cat $1 | while read product_id quantity; do
-		PRODUCT_PATH="`get_product_path $product_id`"
-		PRODUCT_PRICE="`cat $PRODUCT_PATH/price`"
-		echo $quantity \* $PRODUCT_PRICE
+		local product_path="`get_product_path $product_id`"
+		local product_price="`cat $product_path/price`"
+		echo $quantity \* $product_price
 	done | sum_lines_exp
 }
 
-ProductsFromCart() {
-	TEMP="`getopt r: $*`"
-	if [ $? -ne 0 ]; then
-		exit 1;
-	fi
-	set -- $TEMP
-	while [ $# -ne 0 ]; do
-		case "$1" in
-			-r)
-				ret=$1$2
-				shift 2
-				;;
-			--)
-				shift
-				break;
-				;;
-		esac
-	done
-
-
-	cat $1 | while read product_id quantity; do
-		Product $ret $1 $product_id
+product_rm() {
+	local product_id=$1
+	cat $SHOP_PATH/$product_id/images | while read line; do
+		[[ ! -f "$DOCUMENT_ROOT$line" ]] || rm $DOCUMENT_ROOT$line
 	done
+	rm -rf $SHOP_PATH/$product_id
 }
 
-SHOP_PATH=$ROOT/shops/$shop_id
-USER_PATH=$ROOT/users/$REMOTE_USER
-USER_SHOPS_PATH=$USER_PATH/shops
-USER_SHOP_PATH=$USER_SHOPS_PATH/$shop_id
-CART_PATH=$USER_SHOP_PATH/cart
-
-export shop_id
+shop_source() {
+	if [[ -z "$shop_id" ]]; then
+		shop_id="$ARG"
+	fi
+	SHOP_PATH=$DOCUMENT_ROOT/shop/$shop_id
+	if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
+		Fatal 404 Shop not found
+	fi
+	USER_PATH=$DOCUMENT_ROOT/users/$REMOTE_USER
+	USER_SHOPS_PATH=$USER_PATH/shops
+	USER_SHOP_PATH=$USER_SHOPS_PATH/$shop_id
+	CART_PATH=$USER_SHOP_PATH/cart
+	SHOP_OWNER="`cat $SHOP_PATH/.owner`"
+	export shop_id
+}
diff --git a/lib/very-common.sh b/lib/very-common.sh
index dc4116b..23cd447 100644
--- a/lib/very-common.sh
+++ b/lib/very-common.sh
@@ -5,6 +5,7 @@ set -e
 
 [[ -z "$VERY_COMMON" ]] || return 0
 VERY_COMMON=y
+RES_CONTENT_TYPE="text/html; charset=utf-8"
 
 debug() {
 	echo Status: 500 Internal Error
@@ -12,17 +13,23 @@ debug() {
 	echo Sorry, I\'m currently debugging. Please wait.
 }
 
+zcat() {
+	[[ -f "$@" ]] && cat $@ || true
+}
+
 NormalHead() {
 	case "$1" in
 		200) STATUS_TEXT="OK";;
 		400) STATUS_TEXT="Bad Request";;
 		401) STATUS_TEXT="Unauthorized";;
 		404) STATUS_TEXT="Not Found";;
+		409) STATUS_TEXT="Conflict";;
 	esac
 	export STATUS_TEXT
 	export STATUS_CODE=$1
 	echo "Status: $1 $STATUS_TEXT"
-	echo 'Content-Type: text/html; charset=utf-8'
+	echo "Content-Type: $RES_CONTENT_TYPE"
+	[[ -z "$HEADERS" ]] || echo -n $HEADERS
 }
 
 Head() {
@@ -37,11 +44,17 @@ Head() {
 !
 }
 
+Scat() {
+	cat $1.html | envsubst
+	echo "</html>"
+	exit
+}
+
 Cat() {
 	if [[ $# -lt 1 ]]; then
 		envsubst
 	else
-		cat $ROOT/templates/$1.html | envsubst
+		cat $DOCUMENT_ROOT/templates/$1.html | envsubst
 	fi
 	echo "</html>"
 }
@@ -50,7 +63,7 @@ Cat() {
 get_lang() {
 	echo $HTTP_ACCEPT_LANGUAGE | tr ',' '\n' | tr '-' '_' | tr ';' ' ' | \
 		while read alang qlang; do \
-			if grep "$alang" $ROOT/locale/langs; then
+			if grep "$alang" $DOCUMENT_ROOT/locale/langs; then
 				break
 			fi
 		done
@@ -63,33 +76,35 @@ if [[ -z "$LANG" ]]; then
 	LANG=pt_PT
 fi
 ILANG=$LANG
-# debug
-# echo $HTTP_ACCEPT_LANGUAGE
-# echo lang=$lang
 
 _() {
+	arg="$@"
 	IFS='$'
 	TEXTDOMAIN=site
-	value="`cat $ROOT/locale/$TEXTDOMAIN-$lang.txt | sed -n "s|^$@\|||p"`"
-	[[ -z "$value" ]] && echo $@ || echo $value
+	value="`cat $DOCUMENT_ROOT/locale/$TEXTDOMAIN-$lang.txt | sed -n "s|^$arg\|||p"`"
+	[[ -z "$value" ]] && echo $arg || echo $value
 }
 
-export RB="btn round ps tsxl"
+export RB="btn round p8 ts64"
+export RBS="btn round p8 ts"
+export RBXS="btn round p4 tss"
 export SRB="btn round ps tsl"
 
 Menu() {
 	if [[ ! -z "$REMOTE_USER" ]]; then
 		USER_NAME="<span class=\"ts\">$REMOTE_USER</span>"
-		USER_ICON="<a class=\"tsxl f _ fic btn ps\" href=\"/e/user\"><span>🔑 </span><span> $USER_NAME</span></a>"
+		USER_ICON="<a class=\"tsxl f h fic btn p8\" href=\"/user\"><span role=\"img\" aria-label=\"user\">🔑 </span><span> $USER_NAME</span></a>"
 	else
-		USER_ICON="<a class=\"$RB\" href=\"/e/login\">🔑 </a>"
+		USER_ICON="<a class=\"$RB\" href=\"/login\"><span role=\"img\" aria-label=\"login\">🔑 </span></a>"
 	fi
 	export USER_ICON
-	cat $ROOT/components/menu.html | envsubst
+	cat $DOCUMENT_ROOT/components/menu.html | envsubst
 }
 
 Unauthorized() {
 	NormalHead 401
+	echo "Date: `TZ=GMT date '+%a, %d %b %Y %T %Z'`"
+	echo WWW-Authenticate: Basic realm="tty-pt"
 	export _TITLE="`_ Unauthorized`"
 	echo
 	Head
@@ -101,3 +116,25 @@ Unauthorized() {
 cookie=$HTTP_COOKIE
 cookie="`echo $cookie | tr ' ' '\n' | head -n 1`"
 cookie="`echo $cookie | awk 'BEGIN { FS = "=" } { print $2 }'`"
+
+rand_str_1() {
+	# TODO maybe remove lowercase conversion
+	xxd -l32 -ps $DOCUMENT_ROOT/dev/urandom | xxd -r -ps | openssl base64 \
+		    | tr -d = | tr + - | tr / _ | tr '[A-Z]' '[a-z]'
+}
+
+auth() {
+	username=$1
+	password=$2
+	hash="`grep "^$username:" $DOCUMENT_ROOT/.htpasswd | awk 'BEGIN{FS=":"} {print $2}'`"
+	[[ ! -z "$hash" ]] || Fatal 400 No such user
+	if crypt_checkpass "$password" "$hash"; then
+		Unauthorized
+	fi
+	[[ ! -f $ROOT/users/$username/rcode ]] || Fatal 400 The account was not activated
+
+	TOKEN="`rand_str_1`"
+	#[[ -d $ROOT/sessions ]] || mkdir $ROOT/sessions
+	echo $username > $DOCUMENT_ROOT/sessions/$TOKEN
+	HEADERS=$HEADERS"Set-Cookie: QSESSION=$TOKEN; SameSite=Lax\n"
+}
diff --git a/poem/.add b/poem/.add
new file mode 100644
index 0000000..54385d8
--- /dev/null
+++ b/poem/.add
@@ -0,0 +1,51 @@
+#!/bin/ksh
+
+[[ ! -z "$REMOTE_USER" ]] || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		poem_id="`cat $ROOT/tmp/mpfd/poem_id`"
+
+		if invalid_id $poem_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		POEM_PATH="$ROOT/poem/$poem_id"
+
+		fmkdir $POEM_PATH
+		echo $REMOTE_USER | fwrite $POEM_PATH/.owner
+
+		mpfd-ls | while read FILE_PATH filename ; do
+			file_lang="`echo $filename | sed 's/\.[^.]*$//'`"
+			if invalid_lang "$file_lang"; then
+				Fatal 400 Not a valid locale
+			fi
+			fbytes $FILE_PATH
+			cat $FILE_PATH | cslash > $POEM_PATH/$file_lang.html
+			rm $FILE_PATH
+			touch $POEM_PATH/comments.txt
+		done
+
+		if [[ ! -f $POEM_PATH/$lang.html ]]; then
+			rm -rf $POEM_PATH
+			Fatal 400 You must submit a poem in your language
+		fi
+
+		_see_other /poem/$poem_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add poem"`"
+		export _POEM_ID="`_ "Poem ID"`"
+		export _FILES="`_ Files`"
+		export _ADD_POEM_NOTE="`_ add_poem_note` $lang.html"
+		export _SUBMIT="`_ Submit`"
+
+		Normal 200 poem/add
+		Scat .template/add
+		;;
+	*) NotAllowed ;;
+esac
+
+
+
diff --git a/poem/.index b/poem/.index
new file mode 100644
index 0000000..4c86d16
--- /dev/null
+++ b/poem/.index
@@ -0,0 +1,19 @@
+#!/bin/ksh
+
+if [[ ! -z "$1" ]]; then
+	case "$1" in
+		add) shift; . ./.add $@ ; exit 0;;
+		*) . ./.sub-index $@ ; exit 0;;
+	esac
+fi
+
+[[ "$REQUEST_METHOD" == "GET" ]] || NotAllowed
+export _TITLE="`_ Poems`"
+
+if [[ ! -z "$REMOTE_USER" ]]; then
+	export POEM_ADD="<a class=\"$RB\" href=\"/poem/add\">+</p>"
+fi
+
+export POEMS="`ls_shown $ROOT/poem | BigButtons poem`"
+Normal 200 poem
+Scat .template/index
diff --git a/poem/.sub-index b/poem/.sub-index
new file mode 100644
index 0000000..c4a4b85
--- /dev/null
+++ b/poem/.sub-index
@@ -0,0 +1,43 @@
+#!/bin/ksh
+
+poem_id=$1
+POEM_PATH=$ROOT/poem/$poem_id
+COMMENTS_PATH="$POEM_PATH/comments.txt"
+
+case "$REQUEST_METHOD" in
+	POST)
+		[[ ! -z "$REMOTE_USER" ]] || Unauthorized
+		echo $REMOTE_USER: "`urldecode "$comment"`" | fappend $COMMENTS_PATH
+		_see_other $poem_id\#comments
+		;;
+	GET)
+		if [[ -z "$poem_id" ]]; then
+			Fatal 404 Poem not found
+		fi
+
+		POEM_HTML_PATH="$POEM_PATH/pt_PT.html"
+
+		if [[ ! -f "$POEM_HTML_PATH" ]] || [[ ! -d "$POEM_PATH" ]]; then
+			Fatal 404 Poem not found
+		fi
+
+		NEW_POEM_HTML_PATH="$POEM_PATH/$lang.html"
+		if [[ -f $NEW_POEM_HTML_PATH ]]; then
+			POEM_HTML_PATH=$NEW_POEM_HTML_PATH
+		fi
+
+		export poem_id
+
+		export _POEM="`cat $POEM_HTML_PATH`"
+		export _COMMENTS="`cat $COMMENTS_PATH | revlines | no_html`"
+
+		COUNTER_PATH=$POEM_PATH/counter.txt
+		export COUNTER="`counter_inc $COUNTER_PATH`"
+
+		export _TITLE="`_ "$poem_id"`"
+
+		Normal 200 poem/$poem_id
+		Scat .template/sub-index
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/poem/.template/add.html b/poem/.template/add.html
new file mode 100644
index 0000000..77a9d56
--- /dev/null
+++ b/poem/.template/add.html
@@ -0,0 +1,18 @@
+<body class="f fic v">
+	$MENU
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="add" method="POST" class="v f fic" enctype="multipart/form-data">
+		<label>
+			$_POEM_ID
+			<input required name="poem_id"></input>
+		</label>
+		<label>
+			$_FILES
+			<input required type="file" name="file[]" multiple></input>
+		</label>
+		<div>$_ADD_POEM_NOTE</div>
+		<button>$_SUBMIT</button>
+	</form>
+</body>
diff --git a/templates/poems.html b/poem/.template/index.html
similarity index 100%
rename from templates/poems.html
rename to poem/.template/index.html
diff --git a/poem/.template/sub-index.html b/poem/.template/sub-index.html
new file mode 100644
index 0000000..b7d6be1
--- /dev/null
+++ b/poem/.template/sub-index.html
@@ -0,0 +1,34 @@
+<style>
+pre,form { margin: 0 }
+pre {
+	display: inline-block;
+	white-space: pre-wrap;
+	word-wrap: break-word;
+}
+.s_k9 {
+	max-width: 512px;
+}
+</style>
+
+<body class="v">
+	<header class="h f fw v fic fcc">
+		<a href="#comments" class="$RB">💬</a>
+		$MENU
+		<div class="cf15 tsxl">$COUNTER</div>
+	</header>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<pre>
+$_POEM
+	</pre>
+
+	<form action="$poem_id" method="post" class="h f fic">
+		<input type="hidden" name="poem_id" value="$poem_id"></input>
+		<input class="fg" name="comment"></input>
+	</form>
+
+	<pre id="comments">
+$_COMMENTS
+	</pre>
+</body>
diff --git a/sem/.add b/sem/.add
new file mode 100644
index 0000000..58f396e
--- /dev/null
+++ b/sem/.add
@@ -0,0 +1,50 @@
+#!/bin/ksh
+
+ginit() {
+	mkdir $SEM_PATH/.git
+	git -C $SEM_PATH/.git init --bare
+	git --git-dir=$SEM_PATH/.git --work-tree=$SEM_PATH/. add $SEM_PATH/data.txt
+	git --git-dir=$SEM_PATH/.git --work-tree=$SEM_PATH/. commit -m "First commit"
+	cat - >> $SEM_PATH/.git/config <<!
+[http]
+	receivepack = true
+!
+	mkdir $SEM_PATH/.git/hooks
+	cp $DOCUMENT_ROOT/etc/skel/hooks/user-post-update .git/hooks/post-update
+	chmod +x $SEM_PATH/.git/hooks/post-update
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_id="`cat $ROOT/tmp/mpfd/sem_id`"
+		sem_id="`urldecode $sem_id`"
+
+		if invalid_id $sem_id; then
+			Fatal 400 Not a valid ID
+		fi
+
+		SEM_PATH="$ROOT/sem/$sem_id"
+
+		fmkdir $SEM_PATH
+		echo $REMOTE_USER | fwrite $SEM_PATH/.owner
+
+		FILE_PATH=$ROOT/tmp/mpfd/file
+		if [[ -f $FILE_PATH ]]; then
+			fbytes $FILE_PATH
+			mv $FILE_PATH $SEM_PATH/data.txt
+		fi
+		ginit >/dev/null
+
+		see_other sem ?sem_id=$sem_id
+		;;
+
+	GET)
+		export _TITLE="`_ "Add sem"`"
+		export _SEM_ID="`_ "Sem ID"`"
+		export _SUBMIT="`_ Submit`"
+
+		Normal 200 sem/add
+		Scat .template/add
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.buy b/sem/.buy
new file mode 100644
index 0000000..2429ce3
--- /dev/null
+++ b/sem/.buy
@@ -0,0 +1,32 @@
+#!/bin/ksh
+
+sem_source
+
+buy_options() {
+	if im $SEM_OWNER; then
+		$SEM -p < $SEM_FILE | awk '{ print $2 }'
+	else
+		echo $REMOTE_USER
+	fi
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op BUY `urldecode $ts` $id `urldecode $value` `urldecode $rest`
+
+		;;
+	GET)
+		export _TITLE="`_ buy`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _VALUE="`_ Value`"
+		export _MORE_INFO="`_ "More information"`"
+		export _SUBMIT="`_ Submit`"
+		export SEM_MENU="`SemMenu buy`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		export ID_OPTIONS="`buy_options | IdOptions`"
+		Normal 200 sem/$sem_id/buy
+		Scat .template/buy
+
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.diff b/sem/.diff
new file mode 100755
index 0000000..8313678
--- /dev/null
+++ b/sem/.diff
@@ -0,0 +1,4 @@
+#!/bin/ksh
+
+. $DOCUMENT_ROOT/lib/command.sh
+git -C $DOCUMENT_ROOT/sem/$sem_id diff | Command sem diff
diff --git a/sem/.download b/sem/.download
new file mode 100644
index 0000000..9f11693
--- /dev/null
+++ b/sem/.download
@@ -0,0 +1,6 @@
+#!/bin/ksh
+
+echo Status 200: Ok
+echo Content-Type: text
+echo
+cat $SEM_FILE
diff --git a/sem/.index b/sem/.index
new file mode 100755
index 0000000..c43f7b8
--- /dev/null
+++ b/sem/.index
@@ -0,0 +1,21 @@
+#!/bin/ksh
+
+. $DOCUMENT_ROOT/lib/sem.sh
+
+if [[ ! -z "$ARG" ]]; then
+	case "$ARG" in
+		add) . ./.add ; exit 0;;
+		*) . ./.sub-index $@ ; exit 0;;
+	esac
+fi
+
+[[ "$REQUEST_METHOD" == "GET" ]] || NotAllowed
+
+export _TITLE="`_ Sems`"
+if [[ ! -z "$REMOTE_USER" ]]; then
+export SEM_ADD="<a class=\"$RB\" href=\"/sem/add\">+</a>"
+fi
+
+export SEMS="`ls $DOCUMENT_ROOT/sem | BigButtons sem`"
+Normal 200 sem
+Scat .template/index
diff --git a/sem/.pause b/sem/.pause
new file mode 100644
index 0000000..1df730b
--- /dev/null
+++ b/sem/.pause
@@ -0,0 +1,27 @@
+#!/bin/ksh
+
+pause_options() {
+	if im $SEM_OWNER; then
+		$SEM -p < $SEM_FILE | grep '^P ' | awk '{ print $2 }'
+	elif [[ "$PRESENT" == "P $REMOTE_USER" ]]; then
+		echo $REMOTE_USER
+	fi
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op PAUSE `urldecode $ts` $id `urldecode $rest`
+
+		;;
+	GET)
+		export _TITLE="`_ pause`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _MORE_INFO="`_ "More information"`"
+		export SEM_MENU="`SemMenu pause`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		export ID_OPTIONS="`pause_options | IdOptions`"
+		Normal 200 pause
+		Scat .template/pause
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.pay b/sem/.pay
new file mode 100644
index 0000000..1f8dcb3
--- /dev/null
+++ b/sem/.pay
@@ -0,0 +1,82 @@
+#!/bin/ksh
+
+pay_options() {
+	if im $SEM_OWNER; then
+		$SEM -p < $SEM_FILE | sed "/^. $REMOTE_USER$/d" | awk '{print $2}'
+	fi
+	echo $REMOTE_USER y
+}
+
+payments() {
+	cat $SEM_FILE | sed -n '/^PAY /p'
+}
+
+types="`payments | awk '{print $7; print $3}' | sort -u`"
+last_payment="`payments | sed -n "/^PAY [^ ]* $REMOTE_USER .* $type/p" | tail -n 1`"
+
+pay_info() {
+	cat $SEM_FILE | sed -n '/^PAY /p' | awk '{print $7 " " $3}'
+}
+
+type_options() {
+	echo $TYPE y
+	pay_info | awk '{ print $1 }' | sort -u | sed "/^$TYPE$/d"
+}
+
+date_get() {
+	local res="`date -j -f "%Y-%m-%dT%H:%M:%S" "$1" +"%Y-%m-%d" || true`"
+	if test -z "$res"; then
+		res="`date -j -f "%Y-%m-%d" "$1" +"%Y-%m-%d"`"
+	fi
+	echo $res
+}
+
+month_inc() {
+	read date
+	local year="`echo $date | awk '{print $1}'`"
+	local month="`echo $date | awk '{print $2}'`"
+	year="`echo | bc -e "if ( $month + 1 > 12 ) $year + 1 else $year"`"
+	year="`printf "%04u" $year`"
+	month="`echo | bc -e "if ( $month + 1 > 12 ) 1 else ( $month + 1 )"`"
+	month="`printf "%02u" $month`"
+	echo $year-$month-`echo $date | awk '{print $3}'`
+}
+
+set -- $last_payment
+export VALUE=$4
+export START="`date_get "$5" | tr '-' ' '`"
+export END="`date_get "$6" | tr '-' ' '` | "
+START="`echo $START | month_inc | tr ' ' '-'`T00:00:00"
+END="`echo $END | month_inc | tr ' ' '-'`T00:00:00"
+TYPE=$7
+if ! test -z "$type"; then
+	TYPE=$type
+fi
+export ENTITY=$8
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op PAY `urldecode $ts` $id `urldecode $value` `urldecode $from` `urldecode $to` $type $entity $reference
+
+		;;
+	GET)
+		export _TITLE="`_ pay`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _VALUE="`_ Value`"
+		export _BP_START="`_ "Start of billing period"`"
+		export _BP_END="`_ "End of billing period"`"
+		export _TYPE="`_ Type`"
+		export _ENTITY="`_ Entity`"
+		export _REFERENCE="`_ Reference`"
+		export _SUBMIT="`_ Submit`"
+		export SEM_MENU="`SemMenu pay`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		export DNOW="`date -u +"%Y-%m-%d"`T00:00:00"
+		export ID_OPTIONS="`pay_options | IdOptions`"
+		export TYPE_OPTIONS="`type_options | IdOptions`"
+		Normal 200 pay
+		Scat .template/pay
+
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.resume b/sem/.resume
new file mode 100644
index 0000000..52cd3df
--- /dev/null
+++ b/sem/.resume
@@ -0,0 +1,29 @@
+#!/bin/ksh
+
+resume_options() {
+	if im $SEM_OWNER; then
+		$SEM -p < $SEM_FILE | grep '^A ' | awk '{ print $2 }'
+	elif [[ "$PRESENT" == "A $REMOTE_USER" ]]; then
+		echo $REMOTE_USER
+	fi
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op RESUME `urldecode $ts` $id `urldecode $rest`
+
+		;;
+	GET)
+		export _TITLE="`_ resume`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _MORE_INFO="`_ "More information"`"
+		export _SUBMIT="`_ Submit`"
+		export SEM_MENU="`SemMenu resume`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		export ID_OPTIONS="`resume_options | IdOptions`"
+		Normal 200 resume
+		Scat .template/resume
+
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.start b/sem/.start
new file mode 100644
index 0000000..79c8140
--- /dev/null
+++ b/sem/.start
@@ -0,0 +1,24 @@
+#!/bin/ksh
+
+im $SEM_OWNER || Forbidden
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op START `urldecode $ts` $id `urldecode $tel` `urldecode $email` `urldecode $name`
+
+		;;
+	GET)
+		export _TITLE="`_ start`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _TELEPHONE="`_ Telephone`"
+		export _EMAIL="`_ Email`"
+		export _NAME="`_ Name`"
+		export _SUBMIT="`_ Submit`"
+		export SEM_MENU="`SemMenu start`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		Normal 200 start
+		Scat .template/start
+
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.stop b/sem/.stop
new file mode 100644
index 0000000..dc3dfc1
--- /dev/null
+++ b/sem/.stop
@@ -0,0 +1,29 @@
+#!/bin/ksh
+
+stop_options() {
+	if im $SEM_OWNER; then
+		$SEM -p < $SEM_FILE | awk '{ print $2 }'
+	else
+		echo $REMOTE_USER
+	fi
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op STOP `urldecode $ts` $id `urldecode $rest`
+
+		;;
+	GET)
+		export _TITLE="`_ stop`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _MORE_INFO="`_ "More information"`"
+		export _SUBMIT="`_ Submit`"
+		export SEM_MENU="`SemMenu stop`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		export ID_OPTIONS="`stop_options | IdOptions`"
+		Normal 200 stop
+		Scat .template/stop
+
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/sem/.sub-index b/sem/.sub-index
new file mode 100755
index 0000000..feb7427
--- /dev/null
+++ b/sem/.sub-index
@@ -0,0 +1,66 @@
+#!/bin/ksh
+
+[[ ! -z "$REMOTE_USER" ]] || Unauthorized
+
+sem_source
+
+SUB_ARG="`echo $DOCUMENT_URI | awk -F '/' '{print $4}'`"
+
+case "$SUB_ARG" in
+	download) . ./.download ; exit;;
+	buy) . ./.buy ; exit;;
+	pause) . ./.pause ; exit;;
+	pay) . ./.pay ; exit;;
+	resume) . ./.resume ; exit;;
+	start) . ./.start ; exit;;
+	stop) . ./.stop ; exit;;
+	transfer) . ./.transfer ; exit;;
+	diff) . ./.diff ; exit;;
+	.git) git_backend; exit;;
+esac
+
+export _TITLE="`_ $sem_id`"
+export _SUBMIT="`_ Submit`"
+export _DOWNLOAD="`_ Download`"
+export _GRAPH="`_ Graph`"
+export _DEBUG="`_ Debug`"
+export _HUMAN="`_ Human`"
+export _MACHINE="`_ Machine`"
+export _PRESENT="`_ Present`"
+options=""
+
+export SEM_MENU="`SemMenu`"
+
+if [[ "$graph" == "on" ]]; then
+	export graph_checked=checked
+	options=$options"g"
+fi
+
+if [[ "$debug" == "on" ]]; then
+	export debug_checked=checked
+	options=$options"d"
+fi
+
+if [[ "$human" == "on" ]]; then
+	export human_checked=checked
+	options=$options"h"
+fi
+
+if [[ "$machine" == "on" ]]; then
+	export machine_checked=checked
+	options=$options"m"
+fi
+
+if [[ "$present" == "on" ]]; then
+	export present_checked=checked
+	options=$options"p"
+fi
+
+if [[ ! -z "$options" ]]; then
+	options="-$options"
+fi
+
+export SEM="`$SEM $options 2>&1 < $SEM_FILE`"
+
+Normal 200 sem/$sem_id
+Scat .template/sub-index
diff --git a/sem/.template/add.html b/sem/.template/add.html
new file mode 100644
index 0000000..aae5373
--- /dev/null
+++ b/sem/.template/add.html
@@ -0,0 +1,17 @@
+<body class="f fic v">
+	$MENU
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/sem/add" method="POST" class="v f fic" enctype="multipart/form-data">
+		<label>
+			$_SEM_ID
+			<input required name="sem_id"></input>
+		</label>
+		<label>
+			File
+			<input type="file" name="file" accept="txt"></input>
+		</label>
+		<button>$_SUBMIT</button>
+	</form>
+</body>
diff --git a/sem/.template/buy.html b/sem/.template/buy.html
new file mode 100644
index 0000000..3e4c711
--- /dev/null
+++ b/sem/.template/buy.html
@@ -0,0 +1,39 @@
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./buy" method="POST" class="v f">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<select required name="id">
+				$ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_VALUE
+			<input required type="number" name="value" placeholder="33.33" step="0.01"></input>
+		</label>
+
+		<label>
+			$_MORE_INFO
+			<textarea name="rest"></textarea>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
diff --git a/sem/.template/index.html b/sem/.template/index.html
new file mode 100644
index 0000000..9378c98
--- /dev/null
+++ b/sem/.template/index.html
@@ -0,0 +1,6 @@
+<body class="v f fic">
+	$MENU
+	<h1 class="tac">$_TITLE</h1>
+	$SEMS
+	$SEM_ADD
+</body>
diff --git a/sem/.template/pause.html b/sem/.template/pause.html
new file mode 100644
index 0000000..a9e844a
--- /dev/null
+++ b/sem/.template/pause.html
@@ -0,0 +1,38 @@
+<style>
+pre { display: inline-block; }
+</style>
+
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./pause" method="POST" class="v f">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<select required name="id">
+				$ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_MORE_INFO
+			<textarea name="rest"></textarea>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>Submit</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
diff --git a/sem/.template/pay.html b/sem/.template/pay.html
new file mode 100644
index 0000000..b664930
--- /dev/null
+++ b/sem/.template/pay.html
@@ -0,0 +1,88 @@
+<script src="/qr-scanner.umd.min.js"></script>
+<!-- <script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script> -->
+
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./pay" method="POST" class="v f fic">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<select required name="id">
+				$ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_VALUE
+			<input required type="number" name="value" placeholder="33.33" step="0.01" value="$VALUE"></input>
+		</label>
+
+		<label>
+			$_BP_START
+			<input required name="from" type="datetime-local" value="$START" step="1"></input>
+		</label>
+
+		<label>
+			$_BP_END
+			<input required name="to" type="datetime-local" value="$END" step="1"></input>
+		</label>
+
+		<label>
+			$_TYPE
+			<select name="type">
+				$TYPE_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_ENTITY
+			<input required name="entity" pattern="[0-9]{5}" value="$ENTITY"></input>
+		</label>
+
+
+		<label>
+			$_REFERENCE
+			<input required name="reference" pattern="[0-9]{9}"></input>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>$_SUBMIT</button>
+
+		<!-- <div id="reader" class="s_f"></div> -->
+		<video class="s_f"></video>
+	</form>
+</body>
+
+<script>
+$JS
+
+const video = document.querySelector("video");
+const entity = document.querySelector("input[name=entity]");
+
+function fill(data) {
+	alert(data.split("*").join("\n"));
+}
+
+const qrScanner = new QrScanner(
+	video,
+	result => fill(
+		result.data,
+	),
+	{
+		highlightScanRegion: true,
+		returnDetailedScanResult: true,
+	}
+);
+// qrScanner.start();
+console.log(entity);
+</script>
diff --git a/sem/.template/resume.html b/sem/.template/resume.html
new file mode 100644
index 0000000..d8c83ba
--- /dev/null
+++ b/sem/.template/resume.html
@@ -0,0 +1,35 @@
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./resume" method="POST" class="v f">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<select required name="id">
+				$ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_MORE_INFO
+			<textarea name="rest"></textarea>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
+
diff --git a/sem/.template/start.html b/sem/.template/start.html
new file mode 100644
index 0000000..3a4d2d1
--- /dev/null
+++ b/sem/.template/start.html
@@ -0,0 +1,42 @@
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./start" method="POST" class="v f">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<input required name="id"></input>
+		</label>
+
+		<label>
+			$_TELEPHONE
+			<input required type="tel" name="tel" pattern="\+[0-9]{12}"></input>
+		</label>
+
+		<label>
+			$_EMAIL
+			<input required type="email" name="email"></input>
+		</label>
+
+		<label>
+			$_NAME
+			<input required name="name"></input>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
diff --git a/sem/.template/stop.html b/sem/.template/stop.html
new file mode 100644
index 0000000..55da3c3
--- /dev/null
+++ b/sem/.template/stop.html
@@ -0,0 +1,34 @@
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./stop" method="POST" class="v f">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<select required name="id">
+				$ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_MORE_INFO
+			<input name="rest"></input>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
diff --git a/sem/.template/sub-index.html b/sem/.template/sub-index.html
new file mode 100644
index 0000000..9294d99
--- /dev/null
+++ b/sem/.template/sub-index.html
@@ -0,0 +1,54 @@
+<style>
+pre { display: inline-block; }
+</style>
+
+<body class="v f fic">
+	<header class="h f fic">
+		$SEM_MENU
+		$MENU
+	</header>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<pre>$SEM</pre>
+
+	<form action="./$sem_id" method="GET" class="v tac">
+		<div class="fw f h">
+			<label class="h">
+				<input type="checkbox" name="graph" $graph_checked></input>
+				$_GRAPH
+			</label>
+
+			<label class="h">
+				<input type="checkbox" name="debug" $debug_checked></input>
+				$_DEBUG
+			</label>
+
+			<label class="h">
+				<input type="checkbox" name="human" $human_checked></input>
+				$_HUMAN
+			</label>
+
+			<label class="h">
+				<input type="checkbox" name="machine" $machine_checked></input>
+				$_MACHINE
+			</label>
+
+			<label class="h">
+				<input type="checkbox" name="present" $present_checked></input>
+				$_PRESENT
+			</label>
+		</div>
+		<span class="h f fcc">
+			<button>$_SUBMIT</button>
+			<a class="btn" href="./$sem_id/download">$_DOWNLOAD</a>
+		</span>
+	</form>
+	<div class="tac">
+		git clone https://tty.pt/sem/$sem_id/.git
+	</div>
+</body>
+
+<script>
+$JS
+</script>
diff --git a/sem/.template/transfer.html b/sem/.template/transfer.html
new file mode 100644
index 0000000..c7d0bf4
--- /dev/null
+++ b/sem/.template/transfer.html
@@ -0,0 +1,46 @@
+<body class="v f fic rel">
+	<header class="h f fic">
+		$SEM_MENU
+		<a class="$RB" href="/sem/$sem_id">👁</a>
+		$MENU
+	</header>
+	<h1 class="tac ttc">$_TITLE</h1>
+	<form action="./transfer" method="POST" class="v f">
+		<label>
+			$_DATE_TIME
+			<input required name="ts" type="datetime-local" value="$NOW" step="1"></input>
+		</label>
+
+		<label>
+			Id
+			<select required name="id">
+				$FROM_ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_TO
+			<select required name="to">
+				$TO_ID_OPTIONS
+			</select>
+		</label>
+
+		<label>
+			$_VALUE
+			<input required type="number" name="value" placeholder="33.33" step="0.01"></input>
+		</label>
+
+		<label>
+			$_MORE_INFO
+			<textarea name="rest"></textarea>
+		</label>
+
+		<input type="hidden" name="sem_id" value="$sem_id"></input>
+
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
diff --git a/sem/.transfer b/sem/.transfer
new file mode 100644
index 0000000..4f91855
--- /dev/null
+++ b/sem/.transfer
@@ -0,0 +1,36 @@
+#!/bin/ksh
+
+transfer_from_options() {
+	if im $SEM_OWNER; then
+		$SEM -p < $SEM_FILE | awk '{ print $2 }'
+	else
+		echo $REMOTE_USER
+	fi
+}
+
+transfer_to_options() {
+	$SEM -p < $SEM_FILE | awk '{ print $2 }'
+}
+
+case "$REQUEST_METHOD" in
+	POST)
+		sem_op TRANSFER `urldecode $ts` $id `urldecode $to` `urldecode $value` `urldecode $rest`
+
+		;;
+	GET)
+		export _TITLE="`_ transfer`"
+		export _DATE_TIME="`_ "Date / Time"`"
+		export _TO="`_ To`"
+		export _VALUE="`_ Value`"
+		export _MORE_INFO="`_ "More information"`"
+		export _SUBMIT="`_ Submit`"
+		export SEM_MENU="`SemMenu transfer`"
+		export NOW="`date -u +"%Y-%m-%dT%H:%M:%S"`"
+		export FROM_ID_OPTIONS="`transfer_from_options | IdOptions`"
+		export TO_ID_OPTIONS="`transfer_to_options | IdOptions`"
+		Normal 200 sem/$sem_id/transfer
+		Scat .template/transfer
+
+		;;
+	*) NotAllowed ;;
+esac
diff --git a/shop/.add b/shop/.add
new file mode 100644
index 0000000..a6ba658
--- /dev/null
+++ b/shop/.add
@@ -0,0 +1,25 @@
+#!/bin/ksh
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)	export _TITLE="`_ "Add shop"`"
+		export _SHOP_ID="`_ "Shop ID"`"
+		export _SUBMIT="`_ Submit`"
+
+		Normal 200 shop/add
+		Scat .template/add
+		;;
+	*) NotAllowed ;;
+esac
+
+shop_id="`urldecode $shop_id`"
+
+invalid_id $shop_id && Fatal 400 Not a valid ID || true
+
+SHOP_PATH="$ROOT/shop/$shop_id"
+
+fmkdir $SHOP_PATH
+echo $REMOTE_USER | fwrite $SHOP_PATH/.owner
+fmkdir $SHOP_PATH/.orders
+
+_see_other /shop/$shop_id
diff --git a/shop/.cart b/shop/.cart
new file mode 100644
index 0000000..78a7323
--- /dev/null
+++ b/shop/.cart
@@ -0,0 +1,88 @@
+#!/bin/ksh
+
+case "$1" in
+	edit) shift; . ./.cart-edit ; exit 0 ;;
+esac
+
+
+EmptyContents() {
+	_EMPTY="`_ "Empty"`"
+	echo "<div class=\"tsxl tac\">$_EMPTY</div>"
+}
+
+Products() {
+	local product_id
+	while read product_id quantity; do
+		product_env $product_id
+		cat <<!
+<a class="card f v8 b0 fic p" href="/shop/$shop_id/$product_id">
+	`PImage $product_image_path`
+	<div class="tsxl">$product_title</div>
+	<div class="tsl">$quantity x $product_price€</div>
+	`echo "$product_description" | csurround p`
+</a>
+!
+	done
+}
+
+Contents() {
+	TOTAL_CART_EXP="`process_cart $CART_PATH`"
+	TOTAL="`echo "$TOTAL_CART_EXP" | bc -l`"
+	_SUBMIT="`_ Submit`"
+	PRODUCTS="`cat $CART_PATH | Products | fw`"
+
+	cat <<!
+$PRODUCTS
+
+<div class="tcv fic v">
+	<div class="tsxl">$TOTAL€</div>
+	<form action="/shop/$shop_id/order" method="POST">
+		<button>$_SUBMIT</Button>
+	</form>
+</div>
+!
+}
+
+if [[ -z "$shop_id" ]] || [[ ! -d $SHOP_PATH ]]; then
+	Fatal 404 Shop not found
+fi
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		if [[ -f "$CART_PATH" ]] && [[ ! -z `cat $CART_PATH` ]]; then
+			CONTENTS="`Contents`"
+			export EDIT_BTN="`EditBtn "/shop/$shop_id/cart/edit"`"
+
+		else
+			CONTENTS="`EmptyContents`"
+		fi
+		export CONTENTS
+
+		export _TITLE="`_ $shop_id` - `_ Cart`"
+
+		Normal 200 shop/$shop_id/cart
+		Scat .template/cart
+		;;
+	*) NotAllowed ;;
+esac
+
+local PRODUCT_PATH=$SHOP_PATH/$product_id
+
+if [[ -z "$product_id" ]] || [[ ! -d "$PRODUCT_PATH" ]]; then
+	Fatal 404 Product not found
+fi
+
+product_env $product_id # TODO think twice about this use case
+
+if [[ -f $CART_PATH ]] && cat $CART_PATH | grep -q $product_id; then
+	# only do something if this is not the case,
+	# otherwise just redir
+	echo -n ""
+elif [[ "$product_stock" != 0 ]]; then
+	echo $product_id 1 | fappend $CART_PATH
+else
+	Fatal 400 Not enough stock
+fi
+
+_see_other /shop/$shop_id/cart/edit
diff --git a/shop/.cart-edit b/shop/.cart-edit
new file mode 100644
index 0000000..871c653
--- /dev/null
+++ b/shop/.cart-edit
@@ -0,0 +1,64 @@
+#!/bin/ksh
+
+Products() {
+	local product_id
+	while read product_id quantity; do
+		product_env $product_id
+		cat <<!
+<div class="card f v8 b0 fic p">
+	`PImage $product_image_path`
+	<a class="tsxl" href="/shop/$shop_id/$product_id">$product_title</a>
+	<div class="f _s fic">
+		<input name="quantity_$product_id" type="number" min="0" max="$product_stock" value="$quantity" class="s_4_5"></input>
+		<div class="tsl">x $product_price€</div>
+	</div>
+	`echo "$product_description" | csurround p`
+</div>
+!
+	done
+}
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		if [[ ! -f "$CART_PATH" ]] || [[ -z "`cat $CART_PATH`" ]]; then
+			Fatal 400 You can not edit an empty cart
+		fi
+
+		export PRODUCTS="`cat $CART_PATH | Products | fw`"
+
+		export _TITLE="`_ $shop_id` - `_ "Edit cart"`"
+		export _SUBMIT="`_ Submit`"
+
+		# TODO im $CART_OWNER $SHOP_OWNER || Forbidden
+
+		Normal 200 shop/$shop_id/cart/edit
+		Scat .template/cart-edit
+		;;
+	*) NotAllowed ;;
+esac
+
+cat $CART_PATH | while read product_id quantity; do
+	local qname=quantity_$product_id
+	eval echo $product_id \$$qname
+done | cat $CART_PATH - | sort -s -k1,1 | while read product_id old_quantity; do
+	read ignore quantity
+
+	product_env $product_id
+
+	local diff_exp="($quantity - $old_quantity)"
+	local new_stock_exp="$product_stock - $diff_exp"
+	local available_exp="$new_stock_exp >= 0"
+	local available="`echo $available_exp | bc`"
+
+	[[ "$available" != "0" ]] || Fatal 400 Not enough stock
+
+	# this should only happen on order placement
+	# local new_stock="`echo "$new_stock_exp" | bc`"
+	# echo $new_stock | fwrite $product_path/stock
+
+	# echo $product_id $quantity
+	[[ "$quantity" == "0" ]] || echo $product_id $quantity
+done > $CART_PATH
+
+_see_other /shop/$shop_id/cart
diff --git a/shop/.edit b/shop/.edit
new file mode 100644
index 0000000..5b7c51a
--- /dev/null
+++ b/shop/.edit
@@ -0,0 +1,58 @@
+#!/bin/ksh
+
+lsshown() {
+	 find $1 -type d -mindepth 1 -maxdepth 1 -name "[!.]*" | sed "s|$1||" | sed "s/\/$//"
+}
+
+Products() {
+	local product_id
+	while read product_id; do
+		if [[ "`cat $SHOP_PATH/$product_id/stock`" -gt 0 ]]; then
+			product_env $product_id
+			cat <<!
+<label class="card f v8 b0 fic p">
+	`PImage $product_image_path`
+	<div class="tsl">$product_price€</div>
+	<a class="tsxl" href="/shop/$shop_id/$product_id">$product_title</a>
+	`echo "$product_description" | csurround p`
+	<input name="delete_$product_id" type="checkbox"></input>
+</label>
+!
+		fi
+	done
+}
+
+if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
+	Fatal 404 Shop not found
+fi
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		export _TITLE="`_ $shop_id` - `_ "Edit shop"`"
+		export _DELETE_PRODUCTS="`_ "Delete products"`"
+
+		export PRODUCTS="`lsshown $SHOP_PATH/ | Products | fw`"
+		SHOP_OWNER="`cat $SHOP_PATH/.owner`"
+		export SHOP_BUTTONS="`ShopButtons`"
+		if im $SHOP_OWNER; then
+			export ADD_PRODUCT_BUTTON="<div class=\"tar\"><a class=\"tsxl round p8 btn\" href=\"/e/product-add?shop_id=$shop_id\">+</a></div>"
+			export EDIT_BTN="`EditBtn "/shop/$shop_id/edit"`"
+		fi
+
+		Normal 200 shop/$shop_id/edit
+		Scat .template/edit
+		;;
+	*) NotAllowed ;;
+esac
+
+im $SHOP_OWNER || Forbidden
+
+lsshown $SHOP_PATH/ | while read product_id; do
+qname=delete_$product_id
+eval echo $product_id \$$qname
+done | while read product_id state; do
+	[[ "$state" != "on" ]] || product_rm $product_id
+done
+
+_see_other .
diff --git a/shop/.index b/shop/.index
new file mode 100755
index 0000000..ad8924f
--- /dev/null
+++ b/shop/.index
@@ -0,0 +1,22 @@
+#!/bin/ksh
+
+. $DOCUMENT_ROOT/lib/shop.sh
+
+if [[ ! -z "$1" ]]; then
+	case "$1" in
+		add) shift; . ./.add $@ ; exit;;
+		*) shift; . ./.sub-index $@ ; exit;;
+	esac
+fi
+
+[[ "$REQUEST_METHOD" == "GET" ]] || NotAllowed
+
+export _TITLE="`_ Shops`"
+
+if [[ ! -z "$REMOTE_USER" ]]; then
+	export SHOP_ADD="<a class=\"$RB\" href=\"/shop/add\">+</a>"
+fi
+
+export SHOPS="`ls $DOCUMENT_ROOT/shop | BigButtons shop`"
+Normal 200 shop
+Scat .template/index
diff --git a/shop/.order/index b/shop/.order/index
new file mode 100644
index 0000000..7c170c1
--- /dev/null
+++ b/shop/.order/index
@@ -0,0 +1,96 @@
+#!/bin/ksh
+
+. $ROOT/lib/order.sh
+
+if [[ ! -z "$1" ]]; then
+	. ./sub-index $@
+	exit 0
+fi
+
+Order() {
+	ORDER_PATH=$SHOP_PATH/.orders/$1
+	ORDER_OWNER="`cat $ORDER_PATH/owner`"
+	TOTAL_EXP="`process_cart $ORDER_PATH/raw`"
+	TOTAL="`echo "$TOTAL_EXP" | bc -l`"
+	ORDER_STATE_TEXT="`cat $ORDER_PATH/state`"
+	ORDER_STATE="`OrderState orders "$ORDER_STATE_TEXT" $1`"
+
+	cat <<!
+<a href="/shop/$shop_id/order/$1" class="b0 p v">
+	<span>
+		$_ORDER #$1 - $ORDER_OWNER $TOTAL€
+	</span>
+
+	$ORDER_STATE
+</a>
+!
+}
+
+VendorOrders() {
+	ls $SHOP_PATH/.orders | while read line; do
+		Order $line
+	done
+}
+
+UserOrders() {
+	ls $SHOP_PATH/.orders | while read line; do
+		ORDER_PATH=$SHOP_PATH/.orders/$line
+		ORDER_OWNER="`cat $ORDER_PATH/owner`"
+		im $ORDER_OWNER && Order $line || true
+	done
+}
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		ORDERS_PATH=$SHOP_PATH/.orders
+
+		export _TITLE="`_ $shop_id` - `_ Orders`"
+
+		_ORDER="`_ Order`"
+
+		if im $SHOP_OWNER; then
+			ORDERS="`VendorOrders`"
+		else
+			ORDERS="`UserOrders`"
+		fi
+
+		export ORDERS="`echo "$ORDERS" | fw`"
+
+		Normal 200 shop/$shop_id/order
+		Scat template/index
+		;;
+	*) NotAllowed ;;
+esac
+
+if [[ -z "`zcat $CART_PATH`" ]]; then
+	Fatal 404 Cart not found
+fi
+
+if [[ -z "`zcat $USER_PATH/address_line_1`" ]] \
+	|| [[ -z "`zcat $USER_PATH/address_line_2`" ]] \
+	|| [[ -z "`zcat $USER_PATH/zip`" ]]; then
+			Fatal 400 Invalid shipping address 
+fi
+
+ORDERS_PATH=$SHOP_PATH/.orders
+ORDER_ID_PATH=$SHOP_PATH/.orders/.count
+ORDER_ID="`counter_inc $ORDER_ID_PATH`"
+ORDER_PATH=$SHOP_PATH/.orders/$ORDER_ID
+
+fmkdir $ORDERS_PATH
+fmkdir $ORDER_PATH
+cat $CART_PATH | fwrite $ORDER_PATH/raw
+echo $REMOTE_USER | fwrite $ORDER_PATH/owner
+echo Pending_payment | fwrite $ORDER_PATH/state
+cat $USER_PATH/address_line_1 | fwrite $ORDER_PATH/address_line_1
+cat $USER_PATH/address_line_2 | fwrite $ORDER_PATH/address_line_2
+cat $USER_PATH/zip | fwrite $ORDER_PATH/zip
+
+cat $ORDER_PATH/raw | while read product_id quantity; do
+	counter_dec $SHOP_PATH/$product_id/stock $quantity
+done >/tmp/null
+
+rm $CART_PATH # TODO also remove unneeded directories?
+
+_see_other order/$ORDER_ID
diff --git a/shop/.order/sub-index b/shop/.order/sub-index
new file mode 100644
index 0000000..302e188
--- /dev/null
+++ b/shop/.order/sub-index
@@ -0,0 +1,150 @@
+#!/bin/ksh
+
+order_id=$1
+
+AccountInfo() {
+	ACCOUNT_IBAN="`zcat $ROOT/users/$SHOP_OWNER/iban`"
+
+	if [[ -z "$ACCOUNT_IBAN" ]]; then
+		return
+	fi
+
+	cat <<!
+<div>
+	<div>`_ "Account IBAN"`</div>
+	<div>$ACCOUNT_IBAN</div>
+</div>
+!
+}
+
+MBWayInfo() {
+	PHONE_NUMBER="`zcat $ROOT/users/$SHOP_OWNER/phone_number`"
+
+	if [[ -z $PHONE_NUMBER ]]; then
+		return
+	fi
+
+	cat <<!
+<div>
+	<span>`_ "Phone number"` (MBWay): </span>
+	<a href="tel:$PHONE_NUMBER">$PHONE_NUMBER</a>
+</div>
+!
+}
+
+TransferData() {
+	OWNER_PATH=$ROOT/users/$SHOP_OWNER
+	OWNER_EMAIL="`cat $OWNER_PATH/email`"
+	cat <<!
+<div>`_ "Please refer to the order number in the credit description."` (#$order_id)</div>
+<div>`_ "You can send an e-mail to"` <a href="mailto:$OWNER_EMAIL">$OWNER_EMAIL</a> `_ "to speed up shipment."`</div>
+`AccountInfo`
+`MBWayInfo`
+!
+}
+
+AddressInfo() {
+	if [[ "$ORDER_STATE_TEXT" != "Pending_shipment" ]] \
+		|| [[ "$REMOTE_USER" != "$SHOP_OWNER" ]]; then
+		return;
+	fi
+
+	cat <<!
+<pre>
+`cat $ORDER_PATH/address_line_1`
+`cat $ORDER_PATH/address_line_2`
+`cat $ORDER_PATH/zip`
+</pre>
+
+!
+}
+
+Products() {
+	local product_id
+	while read product_id quantity; do
+		product_env $product_id
+		cat <<!
+<a class="card f v8 b0 fic p" href="/shop/$shop_id/$product_id">
+	`PImage $product_image_path`
+	<div class="tsxl">$product_title</div>
+	`echo "$product_description" | csurround p`
+	<div class="tsl">$quantity x $product_price€</div>
+</a>
+!
+	done
+}
+
+if [[ -z "$shop_id" ]] || [[ ! -d "$SHOP_PATH" ]]; then
+	Fatal 404 Shop not found
+fi
+
+DF_USER=$SHOP_OWNER
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		ORDER_PATH=$SHOP_PATH/.orders/$order_id
+		if [[ -z "$order_id" ]] || [[ ! -d "$ORDER_PATH" ]]; then
+			Fatal 404 Order not found
+		fi
+
+		ORDER_OWNER="`cat $ORDER_PATH/owner`"
+
+		im $ORDER_OWNER $SHOP_OWNER || Forbidden
+
+		export order_id
+
+		export _TITLE="`_ $shop_id` - `_ Order` #$order_id"
+
+		TOTAL_EXP="`process_cart $ORDER_PATH/raw`"
+		export TOTAL="`echo "$TOTAL_EXP" | bc -l`"
+		export PRODUCTS="`cat $ORDER_PATH/raw | Products`"
+		ORDER_STATE_TEXT="`cat $ORDER_PATH/state`"
+		export ADDRESS_INFO="`AddressInfo`"
+		export ORDER_STATE="`OrderState order "$ORDER_STATE_TEXT" $order_id`"
+		if im $ORDER_OWNER; then
+			case "$ORDER_STATE_TEXT" in
+				Pending_payment)
+					TRANSFER_DATA=`TransferData`
+					export TRANSFER_DATA
+					;;
+			esac
+		fi
+		Normal 200 order/$order_id
+		Scat template/sub-index
+		;;
+	*) NotAllowed ;;
+esac
+
+ORDER_PATH=$SHOP_PATH/.orders/$order_id
+
+im $SHOP_OWNER || Forbidden
+
+ORDER_STATE_TEXT="`cat $ORDER_PATH/state`"
+case "$ORDER_STATE_TEXT" in
+	Pending_payment)
+		ORDER_STATE_TEXT=Pending_shipment
+		;;
+	Pending_shipment)
+		ORDER_STATE_TEXT=Shipped
+		;;
+	Shipped)
+		ORDER_STATE_TEXT=Delivered
+		;;
+	Delivered)
+		rm -rf $ORDER_PATH
+		_see_other /shop/$shop_id/order
+		exit
+		;;
+esac
+
+echo $ORDER_STATE_TEXT | fwrite $ORDER_PATH/state
+
+case "$return" in
+	order)
+		_see_other /shop/$shop_id/order/$order_id
+		;;
+	orders)
+		_see_other /shop/$shop_id/order
+		;;
+esac
diff --git a/templates/orders.html b/shop/.order/template/index.html
similarity index 61%
rename from templates/orders.html
rename to shop/.order/template/index.html
index 43bea7a..6c7fb17 100644
--- a/templates/orders.html
+++ b/shop/.order/template/index.html
@@ -2,8 +2,8 @@
 pre { display: inline-block; }
 </style>
 <body class="v f fic">
-	<header class="_ f fic">
-		<a class="$RB" href="/e/shop?shop_id=$shop_id">💰</a>
+	<header class="h f fic">
+		<a class="$RB" href="/shop/$shop_id">💰</a>
 		$MENU
 	</header>
 	<h1 class="tac">$_TITLE</h1>
diff --git a/templates/order.html b/shop/.order/template/sub-index.html
similarity index 72%
rename from templates/order.html
rename to shop/.order/template/sub-index.html
index aa6672a..f514767 100644
--- a/templates/order.html
+++ b/shop/.order/template/sub-index.html
@@ -2,8 +2,8 @@
 pre { display: inline-block; }
 </style>
 <body class="v f fic">
-	<header class="_ f fic">
-		<a class="$RB" href="/e/shop?shop_id=$shop_id">💰</a>
+	<header class="h f fic">
+		<a class="$RB" href="/shop/$shop_id">💰</a>
 		$MENU
 	</header>
 	<h1 class="tac">$_TITLE</h1>
@@ -13,3 +13,4 @@ pre { display: inline-block; }
 	$TRANSFER_DATA
 	$ADDRESS_INFO
 </body>
+
diff --git a/shop/.product/add b/shop/.product/add
new file mode 100644
index 0000000..5c599e0
--- /dev/null
+++ b/shop/.product/add
@@ -0,0 +1,47 @@
+#!/bin/ksh
+
+im $SHOP_OWNER || Forbidden
+
+js() {
+	cat <<!
+`cat $ROOT/js/nfiles.js`
+
+nfiles( 'images',
+	document.getElementById('images'),
+	document.getElementById('submit'),
+	'`_ Submit`', 'file[]', '/e/image-add', '/e/images-edit');
+!
+}
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		export _TITLE="`_ $shop_id` - `_ "Add product"`"
+		export __TITLE="`_ Title`"
+		export _DESCRIPTION="`_ Description`"
+		export _IMAGES="`_ Images`"
+		export _STOCK="`_ Stock`"
+		export _PRICE="`_ Price`"
+		export _SUBMIT="`_ Submit`"
+		export JS="`js`"
+
+		Normal 200 shop/$shop_id/add
+		Scat template/add
+		;;
+	*) NotAllowed ;;
+esac
+
+PRODUCT_ID_PATH=$SHOP_PATH/.count
+PRODUCT_ID="`counter_inc $PRODUCT_ID_PATH`"
+PRODUCT_PATH=$SHOP_PATH/$PRODUCT_ID
+DF_USER=$SHOP_OWNER
+
+fmkdir $PRODUCT_PATH
+urldecode $title | fwrite $PRODUCT_PATH/title
+urldecode $description | fwrite $PRODUCT_PATH/description
+images="`nfiles $images`"
+[[ -z "$images" ]] || a2l $images | fwrite $PRODUCT_PATH/images
+echo $price | fwrite $PRODUCT_PATH/price
+echo $stock | fwrite $PRODUCT_PATH/stock
+
+_see_other /shop/$shop_id
diff --git a/shop/.product/delete b/shop/.product/delete
new file mode 100644
index 0000000..9164468
--- /dev/null
+++ b/shop/.product/delete
@@ -0,0 +1,8 @@
+#!/bin/ksh
+[[ "$REQUEST_METHOD" == "POST" ]] || NotAllowed
+
+local product_path=$SHOP_PATH/$product_id
+SHOP_OWNER="`cat $SHOP_PATH/.owner`"
+im $SHOP_OWNER || Forbidden
+product_rm $product_id
+_see_other /shop/$shop_id
diff --git a/shop/.product/edit b/shop/.product/edit
new file mode 100644
index 0000000..7f6b8db
--- /dev/null
+++ b/shop/.product/edit
@@ -0,0 +1,56 @@
+#!/bin/ksh
+
+im $SHOP_OWNER || Forbidden
+
+js() {
+	cat <<!
+`cat $ROOT/js/nfiles.js`
+
+nfiles( 'images',
+	document.getElementById('images'),
+	document.getElementById('submit'),
+	'`_ Submit`', 'file[]', '/e/image-add', '/e/images-edit');
+!
+}
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		export _TITLE="`_ $shop_id` - `_ "Edit product"` #$product_id"
+		export __TITLE="`_ Title`"
+		export _DESCRIPTION="`_ Description`"
+		export _IMAGES="`_ Images`"
+		export _ADD_TO_STOCK="`_ "Add to stock"`"
+		export _PRICE="`_ Price`"
+		export _SUBMIT="`_ Submit`"
+		export JS="`js`"
+
+		product_env $product_id
+		export product_id
+		export product_stock
+		export product_title
+		export product_description
+		export product_images
+		export product_price
+
+		Normal 200 shop/$shop_id/$product_id/edit
+		Scat template/edit
+		;;
+	*) NotAllowed ;;
+esac
+
+PRODUCT_PATH=$SHOP_PATH/$product_id
+DF_USER=$SHOP_OWNER
+
+fmkdir $PRODUCT_PATH
+urldecode $title | fwrite $PRODUCT_PATH/title
+urldecode $description | fwrite $PRODUCT_PATH/description
+images="`nfiles $images`"
+[[ -z "$images" ]] || a2l $images | fwrite $PRODUCT_PATH/images
+echo $price | fwrite $PRODUCT_PATH/price
+
+local stock_exp="`cat $PRODUCT_PATH/stock` + $stock"
+local new_stock="`echo $stock_exp | bc`"
+echo $new_stock | fwrite $PRODUCT_PATH/stock
+
+_see_other /shop/$shop_id/$product_id
diff --git a/shop/.product/index b/shop/.product/index
new file mode 100644
index 0000000..2a49b68
--- /dev/null
+++ b/shop/.product/index
@@ -0,0 +1,7 @@
+#!/bin/ksh
+
+case "$1" in
+	add) shift; . ./add ; exit 0 ;;
+esac
+
+. ./sub-index
diff --git a/shop/.product/sub-index b/shop/.product/sub-index
new file mode 100644
index 0000000..e828177
--- /dev/null
+++ b/shop/.product/sub-index
@@ -0,0 +1,67 @@
+#!/bin/ksh
+
+product_id=$1
+PRODUCT_PATH=$SHOP_PATH/$product_id
+
+if [[ -z "$product_id" ]] || [[ ! -d "$PRODUCT_PATH" ]]; then
+	Fatal 404 Product not found
+fi
+
+shift
+case "$1" in
+	delete) shift; . ./delete ; exit 0 ;;
+	edit) shift; . ./edit ; exit 0 ;;
+esac
+
+ProductImages() {
+	while read product_image_path; do
+		PImage $product_image_path | surround a "href=\"$product_image_path\""
+	done
+}
+
+CartAdd() {
+	if [[ $product_stock -le 0 ]]; then
+		return
+	fi
+
+	cat <<!
+<form action="/shop/$shop_id/cart" method="POST">
+	<button class="rs ps">`_ "Add to cart"`</button>	
+	<input type="hidden" name="shop_id" value="$shop_id"></input>
+	<input type="hidden" name="product_id" value="$product_id"></input>
+</form>
+!
+}
+
+ProductDelete() {
+	cat <<!
+<form action="/shop/$shop_id/$product_id/delete" method="POST">
+	<input type="hidden" name="shop_id" value="$shop_id"></input>
+	<input type="hidden" name="product_id" value="$product_id"></input>
+	<button class="rs ps c1">`_ "Delete product"`</button>
+</form>
+!
+}
+
+[[ "$REQUEST_METHOD" == "GET" ]] || NotAllowed
+
+export _TITLE="`_ $shop_id` - `_ Product` #$product_id"
+
+product_env $product_id
+export product_title
+export product_price
+export product_stock
+export PRODUCT_IMAGES="`echo "$product_images" | ProductImages | fw 8`"
+export _STOCK="`_ Stock`"
+
+if [[ "$product_stock" != "0" ]]; then
+	export ADD_TO_CART="`CartAdd $CART_PATH`"
+fi
+
+if im $SHOP_OWNER; then
+	export DELETE_PRODUCT="`ProductDelete $product_id`"
+	export EDIT_BTN="`EditBtn "/shop/$shop_id/$product_id/edit"`"
+fi
+
+Normal 200 shop/$shop_id/$product_id
+Scat template/index
diff --git a/templates/product-add.html b/shop/.product/template/add.html
similarity index 76%
rename from templates/product-add.html
rename to shop/.product/template/add.html
index c280ac1..9950c48 100644
--- a/templates/product-add.html
+++ b/shop/.product/template/add.html
@@ -2,14 +2,14 @@
 pre { display: inline-block; }
 </style>
 <body class="f fic v">
-	<div class="f _">
-		<a class="$RB" href="/e/shop?shop_id=$shop_id">💰</a>
+	<div class="f h">
+		<a class="$RB" href="/shop/$shop_id">💰</a>
 		$MENU
 	</div>
 
 	<h1 class="tac">$_TITLE</h1>
 
-	<form action="/e/product-add" method="POST" class="v f fic">
+	<form action="add" method="POST" class="v f fic">
 		<input type="hidden" name="shop_id" value="$shop_id" />
 		<label>
 			$__TITLE
@@ -21,7 +21,7 @@ pre { display: inline-block; }
 		</label>
 		<label>
 			$_IMAGES
-			<div class="_ f fic">
+			<div id="images" class="h f fic">
 				<textarea type="url" name="images"></textarea>
 				<a class="$RB" href="/e/images" target="_blank">🔍</a>
 			</div>
@@ -34,6 +34,10 @@ pre { display: inline-block; }
 			$_PRICE
 			<input required type="number" name="price" min="0" step="0.01"></input>
 		</label>
-		<button>$_SUBMIT</button>
+		<button id="submit">$_SUBMIT</button>
 	</form>
 </body>
+
+<script>
+$JS
+</script>
diff --git a/shop/.product/template/edit.html b/shop/.product/template/edit.html
new file mode 100644
index 0000000..731072d
--- /dev/null
+++ b/shop/.product/template/edit.html
@@ -0,0 +1,46 @@
+<style>
+pre { display: inline-block; }
+</style>
+<body class="f fic v">
+	<div class="f h">
+		<a class="$RB" href="/shop/$shop_id">💰</a>
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="edit" method="POST" class="v f fic">
+		<input type="hidden" name="shop_id" value="$shop_id" />
+		<input type="hidden" name="product_id" value="$product_id" />
+		<label>
+			$__TITLE
+			<input required name="title" value="$product_title"></input>
+		</label>
+		<label>
+			$_DESCRIPTION
+			<textarea name="description">$product_description</textarea>
+		</label>
+		<label>
+			$_IMAGES
+			<div id="images" class="h f fic">
+				<textarea type="url" name="images">$product_images</textarea>
+				<a class="$RB" href="/e/images" target="_blank">🔍</a>
+			</div>
+		</label>
+		<label>
+			$_ADD_TO_STOCK
+			<input required type="number" name="stock" value="0"></input>
+		</label>
+		<label>
+			$_PRICE
+			<input required type="number" name="price" min="0" step="0.01" value="$product_price"></input>
+		</label>
+		<button id="submit">$_SUBMIT</button>
+	</form>
+</body>
+
+<script>
+$JS
+</script>
+
+
diff --git a/shop/.product/template/index.html b/shop/.product/template/index.html
new file mode 100644
index 0000000..517294c
--- /dev/null
+++ b/shop/.product/template/index.html
@@ -0,0 +1,14 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$EDIT_BTN
+		<a class="$RB" href="/shop/$shop_id">💰</a>
+		$MENU
+	</header>
+	<h1 class="tac">$_TITLE</h1>
+	<div class="f fw h8 v8 fic">$PRODUCT_IMAGES</div>
+	<span class="tsxl">$product_title</span>
+	<div class="tsl">$product_price€</div>
+	<div>$_STOCK: $product_stock</div>
+	$ADD_TO_CART
+	$DELETE_PRODUCT
+</body>
diff --git a/shop/.sub-index b/shop/.sub-index
new file mode 100644
index 0000000..b2d91a4
--- /dev/null
+++ b/shop/.sub-index
@@ -0,0 +1,93 @@
+#!/bin/ksh
+
+shop_source
+
+case "$1" in
+	cart) shift; . ./.cart ; exit 0 ;;
+	prod) shift; . ./.product ; exit 0 ;;
+	edit) shift; . ./.edit ; exit 0;;
+	order)
+		shift
+		cd .order
+		. ./index
+		exit 0
+		;;
+	*)
+		if [[ ! -z "$1" ]]; then
+			cd .product
+			. ./index $@
+			exit 0
+		fi
+		;;
+esac
+
+lsshown() {
+	 find $1 -type d -mindepth 1 -maxdepth 1 -name "[!.]*" | sed "s|$1||" | sed "s/\/$//"
+}
+
+Products() {
+	local product_id
+	while read product_id; do
+		local card_class=""
+
+		product_env $product_id
+
+		if [[ "$product_stock" == "0" ]]; then
+			card_class="c0"
+		fi
+
+		cat <<!
+<a class="card f v8 b0 fic p $card_class" href="/shop/$shop_id/$product_id">
+	`PImage $product_image_path`
+	<div class="tsl">$product_price€</div>
+	<span class="tsxl">$product_title</span>
+	`echo "$product_description" | csurround p`
+</a>
+!
+	done
+}
+
+ShopButtons() {
+	[[ ! -z "$REMOTE_USER" ]] || return
+
+	cat <<!
+<a class="$RB" href="/shop/$shop_id/order">🚚</a>
+<a class="$RB" href="/shop/$shop_id/cart">🛒</a>
+!
+}
+
+case "$REQUEST_METHOD" in
+	POST) ;;
+	GET)
+		export _TITLE="`_ $shop_id`"
+
+		export PRODUCTS="`lsshown $SHOP_PATH/ | Products | fw`"
+		export SHOP_BUTTONS="`ShopButtons`"
+		if im $SHOP_OWNER; then
+			export ADD_PRODUCT_BUTTON="<div class=\"tar\"><a class=\"tsxl round ps btn\" href=\"/shop/$shop_id/add\">+</a></div>"
+			export EDIT_BTN="`EditBtn "/shop/$shop_id/edit"`"
+		fi
+
+		Normal 200 shop/$shop_id
+		Scat .template/sub-index
+		;;
+	*) NotAllowed ;;
+esac
+
+case "$action" in
+	delete)
+		PRODUCT_PATH=$SHOP_PATH/$product_id
+		if [[ -z "$product_id" ]] || [[ ! -d "$PRODUCT_PATH" ]]; then
+			Fatal 404 Product not found
+		fi
+
+		im $SHOP_OWNER || Forbidden
+
+		rm -rf $SHOP_PATH/$product_id
+
+		_see_other shop/$shop_id
+		;;
+	*)
+		Fatal 400 Invalid action
+		;;
+esac
diff --git a/templates/shop-add.html b/shop/.template/add.html
similarity index 75%
rename from templates/shop-add.html
rename to shop/.template/add.html
index 4e4e0c1..a0b4fbc 100644
--- a/templates/shop-add.html
+++ b/shop/.template/add.html
@@ -3,7 +3,7 @@
 
 	<h1 class="tac">$_TITLE</h1>
 
-	<form action="/e/shop-add" method="POST" class="v f fic">
+	<form action="add" method="POST" class="v f fic">
 		<label>
 			$_SHOP_ID
 			<input required name="shop_id"></input>
diff --git a/shop/.template/cart-edit.html b/shop/.template/cart-edit.html
new file mode 100644
index 0000000..8fd0107
--- /dev/null
+++ b/shop/.template/cart-edit.html
@@ -0,0 +1,16 @@
+<body class="v f fic">
+	<header class="h f fic">
+		<a class="$RB" href="/shop/$shop_id">💰</a>
+		$MENU
+	</header>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/shop/$shop_id/cart/edit" method="POST" class="v f fic">
+		$PRODUCTS
+
+		<input type="hidden" name="shop_id" value="$shop_id"></input>
+
+		<button>$_SUBMIT</Button>
+	</form>
+</body>
diff --git a/templates/cart.html b/shop/.template/cart.html
similarity index 61%
rename from templates/cart.html
rename to shop/.template/cart.html
index 61b23b8..289037b 100644
--- a/templates/cart.html
+++ b/shop/.template/cart.html
@@ -2,8 +2,9 @@
 pre { display: inline-block; }
 </style>
 <body class="v f fic">
-	<header class="_ f fic">
-		<a class="$RB" href="/e/shop?shop_id=$shop_id">💰</a>
+	<header class="h f fic">
+		$EDIT_BTN
+		<a class="$RB" href="/shop/$shop_id">💰</a>
 		$MENU
 	</header>
 	<h1 class="tac">$_TITLE</h1>
diff --git a/shop/.template/edit.html b/shop/.template/edit.html
new file mode 100644
index 0000000..2625ef3
--- /dev/null
+++ b/shop/.template/edit.html
@@ -0,0 +1,22 @@
+<style>
+pre { display: inline-block; }
+//.card input[type="checkbox"] { display: none; }
+.card:has(:checked) {
+	background-color: var(--C1);
+}
+</style>
+
+<body class="v f fic">
+	<header class="h f fic">
+		<a class="$RB" href="/e/shop?shop_id=$shop_id">💰</a>
+		$MENU
+	</header>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/shop-edit" method="POST" class="v f fic">
+		$PRODUCTS
+		<button>$_DELETE_PRODUCTS</button>
+		<input type="hidden" name="shop_id" value="$shop_id"></input>
+	</form>
+</body>
diff --git a/templates/shops.html b/shop/.template/index.html
similarity index 100%
rename from templates/shops.html
rename to shop/.template/index.html
diff --git a/shop/.template/sub-index.html b/shop/.template/sub-index.html
new file mode 100644
index 0000000..7a4c4e1
--- /dev/null
+++ b/shop/.template/sub-index.html
@@ -0,0 +1,14 @@
+<style>
+pre { display: inline-block; }
+</style>
+
+<body class="v f fic">
+	<header class="h f fic">
+		$EDIT_BTN
+		$SHOP_BUTTONS
+		$MENU
+	</header>
+	<h1 class="tac">$_TITLE</h1>
+	$PRODUCTS
+	$ADD_PRODUCT_BUTTON
+</body>
diff --git a/template/commands.html b/template/commands.html
new file mode 100644
index 0000000..0b9c673
--- /dev/null
+++ b/template/commands.html
@@ -0,0 +1,11 @@
+<body class="f fic tac v tsxl">
+	<form action="/whisper" method="POST">
+		<input name="whisper" class="c0"></input>
+	</form>
+	$MENU
+	<h2>$_TITLE</h2>
+	<a class="btn" href="/diff">diff</a>
+	<a class="btn" href="/nd-diff">$_NEVERDARK diff</a>
+	<a class="btn" href="/df">df</a>
+	<a class="btn" href="/quota">quota</a>
+</body>
diff --git a/template/index.html b/template/index.html
new file mode 100644
index 0000000..57d52e5
--- /dev/null
+++ b/template/index.html
@@ -0,0 +1,14 @@
+<body class="f fic tac v tsxl">
+	$MENU
+	<h2>tty.pt</h2>
+	<div><a class="btn" href="poem">$_POEMS</a></div>
+	<div><a class="btn" href="nd">$_NEVERDARK</a></div>
+	<!-- <div><a class="btn" href="/e/schools">$_SCHOOLS</a></div> -->
+	<div><a class="btn" href="shop">$_SHOPS</a></div>
+	<div><a class="btn" href="sem">$_SEM</a></div>
+	<div><a class="btn" href="/e/cgit.cgi">$_SOURCE_CODE</a></div>
+	<div><a class="btn" href="tty">$_TERMINAL</a></div>
+	<div><a class="btn" href="commands">$_COMMANDS</a></div>
+	<div><a class="btn" href="winbuntu">Winbuntu</a></div>
+</body>
+
diff --git a/templates/login.html b/template/login.html
similarity index 76%
rename from templates/login.html
rename to template/login.html
index c97b6eb..d1f9700 100644
--- a/templates/login.html
+++ b/template/login.html
@@ -3,7 +3,7 @@
 
 	<h1 class="tac">$_TITLE</h1>
 
-	<form action="./login" method="POST" class="v f fic">
+	<form action="login" method="POST" class="v f fic">
 		<label>
 			$_USERNAME: <input required name="username"></input>
 		</label>
@@ -14,7 +14,7 @@
 		<button>$_SUBMIT</button>
 	</form>
 
-	<a class="btn" href="/e/register">
+	<a class="btn" href="register">
 		$_REGISTER
 	</a>
 </body>
diff --git a/templates/register.html b/template/register.html
similarity index 79%
rename from templates/register.html
rename to template/register.html
index 5ccc70c..d18fad1 100644
--- a/templates/register.html
+++ b/template/register.html
@@ -1,7 +1,7 @@
 <body class="f fic v">
 	$MENU
 	<h1 class="tac">$_REGISTER</h1>
-	<form action="./register" method="post" class="v f fic">
+	<form action="register" method="post" class="v f fic">
 		<label>
 			$_USERNAME: <input required name="username"></input>
 		</label>
@@ -18,11 +18,11 @@
 		<button>$_SUBMIT</button>
 	</form>
 
-	<a class="btn c1" href="/e/cookie">
+	<a class="btn c1" href="cookie">
 		$_COOKIE_POLICY
 	</a>
 
-	<a class="btn" href="/e/login">
+	<a class="btn" href="login">
 		$_LOGIN
 	</a>
 </body>
diff --git a/templates/tty.html b/template/tty.html
similarity index 88%
rename from templates/tty.html
rename to template/tty.html
index 9ec0e64..2c3748c 100644
--- a/templates/tty.html
+++ b/template/tty.html
@@ -3,7 +3,7 @@ pre,form { margin: 0 }
 pre { display: inline-block; }
 </style>
 <body class="v f fic">
-	<header class="_ f fw v fic fcc">
+	<header class="h f fw v fic fcc">
 		$MENU
 	</header>
 	<h1 class="tac">$_TITLE</h1>
diff --git a/templates/registration-complete.html b/template/welcome.html
similarity index 99%
rename from templates/registration-complete.html
rename to template/welcome.html
index a130cbd..3652593 100644
--- a/templates/registration-complete.html
+++ b/template/welcome.html
@@ -3,4 +3,3 @@
 	<h1 class="tac">$_REGISTRATION_COMPLETE</h1>
 	<p>$_ACCOUNT_CREATED</p>
 </body>
-
diff --git a/templates/class-add.html b/templates/class-add.html
new file mode 100644
index 0000000..c9cb098
--- /dev/null
+++ b/templates/class-add.html
@@ -0,0 +1,29 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/class-add" method="POST" class="v f fic">
+		<label>
+			$_CLASS_ID
+			<input required name="class_id"></input>
+		</label>
+		<label>
+			$_CLASS_TITLE
+			<input required name="class_title"></input>
+		</label>
+		<label>
+			$_TEACHER
+			<input required name="teacher"></input>
+		</label>
+		<label>
+			$_SEMESTER
+			<input required name="semester" type="number" value="1" min="1" max="6"></input>
+		</label>
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+	</form>
+</body>
diff --git a/templates/class-grading.html b/templates/class-grading.html
new file mode 100644
index 0000000..532b7d9
--- /dev/null
+++ b/templates/class-grading.html
@@ -0,0 +1,20 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE ($class_id $student_id)</h1>
+
+	<form action="/e/class-grading" method="POST" class="v f fic">
+		<label>
+			$_GRADE
+			<input required name="grade" type="number" min="0" max="20" step="0.01"></input>
+		</label>
+
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="class_id" value="$class_id"></input>
+		<input type="hidden" name="student_id" value="$student_id"></input>
+	</form>
+</body>
diff --git a/templates/class-teacher-associate.html b/templates/class-teacher-associate.html
new file mode 100644
index 0000000..a959220
--- /dev/null
+++ b/templates/class-teacher-associate.html
@@ -0,0 +1,19 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE ($class_id)</h1>
+
+	<form action="/e/class-teacher-associate" method="POST" class="v f fic">
+		<label>
+			$_TEACHER_ID
+			<input required name="teacher_id"></input>
+		</label>
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="class_id" value="$class_id"></input>
+	</form>
+</body>
+
diff --git a/templates/class.html b/templates/class.html
new file mode 100644
index 0000000..d9d08cc
--- /dev/null
+++ b/templates/class.html
@@ -0,0 +1,21 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$class_id - $class_title</h1>
+
+	<div class="h">
+		<div>
+			$_SEMESTER $class_semester
+		</div>
+
+		$TEACHER
+	</div>
+
+	$COURSES
+
+	$ASSIGN_GRADES
+</body>
+
diff --git a/templates/classes.html b/templates/classes.html
new file mode 100644
index 0000000..e898ffd
--- /dev/null
+++ b/templates/classes.html
@@ -0,0 +1,9 @@
+<body class="v f fic">
+	<div class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+	<h1 class="tac">$_TITLE</h1>
+	$CLASSES
+	$CLASS_ADD
+</body>
diff --git a/templates/command.html b/templates/command.html
new file mode 100644
index 0000000..873cbab
--- /dev/null
+++ b/templates/command.html
@@ -0,0 +1,13 @@
+<style>
+pre {
+	text-align: left;
+}
+</style>
+
+<body class="f fic tac v">
+	$MENU
+	<div class="tsxl">$_TITLE</div>
+	<pre>
+$OUTPUT
+	</pre>
+</body>
diff --git a/templates/course-add.html b/templates/course-add.html
new file mode 100644
index 0000000..0980f2e
--- /dev/null
+++ b/templates/course-add.html
@@ -0,0 +1,25 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/course-add" method="POST" class="v f fic">
+		<label>
+			$_COURSE_ID
+			<input required name="course_id"></input>
+		</label>
+		<label>
+			$_COURSE_TITLE
+			<input required name="course_title"></input>
+		</label>
+		<label>
+			$_REGENT
+			<input required name="teacher_id"></input>
+		</label>
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+	</form>
+</body>
diff --git a/templates/course-class-associate.html b/templates/course-class-associate.html
new file mode 100644
index 0000000..3a9960c
--- /dev/null
+++ b/templates/course-class-associate.html
@@ -0,0 +1,18 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE ($course_id)</h1>
+
+	<form action="/e/course-class-associate" method="POST" class="v f fic">
+		<label>
+			$_CLASS_ID
+			<input required name="class_id"></input>
+		</label>
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="course_id" value="$course_id"></input>
+	</form>
+</body>
diff --git a/templates/course-teacher-associate.html b/templates/course-teacher-associate.html
new file mode 100644
index 0000000..e118f7e
--- /dev/null
+++ b/templates/course-teacher-associate.html
@@ -0,0 +1,18 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE ($course_id)</h1>
+
+	<form action="/e/course-teacher-associate" method="POST" class="v f fic">
+		<label>
+			$_TEACHER_ID
+			<input required name="teacher_id"></input>
+		</label>
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="course_id" value="$course_id"></input>
+	</form>
+</body>
diff --git a/templates/course.html b/templates/course.html
new file mode 100644
index 0000000..7c5b9a9
--- /dev/null
+++ b/templates/course.html
@@ -0,0 +1,16 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$course_id - $course_title</h1>
+
+	$REGENT
+
+	$CLASSES
+
+	$STUDENTS
+</body>
+
+
diff --git a/templates/courses.html b/templates/courses.html
new file mode 100644
index 0000000..af34045
--- /dev/null
+++ b/templates/courses.html
@@ -0,0 +1,9 @@
+<body class="v f fic">
+	<div class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+	<h1 class="tac">$_TITLE</h1>
+	$COURSES
+	$COURSE_ADD
+</body>
diff --git a/templates/index.html b/templates/index.html
index 7ce16ea..f613052 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -3,8 +3,10 @@
 	<h2>tty.pt</h2>
 	<a class="btn" href="/e/poems">$_POEMS</a>
 	<a class="btn" href="/e/neverdark">$_NEVERDARK</a>
+	<a class="btn" href="/e/schools">$_SCHOOLS</a>
 	<a class="btn" href="/e/shops">$_SHOPS</a>
-	<a class="btn" href="/sem.html">$_SEM</a>
-	<a class="btn" href="https://github.com/quirinpa/vim.css">vim.css</a>
+	<a class="btn" href="/sem">$_SEM</a>
+	<a class="btn" href="/e/cgit.cgi">$_SOURCE_CODE</a>
 	<a class="btn" href="/e/tty">$_TERMINAL</a>
+	<a class="btn" href="/e/commands">$_COMMANDS</a>
 </body>
diff --git a/templates/poem.html b/templates/poem.html
index 0e6bdae..2aa9136 100644
--- a/templates/poem.html
+++ b/templates/poem.html
@@ -11,7 +11,7 @@ pre {
 </style>
 
 <body class="v">
-	<header class="_ f fw v fic fcc">
+	<header class="h f fw v fic fcc">
 		<a href="#comments" class="$RB">💬</a>
 		$MENU
 		<div class="cf15 tsxl">$COUNTER</div>
@@ -23,7 +23,7 @@ pre {
 $_POEM
 	</pre>
 
-	<form action="./poem#comments" method="post" class="_ f fic">
+	<form action="./poem#comments" method="post" class="h f fic">
 		<input type="hidden" name="poem_id" value="$poem_id"></input>
 		<input class="fg" name="comment"></input>
 	</form>
diff --git a/templates/product.html b/templates/product.html
deleted file mode 100644
index c106684..0000000
--- a/templates/product.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<body class="v f fic">
-	<header class="_ f fic">
-		<a class="$RB" href="/e/shop?shop_id=$shop_id">💰</a>
-		$MENU
-	</header>
-	<h1 class="tac">$_TITLE</h1>
-	$PRODUCT
-</body>
-
diff --git a/templates/school-add.html b/templates/school-add.html
new file mode 100644
index 0000000..a1e2f52
--- /dev/null
+++ b/templates/school-add.html
@@ -0,0 +1,19 @@
+<body class="f fic v">
+	$MENU
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/school-add" method="POST" class="v f fic">
+		<label>
+			$_SCHOOL_ID
+			<input required name="school_id"></input>
+		</label>
+		<label>
+			$_SCHOOL_TITLE
+			<input required name="school_title"></input>
+		</label>
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
+
diff --git a/templates/school.html b/templates/school.html
new file mode 100644
index 0000000..cad5adf
--- /dev/null
+++ b/templates/school.html
@@ -0,0 +1,13 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$SCHOOL_MENU
+		$MENU
+	</header>
+
+	<h1 class="tac">$school_title</h1>
+
+	<a class="btn" href="/e/teachers?school_id=$school_id">$_TEACHERS</a>
+	<a class="btn" href="/e/students?school_id=$school_id">$_STUDENTS</a>
+	<a class="btn" href="/e/courses?school_id=$school_id">$_COURSES</a>
+	<a class="btn" href="/e/classes?school_id=$school_id">$_CLASSES</a>
+</body>
diff --git a/templates/schools.html b/templates/schools.html
new file mode 100644
index 0000000..3a9f3ce
--- /dev/null
+++ b/templates/schools.html
@@ -0,0 +1,6 @@
+<body class="v f fic">
+	$MENU
+	<h1 class="tac">$_TITLE</h1>
+	$SCHOOLS
+	$SCHOOL_ADD
+</body>
diff --git a/templates/shop.html b/templates/shop.html
deleted file mode 100644
index 953a820..0000000
--- a/templates/shop.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<style>
-pre { display: inline-block; }
-</style>
-
-<body class="v f fic">
-	<header class="_ f fic">
-		<!--
-		<form action="./shops" method="get" class="_ f ps fic">
-			<input autofocus type="search" name="query"></input>
-			<select name="category" multiple>
-				$SHOP_CATEGORIES
-			</select>
-			<button class="tl">🔎</button>
-		</form>
-		-->
-		$SHOP_BUTTONS
-		$MENU
-	</header>
-	<h1 class="tac">$_TITLE</h1>
-	$PRODUCTS
-	$ADD_PRODUCT_BUTTON
-</body>
diff --git a/templates/student-add.html b/templates/student-add.html
new file mode 100644
index 0000000..126e590
--- /dev/null
+++ b/templates/student-add.html
@@ -0,0 +1,29 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/student-add" method="POST" class="v f fic">
+		<label>
+			$_STUDENT_ID
+			<input required name="student_id"></input>
+		</label>
+
+		<label>
+			$_NAME
+			<input required type="text" name="name"></input>
+		</label>
+
+		<label>
+			$_DOB
+			<input required type="date" name="dob"></input>
+		</label>
+
+		<button>$_SUBMIT</button>
+
+		<input type="hidden" name="school_id" value="$school_id"></input>
+	</form>
+</body>
diff --git a/templates/student-course-associate.html b/templates/student-course-associate.html
new file mode 100644
index 0000000..28eae8c
--- /dev/null
+++ b/templates/student-course-associate.html
@@ -0,0 +1,19 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE ($student_id)</h1>
+
+	<form action="/e/student-course-associate" method="POST" class="v f fic">
+		<label>
+			$_COURSE_ID
+			<input required name="course_id"></input>
+		</label>
+		<button>$_SUBMIT</button>
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="student_id" value="$student_id"></input>
+	</form>
+</body>
+
diff --git a/templates/student-edit.html b/templates/student-edit.html
new file mode 100644
index 0000000..75d30ed
--- /dev/null
+++ b/templates/student-edit.html
@@ -0,0 +1,25 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$_EDIT $_STUDENT - $student_id</h1>
+
+	<form action="/e/student-edit" method="POST" class="v f fic">
+		<label>
+			$_NAME
+			<input required type="text" name="name" value="$name"></input>
+		</label>
+
+		<label>
+			$_DOB
+			<input required type="date" name="dob" value="$dob"></input>
+		</label>
+
+		<button>$_SUBMIT</button>
+
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="student_id" value="$student_id"></input>
+	</form>
+</body>
diff --git a/templates/student.html b/templates/student.html
new file mode 100644
index 0000000..660ca6a
--- /dev/null
+++ b/templates/student.html
@@ -0,0 +1,19 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$EDIT_BTN
+		$SCHOOL_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$_STUDENT - $student_id</h1>
+
+	$RN
+
+	$NAME
+
+	$DOB
+
+	$COURSES
+
+	$GRADES
+</body>
diff --git a/templates/students.html b/templates/students.html
new file mode 100644
index 0000000..06db46c
--- /dev/null
+++ b/templates/students.html
@@ -0,0 +1,12 @@
+<body class="v f fic">
+	<div class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	$STUDENTS
+
+	$STUDENT_ADD
+</body>
diff --git a/templates/teacher-add.html b/templates/teacher-add.html
new file mode 100644
index 0000000..bd57b3b
--- /dev/null
+++ b/templates/teacher-add.html
@@ -0,0 +1,29 @@
+<body class="f fic v">
+	<div class="f fic h">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/teacher-add" method="POST" class="v f fic">
+		<label>
+			$_TEACHER_ID
+			<input required name="teacher_id"></input>
+		</label>
+
+		<label>
+			$_NAME
+			<input required type="text" name="name"></input>
+		</label>
+
+		<label>
+			$_DOB
+			<input required type="date" name="dob"></input>
+		</label>
+
+		<button>$_SUBMIT</button>
+
+		<input type="hidden" name="school_id" value="$school_id"></input>
+	</form>
+</body>
diff --git a/templates/teacher-edit.html b/templates/teacher-edit.html
new file mode 100644
index 0000000..5cceebb
--- /dev/null
+++ b/templates/teacher-edit.html
@@ -0,0 +1,25 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$_EDIT $_TEACHER - $teacher_id</h1>
+
+	<form action="/e/teacher-edit" method="POST" class="v f fic">
+		<label>
+			$_NAME
+			<input required type="text" name="name" value="$name"></input>
+		</label>
+
+		<label>
+			$_DOB
+			<input required type="date" name="dob" value="$dob"></input>
+		</label>
+
+		<button>$_SUBMIT</button>
+
+		<input type="hidden" name="school_id" value="$school_id"></input>
+		<input type="hidden" name="teacher_id" value="$teacher_id"></input>
+	</form>
+</body>
diff --git a/templates/teacher.html b/templates/teacher.html
new file mode 100644
index 0000000..f5e1f78
--- /dev/null
+++ b/templates/teacher.html
@@ -0,0 +1,19 @@
+<body class="v f fic">
+	<header class="h f fic">
+		$EDIT_BTN
+		$SCHOOL_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$_TEACHER - $teacher_id</h1>
+
+	$NAME
+
+	$DOB
+
+	$SALARY
+
+	$CLASSES
+
+	$COURSES
+</body>
diff --git a/templates/teachers.html b/templates/teachers.html
new file mode 100644
index 0000000..24beb00
--- /dev/null
+++ b/templates/teachers.html
@@ -0,0 +1,12 @@
+<body class="v f fic">
+	<div class="h f fic">
+		$SCHOOL_BTN
+		$MENU
+	</div>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	$TEACHERS
+
+	$TEACHER_ADD
+</body>
diff --git a/templates/user-edit.html b/templates/user-edit.html
new file mode 100644
index 0000000..c1aac5e
--- /dev/null
+++ b/templates/user-edit.html
@@ -0,0 +1,45 @@
+<body class="f fic v">
+	$MENU
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<form action="/e/user-edit" method="POST" class="f v fic">
+		<div class="h f fw v fic fcc">
+			<div class="f v fic">
+				<label>
+					$_NAME
+					<input type="text" name="name" value="$name"></input>
+				</label>
+				<label>
+					$_ADDRESS_LINE_1
+					<input type="text" name="address_line_1" value="$address_line_1" autocomplete="address-line1"></input>
+				</label>
+				<label>
+					$_ADDRESS_LINE_2
+					<input type="text" name="address_line_2" value="$address_line_2" autocomplete="address-line2"></input>
+				</label>
+				<label>
+					$_ZIP_CODE
+					<input name="zip" type="text" value="$zip" autocomplete="postal-code">
+				</label>
+			</div>
+			<div class="f v fic">
+				<label>
+					$_PHONE_NUMBER
+					<input name="phone_number" type="tel" value="$phone_number">
+				</label>
+				<label>
+					$_ACCOUNT_IBAN
+					<input type="text" name="iban" value="$iban"></input>
+				</label>
+				<label>
+					$_ACCOUNT_BICSWIFT
+					<input type="text" name="bicswift" value="$bicswift"></input>
+				</label>
+			</div>
+		</div>
+
+		<button>$_SUBMIT</button>
+	</form>
+</body>
+
diff --git a/templates/user.html b/templates/user.html
deleted file mode 100644
index 2043372..0000000
--- a/templates/user.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<style>
-pre { display: inline-block; }
-</style>
-<body class="f fic v">
-	$MENU
-
-	<h1 class="tac">$_TITLE</h1>
-
-	<div class="tsxl">$_WELCOME</div>
-
-	<div class="tac tsxl f">
-		<a class="btn" href="/e/logout">$_LOGOUT</a>
-	</div>
-
-	<div class="f _ fw v fic fcc">
-		<form method="POST" class="v f fic">
-			<input type="hidden" name="action" value="personal_data"></input>
-			<p>
-			$_CHANGE_PERSONAL_DATA
-			</p>
-			<label>
-				$_ADDRESS_LINE_1
-				<input type="text" name="address_line_1" value="$address_line_1" autocomplete="address-line1"></input>
-			</label>
-			<label>
-				$_ADDRESS_LINE_2
-				<input type="text" name="address_line_2" value="$address_line_2" autocomplete="address-line2"></input>
-			</label>
-			<label>
-				$_ZIP_CODE
-				<input name="zip" type="text" value="$zip" autocomplete="postal-code">
-			</label>
-			<label>
-				$_PHONE_NUMBER
-				<input name="phone_number" type="tel" value="$phone_number">
-			</label>
-			<button>$_SUBMIT</button>
-		</form>
-
-		<form method="POST" class="v f fic">
-			<input type="hidden" name="action" value="vendor_data"></input>
-			<p>
-			$_CHANGE_VENDOR_DATA
-			</p>
-			<label>
-				$_ACCOUNT_IBAN
-				<input type="text" name="iban" value="$iban"></input>
-			</label>
-			<label>
-				$_ACCOUNT_BICSWIFT
-				<input type="text" name="bicswift" value="$bicswift"></input>
-			</label>
-			<button>$_SUBMIT</button>
-		</form>
-	</div>
-</body>
diff --git a/user/.index b/user/.index
new file mode 100644
index 0000000..4d2b153
--- /dev/null
+++ b/user/.index
@@ -0,0 +1,88 @@
+#!/bin/ksh
+
+USER_PATH=$ROOT/users/$REMOTE_USER
+
+Field() {
+	cat | cond || {
+		cat <<!
+<div class="vxs">
+	<div class="tsxs">$1</div>
+	<pre class="cf15">`cat $contents`</pre>
+</div>
+!
+	}
+}
+
+Address() {
+	Field "$_ADDRESS" <<!
+`zcat $USER_PATH/address_line_1`
+`zcat $USER_PATH/address_line_2`
+`zcat $USER_PATH/zip`
+!
+}
+
+PhoneNumber() {
+	if [[ ! -f "$USER_PATH/phone_number" ]]; then
+		return
+	fi
+
+	local label="`_ "Phone number"`"
+	local value="`cat $USER_PATH/phone_number`"
+
+	cat <<!
+<div class="vxs">
+	<div class="tsxs">$label</div>
+	<a href="tel:$value" class="ts cf15">$value</a>
+</div>
+!
+
+}
+
+AccountInfo() {
+	if [[ ! -f "$USER_PATH/iban" ]] || [[ ! -f "$USER_PATH/bicswift" ]]; then
+		return
+	fi
+
+	local label="`_ "Account information"`"
+	local value="`cat $USER_PATH/phone_number`"
+
+	cat <<!
+<div class="vxs tss">
+	<div class="tsxs">$label</div>
+	<div class="_xs">
+		<span class="tsxs">`_ IBAN`</span>
+		<span class="cf15">`cat $USER_PATH/iban`</span>
+	</div>
+	<div class="_xs">
+		<span class="tsxs">`_ BICSWIFT`</span>
+		<span class="cf15">`cat $USER_PATH/bicswift`</span>
+	</div>
+</div>
+!
+
+}
+
+[[ "$REQUEST_METHOD" == "GET" ]] || NotAllowed
+
+if [[ -z "$user_id" ]] || im $user_id; then
+	export EDIT_BTN="`EditBtn "/e/user-edit?user_id=$user_id"`"
+fi
+
+_NAME="`_ Name`"
+
+export NAME="`zcat $USER_PATH/name | Field "$_NAME"`"
+export ADDRESS="`Address`"
+export PHONE_NUMBER="`PhoneNumber`"
+export ACCOUNT_INFO="`AccountInfo`"
+
+export _TITLE="`_ User` - $REMOTE_USER"
+export _LOGOUT="`_ Logout`"
+export _SUBMIT="`_ Submit`"
+export _PAGE="`_ Page`"
+
+export iban="`zcat $USER_PATH/iban`"
+export bicswift="`zcat $USER_PATH/bicswift`"
+export REMOTE_USER
+
+Normal 200 user
+Scat .template/index
diff --git a/user/.template/index.html b/user/.template/index.html
new file mode 100644
index 0000000..a34031e
--- /dev/null
+++ b/user/.template/index.html
@@ -0,0 +1,29 @@
+<body class="f fic v">
+	<header class="h f fic">
+		$EDIT_BTN
+		$MENU
+	</header>
+
+	<h1 class="tac">$_TITLE</h1>
+
+	<div class="h f fw v">
+		<div class="f v">
+			$NAME
+
+			$ADDRESS
+		</div>
+		<div class="f v">
+			$PHONE_NUMBER
+
+			$ACCOUNT_INFO
+		</div>
+	</div>
+
+	<div class="tac tsxl f">
+		<a class="btn" href="~$REMOTE_USER">$_PAGE</a>
+	</div>
+
+	<div class="tac tsxl f">
+		<a class="btn" href="logout">$_LOGOUT</a>
+	</div>
+</body>
diff --git a/winbuntu/.index b/winbuntu/.index
deleted file mode 100755
index e87b548..0000000
--- a/winbuntu/.index
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/ksh
-
-[[ "$REQUEST_METHOD" == "GET" ]] || NotAllowed
-export _TITLE="Winbuntu"
-
-Normal 200 winbuntu
-Scat .template/index
diff --git a/winbuntu/.template/index.html b/winbuntu/.template/index.html
deleted file mode 100644
index 9a6ca35..0000000
--- a/winbuntu/.template/index.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<body class="v f fic">
-	$MENU
-	<h1 class="tac">$_TITLE</h1>
-	<div class="tal v">
-		<p>This is a Linux distro based on Ubuntu 22.04.1 with a customized gnome environment to look like windows. Currently only for amd64.</p>
-		<h2>Installation</h2>
-		<p><a href="winbuntu-22.04.4-2024.03.02-desktop-amd64.iso">Click here to download winbuntu-22.04.4</a> or:</p>
-		<pre class="p c0">
-curl tty.pt/winbuntu/winbuntu-22.04.4-2024.03.02-desktop-amd64.iso -O
-		</pre>
-		<p>Check that the checksum matches:</p>
-		<pre class="p c0">
-md5sum ./winbuntu-22.04.4-2024.03.02-desktop-amd64.iso
-		</pre>
-		<p>Should match:</p>
-		<pre class="p c0">
-a00f121bc5c5dd525e253a4193d3867b  winbuntu-22.04.4-2024.03.02-desktop-amd64.iso
-		</pre>
-		<p>Write to a pen drive:</p>
-		<pre class="p c0">
-dd if=./winbuntu-22.04.4-2024.03.02-desktop-amd64.iso of=/dev/sda bs=1M
-		</pre>
-		<p>Follow the installation wizard, and you are good to go.</p>
-		<h2>Screenshots</h2>
-		<div class="h v f fw">
-			<a href="img/winbuntu0.png"><img height="320" src="img/winbuntu0.png"></img></a>
-			<a href="img/winbuntu1.png"><img height="320" src="img/winbuntu1.png"></img></a>
-			<a href="img/winbuntu2.png"><img height="320" src="img/winbuntu2.png"></img></a>
-			<a href="img/winbuntu3.png"><img height="320" src="img/winbuntu3.png"></img></a>
-		</div>
-	</div>
-</body>
-