import { getCredentials } from "./credentials" ; import { Encryption } from "./encryption" ; import { EntryStorage } from "./storage" ; export class Dropbox implements BackupProvider { private async getToken ( ) { return localStorage . dropboxToken || "" ; } async upload ( encryption ) { if ( localStorage . dropboxEncrypted === undefined ) { localStorage . dropboxEncrypted = "true" ; } const exportData = await EntryStorage . backupGetExport ( encryption , localStorage . dropboxEncrypted === "true" ) ; const backup = JSON . stringify ( exportData , null , 2 ) ; const url = "https://content.dropboxapi.com/2/files/upload" ; const token = await this . getToken ( ) ; return new Promise ( ( resolve , reject ) => { if ( ! token ) { return resolve ( false ) ; } try { const xhr = new XMLHttpRequest ( ) ; const now = new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) . replace ( /-/g , "" ) ; const apiArg = { path : `/${ now }.json` , mode : "add" , autorename : true , } ; xhr . open ( "POST" , url ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . setRequestHeader ( "Content-type" , "application/octet-stream" ) ; xhr . setRequestHeader ( "Dropbox-API-Arg" , JSON . stringify ( apiArg ) ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "dropboxToken" ) ; localStorage . dropboxRevoked = true ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( res . name ) { resolve ( true ) ; } else { resolve ( false ) ; } } catch ( error ) { reject ( error ) ; } } return ; } ; xhr . send ( backup ) ; } catch ( error ) { return reject ( error ) ; } } ) ; } async getUser ( ) { const url = "https://api.dropboxapi.com/2/users/get_current_account" ; const token = await this . getToken ( ) ; return new Promise ( ( resolve ) => { if ( ! token ) { return resolve ( "Error: No token" ) ; } const xhr = new XMLHttpRequest ( ) ; xhr . open ( "POST" , url ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "dropboxToken" ) ; localStorage . dropboxRevoked = true ; resolve ( "Error: Response was 401. You will be logged out the next time you open Authenticator." ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( res . email ) { resolve ( res . email ) ; } else { console . error ( "Could not find email in reponse." , res ) ; resolve ( "Error: res.email was undefined." ) ; } } catch ( e ) { console . error ( e ) ; resolve ( "Error" ) ; } } return ; } ; xhr . send ( null ) ; } ) ; } } export class Drive implements BackupProvider { private async getToken ( ) { if ( ! localStorage . driveToken || ( await new Promise ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "GET" , "https://www.googleapis.com/drive/v3/files" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + localStorage . driveToken ) ; xhr . onreadystatechange = async ( ) => { if ( xhr . readyState === 4 ) { try { const res = JSON . parse ( xhr . responseText ) ; if ( res . error ) { if ( res . error . code === 401 ) { if ( navigator . userAgent . indexOf ( "Chrome" ) !== - 1 && navigator . userAgent . indexOf ( "OPR" ) === - 1 && navigator . userAgent . indexOf ( "Edg" ) === - 1 ) { await chrome . identity . removeCachedAuthToken ( { token : localStorage . driveToken , } ) ; } localStorage . driveToken = "" ; resolve ( true ) ; } } else { resolve ( false ) ; } } catch ( error ) { console . error ( error ) ; reject ( error ) ; } } return ; } ; xhr . send ( ) ; } ) ) ) { await this . refreshToken ( ) ; } return localStorage . driveToken ; } private async refreshToken ( ) { if ( navigator . userAgent . indexOf ( "Chrome" ) !== - 1 && navigator . userAgent . indexOf ( "OPR" ) === - 1 && navigator . userAgent . indexOf ( "Edg" ) === - 1 ) { return new Promise ( ( resolve ) => { return chrome . identity . getAuthToken ( { interactive : false , scopes : [ "https://www.googleapis.com/auth/drive.file" ] , } , ( token ) => { localStorage . driveToken = token ; if ( ! token ) { localStorage . driveRevoked = true ; } resolve ( Boolean ( token ) ) ; } ) ; } ) ; } else { return new Promise ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "POST" , "https://www.googleapis.com/oauth2/v4/token?client_id=" + getCredentials ( ) . drive . client_id + "&client_secret=" + getCredentials ( ) . drive . client_secret + "&refresh_token=" + localStorage . driveRefreshToken + "&grant_type=refresh_token" ) ; xhr . setRequestHeader ( "Accept" , "application/json" ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "driveRefreshToken" ) ; localStorage . driveRevoked = true ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( res . error ) { if ( res . error === "invalid_grant" ) { localStorage . removeItem ( "driveRefreshToken" ) ; localStorage . driveRevoked = true ; } console . error ( res . error_description ) ; resolve ( false ) ; } else { localStorage . driveToken = res . access_token ; resolve ( true ) ; } } catch ( error ) { console . error ( error ) ; reject ( error ) ; } } return ; } ; xhr . send ( ) ; } ) ; } } private async getFolder ( ) { const token = await this . getToken ( ) ; if ( ! token ) { return false ; } if ( localStorage . driveFolder ) { await new Promise ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "GET" , "https://www.googleapis.com/drive/v3/files/" + localStorage . driveFolder + "?fields=trashed" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . setRequestHeader ( "Accept" , "application/json" ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "driveToken" ) ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( res . error ) { if ( res . error . code === 404 ) { localStorage . driveFolder = "" ; resolve ( true ) ; } } else if ( res . trashed ) { localStorage . driveFolder = "" ; resolve ( true ) ; } else if ( res . error ) { console . error ( res . error . message ) ; resolve ( false ) ; } else { resolve ( true ) ; } } catch ( error ) { console . error ( error ) ; reject ( error ) ; } } return ; } ; xhr . send ( ) ; } ) ; } if ( ! localStorage . driveFolder ) { await new Promise ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "POST" , "https://www.googleapis.com/drive/v3/files/" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . setRequestHeader ( "Accept" , "application/json" ) ; xhr . setRequestHeader ( "Content-Type" , "application/json" ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "driveToken" ) ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( ! res . error ) { localStorage . driveFolder = res . id ; resolve ( true ) ; } else { console . error ( res . error . message ) ; resolve ( false ) ; } } catch ( error ) { console . error ( error ) ; reject ( error ) ; } } return ; } ; xhr . send ( JSON . stringify ( { name : "Authenticator Backups" , mimeType : "application/vnd.google-apps.folder" , } ) ) ; } ) ; } return localStorage . driveFolder ; } async upload ( encryption ) { if ( localStorage . driveEncrypted === undefined ) { localStorage . driveEncrypted = "true" ; } const exportData = await EntryStorage . backupGetExport ( encryption , localStorage . driveEncrypted === "true" ) ; const backup = JSON . stringify ( exportData , null , 2 ) ; const token = await this . getToken ( ) ; if ( ! token ) { return false ; } const folderId = await this . getFolder ( ) ; return new Promise ( ( resolve , reject ) => { if ( ! token || ! folderId ) { return resolve ( false ) ; } try { const xhr = new XMLHttpRequest ( ) ; const now = new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) . replace ( /-/g , "" ) ; xhr . open ( "POST" , "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . setRequestHeader ( "Content-type" , "multipart/related; boundary=segment_marker" ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "driveToken" ) ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( ! res . error ) { resolve ( true ) ; } else { console . error ( res . error . message ) ; resolve ( false ) ; } } catch ( error ) { reject ( error ) ; } } return ; } ; const requestDataPrototype = [ "--segment_marker" , "Content-Type: application/json; charset=UTF-8" , "" , JSON . stringify ( { name : `${ now }.json` , parents : [ localStorage . driveFolder ] , } ) , "" , "--segment_marker" , "Content-Type: application/octet-stream" , "" , backup , "--segment_marker--" , ] ; let requestData = "" ; requestDataPrototype . forEach ( ( line ) => { requestData = requestData + line + "\n" ; } ) ; xhr . send ( requestData ) ; } catch ( error ) { return reject ( error ) ; } } ) ; } async getUser ( ) { const token = await this . getToken ( ) ; if ( ! token ) { return "Error: Access revoked or expired." ; } return new Promise ( ( resolve ) => { if ( ! token ) { resolve ( "Error: Access revoked or expired." ) ; } const xhr = new XMLHttpRequest ( ) ; xhr . open ( "GET" , "https://www.googleapis.com/drive/v2/about?fields=user" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "driveToken" ) ; resolve ( "Error: Response was 401. You will be logged out the next time you open Authenticator." ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( ! res . error ) { resolve ( res . user . emailAddress ) ; } else { console . error ( res . error . message ) ; resolve ( "Error" ) ; } } catch ( e ) { console . error ( e ) ; resolve ( "Error" ) ; } } return ; } ; xhr . send ( ) ; } ) ; } } export class OneDrive implements BackupProvider { private async getToken ( ) { if ( ! localStorage . oneDriveToken || ( await new Promise ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "GET" , "https://graph.microsoft.com/v1.0/me/drive/special/approot" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + localStorage . oneDriveToken ) ; xhr . onreadystatechange = async ( ) => { if ( xhr . readyState === 4 ) { try { const res = JSON . parse ( xhr . responseText ) ; if ( res . error ) { if ( res . error . code === 401 ) { localStorage . oneDriveToken = "" ; resolve ( true ) ; } } else { resolve ( false ) ; } } catch ( error ) { console . error ( error ) ; reject ( error ) ; } } return ; } ; xhr . send ( ) ; } ) ) ) { await this . refreshToken ( ) ; } return localStorage . oneDriveToken ; } private async refreshToken ( ) { return new Promise ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "POST" , "https://login.microsoftonline.com/common/oauth2/v2.0/token" ) ; xhr . setRequestHeader ( "Content-Type" , "application/x-www-form-urlencoded" ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "oneDriveRefreshToken" ) ; localStorage . oneDriveRevoked = true ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( res . error ) { if ( res . error === "invalid_grant" ) { localStorage . removeItem ( "oneDriveRefreshToken" ) ; localStorage . oneDriveRevoked = true ; } console . error ( res . error_description ) ; resolve ( false ) ; } else { localStorage . oneDriveToken = res . access_token ; resolve ( true ) ; } } catch ( error ) { console . error ( error ) ; reject ( error ) ; } } return ; } ; xhr . send ( `client_id=${ getCredentials ( ) . onedrive . client_id }&refresh_token=${ localStorage . oneDriveRefreshToken }&client_secret=${ encodeURIComponent ( getCredentials ( ) . onedrive . client_secret ) }&grant_type=refresh_token&scope=https%3A%2F%2Fgraph.microsoft.com%2FFiles.ReadWrite${ localStorage . oneDriveBusiness !== "true" ? ".AppFolder" : "" }%20https%3A%2F%2Fgraph.microsoft.com%2FUser.Read%20offline_access` ) ; } ) ; } async upload ( encryption ) { if ( localStorage . oneDriveEncrypted === undefined ) { localStorage . oneDriveEncrypted = "true" ; } const exportData = await EntryStorage . backupGetExport ( encryption , localStorage . oneDriveEncrypted === "true" ) ; const backup = JSON . stringify ( exportData , null , 2 ) ; const token = await this . getToken ( ) ; if ( ! token ) { return false ; } return new Promise ( ( resolve , reject ) => { if ( ! token ) { return resolve ( false ) ; } try { const xhr = new XMLHttpRequest ( ) ; const now = new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) . replace ( /-/g , "" ) ; xhr . open ( "PUT" , `https://graph.microsoft.com/v1.0/me/drive/special/approot:/${ now }.json:/content` ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . setRequestHeader ( "Content-type" , "application/octet-stream" ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "oneDriveToken" ) ; return resolve ( false ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( ! res . error ) { resolve ( true ) ; } else { console . error ( res . error . message ) ; resolve ( false ) ; } } catch ( error ) { reject ( error ) ; } } return ; } ; xhr . send ( backup ) ; } catch ( error ) { return reject ( error ) ; } } ) ; } async getUser ( ) { const token = await this . getToken ( ) ; if ( ! token ) { return "Error: Access revoked or expired." ; } return new Promise ( ( resolve ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( "GET" , "https://graph.microsoft.com/v1.0/me/" ) ; xhr . setRequestHeader ( "Authorization" , "Bearer " + token ) ; xhr . onreadystatechange = ( ) => { if ( xhr . readyState === 4 ) { if ( xhr . status === 401 ) { localStorage . removeItem ( "oneDriveToken" ) ; resolve ( "Error: Response was 401. You will be logged out the next time you open Authenticator." ) ; } try { const res = JSON . parse ( xhr . responseText ) ; if ( ! res . error ) { resolve ( res . userPrincipalName ) ; } else { console . error ( res . error . message ) ; resolve ( "Error" ) ; } } catch ( e ) { console . error ( e ) ; resolve ( "Error" ) ; } } return ; } ; xhr . send ( ) ; } ) ; } }