2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								/ *  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								   GoToSocial 
							 
						 
					
						
							
								
									
										
										
										
											2021-12-20 18:42:19 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								   Copyright  ( C )  2021 - 2022  GoToSocial  Authors  admin @ gotosocial . org 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02: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 . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								   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 . 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								   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/>. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								* /  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								package  media  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  (  
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"bufio" 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-25 15:34:33 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"context" 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"fmt" 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"io" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"net/url" 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"strings" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									apimodel  "github.com/superseriousbusiness/gotosocial/internal/api/model" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/gtserror" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/media" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								)  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( p  * processor )  GetFile ( ctx  context . Context ,  account  * gtsmodel . Account ,  form  * apimodel . GetContentRequestForm )  ( * apimodel . Content ,  gtserror . WithCode )  {  
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									// parse the form fields 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									mediaSize ,  err  :=  media . ParseMediaSize ( form . MediaSize ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "media size %s not valid" ,  form . MediaSize ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									mediaType ,  err  :=  media . ParseMediaType ( form . MediaType ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "media type %s not valid" ,  form . MediaType ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									spl  :=  strings . Split ( form . FileName ,  "." ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  len ( spl )  !=  2  ||  spl [ 0 ]  ==  ""  ||  spl [ 1 ]  ==  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "file name %s not parseable" ,  form . FileName ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									wantedMediaID  :=  spl [ 0 ] 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									expectedAccountID  :=  form . AccountID 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// get the account that owns the media and make sure it's not suspended 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									acct ,  err  :=  p . db . GetAccountByID ( ctx ,  expectedAccountID ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-25 15:34:33 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "account with id %s could not be selected from the db: %s" ,  expectedAccountID ,  err ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  ! acct . SuspendedAt . IsZero ( )  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "account with id %s is suspended" ,  expectedAccountID ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// make sure the requesting account and the media account don't block each other 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  account  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										blocked ,  err  :=  p . db . IsBlocked ( ctx ,  account . ID ,  expectedAccountID ,  true ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "block status could not be established between accounts %s and %s: %s" ,  expectedAccountID ,  account . ID ,  err ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  blocked  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "block exists between accounts %s and %s" ,  expectedAccountID ,  account . ID ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// the way we store emojis is a little different from the way we store other attachments, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// so we need to take different steps depending on the media type being requested 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									switch  mediaType  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-12-20 15:19:53 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									case  media . TypeEmoji : 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  p . getEmojiContent ( ctx ,  wantedMediaID ,  mediaSize ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-12-20 15:19:53 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									case  media . TypeAttachment ,  media . TypeHeader ,  media . TypeAvatar : 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  p . getAttachmentContent ( ctx ,  account ,  wantedMediaID ,  expectedAccountID ,  mediaSize ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									default : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "media type %s not recognized" ,  mediaType ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( p  * processor )  getAttachmentContent ( ctx  context . Context ,  requestingAccount  * gtsmodel . Account ,  wantedMediaID  string ,  expectedAccountID  string ,  mediaSize  media . Size )  ( * apimodel . Content ,  gtserror . WithCode )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									attachmentContent  :=  & apimodel . Content { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  storagePath  string 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// retrieve attachment from the database and do basic checks on it 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									a ,  err  :=  p . db . GetAttachmentByID ( ctx ,  wantedMediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "attachment %s could not be taken from the db: %s" ,  wantedMediaID ,  err ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  a . AccountID  !=  expectedAccountID  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "attachment %s is not owned by %s" ,  wantedMediaID ,  expectedAccountID ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// get file information from the attachment depending on the requested media size 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									switch  mediaSize  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  media . SizeOriginal : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachmentContent . ContentType  =  a . File . ContentType 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachmentContent . ContentLength  =  int64 ( a . File . FileSize ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										storagePath  =  a . File . Path 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  media . SizeSmall : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachmentContent . ContentType  =  a . Thumbnail . ContentType 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachmentContent . ContentLength  =  int64 ( a . Thumbnail . FileSize ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										storagePath  =  a . Thumbnail . Path 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									default : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "media size %s not recognized for attachment" ,  mediaSize ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// if we have the media cached on our server already, we can now simply return it from storage 
							 
						 
					
						
							
								
									
										
										
										
											2022-08-15 12:35:05 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  * a . Cached  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-03 12:08:30 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  p . retrieveFromStorage ( ctx ,  storagePath ,  attachmentContent ) 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// if we don't have it cached, then we can assume two things: 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 1. this is remote media, since local media should never be uncached 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 2. we need to fetch it again using a transport and the media manager 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									remoteMediaIRI ,  err  :=  url . Parse ( a . RemoteURL ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "error parsing remote media iri %s: %s" ,  a . RemoteURL ,  err ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// use an empty string as requestingUsername to use the instance account, unless the request for this 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// media has been http signed, then use the requesting account to make the request to remote server 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  requestingUsername  string 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  requestingAccount  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										requestingUsername  =  requestingAccount . Username 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  data  media . DataFunc 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  postDataCallback  media . PostDataCallbackFunc 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  mediaSize  ==  media . SizeSmall  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// if it's the thumbnail that's requested then the user will have to wait a bit while we process the 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// large version and derive a thumbnail from it, so use the normal recaching procedure: fetch the media, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// process it, then return the thumbnail data 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										data  =  func ( innerCtx  context . Context )  ( io . Reader ,  int ,  error )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											transport ,  err  :=  p . transportController . NewTransportForUsername ( innerCtx ,  requestingUsername ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												return  nil ,  0 ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  transport . DereferenceMedia ( innerCtx ,  remoteMediaIRI ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// if it's the full-sized version being requested, we can cheat a bit by streaming data to the user as 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// it's retrieved from the remote server, using tee; this saves the user from having to wait while 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// we process the media on our side 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// this looks a bit like this: 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										//                http fetch                   buffered pipe 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// remote server ------------> data function ----------------> api caller 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										//                                   | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										//                                   | tee 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										//                                   | 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										//                                   ▼ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										//                            instance storage 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Buffer each end of the pipe, so that if the caller drops the connection during the flow, the tee 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// reader can continue without having to worry about tee-ing into a closed or blocked pipe. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										pipeReader ,  pipeWriter  :=  io . Pipe ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										bufferedWriter  :=  bufio . NewWriterSize ( pipeWriter ,  int ( attachmentContent . ContentLength ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										bufferedReader  :=  bufio . NewReaderSize ( pipeReader ,  int ( attachmentContent . ContentLength ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// the caller will read from the buffered reader, so it doesn't matter if they drop out without reading everything 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachmentContent . Content  =  bufferedReader 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										data  =  func ( innerCtx  context . Context )  ( io . Reader ,  int ,  error )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											transport ,  err  :=  p . transportController . NewTransportForUsername ( innerCtx ,  requestingUsername ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												return  nil ,  0 ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											readCloser ,  fileSize ,  err  :=  transport . DereferenceMedia ( innerCtx ,  remoteMediaIRI ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												return  nil ,  0 ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-03-21 13:41:44 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											// Make a TeeReader so that everything read from the readCloser by the media manager will be written into the bufferedWriter. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											// We wrap this in a teeReadCloser which implements io.ReadCloser, so that whoever uses the teeReader can close the readCloser 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											// when they're done with it. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											trc  :=  teeReadCloser { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												teeReader :  io . TeeReader ( readCloser ,  bufferedWriter ) , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												close :      readCloser . Close , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  trc ,  fileSize ,  nil 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// close the pipewriter after data has been piped into it, so the reader on the other side doesn't block; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// we don't need to close the reader here because that's the caller's responsibility 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										postDataCallback  =  func ( innerCtx  context . Context )  error  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											// flush the buffered writer into the buffer of the reader... 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  :=  bufferedWriter . Flush ( ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												return  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											// and close the underlying pipe writer 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  :=  pipeWriter . Close ( ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												return  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// put the media recached in the queue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									processingMedia ,  err  :=  p . mediaManager . RecacheMedia ( ctx ,  data ,  postDataCallback ,  wantedMediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "error recaching media: %s" ,  err ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// if it's the thumbnail, stream the processed thumbnail from storage, after waiting for processing to finish 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  mediaSize  ==  media . SizeSmall  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// below function call blocks until all processing on the attachment has finished... 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  _ ,  err  :=  processingMedia . LoadAttachment ( ctx ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "error loading recached attachment: %s" ,  err ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// ... so now we can safely return it 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-03 12:08:30 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  p . retrieveFromStorage ( ctx ,  storagePath ,  attachmentContent ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  attachmentContent ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( p  * processor )  getEmojiContent ( ctx  context . Context ,  wantedEmojiID  string ,  emojiSize  media . Size )  ( * apimodel . Content ,  gtserror . WithCode )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									emojiContent  :=  & apimodel . Content { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  storagePath  string 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-09-06 12:42:55 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									e ,  err  :=  p . db . GetEmojiByID ( ctx ,  wantedEmojiID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "emoji %s could not be taken from the db: %s" ,  wantedEmojiID ,  err ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-08-15 12:35:05 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  * e . Disabled  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "emoji %s has been disabled" ,  wantedEmojiID ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									switch  emojiSize  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  media . SizeOriginal : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										emojiContent . ContentType  =  e . ImageContentType 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										emojiContent . ContentLength  =  int64 ( e . ImageFileSize ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										storagePath  =  e . ImagePath 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  media . SizeStatic : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										emojiContent . ContentType  =  e . ImageStaticContentType 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										emojiContent . ContentLength  =  int64 ( e . ImageStaticFileSize ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										storagePath  =  e . ImageStaticPath 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									default : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "media size %s not recognized for emoji" ,  emojiSize ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-03 12:08:30 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  p . retrieveFromStorage ( ctx ,  storagePath ,  emojiContent ) 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-07 11:08:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-03 12:08:30 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( p  * processor )  retrieveFromStorage ( ctx  context . Context ,  storagePath  string ,  content  * apimodel . Content )  ( * apimodel . Content ,  gtserror . WithCode )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  url  :=  p . storage . URL ( ctx ,  storagePath ) ;  url  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										content . URL  =  url 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  content ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									reader ,  err  :=  p . storage . GetStream ( ctx ,  storagePath ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorNotFound ( fmt . Errorf ( "error retrieving from storage: %s" ,  err ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-02-19 11:44:56 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									content . Content  =  reader 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-05 13:23:03 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									return  content ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}