account.go
changeset 226 212a0e74e719
parent 218 22ed9de1c5fe
child 229 69f29aacd5bc
equal deleted inserted replaced
225:9aa1cb3e1dee 226:212a0e74e719
     6 
     6 
     7 package madon
     7 package madon
     8 
     8 
     9 import (
     9 import (
    10 	"bytes"
    10 	"bytes"
    11 	"encoding/base64"
       
    12 	"encoding/json"
    11 	"encoding/json"
    13 	"fmt"
    12 	"fmt"
    14 	"mime/multipart"
    13 	"mime/multipart"
    15 	"net/http"
       
    16 	"os"
    14 	"os"
       
    15 	"path/filepath"
    17 	"strconv"
    16 	"strconv"
    18 	"strings"
       
    19 
    17 
    20 	"github.com/pkg/errors"
    18 	"github.com/pkg/errors"
    21 	"github.com/sendgrid/rest"
    19 	"github.com/sendgrid/rest"
    22 )
    20 )
    23 
    21 
   400 	_, err := mc.getSingleAccount(endPoint, accountID)
   398 	_, err := mc.getSingleAccount(endPoint, accountID)
   401 	return err
   399 	return err
   402 }
   400 }
   403 
   401 
   404 // UpdateAccount updates the connected user's account data
   402 // UpdateAccount updates the connected user's account data
   405 // The fields avatar & headerImage can contain base64-encoded images; if
   403 //
   406 // they do not (that is; if they don't contain ";base64,"), they are considered
   404 // The fields avatar & headerImage are considered as file paths
   407 // as file paths and their content will be encoded.
   405 // and their content will be uploaded.
       
   406 // Please note that currently Mastodon leaks the avatar file name:
       
   407 // https://github.com/tootsuite/mastodon/issues/5776
       
   408 //
   408 // Setting 'locked' to true means all followers should be approved.
   409 // Setting 'locked' to true means all followers should be approved.
   409 // All fields can be nil, in which case they are not updated.
   410 // All fields can be nil, in which case they are not updated.
   410 // displayName and note can be set to "" to delete previous values;
   411 // displayName and note can be set to "" to delete previous values;
   411 // I'm not sure images can be deleted -- only replaced AFAICS.
   412 // I'm not sure images can be deleted -- only replaced AFAICS.
   412 func (mc *Client) UpdateAccount(displayName, note, avatar, headerImage *string, locked *bool) (*Account, error) {
   413 func (mc *Client) UpdateAccount(displayName, note, avatarImagePath, headerImagePath *string, locked *bool) (*Account, error) {
   413 	const endPoint = "accounts/update_credentials"
   414 	const endPoint = "accounts/update_credentials"
   414 	params := make(apiCallParams)
   415 	params := make(apiCallParams)
   415 
   416 
   416 	if displayName != nil {
   417 	if displayName != nil {
   417 		params["display_name"] = *displayName
   418 		params["display_name"] = *displayName
   426 			params["locked"] = "false"
   427 			params["locked"] = "false"
   427 		}
   428 		}
   428 	}
   429 	}
   429 
   430 
   430 	var err error
   431 	var err error
   431 	avatar, err = fileToBase64(avatar, nil)
   432 	var avatar, headerImage []byte
   432 	if err != nil {
   433 
   433 		return nil, err
   434 	avatar, err = readFile(avatarImagePath)
   434 	}
   435 	if err != nil {
   435 	headerImage, err = fileToBase64(headerImage, nil)
   436 		return nil, err
       
   437 	}
       
   438 
       
   439 	headerImage, err = readFile(headerImagePath)
   436 	if err != nil {
   440 	if err != nil {
   437 		return nil, err
   441 		return nil, err
   438 	}
   442 	}
   439 
   443 
   440 	var formBuf bytes.Buffer
   444 	var formBuf bytes.Buffer
   441 	w := multipart.NewWriter(&formBuf)
   445 	w := multipart.NewWriter(&formBuf)
   442 
   446 
   443 	if avatar != nil {
   447 	if avatar != nil {
   444 		w.WriteField("avatar", *avatar)
   448 		formWriter, err := w.CreateFormFile("avatar", filepath.Base(*avatarImagePath))
       
   449 		if err != nil {
       
   450 			return nil, errors.Wrap(err, "avatar upload")
       
   451 		}
       
   452 		formWriter.Write(avatar)
   445 	}
   453 	}
   446 	if headerImage != nil {
   454 	if headerImage != nil {
   447 		w.WriteField("header", *headerImage)
   455 		formWriter, err := w.CreateFormFile("header", filepath.Base(*headerImagePath))
       
   456 		if err != nil {
       
   457 			return nil, errors.Wrap(err, "header upload")
       
   458 		}
       
   459 		formWriter.Write(headerImage)
   448 	}
   460 	}
   449 	w.Close()
   461 	w.Close()
   450 
   462 
   451 	// Prepare the request
   463 	// Prepare the request
   452 	req, err := mc.prepareRequest(endPoint, rest.Patch, params)
   464 	req, err := mc.prepareRequest(endPoint, rest.Patch, params)
   477 		return nil, errors.Wrap(err, "cannot decode API response")
   489 		return nil, errors.Wrap(err, "cannot decode API response")
   478 	}
   490 	}
   479 	return &account, nil
   491 	return &account, nil
   480 }
   492 }
   481 
   493 
   482 // fileToBase64 is a helper function to convert a file's contents to
   494 // readFile is a helper function to read a file's contents.
   483 // base64-encoded data.  Is the data string already contains base64 data, it
   495 func readFile(filename *string) ([]byte, error) {
   484 // is not modified.
   496 	if filename == nil || *filename == "" {
   485 // If contentType is nil, it is detected.
       
   486 func fileToBase64(data, contentType *string) (*string, error) {
       
   487 	if data == nil {
       
   488 		return nil, nil
   497 		return nil, nil
   489 	}
   498 	}
   490 
   499 
   491 	if *data == "" {
   500 	file, err := os.Open(*filename)
   492 		return data, nil
       
   493 	}
       
   494 
       
   495 	if strings.Contains(*data, ";base64,") {
       
   496 		return data, nil
       
   497 	}
       
   498 
       
   499 	// We need to convert the file and file name to base64
       
   500 
       
   501 	file, err := os.Open(*data)
       
   502 	if err != nil {
   501 	if err != nil {
   503 		return nil, err
   502 		return nil, err
   504 	}
   503 	}
   505 	defer file.Close()
   504 	defer file.Close()
   506 
   505 
   513 	_, err = file.Read(buffer)
   512 	_, err = file.Read(buffer)
   514 	if err != nil {
   513 	if err != nil {
   515 		return nil, err
   514 		return nil, err
   516 	}
   515 	}
   517 
   516 
   518 	var cType string
   517 	return buffer, nil
   519 	if contentType == nil || *contentType == "" {
   518 }
   520 		cType = http.DetectContentType(buffer[:512])
       
   521 	} else {
       
   522 		cType = *contentType
       
   523 	}
       
   524 	contentData := base64.StdEncoding.EncodeToString(buffer)
       
   525 	newData := "data:" + cType + ";base64," + contentData
       
   526 	return &newData, nil
       
   527 }