Add ability to dump headers or mimetypes for original messages.
Add ability to dump all messages, instead of just hashes listed.
Add new command cmd/headers to import all headers from messages in table
original. Utility can work incrementally or do fully table imports.
Moved 'fetch all original messages' code to db/util.go. Added conditional
version given table name that will only return original messages that do not
have matching hashes in the specified table.
131 lines
2.3 KiB
Go
131 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"net/mail"
|
|
"sort"
|
|
"xinu.tv/email/db"
|
|
|
|
"github.com/golang/glog"
|
|
)
|
|
|
|
var (
|
|
dumpMime = flag.Bool("mime", false, "show multipart mime types for message")
|
|
headers = flag.Bool("headers", false, "show only headers")
|
|
)
|
|
|
|
func printMultipartMime(hash string, r io.Reader) error {
|
|
msg, err := mail.ReadMessage(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mediatype, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
glog.Infof("%s mt: %s p: %v", hash, mediatype, params)
|
|
|
|
return nil
|
|
}
|
|
|
|
func printHeaders(hash string, r io.Reader) error {
|
|
msg, err := mail.ReadMessage(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hdrs := make([]string, 0, len(msg.Header))
|
|
for k := range msg.Header {
|
|
hdrs = append(hdrs, k)
|
|
}
|
|
fmt.Println(hash)
|
|
sort.Strings(hdrs)
|
|
for _, k := range hdrs {
|
|
vs := msg.Header[k]
|
|
fmt.Printf("%s:", k)
|
|
for _, v := range vs {
|
|
fmt.Printf(" %s", v)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func printOneBlob(hash string, blob []byte) error {
|
|
r := bytes.NewReader(blob)
|
|
switch {
|
|
case *dumpMime:
|
|
if err := printMultipartMime(hash, r); err != nil {
|
|
// err != nil expected when mime type not found, so we log and
|
|
// squelch the error.
|
|
glog.Warningf("%s: %v", hash, err)
|
|
return nil
|
|
}
|
|
case *headers:
|
|
if err := printHeaders(hash, r); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
fmt.Println(string(blob))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type hashError struct {
|
|
hash string
|
|
error
|
|
}
|
|
|
|
func (he hashError) Error() string {
|
|
return fmt.Sprintf("%s: %v", he.hash, he.error)
|
|
}
|
|
|
|
func printAllBlobs(c *db.Conn) error {
|
|
oCh := make(chan db.Original)
|
|
errc := make(chan error)
|
|
donec := make(chan struct{})
|
|
defer close(donec)
|
|
go c.Originals(oCh, errc, donec)
|
|
for {
|
|
select {
|
|
case o := <-oCh:
|
|
if err := printOneBlob(o.Hash, o.Blob); err != nil {
|
|
return hashError{hash: o.Hash, error: err}
|
|
}
|
|
case err := <-errc:
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
defer glog.Flush()
|
|
flag.Parse()
|
|
|
|
c, err := db.NewConn("")
|
|
if err != nil {
|
|
glog.Fatal(err)
|
|
}
|
|
|
|
if flag.NArg() == 0 {
|
|
if err := printAllBlobs(c); err != nil {
|
|
glog.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
var blob []byte
|
|
for _, hash := range flag.Args() {
|
|
if err := c.OriginalBlobByHash(hash, &blob); err != nil {
|
|
glog.Fatal(err)
|
|
}
|
|
if err := printOneBlob(hash, blob); err != nil {
|
|
glog.Fatal(err)
|
|
}
|
|
}
|
|
}
|