package mail import ( "context" "errors" "io" "path" "strconv" "strings" ) // DriveUploader copies mail attachment bytes into a user's Nextcloud drive. type DriveUploader interface { EnsureNextcloudFolder(ctx context.Context, email, sub, displayName, folderPath string) error UploadFile(ctx context.Context, email, sub, displayName, destPath string, body io.Reader, contentType string, size int64) error FileExists(ctx context.Context, email, sub, displayName, filePath string) (bool, error) NotifyFileChanged(platformUserID, filePath string) } var ErrDriveUnavailable = errors.New("drive unavailable") func normalizeDriveFolder(folderPath string) string { p := strings.TrimSpace(folderPath) if p == "" { return "/" } if !strings.HasPrefix(p, "/") { p = "/" + p } p = path.Clean(p) if p == "." { return "/" } return p } func uniqueDriveFilePath(ctx context.Context, uploader DriveUploader, email, sub, displayName, folderPath, filename string) (string, error) { base := strings.TrimSpace(filename) if base == "" { base = "piece-jointe" } ext := path.Ext(base) stem := strings.TrimSuffix(base, ext) if stem == "" { stem = base ext = "" } candidate := path.Join(folderPath, base) for i := 0; i < 100; i++ { exists, err := uploader.FileExists(ctx, email, sub, displayName, candidate) if err != nil { return "", err } if !exists { return candidate, nil } if i == 0 { candidate = path.Join(folderPath, stem+" (1)"+ext) continue } candidate = path.Join(folderPath, stem+" ("+strconv.Itoa(i+1)+")"+ext) } return "", errors.New("could not allocate unique drive path") }