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.
		
		
		
		
		
			
		
			
				
	
	
		
			143 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			143 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			JavaScript
		
	
| /* global
 | |
|   Signal,
 | |
|   textsecure,
 | |
|   StringView
 | |
| */
 | |
| 
 | |
| /* eslint-disable no-bitwise */
 | |
| 
 | |
| // eslint-disable-next-line func-names
 | |
| (function() {
 | |
|   'use strict';
 | |
| 
 | |
|   window.Signal = window.Signal || {};
 | |
|   window.Signal.LinkPreviews = window.Signal.LinkPreviews || {};
 | |
| 
 | |
|   async function makeChunkedRequest(url) {
 | |
|     const PARALLELISM = 3;
 | |
|     const size = await textsecure.messaging.getProxiedSize(url);
 | |
|     const chunks = await Signal.LinkPreviews.getChunkPattern(size);
 | |
| 
 | |
|     let results = [];
 | |
|     const jobs = chunks.map(chunk => async () => {
 | |
|       const { start, end } = chunk;
 | |
| 
 | |
|       const result = await textsecure.messaging.makeProxiedRequest(url, {
 | |
|         start,
 | |
|         end,
 | |
|         returnArrayBuffer: true,
 | |
|       });
 | |
| 
 | |
|       return {
 | |
|         ...chunk,
 | |
|         ...result,
 | |
|       };
 | |
|     });
 | |
| 
 | |
|     while (jobs.length > 0) {
 | |
|       const activeJobs = [];
 | |
|       for (let i = 0, max = PARALLELISM; i < max; i += 1) {
 | |
|         if (!jobs.length) {
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         const job = jobs.shift();
 | |
|         activeJobs.push(job());
 | |
|       }
 | |
| 
 | |
|       // eslint-disable-next-line no-await-in-loop
 | |
|       results = results.concat(await Promise.all(activeJobs));
 | |
|     }
 | |
| 
 | |
|     if (!results.length) {
 | |
|       throw new Error('No responses received');
 | |
|     }
 | |
| 
 | |
|     const { contentType } = results[0];
 | |
|     const data = Signal.LinkPreviews.assembleChunks(results);
 | |
| 
 | |
|     return {
 | |
|       contentType,
 | |
|       data,
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   async function sha256(string) {
 | |
|     const arraybuffer = new TextEncoder('utf-8').encode(string);
 | |
|     const digest = await window.crypto.subtle.digest('SHA-256', arraybuffer);
 | |
|     return StringView.arrayBufferToHex(digest);
 | |
|   }
 | |
| 
 | |
|   async function getPreview(url) {
 | |
|     let html;
 | |
|     try {
 | |
|       html = await textsecure.messaging.makeProxiedRequest(url);
 | |
|     } catch (error) {
 | |
|       if (error.code >= 300) {
 | |
|         return null;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const title = Signal.LinkPreviews.getTitleMetaTag(html);
 | |
|     const imageUrl = Signal.LinkPreviews.getImageMetaTag(html);
 | |
| 
 | |
|     let image;
 | |
|     let objectUrl;
 | |
|     try {
 | |
|       if (imageUrl) {
 | |
|         if (!Signal.LinkPreviews.isMediaLinkInWhitelist(imageUrl)) {
 | |
|           const primaryDomain = Signal.LinkPreviews.getDomain(url);
 | |
|           const imageDomain = Signal.LinkPreviews.getDomain(imageUrl);
 | |
|           throw new Error(
 | |
|             `imageUrl for domain ${primaryDomain} did not match media whitelist. Domain: ${imageDomain}`
 | |
|           );
 | |
|         }
 | |
| 
 | |
|         const data = await makeChunkedRequest(imageUrl);
 | |
| 
 | |
|         // Calculate dimensions
 | |
|         const file = new Blob([data.data], {
 | |
|           type: data.contentType,
 | |
|         });
 | |
|         objectUrl = URL.createObjectURL(file);
 | |
| 
 | |
|         const dimensions = await Signal.Types.VisualAttachment.getImageDimensions(
 | |
|           {
 | |
|             objectUrl,
 | |
|             logger: window.log,
 | |
|           }
 | |
|         );
 | |
| 
 | |
|         image = {
 | |
|           ...data,
 | |
|           ...dimensions,
 | |
|           contentType: file.type,
 | |
|         };
 | |
|       }
 | |
|     } catch (error) {
 | |
|       // We still want to show the preview if we failed to get an image
 | |
|       window.log.error(
 | |
|         'getPreview failed to get image for link preview:',
 | |
|         error.message
 | |
|       );
 | |
|     } finally {
 | |
|       if (objectUrl) {
 | |
|         URL.revokeObjectURL(objectUrl);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const hash = await sha256(url);
 | |
| 
 | |
|     return {
 | |
|       title,
 | |
|       url,
 | |
|       image,
 | |
|       hash,
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   window.Signal.LinkPreviews.helper = {
 | |
|     getPreview,
 | |
|   };
 | |
| })();
 |