dav.go 8.0 KB

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