// Adds a peer to the swarm.info file // @param string addPath - the path of the file to be added // @param string swarmPath - the path of the swarminfo file // @return error - An error can be produced when issues arise from trying to access // the swarm file or if the file to be added already exists in the swarm file - otherwise // error will be nil. func addToSwarminfo(addPeer lynxutil.Peer, swarmPath string) error { swarmFile, err := os.OpenFile(swarmPath, os.O_APPEND|os.O_WRONLY, 0644) // Opens for appending if err != nil { return err } lynkName := getTLynkName(swarmPath) lynk := lynxutil.GetLynk(tLynks, lynkName) if lynk == nil { tLynks = append(tLynks, lynxutil.Lynk{Name: lynkName}) lynk = lynxutil.GetLynk(tLynks, lynkName) } parseSwarminfo(swarmPath) i := 0 for i < len(lynk.Peers) { if lynk.Peers[i].IP == addPeer.IP && lynk.Peers[i].Port == addPeer.Port { return errors.New("Can't Add Duplicates To Swarminfo") } i++ } // Write to swarminfo file using ::: to IP and Port swarmFile.WriteString(addPeer.IP + ":::" + addPeer.Port + "\n") return swarmFile.Close() }
// Gets a file from the peer(s) // @param string fileName - The name of the file to find in the peers // @param string metaPath - The meta.info path associated with the lynk we're interested in // @return error - An error can be produced if there are connection issues, // problems creating or writing to the file, or from not being able to get there // desired file - otherwise error will be nil. func getFile(fileName, metaPath string) error { // Will parseMetainfo file and then ask tracker for list of peers parseMetainfo(metaPath) lynkName := GetLynkName(metaPath) lynk := lynxutil.GetLynk(lynks, lynkName) //fmt.Println("Asking For File From: " + metaPath) askTrackerForPeers(lynkName) //fmt.Println(lynk.Peers) i := 0 gotFile := false for i < len(lynk.Peers) && !gotFile { conn, err := net.Dial("tcp", lynk.Peers[i].IP+":"+lynk.Peers[i].Port) // We don't want to return on err because we might be able to connect to next peer. if err == nil { gotFile = askForFile(lynkName, fileName, conn) } //fmt.Println(i) i++ } if gotFile { return nil } return errors.New("Did not receive file") // If we got here - we didn't have the file. }
// Parses the information in swarm.info file and places each entry into a Peer // struct and appends that struct to the array of peers // @param string swarmPath - The path to the swarminfo file // @return error - An error can be produced when issues arise from trying to access // the swarm file or from an invalid swarm file type - otherwise error will be nil. func parseSwarminfo(swarmPath string) error { lynkName := getTLynkName(swarmPath) lynk := lynxutil.GetLynk(tLynks, lynkName) //fmt.Println(lynk) lynk.Peers = nil // Resets peers array swarmFile, err := os.Open(swarmPath) if err != nil { return err } else if !strings.Contains(swarmPath, "swarm.info") { return errors.New("Invalid File Type") } scanner := bufio.NewScanner(swarmFile) tempPeer := lynxutil.Peer{} // Scan each line for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) // Trim helps with errors in \n split := strings.Split(line, ":::") tempPeer.IP = split[0] tempPeer.Port = split[1] lynk.Peers = append(lynk.Peers, tempPeer) } //fmt.Println(lynk.Peers) return swarmFile.Close() }
// Deletes the current swarm.info and replaces it with a new version that // accurately reflects the array of Peers after they have been modified // @return error - An error can be produced when issues arise from trying to create // or remove the swarm file - otherwise error will be nil. func updateSwarminfo(swarmPath string) error { parseSwarminfo(swarmPath) err := os.Remove(swarmPath) if err != nil { fmt.Println(err) return err } newSwarmInfo, err := os.Create(swarmPath) if err != nil { fmt.Println(err) return err } lynkName := getTLynkName(swarmPath) lynk := lynxutil.GetLynk(tLynks, lynkName) i := 0 for i < len(lynk.Peers) { newSwarmInfo.WriteString(lynk.Peers[i].IP + ":::" + lynk.Peers[i].Port + "\n") i++ } return newSwarmInfo.Close() }
// UpdateMetainfo - Deletes the current meta.info and replaces it with a new version that // accurately reflects the array of Files after they have been modified. // @return error - An error can be produced when issues arise from trying to create // or remove the meta file - otherwise error will be nil. func UpdateMetainfo(metaPath string) error { parseMetainfo(metaPath) lynkName := GetLynkName(metaPath) lynk := lynxutil.GetLynk(lynks, lynkName) err := os.Remove(metaPath) if err != nil { fmt.Println(err) return err } newMetainfo, err := os.Create(metaPath) if err != nil { fmt.Println(err) return err } newMetainfo.WriteString("announce:::" + lynk.Tracker + "\n") // Write tracker IP newMetainfo.WriteString("lynkName:::" + lynk.Name + "\n") newMetainfo.WriteString("owner:::" + lynk.Owner + "\n") i := 0 for i < len(lynk.Files) { newMetainfo.WriteString("length:::" + strconv.Itoa(lynk.Files[i].Length) + "\n") // str conv newMetainfo.WriteString("path:::" + lynk.Files[i].Path + "\n") newMetainfo.WriteString("name:::" + lynk.Files[i].Name + "\n") newMetainfo.WriteString("chunkLength:::" + strconv.Itoa(lynk.Files[i].ChunkLength) + "\n") newMetainfo.WriteString("chunks:::" + lynk.Files[i].Chunks + "\n") newMetainfo.WriteString(endOfEntry + "\n") i++ } return newMetainfo.Close() }
// HaveFile - Checks to see if we have the passed in file. // @param string filePath - The name of the file to check for - This includes the lynk name. // E.G. - 'Cool_Lynk/coolFile.txt' // @return bool - A boolean indicating whether or not we have a file in our // files array. func HaveFile(filePath string) bool { have := false lynkInfo := strings.Split(filePath, "/") if len(lynkInfo) != 2 { fmt.Println(filePath + " is an invalid filepath") return have } lynkName := lynkInfo[0] fileName := lynkInfo[1] metaPath := lynxutil.HomePath + lynkName + "/meta.info" parseMetainfo(metaPath) lynk := lynxutil.GetLynk(lynks, lynkName) i := 0 for i < len(lynk.Files) && !have { if lynk.Files[i].Name == fileName { have = true } i++ } return have }
// Helper function that verifies there has been a change in the files for the system tests lynk. // @returns True if there has been a change, False if there has not been a change func checkChanges() bool { changed := len(lynxutil.GetLynk(client.GetLynks(), "SysTests").Files) result := false if changed != original { original = changed result = true } return result }
// Unit tests for addToMetainfo function // @param *testing.T t - The wrapper for the test func TestAddToMetainfo(t *testing.T) { fmt.Println("\n----------------TestAddToMetainfo----------------") parseMetainfo(mPath) hasTest := false tLynk := lynxutil.GetLynk(lynks, "Tests") i := 0 for i < len(tLynk.Files) { //fmt.Print(files[i].name) if tLynk.Files[i].Name == "test.txt" { //fmt.Print(files[i].name) hasTest = true } i++ } // add test.txt to the metainfo result := AddToMetainfo("test.txt", mPath) if result != nil && !hasTest { t.Error("Test failed, expected no errors. Got ", result) } else { fmt.Println("Successfully Added To Metainfo") successful++ } parseMetainfo(mPath) // check that test.txt is in the File struct list i = 0 for i < len(tLynk.Files) { if tLynk.Files[i].Name == "test.txt" { hasTest = true } i++ } if !hasTest { t.Error("test.txt was not added to metainfo.") } else { fmt.Println("Successfully Added test.txt To Metainfo") successful++ } result = AddToMetainfo("test.txt", mPath) if result == nil { t.Error("Test failed, expected failure due to duplicates. Got ", result) } else { fmt.Println("Successfully Avoided Adding Duplicates") successful++ } }
// UpdateLynk - Function which will update the files of a Lynk with the current versions. // @param lynkName string - the name of the Lynk we want to update func UpdateLynk(lynkName string) error { // We actually get the files we need over the network. lynk := lynxutil.GetLynk(lynks, lynkName) var err error // Creates nil error for _, file := range lynk.Files { err = getFile(file.Name, lynxutil.HomePath+lynkName+"/meta.info") // If we fail to get the file the first time, we attempt again. if err.Error() == "Did not receive file" { for i := 0; i < lynxutil.ReconnAttempts; i++ { err = getFile(file.Name, lynxutil.HomePath+lynkName+"/meta.info") } } } return err }
// Unit tests for GetTrackerIP function // @param *testing.T t - The wrapper for the test func TestAskTrackerForPeers(t *testing.T) { fmt.Println("\n----------------TestAskTracker----------------") lynkName := GetLynkName(mPath) lynk := lynxutil.GetLynk(lynks, lynkName) askTrackerForPeers(lynkName) if len(lynk.Peers) <= 0 { t.Error("Did Not Get Correct List Of Peers") } else { fmt.Println("Successfully Got Peers") successful++ } fmt.Println("\nSuccess on ", successful, "/", total, " tests.") }
// BroadcastNewIP - This function broadcasts a tracker's new IP address to all of its peers // @param string swarmPath - The swarm.info path associated with the lynk we're interested in func BroadcastNewIP(swarmPath string) { // Can update Meta here if needed lynkName := getTLynkName(swarmPath) lynk := lynxutil.GetLynk(tLynks, lynkName) i := 0 for i < len(lynk.Peers) { fmt.Println(i) conn, err := net.Dial("tcp", lynk.Peers[i].IP+":"+lynk.Peers[i].Port) if err == nil { sendFile(lynxutil.HomePath+lynk.Name+"/meta.info", conn) } fmt.Println(lynk.Peers[i].IP) i++ } }
// Asks the tracker for a list of peers and then places them into a lynk's peers array // @param string lynkName - The name of the lynk we're interested in func askTrackerForPeers(lynkName string) error { lynk := lynxutil.GetLynk(lynks, lynkName) // Connects to tracker conn, err := net.Dial("tcp", lynk.Tracker) if err != nil { i := 0 for i < len(lynk.Peers) && err != nil { pConn, _ := net.Dial("tcp", lynk.Peers[i].IP+":"+lynk.Peers[i].Port) fmt.Fprintf(pConn, "Tracker_Request:"+lynkName+"/\n") reply := "" reply, err = bufio.NewReader(pConn).ReadString('\n') // Waits for a String ending in newline reply = strings.TrimSpace(reply) conn, err = net.Dial("tcp", reply) i++ } // We could not connect to the tracker if err != nil { return err } } // Gives IP and ServerPort So It Can Be Added To swarm.info fmt.Fprintf(conn, "Swarm_Request:"+lynxutil.GetIP()+":"+lynxutil.ServerPort+":"+lynkName+"\n") reader := bufio.NewReader(conn) tp := textproto.NewReader(reader) reply, err := tp.ReadLine() //fmt.Println(reply) // Tracker will close connection when finished - which will break us out of this loop for err == nil { peerArray := strings.Split(reply, ":::") tmpPeer := lynxutil.Peer{IP: peerArray[0], Port: peerArray[1]} if !contains(lynk.Peers, tmpPeer) { lynk.Peers = append(lynk.Peers, tmpPeer) } reply, err = tp.ReadLine() } return nil // Did not have an error if we reached this point }
// Unit tests for deleteEntry function // @param *testing.T t - The wrapper for the test func TestDeleteFile(t *testing.T) { fmt.Println("\n----------------TestDeleteFiley----------------") failed := false parseMetainfo(mPath) lynkName := GetLynkName(mPath) DeleteFile("test.txt", lynkName) tLynk := lynxutil.GetLynk(lynks, lynkName) i := 0 for i < len(tLynk.Files) { if tLynk.Files[i].Name == "test.txt" { t.Error("Error, test.txt is still in files") failed = true } i++ } if !failed { fmt.Println("Successfully Removed The test.txt File") successful++ } else { failed = false } DeleteFile("test11.txt", lynkName) i = 0 for i < len(tLynk.Files) { if tLynk.Files[i].Name == "test11.txt" { t.Error("Error, test11.txt is still in files") failed = true } i++ } if !failed { fmt.Println("Successfully Removed The test11.txt File") successful++ } }
// DeleteFile - Function that deletes an entry from a lynk's files array. // @param string nameToDelete - This is the name of the file we want to delete // @param string lynkName - The lynk we want to delete it from func DeleteFile(nameToDelete, lynkName string) error { // Need to delete the local file too - so parseMeta properly picks it up lynk := lynxutil.GetLynk(lynks, lynkName) var err error if lynk == nil { err = errors.New("Could not delete file") } i := 0 for i < len(lynk.Files) { if nameToDelete == lynk.Files[i].Name { lynk.Files = append(lynk.Files[:i], lynk.Files[i+1:]...) } i++ } fmt.Println(lynk.Files) return err }
// Parses the information in meta.info file and places each entry into a File // struct and appends that struct to the array of structs // @param string metaPath - The path to the metainfo file // @return error - An error can be produced when issues arise from trying to access // the meta file or from an invalid meta file type - otherwise error will be nil. func parseMetainfo(metaPath string) error { lynk := lynxutil.GetLynk(lynks, GetLynkName(metaPath)) if lynk == nil { return errors.New("Lynk Not Found") } lynk.Files = nil // Resets files array metaFile, err := os.Open(metaPath) if err != nil { return err } else if !strings.Contains(metaPath, "meta.info") { return errors.New("Invalid File Type") } scanner := bufio.NewScanner(metaFile) tempFile := lynxutil.File{} for scanner.Scan() { // Scan each line split := strings.Split(strings.TrimSpace(scanner.Text()), ":::") if split[0] == "announce" { lynk.Tracker = split[metaValueIndex] } else if split[0] == "owner" { lynk.Owner = split[metaValueIndex] } else if split[0] == "lynkName" { lynk.Name = split[metaValueIndex] } else if split[0] == "chunkLength" { tempFile.ChunkLength, _ = strconv.Atoi(split[metaValueIndex]) } else if split[0] == "length" { tempFile.Length, _ = strconv.Atoi(split[metaValueIndex]) } else if split[0] == "path" { tempFile.Path = split[metaValueIndex] } else if split[0] == "name" { tempFile.Name = split[metaValueIndex] } else if split[0] == "chunks" { tempFile.Chunks = split[metaValueIndex] } else if split[0] == endOfEntry { lynk.Files = append(lynk.Files, tempFile) // Append the current file to the file array tempFile = lynxutil.File{} // Empty the current file } } return metaFile.Close() }
// AddToMetainfo - Adds a file to the meta.info by parsing that file's information // @param string addPath - the path of the file to be added // @param string metaPath - the path of the metainfo file - must be full path from root. // @return error - An error can be produced when issues arise from trying to access // the meta file or if the file to be added already exists in the meta file - otherwise // error will be nil. func AddToMetainfo(addPath, metaPath string) error { metaFile, err := os.OpenFile(metaPath, os.O_APPEND|os.O_WRONLY, 0644) // Opens for appending if err != nil { fmt.Println(err) return err } addStat, err := os.Stat(addPath) if err != nil { fmt.Println(err) return err } parseMetainfo(metaPath) lynkName := GetLynkName(metaPath) lynk := lynxutil.GetLynk(lynks, lynkName) i := 0 for i < len(lynk.Files) { if lynk.Files[i].Name == addStat.Name() { return errors.New("Can't Add Duplicates To Metainfo") } i++ } lengthStr := strconv.FormatInt(addStat.Size(), 10) // Convert int64 to string metaFile.WriteString("length:::" + lengthStr + "\n") tempPath, err := filepath.Abs(addPath) // Find the path of the current file if err != nil { return err } // Write to metainfo file using ::: to separate keys and values metaFile.WriteString("path:::" + tempPath + "\n") metaFile.WriteString("name:::" + addStat.Name() + "\n") metaFile.WriteString("chunkLength:::32\n") metaFile.WriteString("chunks:::256\n") metaFile.WriteString(endOfEntry + "\n") return metaFile.Close() }
// Function that deletes an entry from a lynk's peers array and the swarm.info file. // @param string peerToDelete - This is the peer struct we want to delete - uses the IP address // @param string lynkName - The lynk we want to delete it from func deletePeer(peerToDelete, lynkName string) { lynk := lynxutil.GetLynk(tLynks, lynkName) i := 0 for i < len(lynk.Peers) { if peerToDelete == lynk.Peers[i].IP { lynk.Peers = append(lynk.Peers[:i], lynk.Peers[i+1:]...) } i++ } swarmPath := lynxutil.HomePath + lynkName + "/" + lynkName + "_Tracker/" + "swarm.info" os.Remove(swarmPath) newSwarmInfo, _ := os.Create(swarmPath) i = 0 for i < len(lynk.Peers) { newSwarmInfo.WriteString(lynk.Peers[i].IP + ":::" + lynk.Peers[i].Port + "\n") i++ } }
// StopDownload - Sets a boolean to stop the lynk from downloading func StopDownload(lynkName string) { lynk := lynxutil.GetLynk(lynks, lynkName) lynk.DLing = false }
// IsDownloading - Returns whether or not the client associated the specified lynk is downloading // @param lynkName // @returns - Returns whether or not the client associated the specified lynk is downloading func IsDownloading(lynkName string) bool { lynk := lynxutil.GetLynk(lynks, lynkName) return lynk.DLing }
// GetTracker - Simply returns the tracker associated with the passed in Lynk // @param string metaPath - The meta.info path associated with the lynk we're interested in // @return string - A string representing the tracker's IP address. func GetTracker(metaPath string) string { parseMetainfo(metaPath) lynkName := GetLynkName(metaPath) lynk := lynxutil.GetLynk(lynks, lynkName) return lynk.Tracker }