/yenc/yenc.go

http://github.com/DanielMorsing/gonzbee · Go · 381 lines · 313 code · 36 blank · 32 comment · 110 complexity · 13edd2106f46358d3abed76a23b51539 MD5 · raw file

  1. //Copyright 2012, Daniel Morsing
  2. //For licensing information, See the LICENSE file
  3. //Package yenc gives a decoding interface for yEnc encoded binaries.
  4. package yenc
  5. import (
  6. "bufio"
  7. "bytes"
  8. "fmt"
  9. "hash"
  10. "hash/crc32"
  11. "io"
  12. )
  13. //Part holds the information contained in a parsed yEnc header.
  14. //It also implements the read interface for easy decoding.
  15. //Normally, you will need to read the Filename to find out which file to open
  16. //and Begin to know where to seek before writing.
  17. type Part struct {
  18. Filename string
  19. Begin int64
  20. Size int64
  21. NumParts int
  22. Number int
  23. end int64
  24. multipart bool
  25. br readInterface
  26. crc hash.Hash32
  27. byteCount int
  28. }
  29. type DecodeError string
  30. func (d DecodeError) Error() string {
  31. return string(d)
  32. }
  33. // the minimal interface that the parser requires
  34. // for decoding yenc files. Notably implemented by bytes.Buffer
  35. type readInterface interface {
  36. ReadByte() (byte, error)
  37. ReadString(delim byte) (line string, err error)
  38. }
  39. //NewPart finds and parses the yEnc header in the reader and returns a
  40. //part to use for further decoding.
  41. func NewPart(r io.Reader) (*Part, error) {
  42. y := new(Part)
  43. br, ok := r.(readInterface)
  44. if ok {
  45. y.br = br
  46. } else {
  47. y.br = bufio.NewReader(r)
  48. }
  49. err := y.findHeader()
  50. if err != nil {
  51. return nil, err
  52. }
  53. err = y.parseHeader()
  54. if err != nil {
  55. return nil, err
  56. }
  57. y.crc = crc32.NewIEEE()
  58. return y, nil
  59. }
  60. //Read will read the content of a yEnc part, obeying the normal read rules
  61. func (y *Part) Read(b []byte) (n int, err error) {
  62. for n < len(b) {
  63. var tok byte
  64. tok, err = y.br.ReadByte()
  65. if err != nil {
  66. if err == io.EOF {
  67. err = DecodeError("Unexpected End-of-File")
  68. }
  69. return
  70. }
  71. if tok == '\n' {
  72. continue
  73. }
  74. if tok == '=' {
  75. tok, err = y.br.ReadByte()
  76. if err != nil {
  77. if err == io.EOF {
  78. err = DecodeError("Unexpected End-of-File")
  79. }
  80. return
  81. }
  82. if tok == 'y' {
  83. y.crc.Write(b[:n])
  84. err = y.epilogue()
  85. return
  86. }
  87. tok -= 64
  88. }
  89. var c byte
  90. c = tok - 42
  91. b[n] = c
  92. n++
  93. y.byteCount++
  94. }
  95. y.crc.Write(b[:n])
  96. return
  97. }
  98. // handle footer and give an error if it fails crc.
  99. func (y *Part) epilogue() error {
  100. footer, err := y.parseFooter()
  101. if err != nil {
  102. return err
  103. }
  104. if footer.size != y.byteCount {
  105. return DecodeError("Could not verify decoding: Sizes differ")
  106. }
  107. var crcp *uint32
  108. if y.multipart || footer.crc == 0 {
  109. crcp = &footer.pcrc
  110. } else {
  111. crcp = &footer.crc
  112. }
  113. if *crcp != y.crc.Sum32() {
  114. return DecodeError("Could not verify decoding: Bad CRC")
  115. }
  116. return io.EOF
  117. }
  118. func (y *Part) findHeader() error {
  119. const (
  120. StatePotential = iota
  121. StateNormal
  122. )
  123. i := 0
  124. str := "=ybegin "
  125. //regexp package will read past the end of the match, so making my own little matching statemachine
  126. state := StatePotential
  127. for {
  128. //when completely matched
  129. if i == len(str) {
  130. return nil
  131. }
  132. c, err := y.br.ReadByte()
  133. if err != nil {
  134. return DecodeError("Could not find header")
  135. }
  136. switch state {
  137. case StatePotential:
  138. if str[i] == c {
  139. i++
  140. continue
  141. } else if c != '\n' {
  142. state = StateNormal
  143. }
  144. i = 0
  145. case StateNormal:
  146. if c == '\n' {
  147. state = StatePotential
  148. }
  149. }
  150. }
  151. panic("unreachable")
  152. }
  153. func (y *Part) parseHeader() error {
  154. err := y.parseDataline()
  155. if err != nil {
  156. return err
  157. }
  158. //dealing with single part. don't handle partline
  159. if y.NumParts == 0 && y.Number == 0 {
  160. return nil
  161. }
  162. err = y.parsePartline()
  163. y.Size = y.end - y.Begin
  164. y.multipart = true
  165. return err
  166. }
  167. func (y *Part) parseDataline() error {
  168. dline, err := y.br.ReadString('\n')
  169. if err != nil {
  170. if err == io.EOF {
  171. err = DecodeError("Unexpected End-of-File")
  172. }
  173. return err
  174. }
  175. dline = dline[:len(dline)-1]
  176. dbuf := bytes.NewBufferString(dline)
  177. for {
  178. name, err := consumeName(dbuf)
  179. if err != nil {
  180. return DecodeError("Malformed header")
  181. }
  182. if name == "name" {
  183. break
  184. }
  185. value, err := consumeValue(dbuf)
  186. if err != nil {
  187. return DecodeError("Malformed header")
  188. }
  189. err = y.handleAttrib(name, value)
  190. if err != nil {
  191. return DecodeError("Malformed header")
  192. }
  193. }
  194. y.Filename = dbuf.String()
  195. return nil
  196. }
  197. func (y *Part) parsePartline() error {
  198. //move past =ypart
  199. _, err := y.br.ReadString(' ')
  200. if err != nil {
  201. if err == io.EOF {
  202. err = DecodeError("Unexpected End-of-File")
  203. }
  204. return err
  205. }
  206. pline, err := y.br.ReadString('\n')
  207. if err != nil {
  208. if err == io.EOF {
  209. err = DecodeError("Unexpected End-of-File")
  210. }
  211. return err
  212. }
  213. pline = pline[:len(pline)-1]
  214. pbuf := bytes.NewBufferString(pline)
  215. var name, value string
  216. for {
  217. name, err = consumeName(pbuf)
  218. if err != nil {
  219. return DecodeError("Malformed header")
  220. }
  221. value, err = consumeValue(pbuf)
  222. if err == io.EOF {
  223. break
  224. } else if err != nil {
  225. return DecodeError("Malformed header")
  226. }
  227. err = y.handleAttrib(name, value)
  228. if err != nil {
  229. return DecodeError("Malformed header")
  230. }
  231. }
  232. //handle the last value through the loop
  233. err = y.handleAttrib(name, value)
  234. if err != nil {
  235. return DecodeError("Malformed header")
  236. }
  237. return nil
  238. }
  239. func (y *Part) handleAttrib(name, value string) error {
  240. var err error
  241. switch name {
  242. case "line":
  243. //ignore because noone actually cares
  244. case "size":
  245. if y.Size == 0 {
  246. _, err = fmt.Sscan(value, &y.Size)
  247. }
  248. case "part":
  249. //noone cares
  250. _, err = fmt.Sscan(value, &y.Number)
  251. case "total":
  252. _, err = fmt.Sscan(value, &y.NumParts)
  253. case "begin":
  254. _, err = fmt.Sscan(value, &y.Begin)
  255. y.Begin--
  256. case "end":
  257. _, err = fmt.Sscan(value, &y.end)
  258. default:
  259. err = fmt.Errorf("Unknown Attribute: %s=%s", name, value)
  260. }
  261. return err
  262. }
  263. type footer struct {
  264. size int
  265. crc uint32
  266. pcrc uint32
  267. }
  268. // Parse the footer of a yenc part
  269. // we can sorta handle a corrupted footer
  270. // so instead of dumping out, return the error
  271. func (y *Part) parseFooter() (*footer, error) {
  272. f := new(footer)
  273. // move past =yend
  274. // really, we've only checked for "=y"
  275. // but in practice this works.
  276. _, err := y.br.ReadString(' ')
  277. if err != nil {
  278. if err == io.EOF {
  279. err = DecodeError("Unexpected End-of-File")
  280. }
  281. return nil, err
  282. }
  283. // carve out a line to read from
  284. fline, err := y.br.ReadString('\n')
  285. if err != nil {
  286. // EOF is fine. just means that someone didn't end this line with \n
  287. // all other errors are not fine
  288. if err != io.EOF {
  289. return nil, err
  290. }
  291. }
  292. fbuf := bytes.NewBufferString(fline)
  293. var name, value string
  294. for {
  295. name, err = consumeName(fbuf)
  296. if err != nil {
  297. // if someone added whitespace to the end of this line, this will fail with an EOF
  298. // surface this to the callers
  299. if err == io.EOF {
  300. err = nil
  301. }
  302. return f, err
  303. }
  304. value, err = consumeValue(fbuf)
  305. if err != nil {
  306. if err != io.EOF {
  307. return f, DecodeError("Corrupt footer")
  308. }
  309. }
  310. err = f.handleAttrib(name, value)
  311. if err != nil {
  312. return f, DecodeError("Corrupt footer")
  313. }
  314. }
  315. }
  316. func (f *footer) handleAttrib(name, value string) error {
  317. var err error
  318. switch name {
  319. case "size":
  320. _, err = fmt.Sscan(value, &f.size)
  321. case "pcrc32":
  322. _, err = fmt.Sscanf(value, "%x", &f.pcrc)
  323. case "crc32":
  324. _, err = fmt.Sscanf(value, "%x", &f.crc)
  325. case "part":
  326. //noone cares
  327. }
  328. return err
  329. }
  330. func consumeName(b *bytes.Buffer) (string, error) {
  331. name, err := b.ReadString('=')
  332. if err != nil {
  333. return name, err
  334. }
  335. name = name[:len(name)-1]
  336. return name, nil
  337. }
  338. func consumeValue(b *bytes.Buffer) (string, error) {
  339. value, err := b.ReadString(' ')
  340. if err != nil {
  341. return value, err
  342. }
  343. value = value[:len(value)-1]
  344. return value, nil
  345. }