<commit_before>package core

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"time"

	"github.com/tinzenite/channel"
	"github.com/tinzenite/model"
	"github.com/tinzenite/shared"
)

/*
SyncModel fetches and synchronizes a remote model.
*/
func (c *chaninterface) SyncModel(address string) {
	// create & modify must first fetch file
	rm := shared.CreateRequestMessage(shared.OtModel, shared.IDMODEL)
	// request file and apply update on success
	c.requestFile(address, rm, c.onModelFileReceived)
}

/*
requestFile requests the given request from the address and executes the function
when the transfer was successful or not. NOTE: only f may be nil.
*/
func (c *chaninterface) requestFile(address string, rm shared.RequestMessage, f onDone) error {
	// build key
	key := c.buildKey(address, rm.Identification)
	if tran, exists := c.inTransfers[key]; exists {
		if time.Since(tran.updated) > transferTimeout {
			c.log("Retransmiting transfer due to timeout.")
			// update
			tran.updated = time.Now()
			c.inTransfers[key] = tran
			// retransmit
			return c.tin.channel.Send(address, rm.JSON())
		}
		c.log("Ignoring multiple request for", rm.Identification)
		return nil
	}
	// create new transfer
	tran := transfer{
		updated: time.Now(),
		peers:   []string{address},
		done:    f}
	c.inTransfers[key] = tran
	/*TODO send request to only one underutilized peer at once*/
	// FOR NOW: just get it from whomever send the update
	return c.tin.channel.Send(address, rm.JSON())
}

func (c *chaninterface) onAuthenticationMessage(address string, msg shared.AuthenticationMessage) {
	// check if reply to sent challenge
	if number, exists := c.challenges[address]; exists {
		// if yes check if answer is valid
		data, err := c.tin.auth.Decrypt(msg.Encrypted, msg.Nonce)
		if err != nil {
			log.Println("Logic: failed to decrypt:", err)
			return
		}
		response, err := binary.ReadVarint(bytes.NewBuffer(data[:]))
		if err != nil {
			log.Println("Logic: failed to read challenge response:", err)
			return
		}
		// response should be one higher than stored number
		expected := number + 1
		if response != expected {
			log.Println("Logic: authentication failed for", address[:8], ": expected", expected, "got", response, "!")
			return
		}
		// if valid, set peer to authenticated
		log.Println("DEBUG:", address[:8], "authenticated itself correctly!")
		peer, exists := c.tin.peers[address]
		if !exists {
			log.Println("Logic: peer lookup failed, doesn't exist!")
			return
		}
		// set value
		peer.SetAuthenticated(true)
		return
	}
	// if not, simply reply
}

func (c *chaninterface) onRequestMessage(address string, msg shared.RequestMessage) {
	// this means we need to send our selfpeer (used for bootstrapping)
	if msg.ObjType == shared.OtPeer {
		// TODO check if this is really still in use?
		log.Println("DEBUG: YES, this is still in use. Why? Bootstrap should have fixed this...")
		// so build a bogus update message and send that
		peerPath := shared.TINZENITEDIR + "/" + shared.ORGDIR + "/" + shared.PEERSDIR + "/" + c.tin.selfpeer.Identification + shared.ENDING
		fullPath := shared.CreatePath(c.tin.model.RootPath, peerPath)
		obj, err := c.tin.model.GetInfo(fullPath)
		if err != nil {
			c.log("onRequestMessage:", err.Error())
			return
		}
		um := shared.CreateUpdateMessage(shared.OpCreate, *obj)
		c.tin.channel.Send(address, um.JSON())
		return
	}
	// get obj for path and directory
	obj, err := c.tin.model.GetInfoFrom(msg.Identification)<commit_msg>implemented both cases for receiving a challenge<commit_after>package core

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"time"

	"github.com/tinzenite/channel"
	"github.com/tinzenite/model"
	"github.com/tinzenite/shared"
)

/*
SyncModel fetches and synchronizes a remote model.
*/
func (c *chaninterface) SyncModel(address string) {
	// create & modify must first fetch file
	rm := shared.CreateRequestMessage(shared.OtModel, shared.IDMODEL)
	// request file and apply update on success
	c.requestFile(address, rm, c.onModelFileReceived)
}

