2020-05-12 02:13:05 +00:00
|
|
|
package irc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math"
|
|
|
|
"net"
|
|
|
|
"path"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DCCSend struct {
|
|
|
|
File string `json:"file"`
|
|
|
|
IP string `json:"ip"`
|
2020-05-18 01:21:34 +00:00
|
|
|
Port string `json:"port"`
|
2020-05-12 02:13:05 +00:00
|
|
|
Length uint64 `json:"length"`
|
2020-06-16 01:04:27 +00:00
|
|
|
|
|
|
|
dialer Dialer
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 01:04:27 +00:00
|
|
|
func (c *Client) ParseDCCSend(ctcp *CTCP) *DCCSend {
|
2020-05-12 02:13:05 +00:00
|
|
|
params := strings.Split(ctcp.Params, " ")
|
|
|
|
|
|
|
|
if len(params) > 4 {
|
|
|
|
ip, err := strconv.Atoi(params[2])
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:21:34 +00:00
|
|
|
length, err := strconv.ParseUint(params[4], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
2020-05-12 02:13:05 +00:00
|
|
|
|
|
|
|
filename := path.Base(params[1])
|
|
|
|
if filename == "/" || filename == "." {
|
|
|
|
filename = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return &DCCSend{
|
|
|
|
File: filename,
|
2020-05-18 01:21:34 +00:00
|
|
|
IP: intToIP(ip),
|
|
|
|
Port: params[3],
|
|
|
|
Length: length,
|
2020-06-16 01:04:27 +00:00
|
|
|
dialer: c.dialer,
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-16 01:04:27 +00:00
|
|
|
func (pack *DCCSend) Download(w io.Writer, progress chan DownloadProgress) error {
|
2020-05-20 05:21:12 +00:00
|
|
|
if progress != nil {
|
|
|
|
progress <- DownloadProgress{
|
|
|
|
File: pack.File,
|
|
|
|
}
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 01:04:27 +00:00
|
|
|
conn, err := pack.dialer.Dial("tcp", net.JoinHostPort(pack.IP, pack.Port))
|
2020-05-12 02:13:05 +00:00
|
|
|
if err != nil {
|
2020-05-20 05:21:12 +00:00
|
|
|
return err
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
2020-05-18 01:21:34 +00:00
|
|
|
defer conn.Close()
|
2020-05-12 02:13:05 +00:00
|
|
|
|
|
|
|
totalBytes := uint64(0)
|
2020-05-18 01:21:34 +00:00
|
|
|
accBytes := uint64(0)
|
|
|
|
averageSpeed := float64(0)
|
|
|
|
buf := make([]byte, 4*1024)
|
|
|
|
start := time.Now()
|
|
|
|
prevUpdate := start
|
|
|
|
|
2020-05-12 02:13:05 +00:00
|
|
|
for {
|
2020-05-20 06:38:18 +00:00
|
|
|
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
2020-05-18 01:21:34 +00:00
|
|
|
n, err := conn.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
2020-05-20 05:21:12 +00:00
|
|
|
return err
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
2020-05-18 01:21:34 +00:00
|
|
|
if n == 0 {
|
2020-05-12 02:13:05 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 05:21:12 +00:00
|
|
|
if _, err := w.Write(buf[:n]); err != nil {
|
|
|
|
return err
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 01:21:34 +00:00
|
|
|
accBytes += uint64(n)
|
|
|
|
totalBytes += uint64(n)
|
2020-05-12 02:13:05 +00:00
|
|
|
|
2020-05-20 05:21:12 +00:00
|
|
|
_, err = conn.Write(uint64Bytes(totalBytes))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-17 01:55:46 +00:00
|
|
|
|
2020-05-20 05:21:12 +00:00
|
|
|
if progress != nil {
|
|
|
|
if dt := time.Since(prevUpdate); dt >= time.Second {
|
|
|
|
prevUpdate = time.Now()
|
|
|
|
|
|
|
|
speed := float64(accBytes) / dt.Seconds()
|
|
|
|
if averageSpeed == 0 {
|
|
|
|
averageSpeed = speed
|
|
|
|
} else {
|
|
|
|
averageSpeed = 0.2*speed + 0.8*averageSpeed
|
|
|
|
}
|
|
|
|
accBytes = 0
|
|
|
|
|
|
|
|
bytesRemaining := float64(pack.Length - totalBytes)
|
|
|
|
percentage := 100 * (float64(totalBytes) / float64(pack.Length))
|
|
|
|
|
|
|
|
progress <- DownloadProgress{
|
2020-06-24 06:09:05 +00:00
|
|
|
Speed: formatByteCount(averageSpeed, true),
|
2020-05-20 05:21:12 +00:00
|
|
|
PercCompletion: percentage,
|
2020-06-24 06:09:05 +00:00
|
|
|
BytesRemaining: formatByteCount(bytesRemaining, false),
|
|
|
|
BytesCompleted: formatByteCount(float64(totalBytes), false),
|
2020-05-20 05:21:12 +00:00
|
|
|
SecondsElapsed: secondsSince(start),
|
|
|
|
SecondsToGo: bytesRemaining / averageSpeed,
|
|
|
|
File: pack.File,
|
|
|
|
}
|
2020-05-12 13:09:14 +00:00
|
|
|
}
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-17 01:55:46 +00:00
|
|
|
|
2020-05-20 05:21:12 +00:00
|
|
|
if progress != nil {
|
|
|
|
progress <- DownloadProgress{
|
|
|
|
PercCompletion: 100,
|
2020-06-24 06:09:05 +00:00
|
|
|
BytesCompleted: formatByteCount(float64(totalBytes), false),
|
2020-05-20 05:21:12 +00:00
|
|
|
SecondsElapsed: secondsSince(start),
|
|
|
|
File: pack.File,
|
|
|
|
}
|
2020-05-18 01:21:34 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 05:21:12 +00:00
|
|
|
return nil
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 06:09:05 +00:00
|
|
|
func (pack *DCCSend) Size() string {
|
|
|
|
return formatByteCount(float64(pack.Length), false)
|
|
|
|
}
|
|
|
|
|
2020-05-12 02:13:05 +00:00
|
|
|
type DownloadProgress struct {
|
|
|
|
File string `json:"file"`
|
|
|
|
Error error `json:"error"`
|
|
|
|
BytesCompleted string `json:"bytes_completed"`
|
|
|
|
BytesRemaining string `json:"bytes_remaining"`
|
|
|
|
PercCompletion float64 `json:"perc_completion"`
|
2020-05-12 13:09:14 +00:00
|
|
|
Speed string `json:"speed"`
|
2020-05-12 02:13:05 +00:00
|
|
|
SecondsElapsed int64 `json:"elapsed"`
|
|
|
|
SecondsToGo float64 `json:"eta"`
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:21:34 +00:00
|
|
|
func intToIP(n int) string {
|
2020-05-12 02:13:05 +00:00
|
|
|
var byte1 = n & 255
|
|
|
|
var byte2 = ((n >> 8) & 255)
|
|
|
|
var byte3 = ((n >> 16) & 255)
|
|
|
|
var byte4 = ((n >> 24) & 255)
|
|
|
|
return fmt.Sprintf("%d.%d.%d.%d", byte4, byte3, byte2, byte1)
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:21:34 +00:00
|
|
|
func uint64Bytes(i uint64) []byte {
|
2020-05-12 02:13:05 +00:00
|
|
|
b := make([]byte, 8)
|
2020-05-18 01:21:34 +00:00
|
|
|
binary.BigEndian.PutUint64(b, i)
|
2020-05-12 02:13:05 +00:00
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:21:34 +00:00
|
|
|
func secondsSince(t time.Time) int64 {
|
|
|
|
return int64(math.Round(time.Since(t).Seconds()))
|
2020-05-12 02:13:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
_ = 1.0 << (10 * iota)
|
|
|
|
kibibyte
|
|
|
|
mebibyte
|
|
|
|
gibibyte
|
|
|
|
)
|
|
|
|
|
2020-06-24 06:09:05 +00:00
|
|
|
func formatByteCount(b float64, speed bool) string {
|
2020-05-12 02:13:05 +00:00
|
|
|
unit := ""
|
|
|
|
value := b
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case b >= gibibyte:
|
|
|
|
unit = "GiB"
|
|
|
|
value = value / gibibyte
|
|
|
|
case b >= mebibyte:
|
|
|
|
unit = "MiB"
|
|
|
|
value = value / mebibyte
|
|
|
|
case b >= kibibyte:
|
|
|
|
unit = "KiB"
|
|
|
|
value = value / kibibyte
|
|
|
|
case b > 1 || b == 0:
|
|
|
|
unit = "bytes"
|
|
|
|
case b == 1:
|
|
|
|
unit = "byte"
|
|
|
|
}
|
|
|
|
|
|
|
|
if speed {
|
|
|
|
unit = unit + "/s"
|
|
|
|
}
|
|
|
|
|
|
|
|
stringValue := strings.TrimSuffix(
|
|
|
|
fmt.Sprintf("%.2f", value), ".00",
|
|
|
|
)
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s %s", stringValue, unit)
|
|
|
|
}
|