1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package buffer2021import (22 "crypto/rand"23 "encoding/hex"24 "fmt"25 "io"26 "os"27 "path/filepath"28)2930// FileBuffer implements Buffer interface using file system.31type FileBuffer struct {32 Path string3334 // LenHint is the size of the stored blob. It can35 // be set to avoid the need to call os.Stat in the36 // Len() method.37 LenHint int38}3940func (fb FileBuffer) Open() (io.ReadCloser, error) {41 return os.Open(fb.Path)42}4344func (fb FileBuffer) Len() int {45 if fb.LenHint != 0 {46 return fb.LenHint47 }4849 info, err := os.Stat(fb.Path)50 if err != nil {51 // Any access to the file will probably fail too. So we can't return a52 // sensible value.53 return 054 }5556 return int(info.Size())57}5859func (fb FileBuffer) Remove() error {60 return os.Remove(fb.Path)61}6263// BufferInFile is a convenience function which creates FileBuffer with underlying64// file created in the specified directory with the random name.65func BufferInFile(r io.Reader, dir string) (Buffer, error) {66 // It is assumed that PRNG is initialized somewhere during program startup.67 nameBytes := make([]byte, 32)68 _, err := rand.Read(nameBytes)69 if err != nil {70 return nil, fmt.Errorf("buffer: failed to generate randomness for file name: %v", err)71 }72 path := filepath.Join(dir, hex.EncodeToString(nameBytes))73 f, err := os.Create(path)74 if err != nil {75 return nil, fmt.Errorf("buffer: failed to create file: %v", err)76 }77 if _, err = io.Copy(f, r); err != nil {78 return nil, fmt.Errorf("buffer: failed to write file: %v", err)79 }80 if err := f.Close(); err != nil {81 return nil, fmt.Errorf("buffer: failed to close file: %v", err)82 }8384 return FileBuffer{Path: path}, nil85}