func (tracker *Tracker) sendHTTPRequest(data *TrackerRequestData) error { var eventString string switch data.event { case TrackerEventCompleted: eventString = "event=completed&" case TrackerEventStarted: eventString = "event=started&" case TrackerEventStopped: eventString = "event=stopped&" } httpResponse, err := http.Get( fmt.Sprintf("%s?%speer_id=%s&info_hash=%s&left=%d&compact=1&downloaded=%d&uploaded=%d&port=6881", tracker.announceURL, eventString, url.QueryEscape(string(tracker.torrent.peerID)), url.QueryEscape(string(tracker.torrent.getInfoHash())), data.remaining, data.downloaded, data.uploaded, ), ) if err != nil { return err } defer httpResponse.Body.Close() if httpResponse.StatusCode != http.StatusOK { return fmt.Errorf("bad response from tracker (%d): %s", httpResponse.StatusCode, httpResponse.Status) } resp, err := bencode.Decode(httpResponse.Body) if err != nil { return err } if failureReason, exists := resp["failure reason"]; exists { return errors.New(failureReason.(string)) } tracker.interval = resp["interval"].(int64) tracker.torrent.peersChannel <- resp["peers"] return nil }
func (torrent *Torrent) Open(filename string) error { torrent.exitFlag = false torrent.setPeerId() file, err := os.Open(filename) if err != nil { return err } defer file.Close() data, err := bencode.Decode(file) if err != nil { return err } if announceLists, ok := data["announce-list"].([]interface{}); ok { for _, announceList := range announceLists { for _, announceURL := range announceList.([]interface{}) { torrent.parseTrackerURL(announceURL.(string)) } } } else { torrent.parseTrackerURL(data["announce"].(string)) } if comment, ok := data["comment"]; ok { torrent.Comment = comment.(string) } info := data["info"].(map[string]interface{}) torrent.Name = info["name"].(string) torrent.PieceLength = info["piece length"].(int64) infoHash := sha1.Sum(bencode.Encode(info)) // Set handshake var buffer bytes.Buffer buffer.WriteByte(19) // length of the string "BitTorrent Protocol" buffer.WriteString("BitTorrent protocol") buffer.WriteString("\x00\x00\x00\x00\x00\x00\x00\x00") // reserved buffer.Write(infoHash[:]) buffer.Write(torrent.peerID) torrent.handshake = buffer.Bytes() // Set pieces pieces := info["pieces"].(string) for i := 0; i < len(pieces); i += 20 { torrent.Pieces = append(torrent.Pieces, TorrentPiece{ hash: pieces[i : i+20], }) } if err := os.Mkdir("Downloads", 0700); err != nil && !os.IsExist(err) { return err } cwd, err := os.Getwd() if err != nil { return err } base := filepath.Join(cwd, "Downloads") // Set files if files, exists := info["files"]; exists { dirName := filepath.Join("Downloads", info["name"].(string)) if err := torrent.validatePath(base, dirName); err != nil { return err } base := filepath.Join(cwd, dirName) for _, v := range files.([]interface{}) { v := v.(map[string]interface{}) torrent.TotalSize += v["length"].(int64) } // Multiple files var begin int64 for k, v := range files.([]interface{}) { v := v.(map[string]interface{}) // Set up directory structure pathList := v["path"].([]interface{}) pathElements := []string{dirName} for i := 0; i < len(pathList)-1; i++ { pathElements = append(pathElements, pathList[i].(string)) } path := filepath.Join(pathElements...) fullPath := filepath.Join(path, pathList[len(pathList)-1].(string)) if err := torrent.validatePath(base, fullPath); err != nil { return err } if len(path) != 0 { if err := os.MkdirAll(path, 0700); err != nil { return err } } length := v["length"].(int64) file, err := os.OpenFile(fullPath, os.O_RDWR, 0600) if err == nil { torrent.findCompletedPieces(file, begin, length, k) file.Close() } torrent.files = append(torrent.files, File{fullPath, begin, length}) begin += length } } else { // Single file fileName := filepath.Join("Downloads", info["name"].(string)) if err := torrent.validatePath(base, fileName); err != nil { return err } length := info["length"].(int64) torrent.TotalSize = length file, err := os.OpenFile(fileName, os.O_RDWR, 0600) if err == nil { torrent.findCompletedPieces(file, 0, length, 0) file.Close() } torrent.files = []File{{fileName, 0, length}} } fileWriterChannel = make(chan *FileWriterMessage) go fileWriterListener() return nil }