func (m *Bmail) generateAck(s ServerOps) (ack obj.Object, powData *pow.Data, err error) { // If this is a broadcast message, no ack is expected. if m.To == "*****@*****.**" { return nil, nil, errors.New("No acks on broadcast messages.") } // if no object form exists, attempt to generate that. if m.object == nil { obj, _, err := m.GenerateObject(s) if obj == nil { smtpLog.Debug("trySend: could not generate message. Pubkey request sent? ", err == nil) if err == nil { return nil, nil, errors.New("Private key missing.") } return nil, nil, err } } // Get our private key. fromAddr, err := emailToBM(m.From) if err != nil { return nil, nil, err } from := s.GetPrivateID(fromAddr) addr := from.ToPublic().Address // TODO make a separate function in bmutil that does this. var signingKey, encKey wire.PubKey var destination wire.RipeHash sk := from.SigningKey.PubKey().SerializeUncompressed()[1:] ek := from.EncryptionKey.PubKey().SerializeUncompressed()[1:] copy(signingKey[:], sk) copy(encKey[:], ek) copy(destination[:], addr.Ripe[:]) // Add the message, which is a random number. buf := new(bytes.Buffer) num := rand.Int31() err = binary.Write(buf, binary.LittleEndian, num) if err != nil { return nil, nil, err } header := m.object.Header() msgAck := wire.NewMsgObject( wire.NewObjectHeader(0, header.Expiration(), wire.ObjectTypeMsg, obj.MessageVersion, addr.Stream, ), buf.Bytes()) // Encode it and save. m.Ack = wire.Encode(msgAck) return msgAck, &from.Data, nil }
// Serialize encodes the message in a protobuf format. func (m *Bmail) Serialize() ([]byte, error) { expr := m.Expiration.Format(dateFormat) var imapData *serialize.ImapData if m.ImapData != nil { t := m.ImapData.TimeReceived.Format(dateFormat) imapData = &serialize.ImapData{ TimeReceived: t, Flags: int32(m.ImapData.Flags), } } var object []byte if m.object != nil { object = wire.Encode(m.object) } var state *serialize.MessageState if m.state != nil { lastsend := m.state.LastSend.Format(dateFormat) state = &serialize.MessageState{ SendTries: m.state.SendTries, AckExpected: m.state.AckExpected, AckReceived: m.state.AckReceived, PubkeyRequested: m.state.PubkeyRequestOutstanding, LastSend: lastsend, Received: m.state.Received, } } smtpLog.Trace("Serializing Bitmessage from " + m.From + " to " + m.To) encode := &serialize.Message{ From: m.From, To: m.To, OfChannel: m.OfChannel, Expiration: expr, Ack: m.Ack, ImapData: imapData, Encoding: m.Content.ToProtobuf(), Object: object, State: state, } data, err := proto.Marshal(encode) if err != nil { return nil, err } return data, nil }
// trySend takes a Bitmessage and does whatever needs to be done to it // next in order to send it into the network. There are some steps in this // process that take a bit of time, so they can't all be handled sequentially // by one function. If we don't have the recipient's private key yet, we // can't send the message and we have to send a pubkey request to him instead. // On the other hand, messages might come in for which we already have the // public key and then we can go on to the next step right away. // // The next step is proof-of-work, and that also takes time. func (u *User) trySend(bmsg *Bmail) error { outbox := u.boxes[OutboxFolderName] // First we attempt to generate the wire.Object form of the message. // If we can't, then it is possible that we don't have the recipient's // pubkey. That is not an error state. If no object and no error is // returned, this is what has happened. If there is no object, then we // can't proceed futher so trySend completes. object, powData, err := bmsg.GenerateObject(u.server) if object == nil { smtpLog.Debug("trySend: could not generate message. Pubkey request sent? ", err == nil) if err == nil { bmsg.state.PubkeyRequestOutstanding = true err := outbox.saveBitmessage(bmsg) if err != nil { return err } return nil } return err } bmsg.state.PubkeyRequestOutstanding = false // sendPow takes a message in wire format with all required information // to send it over the network other than having proof-of-work run on it. // It generates the correct parameters for running the proof-of-work and // sends it to the proof-of-work queue with a function provided by the // user that says what to do with the completed message when the // proof-of-work is done. sendPow := func(object obj.Object, powData *pow.Data, done func([]byte)) { encoded := wire.Encode(object) q := encoded[8:] // exclude the nonce target := pow.CalculateTarget(uint64(len(q)), uint64(object.Header().Expiration().Sub(time.Now()).Seconds()), *powData) // Attempt to run pow on the message. u.server.RunPow(target, q, func(n pow.Nonce) { // Put the nonce bytes into the encoded form of the message. q = append(n.Bytes(), q...) done(q) }) } // sendPreparedMessage is used after we have looked up private keys and // generated an ack message, if applicable. sendPreparedMessage := func() error { err := outbox.saveBitmessage(bmsg) if err != nil { return err } // Put the prepared object in the pow queue and send it off // through the network. sendPow(object, powData, func(completed []byte) { err := func() error { // Save Bitmessage in outbox folder. err := u.boxes[OutboxFolderName].saveBitmessage(bmsg) if err != nil { return err } u.server.Send(completed) // Select new box for the message. var newBoxName string if bmsg.state.AckExpected { newBoxName = LimboFolderName } else { newBoxName = SentFolderName } bmsg.state.SendTries++ bmsg.state.LastSend = time.Now() return u.Move(bmsg, OutboxFolderName, newBoxName) }() // We can't return the error any further because this function // isn't even run until long after trySend completes! if err != nil { smtpLog.Error("trySend: ", err) } }) return nil } // We may desire to receive an ack with this message, which has not yet // been created. In that case, we need to do proof-of-work on the ack just // as on the entire message. if bmsg.Ack != nil && bmsg.state.AckExpected { ack, powData, err := bmsg.generateAck(u.server) if err != nil { return err } err = outbox.saveBitmessage(bmsg) if err != nil { return err } sendPow(ack, powData, func(completed []byte) { err := func() error { // Add the ack to the message. bmsg.Ack = completed return sendPreparedMessage() } // Once again, we can't return the error any further because // trySend is over by the time this function is run. if err != nil { smtpLog.Error("trySend: ", err) } }) } else { return sendPreparedMessage() } return nil }