2021-09-30 00:57:13 +00:00
package filesharing
import (
"crypto/rand"
2023-06-26 22:21:11 +00:00
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"path/filepath"
2021-09-30 00:57:13 +00:00
app2 "cwtch.im/cwtch/app"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/functionality/filesharing"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/model/attr"
2021-10-15 19:38:22 +00:00
"cwtch.im/cwtch/model/constants"
2021-09-30 00:57:13 +00:00
"cwtch.im/cwtch/peer"
"cwtch.im/cwtch/protocol/connections"
"cwtch.im/cwtch/protocol/files"
2022-09-06 19:41:52 +00:00
utils2 "cwtch.im/cwtch/utils"
2021-09-30 00:57:13 +00:00
"git.openprivacy.ca/openprivacy/connectivity/tor"
"git.openprivacy.ca/openprivacy/log"
2021-12-19 00:15:05 +00:00
2021-11-17 22:34:13 +00:00
// Import SQL Cipher
2021-09-30 00:57:13 +00:00
mrand "math/rand"
"os"
"os/user"
"path"
"runtime"
"runtime/pprof"
"testing"
"time"
2021-12-19 00:15:05 +00:00
_ "github.com/mutecomm/go-sqlcipher/v4"
2021-09-30 00:57:13 +00:00
)
func waitForPeerPeerConnection ( t * testing . T , peera peer . CwtchPeer , peerb peer . CwtchPeer ) {
for {
2021-11-17 22:34:13 +00:00
state := peera . GetPeerState ( peerb . GetOnion ( ) )
if state == connections . FAILED {
t . Fatalf ( "%v could not connect to %v" , peera . GetOnion ( ) , peerb . GetOnion ( ) )
}
if state != connections . AUTHENTICATED {
fmt . Printf ( "peer %v waiting connect to peer %v, currently: %v\n" , peera . GetOnion ( ) , peerb . GetOnion ( ) , connections . ConnectionStateName [ state ] )
time . Sleep ( time . Second * 5 )
continue
} else {
2021-12-08 01:02:02 +00:00
peerAName , _ := peera . GetScopedZonedAttribute ( attr . PublicScope , attr . ProfileZone , constants . Name )
peerBName , _ := peerb . GetScopedZonedAttribute ( attr . PublicScope , attr . ProfileZone , constants . Name )
2021-11-17 22:34:13 +00:00
fmt . Printf ( "%v CONNECTED and AUTHED to %v\n" , peerAName , peerBName )
break
2021-09-30 00:57:13 +00:00
}
}
}
func TestFileSharing ( t * testing . T ) {
2022-04-20 20:10:07 +00:00
numGoRoutinesStart := runtime . NumGoroutine ( )
2021-09-30 00:57:13 +00:00
os . RemoveAll ( "cwtch.out.png" )
os . RemoveAll ( "cwtch.out.png.manifest" )
2024-02-26 21:40:47 +00:00
log . SetLevel ( log . LevelDebug )
2023-06-26 22:21:11 +00:00
log . ExcludeFromPattern ( "tapir" )
2021-09-30 00:57:13 +00:00
os . Mkdir ( "tordir" , 0700 )
dataDir := path . Join ( "tordir" , "tor" )
os . MkdirAll ( dataDir , 0700 )
// we don't need real randomness for the port, just to avoid a possible conflict...
socksPort := mrand . Intn ( 1000 ) + 9051
controlPort := mrand . Intn ( 1000 ) + 9052
// generate a random password
key := make ( [ ] byte , 64 )
_ , err := rand . Read ( key )
if err != nil {
panic ( err )
}
2023-01-05 21:52:43 +00:00
useCache := os . Getenv ( "TORCACHE" ) == "true"
2022-01-20 21:27:35 +00:00
torDataDir := ""
2023-01-05 21:52:43 +00:00
if useCache {
log . Infof ( "using tor cache" )
torDataDir = filepath . Join ( dataDir , "data-dir-torcache" )
os . MkdirAll ( torDataDir , 0700 )
} else {
log . Infof ( "using clean tor data dir" )
if torDataDir , err = os . MkdirTemp ( dataDir , "data-dir-" ) ; err != nil {
t . Fatalf ( "could not create data dir" )
}
2022-01-20 21:27:35 +00:00
}
2021-09-30 00:57:13 +00:00
tor . NewTorrc ( ) . WithSocksPort ( socksPort ) . WithOnionTrafficOnly ( ) . WithHashedPassword ( base64 . StdEncoding . EncodeToString ( key ) ) . WithControlPort ( controlPort ) . Build ( "tordir/tor/torrc" )
2023-01-05 21:52:43 +00:00
acn , err := tor . NewTorACNWithAuth ( "./tordir" , path . Join ( ".." , "tor" ) , torDataDir , controlPort , tor . HashedPasswordAuthenticator { Password : base64 . StdEncoding . EncodeToString ( key ) } )
2021-09-30 00:57:13 +00:00
if err != nil {
t . Fatalf ( "Could not start Tor: %v" , err )
}
2021-11-19 23:55:01 +00:00
acn . WaitTillBootstrapped ( )
defer acn . Close ( )
2021-09-30 00:57:13 +00:00
2023-02-28 18:13:11 +00:00
app := app2 . NewApp ( acn , "./storage" , app2 . LoadAppSettings ( "./storage" ) )
2021-09-30 00:57:13 +00:00
2023-11-18 19:49:52 +00:00
usr , err := user . Current ( )
if err != nil {
t . Fatalf ( "current user is undefined" )
}
2021-09-30 00:57:13 +00:00
cwtchDir := path . Join ( usr . HomeDir , ".cwtch" )
os . Mkdir ( cwtchDir , 0700 )
os . RemoveAll ( path . Join ( cwtchDir , "testing" ) )
os . Mkdir ( path . Join ( cwtchDir , "testing" ) , 0700 )
2022-07-05 22:31:44 +00:00
t . Logf ( "Creating Alice..." )
2023-02-21 23:55:14 +00:00
app . CreateProfile ( "alice" , "asdfasdf" , true )
2021-09-30 00:57:13 +00:00
2022-07-05 22:31:44 +00:00
t . Logf ( "Creating Bob..." )
2023-02-21 23:55:14 +00:00
app . CreateProfile ( "bob" , "asdfasdf" , true )
2021-09-30 00:57:13 +00:00
2021-11-11 00:41:43 +00:00
t . Logf ( "** Waiting for Alice, Bob..." )
2022-07-05 22:31:44 +00:00
alice := app2 . WaitGetPeer ( app , "alice" )
2023-02-21 23:55:14 +00:00
app . ActivatePeerEngine ( alice . GetOnion ( ) )
2023-09-18 14:46:33 +00:00
app . ConfigureConnections ( alice . GetOnion ( ) , true , true , true )
2022-07-05 22:31:44 +00:00
bob := app2 . WaitGetPeer ( app , "bob" )
2023-02-21 23:55:14 +00:00
app . ActivatePeerEngine ( bob . GetOnion ( ) )
2023-09-18 14:46:33 +00:00
app . ConfigureConnections ( bob . GetOnion ( ) , true , true , true )
2021-09-30 00:57:13 +00:00
alice . AutoHandleEvents ( [ ] event . Type { event . PeerStateChange , event . NewRetValMessageFromPeer } )
2023-01-25 20:32:26 +00:00
bob . AutoHandleEvents ( [ ] event . Type { event . PeerStateChange , event . NewRetValMessageFromPeer } )
2021-09-30 00:57:13 +00:00
2023-06-26 22:21:11 +00:00
aliceQueueOracle := event . NewQueue ( )
2023-11-18 19:49:52 +00:00
aliceEb := app . GetEventBus ( alice . GetOnion ( ) )
if aliceEb == nil {
t . Fatalf ( "alice's eventbus is undefined" )
}
aliceEb . Subscribe ( event . SearchResult , aliceQueueOracle )
2021-09-30 00:57:13 +00:00
queueOracle := event . NewQueue ( )
2023-11-18 19:49:52 +00:00
bobEb := app . GetEventBus ( bob . GetOnion ( ) )
if bobEb == nil {
t . Fatalf ( "bob's eventbus is undefined" )
}
bobEb . Subscribe ( event . FileDownloaded , queueOracle )
2021-09-30 00:57:13 +00:00
2023-01-25 20:32:26 +00:00
// Turn on File Sharing Experiment...
settings := app . ReadSettings ( )
settings . ExperimentsEnabled = true
settings . Experiments [ constants . FileSharingExperiment ] = true
app . UpdateSettings ( settings )
2021-09-30 00:57:13 +00:00
2023-01-25 20:32:26 +00:00
t . Logf ( "** Launching Peers..." )
2021-09-30 00:57:13 +00:00
waitTime := time . Duration ( 30 ) * time . Second
t . Logf ( "** Waiting for Alice, Bob to connect with onion network... (%v)\n" , waitTime )
time . Sleep ( waitTime )
2021-11-11 00:41:43 +00:00
bob . NewContactConversation ( alice . GetOnion ( ) , model . DefaultP2PAccessControl ( ) , true )
alice . NewContactConversation ( bob . GetOnion ( ) , model . DefaultP2PAccessControl ( ) , true )
2021-09-30 00:57:13 +00:00
2023-03-06 21:06:15 +00:00
filesharingFunctionality := filesharing . FunctionalityGate ( )
2021-09-30 00:57:13 +00:00
2022-07-06 18:06:06 +00:00
_ , fileSharingMessage , err := filesharingFunctionality . ShareFile ( "cwtch.png" , alice )
2021-09-30 00:57:13 +00:00
if err != nil {
t . Fatalf ( "Error!: %v" , err )
}
2023-06-26 22:21:11 +00:00
alice . SendMessage ( 1 , fileSharingMessage )
2024-02-26 21:16:31 +00:00
// Ok this is fun...we just Sent a Message we may not have a connection yet...
// so this test will only pass if sending offline works...
waitForPeerPeerConnection ( t , bob , alice )
2021-09-30 00:57:13 +00:00
2023-06-26 22:21:11 +00:00
bob . SendMessage ( 1 , "this is a test message" )
bob . SendMessage ( 1 , "this is another test message" )
// Wait for the messages to arrive...
time . Sleep ( time . Second * 20 )
2023-07-25 17:28:59 +00:00
alice . SearchConversations ( "test" )
2023-06-26 22:21:11 +00:00
results := 0
for {
ev := aliceQueueOracle . Next ( )
if ev . EventType != event . SearchResult {
t . Fatalf ( "Expected a search result vent" )
}
results += 1
t . Logf ( "found search result (%d)....%v" , results , ev )
if results == 2 {
break
}
}
2022-07-05 22:31:44 +00:00
// test that bob can download and verify the file
testBobDownloadFile ( t , bob , filesharingFunctionality , queueOracle )
// Test stopping and restarting file shares
t . Logf ( "Stopping File Share" )
2023-01-05 21:52:43 +00:00
filesharingFunctionality . StopAllFileShares ( alice )
2022-07-05 22:31:44 +00:00
// Allow time for the stop request to filter through Engine
time . Sleep ( time . Second * 5 )
// Restart
t . Logf ( "Restarting File Share" )
2023-01-25 20:32:26 +00:00
err = filesharingFunctionality . ReShareFiles ( alice )
if err != nil {
t . Fatalf ( "Error!: %v" , err )
}
2022-07-05 22:31:44 +00:00
// run the same download test again...to check that we can actually download the file
testBobDownloadFile ( t , bob , filesharingFunctionality , queueOracle )
2022-11-30 22:13:54 +00:00
// test that we can delete bob...
2023-02-21 23:55:14 +00:00
app . DeleteProfile ( bob . GetOnion ( ) , "asdfasdf" )
2022-11-30 22:13:54 +00:00
2023-06-26 22:21:11 +00:00
aliceQueueOracle . Shutdown ( )
2022-07-05 22:31:44 +00:00
queueOracle . Shutdown ( )
app . Shutdown ( )
acn . Close ( )
time . Sleep ( 5 * time . Second )
numGoRoutinesPostACN := runtime . NumGoroutine ( )
// Printing out the current goroutines
// Very useful if we are leaking any.
pprof . Lookup ( "goroutine" ) . WriteTo ( os . Stdout , 1 )
if numGoRoutinesStart != numGoRoutinesPostACN {
t . Errorf ( "Number of GoRoutines at start (%v) does not match number of goRoutines after cleanup of peers and servers (%v), clean up failed, leak detected!" , numGoRoutinesStart , numGoRoutinesPostACN )
}
}
func testBobDownloadFile ( t * testing . T , bob peer . CwtchPeer , filesharingFunctionality * filesharing . Functionality , queueOracle event . Queue ) {
os . RemoveAll ( "cwtch.out.png" )
os . RemoveAll ( "cwtch.out.png.manifest" )
2021-11-11 00:41:43 +00:00
message , _ , err := bob . GetChannelMessage ( 1 , 0 , 1 )
if err != nil {
t . Fatalf ( "could not find file sharing message: %v" , err )
}
2021-09-30 00:57:13 +00:00
2021-11-10 22:28:52 +00:00
var messageWrapper model . MessageWrapper
json . Unmarshal ( [ ] byte ( message ) , & messageWrapper )
2021-09-30 00:57:13 +00:00
2021-11-10 22:28:52 +00:00
if messageWrapper . Overlay == model . OverlayFileSharing {
var fileMessageOverlay filesharing . OverlayMessage
err := json . Unmarshal ( [ ] byte ( messageWrapper . Data ) , & fileMessageOverlay )
2021-09-30 00:57:13 +00:00
2021-11-10 22:28:52 +00:00
if err == nil {
2023-01-05 21:52:43 +00:00
t . Logf ( "bob attempting to download file with invalid download" )
2022-08-19 16:27:19 +00:00
// try downloading with invalid download dir
err = filesharingFunctionality . DownloadFile ( bob , 1 , "/do/not/download/this/file/cwtch.out.png" , "./cwtch.out.png.manifest" , fmt . Sprintf ( "%s.%s" , fileMessageOverlay . Hash , fileMessageOverlay . Nonce ) , constants . ImagePreviewMaxSizeInBytes )
if err == nil {
t . Fatalf ( "should not download file with invalid download dir" )
}
2023-01-05 21:52:43 +00:00
t . Logf ( "bob attempting to download file with invalid manifest" )
2022-08-19 16:27:19 +00:00
// try downloading with invalid manifest dir
err = filesharingFunctionality . DownloadFile ( bob , 1 , "./cwtch.out.png" , "/do/not/download/this/file/cwtch.out.png.manifest" , fmt . Sprintf ( "%s.%s" , fileMessageOverlay . Hash , fileMessageOverlay . Nonce ) , constants . ImagePreviewMaxSizeInBytes )
if err == nil {
t . Fatalf ( "should not download file with invalid manifest dir" )
}
2023-01-05 21:52:43 +00:00
t . Logf ( "bob attempting to download file" )
2022-08-19 16:27:19 +00:00
err = filesharingFunctionality . DownloadFile ( bob , 1 , "./cwtch.out.png" , "./cwtch.out.png.manifest" , fmt . Sprintf ( "%s.%s" , fileMessageOverlay . Hash , fileMessageOverlay . Nonce ) , constants . ImagePreviewMaxSizeInBytes )
if err != nil {
t . Fatalf ( "could not download file: %v" , err )
}
2021-09-30 00:57:13 +00:00
}
}
// Wait for the file downloaded event
2022-12-05 07:28:24 +00:00
ClientTimeout := utils2 . TimeoutPolicy ( time . Second * 120 )
2022-07-05 22:31:44 +00:00
err = ClientTimeout . ExecuteAction ( func ( ) error {
ev := queueOracle . Next ( )
if ev . EventType != event . FileDownloaded {
t . Fatalf ( "Expected file download event" )
}
2021-09-30 00:57:13 +00:00
2022-07-05 22:31:44 +00:00
manifest , _ := files . CreateManifest ( "cwtch.out.png" )
if hex . EncodeToString ( manifest . RootHash ) != "8f0ed73bbb30db45b6a740b1251cae02945f48e4f991464d5f3607685c45dcd136a325dab2e5f6429ce2b715e602b20b5b16bf7438fb6235fefe912adcedb5fd" {
t . Fatalf ( "file hash does not match expected %x: " , manifest . RootHash )
}
return nil
} )
2021-09-30 00:57:13 +00:00
2022-07-05 22:31:44 +00:00
if err != nil {
t . Fatalf ( "timeout when attempting to download a file" )
2021-09-30 00:57:13 +00:00
}
}