1 package download
2
3 import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "os"
8 "regexp"
9 "strconv"
10
11 "github.com/pkg/errors"
12 "gitlab.com/tslocum/gophast/pkg/log"
13 "gitlab.com/tslocum/gophast/pkg/utils"
14 )
15
16 type ControlFile struct {
17 URLs []string
18 Ranges []*ByteRange
19 }
20
21 func ParseControlFile(filePath string) (*ControlFile, error) {
22 f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
23 if err != nil {
24 return nil, errors.Errorf("failed to open control file: %v", err)
25 }
26 defer f.Close()
27
28 urlRegexp, err := regexp.Compile(`^https?://[\S]+$`)
29 if err != nil {
30 return nil, errors.Errorf("failed to compile URL regexp: %v", err)
31 }
32
33 urlRange, err := regexp.Compile(`^([0-9]+),([0-9]+),([0-9]+)$`)
34 if err != nil {
35 return nil, errors.Errorf("failed to compile range regexp: %v", err)
36 }
37
38 var (
39 cf = &ControlFile{}
40 readURLs bool
41 readRanges bool
42 b []byte
43 m [][][]byte
44 start, end, wrote int64
45 )
46
47 log.Verbose("Parsing control file...")
48
49 scanner := bufio.NewScanner(f)
50 for scanner.Scan() {
51 b = scanner.Bytes()
52 if urlRegexp.Match(b) {
53 if readRanges {
54 return nil, errors.Errorf("unexpected URL after range: %s", b)
55 }
56
57 readURLs = true
58
59 cf.URLs = append(cf.URLs, string(b))
60 } else {
61 m = urlRange.FindAllSubmatch(b, -1)
62 if len(m) == 1 && len(m[0]) == 4 {
63 if !readURLs {
64 return nil, errors.Errorf("unexpected range before URL: %s", b)
65 }
66
67 readRanges = true
68
69 start, err = strconv.ParseInt(string(m[0][1]), 10, 64)
70 if err != nil {
71 return nil, errors.Errorf("failed to parse range start: %v", err)
72 }
73
74 end, err = strconv.ParseInt(string(m[0][2]), 10, 64)
75 if err != nil {
76 return nil, errors.Errorf("failed to parse range end: %v", err)
77 }
78
79 utils.ReverseBytes(m[0][3])
80 wrote, err = strconv.ParseInt(string(m[0][3]), 10, 64)
81 if err != nil {
82 return nil, errors.Errorf("failed to parse range wrote: %v", err)
83 }
84
85 if start < 0 || end < 0 || wrote < 0 || end < start || wrote > (end-start+1) {
86 return nil, errors.New(fmt.Sprintf("failed to parse invalid range: %s", NewByteRange(start, end, wrote)))
87 }
88
89 cf.Ranges = append(cf.Ranges, NewByteRange(start, end, wrote))
90 } else if len(bytes.TrimSpace(b)) > 0 {
91 return nil, errors.Errorf("failed to parse line: %s", b)
92 }
93 }
94 }
95 if err := scanner.Err(); err != nil {
96 return nil, errors.Errorf("failed to read file: %v", err)
97 }
98
99 return cf, nil
100 }
101
102 func (cf *ControlFile) String() string {
103 return fmt.Sprintf("{URLs: %s, Ranges: %s}", cf.URLs, cf.Ranges)
104 }
105
View as plain text