You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			242 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			242 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
| import { MessageModel } from '../../js/models/message';
 | |
| 
 | |
| // TODO: Might convert it to a class later
 | |
| let webAPI: any;
 | |
| 
 | |
| export async function downloadAttachment(attachment: any) {
 | |
|   const _ = window.Lodash;
 | |
| 
 | |
|   if (!webAPI) {
 | |
|     webAPI = window.WebAPI.connect();
 | |
|   }
 | |
| 
 | |
|   // The attachment id is actually just the absolute url of the attachment
 | |
|   let data = await webAPI.getAttachment(attachment.url);
 | |
|   if (!attachment.isRaw) {
 | |
|     const { key, digest, size } = attachment;
 | |
| 
 | |
|     data = await window.textsecure.crypto.decryptAttachment(
 | |
|       data,
 | |
|       window.Signal.Crypto.base64ToArrayBuffer(key),
 | |
|       window.Signal.Crypto.base64ToArrayBuffer(digest)
 | |
|     );
 | |
| 
 | |
|     if (!size || size !== data.byteLength) {
 | |
|       throw new Error(
 | |
|         `downloadAttachment: Size ${size} did not match downloaded attachment size ${data.byteLength}`
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     ..._.omit(attachment, 'digest', 'key'),
 | |
|     data,
 | |
|   };
 | |
| }
 | |
| 
 | |
| async function processLongAttachments(
 | |
|   message: MessageModel,
 | |
|   attachments: Array<any>
 | |
| ): Promise<boolean> {
 | |
|   if (attachments.length === 0) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (attachments.length > 1) {
 | |
|     window.log.error(
 | |
|       `Received more than one long message attachment in message ${message.idForLogging()}`
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   const attachment = attachments[0];
 | |
| 
 | |
|   message.set({ bodyPending: true });
 | |
|   await window.Signal.AttachmentDownloads.addJob(attachment, {
 | |
|     messageId: message.id,
 | |
|     type: 'long-message',
 | |
|     index: 0,
 | |
|   });
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| async function processNormalAttachments(
 | |
|   message: MessageModel,
 | |
|   normalAttachments: Array<any>
 | |
| ): Promise<number> {
 | |
|   const attachments = await Promise.all(
 | |
|     normalAttachments.map((attachment: any, index: any) => {
 | |
|       return window.Signal.AttachmentDownloads.addJob(attachment, {
 | |
|         messageId: message.id,
 | |
|         type: 'attachment',
 | |
|         index,
 | |
|       });
 | |
|     })
 | |
|   );
 | |
| 
 | |
|   message.set({ attachments });
 | |
| 
 | |
|   return attachments.length;
 | |
| }
 | |
| 
 | |
| async function processPreviews(message: MessageModel): Promise<number> {
 | |
|   let addedCount = 0;
 | |
| 
 | |
|   const preview = await Promise.all(
 | |
|     (message.get('preview') || []).map(async (item: any, index: any) => {
 | |
|       if (!item.image) {
 | |
|         return item;
 | |
|       }
 | |
|       addedCount += 1;
 | |
| 
 | |
|       const image = await window.Signal.AttachmentDownloads.addJob(item.image, {
 | |
|         messageId: message.id,
 | |
|         type: 'preview',
 | |
|         index,
 | |
|       });
 | |
| 
 | |
|       return { ...item, image };
 | |
|     })
 | |
|   );
 | |
| 
 | |
|   message.set({ preview });
 | |
| 
 | |
|   return addedCount;
 | |
| }
 | |
| 
 | |
| async function processAvatars(message: MessageModel): Promise<number> {
 | |
|   let addedCount = 0;
 | |
| 
 | |
|   const contacts = message.get('contact') || [];
 | |
| 
 | |
|   const contact = await Promise.all(
 | |
|     contacts.map(async (item: any, index: any) => {
 | |
|       if (!item.avatar || !item.avatar.avatar) {
 | |
|         return item;
 | |
|       }
 | |
| 
 | |
|       addedCount += 1;
 | |
| 
 | |
|       const avatarJob = await window.Signal.AttachmentDownloads.addJob(
 | |
|         item.avatar.avatar,
 | |
|         {
 | |
|           messaeId: message.id,
 | |
|           type: 'contact',
 | |
|           index,
 | |
|         }
 | |
|       );
 | |
| 
 | |
|       return {
 | |
|         ...item,
 | |
|         avatar: {
 | |
|           ...item.avatar,
 | |
|           avatar: avatarJob,
 | |
|         },
 | |
|       };
 | |
|     })
 | |
|   );
 | |
| 
 | |
|   message.set({ contact });
 | |
| 
 | |
|   return addedCount;
 | |
| }
 | |
| 
 | |
| async function processQuoteAttachments(message: MessageModel): Promise<number> {
 | |
|   let addedCount = 0;
 | |
| 
 | |
|   const quote = message.get('quote');
 | |
| 
 | |
|   if (!quote || !quote.attachments || !quote.attachments.length) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   quote.attachments = await Promise.all(
 | |
|     quote.attachments.map(async (item: any, index: any) => {
 | |
|       // If we already have a path, then we copied this image from the quoted
 | |
|       //    message and we don't need to download the attachment.
 | |
|       if (!item.thumbnail || item.thumbnail.path) {
 | |
|         return item;
 | |
|       }
 | |
| 
 | |
|       addedCount += 1;
 | |
| 
 | |
|       const thumbnail = await window.Signal.AttachmentDownloads.addJob(
 | |
|         item.thumbnail,
 | |
|         {
 | |
|           messageId: message.id,
 | |
|           type: 'quote',
 | |
|           index,
 | |
|         }
 | |
|       );
 | |
| 
 | |
|       return { ...item, thumbnail };
 | |
|     })
 | |
|   );
 | |
| 
 | |
|   message.set({ quote });
 | |
| 
 | |
|   return addedCount;
 | |
| }
 | |
| 
 | |
| async function processGroupAvatar(message: MessageModel): Promise<boolean> {
 | |
|   let group = message.get('group');
 | |
| 
 | |
|   if (!group || !group.avatar) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   group = {
 | |
|     ...group,
 | |
|     avatar: await window.Signal.AttachmentDownloads.addJob(group.avatar, {
 | |
|       messageId: message.id,
 | |
|       type: 'group-avatar',
 | |
|       index: 0,
 | |
|     }),
 | |
|   };
 | |
| 
 | |
|   message.set({ group });
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| export async function queueAttachmentDownloads(
 | |
|   message: MessageModel
 | |
| ): Promise<boolean> {
 | |
|   const _ = window.Lodash;
 | |
|   const { Whisper } = window;
 | |
| 
 | |
|   let count = 0;
 | |
| 
 | |
|   const [longMessageAttachments, normalAttachments] = _.partition(
 | |
|     message.get('attachments') || [],
 | |
|     (attachment: any) =>
 | |
|       attachment.contentType === Whisper.Message.LONG_MESSAGE_CONTENT_TYPE
 | |
|   );
 | |
| 
 | |
|   if (await processLongAttachments(message, longMessageAttachments)) {
 | |
|     count += 1;
 | |
|   }
 | |
| 
 | |
|   count += await processNormalAttachments(message, normalAttachments);
 | |
| 
 | |
|   count += await processPreviews(message);
 | |
| 
 | |
|   count += await processAvatars(message);
 | |
| 
 | |
|   count += await processQuoteAttachments(message);
 | |
| 
 | |
|   if (await processGroupAvatar(message)) {
 | |
|     count += 1;
 | |
|   }
 | |
| 
 | |
|   if (count > 0) {
 | |
|     await window.Signal.Data.saveMessage(message.attributes, {
 | |
|       Message: Whisper.Message,
 | |
|     });
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 |