| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  | // Copyright 2024 Signal Messenger, LLC
 | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 12:22:48 -07:00
										 |  |  | import { type Readable } from 'node:stream'; | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  | import { strictAssert } from '../../util/assert'; | 
					
						
							|  |  |  | import type { | 
					
						
							|  |  |  |   WebAPIType, | 
					
						
							| 
									
										
										
										
											2024-07-16 16:25:43 -04:00
										 |  |  |   AttachmentUploadFormResponseType, | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |   GetBackupInfoResponseType, | 
					
						
							|  |  |  |   BackupMediaItemType, | 
					
						
							|  |  |  |   BackupMediaBatchResponseType, | 
					
						
							|  |  |  |   BackupListMediaResponseType, | 
					
						
							|  |  |  | } from '../../textsecure/WebAPI'; | 
					
						
							|  |  |  | import type { BackupCredentials } from './credentials'; | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  | import { BackupCredentialType } from '../../types/backups'; | 
					
						
							| 
									
										
										
										
											2024-05-20 15:29:20 -04:00
										 |  |  | import { uploadFile } from '../../util/uploadAttachment'; | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-27 17:00:41 -04:00
										 |  |  | export type DownloadOptionsType = Readonly<{ | 
					
						
							|  |  |  |   downloadOffset: number; | 
					
						
							|  |  |  |   onProgress: (currentBytes: number, totalBytes: number) => void; | 
					
						
							| 
									
										
										
										
											2024-09-11 11:03:18 -07:00
										 |  |  |   abortSignal?: AbortSignal; | 
					
						
							| 
									
										
										
										
											2024-08-27 17:00:41 -04:00
										 |  |  | }>; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  | export class BackupAPI { | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |   private cachedBackupInfo = new Map< | 
					
						
							|  |  |  |     BackupCredentialType, | 
					
						
							|  |  |  |     GetBackupInfoResponseType | 
					
						
							|  |  |  |   >(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor(private readonly credentials: BackupCredentials) {} | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   public async refresh(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     const headers = await Promise.all( | 
					
						
							|  |  |  |       [BackupCredentialType.Messages, BackupCredentialType.Media].map(type => | 
					
						
							|  |  |  |         this.credentials.getHeadersForToday(type) | 
					
						
							|  |  |  |       ) | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     await Promise.all(headers.map(h => this.server.refreshBackup(h))); | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |   public async getInfo( | 
					
						
							|  |  |  |     credentialType: BackupCredentialType | 
					
						
							|  |  |  |   ): Promise<GetBackupInfoResponseType> { | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |     const backupInfo = await this.server.getBackupInfo( | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |       await this.credentials.getHeadersForToday(credentialType) | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     this.cachedBackupInfo.set(credentialType, backupInfo); | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |     return backupInfo; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |   private async getCachedInfo( | 
					
						
							|  |  |  |     credentialType: BackupCredentialType | 
					
						
							|  |  |  |   ): Promise<GetBackupInfoResponseType> { | 
					
						
							|  |  |  |     const cached = this.cachedBackupInfo.get(credentialType); | 
					
						
							|  |  |  |     if (cached) { | 
					
						
							|  |  |  |       return cached; | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     return this.getInfo(credentialType); | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public async getMediaDir(): Promise<string> { | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     return (await this.getCachedInfo(BackupCredentialType.Media)).mediaDir; | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public async getBackupDir(): Promise<string> { | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     return (await this.getCachedInfo(BackupCredentialType.Media))?.backupDir; | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-14 10:04:50 -07:00
										 |  |  |   public async upload(filePath: string, fileSize: number): Promise<void> { | 
					
						
							|  |  |  |     const form = await this.server.getBackupUploadForm( | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |       await this.credentials.getHeadersForToday(BackupCredentialType.Messages) | 
					
						
							| 
									
										
										
										
											2024-05-14 10:04:50 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-20 15:29:20 -04:00
										 |  |  |     await uploadFile({ | 
					
						
							|  |  |  |       absoluteCiphertextPath: filePath, | 
					
						
							|  |  |  |       ciphertextFileSize: fileSize, | 
					
						
							|  |  |  |       uploadForm: form, | 
					
						
							| 
									
										
										
										
											2024-05-06 09:03:27 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-27 17:00:41 -04:00
										 |  |  |   public async download({ | 
					
						
							|  |  |  |     downloadOffset, | 
					
						
							|  |  |  |     onProgress, | 
					
						
							| 
									
										
										
										
											2024-09-11 11:03:18 -07:00
										 |  |  |     abortSignal, | 
					
						
							| 
									
										
										
										
											2024-08-27 17:00:41 -04:00
										 |  |  |   }: DownloadOptionsType): Promise<Readable> { | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     const { cdn, backupDir, backupName } = await this.getInfo( | 
					
						
							|  |  |  |       BackupCredentialType.Messages | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     const { headers } = await this.credentials.getCDNReadCredentials( | 
					
						
							|  |  |  |       cdn, | 
					
						
							|  |  |  |       BackupCredentialType.Messages | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2024-08-08 12:22:48 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return this.server.getBackupStream({ | 
					
						
							|  |  |  |       cdn, | 
					
						
							|  |  |  |       backupDir, | 
					
						
							|  |  |  |       backupName, | 
					
						
							|  |  |  |       headers, | 
					
						
							| 
									
										
										
										
											2024-08-27 17:00:41 -04:00
										 |  |  |       downloadOffset, | 
					
						
							| 
									
										
										
										
											2024-10-18 10:15:03 -07:00
										 |  |  |       onProgress, | 
					
						
							|  |  |  |       abortSignal, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public async downloadEphemeral({ | 
					
						
							|  |  |  |     downloadOffset, | 
					
						
							|  |  |  |     onProgress, | 
					
						
							|  |  |  |     abortSignal, | 
					
						
							|  |  |  |   }: DownloadOptionsType): Promise<Readable> { | 
					
						
							|  |  |  |     const { cdn, key } = await this.server.getTransferArchive({ | 
					
						
							|  |  |  |       abortSignal, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return this.server.getEphemeralBackupStream({ | 
					
						
							|  |  |  |       cdn, | 
					
						
							|  |  |  |       key, | 
					
						
							|  |  |  |       downloadOffset, | 
					
						
							| 
									
										
										
										
											2024-08-27 17:00:41 -04:00
										 |  |  |       onProgress, | 
					
						
							| 
									
										
										
										
											2024-09-11 11:03:18 -07:00
										 |  |  |       abortSignal, | 
					
						
							| 
									
										
										
										
											2024-08-08 12:22:48 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 16:25:43 -04:00
										 |  |  |   public async getMediaUploadForm(): Promise<AttachmentUploadFormResponseType> { | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |     return this.server.getBackupMediaUploadForm( | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |       await this.credentials.getHeadersForToday(BackupCredentialType.Media) | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public async backupMediaBatch( | 
					
						
							|  |  |  |     items: ReadonlyArray<BackupMediaItemType> | 
					
						
							|  |  |  |   ): Promise<BackupMediaBatchResponseType> { | 
					
						
							|  |  |  |     return this.server.backupMediaBatch({ | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |       headers: await this.credentials.getHeadersForToday( | 
					
						
							|  |  |  |         BackupCredentialType.Media | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |       items, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public async listMedia({ | 
					
						
							|  |  |  |     cursor, | 
					
						
							|  |  |  |     limit, | 
					
						
							|  |  |  |   }: { | 
					
						
							|  |  |  |     cursor?: string; | 
					
						
							|  |  |  |     limit: number; | 
					
						
							|  |  |  |   }): Promise<BackupListMediaResponseType> { | 
					
						
							|  |  |  |     return this.server.backupListMedia({ | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |       headers: await this.credentials.getHeadersForToday( | 
					
						
							|  |  |  |         BackupCredentialType.Media | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |       cursor, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |   public clearCache(): void { | 
					
						
							| 
									
										
										
										
											2024-10-31 10:01:03 -07:00
										 |  |  |     this.cachedBackupInfo.clear(); | 
					
						
							| 
									
										
										
										
											2024-05-02 13:11:34 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-22 16:11:36 +02:00
										 |  |  |   private get server(): WebAPIType { | 
					
						
							|  |  |  |     const { server } = window.textsecure; | 
					
						
							|  |  |  |     strictAssert(server, 'server not available'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return server; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |