diff --git a/webaccount/account.go b/webaccount/account.go index 77213e6..93dd30f 100644 --- a/webaccount/account.go +++ b/webaccount/account.go @@ -306,10 +306,14 @@ func handle(apiHandler http.Handler, isForwarded bool, w http.ResponseWriter, r http.Error(w, "500 - internal server error - "+err.Error(), http.StatusInternalServerError) return } - token, err := importStart(log, accName, tmpf, skipMailboxPrefix) + token, isUserError, err := importStart(log, accName, tmpf, skipMailboxPrefix) if err != nil { - log.Errorx("starting import", err) - http.Error(w, "500 - internal server error - "+err.Error(), http.StatusInternalServerError) + log.Errorx("starting import", err, slog.Bool("usererror", isUserError)) + if isUserError { + http.Error(w, "400 - bad request - "+err.Error(), http.StatusBadRequest) + } else { + http.Error(w, "500 - internal server error - "+err.Error(), http.StatusInternalServerError) + } return } tmpf = nil // importStart is now responsible for cleanup. diff --git a/webaccount/account.js b/webaccount/account.js index 4f65ae1..b84a36f 100644 --- a/webaccount/account.js +++ b/webaccount/account.js @@ -973,7 +973,7 @@ const index = async () => { xhr.addEventListener('load', () => { console.log('upload done', { xhr: xhr, status: xhr.status }); if (xhr.status !== 200) { - reject({ message: 'status ' + xhr.status }); + reject({ message: xhr.status === 400 || xhr.status === 500 ? xhr.responseText : 'status ' + xhr.status }); return; } let resp; diff --git a/webaccount/account.ts b/webaccount/account.ts index f0dd597..9892b8d 100644 --- a/webaccount/account.ts +++ b/webaccount/account.ts @@ -458,7 +458,7 @@ const index = async () => { xhr.addEventListener('load', () => { console.log('upload done', {xhr: xhr, status: xhr.status}) if (xhr.status !== 200) { - reject({message: 'status '+xhr.status}) + reject({message: xhr.status === 400 || xhr.status === 500 ? xhr.responseText : 'status '+xhr.status}) return } let resp: api.ImportProgress diff --git a/webaccount/import.go b/webaccount/import.go index b99ad0d..236b843 100644 --- a/webaccount/import.go +++ b/webaccount/import.go @@ -200,7 +200,7 @@ type importStep struct { // importStart prepare the import and launches the goroutine to actually import. // importStart is responsible for closing f and removing f. -func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix string) (string, error) { +func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix string) (string, bool, error) { defer func() { if f != nil { store.CloseRemoveTempFile(log, f, "upload for import") @@ -209,12 +209,12 @@ func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix str buf := make([]byte, 16) if _, err := cryptrand.Read(buf); err != nil { - return "", err + return "", false, err } token := fmt.Sprintf("%x", buf) if _, err := f.Seek(0, 0); err != nil { - return "", fmt.Errorf("seek to start of file: %v", err) + return "", false, fmt.Errorf("seek to start of file: %v", err) } // Recognize file format. @@ -223,12 +223,12 @@ func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix str magicGzip := []byte{0x1f, 0x8b} magic := make([]byte, 4) if _, err := f.ReadAt(magic, 0); err != nil { - return "", fmt.Errorf("detecting file format: %v", err) + return "", true, fmt.Errorf("detecting file format: %v", err) } if bytes.Equal(magic, magicZip) { iszip = true } else if !bytes.Equal(magic[:2], magicGzip) { - return "", fmt.Errorf("file is not a zip or gzip file") + return "", true, fmt.Errorf("file is not a zip or gzip file") } var zr *zip.Reader @@ -236,23 +236,23 @@ func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix str if iszip { fi, err := f.Stat() if err != nil { - return "", fmt.Errorf("stat temporary import zip file: %v", err) + return "", false, fmt.Errorf("stat temporary import zip file: %v", err) } zr, err = zip.NewReader(f, fi.Size()) if err != nil { - return "", fmt.Errorf("opening zip file: %v", err) + return "", true, fmt.Errorf("opening zip file: %v", err) } } else { gzr, err := gzip.NewReader(f) if err != nil { - return "", fmt.Errorf("gunzip: %v", err) + return "", true, fmt.Errorf("gunzip: %v", err) } tr = tar.NewReader(gzr) } acc, err := store.OpenAccount(log, accName) if err != nil { - return "", fmt.Errorf("open acount: %v", err) + return "", false, fmt.Errorf("open acount: %v", err) } acc.Lock() // Not using WithWLock because importMessage is responsible for unlocking. @@ -261,7 +261,7 @@ func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix str acc.Unlock() xerr := acc.Close() log.Check(xerr, "closing account") - return "", fmt.Errorf("start transaction: %v", err) + return "", false, fmt.Errorf("start transaction: %v", err) } // Ensure token is registered before returning, with context that can be canceled. @@ -272,7 +272,7 @@ func importStart(log mlog.Log, accName string, f *os.File, skipMailboxPrefix str go importMessages(ctx, log.WithCid(mox.Cid()), token, acc, tx, zr, tr, f, skipMailboxPrefix) f = nil // importMessages is now responsible for closing and removing. - return token, nil + return token, false, nil } // importMessages imports the messages from zip/tgz file f.