API: Improve path and filename parameter sanitation #1814

This commit is contained in:
Michael Mayer 2021-12-14 20:01:39 +01:00
parent cce371d35a
commit 0f0c0aaa0b
100 changed files with 683 additions and 452 deletions

View file

@ -21,7 +21,6 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
// SaveAlbumAsYaml saves album data as YAML file.
@ -38,7 +37,7 @@ func SaveAlbumAsYaml(a entity.Album) {
if err := a.SaveAsYaml(fileName); err != nil {
log.Errorf("album: %s (update yaml)", err)
} else {
log.Debugf("album: updated yaml file %s", txt.LogParam(filepath.Base(fileName)))
log.Debugf("album: updated yaml file %s", sanitize.Log(filepath.Base(fileName)))
}
}
@ -89,7 +88,7 @@ func CreateAlbum(router *gin.RouterGroup) {
a.AlbumFavorite = f.AlbumFavorite
if res := entity.Db().Create(a); res.Error != nil {
AbortAlreadyExists(c, txt.LogParam(a.AlbumTitle))
AbortAlreadyExists(c, sanitize.Log(a.AlbumTitle))
return
}
@ -199,7 +198,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
SaveAlbumAsYaml(a)
event.SuccessMsg(i18n.MsgAlbumDeleted, txt.LogParam(a.AlbumTitle))
event.SuccessMsg(i18n.MsgAlbumDeleted, sanitize.Log(a.AlbumTitle))
c.JSON(http.StatusOK, a)
})
@ -328,7 +327,7 @@ func CloneAlbums(router *gin.RouterGroup) {
}
if len(added) > 0 {
event.SuccessMsg(i18n.MsgSelectionAddedTo, txt.LogParam(a.Title()))
event.SuccessMsg(i18n.MsgSelectionAddedTo, sanitize.Log(a.Title()))
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
@ -378,9 +377,9 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
if len(added) > 0 {
if len(added) == 1 {
event.SuccessMsg(i18n.MsgEntryAddedTo, txt.LogParam(a.Title()))
event.SuccessMsg(i18n.MsgEntryAddedTo, sanitize.Log(a.Title()))
} else {
event.SuccessMsg(i18n.MsgEntriesAddedTo, len(added), txt.LogParam(a.Title()))
event.SuccessMsg(i18n.MsgEntriesAddedTo, len(added), sanitize.Log(a.Title()))
}
RemoveFromAlbumCoverCache(a.AlbumUID)
@ -429,9 +428,9 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
if len(removed) > 0 {
if len(removed) == 1 {
event.SuccessMsg(i18n.MsgEntryRemovedFrom, txt.LogParam(a.Title()))
event.SuccessMsg(i18n.MsgEntryRemovedFrom, sanitize.Log(a.Title()))
} else {
event.SuccessMsg(i18n.MsgEntriesRemovedFrom, len(removed), txt.LogParam(txt.LogParam(a.Title())))
event.SuccessMsg(i18n.MsgEntriesRemovedFrom, len(removed), sanitize.Log(sanitize.Log(a.Title())))
}
RemoveFromAlbumCoverCache(a.AlbumUID)
@ -481,12 +480,12 @@ func DownloadAlbum(router *gin.RouterGroup) {
for _, file := range files {
if file.FileHash == "" {
log.Warnf("download: empty file hash, skipped %s", txt.LogParam(file.FileName))
log.Warnf("download: empty file hash, skipped %s", sanitize.Log(file.FileName))
continue
}
if file.FileSidecar {
log.Debugf("download: skipped sidecar %s", txt.LogParam(file.FileName))
log.Debugf("download: skipped sidecar %s", sanitize.Log(file.FileName))
continue
}
@ -506,12 +505,12 @@ func DownloadAlbum(router *gin.RouterGroup) {
Abort(c, http.StatusInternalServerError, i18n.ErrZipFailed)
return
}
log.Infof("download: added %s as %s", txt.LogParam(file.FileName), txt.LogParam(alias))
log.Infof("download: added %s as %s", sanitize.Log(file.FileName), sanitize.Log(alias))
} else {
log.Errorf("download: failed finding %s", txt.LogParam(file.FileName))
log.Errorf("download: failed finding %s", sanitize.Log(file.FileName))
}
}
log.Infof("download: created %s [%s]", txt.LogParam(zipFileName), time.Since(start))
log.Infof("download: created %s [%s]", sanitize.Log(zipFileName), time.Since(start))
})
}

View file

@ -39,7 +39,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
var log = event.Log
@ -65,7 +65,7 @@ func UpdateClientConfig() {
func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
resp := i18n.NewResponse(code, id, params...)
log.Debugf("api: abort %s with code %d (%s)", txt.LogParam(c.FullPath()), code, resp.String())
log.Debugf("api: abort %s with code %d (%s)", sanitize.Log(c.FullPath()), code, resp.String())
c.AbortWithStatusJSON(code, resp)
}
@ -75,7 +75,7 @@ func Error(c *gin.Context, code int, err error, id i18n.Message, params ...inter
if err != nil {
resp.Details = err.Error()
log.Errorf("api: error %s with code %d in %s (%s)", txt.LogParam(err.Error()), code, txt.LogParam(c.FullPath()), resp.String())
log.Errorf("api: error %s with code %d in %s (%s)", sanitize.Log(err.Error()), code, sanitize.Log(c.FullPath()), resp.String())
}
c.AbortWithStatusJSON(code, resp)

View file

@ -14,8 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// BatchPhotosArchive moves multiple photos to the archive.
@ -42,7 +41,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
return
}
log.Infof("photos: archiving %s", txt.LogParam(f.String()))
log.Infof("photos: archiving %s", sanitize.Log(f.String()))
if service.Config().BackupYaml() {
photos, err := query.PhotoSelection(f)
@ -105,7 +104,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
return
}
log.Infof("photos: restoring %s", txt.LogParam(f.String()))
log.Infof("photos: restoring %s", sanitize.Log(f.String()))
if service.Config().BackupYaml() {
photos, err := query.PhotoSelection(f)
@ -167,7 +166,7 @@ func BatchPhotosApprove(router *gin.RouterGroup) {
return
}
log.Infof("photos: approving %s", txt.LogParam(f.String()))
log.Infof("photos: approving %s", sanitize.Log(f.String()))
photos, err := query.PhotoSelection(f)
@ -219,7 +218,7 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
return
}
log.Infof("albums: deleting %s", txt.LogParam(f.String()))
log.Infof("albums: deleting %s", sanitize.Log(f.String()))
entity.Db().Where("album_uid IN (?)", f.Albums).Delete(&entity.Album{})
entity.Db().Where("album_uid IN (?)", f.Albums).Delete(&entity.PhotoAlbum{})
@ -256,7 +255,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
return
}
log.Infof("photos: updating private flag for %s", txt.LogParam(f.String()))
log.Infof("photos: updating private flag for %s", sanitize.Log(f.String()))
if err := entity.Db().Model(entity.Photo{}).Where("photo_uid IN (?)", f.Photos).UpdateColumn("photo_private",
gorm.Expr("CASE WHEN photo_private > 0 THEN 0 ELSE 1 END")).Error; err != nil {
@ -309,7 +308,7 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
return
}
log.Infof("labels: deleting %s", txt.LogParam(f.String()))
log.Infof("labels: deleting %s", sanitize.Log(f.String()))
var labels entity.Labels
@ -361,7 +360,7 @@ func BatchPhotosDelete(router *gin.RouterGroup) {
return
}
log.Infof("photos: deleting %s", txt.LogParam(f.String()))
log.Infof("photos: deleting %s", sanitize.Log(f.String()))
photos, err := query.PhotoSelection(f)

View file

