| 
									
										
										
										
											2021-02-28 15:17:18 +01:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2021-03-01 15:41:43 +01:00
										 |  |  |    GoToSocial | 
					
						
							| 
									
										
										
										
											2021-12-20 18:42:19 +01:00
										 |  |  |    Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org | 
					
						
							| 
									
										
										
										
											2021-02-28 15:17:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-01 15:41:43 +01:00
										 |  |  |    This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |    it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  |    the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  |    (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2021-02-28 15:17:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-01 15:41:43 +01:00
										 |  |  |    This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |    GNU Affero General Public License for more details. | 
					
						
							| 
									
										
										
										
											2021-02-28 15:17:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-01 15:41:43 +01:00
										 |  |  |    You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2021-02-28 15:17:18 +01:00
										 |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:03:40 +01:00
										 |  |  | package media | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-13 12:29:08 +01:00
										 |  |  | 	"codeberg.org/gruf/go-store/kv" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/id" | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/transport" | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/uris" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | type ProcessedCallback func(*gtsmodel.MediaAttachment) error | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 18:14:23 +02:00
										 |  |  | // Handler provides an interface for parsing, storing, and retrieving media objects like photos, videos, and gifs. | 
					
						
							|  |  |  | type Handler interface { | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 	ProcessHeader(ctx context.Context, data []byte, accountID string, cb ProcessedCallback) (*gtsmodel.MediaAttachment, error) | 
					
						
							|  |  |  | 	ProcessAvatar(ctx context.Context, data []byte, accountID string, cb ProcessedCallback) (*gtsmodel.MediaAttachment, error) | 
					
						
							|  |  |  | 	ProcessAttachment(ctx context.Context, data []byte, accountID string, cb ProcessedCallback) (*gtsmodel.MediaAttachment, error) | 
					
						
							|  |  |  | 	ProcessEmoji(ctx context.Context, data []byte, shortcode string) (*gtsmodel.Emoji, error) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type mediaHandler struct { | 
					
						
							| 
									
										
										
										
											2021-09-11 20:18:06 +01:00
										 |  |  | 	db      db.DB | 
					
						
							|  |  |  | 	storage *kv.KVStore | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 13:31:39 +01:00
										 |  |  | // New returns a new handler with the given db and storage | 
					
						
							|  |  |  | func New(database db.DB, storage *kv.KVStore) Handler { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	return &mediaHandler{ | 
					
						
							| 
									
										
										
										
											2021-09-11 20:18:06 +01:00
										 |  |  | 		db:      database, | 
					
						
							|  |  |  | 		storage: storage, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | 	INTERFACE FUNCTIONS | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 18:14:23 +02:00
										 |  |  | // ProcessHeaderOrAvatar takes a new header image for an account, checks it out, removes exif data from it, | 
					
						
							|  |  |  | // puts it in whatever storage backend we're using, sets the relevant fields in the database for the new image, | 
					
						
							|  |  |  | // and then returns information to the caller about the new header. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (mh *mediaHandler) ProcessHeaderOrAvatar(ctx context.Context, attachment []byte, accountID string, mediaType Type, remoteURL string) (*gtsmodel.MediaAttachment, error) { | 
					
						
							| 
									
										
										
										
											2021-10-11 05:37:33 -07:00
										 |  |  | 	l := logrus.WithField("func", "SetHeaderForAccountID") | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	if mediaType != TypeHeader && mediaType != TypeAvatar { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		return nil, errors.New("header or avatar not selected") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	// make sure we have a type we can handle | 
					
						
							|  |  |  | 	contentType, err := parseContentType(attachment) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 	if !supportedImage(contentType) { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		return nil, fmt.Errorf("%s is not an accepted image type", contentType) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	if len(attachment) == 0 { | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 		return nil, fmt.Errorf("passed reader was of size 0") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	l.Tracef("read %d bytes of file", len(attachment)) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// process it | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | 	ma, err := mh.processHeaderOrAvi(attachment, contentType, mediaType, accountID, remoteURL) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		return nil, fmt.Errorf("error processing %s: %s", mediaType, err) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// set it in the database | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	if err := mh.db.SetAccountHeaderOrAvatar(ctx, ma, accountID); err != nil { | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		return nil, fmt.Errorf("error putting %s in database: %s", mediaType, err) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ma, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | // ProcessAttachment takes a new attachment and the owning account, checks it out, removes exif data from it, | 
					
						
							| 
									
										
										
										
											2021-04-20 18:14:23 +02:00
										 |  |  | // puts it in whatever storage backend we're using, sets the relevant fields in the database for the new media, | 
					
						
							|  |  |  | // and then returns information to the caller about the attachment. | 
					
						
							| 
									
										
										
										
											2021-09-04 14:02:01 +02:00
										 |  |  | func (mh *mediaHandler) ProcessAttachment(ctx context.Context, attachmentBytes []byte, minAttachment *gtsmodel.MediaAttachment) (*gtsmodel.MediaAttachment, error) { | 
					
						
							|  |  |  | 	contentType, err := parseContentType(attachmentBytes) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-04 14:02:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	minAttachment.File.ContentType = contentType | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	mainType := strings.Split(contentType, "/")[0] | 
					
						
							|  |  |  | 	switch mainType { | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	// case MIMEVideo: | 
					
						
							|  |  |  | 	// 	if !SupportedVideoType(contentType) { | 
					
						
							|  |  |  | 	// 		return nil, fmt.Errorf("video type %s not supported", contentType) | 
					
						
							|  |  |  | 	// 	} | 
					
						
							|  |  |  | 	// 	if len(attachment) == 0 { | 
					
						
							|  |  |  | 	// 		return nil, errors.New("video was of size 0") | 
					
						
							|  |  |  | 	// 	} | 
					
						
							|  |  |  | 	// 	return mh.processVideoAttachment(attachment, accountID, contentType, remoteURL) | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 	case mimeImage: | 
					
						
							|  |  |  | 		if !supportedImage(contentType) { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 			return nil, fmt.Errorf("image type %s not supported", contentType) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-04 14:02:01 +02:00
										 |  |  | 		if len(attachmentBytes) == 0 { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 			return nil, errors.New("image was of size 0") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-04 14:02:01 +02:00
										 |  |  | 		return mh.processImageAttachment(attachmentBytes, minAttachment) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, fmt.Errorf("content type %s not (yet) supported", contentType) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-20 18:14:23 +02:00
										 |  |  | // ProcessLocalEmoji takes a new emoji and a shortcode, cleans it up, puts it in storage, and creates a new | 
					
						
							|  |  |  | // *gts.Emoji for it, then returns it to the caller. It's the caller's responsibility to put the returned struct | 
					
						
							|  |  |  | // in the database. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (mh *mediaHandler) ProcessLocalEmoji(ctx context.Context, emojiBytes []byte, shortcode string) (*gtsmodel.Emoji, error) { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	var clean []byte | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	var original *imageAndMeta | 
					
						
							|  |  |  | 	var static *imageAndMeta | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// check content type of the submitted emoji and make sure it's supported by us | 
					
						
							|  |  |  | 	contentType, err := parseContentType(emojiBytes) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 	if !supportedEmoji(contentType) { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		return nil, fmt.Errorf("content type %s not supported for emojis", contentType) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(emojiBytes) == 0 { | 
					
						
							|  |  |  | 		return nil, errors.New("emoji was of size 0") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(emojiBytes) > EmojiMaxBytes { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("emoji size %d bytes exceeded max emoji size of %d bytes", len(emojiBytes), EmojiMaxBytes) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 	// clean any exif data from png but leave gifs alone | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	switch contentType { | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 	case mimePng: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		if clean, err = purgeExif(emojiBytes); err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("error cleaning exif data: %s", err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 	case mimeGif: | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		clean = emojiBytes | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, errors.New("media type unrecognized") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// unlike with other attachments we don't need to derive anything here because we don't care about the width/height etc | 
					
						
							|  |  |  | 	original = &imageAndMeta{ | 
					
						
							|  |  |  | 		image: clean, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	static, err = deriveStaticEmoji(clean, contentType) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("error deriving static emoji: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// since emoji aren't 'owned' by an account, but we still want to use the same pattern for serving them through the filserver, | 
					
						
							|  |  |  | 	// (ie., fileserver/ACCOUNT_ID/etc etc) we need to fetch the INSTANCE ACCOUNT from the database. That is, the account that's created | 
					
						
							|  |  |  | 	// with the same username as the instance hostname, which doesn't belong to any particular user. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	instanceAccount, err := mh.db.GetInstanceAccount(ctx, "") | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		return nil, fmt.Errorf("error fetching instance account: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the file extension (either png or gif) | 
					
						
							|  |  |  | 	extension := strings.Split(contentType, "/")[1] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	// generate a ulid for the new emoji | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	newEmojiID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	// activitypub uri for the emoji -- unrelated to actually serving the image | 
					
						
							|  |  |  | 	// will be something like https://example.org/emoji/01FPSVBK3H8N7V8XK6KGSQ86EC | 
					
						
							|  |  |  | 	emojiURI := uris.GenerateURIForEmoji(newEmojiID) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// serve url and storage path for the original emoji -- can be png or gif | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	emojiURL := uris.GenerateURIForAttachment(instanceAccount.ID, string(TypeEmoji), string(SizeOriginal), newEmojiID, extension) | 
					
						
							|  |  |  | 	emojiPath := fmt.Sprintf("%s/%s/%s/%s.%s", instanceAccount.ID, TypeEmoji, SizeOriginal, newEmojiID, extension) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// serve url and storage path for the static version -- will always be png | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 	emojiStaticURL := uris.GenerateURIForAttachment(instanceAccount.ID, string(TypeEmoji), string(SizeStatic), newEmojiID, "png") | 
					
						
							|  |  |  | 	emojiStaticPath := fmt.Sprintf("%s/%s/%s/%s.png", instanceAccount.ID, TypeEmoji, SizeStatic, newEmojiID) | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-11 20:12:47 +01:00
										 |  |  | 	// Store the original emoji | 
					
						
							| 
									
										
										
										
											2021-09-11 20:18:06 +01:00
										 |  |  | 	if err := mh.storage.Put(emojiPath, original.image); err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		return nil, fmt.Errorf("storage error: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-11 20:12:47 +01:00
										 |  |  | 	// Store the static emoji | 
					
						
							| 
									
										
										
										
											2021-09-11 20:18:06 +01:00
										 |  |  | 	if err := mh.storage.Put(emojiStaticPath, static.image); err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 		return nil, fmt.Errorf("storage error: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// and finally return the new emoji data to the caller -- it's up to them what to do with it | 
					
						
							|  |  |  | 	e := >smodel.Emoji{ | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		ID:                     newEmojiID, | 
					
						
							|  |  |  | 		Shortcode:              shortcode, | 
					
						
							|  |  |  | 		Domain:                 "", // empty because this is a local emoji | 
					
						
							|  |  |  | 		CreatedAt:              time.Now(), | 
					
						
							|  |  |  | 		UpdatedAt:              time.Now(), | 
					
						
							|  |  |  | 		ImageRemoteURL:         "", // empty because this is a local emoji | 
					
						
							|  |  |  | 		ImageStaticRemoteURL:   "", // empty because this is a local emoji | 
					
						
							|  |  |  | 		ImageURL:               emojiURL, | 
					
						
							|  |  |  | 		ImageStaticURL:         emojiStaticURL, | 
					
						
							|  |  |  | 		ImagePath:              emojiPath, | 
					
						
							|  |  |  | 		ImageStaticPath:        emojiStaticPath, | 
					
						
							|  |  |  | 		ImageContentType:       contentType, | 
					
						
							| 
									
										
										
										
											2021-12-27 18:03:36 +01:00
										 |  |  | 		ImageStaticContentType: mimePng, // static version will always be a png | 
					
						
							| 
									
										
										
										
											2021-05-08 14:25:55 +02:00
										 |  |  | 		ImageFileSize:          len(original.image), | 
					
						
							|  |  |  | 		ImageStaticFileSize:    len(static.image), | 
					
						
							|  |  |  | 		ImageUpdatedAt:         time.Now(), | 
					
						
							|  |  |  | 		Disabled:               false, | 
					
						
							|  |  |  | 		URI:                    emojiURI, | 
					
						
							|  |  |  | 		VisibleInPicker:        true, | 
					
						
							|  |  |  | 		CategoryID:             "", // empty because this is a new emoji -- no category yet | 
					
						
							| 
									
										
										
										
											2021-04-19 19:42:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return e, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (mh *mediaHandler) ProcessRemoteHeaderOrAvatar(ctx context.Context, t transport.Transport, currentAttachment *gtsmodel.MediaAttachment, accountID string) (*gtsmodel.MediaAttachment, error) { | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | 	if !currentAttachment.Header && !currentAttachment.Avatar { | 
					
						
							|  |  |  | 		return nil, errors.New("provided attachment was set to neither header nor avatar") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if currentAttachment.Header && currentAttachment.Avatar { | 
					
						
							|  |  |  | 		return nil, errors.New("provided attachment was set to both header and avatar") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var headerOrAvi Type | 
					
						
							|  |  |  | 	if currentAttachment.Header { | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 		headerOrAvi = TypeHeader | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | 	} else if currentAttachment.Avatar { | 
					
						
							| 
									
										
										
										
											2021-12-20 15:19:53 +01:00
										 |  |  | 		headerOrAvi = TypeAvatar | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if currentAttachment.RemoteURL == "" { | 
					
						
							|  |  |  | 		return nil, errors.New("no remote URL on media attachment to dereference") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	remoteIRI, err := url.Parse(currentAttachment.RemoteURL) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("error parsing attachment url %s: %s", currentAttachment.RemoteURL, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// for content type, we assume we don't know what to expect... | 
					
						
							|  |  |  | 	expectedContentType := "*/*" | 
					
						
							|  |  |  | 	if currentAttachment.File.ContentType != "" { | 
					
						
							|  |  |  | 		// ... and then narrow it down if we do | 
					
						
							|  |  |  | 		expectedContentType = currentAttachment.File.ContentType | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-04 15:24:19 +02:00
										 |  |  | 	attachmentBytes, err := t.DereferenceMedia(ctx, remoteIRI, expectedContentType) | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("dereferencing remote media with url %s: %s", remoteIRI.String(), err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	return mh.ProcessHeaderOrAvatar(ctx, attachmentBytes, accountID, headerOrAvi, currentAttachment.RemoteURL) | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | } |