init: pristine aerc 0.20.0 source
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
package msg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-message/mail"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/app"
|
||||
"git.sr.ht/~rjarry/aerc/commands"
|
||||
"git.sr.ht/~rjarry/aerc/commands/mode"
|
||||
"git.sr.ht/~rjarry/aerc/lib/log"
|
||||
"git.sr.ht/~rjarry/aerc/lib/send"
|
||||
"git.sr.ht/~rjarry/aerc/worker/types"
|
||||
)
|
||||
|
||||
type Bounce struct {
|
||||
Account string `opt:"-A" complete:"CompleteAccount" desc:"Account from which to re-send the message."`
|
||||
To []string `opt:"..." required:"true" complete:"CompleteTo" desc:"Recipient from address book."`
|
||||
}
|
||||
|
||||
func init() {
|
||||
commands.Register(Bounce{})
|
||||
}
|
||||
|
||||
func (Bounce) Description() string {
|
||||
return "Re-send the selected message(s) to the specified addresses."
|
||||
}
|
||||
|
||||
func (Bounce) Aliases() []string {
|
||||
return []string{"bounce", "resend"}
|
||||
}
|
||||
|
||||
func (*Bounce) CompleteAccount(arg string) []string {
|
||||
return commands.FilterList(app.AccountNames(), arg, commands.QuoteSpace)
|
||||
}
|
||||
|
||||
func (*Bounce) CompleteTo(arg string) []string {
|
||||
return commands.FilterList(commands.GetAddress(arg), arg, commands.QuoteSpace)
|
||||
}
|
||||
|
||||
func (Bounce) Context() commands.CommandContext {
|
||||
return commands.MESSAGE_LIST | commands.MESSAGE_VIEWER
|
||||
}
|
||||
|
||||
func (b Bounce) Execute(args []string) error {
|
||||
if len(b.To) == 0 {
|
||||
return errors.New("No recipients specified")
|
||||
}
|
||||
addresses := strings.Join(b.To, ", ")
|
||||
|
||||
app.PushStatus("Bouncing to "+addresses, 10*time.Second)
|
||||
|
||||
widget := app.SelectedTabContent().(app.ProvidesMessage)
|
||||
|
||||
var err error
|
||||
acct := widget.SelectedAccount()
|
||||
if b.Account != "" {
|
||||
acct, err = app.Account(b.Account)
|
||||
}
|
||||
switch {
|
||||
case err != nil:
|
||||
return fmt.Errorf("Failed to select account %q: %w", b.Account, err)
|
||||
case acct == nil:
|
||||
return errors.New("No account selected")
|
||||
}
|
||||
|
||||
store := widget.Store()
|
||||
if store == nil {
|
||||
return errors.New("Cannot perform action. Messages still loading")
|
||||
}
|
||||
|
||||
config := acct.AccountConfig()
|
||||
|
||||
outgoing, err := config.Outgoing.ConnectionString()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ReadCredentials()")
|
||||
}
|
||||
if outgoing == "" {
|
||||
return errors.New("No outgoing mail transport configured for this account")
|
||||
}
|
||||
uri, err := url.Parse(outgoing)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "url.Parse()")
|
||||
}
|
||||
|
||||
rcpts, err := mail.ParseAddressList(addresses)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ParseAddressList()")
|
||||
}
|
||||
|
||||
var domain string
|
||||
if domain_, ok := config.Params["smtp-domain"]; ok {
|
||||
domain = domain_
|
||||
}
|
||||
|
||||
hostname, err := send.GetMessageIdHostname(config.SendWithHostname, config.From)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetMessageIdHostname()")
|
||||
}
|
||||
|
||||
// According to RFC2822, all of the resent fields corresponding
|
||||
// to a particular resending of the message SHOULD be together.
|
||||
// Each new set of resent fields is prepended to the message;
|
||||
// that is, the most recent set of resent fields appear earlier in the
|
||||
// message.
|
||||
headers := fmt.Sprintf("Resent-From: %s\r\n", config.From)
|
||||
headers += "Resent-Date: %s\r\n"
|
||||
headers += "Resent-Message-ID: <%s>\r\n"
|
||||
headers += fmt.Sprintf("Resent-To: %s\r\n", addresses)
|
||||
|
||||
helper := newHelper()
|
||||
uids, err := helper.markedOrSelectedUids()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mode.NoQuit()
|
||||
|
||||
marker := store.Marker()
|
||||
marker.ClearVisualMark()
|
||||
|
||||
errCh := make(chan error)
|
||||
store.FetchFull(uids, func(fm *types.FullMessage) {
|
||||
defer log.PanicHandler()
|
||||
|
||||
var header mail.Header
|
||||
var msgId string
|
||||
var err, errClose error
|
||||
|
||||
uid := fm.Content.Uid
|
||||
msg := store.Messages[uid]
|
||||
if msg == nil {
|
||||
errCh <- fmt.Errorf("no message info: %v", uid)
|
||||
return
|
||||
}
|
||||
if err = header.GenerateMessageIDWithHostname(hostname); err != nil {
|
||||
errCh <- errors.Wrap(err, "GenerateMessageIDWithHostname()")
|
||||
return
|
||||
}
|
||||
if msgId, err = header.MessageID(); err != nil {
|
||||
errCh <- errors.Wrap(err, "MessageID()")
|
||||
return
|
||||
}
|
||||
reader := strings.NewReader(fmt.Sprintf(headers,
|
||||
time.Now().Format(time.RFC1123Z), msgId))
|
||||
|
||||
go func() {
|
||||
defer log.PanicHandler()
|
||||
defer func() { errCh <- err }()
|
||||
|
||||
var sender io.WriteCloser
|
||||
|
||||
log.Debugf("Bouncing email <%s> to %s",
|
||||
msg.Envelope.MessageId, addresses)
|
||||
|
||||
if sender, err = send.NewSender(acct.Worker(), uri,
|
||||
domain, config.From, rcpts, nil); err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
errClose = sender.Close()
|
||||
// If there has already been an error,
|
||||
// we don't want to clobber it.
|
||||
if err == nil {
|
||||
err = errClose
|
||||
} else if errClose != nil {
|
||||
app.PushError(errClose.Error())
|
||||
}
|
||||
}()
|
||||
if _, err = io.Copy(sender, reader); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(sender, fm.Content.Reader)
|
||||
}()
|
||||
})
|
||||
|
||||
go func() {
|
||||
defer log.PanicHandler()
|
||||
defer mode.NoQuitDone()
|
||||
|
||||
var total, success int
|
||||
|
||||
for err = range errCh {
|
||||
if err != nil {
|
||||
app.PushError(err.Error())
|
||||
} else {
|
||||
success++
|
||||
}
|
||||
total++
|
||||
if total == len(uids) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if success != total {
|
||||
marker.Remark()
|
||||
app.PushError(fmt.Sprintf("Failed to bounce %d of the messages",
|
||||
total-success))
|
||||
} else {
|
||||
plural := ""
|
||||
if success > 1 {
|
||||
plural = "s"
|
||||
}
|
||||
app.PushStatus(fmt.Sprintf("Bounced %d message%s",
|
||||
success, plural), 10*time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user