/
serverfile.go
133 lines (123 loc) · 3.06 KB
/
serverfile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package server
import (
"errors"
"fmt"
"io"
"strconv"
"strings"
"sync"
"time"
"github.com/juju/fslock"
"github.com/rs/zerolog/log"
"github.com/sushshring/torrxfer/pkg/net"
)
const delimiter string = "*?*"
// File server representation of a file
type File struct {
fullPath string
mediaPrefix string
size uint64
currentSize uint64
creationTime time.Time
modifiedTime time.Time
writeChannel *io.PipeWriter
readChannel io.Reader
errorChannel chan error
doneChannel chan struct{}
mux fslock.Lock
sync.Cond
sync.RWMutex
sync.Once
}
func (f *File) getStrings() (stringReprs []string) {
stringReprs = make([]string, 0)
var creationTime []byte
var err error
if f.creationTime != time.Unix(0, 0) {
creationTime, err = f.creationTime.MarshalText()
if err != nil {
return nil
}
}
modifiedTime, err := f.modifiedTime.MarshalText()
if err != nil {
return nil
}
stringReprs = append(stringReprs,
f.fullPath,
delimiter,
f.mediaPrefix,
delimiter,
fmt.Sprintf("%d", f.size),
delimiter,
fmt.Sprintf("%d", f.currentSize),
delimiter,
string(creationTime),
delimiter,
string(modifiedTime))
return stringReprs
}
// MarshalText converts the clientFile representation to a utf encoded byte array
func (f *File) MarshalText() (text []byte, err error) {
err = nil
stringRepr := f.getStrings()
if stringRepr == nil {
err = errors.New("failed to marshal file object")
return nil, err
}
var marshalSize int
for _, i := range stringRepr {
marshalSize += len(i)
}
text = make([]byte, marshalSize)
currentCopy := 0
for _, i := range stringRepr {
copy(text[currentCopy:currentCopy+len(i)], i)
currentCopy += len(i)
}
return text, err
}
// UnmarshalText takes a utf encoded byte array and builds a ClientFile object from it
func (f *File) UnmarshalText(text []byte) error {
var modifiedTime, creationTime string
var size, currentSize uint64
textString := string(text)
tokens := strings.Split(textString, delimiter)
if len(tokens) != 6 {
err := errors.New("not enough tokens in provided text")
log.Error().Strs("tokens", tokens).Msg("Error while unmarshalling")
return err
}
f.fullPath = strings.TrimSpace(tokens[0])
f.mediaPrefix = strings.TrimSpace(tokens[1])
size, err := strconv.ParseUint(strings.TrimSpace(tokens[2]), 10, 64)
if err != nil {
return err
}
f.size = size
currentSize, err = strconv.ParseUint(strings.TrimSpace(tokens[3]), 10, 64)
if err != nil {
return err
}
f.currentSize = currentSize
modifiedTime = tokens[4]
creationTime = tokens[5]
f.modifiedTime = time.Time{}
if err := f.modifiedTime.UnmarshalText([]byte(strings.TrimSpace(modifiedTime))); err != nil {
return err
}
f.creationTime = time.Time{}
if err := f.creationTime.UnmarshalText([]byte(strings.TrimSpace(creationTime))); err != nil {
return err
}
return nil
}
// GenerateRPCFile returns common RPC representation of a server file
func (f *File) GenerateRPCFile() (*net.RPCFile, error) {
rpcFile, err := net.NewFile(f.fullPath)
if err != nil {
return nil, err
}
rpcFile.SetMediaPath(f.mediaPrefix)
return rpcFile, nil
}