mirror of
https://github.com/Forceu/Gokapi.git
synced 2026-02-14 12:38:45 -06:00
Refactoring, table now searchable when using E2E encryption #99
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ wasmServer
|
||||
internal/webserver/web/main.wasm
|
||||
internal/webserver/web/e2e.wasm
|
||||
internal/webserver/web/static/js/wasm_exec.js
|
||||
.vendor/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
//go:build gogenerate
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
//go:build gogenerate
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
//go:build gogenerate
|
||||
|
||||
package main
|
||||
|
||||
@@ -20,6 +20,12 @@ type converter struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
for _, f := range getPaths() {
|
||||
minifyContent(f)
|
||||
}
|
||||
}
|
||||
|
||||
func getPaths() []converter {
|
||||
var result []converter
|
||||
result = append(result, converter{
|
||||
@@ -67,12 +73,6 @@ func getPaths() []converter {
|
||||
return result
|
||||
}
|
||||
|
||||
func main() {
|
||||
for _, f := range getPaths() {
|
||||
minifyContent(f)
|
||||
}
|
||||
}
|
||||
|
||||
func minifyContent(conv converter) {
|
||||
m := minify.New()
|
||||
m.AddFunc("text/css", css.Minify)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
//go:build gogenerate
|
||||
|
||||
package main
|
||||
|
||||
@@ -15,15 +15,15 @@ const fileSetupConstants = "../../internal/configuration/setup/ProtectedUrls.go"
|
||||
const fileDocumentation = "../../docs/setup.rst"
|
||||
|
||||
func main() {
|
||||
checkFileExists(fileSetup)
|
||||
checkFileExists(fileSetupConstants)
|
||||
checkFileExists(fileDocumentation)
|
||||
checkFileExistsUrl(fileSetup)
|
||||
checkFileExistsUrl(fileSetupConstants)
|
||||
checkFileExistsUrl(fileDocumentation)
|
||||
urls := parseProtectedUrls()
|
||||
writeConstantFile(urls)
|
||||
writeDocumentationFile(urls)
|
||||
}
|
||||
|
||||
func checkFileExists(filename string) {
|
||||
func checkFileExistsUrl(filename string) {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println("ERROR: File does not exist: " + filename)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
//go:build gogenerate
|
||||
|
||||
package main
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
const versionJsAdmin = "1"
|
||||
const versionJsDropzone = "3"
|
||||
const versionJsE2EAdmin = "1"
|
||||
const versionJsE2EAdmin = "2"
|
||||
const versionCssMain = "1"
|
||||
|
||||
const fileMain = "../../cmd/gokapi/Main.go"
|
||||
|
||||
@@ -14,7 +14,6 @@ require (
|
||||
github.com/r3labs/sse/v2 v2.10.0
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/oauth2 v0.6.0
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
golang.org/x/term v0.6.0
|
||||
@@ -34,6 +33,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.12.5 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.5 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
||||
2
go.mod
2
go.mod
@@ -14,7 +14,6 @@ require (
|
||||
github.com/r3labs/sse/v2 v2.10.0
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/oauth2 v0.6.0
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
golang.org/x/term v0.6.0
|
||||
@@ -34,6 +33,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.12.5 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.5 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
||||
@@ -60,7 +60,7 @@ func (f *File) IsLocalStorage() bool {
|
||||
}
|
||||
|
||||
// ToFileApiOutput returns a json object without sensitive information
|
||||
func (f *File) ToFileApiOutput(isClientSideDecryption bool) (FileApiOutput, error) {
|
||||
func (f *File) ToFileApiOutput() (FileApiOutput, error) {
|
||||
var result FileApiOutput
|
||||
err := copier.Copy(&result, &f)
|
||||
if err != nil {
|
||||
@@ -69,15 +69,15 @@ func (f *File) ToFileApiOutput(isClientSideDecryption bool) (FileApiOutput, erro
|
||||
result.IsPasswordProtected = f.PasswordHash != ""
|
||||
result.IsEncrypted = f.Encryption.IsEncrypted
|
||||
result.IsSavedOnLocalStorage = f.AwsBucket == ""
|
||||
if f.Encryption.IsEndToEndEncrypted || isClientSideDecryption {
|
||||
if f.Encryption.IsEndToEndEncrypted || f.RequiresClientDecryption() {
|
||||
result.RequiresClientSideDecryption = true
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToJsonResult converts the file info to a json String used for returning a result for an upload
|
||||
func (f *File) ToJsonResult(serverUrl string, isClientSideDecryption bool) string {
|
||||
info, err := f.ToFileApiOutput(isClientSideDecryption)
|
||||
func (f *File) ToJsonResult(serverUrl string) string {
|
||||
info, err := f.ToFileApiOutput()
|
||||
if err != nil {
|
||||
return errorAsJson(err)
|
||||
}
|
||||
@@ -95,6 +95,14 @@ func (f *File) ToJsonResult(serverUrl string, isClientSideDecryption bool) strin
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// RequiresClientDecryption checks if the file needs to be decrypted by the client
|
||||
// (if remote storage or end-to-end encryption)
|
||||
func (f *File) RequiresClientDecryption() bool {
|
||||
if !f.Encryption.IsEncrypted {
|
||||
return false
|
||||
}
|
||||
return !f.IsLocalStorage() || f.Encryption.IsEndToEndEncrypted
|
||||
}
|
||||
func errorAsJson(err error) string {
|
||||
fmt.Println(err)
|
||||
return "{\"Result\":\"error\",\"ErrorMessage\":\"" + err.Error() + "\"}"
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestToJsonResult(t *testing.T) {
|
||||
UnlimitedDownloads: true,
|
||||
UnlimitedTime: true,
|
||||
}
|
||||
test.IsEqualString(t, file.ToJsonResult("serverurl/", true), `{"Result":"OK","FileInfo":{"Id":"testId","Name":"testName","Size":"10 B","HotlinkId":"hotlinkid","ContentType":"text/html","ExpireAt":50,"SizeBytes":10,"ExpireAtString":"future","DownloadsRemaining":1,"DownloadCount":3,"UnlimitedDownloads":true,"UnlimitedTime":true,"RequiresClientSideDecryption":true,"IsEncrypted":true,"IsPasswordProtected":true,"IsSavedOnLocalStorage":false},"Url":"serverurl/d?id=","HotlinkUrl":"serverurl/hotlink/","GenericHotlinkUrl":"serverurl/downloadFile?id="}`)
|
||||
test.IsEqualString(t, file.ToJsonResult("serverurl/"), `{"Result":"OK","FileInfo":{"Id":"testId","Name":"testName","Size":"10 B","HotlinkId":"hotlinkid","ContentType":"text/html","ExpireAt":50,"SizeBytes":10,"ExpireAtString":"future","DownloadsRemaining":1,"DownloadCount":3,"UnlimitedDownloads":true,"UnlimitedTime":true,"RequiresClientSideDecryption":true,"IsEncrypted":true,"IsPasswordProtected":true,"IsSavedOnLocalStorage":false},"Url":"serverurl/d?id=","HotlinkUrl":"serverurl/hotlink/","GenericHotlinkUrl":"serverurl/downloadFile?id="}`)
|
||||
}
|
||||
|
||||
func TestIsLocalStorage(t *testing.T) {
|
||||
@@ -43,3 +43,20 @@ func TestErrorAsJson(t *testing.T) {
|
||||
result := errorAsJson(errors.New("testerror"))
|
||||
test.IsEqualString(t, result, "{\"Result\":\"error\",\"ErrorMessage\":\"testerror\"}")
|
||||
}
|
||||
|
||||
func TestRequiresClientDecryption(t *testing.T) {
|
||||
file := File{
|
||||
Id: "test",
|
||||
AwsBucket: "bucket",
|
||||
Encryption: EncryptionInfo{
|
||||
IsEncrypted: true,
|
||||
},
|
||||
}
|
||||
test.IsEqualBool(t, file.RequiresClientDecryption(), true)
|
||||
file.Encryption.IsEncrypted = false
|
||||
test.IsEqualBool(t, file.RequiresClientDecryption(), false)
|
||||
file.AwsBucket = ""
|
||||
test.IsEqualBool(t, file.RequiresClientDecryption(), false)
|
||||
file.Encryption.IsEncrypted = true
|
||||
test.IsEqualBool(t, file.RequiresClientDecryption(), false)
|
||||
}
|
||||
|
||||
@@ -457,7 +457,7 @@ var imageFileExtensions = []string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".b
|
||||
|
||||
// If file is an image, create link for hotlinking
|
||||
func addHotlink(file *models.File) {
|
||||
if RequiresClientDecryption(*file) {
|
||||
if file.RequiresClientDecryption() {
|
||||
return
|
||||
}
|
||||
if file.PasswordHash != "" {
|
||||
@@ -514,15 +514,6 @@ func GetFileByHotlink(id string) (models.File, bool) {
|
||||
return GetFile(fileId)
|
||||
}
|
||||
|
||||
// RequiresClientDecryption checks if the file needs to be decrypted by the client
|
||||
// (if remote storage or end-to-end encryption)
|
||||
func RequiresClientDecryption(file models.File) bool {
|
||||
if !file.Encryption.IsEncrypted {
|
||||
return false
|
||||
}
|
||||
return !file.IsLocalStorage() || file.Encryption.IsEndToEndEncrypted
|
||||
}
|
||||
|
||||
// ServeFile subtracts a download allowance and serves the file to the browser
|
||||
func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDownload bool) {
|
||||
file.DownloadsRemaining = file.DownloadsRemaining - 1
|
||||
@@ -539,7 +530,7 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo
|
||||
return
|
||||
}
|
||||
fileData, size := getFileHandler(file, configuration.Get().DataDir)
|
||||
if file.Encryption.IsEncrypted && !RequiresClientDecryption(file) {
|
||||
if file.Encryption.IsEncrypted && !file.RequiresClientDecryption() {
|
||||
if !encryption.IsCorrectKey(file.Encryption, fileData) {
|
||||
w.Write([]byte("Internal error - Error decrypting file, source data might be damaged or an incorrect key has been used"))
|
||||
return
|
||||
@@ -547,7 +538,7 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo
|
||||
}
|
||||
statusId := downloadstatus.SetDownload(file)
|
||||
writeDownloadHeaders(file, w, forceDownload)
|
||||
if file.Encryption.IsEncrypted && !RequiresClientDecryption(file) {
|
||||
if file.Encryption.IsEncrypted && !file.RequiresClientDecryption() {
|
||||
err := encryption.DecryptReader(file.Encryption, fileData, w)
|
||||
if err != nil {
|
||||
w.Write([]byte("Error decrypting file"))
|
||||
|
||||
@@ -746,27 +746,6 @@ func TestDeleteFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiresClientDecryption(t *testing.T) {
|
||||
file := models.File{
|
||||
Id: "test",
|
||||
AwsBucket: "bucket",
|
||||
Encryption: models.EncryptionInfo{
|
||||
IsEncrypted: true,
|
||||
},
|
||||
}
|
||||
result := RequiresClientDecryption(file)
|
||||
test.IsEqualBool(t, result, true)
|
||||
file.Encryption.IsEncrypted = false
|
||||
result = RequiresClientDecryption(file)
|
||||
test.IsEqualBool(t, result, false)
|
||||
file.AwsBucket = ""
|
||||
result = RequiresClientDecryption(file)
|
||||
test.IsEqualBool(t, result, false)
|
||||
file.Encryption.IsEncrypted = true
|
||||
result = RequiresClientDecryption(file)
|
||||
test.IsEqualBool(t, result, false)
|
||||
}
|
||||
|
||||
func createBigFile(name string, megabytes int64) {
|
||||
size := megabytes * 1024 * 1024
|
||||
file, _ := os.Create(name)
|
||||
|
||||
@@ -335,7 +335,7 @@ func showDownload(w http.ResponseWriter, r *http.Request) {
|
||||
UsesHttps: configuration.UsesHttps(),
|
||||
}
|
||||
|
||||
if storage.RequiresClientDecryption(file) {
|
||||
if file.RequiresClientDecryption() {
|
||||
view.ClientSideDecryption = true
|
||||
if !file.Encryption.IsEndToEndEncrypted {
|
||||
cipher, err := encryption.GetCipherFromFile(file.Encryption)
|
||||
@@ -548,7 +548,7 @@ func (u *UploadView) convertGlobalConfig(view int) *UploadView {
|
||||
switch view {
|
||||
case ViewMain:
|
||||
for _, element := range database.GetAllMetadata() {
|
||||
fileInfo, err := element.ToFileApiOutput(storage.RequiresClientDecryption(element))
|
||||
fileInfo, err := element.ToFileApiOutput()
|
||||
helper.Check(err)
|
||||
result = append(result, fileInfo)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func list(w http.ResponseWriter) {
|
||||
timeNow := time.Now().Unix()
|
||||
for _, element := range database.GetAllMetadata() {
|
||||
if !storage.IsExpiredFile(element, timeNow) {
|
||||
file, err := element.ToFileApiOutput(storage.RequiresClientDecryption(element))
|
||||
file, err := element.ToFileApiOutput()
|
||||
helper.Check(err)
|
||||
validFiles = append(validFiles, file)
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func duplicateFile(w http.ResponseWriter, request apiRequest) {
|
||||
sendError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
publicOutput, err := newFile.ToFileApiOutput(storage.RequiresClientDecryption(newFile))
|
||||
publicOutput, err := newFile.ToFileApiOutput()
|
||||
helper.Check(err)
|
||||
result, err := json.Marshal(publicOutput)
|
||||
helper.Check(err)
|
||||
|
||||
@@ -33,7 +33,7 @@ func Process(w http.ResponseWriter, r *http.Request, isWeb bool, maxMemory int)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = io.WriteString(w, result.ToJsonResult(config.ExternalUrl, storage.RequiresClientDecryption(result)))
|
||||
_, _ = io.WriteString(w, result.ToJsonResult(config.ExternalUrl))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func CompleteChunk(w http.ResponseWriter, r *http.Request, isApiCall bool) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = io.WriteString(w, result.ToJsonResult(config.ExternalUrl, storage.RequiresClientDecryption(result)))
|
||||
_, _ = io.WriteString(w, result.ToJsonResult(config.ExternalUrl))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -165,27 +165,32 @@ function setE2eUpload() {
|
||||
|
||||
|
||||
function decryptFileEntry(id, filename, cipher) {
|
||||
let cellName = document.getElementById("cell-name-" + id);
|
||||
if (cellName != null) {
|
||||
cellName.innerText = filename;
|
||||
}
|
||||
let datatable = $('#maintable').DataTable();
|
||||
const rows = datatable.rows().nodes();
|
||||
|
||||
let urlLink = document.getElementById("url-href-" + id);
|
||||
if (urlLink != null) {
|
||||
let url = urlLink.href;
|
||||
if (!url.includes(cipher)) {
|
||||
urlLink.href = url + "#" + cipher;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const cell = datatable.cell(i, 0).node();
|
||||
if ("cell-name-" + id === $(cell).attr("id")) {
|
||||
datatable.cell(i, 0).data(filename);
|
||||
let urlNode = datatable.cell(i, 5).node();
|
||||
let urlLink = urlNode.querySelector("a");
|
||||
let url = urlLink.getAttribute("href");
|
||||
if (!url.includes(cipher)) {
|
||||
urlLink.setAttribute("href", url + "#" + cipher);
|
||||
}
|
||||
datatable.cell(i, 5).node(urlNode);
|
||||
|
||||
|
||||
let buttonNode = datatable.cell(i, 6).node();
|
||||
let button = buttonNode.querySelector("button");
|
||||
let urlButton = button.getAttribute("data-clipboard-text");
|
||||
if (!urlButton.includes(cipher)) {
|
||||
button.setAttribute("data-clipboard-text", urlButton + "#" + cipher);
|
||||
}
|
||||
datatable.cell(i, 6).node(buttonNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let urlButton = document.getElementById("url-button-" + id)
|
||||
if (urlButton != null) {
|
||||
let url = urlButton.getAttribute("data-clipboard-text");
|
||||
if (!url.includes(cipher)) {
|
||||
urlButton.setAttribute("data-clipboard-text", url + "#" + cipher);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
Blob.prototype.arrayBuffer??=function(){return new Response(this).arrayBuffer()},isE2EEnabled=!0;function displayError(e){document.getElementById("errordiv").style.display="block",document.getElementById("errormessage").innerHTML="<b>Error: </b> "+e.toString().replace(/^Error:/gi,""),console.error("Caught exception",e)}function checkIfE2EKeyIsSet(){isE2EKeySet()?loadWasm(function(){let t=localStorage.getItem("e2ekey"),e=GokapiE2ESetCipher(t);if(e!==null){displayError(e);return}getE2EInfo(),GokapiE2EDecryptMenu(),dropzoneObject.enable(),document.getElementsByClassName("dz-button")[0].innerText="Drop files, paste or click here to upload (end-to-end encrypted)"}):window.location="./e2eSetup"}function getE2EInfo(){var e=new XMLHttpRequest;e.open("GET","./e2eInfo?action=get",!1),e.onreadystatechange=function(){if(this.readyState==4)if(this.status==200){let t=GokapiE2EInfoParse(e.response);t!==null&&(displayError(t),t.message==="cipher: message authentication failed"&&invalidCipherRedirectConfim())}else displayError("Trying to get E2E info: "+e.statusText)},e.send()}function invalidCipherRedirectConfim(){confirm("It appears that an invalid end-to-end encryption key has been entered. Would you like to enter the correct one?")&&(window.location="./e2eSetup")}function storeE2EInfo(e){var t=new XMLHttpRequest;t.open("POST","./e2eInfo?action=store",!1),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onreadystatechange=function(){this.readyState==4&&this.status!=200&&displayError("Trying to store E2E info: "+t.statusText)};let n=new FormData;n.append("info",e),t.send(urlencodeFormData(n))}function isE2EKeySet(){let e=localStorage.getItem("e2ekey");return e!==null&&e!==""}function loadWasm(e){const t=new Go,s="e2e.wasm?v=1";var n;try{"instantiateStreaming"in WebAssembly?WebAssembly.instantiateStreaming(fetch(s),t.importObject).then(function(s){n=s.instance,t.run(n),e()}):fetch(s).then(e=>e.arrayBuffer()).then(s=>WebAssembly.instantiate(s,t.importObject).then(function(s){n=s.instance,t.run(n),e()}))}catch(e){displayError(e)}}function urlencodeFormData(e){let t="";function s(e){return encodeURIComponent(e).replace(/%20/g,"+")}for(var n of e.entries())typeof n[1]=="string"&&(t+=(t?"&":"")+s(n[0])+"="+s(n[1]));return t}function setE2eUpload(){dropzoneObject.uploadFiles=function(e){this._transformFiles(e,t=>{let o=t[0];e[0].upload.chunked=!0,e[0].isEndToEndEncrypted=!0;let i=e[0].upload.filename,s=o.size,r=0,n=GokapiE2EEncryptNew(e[0].upload.uuid,s,i);if(n instanceof Error){displayError(n);return}e[0].upload.totalChunkCount=Math.ceil(n/this.options.chunkSize),e[0].sizeEncrypted=n;let a=e[0],c=0,l=0,d=!1,u=0;uploadChunk(a,0,n,s,dropzoneObject.options.chunkSize,0)})}}function decryptFileEntry(e,t,n){let i=document.getElementById("cell-name-"+e);i!=null&&(i.innerText=t);let s=document.getElementById("url-href-"+e);if(s!=null){let e=s.href;e.includes(n)||(s.href=e+"#"+n)}let o=document.getElementById("url-button-"+e);if(o!=null){let e=o.getAttribute("data-clipboard-text");e.includes(n)||o.setAttribute("data-clipboard-text",e+"#"+n)}}async function uploadChunk(e,t,n,s,o,i){let c=!1,l=t*o,d=l+o;t===e.upload.totalChunkCount-1&&(c=!0,d=s);let u=e.webkitSlice?e.webkitSlice(l,d):e.slice(l,d),a=await u.arrayBuffer(),r=await GokapiE2EUploadChunk(e.upload.uuid,a.byteLength,c,new Uint8Array(a));if(r instanceof Error){displayError(a);return}let h=await postChunk(e.upload.uuid,i,n,r,e);if(h!==null){e.accepted=!1,dropzoneObject._errorProcessing([e],h);return}i=i+r.byteLength,a=null,r=null,u=null,c?(e.status=Dropzone.SUCCESS,dropzoneObject.emit("success",e,"success",null),dropzoneObject.emit("complete",e),dropzoneObject.processQueue(),dropzoneObject.options.chunksUploaded(e,()=>{})):await uploadChunk(e,t+1,n,s,o,i)}async function postChunk(e,t,n,s,o){return new Promise(i=>{let r=new FormData;r.append("dztotalfilesize",n),r.append("dzchunkbyteoffset",t),r.append("dzuuid",e),r.append("file",new Blob([s]),"encrypted.file");let a=new XMLHttpRequest;a.open("POST","./uploadChunk");let c=a.upload!=null?a.upload:a;c.onprogress=e=>{try{dropzoneObject.emit("uploadprogress",o,100*(e.loaded+t)/n,e.loaded+t)}catch(e){console.log(e)}},a.onreadystatechange=function(){this.readyState==4&&(this.status==200?i(null):(console.log(a.responseText),i(a.responseText)))},a.send(r)})}
|
||||
Blob.prototype.arrayBuffer??=function(){return new Response(this).arrayBuffer()},isE2EEnabled=!0;function displayError(e){document.getElementById("errordiv").style.display="block",document.getElementById("errormessage").innerHTML="<b>Error: </b> "+e.toString().replace(/^Error:/gi,""),console.error("Caught exception",e)}function checkIfE2EKeyIsSet(){isE2EKeySet()?loadWasm(function(){let t=localStorage.getItem("e2ekey"),e=GokapiE2ESetCipher(t);if(e!==null){displayError(e);return}getE2EInfo(),GokapiE2EDecryptMenu(),dropzoneObject.enable(),document.getElementsByClassName("dz-button")[0].innerText="Drop files, paste or click here to upload (end-to-end encrypted)"}):window.location="./e2eSetup"}function getE2EInfo(){var e=new XMLHttpRequest;e.open("GET","./e2eInfo?action=get",!1),e.onreadystatechange=function(){if(this.readyState==4)if(this.status==200){let t=GokapiE2EInfoParse(e.response);t!==null&&(displayError(t),t.message==="cipher: message authentication failed"&&invalidCipherRedirectConfim())}else displayError("Trying to get E2E info: "+e.statusText)},e.send()}function invalidCipherRedirectConfim(){confirm("It appears that an invalid end-to-end encryption key has been entered. Would you like to enter the correct one?")&&(window.location="./e2eSetup")}function storeE2EInfo(e){var t=new XMLHttpRequest;t.open("POST","./e2eInfo?action=store",!1),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onreadystatechange=function(){this.readyState==4&&this.status!=200&&displayError("Trying to store E2E info: "+t.statusText)};let n=new FormData;n.append("info",e),t.send(urlencodeFormData(n))}function isE2EKeySet(){let e=localStorage.getItem("e2ekey");return e!==null&&e!==""}function loadWasm(e){const t=new Go,s="e2e.wasm?v=1";var n;try{"instantiateStreaming"in WebAssembly?WebAssembly.instantiateStreaming(fetch(s),t.importObject).then(function(s){n=s.instance,t.run(n),e()}):fetch(s).then(e=>e.arrayBuffer()).then(s=>WebAssembly.instantiate(s,t.importObject).then(function(s){n=s.instance,t.run(n),e()}))}catch(e){displayError(e)}}function urlencodeFormData(e){let t="";function s(e){return encodeURIComponent(e).replace(/%20/g,"+")}for(var n of e.entries())typeof n[1]=="string"&&(t+=(t?"&":"")+s(n[0])+"="+s(n[1]));return t}function setE2eUpload(){dropzoneObject.uploadFiles=function(e){this._transformFiles(e,t=>{let o=t[0];e[0].upload.chunked=!0,e[0].isEndToEndEncrypted=!0;let i=e[0].upload.filename,s=o.size,r=0,n=GokapiE2EEncryptNew(e[0].upload.uuid,s,i);if(n instanceof Error){displayError(n);return}e[0].upload.totalChunkCount=Math.ceil(n/this.options.chunkSize),e[0].sizeEncrypted=n;let a=e[0],c=0,l=0,d=!1,u=0;uploadChunk(a,0,n,s,dropzoneObject.options.chunkSize,0)})}}function decryptFileEntry(e,t,n){let s=$("#maintable").DataTable();const o=s.rows().nodes();for(let i=0;i<o.length;i++){const a=s.cell(i,0).node();if("cell-name-"+e===$(a).attr("id")){s.cell(i,0).data(t);let e=s.cell(i,5).node(),o=e.querySelector("a"),a=o.getAttribute("href");a.includes(n)||o.setAttribute("href",a+"#"+n),s.cell(i,5).node(e);let r=s.cell(i,6).node(),c=r.querySelector("button"),l=c.getAttribute("data-clipboard-text");l.includes(n)||c.setAttribute("data-clipboard-text",l+"#"+n),s.cell(i,6).node(r);break}}}async function uploadChunk(e,t,n,s,o,i){let c=!1,l=t*o,d=l+o;t===e.upload.totalChunkCount-1&&(c=!0,d=s);let u=e.webkitSlice?e.webkitSlice(l,d):e.slice(l,d),a=await u.arrayBuffer(),r=await GokapiE2EUploadChunk(e.upload.uuid,a.byteLength,c,new Uint8Array(a));if(r instanceof Error){displayError(a);return}let h=await postChunk(e.upload.uuid,i,n,r,e);if(h!==null){e.accepted=!1,dropzoneObject._errorProcessing([e],h);return}i=i+r.byteLength,a=null,r=null,u=null,c?(e.status=Dropzone.SUCCESS,dropzoneObject.emit("success",e,"success",null),dropzoneObject.emit("complete",e),dropzoneObject.processQueue(),dropzoneObject.options.chunksUploaded(e,()=>{})):await uploadChunk(e,t+1,n,s,o,i)}async function postChunk(e,t,n,s,o){return new Promise(i=>{let r=new FormData;r.append("dztotalfilesize",n),r.append("dzchunkbyteoffset",t),r.append("dzuuid",e),r.append("file",new Blob([s]),"encrypted.file");let a=new XMLHttpRequest;a.open("POST","./uploadChunk");let c=a.upload!=null?a.upload:a;c.onprogress=e=>{try{dropzoneObject.emit("uploadprogress",o,100*(e.loaded+t)/n,e.loaded+t)}catch(e){console.log(e)}},a.onreadystatechange=function(){this.readyState==4&&(this.status==200?i(null):(console.log(a.responseText),i(a.responseText)))},a.send(r)})}
|
||||
@@ -6,5 +6,5 @@
|
||||
// use a cached version, if the file has been updated
|
||||
{{define "js_admin_version"}}1{{end}}
|
||||
{{define "js_dropzone_version"}}3{{end}}
|
||||
{{define "js_e2eversion"}}1{{end}}
|
||||
{{define "js_e2eversion"}}2{{end}}
|
||||
{{define "css_main"}}1{{end}}
|
||||
Reference in New Issue
Block a user