func fetchLatestMessage(msg *pandora.Message, db querier, inbox string, now time.Time, dur time.Duration) error { err := reEnqueueMessages(db, now) if err != nil { return err } inboxId, err := findInbox(db, inbox, false) if err != nil { return err } var buf []byte var id int64 err = db.QueryRow(`select id, mid, status, receivedat, sendwhen, deliverycount from pgstore_messages where receiverid = $1 and lid is null and sendwhen <= $2 and status <> $3 order by sendwhen asc limit 1`, inboxId, now, pandora.StatusConfirmed).Scan(&id, &buf, &msg.Status, &msg.ReceivedAt, &msg.SendWhen, &msg.DeliveryCount) if err == sql.ErrNoRows { return pandora.ErrNoMessages } if err != nil { return err } msg.Mid = &pandora.SHA1Key{} copy(msg.Mid.Bytes(), buf) msg.CalcualteLeaseFor(now, dur) _, err = db.Exec("update pgstore_messages set lid = $1, deliverycount = deliverycount + 1, leaseuntil = $2 where id = $3", msg.Lid.Bytes(), msg.LeasedUntil, id) return err }
func findSenderReceiver(db querier, msg *pandora.Message, create bool) (sid int64, rid int64, err error) { senderName, receiverName := msg.Sender(), msg.Receiver() sid, err = findInbox(db, senderName, create) if err != nil { return } rid, err = findInbox(db, receiverName, create) if err != nil { return } return }
// Enqueue will place the message inside the receiver inbox func (ms *MessageStore) Enqueue(msg *pandora.Message) error { msg.Status = pandora.StatusNotDelivered msg.CalculateMid() return doInsideTransaction(ms.conn, func(tx querier) error { senderId, receiverId, err := findSenderReceiver(tx, msg, true) if err != nil { return err } var id int64 err = tx.QueryRow("insert into pgstore_messages(mid, status, receivedat, sendwhen, deliverycount, senderid, receiverid) values ($1, $2, $3, $4, $5, $6, $7) returning id", msg.Mid.Bytes(), msg.Status, msg.ReceivedAt, msg.SendWhen, msg.DeliveryCount, senderId, receiverId).Scan(&id) return err }) }
func fetchHeaders(out []pandora.Message, db querier, inbox string, now, min time.Time) ([]pandora.Message, error) { err := reEnqueueMessages(db, now) if err != nil { return nil, err } inboxId, err := findInbox(db, inbox, false) if err != nil { return nil, err } results, err := db.Query(`select mid, status, receivedat, sendwhen, deliverycount from pgstore_messages where receiverid = $1 and lid is null and sendwhen <= $2 and receivedat > $3 and status <> $4 order by receivedat asc, sendwhen asc limit $5`, inboxId, now, min, pandora.StatusConfirmed, len(out)) if err != nil { return nil, err } defer results.Close() var idx int var buf []byte for results.Next() { var msg *pandora.Message if idx < len(out) { msg = &out[idx] } else { break } msg.Mid = &pandora.SHA1Key{} err := results.Scan(&buf, &msg.Status, &msg.ReceivedAt, &msg.SendWhen, &msg.DeliveryCount) if err != nil { return out[:idx], err } copy(msg.Mid.Bytes(), buf) idx++ } return out[:idx], err }
// Send will update the given body with the paramters expected by a Pandora server // and return the Mid generated by the server or an error. func (mb *Mailbox) Send(from, to string, delay time.Duration, body url.Values) (string, error) { var msg pandora.Message msg.Empty(body) msg.SetSender(from) msg.SetReceiver(to) msg.SetClientTime(time.Now()) msg.Set("delay", delay.String()) res, err := mb.Client.PostForm(mb.BaseUrl+"/send", body) if err != nil { return "", err } defer res.Body.Close() if res.StatusCode != http.StatusOK { return "", fmt.Errorf("invalid status code: %v", res.StatusCode) } buf, err := ioutil.ReadAll(res.Body) if err != nil { return "", err } values, err := url.ParseQuery(string(buf)) if err != nil { return "", err } if len(values.Get("error")) > 0 { return "", errors.New(values.Get("error")) } return values.Get("mid"), nil }