1 package reproducer
2
3 import (
4 "archive/tar"
5 "archive/zip"
6 "bytes"
7 "compress/gzip"
8 "io"
9 "io/ioutil"
10 "net/http"
11 "os"
12 "path/filepath"
13 "strings"
14
15 "github.com/pkg/errors"
16 )
17
18
19
20 type fileVisitor func(path, contentType, encoding string, content []byte) error
21
22
23 var breakSearch = errors.Errorf("break search")
24
25
26
27
28
29
30 func search(filename string, action fileVisitor) error {
31 f, err := os.Open(filename)
32 if err != nil {
33 return errors.Wrapf(err, "opening file %s", filename)
34 }
35 content, err := ioutil.ReadAll(f)
36 if err != nil {
37 return errors.Wrapf(err, "reading file %s", filename)
38 }
39 return search_r(filename, "", content, action)
40 }
41
42 func search_r(base, filename string, content []byte, action fileVisitor) error {
43 contentType := http.DetectContentType(content)
44 parts := strings.SplitN(contentType, ";", 2)
45 encoding := ""
46 if len(parts) == 2 {
47 contentType = parts[0]
48 encoding = parts[1]
49 }
50
51 switch contentType {
52 case "application/x-gzip":
53 zr, err := gzip.NewReader(bytes.NewReader(content))
54 if err != nil {
55 return errors.Wrapf(err, "ungzipping file contents")
56 }
57 unzippedContents, err := ioutil.ReadAll(zr)
58 if err != nil {
59 return errors.Wrapf(err, "reading gzip contents %s", filename)
60 }
61
62 return search_r(base, filename, unzippedContents, action)
63 case "application/zip":
64 zr, err := zip.NewReader(bytes.NewReader(content), int64(len(content)))
65 if err != nil {
66 return errors.Wrapf(err, "unzipping contents")
67 }
68 for _, f := range zr.File {
69 rc, err := f.Open()
70 if err != nil {
71 return errors.Wrapf(err, "opening zip entry %s", f.Name)
72 }
73 entryContents, err := ioutil.ReadAll(rc)
74 if err != nil {
75 return errors.Wrapf(err, "reading zip entry %s", f.Name)
76 }
77 err = search_r(base, filepath.Join(filename, f.Name), entryContents, action)
78 if err != nil {
79 return err
80 }
81 }
82
83 return nil
84 case "text/plain":
85 if filename == "" {
86 filename = base
87 }
88 return action(filename, contentType, encoding, content)
89 case "application/octet-stream":
90 tin := tar.NewReader(bytes.NewReader(content))
91 for {
92 header, err := tin.Next()
93 if err == io.EOF {
94 break
95 }
96
97 if err != nil {
98 return errors.Wrapf(err, "untarring file %s", filename)
99 }
100
101 switch header.Typeflag {
102 case tar.TypeReg:
103 name := header.Name
104 entryContents, err := ioutil.ReadAll(tin)
105 if err != nil {
106 return errors.Wrapf(err, "decoding tar entry %s", name)
107 }
108
109 err = search_r(base, filepath.Join(filename, name), entryContents, action)
110 if err != nil {
111 return err
112 }
113 }
114 }
115 default:
116 return errors.Errorf("unrecognized content type %s", contentType)
117 }
118
119 return nil
120 }
121
View as plain text