Read target mailbox for audit pane
This commit is contained in:
parent
2bdf622257
commit
ffa28f6182
3 changed files with 114 additions and 5 deletions
|
|
@ -11,6 +11,7 @@ type TargetMailbox interface {
|
|||
Delim() string // Hierarchie-Trenner des Ziels
|
||||
EnsureFolder(name string) error // anlegen + subscriben, falls fehlt
|
||||
Append(folder string, m RawMessage) error // mit m.Flags und m.InternalDate
|
||||
Fetch(folder string) ([]RawMessage, error) // Ziel-Audit: geschriebene Mail wiederfinden
|
||||
Close() error
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +79,13 @@ func (t *imapTarget) Append(folder string, m RawMessage) error {
|
|||
return t.c.appendMessage(folder, m)
|
||||
}
|
||||
|
||||
func (t *imapTarget) Fetch(folder string) ([]RawMessage, error) {
|
||||
if err := t.c.selectMailbox(folder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.c.fetchAll()
|
||||
}
|
||||
|
||||
func (t *imapTarget) Close() error {
|
||||
return t.c.Close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,27 +186,37 @@ func renderReadPane(w io.Writer, title, subject, from, date, body string) {
|
|||
func renderTargetPane(account, folder, targetAccount string, index int, raw []byte) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(renderTargetAccountSelect(account, folder, targetAccount, index))
|
||||
if match, ok, err := findTargetIMAPCopy(account, targetAccount, raw); ok {
|
||||
renderTargetMatch(&b, match)
|
||||
return b.String()
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(&b, `<div class="audit-note bad">Zielpostfach-Pruefung fehlgeschlagen: %s</div>`, html.EscapeString(err.Error()))
|
||||
}
|
||||
match, ok := findForwardedCopy(account, targetAccount, raw)
|
||||
if !ok {
|
||||
id := messageIDHeader(raw)
|
||||
if id == "" {
|
||||
id = "(ohne Message-ID)"
|
||||
}
|
||||
fmt.Fprintf(&b, `<div class="ef-empty">Keine lokale Zielkopie gefunden.<br><br>Message-ID: %s</div>`, html.EscapeString(id))
|
||||
fmt.Fprintf(&b, `<div class="ef-empty">Keine Zielkopie gefunden.<br><br>Message-ID: %s</div>`, html.EscapeString(id))
|
||||
return b.String()
|
||||
}
|
||||
renderTargetMatch(&b, match)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func renderTargetMatch(b *strings.Builder, match forwardedMatch) {
|
||||
msg, err := mail.ReadMessage(bytes.NewReader(match.Raw))
|
||||
if err != nil {
|
||||
renderReadContent(&b, "(unlesbar)", match.Location(), "", string(match.Raw))
|
||||
return b.String()
|
||||
renderReadContent(b, "(unlesbar)", match.Location(), "", string(match.Raw))
|
||||
return
|
||||
}
|
||||
renderReadContent(&b,
|
||||
renderReadContent(b,
|
||||
decodeHeader(msg.Header.Get("Subject")),
|
||||
decodeHeader(msg.Header.Get("From")),
|
||||
decodeHeader(msg.Header.Get("Date"))+" | "+match.Location(),
|
||||
messageBody(match.Raw),
|
||||
)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func renderInitialTargetPane() string {
|
||||
|
|
@ -279,6 +289,63 @@ func (m forwardedMatch) Location() string {
|
|||
return fmt.Sprintf("%s / %s #%d", m.Account, m.Folder, m.Index)
|
||||
}
|
||||
|
||||
func findTargetIMAPCopy(account, targetAccount string, raw []byte) (forwardedMatch, bool, error) {
|
||||
id := messageIDHeader(raw)
|
||||
if id == "" {
|
||||
return forwardedMatch{}, false, nil
|
||||
}
|
||||
targetUser := strings.TrimSpace(targetAccount)
|
||||
if targetUser == "" {
|
||||
source, ok := accountByMboxDir(account)
|
||||
if !ok {
|
||||
return forwardedMatch{}, false, nil
|
||||
}
|
||||
targetUser = strings.TrimSpace(source.DstUser)
|
||||
}
|
||||
for _, a := range accountsForTargetUser(targetUser) {
|
||||
dst, err := OpenIMAPTarget(a)
|
||||
if err != nil {
|
||||
return forwardedMatch{}, false, err
|
||||
}
|
||||
match, ok, scanErr := scanTargetForMessage(dst, targetUser, id)
|
||||
closeErr := dst.Close()
|
||||
if scanErr != nil {
|
||||
return forwardedMatch{}, false, scanErr
|
||||
}
|
||||
if closeErr != nil {
|
||||
return forwardedMatch{}, false, closeErr
|
||||
}
|
||||
if ok {
|
||||
return match, true, nil
|
||||
}
|
||||
}
|
||||
return forwardedMatch{}, false, nil
|
||||
}
|
||||
|
||||
func scanTargetForMessage(dst TargetMailbox, targetUser, messageID string) (forwardedMatch, bool, error) {
|
||||
folders, err := dst.Folders()
|
||||
if err != nil {
|
||||
return forwardedMatch{}, false, err
|
||||
}
|
||||
for _, folder := range folders {
|
||||
msgs, err := dst.Fetch(folder.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for i, msg := range msgs {
|
||||
if msg.MessageID == messageID || messageIDHeader(msg.Body) == messageID {
|
||||
return forwardedMatch{
|
||||
Account: "Ziel: " + targetUser,
|
||||
Folder: folder.Name,
|
||||
Index: i,
|
||||
Raw: msg.Body,
|
||||
}, true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return forwardedMatch{}, false, nil
|
||||
}
|
||||
|
||||
func findForwardedCopy(account, targetAccount string, raw []byte) (forwardedMatch, bool) {
|
||||
id := messageIDHeader(raw)
|
||||
if id == "" {
|
||||
|
|
@ -440,6 +507,39 @@ func accountLabels() map[string]viewerAccount {
|
|||
return out
|
||||
}
|
||||
|
||||
func accountByMboxDir(mboxDir string) (Account, bool) {
|
||||
if DB == nil {
|
||||
return Account{}, false
|
||||
}
|
||||
accounts, err := ListAccounts()
|
||||
if err != nil {
|
||||
return Account{}, false
|
||||
}
|
||||
for _, a := range accounts {
|
||||
if accountMboxDir(a) == mboxDir {
|
||||
return a, true
|
||||
}
|
||||
}
|
||||
return Account{}, false
|
||||
}
|
||||
|
||||
func accountsForTargetUser(targetUser string) []Account {
|
||||
if DB == nil {
|
||||
return nil
|
||||
}
|
||||
accounts, err := ListAccounts()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var out []Account
|
||||
for _, a := range accounts {
|
||||
if strings.EqualFold(strings.TrimSpace(a.DstUser), targetUser) {
|
||||
out = append(out, a)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func targetAccountOptions() []targetAccountOption {
|
||||
if DB == nil {
|
||||
return nil
|
||||
|
|
|
|||
1
frontend-js/dist/style.css
vendored
1
frontend-js/dist/style.css
vendored
|
|
@ -144,6 +144,7 @@ a.tree-node { display: block; color: inherit; text-decoration: none; }
|
|||
margin: 0;
|
||||
}
|
||||
.ef-empty { color: var(--ol-muted); padding: 24px; }
|
||||
.audit-note { padding: 10px 16px; border-bottom: 1px solid var(--ol-border); font-size: 12px; }
|
||||
|
||||
/* Buttons / Formulare (flach, 2013er) */
|
||||
.btn {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue