No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

191 líneas
5.2 KiB

  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file or at
  5. // https://developers.google.com/open-source/licenses/bsd.
  6. package gosrc
  7. import (
  8. "errors"
  9. "net/http"
  10. "net/url"
  11. "regexp"
  12. "strings"
  13. )
  14. func init() {
  15. addService(&service{
  16. pattern: regexp.MustCompile(`^code\.google\.com/(?P<pr>[pr])/(?P<repo>[a-z0-9\-]+)(:?\.(?P<subrepo>[a-z0-9\-]+))?(?P<dir>/[a-z0-9A-Z_.\-/]+)?$`),
  17. prefix: "code.google.com/",
  18. get: getGoogleDir,
  19. getPresentation: getGooglePresentation,
  20. })
  21. }
  22. var (
  23. googleRepoRe = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
  24. googleRevisionRe = regexp.MustCompile(`<h2>(?:[^ ]+ - )?Revision *([^:]+):`)
  25. googleEtagRe = regexp.MustCompile(`^(hg|git|svn)-`)
  26. googleFileRe = regexp.MustCompile(`<li><a href="([^"]+)"`)
  27. )
  28. func checkGoogleRedir(c *httpClient, match map[string]string) error {
  29. resp, err := c.getNoFollow(expand("https://code.google.com/{pr}/{repo}/", match))
  30. if err != nil {
  31. return err
  32. }
  33. defer resp.Body.Close()
  34. if resp.StatusCode == http.StatusOK {
  35. return nil
  36. }
  37. if resp.StatusCode == http.StatusMovedPermanently {
  38. if u, err := url.Parse(resp.Header.Get("Location")); err == nil {
  39. p := u.Host + u.Path + match["dir"]
  40. return NotFoundError{Message: "Project moved", Redirect: p}
  41. }
  42. }
  43. return c.err(resp)
  44. }
  45. func getGoogleDir(client *http.Client, match map[string]string, savedEtag string) (*Directory, error) {
  46. setupGoogleMatch(match)
  47. c := &httpClient{client: client}
  48. if err := checkGoogleRedir(c, match); err != nil {
  49. return nil, err
  50. }
  51. if m := googleEtagRe.FindStringSubmatch(savedEtag); m != nil {
  52. match["vcs"] = m[1]
  53. } else if err := getGoogleVCS(c, match); err != nil {
  54. return nil, err
  55. }
  56. // Scrape the repo browser to find the project revision and individual Go files.
  57. p, err := c.getBytes(expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match))
  58. if err != nil {
  59. return nil, err
  60. }
  61. var etag string
  62. m := googleRevisionRe.FindSubmatch(p)
  63. if m == nil {
  64. return nil, errors.New("Could not find revision for " + match["importPath"])
  65. }
  66. etag = expand("{vcs}-{0}", match, string(m[1]))
  67. if etag == savedEtag {
  68. return nil, NotModifiedError{}
  69. }
  70. var subdirs []string
  71. var files []*File
  72. var dataURLs []string
  73. for _, m := range googleFileRe.FindAllSubmatch(p, -1) {
  74. fname := string(m[1])
  75. switch {
  76. case strings.HasSuffix(fname, "/"):
  77. fname = fname[:len(fname)-1]
  78. if isValidPathElement(fname) {
  79. subdirs = append(subdirs, fname)
  80. }
  81. case isDocFile(fname):
  82. files = append(files, &File{Name: fname, BrowseURL: expand("http://code.google.com/{pr}/{repo}/source/browse{dir}/{0}{query}", match, fname)})
  83. dataURLs = append(dataURLs, expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/{0}", match, fname))
  84. }
  85. }
  86. if err := c.getFiles(dataURLs, files); err != nil {
  87. return nil, err
  88. }
  89. var projectURL string
  90. if match["subrepo"] == "" {
  91. projectURL = expand("https://code.google.com/{pr}/{repo}/", match)
  92. } else {
  93. projectURL = expand("https://code.google.com/{pr}/{repo}/source/browse?repo={subrepo}", match)
  94. }
  95. return &Directory{
  96. BrowseURL: expand("http://code.google.com/{pr}/{repo}/source/browse{dir}/{query}", match),
  97. Etag: etag,
  98. Files: files,
  99. LineFmt: "%s#%d",
  100. ProjectName: expand("{repo}{dot}{subrepo}", match),
  101. ProjectRoot: expand("code.google.com/{pr}/{repo}{dot}{subrepo}", match),
  102. ProjectURL: projectURL,
  103. VCS: match["vcs"],
  104. }, nil
  105. }
  106. func setupGoogleMatch(match map[string]string) {
  107. if s := match["subrepo"]; s != "" {
  108. match["dot"] = "."
  109. match["query"] = "?repo=" + s
  110. } else {
  111. match["dot"] = ""
  112. match["query"] = ""
  113. }
  114. }
  115. func getGoogleVCS(c *httpClient, match map[string]string) error {
  116. // Scrape the HTML project page to find the VCS.
  117. p, err := c.getBytes(expand("http://code.google.com/{pr}/{repo}/source/checkout", match))
  118. if err != nil {
  119. return err
  120. }
  121. m := googleRepoRe.FindSubmatch(p)
  122. if m == nil {
  123. return NotFoundError{Message: "Could not find VCS on Google Code project page."}
  124. }
  125. match["vcs"] = string(m[1])
  126. return nil
  127. }
  128. func getGooglePresentation(client *http.Client, match map[string]string) (*Presentation, error) {
  129. c := &httpClient{client: client}
  130. setupGoogleMatch(match)
  131. if err := getGoogleVCS(c, match); err != nil {
  132. return nil, err
  133. }
  134. rawBase, err := url.Parse(expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match))
  135. if err != nil {
  136. return nil, err
  137. }
  138. p, err := c.getBytes(expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/{file}", match))
  139. if err != nil {
  140. return nil, err
  141. }
  142. b := &presBuilder{
  143. data: p,
  144. filename: match["file"],
  145. fetch: func(fnames []string) ([]*File, error) {
  146. var files []*File
  147. var dataURLs []string
  148. for _, fname := range fnames {
  149. u, err := rawBase.Parse(fname)
  150. if err != nil {
  151. return nil, err
  152. }
  153. files = append(files, &File{Name: fname})
  154. dataURLs = append(dataURLs, u.String())
  155. }
  156. err := c.getFiles(dataURLs, files)
  157. return files, err
  158. },
  159. resolveURL: func(fname string) string {
  160. u, err := rawBase.Parse(fname)
  161. if err != nil {
  162. return "/notfound"
  163. }
  164. return u.String()
  165. },
  166. }
  167. return b.build()
  168. }