package send import ( "cwtch.im/cwtch/protocol" "cwtch.im/cwtch/protocol/spam" "errors" "git.openprivacy.ca/openprivacy/libricochet-go/channels" "git.openprivacy.ca/openprivacy/libricochet-go/utils" "git.openprivacy.ca/openprivacy/libricochet-go/wire/control" "github.com/golang/protobuf/proto" ) // CwtchPeerSendChannel is the peer implementation of im.cwtch.server.send type CwtchPeerSendChannel struct { channel *channels.Channel spamGuard spam.Guard challenge []byte } // Type returns the type string for this channel, e.g. "im.ricochet.server.send". func (cpsc *CwtchPeerSendChannel) Type() string { return "im.cwtch.server.send" } // Closed is called when the channel is closed for any reason. func (cpsc *CwtchPeerSendChannel) Closed(err error) { } // OnlyClientCanOpen - for Cwtch server channels only peers may open. func (cpsc *CwtchPeerSendChannel) OnlyClientCanOpen() bool { return true } // Singleton - for Cwtch channels there can only be one instance per direction func (cpsc *CwtchPeerSendChannel) Singleton() bool { return true } // Bidirectional - for Cwtch channels are not bidrectional func (cpsc *CwtchPeerSendChannel) Bidirectional() bool { return false } // RequiresAuthentication - Cwtch channels require no auth func (cpsc *CwtchPeerSendChannel) RequiresAuthentication() string { return "none" } // OpenInbound should never be called on peers. func (cpsc *CwtchPeerSendChannel) OpenInbound(channel *channels.Channel, raw *Protocol_Data_Control.OpenChannel) ([]byte, error) { return nil, errors.New("client does not receive inbound listen channels") } // OpenOutbound is used to set up a new send channel and initialize spamguard func (cpsc *CwtchPeerSendChannel) OpenOutbound(channel *channels.Channel) ([]byte, error) { cpsc.spamGuard.Difficulty = 2 cpsc.channel = channel messageBuilder := new(utils.MessageBuilder) return messageBuilder.OpenChannel(channel.ID, cpsc.Type()), nil } // OpenOutboundResult confirms the open channel request and sets the spamguard challenge func (cpsc *CwtchPeerSendChannel) OpenOutboundResult(err error, crm *Protocol_Data_Control.ChannelResult) { if err == nil { if crm.GetOpened() { ce, _ := proto.GetExtension(crm, protocol.E_ServerNonce) cpsc.challenge = ce.([]byte)[:] cpsc.channel.Pending = false } } } // SendGroupMessage performs the spamguard proof of work and sends a message. func (cpsc *CwtchPeerSendChannel) SendGroupMessage(gm *protocol.GroupMessage) error { if cpsc.channel.Pending == false { sgsolve := cpsc.spamGuard.SolveChallenge(cpsc.challenge, gm.GetCiphertext()) gm.Spamguard = sgsolve[:] csp := &protocol.CwtchServerPacket{ GroupMessage: gm, } packet, _ := proto.Marshal(csp) cpsc.channel.SendMessage(packet) cpsc.channel.CloseChannel() } else { return errors.New("channel isn't set up yet") } return nil } // Packet should never be func (cpsc *CwtchPeerSendChannel) Packet(data []byte) { // If we receive a packet on this channel, close the connection cpsc.channel.CloseChannel() }