Don't use base64 encoding for header & avatar uploads
authorMikael Berthe <mikael@lilotux.net>
Mon, 28 May 2018 20:51:10 +0200
changeset 226 212a0e74e719
parent 225 9aa1cb3e1dee
child 227 2b46eb72c78a
Don't use base64 encoding for header & avatar uploads This should fix https://github.com/McKael/madonctl/issues/14.
account.go
--- a/account.go	Wed Mar 21 22:43:32 2018 +0100
+++ b/account.go	Mon May 28 20:51:10 2018 +0200
@@ -8,14 +8,12 @@
 
 import (
 	"bytes"
-	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"mime/multipart"
-	"net/http"
 	"os"
+	"path/filepath"
 	"strconv"
-	"strings"
 
 	"github.com/pkg/errors"
 	"github.com/sendgrid/rest"
@@ -402,14 +400,17 @@
 }
 
 // UpdateAccount updates the connected user's account data
-// The fields avatar & headerImage can contain base64-encoded images; if
-// they do not (that is; if they don't contain ";base64,"), they are considered
-// as file paths and their content will be encoded.
+//
+// The fields avatar & headerImage are considered as file paths
+// and their content will be uploaded.
+// Please note that currently Mastodon leaks the avatar file name:
+// https://github.com/tootsuite/mastodon/issues/5776
+//
 // Setting 'locked' to true means all followers should be approved.
 // All fields can be nil, in which case they are not updated.
 // displayName and note can be set to "" to delete previous values;
 // I'm not sure images can be deleted -- only replaced AFAICS.
-func (mc *Client) UpdateAccount(displayName, note, avatar, headerImage *string, locked *bool) (*Account, error) {
+func (mc *Client) UpdateAccount(displayName, note, avatarImagePath, headerImagePath *string, locked *bool) (*Account, error) {
 	const endPoint = "accounts/update_credentials"
 	params := make(apiCallParams)
 
@@ -428,11 +429,14 @@
 	}
 
 	var err error
-	avatar, err = fileToBase64(avatar, nil)
+	var avatar, headerImage []byte
+
+	avatar, err = readFile(avatarImagePath)
 	if err != nil {
 		return nil, err
 	}
-	headerImage, err = fileToBase64(headerImage, nil)
+
+	headerImage, err = readFile(headerImagePath)
 	if err != nil {
 		return nil, err
 	}
@@ -441,10 +445,18 @@
 	w := multipart.NewWriter(&formBuf)
 
 	if avatar != nil {
-		w.WriteField("avatar", *avatar)
+		formWriter, err := w.CreateFormFile("avatar", filepath.Base(*avatarImagePath))
+		if err != nil {
+			return nil, errors.Wrap(err, "avatar upload")
+		}
+		formWriter.Write(avatar)
 	}
 	if headerImage != nil {
-		w.WriteField("header", *headerImage)
+		formWriter, err := w.CreateFormFile("header", filepath.Base(*headerImagePath))
+		if err != nil {
+			return nil, errors.Wrap(err, "header upload")
+		}
+		formWriter.Write(headerImage)
 	}
 	w.Close()
 
@@ -479,26 +491,13 @@
 	return &account, nil
 }
 
-// fileToBase64 is a helper function to convert a file's contents to
-// base64-encoded data.  Is the data string already contains base64 data, it
-// is not modified.
-// If contentType is nil, it is detected.
-func fileToBase64(data, contentType *string) (*string, error) {
-	if data == nil {
+// readFile is a helper function to read a file's contents.
+func readFile(filename *string) ([]byte, error) {
+	if filename == nil || *filename == "" {
 		return nil, nil
 	}
 
-	if *data == "" {
-		return data, nil
-	}
-
-	if strings.Contains(*data, ";base64,") {
-		return data, nil
-	}
-
-	// We need to convert the file and file name to base64
-
-	file, err := os.Open(*data)
+	file, err := os.Open(*filename)
 	if err != nil {
 		return nil, err
 	}
@@ -515,13 +514,5 @@
 		return nil, err
 	}
 
-	var cType string
-	if contentType == nil || *contentType == "" {
-		cType = http.DetectContentType(buffer[:512])
-	} else {
-		cType = *contentType
-	}
-	contentData := base64.StdEncoding.EncodeToString(buffer)
-	newData := "data:" + cType + ";base64," + contentData
-	return &newData, nil
+	return buffer, nil
 }