/*
requestFile requests the given request from the address and executes the function
when the transfer was successful or not. NOTE: only f may be nil.
*/
func (c *chaninterface) requestFile(address string, rm shared.RequestMessage, f onDone) error {
	// build key
	key := c.buildKey(address, rm.Identification)
	if tran, exists := c.inTransfers[key]; exists {
		if time.Since(tran.updated) > transferTimeout {
			c.log("Retransmiting transfer due to timeout.")
			// update
			tran.updated = time.Now()
			c.inTransfers[key] = tran
			// retransmit
			return c.tin.channel.Send(address, rm.JSON())
		}
		c.log("Ignoring multiple request for", rm.Identification)
		return nil
	}
	// create new transfer
	tran := transfer{
		updated: time.Now(),
		peers:   []string{address},
		done:    f}
	c.inTransfers[key] = tran
	/*TODO send request to only one underutilized peer at once*/
	// FOR NOW: just get it from whomever send the update
	return c.tin.channel.Send(address, rm.JSON())
}

func (c *chaninterface) onAuthenticationMessage(address string, msg shared.AuthenticationMessage) {
	// check if reply to sent challenge
	if number, exists := c.challenges[address]; exists {
		// whatever happens we remove the note that we've sent a challenge: if not valid we'll need to send a new one anyway
		delete(c.challenges, address)
		// now get the reply
		data, err := c.tin.auth.Decrypt(msg.Encrypted, msg.Nonce)
		if err != nil {
			log.Println("Logic: failed to decrypt:", err)
			return
		}
		response, err := binary.ReadVarint(bytes.NewBuffer(data[:]))
		if err != nil {
			log.Println("Logic: failed to read challenge response:", err)
			return
		}
		// response should be one higher than stored number
		expected := number + 1
		if response != expected {
			log.Println("Logic: authentication failed for", address[:8], ": expected", expected, "got", response, "!")
			return
		}
		// if valid, set peer to authenticated
		log.Println("DEBUG:", address[:8], "authenticated itself correctly!")
		peer, exists := c.tin.peers[address]
		if !exists {
			log.Println("Logic: peer lookup failed, doesn't exist!")
			return
		}
		// set value
		peer.SetAuthenticated(true)
		// and done
		return
	}
	// if we didn't send a challenge, we just reply validly:
	// read number
	data, err := c.tin.auth.Decrypt(msg.Encrypted, msg.Nonce)
	if err != nil {
		log.Println("Logic: failed to decrypt:", err)
		return
	}
	number, err := binary.ReadVarint(bytes.NewBuffer(data[:]))
	if err != nil {
		log.Println("Logic: failed to read challenge response:", err)
		return
	}
	// apply operation
	number++
	// convert to data payload
	data = make([]byte, binary.MaxVarintLen64)
	_ = binary.PutVarint(data, number)
	// get a nonce
	nonce := c.tin.auth.createNonce()
	// encrypt number with nonce
	encrypted, err := c.tin.auth.Encrypt(data, nonce)
	reply := shared.CreateAuthenticationMessage(encrypted, nonce)
	// send reply
	_ = c.tin.channel.Send(address, reply.JSON())
	// set the other peer to trusted (since they could send a valid challenge)
	log.Println("DEBUG:", address[:8], "sent correct challenge!")
	peer, exists := c.tin.peers[address]
	if !exists {
		log.Println("Logic: peer lookup failed, doesn't exist!")
		return
	}
	// set value
	peer.SetAuthenticated(true)
	// and done!
}

func (c *chaninterface) onRequestMessage(address string, msg shared.RequestMessage) {
	// this means we need to send our selfpeer (used for bootstrapping)
	if msg.ObjType == shared.OtPeer {
		// TODO check if this is really still in use?
		log.Println("DEBUG: YES, this is still in use. Why? Bootstrap should have fixed this...")
		// so build a bogus update message and send that
		peerPath := shared.TINZENITEDIR + "/" + shared.ORGDIR + "/" + shared.PEERSDIR + "/" + c.tin.selfpeer.Identification + shared.ENDING
		fullPath := shared.CreatePath(c.tin.model.RootPath, peerPath)
		obj, err := c.tin.model.GetInfo(fullPath)
		if err != nil {
			c.log("onRequestMessage:", err.Error())
			return
		}
		um := shared.CreateUpdateMessage(shared.OpCreate, *obj)
		c.tin.channel.Send(address, um.JSON())
		return
	}
	// get obj for path and directory
	obj, err := c.tin.model.GetInfoFrom(msg.Identification)
{'fraction_non_alphanumeric': 0.12828234160443266, 'fraction_numerical': 0.0010840761262346423, 'mean_word_length': 3.824520627542127, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7158606', 'n_tokens_mistral': 2912, 'n_tokens_neox': 2690, 'n_words': 816}