mlisting

Mailing list service

git clone git://git.lin.moe/go/mlisting.git

  1package admin
  2
  3import (
  4	"bytes"
  5	"database/sql"
  6	"fmt"
  7	"io"
  8
  9	gomail "net/mail"
 10
 11	"git.lin.moe/go/mlisting/storage"
 12	"github.com/emersion/go-mbox"
 13	"github.com/spf13/cobra"
 14)
 15
 16func init() {
 17	msgDelCmd := &cobra.Command{
 18		Use:     "delete {group_address} {msg_id ...}",
 19		Aliases: []string{"del"},
 20		Short:   "delete messages recursive",
 21		Args:    cobra.MatchAll(cobra.MinimumNArgs(2), cobra.OnlyValidArgs),
 22		RunE:    msgDel,
 23	}
 24	msgSearchCmd := &cobra.Command{
 25		Use:   "search {group_address} {keyword}",
 26		Short: "search messages",
 27		Args:  cobra.MatchAll(cobra.RangeArgs(1, 2)),
 28		RunE:  msgSearch,
 29	}
 30	msgSearchCmd.Flags().Uint("offset", 0, "messages search offset")
 31	msgSearchCmd.Flags().Uint("limit", 50, "messages search limit")
 32
 33	msgImportCmd := &cobra.Command{
 34		Use:   "import {group_address}",
 35		Short: "import messages from stdin(mbox)",
 36		Args:  cobra.MatchAll(cobra.ExactArgs(1)),
 37		RunE:  msgImport,
 38	}
 39
 40	msgCmd := &cobra.Command{
 41		Use:     "message",
 42		Aliases: []string{"msg"},
 43		Short:   "messages management",
 44	}
 45
 46	msgCmd.AddCommand(msgDelCmd)
 47	msgCmd.AddCommand(msgSearchCmd)
 48	msgCmd.AddCommand(msgImportCmd)
 49
 50	cmd.AddCommand(msgCmd)
 51}
 52
 53func msgDel(cmd *cobra.Command, args []string) error {
 54	st, ok := storage.FromContext(cmd.Context())
 55	if !ok {
 56		return fmt.Errorf("storage not found")
 57	}
 58	list, err := st.GetList(cmd.Context(), args[0])
 59	if err != nil {
 60		if err == sql.ErrNoRows {
 61			return fmt.Errorf("not found list %s", args[0])
 62		}
 63		return err
 64	}
 65
 66	for _, msg_id := range args[1:] {
 67		msg, err := list.Message(cmd.Context(), msg_id)
 68		if err != nil {
 69			cmd.PrintErrf("query message [%s] failed: %v", msg_id, err)
 70			continue
 71		}
 72		submsgs, err := msg.SubMessages(cmd.Context(), true)
 73		if err != nil {
 74			cmd.PrintErrf("query submessage of [%s] failed: %v", msg_id, err)
 75			continue
 76		}
 77
 78		if !ask(cmd,
 79			fmt.Sprintf("Delete message [%s] and it's %d submessages, continue? [y/N]",
 80				msg.Subject(), len(submsgs)), false) {
 81			continue
 82		}
 83		msgs, err := list.DelMessagesRecursive(cmd.Context(), msg)
 84		if err != nil {
 85			cmd.PrintErrf("delete message [%s] failed: %v", msg_id, err)
 86			continue
 87		}
 88		for _, msg := range msgs {
 89			cmd.Printf("deleted message [%s]\n", msg.Subject())
 90		}
 91	}
 92
 93	return nil
 94}
 95
 96func msgSearch(cmd *cobra.Command, args []string) error {
 97	st, ok := storage.FromContext(cmd.Context())
 98	if !ok {
 99		return fmt.Errorf("storage not found")
100	}
101	list, err := st.GetList(cmd.Context(), args[0])
102	if err != nil {
103		if err == sql.ErrNoRows {
104			return fmt.Errorf("not found list %s", args[0])
105		}
106		return err
107	}
108	offset, _ := cmd.Flags().GetUint("offset")
109	limit, _ := cmd.Flags().GetUint("limit")
110	var key string = ""
111	if len(args) > 1 {
112		key = args[1]
113	}
114
115	msgs, _, err := list.Messages(cmd.Context(), false, key, offset, limit)
116	if err != nil {
117		return err
118	}
119	cmd.Printf("results %d:\n", len(msgs))
120	for _, msg := range msgs {
121		header := msg.Header()
122		msgID, _ := header.MessageID()
123
124		cmd.Printf("<%s>: %s\n", msgID, msg.Subject())
125	}
126
127	return nil
128}
129
130func msgImport(cmd *cobra.Command, args []string) error {
131	st, ok := storage.FromContext(cmd.Context())
132	if !ok {
133		return fmt.Errorf("storage not found")
134	}
135
136	list, err := st.GetList(cmd.Context(), args[0])
137	if err != nil {
138		if err == sql.ErrNoRows {
139			return fmt.Errorf("not found list %s", args[0])
140		}
141		return err
142	}
143
144	box_r := mbox.NewReader(cmd.InOrStdin())
145	var msgs []*gomail.Message
146	for {
147		msg_reader, err := box_r.NextMessage()
148		if err != nil {
149			if err == io.EOF {
150				break
151			} else {
152				return err
153			}
154		}
155		msg_raw, err := io.ReadAll(msg_reader)
156		if err != nil {
157			return err
158		}
159
160		msg, err := gomail.ReadMessage(bytes.NewReader(msg_raw))
161		if err != nil {
162			return err
163		}
164
165		msgs = append(msgs, msg)
166	}
167
168	for i, msg := range msgs {
169		subj := msg.Header.Get("Subject")
170		body, err := io.ReadAll(msg.Body)
171		if err != nil {
172			return fmt.Errorf("read message[%d][%s] body: %v", i, subj, err)
173		}
174
175		if _, err = list.AddMessage(cmd.Context(), msg.Header, body); err != nil {
176			return fmt.Errorf("add message[%d][%s]: %v", i, subj, err)
177		}
178		cmd.Printf("[%d]add message [%s]\n", i, subj)
179	}
180
181	return nil
182}