@ -13,7 +13,6 @@ import (
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
)
// Namespaces for caching and logs.
@ -45,7 +44,7 @@ func AlbumCover(router *gin.RouterGroup) {
size, ok := thumb.Sizes[thumbName]
if !ok {
log.Errorf("%s: invalid size %s", albumCover, txt.LogParam(thumbName.String()))
log.Errorf("%s: invalid size %s", albumCover, sanitize.Log(thumbName.String()))
c.Data(http.StatusOK, "image/svg+xml", albumIconSvg)
return
}
@ -86,11 +85,11 @@ func AlbumCover(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("%s: found no original for %s", albumCover, txt.LogParam(fileName))
log.Errorf("%s: found no original for %s", albumCover, sanitize.Log(fileName))
c.Data(http.StatusOK, "image/svg+xml", albumIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
log.Warnf("%s: %s is missing", albumCover, txt.LogParam(f.FileName))
log.Warnf("%s: %s is missing", albumCover, sanitize.Log(f.FileName))
logError(albumCover, f.Update("FileMissing", true))
return
}
@ -157,7 +156,7 @@ func LabelCover(router *gin.RouterGroup) {
size, ok := thumb.Sizes[thumbName]
if !ok {
log.Errorf("%s: invalid size %s", labelCover, txt.LogParam(thumbName.String()))
log.Errorf("%s: invalid size %s", labelCover, sanitize.Log(thumbName.String()))
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
return
}
@ -198,7 +197,7 @@ func LabelCover(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("%s: file %s is missing", labelCover, txt.LogParam(f.FileName))
log.Errorf("%s: file %s is missing", labelCover, sanitize.Log(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.

View file

@ -12,7 +12,6 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
// TODO: GET /api/v1/dl/file/:hash
@ -56,7 +55,7 @@ func GetDownload(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("download: file %s is missing", txt.LogParam(f.FileName))
log.Errorf("download: file %s is missing", sanitize.Log(f.FileName))
c.Data(404, "image/svg+xml", brokenIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.

View file

@ -13,7 +13,6 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
)
// DELETE /api/v1/photos/:uid/files/:file_uid
@ -60,17 +59,17 @@ func DeleteFile(router *gin.RouterGroup) {
mediaFile, err := photoprism.NewMediaFile(fileName)
if err != nil {
log.Errorf("photo: %s (delete %s)", err, txt.LogParam(baseName))
log.Errorf("photo: %s (delete %s)", err, sanitize.Log(baseName))
AbortEntityNotFound(c)
return
}
if err := mediaFile.Remove(); err != nil {
log.Errorf("photo: %s (delete %s from folder)", err, txt.LogParam(baseName))
log.Errorf("photo: %s (delete %s from folder)", err, sanitize.Log(baseName))
}
if err := file.Delete(true); err != nil {
log.Errorf("photo: %s (delete %s from index)", err, txt.LogParam(baseName))
log.Errorf("photo: %s (delete %s from index)", err, sanitize.Log(baseName))
AbortDeleteFailed(c)
return
}

View file

@ -13,7 +13,6 @@ import (
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
)
const (
@ -99,7 +98,7 @@ func FolderCover(router *gin.RouterGroup) {
c.Data(http.StatusOK, "image/svg+xml", folderIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
log.Warnf("%s: %s is missing", folderCover, txt.LogParam(f.FileName))
log.Warnf("%s: %s is missing", folderCover, sanitize.Log(f.FileName))
logError(folderCover, f.Update("FileMissing", true))
return
}

View file

@ -7,18 +7,19 @@ import (
"strings"
"time"
"github.com/photoprism/photoprism/internal/query"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// StartImport imports media files from a directory and converts/indexes them as needed.
@ -52,7 +53,7 @@ func StartImport(router *gin.RouterGroup) {
subPath := ""
path := conf.ImportPath()
if subPath = c.Param("path"); subPath != "" && subPath != "/" {
if subPath = sanitize.Path(c.Param("path")); subPath != "" && subPath != "/" {
subPath = strings.Replace(subPath, ".", "", -1)
path = filepath.Join(path, subPath)
} else if f.Path != "" {
@ -69,15 +70,15 @@ func StartImport(router *gin.RouterGroup) {
var opt photoprism.ImportOptions
if f.Move {
event.InfoMsg(i18n.MsgMovingFilesFrom, txt.LogParam(filepath.Base(path)))
event.InfoMsg(i18n.MsgMovingFilesFrom, sanitize.Log(filepath.Base(path)))
opt = photoprism.ImportOptionsMove(path)
} else {
event.InfoMsg(i18n.MsgCopyingFilesFrom, txt.LogParam(filepath.Base(path)))
event.InfoMsg(i18n.MsgCopyingFilesFrom, sanitize.Log(filepath.Base(path)))
opt = photoprism.ImportOptionsCopy(path)
}
if len(f.Albums) > 0 {
log.Debugf("import: adding files to album %s", txt.LogParam(strings.Join(f.Albums, " and ")))
log.Debugf("import: adding files to album %s", sanitize.Log(strings.Join(f.Albums, " and ")))
opt.Albums = f.Albums
}
@ -85,9 +86,9 @@ func StartImport(router *gin.RouterGroup) {
if subPath != "" && path != conf.ImportPath() && fs.IsEmpty(path) {
if err := os.Remove(path); err != nil {
log.Errorf("import: failed deleting empty folder %s: %s", txt.LogParam(path), err)
log.Errorf("import: failed deleting empty folder %s: %s", sanitize.Log(path), err)
} else {
log.Infof("import: deleted empty folder %s", txt.LogParam(path))
log.Infof("import: deleted empty folder %s", sanitize.Log(path))
}
}

View file

@ -13,6 +13,7 @@ import (
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -56,7 +57,7 @@ func StartIndexing(router *gin.RouterGroup) {
}
if len(indOpt.Path) > 1 {
event.InfoMsg(i18n.MsgIndexingFiles, txt.LogParam(indOpt.Path))
event.InfoMsg(i18n.MsgIndexingFiles, sanitize.Log(indOpt.Path))
} else {
event.InfoMsg(i18n.MsgIndexingOriginals)
}

View file

@ -17,7 +17,6 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
// SavePhotoAsYaml saves photo data as YAML file.
@ -34,7 +33,7 @@ func SavePhotoAsYaml(p entity.Photo) {
if err := p.SaveAsYaml(fileName); err != nil {
log.Errorf("photo: %s (update yaml)", err)
} else {
log.Debugf("photo: updated yaml file %s", txt.LogParam(filepath.Base(fileName)))
log.Debugf("photo: updated yaml file %s", sanitize.Log(filepath.Base(fileName)))
}
}
@ -147,7 +146,7 @@ func GetPhotoDownload(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("photo: file %s is missing", txt.LogParam(f.FileName))
log.Errorf("photo: file %s is missing", sanitize.Log(f.FileName))
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.

View file

@ -15,7 +15,6 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
// GetThumb returns a thumbnail image matching the file hash, crop area, and type.
@ -47,7 +46,7 @@ func GetThumb(router *gin.RouterGroup) {
cropSize, ok := crop.Sizes[cropName]
if !ok {
log.Errorf("%s: invalid size %s", logPrefix, txt.LogParam(string(cropName)))
log.Errorf("%s: invalid size %s", logPrefix, sanitize.Log(string(cropName)))
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
return
}
@ -80,7 +79,7 @@ func GetThumb(router *gin.RouterGroup) {
size, ok := thumb.Sizes[thumbName]
if !ok {
log.Errorf("%s: invalid size %s", logPrefix, txt.LogParam(thumbName.String()))
log.Errorf("%s: invalid size %s", logPrefix, sanitize.Log(thumbName.String()))
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
return
}
@ -155,17 +154,17 @@ func GetThumb(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("%s: file %s is missing", logPrefix, txt.LogParam(f.FileName))
log.Errorf("%s: file %s is missing", logPrefix, sanitize.Log(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
logError(logPrefix, f.Update("FileMissing", true))
if f.AllFilesMissing() {
log.Infof("%s: deleting photo, all files missing for %s", logPrefix, txt.LogParam(f.FileName))
log.Infof("%s: deleting photo, all files missing for %s", logPrefix, sanitize.Log(f.FileName))
if _, err := f.RelatedPhoto().Delete(false); err != nil {
log.Errorf("%s: %s while deleting %s", logPrefix, err, txt.LogParam(f.FileName))
log.Errorf("%s: %s while deleting %s", logPrefix, err, sanitize.Log(f.FileName))
}
}

View file

@ -15,7 +15,6 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
)
// POST /api/v1/photos/:uid/files/:file_uid/unstack
@ -62,7 +61,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
unstackFile, err := photoprism.NewMediaFile(fileName)
if err != nil {
log.Errorf("photo: %s (unstack %s)", err, txt.LogParam(baseName))
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
AbortEntityNotFound(c)
return
}
@ -71,7 +70,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
stackPrimary, err := stackPhoto.PrimaryFile()
if err != nil {
log.Errorf("photo: can't find primary file for %s (unstack)", txt.LogParam(baseName))
log.Errorf("photo: can't find primary file for %s (unstack)", sanitize.Log(baseName))
AbortUnexpected(c)
return
}
@ -79,15 +78,15 @@ func PhotoUnstack(router *gin.RouterGroup) {
related, err := unstackFile.RelatedFiles(false)
if err != nil {
log.Errorf("photo: %s (unstack %s)", err, txt.LogParam(baseName))
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
AbortEntityNotFound(c)
return
} else if related.Len() == 0 {
log.Errorf("photo: found no files for %s (unstack)", txt.LogParam(baseName))
log.Errorf("photo: found no files for %s (unstack)", sanitize.Log(baseName))
AbortEntityNotFound(c)
return
} else if related.Main == nil {
log.Errorf("photo: found no main file for %s (unstack)", txt.LogParam(baseName))
log.Errorf("photo: found no main file for %s (unstack)", sanitize.Log(baseName))
AbortEntityNotFound(c)
return
}
@ -97,7 +96,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
if unstackFile.BasePrefix(false) == stackPhoto.PhotoName {
if conf.ReadOnly() {
log.Errorf("photo: can't rename files in read only mode (unstack %s)", txt.LogParam(baseName))
log.Errorf("photo: can't rename files in read only mode (unstack %s)", sanitize.Log(baseName))
AbortFeatureDisabled(c)
return
}
@ -105,7 +104,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
destName := fmt.Sprintf("%s.%s%s", unstackFile.AbsPrefix(false), unstackFile.Checksum(), unstackFile.Extension())
if err := unstackFile.Move(destName); err != nil {
log.Errorf("photo: can't rename %s to %s (unstack)", txt.LogParam(unstackFile.BaseName()), txt.LogParam(filepath.Base(destName)))
log.Errorf("photo: can't rename %s to %s (unstack)", sanitize.Log(unstackFile.BaseName()), sanitize.Log(filepath.Base(destName)))
AbortUnexpected(c)
return
}
@ -121,7 +120,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
newPhoto.PhotoName = unstackFile.BasePrefix(false)
if err := newPhoto.Create(); err != nil {
log.Errorf("photo: %s (unstack %s)", err.Error(), txt.LogParam(baseName))
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(baseName))
AbortSaveFailed(c)
return
}
@ -141,17 +140,17 @@ func PhotoUnstack(router *gin.RouterGroup) {
newPhoto.ID, newPhoto.PhotoUID, r.RootRelName(),
relName, relRoot).Error; err != nil {
// Handle error...
log.Errorf("photo: %s (unstack %s)", err.Error(), txt.LogParam(r.BaseName()))
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(r.BaseName()))
// Remove new photo from index.
if _, err := newPhoto.Delete(true); err != nil {
log.Errorf("photo: %s (unstack %s)", err.Error(), txt.LogParam(r.BaseName()))
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(r.BaseName()))
}
// Revert file rename.
if unstackSingle {
if err := r.Move(photoprism.FileName(relRoot, relName)); err != nil {
log.Errorf("photo: %s (unstack %s)", err.Error(), txt.LogParam(r.BaseName()))
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(r.BaseName()))
}
}
@ -164,21 +163,21 @@ func PhotoUnstack(router *gin.RouterGroup) {
// Index unstacked files.
if res := ind.FileName(unstackFile.FileName(), photoprism.IndexOptionsSingle()); res.Failed() {
log.Errorf("photo: %s (unstack %s)", res.Err, txt.LogParam(baseName))
log.Errorf("photo: %s (unstack %s)", res.Err, sanitize.Log(baseName))
AbortSaveFailed(c)
return
}
// Reset type for existing photo stack to image.
if err := stackPhoto.Update("PhotoType", entity.TypeImage); err != nil {
log.Errorf("photo: %s (unstack %s)", err, txt.LogParam(baseName))
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
AbortUnexpected(c)
return
}
// Re-index existing photo stack.
if res := ind.FileName(photoprism.FileName(stackPrimary.FileRoot, stackPrimary.FileName), photoprism.IndexOptionsSingle()); res.Failed() {
log.Errorf("photo: %s (unstack %s)", res.Err, txt.LogParam(baseName))
log.Errorf("photo: %s (unstack %s)", res.Err, sanitize.Log(baseName))
AbortSaveFailed(c)
return
}

View file

@ -6,6 +6,8 @@ import (
"path/filepath"
"time"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
@ -66,7 +68,7 @@ func SearchFolders(router *gin.RouterGroup, urlPath, rootName, rootPath string)
listFiles := f.Files
uncached := listFiles || f.Uncached
resp := FoldersResponse{Root: rootName, Recursive: recursive, Cached: !uncached}
path := c.Param("path")
path := sanitize.Path(c.Param("path"))
cacheKey := fmt.Sprintf("folder:%s:%t:%t", filepath.Join(rootName, path), recursive, listFiles)

View file

@ -131,7 +131,7 @@ func InvalidPreviewToken(c *gin.Context) bool {
token := sanitize.Token(c.Param("token"))
if token == "" {
token = c.Query("t")
token = sanitize.Token(c.Query("t"))
}
return service.Config().InvalidPreviewToken(token)
@ -139,5 +139,5 @@ func InvalidPreviewToken(c *gin.Context) bool {
// InvalidDownloadToken returns true if the token is invalid.
func InvalidDownloadToken(c *gin.Context) bool {
return service.Config().InvalidDownloadToken(c.Query("t"))
return service.Config().InvalidDownloadToken(sanitize.Token(c.Query("t")))
}

View file

@ -20,7 +20,6 @@ import (
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
)
// SharePreview returns a link share preview image.
@ -53,13 +52,13 @@ func SharePreview(router *gin.RouterGroup) {
yesterday := time.Now().Add(-24 * time.Hour)
if info, err := os.Stat(previewFilename); err != nil {
log.Debugf("share: creating new preview for %s", txt.LogParam(share))
log.Debugf("share: creating new preview for %s", sanitize.Log(share))
} else if info.ModTime().After(yesterday) {
log.Debugf("share: using cached preview for %s", txt.LogParam(share))
log.Debugf("share: using cached preview for %s", sanitize.Log(share))
c.File(previewFilename)
return
} else if err := os.Remove(previewFilename); err != nil {
log.Errorf("share: could not remove old preview of %s", txt.LogParam(share))
log.Errorf("share: could not remove old preview of %s", sanitize.Log(share))
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
return
}
@ -97,7 +96,7 @@ func SharePreview(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("share: file %s is missing (preview)", txt.LogParam(f.FileName))
log.Errorf("share: file %s is missing (preview)", sanitize.Log(f.FileName))
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
return
}
@ -127,7 +126,7 @@ func SharePreview(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("share: file %s is missing (preview)", txt.LogParam(f.FileName))
log.Errorf("share: file %s is missing (preview)", sanitize.Log(f.FileName))
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
return
}

View file

@ -7,13 +7,12 @@ import (
"path/filepath"
"time"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// POST /api/v1/upload/:path
@ -33,7 +32,7 @@ func Upload(router *gin.RouterGroup) {
}
start := time.Now()
subPath := c.Param("path")
subPath := sanitize.Path(c.Param("path"))
f, err := c.MultipartForm()
@ -52,7 +51,7 @@ func Upload(router *gin.RouterGroup) {
p := path.Join(conf.ImportPath(), "upload", subPath)
if err := os.MkdirAll(p, os.ModePerm); err != nil {
log.Errorf("upload: failed creating folder %s", txt.LogParam(subPath))
log.Errorf("upload: failed creating folder %s", sanitize.Log(subPath))
AbortBadRequest(c)
return
}
@ -60,10 +59,10 @@ func Upload(router *gin.RouterGroup) {
for _, file := range files {
filename := path.Join(p, filepath.Base(file.Filename))
log.Debugf("upload: saving file %s", txt.LogParam(file.Filename))
log.Debugf("upload: saving file %s", sanitize.Log(file.Filename))
if err := c.SaveUploadedFile(file, filename); err != nil {
log.Errorf("upload: failed saving file %s", txt.LogParam(filepath.Base(file.Filename)))
log.Errorf("upload: failed saving file %s", sanitize.Log(filepath.Base(file.Filename)))
AbortBadRequest(c)
return
}
@ -88,7 +87,7 @@ func Upload(router *gin.RouterGroup) {
continue
}
log.Infof("nsfw: %s might be offensive", txt.LogParam(filename))
log.Infof("nsfw: %s might be offensive", sanitize.Log(filename))
containsNSFW = true
}
@ -96,7 +95,7 @@ func Upload(router *gin.RouterGroup) {
if containsNSFW {
for _, filename := range uploads {
if err := os.Remove(filename); err != nil {
log.Errorf("nsfw: could not delete %s", txt.LogParam(filename))
log.Errorf("nsfw: could not delete %s", sanitize.Log(filename))
}
}

View file

@ -11,7 +11,6 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/video"
"github.com/photoprism/photoprism/pkg/txt"
)
// GET /api/v1/videos/:hash/:token/:type
@ -32,7 +31,7 @@ func GetVideo(router *gin.RouterGroup) {
videoType, ok := video.Types[typeName]
if !ok {
log.Errorf("video: invalid type %s", txt.LogParam(typeName))
log.Errorf("video: invalid type %s", sanitize.Log(typeName))
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
return
}
@ -64,7 +63,7 @@ func GetVideo(router *gin.RouterGroup) {
fileName := photoprism.FileName(f.FileRoot, f.FileName)
if mf, err := photoprism.NewMediaFile(fileName); err != nil {
log.Errorf("video: file %s is missing", txt.LogParam(f.FileName))
log.Errorf("video: file %s is missing", sanitize.Log(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
@ -75,7 +74,7 @@ func GetVideo(router *gin.RouterGroup) {
conv := service.Convert()
if avcFile, err := conv.ToAvc(mf, service.Config().FFmpegEncoder()); err != nil {
log.Errorf("video: transcoding %s failed", txt.LogParam(f.FileName))
log.Errorf("video: transcoding %s failed", sanitize.Log(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
return
} else {

View file

@ -19,9 +19,10 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// POST /api/v1/zip
@ -92,12 +93,12 @@ func CreateZip(router *gin.RouterGroup) {
for _, file := range files {
if file.FileHash == "" {
log.Warnf("download: empty file hash, skipped %s", txt.LogParam(file.FileName))
log.Warnf("download: empty file hash, skipped %s", sanitize.Log(file.FileName))
continue
}
if file.FileSidecar {
log.Debugf("download: skipped sidecar %s", txt.LogParam(file.FileName))
log.Debugf("download: skipped sidecar %s", sanitize.Log(file.FileName))
continue
}
@ -116,16 +117,16 @@ func CreateZip(router *gin.RouterGroup) {
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
return
}
log.Infof("download: added %s as %s", txt.LogParam(file.FileName), txt.LogParam(alias))
log.Infof("download: added %s as %s", sanitize.Log(file.FileName), sanitize.Log(alias))
} else {
log.Warnf("download: file %s is missing", txt.LogParam(file.FileName))
log.Warnf("download: file %s is missing", sanitize.Log(file.FileName))
logError("download", file.Update("FileMissing", true))
}
}
elapsed := int(time.Since(start).Seconds())
log.Infof("download: created %s [%s]", txt.LogParam(zipBaseName), time.Since(start))
log.Infof("download: created %s [%s]", sanitize.Log(zipBaseName), time.Since(start))
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgZipCreatedIn, elapsed), "filename": zipBaseName})
})
@ -140,12 +141,12 @@ func DownloadZip(router *gin.RouterGroup) {
}
conf := service.Config()
zipBaseName := filepath.Base(c.Param("filename"))
zipBaseName := sanitize.FileName(filepath.Base(c.Param("filename")))
zipPath := path.Join(conf.TempPath(), "zip")
zipFileName := path.Join(zipPath, zipBaseName)
if !fs.FileExists(zipFileName) {
log.Errorf("could not find zip file: %s", txt.LogParam(zipFileName))
log.Errorf("could not find zip file: %s", sanitize.Log(zipFileName))
c.Data(404, "image/svg+xml", photoIconSvg)
return
}
@ -153,7 +154,7 @@ func DownloadZip(router *gin.RouterGroup) {
c.FileAttachment(zipFileName, zipBaseName)
if err := os.Remove(zipFileName); err != nil {
log.Errorf("download: failed removing %s (%s)", txt.LogParam(zipFileName), err.Error())
log.Errorf("download: failed removing %s (%s)", sanitize.Log(zipFileName), err.Error())
}
})
}

View file

@ -12,7 +12,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
var autoImport = time.Time{}
@ -66,7 +66,7 @@ func Import() error {
api.RemoveFromFolderCache(entity.RootImport)
event.InfoMsg(i18n.MsgCopyingFilesFrom, txt.LogParam(filepath.Base(path)))
event.InfoMsg(i18n.MsgCopyingFilesFrom, sanitize.Log(filepath.Base(path)))
var opt photoprism.ImportOptions

View file

@ -12,7 +12,7 @@ import (
"unicode"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
"gopkg.in/yaml.v2"
)
@ -34,7 +34,7 @@ func main() {
fileName := "rules.yml"
if !fs.FileExists(fileName) {
log.Panicf("classify: found no label rules in %s", txt.LogParam(filepath.Base(fileName)))
log.Panicf("classify: found no label rules in %s", sanitize.Log(filepath.Base(fileName)))
}
yamlConfig, err := os.ReadFile(fileName)

View file

@ -14,7 +14,7 @@ import (
"strings"
"github.com/disintegration/imaging"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
tf "github.com/tensorflow/tensorflow/tensorflow/go"
)
@ -148,7 +148,7 @@ func (t *TensorFlow) loadModel() error {
modelPath := path.Join(t.modelsPath, t.modelName)
log.Infof("classify: loading %s", txt.LogParam(filepath.Base(modelPath)))
log.Infof("classify: loading %s", sanitize.Log(filepath.Base(modelPath)))
// Load model
model, err := tf.LoadSavedModel(modelPath, t.modelTags, nil)

View file

@ -17,7 +17,7 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// BackupCommand configures the backup cli command.
@ -113,7 +113,7 @@ func backupAction(ctx *cli.Context) error {
}
}
log.Infof("backing up database to %s", txt.LogParam(indexFileName))
log.Infof("backing up database to %s", sanitize.Log(indexFileName))
}
var cmd *exec.Cmd
@ -175,7 +175,7 @@ func backupAction(ctx *cli.Context) error {
albumsPath = conf.AlbumsPath()
}
log.Infof("backing up albums to %s", txt.LogParam(albumsPath))
log.Infof("backing up albums to %s", sanitize.Log(albumsPath))
if count, err := photoprism.BackupAlbums(albumsPath, true); err != nil {
return err

View file

@ -10,7 +10,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// ConvertCommand registers the convert cli command.
@ -48,7 +48,7 @@ func convertAction(ctx *cli.Context) error {
convertPath = filepath.Join(convertPath, subPath)
}
log.Infof("converting originals in %s", txt.LogParam(convertPath))
log.Infof("converting originals in %s", sanitize.Log(convertPath))
w := service.Convert()

View file

@ -15,7 +15,7 @@ import (
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// FacesCommand registers the facial recognition subcommands.
@ -239,9 +239,9 @@ func facesIndexAction(ctx *cli.Context) error {
subPath := strings.TrimSpace(ctx.Args().First())
if subPath == "" {
log.Infof("finding faces in %s", txt.LogParam(conf.OriginalsPath()))
log.Infof("finding faces in %s", sanitize.Log(conf.OriginalsPath()))
} else {
log.Infof("finding faces in %s", txt.LogParam(filepath.Join(conf.OriginalsPath(), subPath)))
log.Infof("finding faces in %s", sanitize.Log(filepath.Join(conf.OriginalsPath(), subPath)))
}
if conf.ReadOnly() {

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// IndexCommand registers the index cli command.
@ -57,9 +57,9 @@ func indexAction(ctx *cli.Context) error {
subPath := strings.TrimSpace(ctx.Args().First())
if subPath == "" {
log.Infof("indexing originals in %s", txt.LogParam(conf.OriginalsPath()))
log.Infof("indexing originals in %s", sanitize.Log(conf.OriginalsPath()))
} else {
log.Infof("indexing originals in %s", txt.LogParam(filepath.Join(conf.OriginalsPath(), subPath)))
log.Infof("indexing originals in %s", sanitize.Log(filepath.Join(conf.OriginalsPath(), subPath)))
}
if conf.ReadOnly() {

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// PasswdCommand updates a password.
@ -39,7 +39,7 @@ func passwdAction(ctx *cli.Context) error {
user := entity.Admin
log.Infof("please enter a new password for %s (at least 6 characters)\n", txt.LogParam(user.Username()))
log.Infof("please enter a new password for %s (at least 6 characters)\n", sanitize.Log(user.Username()))
newPassword := getPassword("New Password: ")
@ -57,7 +57,7 @@ func passwdAction(ctx *cli.Context) error {
return err
}
log.Infof("changed password for %s\n", txt.LogParam(user.Username()))
log.Infof("changed password for %s\n", sanitize.Log(user.Username()))
conf.Shutdown()

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// PurgeCommand registers the index cli command.
@ -56,9 +56,9 @@ func purgeAction(ctx *cli.Context) error {
subPath := strings.TrimSpace(ctx.Args().First())
if subPath == "" {
log.Infof("purge: removing missing files in %s", txt.LogParam(filepath.Base(conf.OriginalsPath())))
log.Infof("purge: removing missing files in %s", sanitize.Log(filepath.Base(conf.OriginalsPath())))
} else {
log.Infof("purge: removing missing files in %s", txt.LogParam(fs.RelName(filepath.Join(conf.OriginalsPath(), subPath), filepath.Dir(conf.OriginalsPath()))))
log.Infof("purge: removing missing files in %s", sanitize.Log(fs.RelName(filepath.Join(conf.OriginalsPath(), subPath), filepath.Dir(conf.OriginalsPath()))))
}
if conf.ReadOnly() {

View file

@ -19,7 +19,7 @@ import (
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// RestoreCommand configures the backup cli command.
@ -123,7 +123,7 @@ func restoreAction(ctx *cli.Context) error {
log.Warnf("replacing existing index with %d photos", counts.Photos)
}
log.Infof("restoring index from %s", txt.LogParam(indexFileName))
log.Infof("restoring index from %s", sanitize.Log(indexFileName))
sqlBackup, err := os.ReadFile(indexFileName)
@ -199,9 +199,9 @@ func restoreAction(ctx *cli.Context) error {
}
if !fs.PathExists(albumsPath) {
log.Warnf("albums backup path %s not found", txt.LogParam(albumsPath))
log.Warnf("albums backup path %s not found", sanitize.Log(albumsPath))
} else {
log.Infof("restoring albums from %s", txt.LogParam(albumsPath))
log.Infof("restoring albums from %s", sanitize.Log(albumsPath))
if count, err := photoprism.RestoreAlbums(albumsPath, true); err != nil {
return err

View file

@ -19,7 +19,7 @@ import (
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/workers"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// StartCommand registers the start cli command.
@ -95,7 +95,7 @@ func startAction(ctx *cli.Context) error {
if child != nil {
if !fs.Overwrite(conf.PIDFilename(), []byte(strconv.Itoa(child.Pid))) {
log.Fatalf("failed writing process id to %s", txt.LogParam(conf.PIDFilename()))
log.Fatalf("failed writing process id to %s", sanitize.Log(conf.PIDFilename()))
}
log.Infof("daemon started with process id %v\n", child.Pid)

View file

@ -7,7 +7,7 @@ import (
"github.com/urfave/cli"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// StopCommand registers the stop cli command.
@ -22,7 +22,7 @@ var StopCommand = cli.Command{
func stopAction(ctx *cli.Context) error {
conf := config.NewConfig(ctx)
log.Infof("looking for pid in %s", txt.LogParam(conf.PIDFilename()))
log.Infof("looking for pid in %s", sanitize.Log(conf.PIDFilename()))
dcxt := new(daemon.Context)
dcxt.PidFileName = conf.PIDFilename()

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// ThumbsCommand registers the resample cli command.
@ -34,7 +34,7 @@ func thumbsAction(ctx *cli.Context) error {
return err
}
log.Infof("creating thumbnails in %s", txt.LogParam(conf.ThumbPath()))
log.Infof("creating thumbnails in %s", sanitize.Log(conf.ThumbPath()))
rs := service.Resample()

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// UsersCommand registers user management subcommands.
@ -183,7 +183,7 @@ func usersDeleteAction(ctx *cli.Context) error {
}
actionPrompt := promptui.Prompt{
Label: fmt.Sprintf("Delete %s?", txt.LogParam(userName)),
Label: fmt.Sprintf("Delete %s?", sanitize.Log(userName)),
IsConfirm: true,
}
@ -193,7 +193,7 @@ func usersDeleteAction(ctx *cli.Context) error {
} else if err := m.Delete(); err != nil {
return err
} else {
log.Infof("%s deleted", txt.LogParam(userName))
log.Infof("%s deleted", sanitize.Log(userName))
}
} else {
log.Infof("keeping user")
@ -242,7 +242,7 @@ func usersUpdateAction(ctx *cli.Context) error {
if err != nil {
return err
}
fmt.Printf("password successfully changed: %s\n", txt.LogParam(u.Username()))
fmt.Printf("password successfully changed: %s\n", sanitize.Log(u.Username()))
}
if ctx.IsSet("fullname") {
@ -261,7 +261,7 @@ func usersUpdateAction(ctx *cli.Context) error {
return err
}
fmt.Printf("user successfully updated: %s\n", txt.LogParam(u.Username()))
fmt.Printf("user successfully updated: %s\n", sanitize.Log(u.Username()))
return nil
})

View file

@ -31,7 +31,7 @@ import (
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
var log = event.Log
@ -124,7 +124,7 @@ func NewConfig(ctx *cli.Context) *Config {
if err := c.options.Load(configFile); err != nil {
log.Warnf("config: %s", err)
} else {
log.Debugf("config: options loaded from %s", txt.LogParam(configFile))
log.Debugf("config: options loaded from %s", sanitize.Log(configFile))
}
}
@ -192,7 +192,7 @@ func (c *Config) Init() error {
}
if cpuName := cpuid.CPU.BrandName; cpuName != "" {
log.Debugf("config: running on %s, %s memory detected", txt.LogParam(cpuid.CPU.BrandName), humanize.Bytes(TotalMem))
log.Debugf("config: running on %s, %s memory detected", sanitize.Log(cpuid.CPU.BrandName), humanize.Bytes(TotalMem))
}
// Check memory requirements.

View file

@ -8,7 +8,7 @@ import (
"path/filepath"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
func findExecutable(configBin, defaultBin string) (result string) {
@ -33,9 +33,9 @@ func findExecutable(configBin, defaultBin string) (result string) {
func (c *Config) CreateDirectories() error {
createError := func(path string, err error) (result error) {
if fs.FileExists(path) {
result = fmt.Errorf("%s is a file, not a folder: please check your configuration", txt.LogParam(path))
result = fmt.Errorf("%s is a file, not a folder: please check your configuration", sanitize.Log(path))
} else {
result = fmt.Errorf("can't create %s: please check configuration and permissions", txt.LogParam(path))
result = fmt.Errorf("can't create %s: please check configuration and permissions", sanitize.Log(path))
}
log.Debug(err)

View file

@ -8,7 +8,7 @@ import (
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
"gopkg.in/yaml.v2"
)
@ -181,7 +181,7 @@ func (s Settings) StackMeta() bool {
// Load user settings from file.
func (s *Settings) Load(fileName string) error {
if !fs.FileExists(fileName) {
return fmt.Errorf("settings file not found: %s", txt.LogParam(fileName))
return fmt.Errorf("settings file not found: %s", sanitize.Log(fileName))
}
yamlConfig, err := os.ReadFile(fileName)

View file

@ -7,7 +7,7 @@ import (
"path"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// FromCache returns the crop file name if cached.
@ -28,7 +28,7 @@ func FromCache(hash, area string, size Size, thumbPath string) (fileName string,
// FileName returns the crop file name based on cache path, size, and area.
func FileName(hash, area string, width, height int, thumbPath string) (fileName string, err error) {
if len(hash) < 4 {
return "", fmt.Errorf("crop: invalid file hash %s", txt.LogParam(hash))
return "", fmt.Errorf("crop: invalid file hash %s", sanitize.Log(hash))
}
if len(thumbPath) < 1 {

View file

@ -9,11 +9,10 @@ import (
"path/filepath"
"strings"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/disintegration/imaging"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Filenames of usable thumb sizes.
@ -92,7 +91,7 @@ func ImageFromThumb(thumbName string, area Area, size Size, cache bool) (img ima
// ThumbFileName returns the ideal thumb file name.
func ThumbFileName(hash string, area Area, size Size, thumbPath string) (string, error) {
if len(hash) < 4 {
return "", fmt.Errorf("invalid file hash %s", txt.LogParam(hash))
return "", fmt.Errorf("invalid file hash %s", sanitize.Log(hash))
}
if len(thumbPath) < 1 {

View file

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
type Addresses []Address
@ -64,7 +64,7 @@ func FirstOrCreateAddress(m *Address) *Address {
// String returns an identifier that can be used in logs.
func (m *Address) String() string {
return txt.LogParam(fmt.Sprintf("%s, %s %s, %s", m.AddressLine1, m.AddressZip, m.AddressCity, m.AddressCountry))
return sanitize.Log(fmt.Sprintf("%s, %s %s, %s", m.AddressLine1, m.AddressZip, m.AddressCity, m.AddressCountry))
}
// Unknown returns true if the address is unknown.

View file

@ -15,6 +15,7 @@ import (
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -336,15 +337,15 @@ func (m *Album) BeforeCreate(scope *gorm.Scope) error {
// String returns the id or name as string.
func (m *Album) String() string {
if m.AlbumSlug != "" {
return txt.LogParam(m.AlbumSlug)
return sanitize.Log(m.AlbumSlug)
}
if m.AlbumTitle != "" {
return txt.LogParam(m.AlbumTitle)
return sanitize.Log(m.AlbumTitle)
}
if m.AlbumUID != "" {
return txt.LogParam(m.AlbumUID)
return sanitize.Log(m.AlbumUID)
}
return "[unknown album]"

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -123,7 +124,7 @@ func FirstOrCreateCamera(m *Camera) *Camera {
cameraCache.SetDefault(m.CameraSlug, &result)
return &result
} else {
log.Errorf("camera: %s (create %s)", err.Error(), txt.LogParam(m.String()))
log.Errorf("camera: %s (create %s)", err.Error(), sanitize.Log(m.String()))
}
return &UnknownCamera
@ -131,7 +132,7 @@ func FirstOrCreateCamera(m *Camera) *Camera {
// String returns an identifier that can be used in logs.
func (m *Camera) String() string {
return txt.LogParam(m.CameraName)
return sanitize.Log(m.CameraName)
}
// Unknown returns true if the camera is not a known make or model.

View file

@ -7,7 +7,7 @@ import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/migrate"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
type Tables map[string]interface{}
@ -56,10 +56,10 @@ func (list Tables) WaitForMigration(db *gorm.DB) {
for i := 0; i <= attempts; i++ {
count := RowCount{}
if err := db.Raw(fmt.Sprintf("SELECT COUNT(*) AS count FROM %s", name)).Scan(&count).Error; err == nil {
log.Tracef("entity: %s migrated", txt.LogParam(name))
log.Tracef("entity: %s migrated", sanitize.Log(name))
break
} else {
log.Debugf("entity: waiting for %s migration (%s)", txt.LogParam(name), err.Error())
log.Debugf("entity: waiting for %s migration (%s)", sanitize.Log(name), err.Error())
}
if i == attempts {
@ -78,7 +78,7 @@ func (list Tables) Truncate(db *gorm.DB) {
// log.Debugf("entity: removed all data from %s", name)
break
} else if err.Error() != "record not found" {
log.Debugf("entity: %s in %s", err, txt.LogParam(name))
log.Debugf("entity: %s in %s", err, sanitize.Log(name))
}
}
}
@ -92,7 +92,7 @@ func (list Tables) Migrate(db *gorm.DB, runFailed bool) {
time.Sleep(time.Second)
if err := db.AutoMigrate(entity).Error; err != nil {
log.Errorf("entity: failed migrating %s", txt.LogParam(name))
log.Errorf("entity: failed migrating %s", sanitize.Log(name))
panic(err)
}
}

View file

@ -3,7 +3,7 @@ package entity
import (
"fmt"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
type Duplicates []Duplicate
@ -56,7 +56,7 @@ func PurgeDuplicate(fileName, fileRoot string) error {
}
if err := UnscopedDb().Delete(Duplicate{}, "file_name = ? AND file_root = ?", fileName, fileRoot).Error; err != nil {
log.Errorf("duplicate: %s in %s (purge)", err, txt.LogParam(fileName))
log.Errorf("duplicate: %s in %s (purge)", err, sanitize.Log(fileName))
return err
}
@ -101,7 +101,7 @@ func (m *Duplicate) Save() error {
}
if err := UnscopedDb().Save(m).Error; err != nil {
log.Errorf("duplicate: %s in %s (save)", err, txt.LogParam(m.FileName))
log.Errorf("duplicate: %s in %s (save)", err, sanitize.Log(m.FileName))
return err
}

View file

@ -16,7 +16,7 @@ import (
"github.com/photoprism/photoprism/pkg/colors"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
type DownloadName string
@ -206,23 +206,23 @@ func (m File) Missing() bool {
// DeletePermanently permanently removes a file from the index.
func (m *File) DeletePermanently() error {
if m.ID < 1 || m.FileUID == "" {
return fmt.Errorf("invalid file id %d / uid %s", m.ID, txt.LogParam(m.FileUID))
return fmt.Errorf("invalid file id %d / uid %s", m.ID, sanitize.Log(m.FileUID))
}
if err := UnscopedDb().Delete(Marker{}, "file_uid = ?", m.FileUID).Error; err != nil {
log.Errorf("file %s: %s while removing markers", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while removing markers", sanitize.Log(m.FileUID), err)
}
if err := UnscopedDb().Delete(FileShare{}, "file_id = ?", m.ID).Error; err != nil {
log.Errorf("file %s: %s while removing share info", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while removing share info", sanitize.Log(m.FileUID), err)
}
if err := UnscopedDb().Delete(FileSync{}, "file_id = ?", m.ID).Error; err != nil {
log.Errorf("file %s: %s while removing remote sync info", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while removing remote sync info", sanitize.Log(m.FileUID), err)
}
if err := m.ReplaceHash(""); err != nil {
log.Errorf("file %s: %s while removing covers", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while removing covers", sanitize.Log(m.FileUID), err)
}
return UnscopedDb().Delete(m).Error
@ -237,9 +237,9 @@ func (m *File) ReplaceHash(newHash string) error {
// Log values.
if m.FileHash != "" && newHash == "" {
log.Tracef("file %s: removing hash %s", txt.LogParam(m.FileUID), txt.LogParam(m.FileHash))
log.Tracef("file %s: removing hash %s", sanitize.Log(m.FileUID), sanitize.Log(m.FileHash))
} else if m.FileHash != "" && newHash != "" {
log.Tracef("file %s: hash %s changed to %s", txt.LogParam(m.FileUID), txt.LogParam(m.FileHash), txt.LogParam(newHash))
log.Tracef("file %s: hash %s changed to %s", sanitize.Log(m.FileUID), sanitize.Log(m.FileHash), sanitize.Log(newHash))
// Reset error when hash changes.
m.FileError = ""
}
@ -275,7 +275,7 @@ func (m *File) ReplaceHash(newHash string) error {
// Delete deletes the entity from the database.
func (m *File) Delete(permanently bool) error {
if m.ID < 1 || m.FileUID == "" {
return fmt.Errorf("invalid file id %d / uid %s", m.ID, txt.LogParam(m.FileUID))
return fmt.Errorf("invalid file id %d / uid %s", m.ID, sanitize.Log(m.FileUID))
}
if permanently {
@ -326,7 +326,7 @@ func (m *File) Create() error {
}
if _, err := m.SaveMarkers(); err != nil {
log.Errorf("file %s: %s while saving markers", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while saving markers", sanitize.Log(m.FileUID), err)
return err
}
@ -349,12 +349,12 @@ func (m *File) Save() error {
}
if err := UnscopedDb().Save(m).Error; err != nil {
log.Errorf("file %s: %s while saving", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while saving", sanitize.Log(m.FileUID), err)
return err
}
if _, err := m.SaveMarkers(); err != nil {
log.Errorf("file %s: %s while saving markers", txt.LogParam(m.FileUID), err)
log.Errorf("file %s: %s while saving markers", sanitize.Log(m.FileUID), err)
return err
}
@ -384,7 +384,7 @@ func (m *File) Updates(values interface{}) error {
// Rename updates the name and path of this file.
func (m *File) Rename(fileName, rootName, filePath, fileBase string) error {
log.Debugf("file %s: renaming %s to %s", txt.LogParam(m.FileUID), txt.LogParam(m.FileName), txt.LogParam(fileName))
log.Debugf("file %s: renaming %s to %s", sanitize.Log(m.FileUID), sanitize.Log(m.FileName), sanitize.Log(fileName))
// Update database row.
if err := m.Updates(map[string]interface{}{
@ -428,7 +428,7 @@ func (m *File) Undelete() error {
return err
}
log.Debugf("file %s: removed missing flag from %s", txt.LogParam(m.FileUID), txt.LogParam(m.FileName))
log.Debugf("file %s: removed missing flag from %s", sanitize.Log(m.FileUID), sanitize.Log(m.FileName))
m.FileMissing = false
m.DeletedAt = nil
@ -562,7 +562,7 @@ func (m *File) Markers() *Markers {
} else if m.FileUID == "" {
m.markers = &Markers{}
} else if res, err := FindMarkers(m.FileUID); err != nil {
log.Warnf("file %s: %s while loading markers", txt.LogParam(m.FileUID), err)
log.Warnf("file %s: %s while loading markers", sanitize.Log(m.FileUID), err)
m.markers = &Markers{}
} else {
m.markers = &res

View file

@ -10,6 +10,7 @@ import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/ulule/deepcopier"
)
@ -179,7 +180,7 @@ func (m *Folder) Create() error {
if err := a.Create(); err != nil {
log.Errorf("folder: %s (add album)", err)
} else {
log.Infof("folder: added album %s (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Infof("folder: added album %s (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
}

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -124,7 +125,7 @@ func FirstOrCreateLens(m *Lens) *Lens {
lensCache.SetDefault(m.LensSlug, &result)
return &result
} else {
log.Errorf("lens: %s (create %s)", err.Error(), txt.LogParam(m.String()))
log.Errorf("lens: %s (create %s)", err.Error(), sanitize.Log(m.String()))
}
return &UnknownLens
@ -132,7 +133,7 @@ func FirstOrCreateLens(m *Lens) *Lens {
// String returns an identifier that can be used in logs.
func (m *Lens) String() string {
return txt.LogParam(m.LensName)
return sanitize.Log(m.LensName)
}
// Unknown returns true if the lens is not a known make or model.

View file

@ -7,6 +7,7 @@ import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -192,5 +193,5 @@ func FindValidLinks(token, share string) (result Links) {
// String returns an human readable identifier for logging.
func (m *Link) String() string {
return txt.LogParam(m.LinkUID)
return sanitize.Log(m.LinkUID)
}

View file

@ -15,6 +15,7 @@ import (
"github.com/photoprism/photoprism/internal/face"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -232,7 +233,7 @@ func (m *Marker) SetFace(f *Face, dist float64) (updated bool, err error) {
} else if reported, err := f.ResolveCollision(m.Embeddings()); err != nil {
return false, err
} else if reported {
log.Warnf("marker %s: face %s has ambiguous subjects %s <> %s, subject source %s", txt.LogParam(m.MarkerUID), txt.LogParam(f.ID), txt.LogParam(m.SubjUID), txt.LogParam(f.SubjUID), SrcString(m.SubjSrc))
log.Warnf("marker %s: face %s has ambiguous subjects %s <> %s, subject source %s", sanitize.Log(m.MarkerUID), sanitize.Log(f.ID), sanitize.Log(m.SubjUID), sanitize.Log(f.SubjUID), SrcString(m.SubjSrc))
return false, nil
} else {
return false, nil
@ -428,10 +429,10 @@ func (m *Marker) Subject() (subj *Subject) {
// Create subject?
if m.SubjSrc != SrcAuto && m.MarkerName != "" && m.SubjUID == "" {
if subj = NewSubject(m.MarkerName, SubjPerson, m.SubjSrc); subj == nil {
log.Errorf("marker %s: invalid subject %s", txt.LogParam(m.MarkerUID), txt.LogParam(m.MarkerName))
log.Errorf("marker %s: invalid subject %s", sanitize.Log(m.MarkerUID), sanitize.Log(m.MarkerName))
return nil
} else if subj = FirstOrCreateSubject(subj); subj == nil {
log.Debugf("marker %s: invalid subject %s", txt.LogParam(m.MarkerUID), txt.LogParam(m.MarkerName))
log.Debugf("marker %s: invalid subject %s", sanitize.Log(m.MarkerUID), sanitize.Log(m.MarkerName))
return nil
} else {
m.subject = subj
@ -457,9 +458,9 @@ func (m *Marker) ClearSubject(src string) error {
// Find and (soft) delete unused subjects.
start := time.Now()
if count, err := DeleteOrphanPeople(); err != nil {
log.Errorf("marker %s: %s while removing unused subjects [%s]", txt.LogParam(m.MarkerUID), err, time.Since(start))
log.Errorf("marker %s: %s while removing unused subjects [%s]", sanitize.Log(m.MarkerUID), err, time.Since(start))
} else if count > 0 {
log.Debugf("marker %s: removed %s [%s]", txt.LogParam(m.MarkerUID), english.Plural(count, "person", "people"), time.Since(start))
log.Debugf("marker %s: removed %s [%s]", sanitize.Log(m.MarkerUID), english.Plural(count, "person", "people"), time.Since(start))
}
}()
@ -472,7 +473,7 @@ func (m *Marker) ClearSubject(src string) error {
} else if resolved, err := m.face.ResolveCollision(m.Embeddings()); err != nil {
return err
} else if resolved {
log.Debugf("marker %s: resolved ambiguous subjects for face %s", txt.LogParam(m.MarkerUID), txt.LogParam(m.face.ID))
log.Debugf("marker %s: resolved ambiguous subjects for face %s", sanitize.Log(m.MarkerUID), sanitize.Log(m.face.ID))
}
// Clear references.
@ -498,21 +499,21 @@ func (m *Marker) Face() (f *Face) {
// Add face if size
if m.SubjSrc != SrcAuto && m.FaceID == "" {
if m.Size < face.ClusterSizeThreshold || m.Score < face.ClusterScoreThreshold {
log.Debugf("marker %s: skipped adding face due to low-quality (size %d, score %d)", txt.LogParam(m.MarkerUID), m.Size, m.Score)
log.Debugf("marker %s: skipped adding face due to low-quality (size %d, score %d)", sanitize.Log(m.MarkerUID), m.Size, m.Score)
return nil
} else if emb := m.Embeddings(); emb.Empty() {
log.Warnf("marker %s: found no face embeddings", txt.LogParam(m.MarkerUID))
log.Warnf("marker %s: found no face embeddings", sanitize.Log(m.MarkerUID))
return nil
} else if f = NewFace(m.SubjUID, m.SubjSrc, emb); f == nil {
log.Warnf("marker %s: failed assigning face", txt.LogParam(m.MarkerUID))
log.Warnf("marker %s: failed assigning face", sanitize.Log(m.MarkerUID))
return nil
} else if f.Unsuitable() {
log.Infof("marker %s: face %s is unsuitable for clustering and matching", txt.LogParam(m.MarkerUID), f.ID)
log.Infof("marker %s: face %s is unsuitable for clustering and matching", sanitize.Log(m.MarkerUID), f.ID)
} else if f = FirstOrCreateFace(f); f == nil {
log.Warnf("marker %s: failed assigning face", txt.LogParam(m.MarkerUID))
log.Warnf("marker %s: failed assigning face", sanitize.Log(m.MarkerUID))
return nil
} else if err := f.MatchMarkers(Faceless); err != nil {
log.Errorf("marker %s: %s while matching with faces", txt.LogParam(m.MarkerUID), err)
log.Errorf("marker %s: %s while matching with faces", sanitize.Log(m.MarkerUID), err)
}
m.face = f
@ -695,7 +696,7 @@ func FindFaceMarker(faceId string) *Marker {
if err := Db().Where("face_id = ?", faceId).
Where("thumb <> '' AND marker_invalid = 0").
Order("face_dist ASC, q DESC").First(&result).Error; err != nil {
log.Warnf("markers: found no marker for face %s", txt.LogParam(faceId))
log.Warnf("markers: found no marker for face %s", sanitize.Log(faceId))
return nil
}
@ -714,7 +715,7 @@ func CreateMarkerIfNotExists(m *Marker) (*Marker, error) {
} else if err := m.Create(); err != nil {
return m, err
} else {
log.Debugf("markers: added %s marker %s for %s", TypeString(m.MarkerType), txt.LogParam(m.MarkerUID), txt.LogParam(m.FileUID))
log.Debugf("markers: added %s marker %s for %s", TypeString(m.MarkerType), sanitize.Log(m.MarkerUID), sanitize.Log(m.FileUID))
}
return m, nil

View file

@ -17,6 +17,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -210,15 +211,15 @@ func SavePhotoForm(model Photo, form form.Photo) error {
func (m *Photo) String() string {
if m.PhotoUID == "" {
if m.PhotoName != "" {
return txt.LogParam(m.PhotoName)
return sanitize.Log(m.PhotoName)
} else if m.OriginalName != "" {
return txt.LogParam(m.OriginalName)
return sanitize.Log(m.OriginalName)
}
return "(unknown)"
}
return "uid " + txt.LogParam(m.PhotoUID)
return "uid " + sanitize.Log(m.PhotoUID)
}
// FirstOrCreate fetches an existing row from the database or inserts a new one.
@ -561,12 +562,12 @@ func (m *Photo) AddLabels(labels classify.Labels) {
labelEntity := FirstOrCreateLabel(NewLabel(classifyLabel.Title(), classifyLabel.Priority))
if labelEntity == nil {
log.Errorf("index: label %s should not be nil - bug? (%s)", txt.LogParam(classifyLabel.Title()), m)
log.Errorf("index: label %s should not be nil - bug? (%s)", sanitize.Log(classifyLabel.Title()), m)
continue
}
if labelEntity.Deleted() {
log.Debugf("index: skipping deleted label %s (%s)", txt.LogParam(classifyLabel.Title()), m)
log.Debugf("index: skipping deleted label %s (%s)", sanitize.Log(classifyLabel.Title()), m)
continue
}
@ -721,7 +722,7 @@ func (m *Photo) Restore() error {
// Delete deletes the photo from the index.
func (m *Photo) Delete(permanently bool) (files Files, err error) {
if m.ID < 1 || m.PhotoUID == "" {
return files, fmt.Errorf("invalid photo id %d / uid %s", m.ID, txt.LogParam(m.PhotoUID))
return files, fmt.Errorf("invalid photo id %d / uid %s", m.ID, sanitize.Log(m.PhotoUID))
}
if permanently {
@ -742,7 +743,7 @@ func (m *Photo) Delete(permanently bool) (files Files, err error) {
// DeletePermanently permanently removes a photo from the index.
func (m *Photo) DeletePermanently() (files Files, err error) {
if m.ID < 1 || m.PhotoUID == "" {
return files, fmt.Errorf("invalid photo id %d / uid %s", m.ID, txt.LogParam(m.PhotoUID))
return files, fmt.Errorf("invalid photo id %d / uid %s", m.ID, sanitize.Log(m.PhotoUID))
}
files = m.AllFiles()

View file

@ -7,6 +7,7 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/geo"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -52,7 +53,7 @@ func (m *Photo) EstimateCountry() {
m.PhotoCountry = countryCode
m.PlaceSrc = SrcEstimate
m.EstimatedAt = TimePointer()
log.Debugf("photo: estimated country for %s is %s", m, txt.LogParam(m.CountryName()))
log.Debugf("photo: estimated country for %s is %s", m, sanitize.Log(m.CountryName()))
}
}
@ -109,7 +110,7 @@ func (m *Photo) EstimateLocation(force bool) {
Order(gorm.Expr("ABS(JulianDay(taken_at) - JulianDay(?))", m.TakenAt)).Limit(2).
Preload("Place").Find(&mostRecent).Error
default:
log.Warnf("photo: unsupported sql dialect %s", txt.LogParam(DbDialect()))
log.Warnf("photo: unsupported sql dialect %s", sanitize.Log(DbDialect()))
return
}
@ -146,7 +147,7 @@ func (m *Photo) EstimateLocation(force bool) {
}
}
} else if recentPhoto.HasCountry() {
log.Debugf("photo: estimated country for %s is %s", m, txt.LogParam(m.CountryName()))
log.Debugf("photo: estimated country for %s is %s", m, sanitize.Log(m.CountryName()))
m.RemoveLocation(SrcEstimate, false)
m.RemoveLocationLabels()
m.PhotoCountry = recentPhoto.PhotoCountry

View file

@ -10,6 +10,7 @@ import (
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/pkg/geo"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
"gopkg.in/photoprism/go-tz.v2/tz"
)
@ -77,7 +78,7 @@ func (m *Photo) SetPosition(pos geo.Position, source string, force bool) {
if m.Place == nil {
log.Warnf("photo: failed updating position of %s", m)
} else {
log.Debugf("photo: approximate place of %s is %s (id %s)", m, txt.LogParam(m.Place.Label()), m.PlaceID)
log.Debugf("photo: approximate place of %s is %s (id %s)", m, sanitize.Log(m.Place.Label()), m.PlaceID)
}
}
}
@ -106,7 +107,7 @@ func (m *Photo) AdoptPlace(other Photo, source string, force bool) {
m.UpdateTimeZone(other.TimeZone)
log.Debugf("photo: %s now located at %s (id %s)", m.String(), txt.LogParam(m.Place.Label()), m.PlaceID)
log.Debugf("photo: %s now located at %s (id %s)", m.String(), sanitize.Log(m.Place.Label()), m.PlaceID)
}
// RemoveLocation removes the current location.

View file

@ -10,6 +10,7 @@ import (
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -66,7 +67,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
// TODO: User defined title format
if names != "" {
log.Debugf("photo: %s title based on %s (%s)", m.String(), english.Plural(len(people), "person", "people"), txt.LogParam(names))
log.Debugf("photo: %s title based on %s (%s)", m.String(), english.Plural(len(people), "person", "people"), sanitize.Log(names))
if l := len([]rune(names)); l > 35 {
m.SetTitle(names, SrcAuto)
@ -80,7 +81,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
m.SetTitle(fmt.Sprintf("%s / %s / %s", names, loc.City(), m.TakenAt.Format("2006")), SrcAuto)
}
} else if title := labels.Title(loc.Name()); title != "" {
log.Debugf("photo: %s title based on label %s", m.String(), txt.LogParam(title))
log.Debugf("photo: %s title based on label %s", m.String(), sanitize.Log(title))
if loc.NoCity() || loc.LongCity() || loc.CityContains(title) {
m.SetTitle(fmt.Sprintf("%s / %s / %s", txt.Title(title), loc.CountryName(), m.TakenAt.Format("2006")), SrcAuto)
} else {
@ -105,7 +106,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
knownLocation = true
if names != "" {
log.Debugf("photo: %s title based on %s (%s)", m.String(), english.Plural(len(people), "person", "people"), txt.LogParam(names))
log.Debugf("photo: %s title based on %s (%s)", m.String(), english.Plural(len(people), "person", "people"), sanitize.Log(names))
if l := len([]rune(names)); l > 35 {
m.SetTitle(names, SrcAuto)
@ -119,7 +120,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
m.SetTitle(fmt.Sprintf("%s / %s / %s", names, m.Place.City(), m.TakenAt.Format("2006")), SrcAuto)
}
} else if title := labels.Title(fileTitle); title != "" {
log.Debugf("photo: %s title based on label %s", m.String(), txt.LogParam(title))
log.Debugf("photo: %s title based on label %s", m.String(), sanitize.Log(title))
if m.Place.NoCity() || m.Place.LongCity() || m.Place.CityContains(title) {
m.SetTitle(fmt.Sprintf("%s / %s / %s", txt.Title(title), m.Place.CountryName(), m.TakenAt.Format("2006")), SrcAuto)
} else {
@ -161,7 +162,7 @@ func (m *Photo) UpdateTitle(labels classify.Labels) error {
}
if m.PhotoTitle != oldTitle {
log.Debugf("photo: %s has new title %s [%s]", m.String(), txt.LogParam(m.PhotoTitle), time.Since(start))
log.Debugf("photo: %s has new title %s [%s]", m.String(), sanitize.Log(m.PhotoTitle), time.Since(start))
}
return nil

View file

@ -6,7 +6,7 @@ import (
"time"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
var placeMutex = sync.Mutex{}
@ -54,7 +54,7 @@ func FindPlace(id string) *Place {
place := Place{}
if err := Db().Where("id = ?", id).First(&place).Error; err != nil {
log.Debugf("place: %s no found", txt.LogParam(id))
log.Debugf("place: %s no found", sanitize.Log(id))
return nil
} else {
return &place

View file

@ -12,6 +12,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -104,7 +105,7 @@ func (m *Subject) Delete() error {
subjectMutex.Lock()
defer subjectMutex.Unlock()
log.Infof("subject: deleting %s %s", TypeString(m.SubjType), txt.LogParam(m.SubjName))
log.Infof("subject: deleting %s %s", TypeString(m.SubjType), sanitize.Log(m.SubjName))
event.EntitiesDeleted("subjects", []string{m.SubjUID})
@ -141,7 +142,7 @@ func (m *Subject) Restore() error {
if m.Deleted() {
m.DeletedAt = nil
log.Infof("subject: restoring %s %s", TypeString(m.SubjType), txt.LogParam(m.SubjName))
log.Infof("subject: restoring %s %s", TypeString(m.SubjType), sanitize.Log(m.SubjName))
event.EntitiesCreated("subjects", []*Subject{m})
@ -179,7 +180,7 @@ func FirstOrCreateSubject(m *Subject) *Subject {
if found := FindSubjectByName(m.SubjName); found != nil {
return found
} else if createErr := m.Create(); createErr == nil {
log.Infof("subject: added %s %s", TypeString(m.SubjType), txt.LogParam(m.SubjName))
log.Infof("subject: added %s %s", TypeString(m.SubjType), sanitize.Log(m.SubjName))
event.EntitiesCreated("subjects", []*Subject{m})
@ -194,7 +195,7 @@ func FirstOrCreateSubject(m *Subject) *Subject {
} else if found = FindSubjectByName(m.SubjName); found != nil {
return found
} else {
log.Errorf("subject: %s while creating %s", createErr, txt.LogParam(m.SubjName))
log.Errorf("subject: %s while creating %s", createErr, sanitize.Log(m.SubjName))
}
return nil
@ -347,7 +348,7 @@ func (m *Subject) UpdateName(name string) (*Subject, error) {
if err := m.SetName(name); err != nil {
return m, err
} else if err := m.Updates(Values{"SubjName": m.SubjName, "SubjSlug": m.SubjSlug}); err == nil {
log.Infof("subject: renamed %s %s", TypeString(m.SubjType), txt.LogParam(m.SubjName))
log.Infof("subject: renamed %s %s", TypeString(m.SubjType), sanitize.Log(m.SubjName))
event.EntitiesUpdated("subjects", []*Subject{m})

View file

@ -11,6 +11,7 @@ import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -176,7 +177,7 @@ func FindUserByName(userName string) *User {
if err := Db().Preload("Address").Where("user_name = ?", userName).First(&result).Error; err == nil {
return &result
} else {
log.Debugf("user %s not found", txt.LogParam(userName))
log.Debugf("user %s not found", sanitize.Log(userName))
return nil
}
}
@ -192,7 +193,7 @@ func FindUserByUID(uid string) *User {
if err := Db().Preload("Address").Where("user_uid = ?", uid).First(&result).Error; err == nil {
return &result
} else {
log.Debugf("user %s not found", txt.LogParam(uid))
log.Debugf("user %s not found", sanitize.Log(uid))
return nil
}
}
@ -214,14 +215,14 @@ func (m *User) Deleted() bool {
// String returns an identifier that can be used in logs.
func (m *User) String() string {
if n := m.Username(); n != "" {
return txt.LogParam(n)
return sanitize.Log(n)
}
if m.FullName != "" {
return txt.LogParam(m.FullName)
return sanitize.Log(m.FullName)
}
return txt.LogParam(m.UserUID)
return sanitize.Log(m.UserUID)
}
// Username returns the normalized username.
@ -256,7 +257,7 @@ func (m *User) SetPassword(password string) error {
}
if len(password) < 4 {
return fmt.Errorf("new password for %s must be at least 4 characters", txt.LogParam(m.Username()))
return fmt.Errorf("new password for %s must be at least 4 characters", sanitize.Log(m.Username()))
}
pw := NewPassword(m.UserUID, password)
@ -398,7 +399,7 @@ func CreateWithPassword(uc form.UserCreate) error {
RoleAdmin: true,
}
if len(uc.Password) < 4 {
return fmt.Errorf("new password for %s must be at least 4 characters", txt.LogParam(u.Username()))
return fmt.Errorf("new password for %s must be at least 4 characters", sanitize.Log(u.Username()))
}
err := u.Validate()
if err != nil {
@ -412,7 +413,7 @@ func CreateWithPassword(uc form.UserCreate) error {
if err := tx.Create(&pw).Error; err != nil {
return err
}
log.Infof("created user %s with uid %s", txt.LogParam(u.Username()), txt.LogParam(u.UserUID))
log.Infof("created user %s with uid %s", sanitize.Log(u.Username()), sanitize.Log(u.UserUID))
return nil
})
}

View file

@ -12,7 +12,7 @@ import (
pigo "github.com/esimov/pigo/core"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
//go:embed cascade/facefinder
@ -92,7 +92,7 @@ func Detect(fileName string, findLandmarks bool, minSize int) (faces Faces, err
}
if !fs.FileExists(fileName) {
return faces, fmt.Errorf("faces: file '%s' not found", txt.LogParam(filepath.Base(fileName)))
return faces, fmt.Errorf("faces: file '%s' not found", sanitize.Log(filepath.Base(fileName)))
}
det, params, err := d.Detect(fileName)

View file

@ -11,7 +11,7 @@ import (
tf "github.com/tensorflow/tensorflow/tensorflow/go"
"github.com/photoprism/photoprism/internal/crop"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Net is a wrapper for the TensorFlow Facenet model.
@ -82,7 +82,7 @@ func (t *Net) loadModel() error {
modelPath := path.Join(t.modelPath)
log.Infof("faces: loading %s", txt.LogParam(filepath.Base(modelPath)))
log.Infof("faces: loading %s", sanitize.Log(filepath.Base(modelPath)))
// Load model
model, err := tf.LoadSavedModel(modelPath, t.modelTags, nil)

View file

@ -18,7 +18,7 @@ import (
"github.com/photoprism/photoprism/internal/hub/places"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
"gopkg.in/yaml.v2"
)
@ -182,7 +182,7 @@ func (c *Config) Refresh() (err error) {
// Load backend api credentials from a YAML file.
func (c *Config) Load() error {
if !fs.FileExists(c.FileName) {
return fmt.Errorf("settings file not found: %s", txt.LogParam(c.FileName))
return fmt.Errorf("settings file not found: %s", sanitize.Log(c.FileName))
}
mutex.Lock()

View file

@ -9,6 +9,7 @@ import (
"time"
"github.com/photoprism/photoprism/pkg/s2"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -46,7 +47,7 @@ func FindLocation(id string) (result Location, err error) {
if len(id) == 0 {
return result, fmt.Errorf("empty cell id")
} else if n := len(id); n < 4 || n > 16 {
return result, fmt.Errorf("invalid cell id %s", txt.LogParam(id))
return result, fmt.Errorf("invalid cell id %s", sanitize.Log(id))
}
// Remember start time.
@ -133,7 +134,7 @@ func FindLocation(id string) (result Location, err error) {
}
cache.SetDefault(id, result)
log.Tracef("places: cached cell %s [%s]", txt.LogParam(id), time.Since(start))
log.Tracef("places: cached cell %s [%s]", sanitize.Log(id), time.Since(start))
result.Cached = false

View file

@ -14,7 +14,7 @@ import (
exifcommon "github.com/dsoprea/go-exif/v3/common"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
"gopkg.in/photoprism/go-tz.v2/tz"
)
@ -51,7 +51,7 @@ func (data *Data) Exif(fileName string, fileType fs.FileFormat) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("metadata: %s in %s (exif panic)\nstack: %s", e, txt.LogParam(filepath.Base(fileName)), debug.Stack())
err = fmt.Errorf("metadata: %s in %s (exif panic)\nstack: %s", e, sanitize.Log(filepath.Base(fileName)), debug.Stack())
}
}()
@ -62,7 +62,7 @@ func (data *Data) Exif(fileName string, fileType fs.FileFormat) (err error) {
return err
}
logName := txt.LogParam(filepath.Base(fileName))
logName := sanitize.Log(filepath.Base(fileName))
if data.All == nil {
data.All = make(map[string]string)

View file

@ -12,20 +12,20 @@ import (
pngstructure "github.com/dsoprea/go-png-image-structure/v2"
tiffstructure "github.com/dsoprea/go-tiff-image-structure/v2"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("metadata: %s in %s (raw exif panic)\nstack: %s", e, txt.LogParam(filepath.Base(fileName)), debug.Stack())
err = fmt.Errorf("metadata: %s in %s (raw exif panic)\nstack: %s", e, sanitize.Log(filepath.Base(fileName)), debug.Stack())
}
}()
// Extract raw EXIF block.
var parsed bool
logName := txt.LogParam(filepath.Base(fileName))
logName := sanitize.Log(filepath.Base(fileName))
if fileType == fs.FormatJpeg {
jpegMp := jpegstructure.NewJpegMediaParser()

View file

@ -8,8 +8,7 @@ import (
"runtime/debug"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// JSON parses a json sidecar file (as used by Exiftool) and returns a Data struct.
@ -23,7 +22,7 @@ func JSON(jsonName, originalName string) (data Data, err error) {
func (data *Data) JSON(jsonName, originalName string) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("metadata: %s in %s (json panic)\nstack: %s", e, txt.LogParam(filepath.Base(jsonName)), debug.Stack())
err = fmt.Errorf("metadata: %s in %s (json panic)\nstack: %s", e, sanitize.Log(filepath.Base(jsonName)), debug.Stack())
}
}()
@ -31,7 +30,7 @@ func (data *Data) JSON(jsonName, originalName string) (err error) {
data.All = make(map[string]string)
}
quotedName := txt.LogParam(filepath.Base(jsonName))
quotedName := sanitize.Log(filepath.Base(jsonName))
if !fs.FileExists(jsonName) {
return fmt.Errorf("metadata: %s not found", quotedName)

View file

@ -10,6 +10,7 @@ import (
"time"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/tidwall/gjson"
"gopkg.in/photoprism/go-tz.v2/tz"
@ -29,7 +30,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
j := gjson.GetBytes(jsonData, "@flatten|@join")
if !j.IsObject() {
return fmt.Errorf("metadata: data is not an object in %s (exiftool)", txt.LogParam(filepath.Base(originalName)))
return fmt.Errorf("metadata: data is not an object in %s (exiftool)", sanitize.Log(filepath.Base(originalName)))
}
jsonStrings := make(map[string]string)
@ -40,7 +41,7 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) {
}
if fileName, ok := jsonStrings["FileName"]; ok && fileName != "" && originalName != "" && fileName != originalName {
return fmt.Errorf("metadata: original name %s does not match %s (exiftool)", txt.LogParam(originalName), txt.LogParam(fileName))
return fmt.Errorf("metadata: original name %s does not match %s (exiftool)", sanitize.Log(originalName), sanitize.Log(fileName))
}
v := reflect.ValueOf(data).Elem()

View file

@ -5,7 +5,7 @@ import (
"path/filepath"
"runtime/debug"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// XMP parses an XMP file and returns a Data struct.
@ -19,14 +19,14 @@ func XMP(fileName string) (data Data, err error) {
func (data *Data) XMP(fileName string) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("metadata: %s in %s (xmp panic)\nstack: %s", e, txt.LogParam(filepath.Base(fileName)), debug.Stack())
err = fmt.Errorf("metadata: %s in %s (xmp panic)\nstack: %s", e, sanitize.Log(filepath.Base(fileName)), debug.Stack())
}
}()
doc := XmpDocument{}
if err := doc.Load(fileName); err != nil {
return fmt.Errorf("metadata: can't read %s (xmp)", txt.LogParam(filepath.Base(fileName)))
return fmt.Errorf("metadata: can't read %s (xmp)", sanitize.Log(filepath.Base(fileName)))
}
if doc.Title() != "" {

View file

@ -8,7 +8,7 @@ import (
"sync"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
tf "github.com/tensorflow/tensorflow/tensorflow/go"
"github.com/tensorflow/tensorflow/tensorflow/go/op"
)
@ -30,7 +30,7 @@ func New(modelPath string) *Detector {
// File returns matching labels for a jpeg media file.
func (t *Detector) File(filename string) (result Labels, err error) {
if fs.MimeType(filename) != "image/jpeg" {
return result, fmt.Errorf("nsfw: %s is not a jpeg file", txt.LogParam(filepath.Base(filename)))
return result, fmt.Errorf("nsfw: %s is not a jpeg file", sanitize.Log(filepath.Base(filename)))
}
imageBuffer, err := os.ReadFile(filename)
@ -118,7 +118,7 @@ func (t *Detector) loadModel() error {
return nil
}
log.Infof("nsfw: loading %s", txt.LogParam(filepath.Base(t.modelPath)))
log.Infof("nsfw: loading %s", sanitize.Log(filepath.Base(t.modelPath)))
// Load model
model, err := tf.LoadSavedModel(t.modelPath, t.modelTags, nil)

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// BackupAlbums creates a YAML file backup of all albums.
@ -36,7 +36,7 @@ func BackupAlbums(backupPath string, force bool) (count int, result error) {
log.Errorf("album: %s (update yaml)", err)
result = err
} else {
log.Tracef("backup: saved album yaml file %s", txt.LogParam(filepath.Base(fileName)))
log.Tracef("backup: saved album yaml file %s", sanitize.Log(filepath.Base(fileName)))
count++
}
}
@ -87,14 +87,14 @@ func RestoreAlbums(backupPath string, force bool) (count int, result error) {
a := entity.Album{}
if err := a.LoadFromYaml(fileName); err != nil {
log.Errorf("restore: %s in %s", err, txt.LogParam(filepath.Base(fileName)))
log.Errorf("restore: %s in %s", err, sanitize.Log(filepath.Base(fileName)))
result = err
} else if a.AlbumType == "" || len(a.Photos) == 0 && a.AlbumFilter == "" {
log.Debugf("restore: skipping %s", txt.LogParam(filepath.Base(fileName)))
log.Debugf("restore: skipping %s", sanitize.Log(filepath.Base(fileName)))
} else if err := a.Find(); err == nil {
log.Infof("%s: %s already exists", a.AlbumType, txt.LogParam(a.AlbumTitle))
log.Infof("%s: %s already exists", a.AlbumType, sanitize.Log(a.AlbumTitle))
} else if err := a.Create(); err != nil {
log.Errorf("%s: %s in %s", a.AlbumType, err, txt.LogParam(filepath.Base(fileName)))
log.Errorf("%s: %s in %s", a.AlbumType, err, sanitize.Log(filepath.Base(fileName)))
} else {
count++
}

View file

@ -17,7 +17,7 @@ import (
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/fastwalk"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// CleanUp represents a worker that deletes unneeded data and files.
@ -82,7 +82,7 @@ func (w *CleanUp) Start(opt CleanUpOptions) (thumbs int, orphans int, err error)
}
hash := base[:i]
logName := txt.LogParam(fs.RelName(fileName, thumbPath))
logName := sanitize.Log(fs.RelName(fileName, thumbPath))
if ok := fileHashes[hash]; ok {
// Do nothing.
@ -119,7 +119,7 @@ func (w *CleanUp) Start(opt CleanUpOptions) (thumbs int, orphans int, err error)
if opt.Dry {
orphans++
log.Infof("cleanup: orphan photo %s would be removed", txt.LogParam(p.PhotoUID))
log.Infof("cleanup: orphan photo %s would be removed", sanitize.Log(p.PhotoUID))
continue
}

View file

@ -9,19 +9,19 @@ import (
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/colors"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Colors returns the ColorPerception of an image (only JPEG supported).
func (m *MediaFile) Colors(thumbPath string) (perception colors.ColorPerception, err error) {
if !m.IsJpeg() {
return perception, fmt.Errorf("%s is not a jpeg", txt.LogParam(m.BaseName()))
return perception, fmt.Errorf("%s is not a jpeg", sanitize.Log(m.BaseName()))
}
img, err := m.Resample(thumbPath, thumb.Colors)
if err != nil {
log.Debugf("colors: %s in %s (resample)", err, txt.LogParam(m.BaseName()))
log.Debugf("colors: %s in %s (resample)", err, sanitize.Log(m.BaseName()))
return perception, err
}

View file

@ -21,7 +21,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
const DefaultAvcEncoder = "libx264" // Default FFmpeg AVC software encoder.
@ -82,7 +82,7 @@ func (c *Convert) Start(path string) (err error) {
}
ignore.Log = func(fileName string) {
log.Infof("convert: ignoring %s", txt.LogParam(filepath.Base(fileName)))
log.Infof("convert: ignoring %s", sanitize.Log(filepath.Base(fileName)))
}
err = godirwalk.Walk(path, &godirwalk.Options{
@ -403,7 +403,7 @@ func (c *Convert) AvcConvertCommand(f *MediaFile, avcName, codecName string) (re
)
}
} else {
return nil, useMutex, fmt.Errorf("convert: file type %s not supported in %s", f.FileType(), txt.LogParam(f.BaseName()))
return nil, useMutex, fmt.Errorf("convert: file type %s not supported in %s", f.FileType(), sanitize.Log(f.BaseName()))
}
return result, useMutex, nil

View file

@ -3,7 +3,7 @@ package photoprism
import (
"strings"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
type ConvertJob struct {
@ -14,7 +14,7 @@ type ConvertJob struct {
func ConvertWorker(jobs <-chan ConvertJob) {
logError := func(err error, job ConvertJob) {
fileName := job.file.RelName(job.convert.conf.OriginalsPath())
log.Errorf("convert: %s for %s", strings.TrimSpace(err.Error()), txt.LogParam(fileName))
log.Errorf("convert: %s for %s", strings.TrimSpace(err.Error()), sanitize.Log(fileName))
}
for job := range jobs {

View file

@ -6,7 +6,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Delete permanently removes a photo and all its files.
@ -24,16 +24,16 @@ func Delete(p entity.Photo) error {
for _, file := range files {
fileName := FileName(file.FileRoot, file.FileName)
log.Debugf("delete: removing file %s", txt.LogParam(file.FileName))
log.Debugf("delete: removing file %s", sanitize.Log(file.FileName))
if f, err := NewMediaFile(fileName); err == nil {
if sidecarJson := f.SidecarJsonName(); fs.FileExists(sidecarJson) {
log.Debugf("delete: removing json sidecar %s", txt.LogParam(filepath.Base(sidecarJson)))
log.Debugf("delete: removing json sidecar %s", sanitize.Log(filepath.Base(sidecarJson)))
logWarn("delete", os.Remove(sidecarJson))
}
if exifJson, err := f.ExifToolJsonName(); err == nil && fs.FileExists(exifJson) {
log.Debugf("delete: removing exiftool sidecar %s", txt.LogParam(filepath.Base(exifJson)))
log.Debugf("delete: removing exiftool sidecar %s", sanitize.Log(filepath.Base(exifJson)))
logWarn("delete", os.Remove(exifJson))
}
@ -47,7 +47,7 @@ func Delete(p entity.Photo) error {
// Remove sidecar backup.
if fs.FileExists(yamlFileName) {
log.Debugf("delete: removing yaml sidecar %s", txt.LogParam(filepath.Base(yamlFileName)))
log.Debugf("delete: removing yaml sidecar %s", sanitize.Log(filepath.Base(yamlFileName)))
logWarn("delete", os.Remove(yamlFileName))
}

View file

@ -6,7 +6,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/face"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Audit face clusters and subjects.
@ -78,13 +78,13 @@ func (w *Faces) Audit(fix bool) (err error) {
log.Infof("face %s: ambiguous subject at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius)
if f1.SubjUID != "" {
log.Infof("face %s: subject %s (%s %s)", f1.ID, txt.LogParam(subj[f1.SubjUID].SubjName), f1.SubjUID, entity.SrcString(f1.FaceSrc))
log.Infof("face %s: subject %s (%s %s)", f1.ID, sanitize.Log(subj[f1.SubjUID].SubjName), f1.SubjUID, entity.SrcString(f1.FaceSrc))
} else {
log.Infof("face %s: has no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc))
}
if f2.SubjUID != "" {
log.Infof("face %s: subject %s (%s %s)", f2.ID, txt.LogParam(subj[f2.SubjUID].SubjName), f2.SubjUID, entity.SrcString(f2.FaceSrc))
log.Infof("face %s: subject %s (%s %s)", f2.ID, sanitize.Log(subj[f2.SubjUID].SubjName), f2.SubjUID, entity.SrcString(f2.FaceSrc))
} else {
log.Infof("face %s: has no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc))
}
@ -115,7 +115,7 @@ func (w *Faces) Audit(fix bool) (err error) {
log.Error(err)
} else {
for _, m := range markers {
log.Infof("marker %s: %s subject %s conflicts with face %s subject %s", m.MarkerUID, entity.SrcString(m.SubjSrc), txt.LogParam(subj[m.SubjUID].SubjName), m.FaceID, txt.LogParam(subj[faceMap[m.FaceID].SubjUID].SubjName))
log.Infof("marker %s: %s subject %s conflicts with face %s subject %s", m.MarkerUID, entity.SrcString(m.SubjSrc), sanitize.Log(subj[m.SubjUID].SubjName), m.FaceID, sanitize.Log(subj[faceMap[m.FaceID].SubjUID].SubjName))
}
}

View file

@ -18,7 +18,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Import represents an importer that can copy/move MediaFiles to the originals directory.
@ -165,7 +165,7 @@ func (imp *Import) Start(opt ImportOptions) fs.Done {
}
if mf.FileSize() == 0 {
log.Infof("import: skipped empty file %s", txt.LogParam(mf.BaseName()))
log.Infof("import: skipped empty file %s", sanitize.Log(mf.BaseName()))
return nil
}
@ -219,9 +219,9 @@ func (imp *Import) Start(opt ImportOptions) fs.Done {
for _, directory := range directories {
if fs.IsEmpty(directory) {
if err := os.Remove(directory); err != nil {
log.Errorf("import: failed deleting empty folder %s (%s)", txt.LogParam(fs.RelName(directory, importPath)), err)
log.Errorf("import: failed deleting empty folder %s (%s)", sanitize.Log(fs.RelName(directory, importPath)), err)
} else {
log.Infof("import: deleted empty folder %s", txt.LogParam(fs.RelName(directory, importPath)))
log.Infof("import: deleted empty folder %s", sanitize.Log(fs.RelName(directory, importPath)))
}
}
}
@ -235,7 +235,7 @@ func (imp *Import) Start(opt ImportOptions) fs.Done {
}
if err := os.Remove(file); err != nil {
log.Errorf("import: failed removing %s (%s)", txt.LogParam(fs.RelName(file, importPath)), err.Error())
log.Errorf("import: failed removing %s (%s)", sanitize.Log(fs.RelName(file, importPath)), err.Error())
}
}
}
@ -278,7 +278,7 @@ func (imp *Import) DestinationFilename(mainFile *MediaFile, mediaFile *MediaFile
if f, err := entity.FirstFileByHash(mediaFile.Hash()); err == nil {
existingFilename := FileName(f.FileRoot, f.FileName)
if fs.FileExists(existingFilename) {
return existingFilename, fmt.Errorf("%s is identical to %s (sha1 %s)", txt.LogParam(filepath.Base(mediaFile.FileName())), txt.LogParam(f.FileName), mediaFile.Hash())
return existingFilename, fmt.Errorf("%s is identical to %s (sha1 %s)", sanitize.Log(filepath.Base(mediaFile.FileName())), sanitize.Log(f.FileName), mediaFile.Hash())
} else {
return existingFilename, nil
}
@ -294,7 +294,7 @@ func (imp *Import) DestinationFilename(mainFile *MediaFile, mediaFile *MediaFile
for fs.FileExists(result) {
if mediaFile.Hash() == fs.Hash(result) {
return result, fmt.Errorf("%s already exists", txt.LogParam(fs.RelName(result, imp.originalsPath())))
return result, fmt.Errorf("%s already exists", sanitize.Log(fs.RelName(result, imp.originalsPath())))
}
iteration++

View file

@ -9,7 +9,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
type ImportJob struct {
@ -30,15 +30,15 @@ func ImportWorker(jobs <-chan ImportJob) {
importPath := job.ImportOpt.Path
if related.Main == nil {
log.Warnf("import: %s belongs to no supported media file", txt.LogParam(fs.RelName(job.FileName, importPath)))
log.Warnf("import: %s belongs to no supported media file", sanitize.Log(fs.RelName(job.FileName, importPath)))
continue
}
if related.Main.NeedsExifToolJson() {
if jsonName, err := imp.convert.ToJson(related.Main); err != nil {
log.Debugf("import: %s in %s (extract metadata)", txt.LogParam(err.Error()), txt.LogParam(related.Main.BaseName()))
log.Debugf("import: %s in %s (extract metadata)", sanitize.Log(err.Error()), sanitize.Log(related.Main.BaseName()))
} else if err := related.Main.ReadExifToolJson(); err != nil {
log.Errorf("import: %s in %s (read metadata)", txt.LogParam(err.Error()), txt.LogParam(related.Main.BaseName()))
log.Errorf("import: %s in %s (read metadata)", sanitize.Log(err.Error()), sanitize.Log(related.Main.BaseName()))
} else {
log.Debugf("import: created %s", filepath.Base(jsonName))
}
@ -60,7 +60,7 @@ func ImportWorker(jobs <-chan ImportJob) {
if fs.PathExists(destDir) {
// Do nothing.
} else if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
log.Errorf("import: failed creating folder for %s (%s)", txt.LogParam(f.BaseName()), err.Error())
log.Errorf("import: failed creating folder for %s (%s)", sanitize.Log(f.BaseName()), err.Error())
} else {
destDirRel := fs.RelName(destDir, imp.originalsPath())
@ -73,20 +73,20 @@ func ImportWorker(jobs <-chan ImportJob) {
if related.Main.HasSameName(f) {
destMainFileName = destFileName
log.Infof("import: moving main %s file %s to %s", f.FileType(), txt.LogParam(relFileName), txt.LogParam(fs.RelName(destFileName, imp.originalsPath())))
log.Infof("import: moving main %s file %s to %s", f.FileType(), sanitize.Log(relFileName), sanitize.Log(fs.RelName(destFileName, imp.originalsPath())))
} else {
log.Infof("import: moving related %s file %s to %s", f.FileType(), txt.LogParam(relFileName), txt.LogParam(fs.RelName(destFileName, imp.originalsPath())))
log.Infof("import: moving related %s file %s to %s", f.FileType(), sanitize.Log(relFileName), sanitize.Log(fs.RelName(destFileName, imp.originalsPath())))
}
if opt.Move {
if err := f.Move(destFileName); err != nil {
logRelName := txt.LogParam(fs.RelName(destMainFileName, imp.originalsPath()))
logRelName := sanitize.Log(fs.RelName(destMainFileName, imp.originalsPath()))
log.Debugf("import: %s", err.Error())
log.Warnf("import: failed moving file to %s, is another import running at the same time?", logRelName)
}
} else {
if err := f.Copy(destFileName); err != nil {
logRelName := txt.LogParam(fs.RelName(destMainFileName, imp.originalsPath()))
logRelName := sanitize.Log(fs.RelName(destMainFileName, imp.originalsPath()))
log.Debugf("import: %s", err.Error())
log.Warnf("import: failed copying file to %s, is another import running at the same time?", logRelName)
}
@ -106,9 +106,9 @@ func ImportWorker(jobs <-chan ImportJob) {
// Remove duplicates to save storage.
if opt.RemoveExistingFiles {
if err := f.Remove(); err != nil {
log.Errorf("import: failed deleting %s (%s)", txt.LogParam(f.BaseName()), err.Error())
log.Errorf("import: failed deleting %s (%s)", sanitize.Log(f.BaseName()), err.Error())
} else {
log.Infof("import: deleted %s (already exists)", txt.LogParam(relFileName))
log.Infof("import: deleted %s (already exists)", sanitize.Log(relFileName))
}
}
}
@ -118,13 +118,13 @@ func ImportWorker(jobs <-chan ImportJob) {
f, err := NewMediaFile(destMainFileName)
if err != nil {
log.Errorf("import: %s in %s", err.Error(), txt.LogParam(fs.RelName(destMainFileName, imp.originalsPath())))
log.Errorf("import: %s in %s", err.Error(), sanitize.Log(fs.RelName(destMainFileName, imp.originalsPath())))
continue
}
if f.NeedsExifToolJson() {
if jsonName, err := imp.convert.ToJson(f); err != nil {
log.Debugf("import: %s in %s (extract metadata)", txt.LogParam(err.Error()), txt.LogParam(f.BaseName()))
log.Debugf("import: %s in %s (extract metadata)", sanitize.Log(err.Error()), sanitize.Log(f.BaseName()))
} else {
log.Debugf("import: created %s", filepath.Base(jsonName))
}
@ -132,10 +132,10 @@ func ImportWorker(jobs <-chan ImportJob) {
if indexOpt.Convert && f.IsMedia() && !f.HasJpeg() {
if jpegFile, err := imp.convert.ToJpeg(f); err != nil {
log.Errorf("import: %s in %s (convert to jpeg)", err.Error(), txt.LogParam(fs.RelName(destMainFileName, imp.originalsPath())))
log.Errorf("import: %s in %s (convert to jpeg)", err.Error(), sanitize.Log(fs.RelName(destMainFileName, imp.originalsPath())))
continue
} else {
log.Debugf("import: created %s", txt.LogParam(jpegFile.BaseName()))
log.Debugf("import: created %s", sanitize.Log(jpegFile.BaseName()))
}
}
@ -143,7 +143,7 @@ func ImportWorker(jobs <-chan ImportJob) {
log.Error(err)
} else {
if err := jpg.ResampleDefault(imp.thumbPath(), false); err != nil {
log.Errorf("import: %s in %s (resample)", err.Error(), txt.LogParam(jpg.BaseName()))
log.Errorf("import: %s in %s (resample)", err.Error(), sanitize.Log(jpg.BaseName()))
continue
}
}
@ -151,7 +151,7 @@ func ImportWorker(jobs <-chan ImportJob) {
related, err := f.RelatedFiles(imp.conf.Settings().StackSequences())
if err != nil {
log.Errorf("import: %s in %s (find related files)", err.Error(), txt.LogParam(fs.RelName(destMainFileName, imp.originalsPath())))
log.Errorf("import: %s in %s (find related files)", err.Error(), sanitize.Log(fs.RelName(destMainFileName, imp.originalsPath())))
continue
}
@ -165,13 +165,13 @@ func ImportWorker(jobs <-chan ImportJob) {
// Enforce file size limit for originals.
if sizeLimit > 0 && f.FileSize() > sizeLimit {
log.Warnf("import: %s exceeds file size limit (%d / %d MB)", txt.LogParam(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
log.Warnf("import: %s exceeds file size limit (%d / %d MB)", sanitize.Log(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
continue
}
res := ind.MediaFile(f, indexOpt, originalName)
log.Infof("import: %s main %s file %s", res, f.FileType(), txt.LogParam(f.RelName(ind.originalsPath())))
log.Infof("import: %s main %s file %s", res, f.FileType(), sanitize.Log(f.RelName(ind.originalsPath())))
done[f.FileName()] = true
if res.Success() {
@ -198,13 +198,13 @@ func ImportWorker(jobs <-chan ImportJob) {
// Enforce file size limit for originals.
if sizeLimit > 0 && f.FileSize() > sizeLimit {
log.Warnf("import: %s exceeds file size limit (%d / %d MB)", txt.LogParam(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
log.Warnf("import: %s exceeds file size limit (%d / %d MB)", sanitize.Log(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
continue
}
if f.NeedsExifToolJson() {
if jsonName, err := imp.convert.ToJson(f); err != nil {
log.Debugf("import: %s in %s (extract metadata)", txt.LogParam(err.Error()), txt.LogParam(f.BaseName()))
log.Debugf("import: %s in %s (extract metadata)", sanitize.Log(err.Error()), sanitize.Log(f.BaseName()))
} else {
log.Debugf("import: created %s", filepath.Base(jsonName))
}
@ -214,12 +214,12 @@ func ImportWorker(jobs <-chan ImportJob) {
if res.Indexed() && f.IsJpeg() {
if err := f.ResampleDefault(ind.thumbPath(), false); err != nil {
log.Errorf("import: failed creating thumbnails for %s (%s)", txt.LogParam(f.BaseName()), err.Error())
log.Errorf("import: failed creating thumbnails for %s (%s)", sanitize.Log(f.BaseName()), err.Error())
query.SetFileError(res.FileUID, err.Error())
}
}
log.Infof("import: %s related %s file %s", res, f.FileType(), txt.LogParam(f.RelName(ind.originalsPath())))
log.Infof("import: %s related %s file %s", res, f.FileType(), sanitize.Log(f.RelName(ind.originalsPath())))
}
}

View file

@ -19,7 +19,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/nsfw"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Index represents an indexer that indexes files in the originals directory.
@ -89,7 +89,7 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
optionsPath := filepath.Join(originalsPath, opt.Path)
if !fs.PathExists(optionsPath) {
event.Error(fmt.Sprintf("index: %s does not exist", txt.LogParam(optionsPath)))
event.Error(fmt.Sprintf("index: %s does not exist", sanitize.Log(optionsPath)))
return done
}
@ -182,7 +182,7 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
}
if mf.FileSize() == 0 {
log.Infof("index: skipped empty file %s", txt.LogParam(mf.BaseName()))
log.Infof("index: skipped empty file %s", sanitize.Log(mf.BaseName()))
return nil
}

View file

@ -7,8 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/face"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Faces finds faces in JPEG media files and returns them.
@ -29,12 +28,12 @@ func (ind *Index) Faces(jpeg *MediaFile, expected int) face.Faces {
thumbName, err := jpeg.Thumbnail(Config().ThumbPath(), thumbSize)
if err != nil {
log.Debugf("index: %s in %s (faces)", err, txt.LogParam(jpeg.BaseName()))
log.Debugf("index: %s in %s (faces)", err, sanitize.Log(jpeg.BaseName()))
return face.Faces{}
}
if thumbName == "" {
log.Debugf("index: thumb %s not found in %s (faces)", thumbSize, txt.LogParam(jpeg.BaseName()))
log.Debugf("index: thumb %s not found in %s (faces)", thumbSize, sanitize.Log(jpeg.BaseName()))
return face.Faces{}
}
@ -43,11 +42,11 @@ func (ind *Index) Faces(jpeg *MediaFile, expected int) face.Faces {
faces, err := ind.faceNet.Detect(thumbName, Config().FaceSize(), true, expected)
if err != nil {
log.Debugf("%s in %s", err, txt.LogParam(jpeg.BaseName()))
log.Debugf("%s in %s", err, sanitize.Log(jpeg.BaseName()))
}
if l := len(faces); l > 0 {
log.Infof("index: found %s in %s [%s]", english.Plural(l, "face", "faces"), txt.LogParam(jpeg.BaseName()), time.Since(start))
log.Infof("index: found %s in %s [%s]", english.Plural(l, "face", "faces"), sanitize.Log(jpeg.BaseName()), time.Since(start))
}
return faces

View file

@ -6,8 +6,7 @@ import (
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Labels classifies a JPEG image and returns matching labels.
@ -28,14 +27,14 @@ func (ind *Index) Labels(jpeg *MediaFile) (results classify.Labels) {
filename, err := jpeg.Thumbnail(Config().ThumbPath(), size)
if err != nil {
log.Debugf("%s in %s", err, txt.LogParam(jpeg.BaseName()))
log.Debugf("%s in %s", err, sanitize.Log(jpeg.BaseName()))
continue
}
imageLabels, err := ind.tensorFlow.File(filename)
if err != nil {
log.Debugf("%s in %s", err, txt.LogParam(jpeg.BaseName()))
log.Debugf("%s in %s", err, sanitize.Log(jpeg.BaseName()))
continue
}
@ -58,9 +57,9 @@ func (ind *Index) Labels(jpeg *MediaFile) (results classify.Labels) {
}
if l := len(labels); l == 1 {
log.Infof("index: matched %d label with %s [%s]", l, txt.LogParam(jpeg.BaseName()), time.Since(start))
log.Infof("index: matched %d label with %s [%s]", l, sanitize.Log(jpeg.BaseName()), time.Since(start))
} else if l > 1 {
log.Infof("index: matched %d labels with %s [%s]", l, txt.LogParam(jpeg.BaseName()), time.Since(start))
log.Infof("index: matched %d labels with %s [%s]", l, sanitize.Log(jpeg.BaseName()), time.Since(start))
}
return results

View file

@ -16,6 +16,7 @@ import (
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -54,7 +55,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
fileRoot, fileBase, filePath, fileName := m.PathNameInfo(stripSequence)
fullBase := m.BasePrefix(false)
logName := txt.LogParam(fileName)
logName := sanitize.Log(fileName)
fileSize, modTime, err := m.Stat()
if err != nil {
@ -160,13 +161,13 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
if fileRenamed {
fileChanged = true
log.Debugf("index: %s was renamed", txt.LogParam(m.BaseName()))
log.Debugf("index: %s was renamed", sanitize.Log(m.BaseName()))
} else if file.Changed(fileSize, modTime) {
fileChanged = true
log.Debugf("index: %s was modified (new size %d, old size %d, new timestamp %d, old timestamp %d)", txt.LogParam(m.BaseName()), fileSize, file.FileSize, modTime.Unix(), file.ModTime)
log.Debugf("index: %s was modified (new size %d, old size %d, new timestamp %d, old timestamp %d)", sanitize.Log(m.BaseName()), fileSize, file.FileSize, modTime.Unix(), file.ModTime)
} else if file.Missing() {
fileChanged = true
log.Debugf("index: %s was missing", txt.LogParam(m.BaseName()))
log.Debugf("index: %s was missing", sanitize.Log(m.BaseName()))
}
}
@ -196,10 +197,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
if err := photo.LoadFromYaml(yamlName); err != nil {
log.Errorf("index: %s in %s (restore from yaml)", err.Error(), logName)
} else if err := photo.Find(); err != nil {
log.Infof("index: %s restored from %s", txt.LogParam(m.BaseName()), txt.LogParam(filepath.Base(yamlName)))
log.Infof("index: %s restored from %s", sanitize.Log(m.BaseName()), sanitize.Log(filepath.Base(yamlName)))
} else {
photoExists = true
log.Infof("index: uid %s restored from %s", photo.PhotoUID, txt.LogParam(filepath.Base(yamlName)))
log.Infof("index: uid %s restored from %s", photo.PhotoUID, sanitize.Log(filepath.Base(yamlName)))
}
}
}
@ -332,7 +333,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
file.SetColorProfile(metaData.ColorProfile)
if metaData.HasInstanceID() {
log.Infof("index: %s has instance_id %s", logName, txt.LogParam(metaData.InstanceID))
log.Infof("index: %s has instance_id %s", logName, sanitize.Log(metaData.InstanceID))
file.InstanceID = metaData.InstanceID
}
@ -371,13 +372,13 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
details.SetCopyright(metaData.Copyright, entity.SrcMeta)
if metaData.HasDocumentID() && photo.UUID == "" {
log.Infof("index: %s has document_id %s", logName, txt.LogParam(metaData.DocumentID))
log.Infof("index: %s has document_id %s", logName, sanitize.Log(metaData.DocumentID))
photo.UUID = metaData.DocumentID
}
if metaData.HasInstanceID() {
log.Infof("index: %s has instance_id %s", logName, txt.LogParam(metaData.InstanceID))
log.Infof("index: %s has instance_id %s", logName, sanitize.Log(metaData.InstanceID))
file.InstanceID = metaData.InstanceID
}
@ -421,13 +422,13 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
details.SetCopyright(metaData.Copyright, entity.SrcMeta)
if metaData.HasDocumentID() && photo.UUID == "" {
log.Infof("index: %s has document_id %s", logName, txt.LogParam(metaData.DocumentID))
log.Infof("index: %s has document_id %s", logName, sanitize.Log(metaData.DocumentID))
photo.UUID = metaData.DocumentID
}
if metaData.HasInstanceID() {
log.Infof("index: %s has instance_id %s", logName, txt.LogParam(metaData.InstanceID))
log.Infof("index: %s has instance_id %s", logName, sanitize.Log(metaData.InstanceID))
file.InstanceID = metaData.InstanceID
}
@ -526,7 +527,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
details.SetCopyright(metaData.Copyright, entity.SrcMeta)
if metaData.HasDocumentID() && photo.UUID == "" {
log.Debugf("index: %s has document_id %s", logName, txt.LogParam(metaData.DocumentID))
log.Debugf("index: %s has document_id %s", logName, sanitize.Log(metaData.DocumentID))
photo.UUID = metaData.DocumentID
}
@ -745,7 +746,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
if err := photo.SaveAsYaml(yamlFile); err != nil {
log.Errorf("index: %s in %s (update yaml)", err.Error(), logName)
} else {
log.Debugf("index: updated yaml file %s", txt.LogParam(filepath.Base(yamlFile)))
log.Debugf("index: updated yaml file %s", sanitize.Log(filepath.Base(yamlFile)))
}
}

View file

@ -3,7 +3,7 @@ package photoprism
import (
"github.com/photoprism/photoprism/internal/nsfw"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// NSFW returns true if media file might be offensive and detection is enabled.
@ -20,7 +20,7 @@ func (ind *Index) NSFW(jpeg *MediaFile) bool {
return false
} else {
if nsfwLabels.NSFW(nsfw.ThresholdHigh) {
log.Warnf("index: %s might contain offensive content", txt.LogParam(jpeg.RelName(Config().OriginalsPath())))
log.Warnf("index: %s might contain offensive content", sanitize.Log(jpeg.RelName(Config().OriginalsPath())))
return true
}
}

View file

@ -5,14 +5,14 @@ import (
"path/filepath"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// IndexMain indexes the main file from a group of related files and returns the result.
func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result IndexResult) {
// Skip sidecar files without related media file.
if related.Main == nil {
result.Err = fmt.Errorf("index: found no main file for %s", txt.LogParam(related.String()))
result.Err = fmt.Errorf("index: found no main file for %s", sanitize.Log(related.String()))
result.Status = IndexFailed
return result
}
@ -22,14 +22,14 @@ func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result Inde
// Enforce file size limit for originals.
if sizeLimit > 0 && f.FileSize() > sizeLimit {
result.Err = fmt.Errorf("index: %s exceeds file size limit (%d / %d MB)", txt.LogParam(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
result.Err = fmt.Errorf("index: %s exceeds file size limit (%d / %d MB)", sanitize.Log(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
result.Status = IndexFailed
return result
}
if f.NeedsExifToolJson() {
if jsonName, err := ind.convert.ToJson(f); err != nil {
log.Debugf("index: %s in %s (extract metadata)", txt.LogParam(err.Error()), txt.LogParam(f.BaseName()))
log.Debugf("index: %s in %s (extract metadata)", sanitize.Log(err.Error()), sanitize.Log(f.BaseName()))
} else {
log.Debugf("index: created %s", filepath.Base(jsonName))
}
@ -37,15 +37,15 @@ func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result Inde
if opt.Convert && f.IsMedia() && !f.HasJpeg() {
if jpegFile, err := ind.convert.ToJpeg(f); err != nil {
result.Err = fmt.Errorf("index: failed converting %s to jpeg (%s)", txt.LogParam(f.BaseName()), err.Error())
result.Err = fmt.Errorf("index: failed converting %s to jpeg (%s)", sanitize.Log(f.BaseName()), err.Error())
result.Status = IndexFailed
return result
} else {
log.Debugf("index: created %s", txt.LogParam(jpegFile.BaseName()))
log.Debugf("index: created %s", sanitize.Log(jpegFile.BaseName()))
if err := jpegFile.ResampleDefault(ind.thumbPath(), false); err != nil {
result.Err = fmt.Errorf("index: failed creating thumbnails for %s (%s)", txt.LogParam(f.BaseName()), err.Error())
result.Err = fmt.Errorf("index: failed creating thumbnails for %s (%s)", sanitize.Log(f.BaseName()), err.Error())
result.Status = IndexFailed
return result
@ -59,12 +59,12 @@ func IndexMain(related *RelatedFiles, ind *Index, opt IndexOptions) (result Inde
if result.Indexed() && f.IsJpeg() {
if err := f.ResampleDefault(ind.thumbPath(), false); err != nil {
log.Errorf("index: failed creating thumbnails for %s (%s)", txt.LogParam(f.BaseName()), err.Error())
log.Errorf("index: failed creating thumbnails for %s (%s)", sanitize.Log(f.BaseName()), err.Error())
query.SetFileError(result.FileUID, err.Error())
}
}
log.Infof("index: %s main %s file %s", result, f.FileType(), txt.LogParam(f.RelName(ind.originalsPath())))
log.Infof("index: %s main %s file %s", result, f.FileType(), sanitize.Log(f.RelName(ind.originalsPath())))
return result
}
@ -104,13 +104,13 @@ func IndexRelated(related RelatedFiles, ind *Index, opt IndexOptions) (result In
// Enforce file size limit for originals.
if sizeLimit > 0 && f.FileSize() > sizeLimit {
log.Warnf("index: %s exceeds file size limit (%d / %d MB)", txt.LogParam(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
log.Warnf("index: %s exceeds file size limit (%d / %d MB)", sanitize.Log(f.BaseName()), f.FileSize()/(1024*1024), sizeLimit/(1024*1024))
continue
}
if f.NeedsExifToolJson() {
if jsonName, err := ind.convert.ToJson(f); err != nil {
log.Debugf("index: %s in %s (extract metadata)", txt.LogParam(err.Error()), txt.LogParam(f.BaseName()))
log.Debugf("index: %s in %s (extract metadata)", sanitize.Log(err.Error()), sanitize.Log(f.BaseName()))
} else {
log.Debugf("index: created %s", filepath.Base(jsonName))
}
@ -118,15 +118,15 @@ func IndexRelated(related RelatedFiles, ind *Index, opt IndexOptions) (result In
if opt.Convert && f.IsMedia() && !f.HasJpeg() {
if jpegFile, err := ind.convert.ToJpeg(f); err != nil {
result.Err = fmt.Errorf("index: failed converting %s to jpeg (%s)", txt.LogParam(f.BaseName()), err.Error())
result.Err = fmt.Errorf("index: failed converting %s to jpeg (%s)", sanitize.Log(f.BaseName()), err.Error())
result.Status = IndexFailed
return result
} else {
log.Debugf("index: created %s", txt.LogParam(jpegFile.BaseName()))
log.Debugf("index: created %s", sanitize.Log(jpegFile.BaseName()))
if err := jpegFile.ResampleDefault(ind.thumbPath(), false); err != nil {
result.Err = fmt.Errorf("index: failed creating thumbnails for %s (%s)", txt.LogParam(f.BaseName()), err.Error())
result.Err = fmt.Errorf("index: failed creating thumbnails for %s (%s)", sanitize.Log(f.BaseName()), err.Error())
result.Status = IndexFailed
return result
@ -140,12 +140,12 @@ func IndexRelated(related RelatedFiles, ind *Index, opt IndexOptions) (result In
if res.Indexed() && f.IsJpeg() {
if err := f.ResampleDefault(ind.thumbPath(), false); err != nil {
log.Errorf("index: failed creating thumbnails for %s (%s)", txt.LogParam(f.BaseName()), err.Error())
log.Errorf("index: failed creating thumbnails for %s (%s)", sanitize.Log(f.BaseName()), err.Error())
query.SetFileError(res.FileUID, err.Error())
}
}
log.Infof("index: %s related %s file %s", res, f.FileType(), txt.LogParam(f.BaseName()))
log.Infof("index: %s related %s file %s", res, f.FileType(), sanitize.Log(f.BaseName()))
}
return result

View file

@ -24,6 +24,7 @@ import (
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/capture"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -60,7 +61,7 @@ func NewMediaFile(fileName string) (*MediaFile, error) {
}
if _, _, err := m.Stat(); err != nil {
return m, fmt.Errorf("media: %s not found", txt.LogParam(m.BaseName()))
return m, fmt.Errorf("media: %s not found", sanitize.Log(m.BaseName()))
}
return m, nil
@ -300,12 +301,12 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e
f, err := NewMediaFile(fileName)
if err != nil {
log.Warnf("media: %s in %s", err, txt.LogParam(filepath.Base(fileName)))
log.Warnf("media: %s in %s", err, sanitize.Log(filepath.Base(fileName)))
continue
}
if f.FileSize() == 0 {
log.Warnf("media: %s is empty", txt.LogParam(filepath.Base(fileName)))
log.Warnf("media: %s is empty", sanitize.Log(filepath.Base(fileName)))
continue
}
@ -335,7 +336,7 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e
t = "unknown type"
}
return result, fmt.Errorf("no supported files found for %s (%s)", txt.LogParam(m.BaseName()), t)
return result, fmt.Errorf("no supported files found for %s (%s)", sanitize.Log(m.BaseName()), t)
}
// Add hidden JPEG if exists.
@ -789,7 +790,7 @@ func (m *MediaFile) HasJpeg() bool {
func (m *MediaFile) decodeDimensions() error {
if !m.IsMedia() {
return fmt.Errorf("failed decoding dimensions for %s", txt.LogParam(m.BaseName()))
return fmt.Errorf("failed decoding dimensions for %s", sanitize.Log(m.BaseName()))
}
if m.IsJpeg() || m.IsPng() || m.IsGif() {
@ -901,7 +902,7 @@ func (m *MediaFile) Thumbnail(path string, sizeName thumb.Name) (filename string
thumbnail, err := thumb.FromFile(m.FileName(), m.Hash(), path, size.Width, size.Height, m.Orientation(), size.Options...)
if err != nil {
err = fmt.Errorf("media: failed creating thumbnail for %s (%s)", txt.LogParam(m.BaseName()), err)
err = fmt.Errorf("media: failed creating thumbnail for %s (%s)", sanitize.Log(m.BaseName()), err)
log.Debug(err)
return "", err
}
@ -949,7 +950,7 @@ func (m *MediaFile) ResampleDefault(thumbPath string, force bool) (err error) {
}
if fileName, err := thumb.FileName(hash, thumbPath, size.Width, size.Height, size.Options...); err != nil {
log.Errorf("media: failed creating %s (%s)", txt.LogParam(string(name)), err)
log.Errorf("media: failed creating %s (%s)", sanitize.Log(string(name)), err)
return err
} else {
@ -961,7 +962,7 @@ func (m *MediaFile) ResampleDefault(thumbPath string, force bool) (err error) {
img, err := thumb.Open(m.FileName(), m.Orientation())
if err != nil {
log.Debugf("media: %s in %s", err.Error(), txt.LogParam(m.BaseName()))
log.Debugf("media: %s in %s", err.Error(), sanitize.Log(m.BaseName()))
return err
}
@ -980,7 +981,7 @@ func (m *MediaFile) ResampleDefault(thumbPath string, force bool) (err error) {
}
if err != nil {
log.Errorf("media: failed creating %s (%s)", txt.LogParam(string(name)), err)
log.Errorf("media: failed creating %s (%s)", sanitize.Log(string(name)), err)
return err
}
@ -1015,9 +1016,9 @@ func (m *MediaFile) RenameSidecars(oldFileName string) (renamed map[string]strin
renamed[fs.RelName(srcName, sidecarPath)] = fs.RelName(destName, sidecarPath)
if err := os.Remove(srcName); err != nil {
log.Errorf("media: failed removing sidecar %s", txt.LogParam(fs.RelName(srcName, sidecarPath)))
log.Errorf("media: failed removing sidecar %s", sanitize.Log(fs.RelName(srcName, sidecarPath)))
} else {
log.Infof("media: removed sidecar %s", txt.LogParam(fs.RelName(srcName, sidecarPath)))
log.Infof("media: removed sidecar %s", sanitize.Log(fs.RelName(srcName, sidecarPath)))
}
continue
@ -1026,7 +1027,7 @@ func (m *MediaFile) RenameSidecars(oldFileName string) (renamed map[string]strin
if err := fs.Move(srcName, destName); err != nil {
return renamed, err
} else {
log.Infof("media: moved existing sidecar to %s", txt.LogParam(newName+filepath.Ext(srcName)))
log.Infof("media: moved existing sidecar to %s", sanitize.Log(newName+filepath.Ext(srcName)))
renamed[fs.RelName(srcName, sidecarPath)] = fs.RelName(destName, sidecarPath)
}
}
@ -1051,9 +1052,9 @@ func (m *MediaFile) RemoveSidecars() (err error) {
for _, sidecarName := range matches {
if err = os.Remove(sidecarName); err != nil {
log.Errorf("media: failed removing sidecar %s", txt.LogParam(fs.RelName(sidecarName, sidecarPath)))
log.Errorf("media: failed removing sidecar %s", sanitize.Log(fs.RelName(sidecarName, sidecarPath)))
} else {
log.Infof("media: removed sidecar %s", txt.LogParam(fs.RelName(sidecarName, sidecarPath)))
log.Infof("media: removed sidecar %s", sanitize.Log(fs.RelName(sidecarName, sidecarPath)))
}
}

View file

@ -8,7 +8,7 @@ import (
"github.com/photoprism/photoprism/internal/meta"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// HasSidecarJson returns true if this file has or is a json sidecar file.
@ -80,7 +80,7 @@ func (m *MediaFile) MetaData() (result meta.Data) {
// Parse regular JSON sidecar files ("img_1234.json")
if !m.IsSidecar() {
if jsonFiles := fs.FormatJson.FindAll(m.FileName(), []string{Config().SidecarPath(), fs.HiddenPath}, Config().OriginalsPath(), false); len(jsonFiles) == 0 {
log.Tracef("metadata: found no additional sidecar file for %s", txt.LogParam(filepath.Base(m.FileName())))
log.Tracef("metadata: found no additional sidecar file for %s", sanitize.Log(filepath.Base(m.FileName())))
} else {
for _, jsonFile := range jsonFiles {
jsonErr := m.metaData.JSON(jsonFile, m.BaseName())
@ -102,7 +102,7 @@ func (m *MediaFile) MetaData() (result meta.Data) {
if err != nil {
m.metaData.Error = err
log.Debugf("metadata: %s in %s", err, txt.LogParam(m.BaseName()))
log.Debugf("metadata: %s in %s", err, sanitize.Log(m.BaseName()))
}
})

View file

@ -13,7 +13,7 @@ import (
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Moments represents a worker that creates albums based on popular locations, dates and labels.
@ -95,11 +95,11 @@ func (w *Moments) Start() (err error) {
if a := entity.FindFolderAlbum(mom.Path); a != nil {
if a.DeletedAt != nil {
// Nothing to do.
log.Tracef("moments: %s was deleted (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s was deleted (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
} else if err := a.UpdateFolder(mom.Path, f.Serialize()); err != nil {
log.Errorf("moments: %s (update folder)", err.Error())
} else {
log.Tracef("moments: %s already exists (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s already exists (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
} else if a := entity.NewFolderAlbum(mom.Title(), mom.Path, f.Serialize()); a != nil {
a.AlbumYear = mom.FolderYear
@ -110,7 +110,7 @@ func (w *Moments) Start() (err error) {
if err := a.Create(); err != nil {
log.Errorf("moments: %s (create folder)", err)
} else {
log.Infof("moments: added %s (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Infof("moments: added %s (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
}
}
@ -127,17 +127,17 @@ func (w *Moments) Start() (err error) {
}
if !a.Deleted() {
log.Tracef("moments: %s already exists (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s already exists (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
} else if err := a.Restore(); err != nil {
log.Errorf("moments: %s (restore month)", err.Error())
} else {
log.Infof("moments: %s restored", txt.LogParam(a.AlbumTitle))
log.Infof("moments: %s restored", sanitize.Log(a.AlbumTitle))
}
} else if a := entity.NewMonthAlbum(mom.Title(), mom.Slug(), mom.Year, mom.Month); a != nil {
if err := a.Create(); err != nil {
log.Errorf("moments: %s", err)
} else {
log.Infof("moments: added %s (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Infof("moments: added %s (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
}
}
@ -161,9 +161,9 @@ func (w *Moments) Start() (err error) {
if a.DeletedAt != nil {
// Nothing to do.
log.Tracef("moments: %s was deleted (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s was deleted (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
} else {
log.Tracef("moments: %s already exists (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s already exists (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
} else if a := entity.NewMomentsAlbum(mom.Title(), mom.Slug(), f.Serialize()); a != nil {
a.AlbumYear = mom.Year
@ -172,7 +172,7 @@ func (w *Moments) Start() (err error) {
if err := a.Create(); err != nil {
log.Errorf("moments: %s", err)
} else {
log.Infof("moments: added %s (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Infof("moments: added %s (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
}
}
@ -195,11 +195,11 @@ func (w *Moments) Start() (err error) {
}
if !a.Deleted() {
log.Tracef("moments: %s already exists (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s already exists (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
} else if err := a.Restore(); err != nil {
log.Errorf("moments: %s (restore state)", err.Error())
} else {
log.Infof("moments: %s restored", txt.LogParam(a.AlbumTitle))
log.Infof("moments: %s restored", sanitize.Log(a.AlbumTitle))
}
} else if a := entity.NewStateAlbum(mom.Title(), mom.Slug(), f.Serialize()); a != nil {
a.AlbumLocation = mom.CountryName()
@ -209,7 +209,7 @@ func (w *Moments) Start() (err error) {
if err := a.Create(); err != nil {
log.Errorf("moments: %s", err)
} else {
log.Infof("moments: added %s (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Infof("moments: added %s (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
}
}
@ -233,20 +233,20 @@ func (w *Moments) Start() (err error) {
}
if a.DeletedAt != nil || f.Serialize() == a.AlbumFilter {
log.Tracef("moments: %s already exists (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Tracef("moments: %s already exists (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
continue
}
if err := a.Update("AlbumFilter", f.Serialize()); err != nil {
log.Errorf("moments: %s", err.Error())
} else {
log.Debugf("moments: updated %s (%s)", txt.LogParam(a.AlbumTitle), f.Serialize())
log.Debugf("moments: updated %s (%s)", sanitize.Log(a.AlbumTitle), f.Serialize())
}
} else if a := entity.NewMomentsAlbum(mom.Title(), mom.Slug(), f.Serialize()); a != nil {
if err := a.Create(); err != nil {
log.Errorf("moments: %s", err.Error())
} else {
log.Infof("moments: added %s (%s)", txt.LogParam(a.AlbumTitle), a.AlbumFilter)
log.Infof("moments: added %s (%s)", sanitize.Log(a.AlbumTitle), a.AlbumFilter)
}
} else {
log.Errorf("moments: failed to create new moment %s (%s)", mom.Title(), f.Serialize())

View file

@ -13,7 +13,7 @@ import (
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Purge represents a worker that removes missing files from search results.
@ -87,20 +87,20 @@ func (w *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPhot
if file.FileMissing {
if fs.FileExists(fileName) {
if opt.Dry {
log.Infof("purge: found %s", txt.LogParam(file.FileName))
log.Infof("purge: found %s", sanitize.Log(file.FileName))
continue
}
if err := file.Found(); err != nil {
log.Errorf("purge: %s", err)
} else {
log.Infof("purge: found %s", txt.LogParam(file.FileName))
log.Infof("purge: found %s", sanitize.Log(file.FileName))
}
}
} else if !fs.FileExists(fileName) {
if opt.Dry {
purgedFiles[fileName] = true
log.Infof("purge: file %s would be flagged as missing", txt.LogParam(file.FileName))
log.Infof("purge: file %s would be flagged as missing", sanitize.Log(file.FileName))
continue
}
@ -113,7 +113,7 @@ func (w *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPhot
w.files.Remove(file.FileName, file.FileRoot)
purgedFiles[fileName] = true
log.Infof("purge: flagged file %s as missing", txt.LogParam(file.FileName))
log.Infof("purge: flagged file %s as missing", sanitize.Log(file.FileName))
if !wasPrimary {
continue
@ -162,7 +162,7 @@ func (w *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPhot
if !fs.FileExists(fileName) {
if opt.Dry {
purgedFiles[fileName] = true
log.Infof("purge: duplicate %s would be removed", txt.LogParam(file.FileName))
log.Infof("purge: duplicate %s would be removed", sanitize.Log(file.FileName))
continue
}
@ -171,7 +171,7 @@ func (w *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPhot
} else {
w.files.Remove(file.FileName, file.FileRoot)
purgedFiles[fileName] = true
log.Infof("purge: removed duplicate %s", txt.LogParam(file.FileName))
log.Infof("purge: removed duplicate %s", sanitize.Log(file.FileName))
}
}
}
@ -210,7 +210,7 @@ func (w *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPhot
if opt.Dry {
purgedPhotos[photo.PhotoUID] = true
log.Infof("purge: %s would be removed", txt.LogParam(photo.PhotoName))
log.Infof("purge: %s would be removed", sanitize.Log(photo.PhotoName))
continue
}
@ -220,9 +220,9 @@ func (w *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPhot
purgedPhotos[photo.PhotoUID] = true
if opt.Hard {
log.Infof("purge: permanently removed %s", txt.LogParam(photo.PhotoName))
log.Infof("purge: permanently removed %s", sanitize.Log(photo.PhotoName))
} else {
log.Infof("purge: flagged photo %s as deleted", txt.LogParam(photo.PhotoName))
log.Infof("purge: flagged photo %s as deleted", sanitize.Log(photo.PhotoName))
}
// Remove files from lookup table.

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/search"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Albums returns a slice of albums.
@ -52,7 +52,7 @@ func AlbumCoverByUID(uid string) (file entity.File, err error) {
if err := a.Delete(); err != nil {
log.Errorf("%s: %s (hide)", a.AlbumType, err)
} else {
log.Infof("%s: %s hidden", a.AlbumType, txt.LogParam(a.AlbumTitle))
log.Infof("%s: %s hidden", a.AlbumType, sanitize.Log(a.AlbumTitle))
}
}

View file

@ -6,7 +6,7 @@ import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/face"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Faces returns all (known / unmatched) faces from the index.
@ -138,15 +138,15 @@ func MergeFaces(merge entity.Faces) (merged *entity.Face, err error) {
for i := 1; i < len(merge); i++ {
if merge[i].SubjUID != subjUID {
return merged, fmt.Errorf("faces: can't merge clusters with conflicting subjects %s <> %s",
txt.LogParam(subjUID), txt.LogParam(merge[i].SubjUID))
sanitize.Log(subjUID), sanitize.Log(merge[i].SubjUID))
}
}
// Find or create merged face cluster.
if merged = entity.NewFace(merge[0].SubjUID, merge[0].FaceSrc, merge.Embeddings()); merged == nil {
return merged, fmt.Errorf("faces: new cluster is nil for subject %s", txt.LogParam(subjUID))
return merged, fmt.Errorf("faces: new cluster is nil for subject %s", sanitize.Log(subjUID))
} else if merged = entity.FirstOrCreateFace(merged); merged == nil {
return merged, fmt.Errorf("faces: failed creating new cluster for subject %s", txt.LogParam(subjUID))
return merged, fmt.Errorf("faces: failed creating new cluster for subject %s", sanitize.Log(subjUID))
} else if err := merged.MatchMarkers(append(merge.IDs(), "")); err != nil {
return merged, err
}
@ -155,9 +155,9 @@ func MergeFaces(merge entity.Faces) (merged *entity.Face, err error) {
if removed, err := PurgeOrphanFaces(merge.IDs()); err != nil {
return merged, err
} else if removed > 0 {
log.Debugf("faces: removed %d orphans for subject %s", removed, txt.LogParam(subjUID))
log.Debugf("faces: removed %d orphans for subject %s", removed, sanitize.Log(subjUID))
} else {
log.Warnf("faces: failed removing merged clusters for subject %s", txt.LogParam(subjUID))
log.Warnf("faces: failed removing merged clusters for subject %s", sanitize.Log(subjUID))
}
return merged, err
@ -185,13 +185,13 @@ func ResolveFaceCollisions() (conflicts, resolved int, err error) {
log.Infof("face %s: ambiguous subject at dist %f, Ø %f from %d samples, collision Ø %f", f1.ID, dist, r, f1.Samples, f1.CollisionRadius)
if f1.SubjUID != "" {
log.Debugf("face %s: subject %s (%s %s)", f1.ID, txt.LogParam(f1.SubjUID), f1.SubjUID, entity.SrcString(f1.FaceSrc))
log.Debugf("face %s: subject %s (%s %s)", f1.ID, sanitize.Log(f1.SubjUID), f1.SubjUID, entity.SrcString(f1.FaceSrc))
} else {
log.Debugf("face %s: has no subject (%s)", f1.ID, entity.SrcString(f1.FaceSrc))
}
if f2.SubjUID != "" {
log.Debugf("face %s: subject %s (%s %s)", f2.ID, txt.LogParam(f2.SubjUID), f2.SubjUID, entity.SrcString(f2.FaceSrc))
log.Debugf("face %s: subject %s (%s %s)", f2.ID, sanitize.Log(f2.SubjUID), f2.SubjUID, entity.SrcString(f2.FaceSrc))
} else {
log.Debugf("face %s: has no subject (%s)", f2.ID, entity.SrcString(f2.FaceSrc))
}

View file

@ -3,9 +3,8 @@ package query
import (
"fmt"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// People returns the sorted names of the first 2000 people.
@ -94,10 +93,10 @@ func CreateMarkerSubjects() (affected int64, err error) {
if name == m.MarkerName && subj != nil {
// Do nothing.
} else if subj = entity.NewSubject(m.MarkerName, entity.SubjPerson, entity.SrcMarker); subj == nil {
log.Errorf("faces: invalid subject %s", txt.LogParam(m.MarkerName))
log.Errorf("faces: invalid subject %s", sanitize.Log(m.MarkerName))
continue
} else if subj = entity.FirstOrCreateSubject(subj); subj == nil {
log.Errorf("faces: failed adding subject %s", txt.LogParam(m.MarkerName))
log.Errorf("faces: failed adding subject %s", sanitize.Log(m.MarkerName))
continue
} else {
affected++

View file

@ -6,6 +6,7 @@ import (
"github.com/gosimple/slug"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -59,7 +60,7 @@ func Labels(f form.SearchLabels) (results []Label, err error) {
likeString := "%" + f.Query + "%"
if result := Db().First(&label, "label_slug = ? OR custom_slug = ?", slugString, slugString); result.Error != nil {
log.Infof("search: label %s not found", txt.LogParam(f.Query))
log.Infof("search: label %s not found", sanitize.Log(f.Query))
s = s.Where("labels.label_name LIKE ?", likeString)
} else {
@ -71,7 +72,7 @@ func Labels(f form.SearchLabels) (results []Label, err error) {
labelIds = append(labelIds, category.LabelID)
}
log.Infof("search: label %s includes %d categories", txt.LogParam(label.LabelName), len(labelIds))
log.Infof("search: label %s includes %d categories", sanitize.Log(label.LabelName), len(labelIds))
s = s.Where("labels.id IN (?)", labelIds)
}

View file

@ -3,9 +3,8 @@ package server
import (
"time"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Logger instances a Logger middleware for Gin.
@ -34,7 +33,7 @@ func Logger() gin.HandlerFunc {
// Use debug level to keep production logs clean.
log.Debugf("http: %s %s (%3d) [%v]",
method,
txt.LogParam(path),
sanitize.Log(path),
statusCode,
latency,
)

View file

@ -6,8 +6,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
const (
@ -83,7 +82,7 @@ func (s *security) process(w http.ResponseWriter, r *http.Request) error {
if !isGoodHost {
s.opt.BadHostHandler.ServeHTTP(w, r)
return fmt.Errorf("http: bad host %s", txt.LogParam(r.Host))
return fmt.Errorf("http: bad host %s", sanitize.Log(r.Host))
}
}

View file

@ -7,8 +7,7 @@ import (
"strings"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/auto"
@ -25,7 +24,7 @@ func MarkUploadAsFavorite(fileName string) {
// Abort if YAML file already exists to avoid overwriting metadata.
if fs.FileExists(yamlName) {
log.Warnf("webdav: %s already exists", txt.LogParam(filepath.Base(yamlName)))
log.Warnf("webdav: %s already exists", sanitize.Log(filepath.Base(yamlName)))
return
}
@ -42,7 +41,7 @@ func MarkUploadAsFavorite(fileName string) {
}
// Log success.
log.Infof("webdav: marked %s as favorite", txt.LogParam(filepath.Base(fileName)))
log.Infof("webdav: marked %s as favorite", sanitize.Log(filepath.Base(fileName)))
}
// WebDAV handles any requests to /originals|import/*
@ -67,11 +66,11 @@ func WebDAV(path string, router *gin.RouterGroup, conf *config.Config) {
if err != nil {
switch r.Method {
case MethodPut, MethodPost, MethodPatch, MethodDelete, MethodCopy, MethodMove:
log.Errorf("webdav: %s in %s %s", txt.LogParam(err.Error()), txt.LogParam(r.Method), txt.LogParam(r.URL.String()))
log.Errorf("webdav: %s in %s %s", sanitize.Log(err.Error()), sanitize.Log(r.Method), sanitize.Log(r.URL.String()))
case MethodPropfind:
log.Tracef("webdav: %s in %s %s", txt.LogParam(err.Error()), txt.LogParam(r.Method), txt.LogParam(r.URL.String()))
log.Tracef("webdav: %s in %s %s", sanitize.Log(err.Error()), sanitize.Log(r.Method), sanitize.Log(r.URL.String()))
default:
log.Debugf("webdav: %s in %s %s", txt.LogParam(err.Error()), txt.LogParam(r.Method), txt.LogParam(r.URL.String()))
log.Debugf("webdav: %s in %s %s", sanitize.Log(err.Error()), sanitize.Log(r.Method), sanitize.Log(r.URL.String()))
}
} else {
@ -86,7 +85,7 @@ func WebDAV(path string, router *gin.RouterGroup, conf *config.Config) {
switch r.Method {
case MethodPut, MethodPost, MethodPatch, MethodDelete, MethodCopy, MethodMove:
log.Infof("webdav: %s %s", txt.LogParam(r.Method), txt.LogParam(r.URL.String()))
log.Infof("webdav: %s %s", sanitize.Log(r.Method), sanitize.Log(r.URL.String()))
if router.BasePath() == WebDAVOriginals {
auto.ShouldIndex()
@ -94,7 +93,7 @@ func WebDAV(path string, router *gin.RouterGroup, conf *config.Config) {
auto.ShouldImport()
}
default:
log.Tracef("webdav: %s %s", txt.LogParam(r.Method), txt.LogParam(r.URL.String()))
log.Tracef("webdav: %s %s", sanitize.Log(r.Method), sanitize.Log(r.URL.String()))
}
}
},

View file

@ -12,7 +12,7 @@ import (
"github.com/disintegration/imaging"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Suffix returns the thumb cache file suffix.
@ -35,7 +35,7 @@ func FileName(hash string, thumbPath string, width, height int, opts ...Resample
}
if len(hash) < 4 {
return "", fmt.Errorf("resample: file hash is empty or too short (%s)", txt.LogParam(hash))
return "", fmt.Errorf("resample: file hash is empty or too short (%s)", sanitize.Log(hash))
}
if len(thumbPath) == 0 {
@ -57,11 +57,11 @@ func FileName(hash string, thumbPath string, width, height int, opts ...Resample
// FromCache returns the thumb cache file name for an image.
func FromCache(imageFilename, hash, thumbPath string, width, height int, opts ...ResampleOption) (fileName string, err error) {
if len(hash) < 4 {
return "", fmt.Errorf("resample: invalid file hash %s", txt.LogParam(hash))
return "", fmt.Errorf("resample: invalid file hash %s", sanitize.Log(hash))
}
if len(imageFilename) < 4 {
return "", fmt.Errorf("resample: invalid file name %s", txt.LogParam(imageFilename))
return "", fmt.Errorf("resample: invalid file name %s", sanitize.Log(imageFilename))
}
fileName, err = FileName(hash, thumbPath, width, height, opts...)
@ -135,7 +135,7 @@ func Create(img image.Image, fileName string, width, height int, opts ...Resampl
err = imaging.Save(result, fileName, saveOption)
if err != nil {
log.Errorf("resample: failed to save %s", txt.LogParam(filepath.Base(fileName)))
log.Errorf("resample: failed to save %s", sanitize.Log(filepath.Base(fileName)))
return result, err
}

View file

@ -5,14 +5,14 @@ import (
"path/filepath"
"github.com/disintegration/imaging"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
func Jpeg(srcFilename, jpgFilename string, orientation int) (img image.Image, err error) {
img, err = imaging.Open(srcFilename)
if err != nil {
log.Errorf("resample: can't open %s", txt.LogParam(filepath.Base(srcFilename)))
log.Errorf("resample: can't open %s", sanitize.Log(filepath.Base(srcFilename)))
return img, err
}
@ -23,7 +23,7 @@ func Jpeg(srcFilename, jpgFilename string, orientation int) (img image.Image, er
saveOption := imaging.JPEGQuality(JpegQuality)
if err = imaging.Save(img, jpgFilename, saveOption); err != nil {
log.Errorf("resample: failed to save %s", txt.LogParam(filepath.Base(jpgFilename)))
log.Errorf("resample: failed to save %s", sanitize.Log(filepath.Base(jpgFilename)))
return img, err
}

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/pkg/colors"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Open loads an image from disk, rotates it, and converts the color profile if necessary.
@ -49,7 +49,7 @@ func OpenJpeg(fileName string, orientation int) (result image.Image, err error)
return result, fmt.Errorf("filename missing")
}
logName := txt.LogParam(filepath.Base(fileName))
logName := sanitize.Log(filepath.Base(fileName))
// Open file.
fileReader, err := os.Open(fileName)
@ -81,7 +81,7 @@ func OpenJpeg(fileName string, orientation int) (result image.Image, err error)
// Do nothing.
log.Tracef("resample: detected no color profile in %s", logName)
} else if profile, err := iccProfile.Description(); err == nil && profile != "" {
log.Debugf("resample: detected color profile %s in %s", txt.LogParam(profile), logName)
log.Debugf("resample: detected color profile %s in %s", sanitize.Log(profile), logName)
switch {
case colors.ProfileDisplayP3.Equal(profile):
img = colors.ToSRGB(img, colors.ProfileDisplayP3)

View file

@ -5,14 +5,13 @@ import (
"path/filepath"
"time"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/remote/webdav"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// Uploads local files to a remote account
@ -56,7 +55,7 @@ func (worker *Sync) upload(a entity.Account) (complete bool, err error) {
continue // try again next time
}
log.Infof("sync: uploaded %s to %s (%s)", txt.LogParam(file.FileName), txt.LogParam(remoteName), a.AccName)
log.Infof("sync: uploaded %s to %s (%s)", sanitize.Log(file.FileName), sanitize.Log(remoteName), a.AccName)
fileSync := entity.NewFileSync(a.ID, remoteName)
fileSync.Status = entity.FileSyncUploaded

36
pkg/sanitize/filename.go Normal file
View file

@ -0,0 +1,36 @@
package sanitize
import (
"strings"
"unicode"
)
// FileName removes invalid character from a filename string.
func FileName(s string) string {
if len(s) > 512 || strings.Contains(s, "${") || strings.Contains(s, "/") || strings.Contains(s, "..") {
return ""
}
// Trim whitespace.
s = strings.TrimSpace(s)
// Remove non-printable and other potentially problematic characters.
s = strings.Map(func(r rune) rune {
if !unicode.IsPrint(r) {
return -1
}
switch r {
case '~', '/', '\\', ':', '|', '"', '?', '*', '<', '>', '{', '}':
return -1
default:
return r
}
}, s)
if s == "." || s == ".." {
return ""
}
return s
}

View file

@ -0,0 +1,37 @@
package sanitize
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFileName(t *testing.T) {
t.Run("Path", func(t *testing.T) {
assert.Equal(t, "", FileName("/go/src/github.com/photoprism/photoprism"))
})
t.Run("File", func(t *testing.T) {
assert.Equal(t, "filename.TXT", FileName("filename.TXT"))
})
t.Run("The quick brown fox.", func(t *testing.T) {
assert.Equal(t, "The quick brown fox.", FileName("The quick brown fox."))
})
t.Run("filename.txt", func(t *testing.T) {
assert.Equal(t, "filename.txt", FileName("filename.txt"))
})
t.Run("Empty", func(t *testing.T) {
assert.Equal(t, "", FileName(""))
})
t.Run("Dot", func(t *testing.T) {
assert.Equal(t, "", FileName("."))
})
t.Run("DotDot", func(t *testing.T) {
assert.Equal(t, "", FileName(".."))
})
t.Run("DotDotDot", func(t *testing.T) {
assert.Equal(t, "", FileName("..."))
})
t.Run("Replace", func(t *testing.T) {
assert.Equal(t, "", FileName("${https://<host>:<port>/<path>}"))
})
}

45
pkg/sanitize/log.go Normal file
View file

@ -0,0 +1,45 @@
package sanitize
import (
"fmt"
"strings"
"unicode"
)
// Log sanitizes strings created from user input in response to the log4j debacle.
func Log(s string) string {
if len(s) > 200 || strings.Contains(s, "${") {
return "?"
}
// Trim quotes, tabs, and newline characters.
s = strings.Trim(s, "'\"“`\t\n\r")
// Remove non-printable and other potentially problematic characters.
s = strings.Map(func(r rune) rune {
if !unicode.IsPrint(r) {
return -1
}
switch r {
case '`', '"':
return '\''
case '\\', '$', '<', '>', '{', '}':
return '?'
default:
return r
}
}, s)
// Empty?
if s == "" || strings.ContainsAny(s, " ") {
return fmt.Sprintf("'%s'", s)
}
return s
}
// LogLower sanitizes strings created from user input and converts them to lowercase.
func LogLower(s string) string {
return Log(strings.ToLower(s))
}

43
pkg/sanitize/log_test.go Normal file
View file

@ -0,0 +1,43 @@
package sanitize
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestLog(t *testing.T) {
t.Run("The quick brown fox.", func(t *testing.T) {
assert.Equal(t, "'The quick brown fox.'", Log("The quick brown fox."))
})
t.Run("filename.txt", func(t *testing.T) {
assert.Equal(t, "filename.txt", Log("filename.txt"))
})
t.Run("empty string", func(t *testing.T) {
assert.Equal(t, "''", Log(""))
})
t.Run("Replace", func(t *testing.T) {
assert.Equal(t, "?", Log("${https://<host>:<port>/<path>}"))
})
t.Run("Ldap", func(t *testing.T) {
assert.Equal(t, "'User-Agent: ?jndi:ldap://?host?:?port?/?path??'", Log("User-Agent: {jndi:ldap://<host>:<port>/<path>}"))
})
}
func TestLogLower(t *testing.T) {
t.Run("The quick brown fox.", func(t *testing.T) {
assert.Equal(t, "'the quick brown fox.'", LogLower("The quick brown fox."))
})
t.Run("filename.txt", func(t *testing.T) {
assert.Equal(t, "filename.txt", LogLower("filename.TXT"))
})
t.Run("empty string", func(t *testing.T) {
assert.Equal(t, "''", LogLower(""))
})
t.Run("Replace", func(t *testing.T) {
assert.Equal(t, "?", LogLower("${https://<host>:<port>/<path>}"))
})
t.Run("Ldap", func(t *testing.T) {
assert.Equal(t, "?", LogLower("User-Agent: ${jndi:ldap://<host>:<port>/<path>}"))
})
}

32
pkg/sanitize/path.go Normal file
View file

@ -0,0 +1,32 @@
package sanitize
import (
"strings"
"unicode"
)
// Path removes invalid character from a path string.
func Path(s string) string {
if len(s) > 512 || strings.Contains(s, "${") || strings.Contains(s, "..") || strings.Contains(s, "//") {
return ""
}
// Trim whitespace.
s = strings.TrimSpace(s)
// Remove non-printable and other potentially problematic characters.
s = strings.Map(func(r rune) rune {
if !unicode.IsPrint(r) {
return -1
}
switch r {
case '~', '\\', ':', '|', '"', '?', '*', '<', '>', '{', '}':
return -1
default:
return r
}
}, s)
return s
}

37
pkg/sanitize/path_test.go Normal file
View file

@ -0,0 +1,37 @@
package sanitize
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPath(t *testing.T) {
t.Run("ValidPath", func(t *testing.T) {
assert.Equal(t, "/go/src/github.com/photoprism/photoprism", Path("/go/src/github.com/photoprism/photoprism"))
})
t.Run("ValidFile", func(t *testing.T) {
assert.Equal(t, "filename.TXT", Path("filename.TXT"))
})
t.Run("The quick brown fox.", func(t *testing.T) {
assert.Equal(t, "The quick brown fox.", Path("The quick brown fox."))
})
t.Run("filename.txt", func(t *testing.T) {
assert.Equal(t, "filename.txt", Path("filename.txt"))
})
t.Run("Empty", func(t *testing.T) {
assert.Equal(t, "", Path(""))
})
t.Run("Dot", func(t *testing.T) {
assert.Equal(t, ".", Path("."))
})
t.Run("DotDot", func(t *testing.T) {
assert.Equal(t, "", Path(".."))
})
t.Run("DotDotDot", func(t *testing.T) {
assert.Equal(t, "", Path("..."))
})
t.Run("Replace", func(t *testing.T) {
assert.Equal(t, "", Path("${https://<host>:<port>/<path>}"))
})
}