dav.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. package dav
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "os"
  7. "regexp"
  8. "strings"
  9. "time"
  10. "io/ioutil"
  11. "github.com/G-Node/git-module"
  12. gannex "github.com/G-Node/go-annex"
  13. "github.com/G-Node/gogs/models"
  14. gctx "github.com/G-Node/gogs/pkg/context"
  15. "github.com/G-Node/gogs/pkg/setting"
  16. "github.com/G-Node/gogs/pkg/tool"
  17. "golang.org/x/net/context"
  18. "golang.org/x/net/webdav"
  19. log "gopkg.in/clog.v1"
  20. )
  21. var (
  22. RE_GETRNAME = regexp.MustCompile(`.+\/(.+)\/_dav`)
  23. RE_GETROWN = regexp.MustCompile(`\/(.+)\/.+\/_dav`)
  24. RE_GETFPATH = regexp.MustCompile("/_dav/(.+)")
  25. )
  26. const ANNEXPEEKSIZE = 1024
  27. func Dav(c *gctx.Context, handler *webdav.Handler) {
  28. if !setting.WebDav.On {
  29. c.WriteHeader(http.StatusUnauthorized)
  30. return
  31. }
  32. if checkPerms(c) != nil {
  33. Webdav401(c)
  34. return
  35. }
  36. handler.ServeHTTP(c.Resp, c.Req.Request)
  37. return
  38. }
  39. // GinFS implements webdav (it implements webdav.Habdler) read only access to a repository
  40. type GinFS struct {
  41. BasePath string
  42. }
  43. // Mkdir not implemented: Just return an error. -> Read Only
  44. func (fs *GinFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
  45. return fmt.Errorf("Mkdir not implemented for read only gin FS")
  46. }
  47. // RemoveAll not implemented: Just return an error. -> Read Only
  48. func (fs *GinFS) RemoveAll(ctx context.Context, name string) error {
  49. return fmt.Errorf("RemoveAll not implemented for read only gin FS")
  50. }
  51. // Rename not implemented: Just return an error. -> Read Only
  52. func (fs *GinFS) Rename(ctx context.Context, oldName, newName string) error {
  53. return fmt.Errorf("Rename not implemented for read only gin FS")
  54. }
  55. // OpenFile returns a named file from the repository
  56. func (fs *GinFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
  57. //todo: catch all the errors
  58. rname, err := getRName(name)
  59. if err != nil {
  60. return nil, err
  61. }
  62. oname, err := getOName(name)
  63. if err != nil {
  64. return nil, err
  65. }
  66. path, _ := getFPath(name)
  67. rpath := fmt.Sprintf("%s/%s/%s.git", fs.BasePath, oname, rname)
  68. grepo, err := git.OpenRepository(rpath)
  69. if err != nil {
  70. return nil, err
  71. }
  72. com, err := grepo.GetBranchCommit("master")
  73. if err != nil {
  74. return nil, err
  75. }
  76. tree, _ := com.SubTree(path)
  77. trentry, _ := com.GetTreeEntryByPath(path)
  78. return &GinFile{trentry: trentry, tree: tree, LChange: com.Committer.When, rpath: rpath}, nil
  79. }
  80. func (fs *GinFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
  81. f, err := fs.OpenFile(ctx, name, 0, 0)
  82. if err != nil {
  83. return nil, err
  84. }
  85. return f.Stat()
  86. }
  87. type GinFile struct {
  88. tree *git.Tree
  89. trentry *git.TreeEntry
  90. dirrcount int
  91. seekoset int64
  92. LChange time.Time
  93. rpath string
  94. afp *os.File
  95. }
  96. func (f *GinFile) Write(p []byte) (n int, err error) {
  97. return 0, fmt.Errorf("write to GinFile not implemented (read only)")
  98. }
  99. func (f *GinFile) Close() error {
  100. if f.afp != nil {
  101. return f.afp.Close()
  102. }
  103. return nil
  104. }
  105. func (f *GinFile) read(p []byte) (int, error) {
  106. if f.trentry == nil {
  107. return 0, fmt.Errorf("File not found")
  108. }
  109. if f.trentry.Type != git.OBJECT_BLOB {
  110. return 0, fmt.Errorf("not a blob")
  111. }
  112. data, err := f.trentry.Blob().Data()
  113. if err != nil {
  114. return 0, err
  115. }
  116. // todo: read with pipes
  117. io.CopyN(ioutil.Discard, data, f.seekoset)
  118. n, err := data.Read(p)
  119. if err != nil {
  120. return n, err
  121. }
  122. return n, nil
  123. }
  124. func (f *GinFile) Read(p []byte) (int, error) {
  125. if f.afp != nil {
  126. return f.afp.Read(p)
  127. }
  128. tmp := make([]byte, len(p))
  129. n, err := f.read(tmp)
  130. tmp = tmp[:n]
  131. if err != nil {
  132. return n, err
  133. }
  134. annexed := tool.IsAnnexedFile(tmp)
  135. if annexed {
  136. af, err := gannex.NewAFile(f.rpath, "annex", f.trentry.Name(), tmp)
  137. if err != nil {
  138. return n, err
  139. }
  140. f.afp, _ = af.Open()
  141. f.afp.Seek(f.seekoset, io.SeekStart)
  142. return f.afp.Read(p)
  143. }
  144. copy(p, tmp)
  145. f.Seek(int64(n), io.SeekCurrent)
  146. return n, nil
  147. }
  148. func (f *GinFile) Seek(offset int64, whence int) (int64, error) {
  149. if f.afp != nil {
  150. return f.afp.Seek(offset, whence)
  151. }
  152. st, err := f.Stat()
  153. if err != nil {
  154. return f.seekoset, err
  155. }
  156. switch whence {
  157. case io.SeekStart:
  158. if offset > st.Size() || offset < 0 {
  159. return 0, fmt.Errorf("cannot seek to %d, only %d big", offset, st.Size())
  160. }
  161. f.seekoset = offset
  162. return f.seekoset, nil
  163. case io.SeekCurrent:
  164. noffset := f.seekoset + offset
  165. if noffset > st.Size() || noffset < 0 {
  166. return 0, fmt.Errorf("cannot seek to %d, only %d big", offset, st.Size())
  167. }
  168. f.seekoset = noffset
  169. return f.seekoset, nil
  170. case io.SeekEnd:
  171. fsize := st.Size()
  172. noffset := fsize - offset
  173. if noffset > fsize || noffset < 0 {
  174. return 0, fmt.Errorf("cannot seek to %d, only %d big", offset, st.Size())
  175. }
  176. f.seekoset = noffset
  177. return f.seekoset, nil
  178. }
  179. return f.seekoset, fmt.Errorf("seeking failed")
  180. }
  181. func (f *GinFile) Readdir(count int) ([]os.FileInfo, error) {
  182. ents, err := f.tree.ListEntries()
  183. if err != nil {
  184. return nil, err
  185. }
  186. // give back all the stuff
  187. if count <= 0 {
  188. return f.getFInfos(ents)
  189. }
  190. // user requested a bufferrd read
  191. switch {
  192. case count > len(ents):
  193. infos, err := f.getFInfos(ents)
  194. if err != nil {
  195. return nil, err
  196. }
  197. return infos, io.EOF
  198. case f.dirrcount >= len(ents):
  199. return nil, io.EOF
  200. case f.dirrcount+count >= len(ents):
  201. infos, err := f.getFInfos(ents[f.dirrcount:])
  202. if err != nil {
  203. return nil, err
  204. }
  205. f.dirrcount = len(ents)
  206. return infos, io.EOF
  207. case f.dirrcount+count < len(ents):
  208. infos, err := f.getFInfos(ents[f.dirrcount : f.dirrcount+count])
  209. if err != nil {
  210. return nil, err
  211. }
  212. f.dirrcount = f.dirrcount + count
  213. return infos, nil
  214. }
  215. return nil, nil
  216. }
  217. func (f *GinFile) getFInfos(ents []*git.TreeEntry) ([]os.FileInfo, error) {
  218. infos := make([]os.FileInfo, len(ents))
  219. for c, ent := range ents {
  220. finfo, err := GinFile{trentry: ent, rpath: f.rpath}.Stat()
  221. if err != nil {
  222. return nil, err
  223. }
  224. infos[c] = finfo
  225. }
  226. return infos, nil
  227. }
  228. func (f GinFile) Stat() (os.FileInfo, error) {
  229. if f.trentry == nil {
  230. return nil, fmt.Errorf("File not found")
  231. }
  232. if f.trentry.Type != git.OBJECT_BLOB {
  233. return GinFinfo{TreeEntry: f.trentry, LChange: f.LChange}, nil
  234. }
  235. peek := make([]byte, ANNEXPEEKSIZE)
  236. offset := f.seekoset
  237. f.seekoset = 0
  238. n, err := f.read(peek)
  239. f.seekoset = offset
  240. if err != nil {
  241. return nil, err
  242. }
  243. peek = peek[:n]
  244. if tool.IsAnnexedFile(peek) {
  245. af, err := gannex.NewAFile(f.rpath, "annex", f.trentry.Name(), peek)
  246. if err != nil {
  247. return nil, err
  248. }
  249. f.trentry.SetSize(af.Info.Size())
  250. }
  251. return GinFinfo{TreeEntry: f.trentry, LChange: f.LChange}, nil
  252. }
  253. type GinFinfo struct {
  254. *git.TreeEntry
  255. LChange time.Time
  256. }
  257. func (i GinFinfo) Mode() os.FileMode {
  258. return 0
  259. }
  260. func (i GinFinfo) ModTime() time.Time {
  261. return i.LChange
  262. }
  263. func (i GinFinfo) Sys() interface{} {
  264. return nil
  265. }
  266. func checkPerms(c *gctx.Context) error {
  267. if !c.Repo.HasAccess() {
  268. return fmt.Errorf("no access")
  269. }
  270. if !setting.WebDav.Logged {
  271. return nil
  272. }
  273. if !c.IsLogged {
  274. return fmt.Errorf("no access")
  275. }
  276. return nil
  277. }
  278. func getRepo(path string) (*models.Repository, error) {
  279. oID, err := getROwnerID(path)
  280. if err != nil {
  281. return nil, err
  282. }
  283. rname, err := getRName(path)
  284. if err != nil {
  285. return nil, err
  286. }
  287. return models.GetRepositoryByName(oID, rname)
  288. }
  289. func getRName(path string) (string, error) {
  290. name := RE_GETRNAME.FindStringSubmatch(path)
  291. if len(name) > 1 {
  292. return strings.ToLower(name[1]), nil
  293. }
  294. return "", fmt.Errorf("could not determine repo name")
  295. }
  296. func getOName(path string) (string, error) {
  297. name := RE_GETROWN.FindStringSubmatch(path)
  298. if len(name) > 1 {
  299. return strings.ToLower(name[1]), nil
  300. }
  301. return "", fmt.Errorf("could not determine repo owner")
  302. }
  303. func getFPath(path string) (string, error) {
  304. name := RE_GETFPATH.FindStringSubmatch(path)
  305. if len(name) > 1 {
  306. return name[1], nil
  307. }
  308. return "", fmt.Errorf("could not determine file path from %s", name)
  309. }
  310. func getROwnerID(path string) (int64, error) {
  311. name := RE_GETROWN.FindStringSubmatch(path)
  312. if len(name) > 1 {
  313. models.GetUserByName(name[1])
  314. }
  315. return -100, fmt.Errorf("could not determine repo owner")
  316. }
  317. func Webdav401(c *gctx.Context) {
  318. c.Header().Add("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s\"", setting.WebDav.AuthRealm))
  319. c.WriteHeader(http.StatusUnauthorized)
  320. return
  321. }
  322. func Logger(req *http.Request, err error) {
  323. if err != nil {
  324. log.Info("davlog: err:%+v", err)
  325. log.Trace("davlog: req:%+v", req)
  326. }
  327. }