Example #1
0
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
}
Example #2
0
// 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
}
Example #3
0
// 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
}