func FetchMessages(c *imap.Client, uidSet *imap.SeqSet) (fetched []MsgData, err error) { cmd, errF := c.UIDFetch(uidSet, "RFC822") if errF != nil { err = errF return } for cmd.InProgress() { errC := c.Recv(-1) if errC != nil { return } for _, rsp := range cmd.Data { uid := imap.AsNumber(rsp.MessageInfo().Attrs["UID"]) mime := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822"]) msg, errR := mail.ReadMessage(bytes.NewReader(mime)) if errR != nil { continue } if msg != nil { msgdata := GetMessage(msg, uid) fetched = append(fetched, msgdata) } } cmd.Data = nil } return }
func (c *chatSyncClient) processChat(resp *imap.Response) { msgInfo := resp.MessageInfo() message := c.getMessage(msgInfo.Seq) headerBytes := msgInfo.Attrs["RFC822.HEADER"] headers := imap.AsBytes(headerBytes) message.headers, _ = mail.ReadMessage(bytes.NewReader(headers)) bodyBytes := msgInfo.Attrs["BODY[TEXT]"] body := imap.AsBytes(bodyBytes) message.body, _ = mail.ReadMessage(bytes.NewReader(body)) err := message.process() checkError(err) c.done <- message.seq }
// Retrieve metadata for all messages in a given mailbox. func (conn *Connection) Messages(box string, count uint32) ([]*MessageMeta, error) { conn.client.Select(box, true) // Create a range set that selects the most recent `count` messages (or // all messages if the mailbox contains fewer than that). Also, allocate a // slice for the results. set, _ := imap.NewSeqSet("") var messages []*MessageMeta totalCount := conn.client.Mailbox.Messages if totalCount >= count { set.AddRange(totalCount-(count-1), totalCount) messages = make([]*MessageMeta, count) } else { set.Add("1:*") messages = make([]*MessageMeta, totalCount) } // Fetch messages. cmd, err := conn.client.Fetch(set, "RFC822.HEADER RFC822.SIZE UID") if err != nil { return nil, err } // Parse each message. i := 0 for cmd.InProgress() { err = conn.client.Recv(time.Second * 5) if err != nil { return nil, err } // Process the message data received so far. for _, rsp := range cmd.Data { msgInfo := rsp.MessageInfo() header := imap.AsBytes(msgInfo.Attrs["RFC822.HEADER"]) msg, err := mail.ReadMessage(bytes.NewReader(header)) if err != nil { return nil, err } messages[i] = &MessageMeta{ UID: msgInfo.UID, Size: msgInfo.Size, Subject: msg.Header.Get("Subject"), } i++ } cmd.Data = nil } return messages, nil }
func FetchMessages(c *imap.Client, uidSet *imap.SeqSet) (err error) { cmd, err := c.UIDFetch(uidSet, "RFC822") for cmd.InProgress() { c.Recv(-1) for _, rsp := range cmd.Data { uid := imap.AsNumber(rsp.MessageInfo().Attrs["UID"]) mime := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822"]) if msg, _ := mail.ReadMessage(bytes.NewReader(mime)); msg != nil { PrintMessageAsJSON(msg, uid) } } cmd.Data = nil } return }
// checkAndPurge will pull message message ids off of requests and do some work func purgeDestination(user string, dsts []*imap.Client, checkRequests chan checkExistsRequest, wg *sync.WaitGroup) { defer wg.Done() cmd, err := GetAllMessages(dsts[0]) if err != nil { log.Printf("Unable to find destination messages: %s", err.Error()) } workRequests := make(chan WorkRequest) // launch purgers var purgers sync.WaitGroup for _, dstConn := range dsts { purgers.Add(1) go checkAndPurgeMessages(dstConn, workRequests, checkRequests, &purgers) } // build the requests and send them var rsp *imap.Response var indx int startTime := time.Now() log.Printf("Beginning check/purge for %s with %d messages", user, len(cmd.Data)) for indx, rsp = range cmd.Data { header := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.HEADER"]) if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil { header := "Message-Id" value := msg.Header.Get(header) // create the store request and pass it to each dst's storers workRequests <- WorkRequest{Value: value, Header: header, UID: rsp.MessageInfo().UID} if ((indx % 100) == 0) && (indx > 0) { since := time.Since(startTime) rate := 100 / since.Seconds() startTime = time.Now() log.Printf("Processed %d messages from %s. Rate: %f msg/s", indx, user, rate) } } } log.Printf("Done passing purge requests for %s", user) close(workRequests) purgers.Wait() return }
func FetchMessage(conn *imap.Client, messageUID uint32) (msg MessageData, err error) { seq, _ := imap.NewSeqSet("") seq.AddNum(messageUID) var cmd *imap.Command cmd, err = imap.Wait(conn.UIDFetch(seq, "INTERNALDATE", "BODY[]", "UID", "RFC822.HEADER")) if err != nil { log.Printf("Unable to fetch message (%d): %s", messageUID, err.Error()) return } if len(cmd.Data) == 0 { log.Printf("Unable to fetch message (%d) from src: NO DATA", messageUID) return msg, NotFound } msgFields := cmd.Data[0].MessageInfo().Attrs msg = MessageData{InternalDate: imap.AsDateTime(msgFields["INTERNALDATE"]), Body: imap.AsBytes(msgFields["BODY[]"])} return msg, nil }
// newEmailMessage will parse an imap.FieldMap into an map[string]interface{}. This // will expect the message to container the internaldate and the body with // all headers included. func newEmailMessage(msgFields imap.FieldMap) (map[string]interface{}, error) { var email map[string]interface{} // parse the header rawHeader := imap.AsBytes(msgFields["RFC822.HEADER"]) msg, err := mail.ReadMessage(bytes.NewReader(rawHeader)) if err != nil { return email, err } email = map[string]interface{}{ "internal_date": imap.AsDateTime(msgFields["INTERNALDATE"]), "body": imap.AsString(msgFields["BODY[]"]), "from": msg.Header.Get("From"), "to": msg.Header.Get("To"), "subject": msg.Header.Get("Subject"), } return email, nil }
// messages are usually created with just header information // this method downloads the actual body of the message from the server, // optionally marking the message as '\Seen', in IMAP terms. func (m *Email) Body(setRead bool) (body []byte, err error) { // cache if m.bodyData != nil { return m.bodyData, nil } // what will our FETCH request? var requestType string if setRead { requestType = "BODY[TEXT]" } else { requestType = "BODY.PEEK[TEXT]" } cmd, err := m.RetrieveRaw(requestType) cmd, err = imap.Wait(cmd, err) if err != nil { return } info := cmd.Data[0].MessageInfo() m.bodyData = imap.AsBytes(info.Attrs["BODY[TEXT]"]) return m.bodyData, nil }
func connectNet(user string) { netTimeout := time.Second * 30 cl, err := imap.DialTLS("imap.gmail.com", nil) if err != nil { log.Println(err) time.Sleep(netTimeout) return } defer cl.Logout(-1) log.Println("server says hello:", cl.Data[0].Info) cl.Data = nil log.Println("logging in") if cl.State() == imap.Login { _, err = cl.Login(user+"@gmail.com", "PASSHERE") if err != nil { log.Println(err) time.Sleep(netTimeout) return } } log.Println("logged in") log.Println("selecting INBOX") _, err = cl.Select("INBOX", false) if err != nil { log.Println(err) time.Sleep(netTimeout) return } log.Println("selected INBOX") set, _ := imap.NewSeqSet("") searchLoop: for { var successCounter int conTimeout := 5 log.Println("searching INBOX") cmd, err := imap.Wait(cl.Search("SUBJECT \"Confirm your email\"")) if err != nil { log.Println(err) break searchLoop } searchResults := cmd.Data[0].SearchResults() if searchResults == nil { log.Println("no email[s] found") time.Sleep(time.Second * 5) break searchLoop } else { log.Println("found", len(searchResults), "emails to spoof, results:", searchResults) set.AddNum(searchResults...) } var rsp *imap.Response link := regexp.MustCompile(`https:=\r\n\/\/invites.oneplus.net\/confirm\/[0-9A-Z]*.`) var UIDCounter int log.Println("beginning spoof confirmation loop") cmd, err = cl.Fetch(set, "RFC822") if err != nil { log.Println(err) break searchLoop } setDone, _ := imap.NewSeqSet("") getMailLoop: for cmd.InProgress() { cl.Recv(-1) for _, rsp = range cmd.Data { message := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822"]) if msg, err := mail.ReadMessage(bytes.NewReader(message)); msg != nil { log.Println("current conTimeout", conTimeout) body, err := ioutil.ReadAll(msg.Body) if err != nil { log.Println(err) break searchLoop } linkRawM := link.FindSubmatch(body) var linkRaw []byte if len(linkRawM) > 0 { linkRaw = linkRawM[0] } else { log.Println("found nolink mail") continue getMailLoop } log.Println(string(linkRaw)) url, err := url.Parse(string(linkRaw[:6]) + string(linkRaw[9:])) if err != nil { log.Println(err) break searchLoop } log.Println(url) req := confirmURL.ReplaceAll(reqTemplate, []byte(url.Path)[:len(url.Path)-1]) for { log.Println("attempting to spoof confirmation for", searchResults[UIDCounter]) c, err := tls.Dial("tcp", url.Host+":443", nil) if err != nil { log.Println(err) break searchLoop } _, err = c.Write(req) if err != nil { log.Println(err) break searchLoop } resp, err := http.ReadResponse(bufio.NewReader(c), nil) if err != nil { log.Println(err) break searchLoop } if resp.StatusCode == 302 { redirectReg := regexp.MustCompile(`https:\/\/oneplus\.net\/invites\?kid=[0-9A-Z]*`) redirectURL, err := resp.Location() if err != nil { log.Println(err) break searchLoop } log.Println(resp.Location()) if redirectReg.MatchString(redirectURL.String()) { log.Println("successfully spoofed confirmation for", searchResults[UIDCounter]) setDone.AddNum(searchResults[UIDCounter]) UIDCounter++ if successCounter == 10 { conTimeout-- successCounter = 0 } break } else { log.Println("server trying to migitate spoofing") conTimeout++ successCounter = 0 } } else { log.Println("gg rip hamsters") conTimeout++ successCounter = 0 } time.Sleep(time.Second * time.Duration(conTimeout)) } } else { log.Println(err) } } time.Sleep(time.Second * time.Duration(conTimeout)) cmd.Data = nil } log.Println("deleting mail", setDone) _, err = imap.Wait(cl.Store(setDone, "+FLAGS", "\\DELETED")) if err != nil { log.Println(err) break searchLoop } _, err = imap.Wait(cl.Expunge(nil)) if err != nil { log.Println(err) break searchLoop } } }
// SearchAndStore will check check if each message in the source inbox // exists in the destinations. If it doesn't exist in a destination, the message info will // be pulled and stored into the destination. func SearchAndStore(src []*imap.Client, dsts map[string][]*imap.Client, dbFile string, quickSyncCount int) (err error) { var cmd *imap.Command cmd, err = GetAllMessages(src[0]) if err != nil { log.Printf("Unable to get all messages!") return } // connect to cache cache, err := NewCache(dbFile) if err != nil { log.Printf("problems initiating cache - %s", err.Error()) return } defer cache.Close() // setup message fetchers to pull from the source/memcache fetchRequests := make(chan fetchRequest) for _, srcConn := range src { go fetchEmails(srcConn, fetchRequests, cache) } var appendRequests []chan WorkRequest var storers sync.WaitGroup // setup storers for each destination for _, dst := range dsts { storeRequests := make(chan WorkRequest) for _, dstConn := range dst { storers.Add(1) go CheckAndAppendMessages(dstConn, storeRequests, fetchRequests, &storers) } appendRequests = append(appendRequests, storeRequests) } // build the requests and send them log.Printf("store processing for %d messages from the source inbox", len(cmd.Data)) var rsp *imap.Response var indx int startTime := time.Now() syncStart := 0 // consider quick sync if quickSyncCount != 0 { syncStart = len(cmd.Data) - quickSyncCount log.Printf("found quick sync count. will only sync messages %d through %d", syncStart, len(cmd.Data)) } for indx, rsp = range cmd.Data[syncStart:] { header := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.HEADER"]) if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil { header := "Message-Id" value := msg.Header.Get(header) // create the store request and pass it to each dst's storers storeRequest := WorkRequest{Value: value, Header: header, UID: rsp.MessageInfo().UID} for _, storeRequests := range appendRequests { storeRequests <- storeRequest } if ((indx % 100) == 0) && (indx > 0) { since := time.Since(startTime) rate := 100 / since.Seconds() startTime = time.Now() log.Printf("Completed store processing for %d messages from the source inbox. Rate: %f msg/s", indx, rate) } } } // after everything is on the channel, close them... for _, storeRequests := range appendRequests { close(storeRequests) } // ... and wait for our workers to finish up. storers.Wait() // once the storers are complete we can close the fetch channel close(fetchRequests) log.Printf("search and store processes complete") return nil }
func GetMessage(folder, uid string, client *imap.Client) (messag *Message, e error) { cmd, err := client.Select(folder, true) if err != nil { return nil, err } uidlist, _ := imap.NewSeqSet(uid) //uidlist.Add(uid) fmt.Println("get_mess", folder, uid) mess := new(Message) mess.Folder = folder cmd, err = imap.Wait(client.UIDFetch(uidlist, "FLAGS", "INTERNALDATE", "RFC822.SIZE", "RFC822")) // "RFC822.HEADER", "BODY.PEEK[TEXT]") ) if err != nil { return mess, err } fmt.Println(len(cmd.Data), cmd.Data) rsp := cmd.Data[0] minfo := rsp.MessageInfo() mess.Uid = minfo.UID msg, _ := mail.ReadMessage(bytes.NewReader(imap.AsBytes(minfo.Attrs["RFC822"]))) mime, mime_err := enmime.ParseMIMEBody(msg) for flag, boo := range minfo.Flags { if flag == "\\Seen" && boo { mess.Seen = true } if flag == "\\Flagged" && boo { mess.Flagged = true } } /* bites := imap.AsBytes(minfo.Attrs["RFC822"]) msg, msg_err := mail.ReadMessage(bytes.NewReader(bites)) if msg_err != nil { return mess, msg_err } */ //fmt.Println(msg.Header.Get("Content-type")) //fmt.Println(msg.Header.Get("To")) //fmt.Println(msg.Header.Get("Delivered-To")) // From from, fro_err := mail.ParseAddress(msg.Header.Get("From")) if fro_err != nil { fmt.Println("address ettot") } else { mess.FromName = from.Name mess.FromEmail = from.Address } //for i, m := range minfo.Attrs { //fmt.Println(i,m) //} // Date dat := imap.AsDateTime(minfo.Attrs["INTERNALDATE"]) mess.Date = dat.Format("2006-01-02 15:04:05") mess.Subject = msg.Header.Get("Subject") mess.ContentType = msg.Header.Get("Content-Type") //fmt.Println("body=", cmd.Data[0].String) //bodyb := imap.AsBytes(minfo.Attrs["BODY[TEXT]"]) //bb := bytes.NewReader(bytes.NewReader(header + bodyb)) //fmt.Println("bodyb=", string(msg.Body)) //fmt.Printf("----\n%v\n", mime.Html == nil) //mess.Body = mime.Text if mime_err != nil { fmt.Println("err=", mime_err, mime) } //*/ //fmt.Println("body=", body) mess.BodyText = mime.Text mess.BodyHtml = mime.Html //imap.AsString(minfo.Attrs["RFC822"]) return mess, nil }
// gets all the messages on the server since the last message in the list func (m *Mailbox) Update() (newMail []*Email, err error) { c, err := m.server.Connect() if err != nil { return nil, err } // sync imap command to select the mailbox for actions c.Select(m.Name, true) lastHad := m.latestMessage // retrieve items wanted := fmt.Sprintf("%d:*", lastHad) set, err := imap.NewSeqSet(wanted) if err != nil { return nil, err } cmd, err := c.UIDFetch(set, "RFC822.HEADER UID") if err != nil { return nil, err } // result newMail = make([]*Email, 0, 5) for cmd.InProgress() { // Wait for the next response (no timeout) c.Recv(-1) // Process command data // retrieve message UID // construct local Message structure from given header // store message in map // append UID to newMessages list for _, rsp := range cmd.Data { info := rsp.MessageInfo() if info.Attrs["UID"] != nil { // construct message header := imap.AsBytes(info.Attrs["RFC822.HEADER"]) // TODO: catch this error if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil { // we could read the message and retrieve the UID // so this is valid to push into our storage system m.latestMessage = info.UID my_msg := &MessageNode{ Header: msg.Header, ContentType: msg.Header.Get(ContentType), } email := &Email{ server: m.server, mailbox: m, UID: info.UID, Message: my_msg, } // store newMail = append(newMail, email) m.Mail[info.UID] = email } else { fmt.Printf("mail.ReadMessage failed on UID %d\n", info.UID) } } else { fmt.Printf("Message %v had no UID. Skipped.\n", info) } } // clear data cmd.Data = nil } return newMail, nil }
func ExampleClient() { // // Note: most of error handling code is omitted for brevity // var ( c *imap.Client cmd *imap.Command rsp *imap.Response ) // Connect to the server c, _ = imap.Dial("imap.example.com") // Remember to log out and close the connection when finished defer c.Logout(30 * time.Second) // Print server greeting (first response in the unilateral server data queue) fmt.Println("Server says hello:", c.Data[0].Info) c.Data = nil // Enable encryption, if supported by the server if c.Caps["STARTTLS"] { c.StartTLS(nil) } // Authenticate if c.State() == imap.Login { c.Login("*****@*****.**", "mysupersecretpassword") } // List all top-level mailboxes, wait for the command to finish cmd, _ = imap.Wait(c.List("", "%")) // Print mailbox information fmt.Println("\nTop-level mailboxes:") for _, rsp = range cmd.Data { fmt.Println("|--", rsp.MailboxInfo()) } // Check for new unilateral server data responses for _, rsp = range c.Data { fmt.Println("Server data:", rsp) } c.Data = nil // Open a mailbox (synchronous command - no need for imap.Wait) c.Select("INBOX", true) fmt.Print("\nMailbox status:\n", c.Mailbox) // Fetch the headers of the 10 most recent messages set, _ := imap.NewSeqSet("") if c.Mailbox.Messages >= 10 { set.AddRange(c.Mailbox.Messages-9, c.Mailbox.Messages) } else { set.Add("1:*") } cmd, _ = c.Fetch(set, "RFC822.HEADER") // Process responses while the command is running fmt.Println("\nMost recent messages:") for cmd.InProgress() { // Wait for the next response (no timeout) c.Recv(-1) // Process command data for _, rsp = range cmd.Data { header := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.HEADER"]) if msg, _ := mail.ReadMessage(bytes.NewReader(header)); msg != nil { fmt.Println("|--", msg.Header.Get("Subject")) } } cmd.Data = nil // Process unilateral server data for _, rsp = range c.Data { fmt.Println("Server data:", rsp) } c.Data = nil } // Check command completion status if rsp, err := cmd.Result(imap.OK); err != nil { if err == imap.ErrAborted { fmt.Println("Fetch command aborted") } else { fmt.Println("Fetch error:", rsp.Info) } } }
// Get a Message out of a MessageInfo attribute. func messageAttr(message *imap.MessageInfo, attrName string) *mail.Message { content := imap.AsBytes(message.Attrs[attrName]) msg, _ := mail.ReadMessage(bytes.NewReader(content)) // panicMaybe(err) return msg }