-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathyEncEncoder.go
108 lines (92 loc) · 3.29 KB
/
yEncEncoder.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
package main
import (
"bytes"
"crypto/md5"
"fmt"
"hash/crc32"
"math"
"golang.org/x/exp/slices"
)
func yEncEncoder() {
for {
select {
case <-ctx.Done():
return // Error somewhere, terminate
default: // Default is must to avoid blocking
chunk, ok := <-inputChunksChan
if !ok {
return
}
select {
case <-ctx.Done():
return // Error somewhere, terminate
default: // Default is must to avoid blocking
func() {
defer chunksWG.Done()
// Calculate the CRC32 checksum of the chunk
partChecksum := crc32.ChecksumIEEE(chunk.Part.Bytes())
// Open a byte writer
var outputChunk bytes.Buffer
// obfuscate yenc file name if activated
filename := ""
if conf.Obfuscate && conf.ObfuscateYenc {
hasher := md5.New()
hasher.Write([]byte(fmt.Sprintf("%s%d", chunk.Filename, chunk.PartNumber)))
filename = fmt.Sprintf("%x", hasher.Sum(nil))
} else {
filename = chunk.Filename
}
// Write yEnc part header
header := fmt.Sprintf("=ybegin part=%d total=%d line=%d size=%d name=%s\r\n",
chunk.PartNumber, chunk.TotalParts, conf.LineLength, chunk.TotalSize, filename)
_, err := outputChunk.WriteString(header)
if err != nil {
checkForFatalErr(fmt.Errorf("error writing yEnc header for part %d: %v", chunk.PartNumber, err))
}
partHeader := fmt.Sprintf("=ypart begin=%d end=%d size=%d\r\n",
chunk.StartByte, chunk.EndByte, chunk.PartSize)
_, err = outputChunk.WriteString(partHeader)
if err != nil {
checkForFatalErr(fmt.Errorf("error writing yEnc part header for part %d: %v", chunk.PartNumber, err))
}
specialChar := []byte{0x3D, 0x00, 0x0A, 0x0D, 0x7F}
// Encode the part to yEnc format
for j := 1; j <= len(chunk.Part.Bytes()); j++ {
b := chunk.Part.Bytes()[j-1]
// yEnc encode the character
b = byte(math.Mod(float64(b+42), float64(256)))
// Escape special yEnc characters
if slices.Contains(specialChar, b) {
outputChunk.WriteByte('=')
outputChunk.WriteByte(byte(math.Mod(float64(b+64), float64(256))))
// Escape spaces at the line start or end
} else if b == ' ' && (math.Mod(float64(j), float64(conf.LineLength)) == 0 || math.Mod(float64(j-1), float64(conf.LineLength)) == 0) {
outputChunk.WriteByte('=')
outputChunk.WriteByte(byte(math.Mod(float64(b+64), float64(256))))
} else {
outputChunk.WriteByte(b)
}
// Write line break after each line
if math.Mod(float64(j), float64(conf.LineLength)) == 0 || j == len(chunk.Part.Bytes()) {
outputChunk.WriteString("\r\n")
}
}
// Write yEnc part footer
var partFooter string
if chunk.PartNumber == chunk.TotalParts {
partFooter = fmt.Sprintf("=yend size=%d part=%d pcrc32=%08X crc32=%08X\r\n", chunk.PartSize, chunk.PartNumber, partChecksum, chunk.Checksum)
} else {
partFooter = fmt.Sprintf("=yend size=%d part=%d pcrc32=%08X\r\n", chunk.PartSize, chunk.PartNumber, partChecksum)
}
_, err = outputChunk.WriteString(partFooter)
if err != nil {
checkForFatalErr(fmt.Errorf("error to writing yEnc part footer for part %d: %v", chunk.PartNumber, err))
}
chunk.Part = outputChunk
chunksWG.Add(1)
outputChunksChan <- chunk
}()
}
}
}
}