main pico / pkg / apps / feeds / scp_hooks.go
Antonio Mika  ·  2026-10-10
 1package feeds
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"net/url"
 7	"strings"
 8
 9	"github.com/adhocore/gronx"
10	"github.com/picosh/pico/pkg/db"
11	"github.com/picosh/pico/pkg/filehandlers"
12	"github.com/picosh/pico/pkg/pssh"
13	"github.com/picosh/pico/pkg/shared"
14)
15
16type FeedHooks struct {
17	Cfg *shared.ConfigSite
18	Db  db.DB
19}
20
21func (p *FeedHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) (bool, error) {
22	if !shared.IsTextFile(string(data.Text)) {
23		err := fmt.Errorf(
24			"WARNING: (%s) invalid file must be plain text (utf-8), skipping",
25			data.Filename,
26		)
27		return false, err
28	}
29
30	if !shared.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
31		extStr := strings.Join(p.Cfg.AllowedExt, ",")
32		err := fmt.Errorf(
33			"WARNING: (%s) invalid file, format must be (%s), skipping",
34			data.Filename,
35			extStr,
36		)
37		return false, err
38	}
39
40	// SFTP validates a file when it is opened, before its contents
41	// arrive, so there is nothing to parse yet.
42	if data.Text == "" {
43		return true, nil
44	}
45
46	parsed := shared.ListParseText(string(data.Text))
47	if parsed.Email == "" {
48		return false, fmt.Errorf("ERROR: no email variable detected for %s, check the format of your file, skipping", data.Filename)
49	}
50
51	if parsed.DigestInterval != "" {
52		return false, fmt.Errorf("ERROR: `digest_interval` is deprecated; use `cron`: https://pico.sh/feeds#cron")
53	}
54
55	if parsed.Cron != "" {
56		if !gronx.IsValid(parsed.Cron) {
57			return false, fmt.Errorf("ERROR: `cron` is invalid, reference: https://github.com/adhocore/gronx?tab=readme-ov-file#cron-expression")
58		}
59	}
60
61	var allErr error
62	for _, txt := range parsed.Items {
63		u := ""
64		if txt.IsText {
65			u = txt.Value
66		} else if txt.IsURL {
67			u = string(txt.URL)
68		}
69
70		_, err := url.Parse(u)
71		if err != nil {
72			allErr = errors.Join(allErr, fmt.Errorf("%s: %w", u, err))
73			continue
74		}
75	}
76	if allErr != nil {
77		return false, fmt.Errorf("ERROR: some urls provided were invalid check the format of your file, skipping: %w", allErr)
78	}
79
80	return true, nil
81}
82
83func (p *FeedHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error {
84	return nil
85}