import { HttpClient, HttpEvent, HttpEventType, HttpHeaders, HttpRequest, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { FileStatus, FileUploadEvent } from '@core/typings/file.typing'; import { VirusScanResult, VirusScanResultType } from '@core/typings/virus-scan.typing'; import { I18nService } from '@yourcause/common/i18n'; import { NotifierService } from '@yourcause/common/notifier'; import { filter, map, mergeMap, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class VirusScanService { constructor ( private i18n: I18nService, private http: HttpClient, private notifier: NotifierService ) { } private fetchVirusScanToken () { const tokenRequest = this.http.post<{ fileScanToken: string }>('/api/token/FileScanToken', null); return tokenRequest; } scanFileForVirusesNew (file: File | Blob): Observable> { return this.fetchVirusScanToken() .pipe(mergeMap((token) => { const virusBody = new FormData(); virusBody.append('file', file); virusBody.append('ApiKey', token.fileScanToken); const virusRequest = new HttpRequest('POST', '/filescan/upload', virusBody, { reportProgress: true, headers: new HttpHeaders({ 'ngsw-bypass': 'true' }), responseType: 'text' }); return this.http.request(virusRequest) .pipe(map(((event: HttpEvent) => { let returnValue: FileUploadEvent|null = null; switch (event.type) { case HttpEventType.UploadProgress: returnValue = { progress: event.loaded / event.total, status: FileStatus.UPLOAD_VIRUS_SCAN }; break; case HttpEventType.Sent: returnValue = { status: FileStatus.AWAITING_VIRUS_SCAN }; break; case HttpEventType.Response: const response: VirusScanResult = JSON.parse((>event).body); if (response.scanResult.result === VirusScanResultType.Valid) { returnValue = { status: FileStatus.COMPLETE_VIRUS_SCAN, result: response.scanToken }; } else { returnValue = { status: FileStatus.FAILED_VIRUS_SCAN, result: response.scanToken }; } break; } return returnValue; }))) .pipe(filter((e) => e !== null)); })); } async scanFileForViruses (file: File | Blob): Promise { const token = await this.fetchVirusScanToken().toPromise(); const virusBody = new FormData(); virusBody.append('file', file); virusBody.append('ApiKey', token.fileScanToken); const virusRequest = new HttpRequest('POST', '/filescan/upload', virusBody, { reportProgress: true, headers: new HttpHeaders({ 'ngsw-bypass': 'true' }), responseType: 'text' }); const virusResponse = await this.http.request(virusRequest).pipe( filter(event => event instanceof HttpResponse), map(event => JSON.parse((>event).body))) .toPromise(); const fileName = 'name' in file ? file.name : this.i18n.translate('common:textTheFile', {}, 'The file'); this.evaluateVirusScanResult(virusResponse, fileName); return virusResponse; } private evaluateVirusScanResult ( virusResponse: VirusScanResult, fileName: string ) { if (virusResponse.scanResult.result === VirusScanResultType.Invalid) { this.notifier.error( this.i18n.translate( 'common:notificationVirusDetectedInFileName', { fileName }, '__fileName__ failed the virus scan. Please use a different file.' ) ); throw new Error('Virus detected in file upload'); } } }