diff --git a/main.go b/main.go
index 9808d5c51465ae1cc64643a79a39cb72977063a6..a02a9efc1de9657df5021dec9817cae0b7dfd216 100644
--- a/main.go
+++ b/main.go
@@ -2,8 +2,10 @@ package main
 
 import (
 	"embed"
+	"errors"
 	"fmt"
 	"html/template"
+	"io/ioutil"
 	"log"
 	"net/http"
 	"strconv"
@@ -19,6 +21,11 @@ var templates = template.Must(template.ParseFS(templateFS, "templates/*.tpl"))
 //go:embed static/*
 var staticFS embed.FS
 
+func httpError(w http.ResponseWriter, msg string, err error, status int) {
+	log.Println(msg)
+	http.Error(w, msg+": "+err.Error(), status)
+}
+
 func main() {
 	db, err := sqlx.Open("sqlite", "vino.sqlite")
 	if err != nil {
@@ -39,7 +46,7 @@ func main() {
 		if r.Method == "GET" {
 			wines, err := ListWines(r.Context(), db)
 			if err != nil {
-				http.Error(w, "can't list wines: "+err.Error(), http.StatusInternalServerError)
+				httpError(w, "can't list wines", err, http.StatusInternalServerError)
 				return
 			}
 
@@ -58,7 +65,7 @@ func main() {
 		}
 
 		if r.Method != "POST" {
-			http.Error(w, "invalid method:"+r.Method, http.StatusMethodNotAllowed)
+			httpError(w, "invalid method", errors.New(r.Method), http.StatusMethodNotAllowed)
 			return
 		}
 
@@ -66,37 +73,57 @@ func main() {
 
 		name := r.FormValue("name")
 		if name == "" {
-			http.Error(w, "name empty or missing", http.StatusBadRequest)
+			httpError(w, "bad name", errors.New("empty or missing"), http.StatusBadRequest)
 			return
 		}
 
 		if len(name) > 80 {
-			http.Error(w, "name too long, max length is 80", http.StatusBadRequest)
+			httpError(w, "bad name", errors.New("name too long, max length is 80"), http.StatusBadRequest)
 			return
 		}
 
 		ratingVal := r.FormValue("rating")
-		rating, err := strconv.Atoi(ratingVal)
+
+		var rating int
+		if ratingVal != "" {
+			rating, err = strconv.Atoi(ratingVal)
+			if err != nil {
+				httpError(w, "can't convert rating", err, http.StatusBadRequest)
+				return
+			}
+		}
+
+		picFile, picHdr, err := r.FormFile("picture")
+		if err != nil {
+			httpError(w, "can't load picture file", err, http.StatusInternalServerError)
+			return
+		}
+		defer picFile.Close()
+
+		log.Println("picture", picHdr.Size, picHdr.Header.Get("Content-Type"))
+
+		picData, err := ioutil.ReadAll(picFile)
 		if err != nil {
-			http.Error(w, "can't convert rating:"+err.Error(), http.StatusBadRequest)
+			httpError(w, "can't read picture file", err, http.StatusInternalServerError)
 			return
 		}
 
 		vino := Vino{
-			Name:   name,
-			Rating: rating,
+			Name:    name,
+			Rating:  rating,
+			Picture: picData,
 		}
 
 		err = vino.Store(r.Context(), db)
 		if err != nil {
-			http.Error(w, fmt.Sprintf("can't store wine %q: %s", vino, err), http.StatusInternalServerError)
+			httpError(w, fmt.Sprintf("can't store wine %q", vino), err, http.StatusInternalServerError)
 			return
 		}
 
 		http.Redirect(w, r, "/", http.StatusSeeOther) // TODO: Is this the correct status?
 	})
 
-	const listenAddr = "127.0.0.1:7878"
+	const listenAddr = ":7878"
 
 	log.Printf("here we go, listening on http://%s", listenAddr)
 
diff --git a/templates/index.tpl b/templates/index.tpl
index 064700604afc7e237d03fea1a2faae879c477a82..bd53485989abb95140f5685e4bf68a65214bf4bc 100644
--- a/templates/index.tpl
+++ b/templates/index.tpl
@@ -11,11 +11,11 @@
 <body>
 	The DB contains {{ .Wines | len }} wine(s) right now:
 
-	<form method="POST" action="/add">
-		<input type="text" name="name" placeholder="Name"></input>
+	<form method="POST" enctype="multipart/form-data" action="/add">
+		<input type="text" name="name" placeholder="Name" required></input>
 		<input type="number" name="rating" placeholder="Rating"></input>
-
-		<span class="todo">TODO: Image</todo>
+		<br/>
+		<input type="file" name="picture" accept="image/png, image/jpeg"></input>
 
 		<button type="submit">+</button>
 	</form>