# #cloud-config-archive Handler
# vim:set filetype=sh:
# shellcheck shell=sh

cloud_config_archive_is_sequence() {
	awk '
		NR == 1 { next }
		/^[[:space:]]*($|#)/ { next }
		/^[[:space:]]*---[[:space:]]*($|#)/ { next }
		{
			found = 1
			exit ($0 ~ /^[[:space:]]*(-([[:space:]]|$)|\[)/ ? 0 : 1)
		}
		END { if (!found) exit 1 }
	' "$1"
}

cloud_config_archive_stage_scalar() {
	local archive="$1" entry="$2" field="$3" target="$4" child

	yx -f "$archive" "$entry" "$field" > "$target" || {
		rm -f "$target"
		return 1
	}
	IFS= read -r child < "$target" || return 0
	[ -n "$child" ] || return 0
	if yx -f "$archive" "$entry" "$field" "$child" \
			>/dev/null 2>&1; then
		rm -f "$target"
		return 1
	fi
	return 0
}

handler__organize_userdata() {
	local archive="$1" archive_origin="$2"
	local entry entries content_type origin source type typefile
	local tmpdir="$TINY_CLOUD_VAR/user-data.organize.XXXXXX"

	tmpdir=$(mktemp -d "$tmpdir") || return
	entries=$(yx -f "$archive") || {
		log warning -i -t "$phase" \
			"$ACTION: $archive_origin: malformed cloud-config-archive"
		rm -rf "$tmpdir"
		return 1
	}
	if ! cloud_config_archive_is_sequence "$archive"; then
		log warning -i -t "$phase" \
			"$ACTION: $archive_origin: malformed cloud-config-archive"
		rm -rf "$tmpdir"
		return 1
	fi

	# Validate and stage the archive before publishing any content.
	for entry in $entries; do
		case "$entry" in
			*[!0-9]*|'')
				log warning -i -t "$phase" \
					"$ACTION: $archive_origin: malformed cloud-config-archive"
				rm -rf "$tmpdir"
				return 1
				;;
		esac
		origin="cloud-config-archive part $entry"
		typefile="$tmpdir/type.$entry"
		if ! cloud_config_archive_stage_scalar \
				"$archive" "$entry" type "$typefile"; then
			log warning -i -t "$phase" \
				"$ACTION: $origin: missing or invalid type"
			continue
		fi
		content_type=$(cat "$typefile")
		case "$content_type" in
			''|*[!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+./-]*)
				rm -f "$typefile"
				log warning -i -t "$phase" \
					"$ACTION: $origin: missing or invalid type"
				continue
				;;
		esac
		source="$tmpdir/part.$entry"
		if ! cloud_config_archive_stage_scalar \
				"$archive" "$entry" content "$source" ||
				! [ -s "$source" ]; then
			rm -f "$source" "$typefile"
			log warning -i -t "$phase" \
				"$ACTION: $origin: missing or invalid content"
			continue
		fi
	done

	for entry in $entries; do
		[ -f "$tmpdir/type.$entry" ] || continue
		content_type=$(cat "$tmpdir/type.$entry")
		case "$content_type" in
			text/cloud-config) type=cloud-config;;
			text/network-config) type=network-config;;
			text/x-shellscript) type=script;;
			*) type="$content_type";;
		esac
		if ! organize_userdata_content "$type" "$tmpdir/part.$entry" \
				"cloud-config-archive part $entry"; then
			log warning -i -t "$phase" \
				"$ACTION: cloud-config-archive part $entry: unable to organize content type '$content_type'"
		fi
	done

	rm -rf "$tmpdir"
}
