parse.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. package gig
  2. import (
  3. "bufio"
  4. "bytes"
  5. "compress/zlib"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "strconv"
  11. "strings"
  12. "time"
  13. )
  14. func parseSignature(line string) (Signature, error) {
  15. //Format: "<name> <email> <unix timestamp> <time zone offset>"
  16. //i.e. "A U Thor <author@example.com> 1462210432 +0200"
  17. u := Signature{}
  18. //<name>
  19. start := strings.Index(line, " <")
  20. if start == -1 {
  21. return u, fmt.Errorf("invalid signature format")
  22. }
  23. u.Name = line[:start]
  24. //<email>
  25. end := strings.Index(line, "> ")
  26. if end == -1 {
  27. return u, fmt.Errorf("invalid signature format")
  28. }
  29. u.Email = line[start+2: end]
  30. //<unix timestamp>
  31. tstr, off := split2(line[end+2:], " ")
  32. i, err := strconv.ParseInt(tstr, 10, 64)
  33. if err != nil || len(off) != 5 {
  34. return u, fmt.Errorf("invalid signature time format")
  35. }
  36. u.Date = time.Unix(i, 0)
  37. //<time zone offset>
  38. h, herr := strconv.Atoi(off[1:3])
  39. m, merr := strconv.Atoi(off[3:])
  40. if herr != nil || merr != nil {
  41. return u, fmt.Errorf("invalid signature offset format")
  42. }
  43. o := (h*60 + m) * 60
  44. if off[0] == '-' {
  45. o *= -1
  46. }
  47. u.Offset = time.FixedZone(off, o)
  48. return u, nil
  49. }
  50. func parseCommitGPGSig(r *bufio.Reader, w *bytes.Buffer) error {
  51. for {
  52. l, err := r.ReadString('\n')
  53. if err != nil {
  54. return nil
  55. } else if l[0] == ' ' {
  56. _, err = w.WriteString(fmt.Sprintf("\n%s", strings.Trim(l, " \n")))
  57. if err != nil {
  58. return err
  59. }
  60. continue
  61. } else if l[0] == '\n' {
  62. return r.UnreadByte()
  63. }
  64. return fmt.Errorf("Unexpected end of gpg signature")
  65. }
  66. }
  67. func parseTagGPGSig(r *bufio.Reader, w *bytes.Buffer) error {
  68. //!Tag signatures do not have trailing whitespaces
  69. for {
  70. l, err := r.ReadString('\n')
  71. if err != nil {
  72. return nil
  73. }
  74. _, err = w.WriteString(fmt.Sprintf("\n%s", strings.Trim(l, " \n")))
  75. if err != nil {
  76. return err
  77. }
  78. if !strings.Contains(l, "-----END PGP SIGNATURE-----") {
  79. continue
  80. } else {
  81. return nil
  82. }
  83. }
  84. }
  85. func openRawObject(path string) (gitObject, error) {
  86. fd, err := os.Open(path)
  87. if err != nil {
  88. return gitObject{}, err
  89. }
  90. // we wrap the zlib reader below, so it will be
  91. // propery closed
  92. r, err := zlib.NewReader(fd)
  93. if err != nil {
  94. return gitObject{}, fmt.Errorf("git: could not create zlib reader: %v", err)
  95. }
  96. // general object format is
  97. // [type][space][length {ASCII}][\0]
  98. line, err := readUntilNul(r)
  99. if err != nil {
  100. return gitObject{}, err
  101. }
  102. tstr, lstr := split2(line, " ")
  103. size, err := strconv.ParseInt(lstr, 10, 64)
  104. if err != nil {
  105. return gitObject{}, fmt.Errorf("git: object parse error: %v", err)
  106. }
  107. otype, err := ParseObjectType(tstr)
  108. if err != nil {
  109. return gitObject{}, err
  110. }
  111. obj := gitObject{otype, size, r}
  112. obj.wrapSource(r)
  113. return obj, nil
  114. }
  115. func parseObject(obj gitObject) (Object, error) {
  116. switch obj.otype {
  117. case ObjCommit:
  118. return parseCommit(obj)
  119. case ObjTree:
  120. return parseTree(obj)
  121. case ObjBlob:
  122. return parseBlob(obj)
  123. case ObjTag:
  124. return parseTag(obj)
  125. }
  126. obj.Close()
  127. return nil, fmt.Errorf("git: unsupported object")
  128. }
  129. func parseCommit(obj gitObject) (*Commit, error) {
  130. c := &Commit{gitObject: obj}
  131. lr := &io.LimitedReader{R: obj.source, N: obj.size}
  132. br := bufio.NewReader(lr)
  133. var err error
  134. for {
  135. var l string
  136. l, err = br.ReadString('\n')
  137. head, tail := split2(l, " ")
  138. switch head {
  139. case "tree":
  140. c.Tree, err = ParseSHA1(tail)
  141. case "parent":
  142. parent, err := ParseSHA1(tail)
  143. if err == nil {
  144. c.Parent = append(c.Parent, parent)
  145. }
  146. case "author":
  147. c.Author, err = parseSignature(strings.Trim(tail, "\n"))
  148. case "committer":
  149. c.Committer, err = parseSignature(strings.Trim(tail, "\n"))
  150. case "gpgsig":
  151. sw := bytes.NewBufferString(strings.Trim(tail, "\n"))
  152. err = parseCommitGPGSig(br, sw)
  153. c.GPGSig = sw.String()
  154. }
  155. if err != nil || head == "\n" {
  156. break
  157. }
  158. }
  159. if err != nil && err != io.EOF {
  160. return nil, err
  161. }
  162. data, err := ioutil.ReadAll(br)
  163. if err != nil {
  164. return nil, err
  165. }
  166. c.Message = string(data)
  167. return c, nil
  168. }
  169. func parseTree(obj gitObject) (*Tree, error) {
  170. tree := Tree{obj, nil, nil}
  171. return &tree, nil
  172. }
  173. func parseTreeEntry(r io.Reader) (*TreeEntry, error) {
  174. //format is: [mode{ASCII, octal}][space][name][\0][SHA1]
  175. entry := &TreeEntry{}
  176. l, err := readUntilNul(r) // read until \0
  177. if err != nil {
  178. return nil, err
  179. }
  180. mstr, name := split2(l, " ")
  181. mode, err := strconv.ParseUint(mstr, 8, 32)
  182. if err != nil {
  183. return nil, err
  184. }
  185. //TODO: this is not correct because
  186. // we need to shift the "st_mode" file
  187. // info bits by 16
  188. entry.Mode = os.FileMode(mode)
  189. if entry.Mode == 040000 {
  190. entry.Type = ObjTree
  191. } else {
  192. entry.Type = ObjBlob
  193. }
  194. entry.Name = name
  195. n, err := r.Read(entry.ID[:])
  196. if err != nil && err != io.EOF {
  197. return nil, err
  198. } else if err == io.EOF && n != 20 {
  199. return nil, fmt.Errorf("git: unexpected EOF")
  200. }
  201. return entry, nil
  202. }
  203. func parseBlob(obj gitObject) (*Blob, error) {
  204. blob := &Blob{obj}
  205. return blob, nil
  206. }
  207. func parseTag(obj gitObject) (*Tag, error) {
  208. c := &Tag{gitObject: obj}
  209. lr := &io.LimitedReader{R: c.source, N: c.size}
  210. br := bufio.NewReader(lr)
  211. var mess bytes.Buffer
  212. var err error
  213. for {
  214. var l string
  215. l, err = br.ReadString('\n')
  216. head, tail := split2(l, " ")
  217. switch head {
  218. case "object":
  219. c.Object, err = ParseSHA1(tail)
  220. case "type":
  221. c.ObjType, err = ParseObjectType(tail)
  222. case "tag":
  223. c.Tag = strings.Trim(tail, "\n")
  224. case "tagger":
  225. c.Tagger, err = parseSignature(strings.Trim(tail, "\n"))
  226. case "-----BEGIN":
  227. //with signed tags (in difference to signed commits) the
  228. // signatures do not start with "gpgsig" but just with
  229. //"-----BEGIN PGP SIGNATURE-----"
  230. //(tbd)
  231. sw := bytes.NewBufferString(strings.Trim(
  232. fmt.Sprintf("%s %s", head, tail),
  233. "\n"))
  234. err = parseTagGPGSig(br, sw)
  235. c.GPGSig = sw.String()
  236. default:
  237. //Capture descriptions for tags here.The old way works for unsigned
  238. //tags but not for signed ones.
  239. // Be Aware! The message comes before the gpg signature
  240. // not after as with commits
  241. mess.WriteString(l)
  242. }
  243. if err != nil {
  244. //For tags gpg signatures can come after the tag description
  245. // which might start and also contain a single newline.
  246. // therefore the ||head=="\n" part
  247. // has been removed. i guess this wont break anything as err will
  248. // eventually become EOF for tags and hence the loop will break
  249. // (tbd)
  250. break
  251. }
  252. }
  253. if err != nil && err != io.EOF {
  254. return nil, err
  255. }
  256. c.Message = mess.String()[1:]
  257. return c, nil
  258. }