server.go 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. package gold
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "log"
  10. "net"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. _path "path"
  15. "path/filepath"
  16. "strings"
  17. "github.com/boltdb/bolt"
  18. "github.com/gorilla/securecookie"
  19. "golang.org/x/net/webdav"
  20. "github.com/linkeddata/gold/pkg/apps"
  21. // "github.com/linkeddata/gold/pkg/routes"
  22. )
  23. const (
  24. // HCType is the header Content-Type
  25. HCType = "Content-Type"
  26. // SystemPrefix is the generic name for the system-reserved namespace (e.g. APIs)
  27. SystemPrefix = ",account"
  28. // LoginEndpoint is the link to the login page
  29. LoginEndpoint = SystemPrefix + "/login"
  30. // ProxyPath provides CORS proxy (empty to disable)
  31. ProxyPath = ",proxy"
  32. // QueryPath provides link-following support for twinql
  33. QueryPath = ",query"
  34. // AgentPath is the path to the agent's WebID profile
  35. AgentPath = ",agent"
  36. // RDFExtension is the default extension for RDF documents (i.e. turtle for now)
  37. RDFExtension = ".ttl"
  38. )
  39. var (
  40. // Streaming (stream data or not)
  41. Streaming = false // experimental
  42. debugFlags = log.Flags() | log.Lshortfile
  43. debugPrefix = "[debug] "
  44. methodsAll = []string{
  45. "OPTIONS", "HEAD", "GET",
  46. "PATCH", "POST", "PUT", "MKCOL", "DELETE",
  47. "COPY", "MOVE", "LOCK", "UNLOCK",
  48. }
  49. )
  50. type errorString struct {
  51. s string
  52. }
  53. func (e *errorString) Error() string {
  54. return e.s
  55. }
  56. // Server object contains http handler, root where the data is found and whether it uses vhosts or not
  57. type Server struct {
  58. http.Handler
  59. Config *ServerConfig
  60. cookie *securecookie.SecureCookie
  61. cookieSalt []byte
  62. debug *log.Logger
  63. webdav *webdav.Handler
  64. BoltDB *bolt.DB
  65. }
  66. type httpRequest struct {
  67. *http.Request
  68. *Server
  69. AcceptType string
  70. ContentType string
  71. User string
  72. IsOwner bool
  73. }
  74. func (req httpRequest) BaseURI() string {
  75. scheme := "http"
  76. if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
  77. scheme += "s"
  78. }
  79. reqHost := req.Host
  80. if len(req.Header.Get("X-Forward-Host")) > 0 {
  81. reqHost = req.Header.Get("X-Forward-Host")
  82. }
  83. host, port, err := net.SplitHostPort(reqHost)
  84. if err != nil {
  85. host = reqHost
  86. }
  87. if len(host) == 0 {
  88. host = "localhost"
  89. }
  90. if len(port) > 0 {
  91. port = ":" + port
  92. }
  93. if (scheme == "https" && port == ":443") || (scheme == "http" && port == ":80") {
  94. port = ""
  95. }
  96. return scheme + "://" + host + port + req.URL.Path
  97. }
  98. func (req httpRequest) ifMatch(etag string) bool {
  99. if len(etag) == 0 {
  100. return true
  101. }
  102. if len(req.Header.Get("If-Match")) == 0 {
  103. return true
  104. }
  105. val := strings.Split(req.Header.Get("If-Match"), ",")
  106. for _, v := range val {
  107. v = strings.TrimSpace(v)
  108. if v == "*" || v == etag {
  109. return true
  110. }
  111. }
  112. return false
  113. }
  114. func (req httpRequest) ifNoneMatch(etag string) bool {
  115. if len(etag) == 0 {
  116. return true
  117. }
  118. if len(req.Header.Get("If-None-Match")) == 0 {
  119. return true
  120. }
  121. val := strings.Split(req.Header.Get("If-None-Match"), ",")
  122. for _, v := range val {
  123. v = strings.TrimSpace(v)
  124. if v != "*" && v != etag {
  125. return true
  126. }
  127. }
  128. return false
  129. }
  130. func handleStatusText(status int, err error) string {
  131. switch status {
  132. case 200:
  133. return "HTTP 200 - OK"
  134. case 401:
  135. return Apps["401"]
  136. case 403:
  137. return Apps["403"]
  138. case 404:
  139. return "HTTP 404 - Not found\n\n" + err.Error()
  140. case 500:
  141. return "HTTP 500 - Internal Server Error\n\n" + err.Error()
  142. default: // 501
  143. return "HTTP 501 - Not implemented\n\n" + err.Error()
  144. }
  145. }
  146. // NewServer is used to create a new Server instance
  147. func NewServer(config *ServerConfig) *Server {
  148. s := &Server{
  149. Config: config,
  150. cookie: securecookie.New(securecookie.GenerateRandomKey(32), securecookie.GenerateRandomKey(32)),
  151. cookieSalt: securecookie.GenerateRandomKey(8),
  152. webdav: &webdav.Handler{
  153. FileSystem: webdav.Dir(config.DataRoot),
  154. LockSystem: webdav.NewMemLS(),
  155. },
  156. }
  157. AddRDFExtension(s.Config.ACLSuffix)
  158. AddRDFExtension(s.Config.MetaSuffix)
  159. if config.Debug {
  160. s.debug = log.New(os.Stderr, debugPrefix, debugFlags)
  161. } else {
  162. s.debug = log.New(ioutil.Discard, "", 0)
  163. }
  164. s.debug.Println("---- starting server ----")
  165. s.debug.Printf("config: %#v\n", s.Config)
  166. return s
  167. }
  168. type response struct {
  169. status int
  170. headers http.Header
  171. argv []interface{}
  172. }
  173. func (r *response) respond(status int, a ...interface{}) *response {
  174. r.status = status
  175. r.argv = a
  176. return r
  177. }
  178. func (r *response) respondNotFound() *response {
  179. page404, err := apps.NotFound()
  180. if err != nil {
  181. return r.respond(500, err)
  182. }
  183. return r.respond(404, page404)
  184. }
  185. // ServeHTTP handles the response
  186. func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  187. // add HSTS
  188. if s.Config.HSTS {
  189. w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
  190. }
  191. origin := ""
  192. origins := req.Header["Origin"] // all CORS requests
  193. if len(origins) > 0 {
  194. origin = origins[0]
  195. w.Header().Set("Access-Control-Allow-Origin", origin)
  196. }
  197. if len(origin) < 1 {
  198. w.Header().Set("Access-Control-Allow-Origin", "*")
  199. }
  200. if websocketUpgrade(req) {
  201. websocketServe(w, req)
  202. return
  203. }
  204. defer func() {
  205. req.Body.Close()
  206. }()
  207. r := s.handle(w, &httpRequest{req, s, "", "", "", false})
  208. for key := range r.headers {
  209. w.Header().Set(key, r.headers.Get(key))
  210. }
  211. if r.status > 0 {
  212. w.WriteHeader(r.status)
  213. }
  214. if len(r.argv) > 0 {
  215. fmt.Fprint(w, r.argv...)
  216. }
  217. }
  218. // Twinql Query
  219. func TwinqlQuery(w http.ResponseWriter, req *httpRequest, s *Server) *response {
  220. r := new(response)
  221. err := ProxyReq(w, req, s, s.Config.QueryTemplate)
  222. if err != nil {
  223. s.debug.Println("Query error:", err.Error())
  224. }
  225. return r
  226. }
  227. // Proxy requests
  228. func ProxyReq(w http.ResponseWriter, req *httpRequest, s *Server, reqUrl string) error {
  229. uri, err := url.Parse(reqUrl)
  230. if err != nil {
  231. return err
  232. }
  233. host := uri.Host
  234. if !s.Config.ProxyLocal {
  235. if strings.HasPrefix(host, "10.") ||
  236. strings.HasPrefix(host, "172.16.") ||
  237. strings.HasPrefix(host, "192.168.") ||
  238. strings.HasPrefix(host, "localhost") {
  239. return errors.New("Proxying requests to the local network is not allowed.")
  240. }
  241. }
  242. if len(req.FormValue("key")) > 0 {
  243. token, err := decodeQuery(req.FormValue("key"))
  244. if err != nil {
  245. s.debug.Println(err.Error())
  246. }
  247. user, err := GetAuthzFromToken(token, req)
  248. if err != nil {
  249. s.debug.Println(err.Error())
  250. } else {
  251. s.debug.Println("Authorization valid for user", user)
  252. }
  253. req.User = user
  254. }
  255. if len(req.Header.Get("Authorization")) > 0 {
  256. token, err := ParseBearerAuthorizationHeader(req.Header.Get("Authorization"))
  257. if err != nil {
  258. s.debug.Println(err.Error())
  259. }
  260. user, err := GetAuthzFromToken(token, req)
  261. if err != nil {
  262. s.debug.Println(err.Error())
  263. } else {
  264. s.debug.Println("Authorization valid for user", user)
  265. }
  266. req.User = user
  267. }
  268. req.URL = uri
  269. req.Host = host
  270. req.RequestURI = uri.RequestURI()
  271. req.Header.Set("User", req.User)
  272. proxy.ServeHTTP(w, req.Request)
  273. return nil
  274. }
  275. func (s *Server) handle(w http.ResponseWriter, req *httpRequest) (r *response) {
  276. r = new(response)
  277. var err error
  278. defer func() {
  279. if rec := recover(); rec != nil {
  280. s.debug.Println("\nRecovered from panic: ", rec)
  281. }
  282. }()
  283. s.debug.Println("\n------ New " + req.Method + " request from " + req.RemoteAddr + " ------")
  284. // CORS
  285. w.Header().Set("Access-Control-Allow-Credentials", "true")
  286. w.Header().Set("Access-Control-Expose-Headers", "User, Location, Link, Vary, Last-Modified, WWW-Authenticate, Content-Length, Content-Type, Accept-Patch, Accept-Post, Allow, Updates-Via, Ms-Author-Via")
  287. w.Header().Set("Access-Control-Max-Age", "1728000")
  288. // RWW
  289. w.Header().Set("MS-Author-Via", "DAV, SPARQL")
  290. w.Header().Set("Updates-Via", "wss://"+req.Host+"/")
  291. // Get request key
  292. rKey := req.Request.FormValue("key")
  293. // Authentication
  294. user := req.authn(w)
  295. req.User = user
  296. w.Header().Set("User", user)
  297. acl := NewWAC(req, s, w, user, rKey)
  298. // check if is owner
  299. req.IsOwner = false
  300. resource, _ := req.pathInfo(req.BaseURI())
  301. if len(user) > 0 {
  302. aclStatus, err := acl.AllowWrite(resource.Base)
  303. if aclStatus == 200 && err == nil {
  304. req.IsOwner = true
  305. }
  306. }
  307. // Intercept API requests
  308. if strings.Contains(req.Request.URL.Path, "/"+SystemPrefix) && req.Method != "OPTIONS" {
  309. resp := HandleSystem(w, req, s)
  310. if resp.Bytes != nil && len(resp.Bytes) > 0 {
  311. // copy raw bytes
  312. io.Copy(w, bytes.NewReader(resp.Bytes))
  313. return
  314. }
  315. return r.respond(resp.Status, resp.Body)
  316. }
  317. // Proxy requests
  318. if ProxyPath != "" && strings.HasSuffix(req.URL.Path, ProxyPath) {
  319. err = ProxyReq(w, req, s, s.Config.ProxyTemplate+req.FormValue("uri"))
  320. if err != nil {
  321. s.debug.Println("Proxy error:", err.Error())
  322. }
  323. return
  324. }
  325. // Query requests
  326. if req.Method == "POST" && QueryPath != "" &&
  327. strings.Contains(req.URL.Path, QueryPath) &&
  328. len(s.Config.QueryTemplate) > 0 {
  329. return TwinqlQuery(w, req, s)
  330. }
  331. s.debug.Println(req.RemoteAddr + " requested resource URI: " + req.URL.String())
  332. s.debug.Println(req.RemoteAddr + " requested resource Path: " + resource.File)
  333. dataMime := req.Header.Get(HCType)
  334. dataMime = strings.Split(dataMime, ";")[0]
  335. dataHasParser := len(mimeParser[dataMime]) > 0
  336. if len(dataMime) > 0 {
  337. s.debug.Println("Content-Type: " + dataMime)
  338. if dataMime != "multipart/form-data" && !dataHasParser && req.Method != "PUT" && req.Method != "HEAD" && req.Method != "OPTIONS" {
  339. s.debug.Println("Request contains unsupported Media Type:" + dataMime)
  340. return r.respond(415, "HTTP 415 - Unsupported Media Type:", dataMime)
  341. }
  342. req.ContentType = dataMime
  343. }
  344. // Content Negotiation
  345. contentType := "text/turtle"
  346. acceptList, _ := req.Accept()
  347. if len(acceptList) > 0 && acceptList[0].SubType != "*" {
  348. contentType, err = acceptList.Negotiate(serializerMimes...)
  349. if err != nil {
  350. s.debug.Println("Accept type not acceptable: " + err.Error())
  351. return r.respond(406, "HTTP 406 - Accept type not acceptable: "+err.Error())
  352. }
  353. req.AcceptType = contentType
  354. }
  355. // set ACL Link header
  356. w.Header().Set("Link", brack(resource.AclURI)+"; rel=\"acl\", "+brack(resource.MetaURI)+"; rel=\"meta\"")
  357. // generic headers
  358. w.Header().Set("Accept-Patch", "application/json, application/sparql-update")
  359. w.Header().Set("Accept-Post", "text/turtle, application/json")
  360. w.Header().Set("Allow", strings.Join(methodsAll, ", "))
  361. w.Header().Set("Vary", "Origin")
  362. switch req.Method {
  363. case "OPTIONS":
  364. // TODO: WAC
  365. corsReqH := req.Header["Access-Control-Request-Headers"] // CORS preflight only
  366. if len(corsReqH) > 0 {
  367. w.Header().Set("Access-Control-Allow-Headers", strings.Join(corsReqH, ", "))
  368. }
  369. corsReqM := req.Header["Access-Control-Request-Method"] // CORS preflight only
  370. if len(corsReqM) > 0 {
  371. w.Header().Set("Access-Control-Allow-Methods", strings.Join(corsReqM, ", "))
  372. } else {
  373. w.Header().Set("Access-Control-Allow-Methods", strings.Join(methodsAll, ", "))
  374. }
  375. // set LDP Link headers
  376. if resource.IsDir {
  377. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#BasicContainer")+"; rel=\"type\"")
  378. }
  379. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#Resource")+"; rel=\"type\"")
  380. // set API Link headers
  381. w.Header().Add("Link", brack(resource.Base+"/"+SystemPrefix+"/login")+"; rel=\"http://www.w3.org/ns/solid/terms#loginEndpoint\"")
  382. w.Header().Add("Link", brack(resource.Base+"/"+SystemPrefix+"/logout")+"; rel=\"http://www.w3.org/ns/solid/terms#logoutEndpoint\"")
  383. w.Header().Add("Link", brack(resource.Base+"/,query")+"; rel=\"http://www.w3.org/ns/solid/terms#twinqlEndpoint\"")
  384. w.Header().Add("Link", brack(resource.Base+"/,proxy?uri=")+"; rel=\"http://www.w3.org/ns/solid/terms#proxyEndpoint\"")
  385. return r.respond(200)
  386. case "GET", "HEAD":
  387. unlock := lock(resource.File)
  388. defer unlock()
  389. var (
  390. magicType = resource.FileType
  391. maybeRDF bool
  392. glob bool
  393. globPath string
  394. etag string
  395. )
  396. // check for glob
  397. glob = false
  398. if strings.Contains(resource.Obj.Path, "*") {
  399. glob = true
  400. path := filepath.Dir(resource.Obj.Path)
  401. globPath = resource.File
  402. if path == "." {
  403. path = ""
  404. } else {
  405. path += "/"
  406. }
  407. resource, err = req.pathInfo(resource.Base + "/" + path)
  408. if err != nil {
  409. return r.respond(500, err)
  410. }
  411. }
  412. if !resource.Exists {
  413. return r.respondNotFound()
  414. }
  415. // First redirect to path + trailing slash if it's missing
  416. if resource.IsDir && glob == false && !strings.HasSuffix(req.BaseURI(), "/") {
  417. w.Header().Set(HCType, contentType)
  418. urlStr := resource.URI
  419. s.debug.Println("Redirecting to", urlStr)
  420. http.Redirect(w, req.Request, urlStr, 301)
  421. return
  422. }
  423. // overwrite ACL Link header
  424. w.Header().Set("Link", brack(resource.AclURI)+"; rel=\"acl\", "+brack(resource.MetaURI)+"; rel=\"meta\"")
  425. // redirect to app
  426. if s.Config.Vhosts && !resource.Exists && resource.Base == strings.TrimRight(req.BaseURI(), "/") && contentType == "text/html" && req.Method != "HEAD" {
  427. w.Header().Set(HCType, contentType)
  428. urlStr := s.Config.SignUpApp + url.QueryEscape(resource.Obj.Scheme+"://"+resource.Obj.Host+"/"+SystemPrefix+"/accountStatus")
  429. http.Redirect(w, req.Request, urlStr, 303)
  430. return
  431. }
  432. if resource.IsDir {
  433. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#BasicContainer")+"; rel=\"type\"")
  434. }
  435. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#Resource")+"; rel=\"type\"")
  436. status := 501
  437. aclStatus, err := acl.AllowRead(resource.URI)
  438. if aclStatus > 200 || err != nil {
  439. return r.respond(aclStatus, handleStatusText(aclStatus, err))
  440. }
  441. if req.Method == "HEAD" {
  442. w.Header().Set("Content-Length", fmt.Sprintf("%d", resource.Size))
  443. }
  444. etag, err = NewETag(resource.File)
  445. if err != nil {
  446. return r.respond(500, err)
  447. }
  448. w.Header().Set("ETag", "\""+etag+"\"")
  449. if !req.ifMatch("\"" + etag + "\"") {
  450. return r.respond(412, "412 - Precondition Failed")
  451. }
  452. if !req.ifNoneMatch("\""+etag+"\"") && contentType != "text/html" {
  453. // do not return cached views of dirs for html requests
  454. return r.respond(304, "304 - Not Modified")
  455. }
  456. g := NewGraph(resource.URI)
  457. if resource.IsDir {
  458. if len(s.Config.DirIndex) > 0 && contentType == "text/html" {
  459. magicType = "text/html"
  460. maybeRDF = false
  461. for _, dirIndex := range s.Config.DirIndex {
  462. _, xerr := os.Stat(resource.File + dirIndex)
  463. status = 200
  464. if xerr == nil {
  465. resource, err = req.pathInfo(resource.Base + "/" + resource.Path + dirIndex)
  466. if err != nil {
  467. return r.respond(500, err)
  468. }
  469. w.Header().Set("Link", brack(resource.MetaURI)+"; rel=\"meta\", "+brack(resource.AclURI)+"; rel=\"acl\"")
  470. break
  471. } else if req.Method != "HEAD" {
  472. //TODO load file manager app from local preference file
  473. w.Header().Set(HCType, contentType)
  474. urlStr := s.Config.DirApp + resource.Obj.Scheme + "/" + resource.Obj.Host + "/" + resource.Obj.Path + "?" + req.Request.URL.RawQuery
  475. s.debug.Println("Redirecting to", urlStr)
  476. http.Redirect(w, req.Request, urlStr, 303)
  477. return
  478. }
  479. }
  480. } else {
  481. w.Header().Add("Link", brack(resource.MetaURI)+"; rel=\"meta\"")
  482. root := NewResource(resource.URI)
  483. g.AddTriple(root, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NewResource("http://www.w3.org/ns/posix/stat#Directory"))
  484. g.AddTriple(root, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NewResource("http://www.w3.org/ns/ldp#Container"))
  485. g.AddTriple(root, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NewResource("http://www.w3.org/ns/ldp#BasicContainer"))
  486. g.AddTriple(root, NewResource("http://www.w3.org/ns/posix/stat#mtime"), NewLiteral(fmt.Sprintf("%d", resource.ModTime.Unix())))
  487. g.AddTriple(root, NewResource("http://www.w3.org/ns/posix/stat#size"), NewLiteral(fmt.Sprintf("%d", resource.Size)))
  488. kb := NewGraph(resource.MetaURI)
  489. kb.ReadFile(resource.MetaFile)
  490. if kb.Len() > 0 {
  491. for triple := range kb.IterTriples() {
  492. var subject Term
  493. if kb.One(NewResource(resource.MetaURI), nil, nil) != nil {
  494. subject = NewResource(resource.URI)
  495. } else {
  496. subject = triple.Subject
  497. }
  498. g.AddTriple(subject, triple.Predicate, triple.Object)
  499. }
  500. }
  501. if glob {
  502. matches, err := filepath.Glob(globPath)
  503. if err == nil {
  504. for _, file := range matches {
  505. res, err := req.pathInfo(resource.Base + "/" + filepath.Dir(resource.Path) + "/" + filepath.Base(file))
  506. if !res.IsDir && res.Exists && err == nil {
  507. aclStatus, err = acl.AllowRead(res.URI)
  508. if aclStatus == 200 && err == nil {
  509. g.AppendFile(res.File, res.URI)
  510. g.AddTriple(root, NewResource("http://www.w3.org/ns/ldp#contains"), NewResource(res.URI))
  511. }
  512. }
  513. }
  514. }
  515. } else {
  516. showContainment := true
  517. showEmpty := false
  518. pref := ParsePreferHeader(req.Header.Get("Prefer"))
  519. if len(pref.headers) > 0 {
  520. w.Header().Set("Preference-Applied", "return=representation")
  521. }
  522. for _, include := range pref.Includes() {
  523. switch include {
  524. case "http://www.w3.org/ns/ldp#PreferContainment":
  525. showContainment = true
  526. case "http://www.w3.org/ns/ldp#PreferEmptyContainer":
  527. showEmpty = true
  528. }
  529. }
  530. for _, omit := range pref.Omits() {
  531. switch omit {
  532. case "http://www.w3.org/ns/ldp#PreferContainment":
  533. showContainment = false
  534. case "http://www.w3.org/ns/ldp#PreferEmptyContainer":
  535. showEmpty = false
  536. }
  537. }
  538. if infos, err := ioutil.ReadDir(resource.File); err == nil {
  539. var _s Term
  540. for _, info := range infos {
  541. if info != nil {
  542. // do not list ACLs and Meta files
  543. if strings.HasSuffix(info.Name(), s.Config.ACLSuffix) || strings.HasSuffix(info.Name(), s.Config.MetaSuffix) {
  544. continue
  545. }
  546. res := resource.URI + info.Name()
  547. if info.IsDir() {
  548. res += "/"
  549. }
  550. f, err := req.pathInfo(res)
  551. if err != nil {
  552. r.respond(500, err)
  553. }
  554. if info.IsDir() {
  555. _s = NewResource(f.URI)
  556. if !showEmpty {
  557. g.AddTriple(_s, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NewResource("http://www.w3.org/ns/ldp#BasicContainer"))
  558. g.AddTriple(_s, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NewResource("http://www.w3.org/ns/ldp#Container"))
  559. }
  560. kb := NewGraph(f.URI)
  561. kb.ReadFile(f.MetaFile)
  562. if kb.Len() > 0 {
  563. for _, st := range kb.All(_s, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), nil) {
  564. if st != nil && st.Object != nil {
  565. g.AddTriple(_s, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), st.Object)
  566. }
  567. }
  568. }
  569. } else {
  570. _s = NewResource(f.URI)
  571. if !showEmpty {
  572. g.AddTriple(_s, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NewResource("http://www.w3.org/ns/ldp#Resource"))
  573. // add type if RDF resource
  574. //infoUrl, _ := url.Parse(info.Name())
  575. guessType := f.FileType
  576. if guessType == "text/plain" {
  577. // open file and attempt to read the first line
  578. // Open an input file, exit on error.
  579. fd, err := os.Open(f.File)
  580. if err != nil {
  581. s.debug.Println("GET find mime type error:" + err.Error())
  582. }
  583. defer fd.Close()
  584. scanner := bufio.NewScanner(fd)
  585. // stop after the first line
  586. for scanner.Scan() {
  587. if strings.HasPrefix(scanner.Text(), "@prefix") || strings.HasPrefix(scanner.Text(), "@base") {
  588. kb := NewGraph(f.URI)
  589. kb.ReadFile(f.File)
  590. if kb.Len() > 0 {
  591. for _, st := range kb.All(NewResource(f.URI), NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), nil) {
  592. if st != nil && st.Object != nil {
  593. g.AddTriple(_s, NewResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), st.Object)
  594. }
  595. }
  596. }
  597. }
  598. break
  599. }
  600. // log potential errors
  601. if err := scanner.Err(); err != nil {
  602. s.debug.Println("GET scan err: " + scanner.Err().Error())
  603. }
  604. }
  605. }
  606. }
  607. if !showEmpty {
  608. g.AddTriple(_s, NewResource("http://www.w3.org/ns/posix/stat#mtime"), NewLiteral(fmt.Sprintf("%d", info.ModTime().Unix())))
  609. g.AddTriple(_s, NewResource("http://www.w3.org/ns/posix/stat#size"), NewLiteral(fmt.Sprintf("%d", info.Size())))
  610. }
  611. if showContainment {
  612. g.AddTriple(root, NewResource("http://www.w3.org/ns/ldp#contains"), _s)
  613. }
  614. }
  615. }
  616. }
  617. }
  618. status = 200
  619. maybeRDF = true
  620. }
  621. } else {
  622. magicType = resource.FileType
  623. maybeRDF = resource.MaybeRDF
  624. if len(mimeRdfExt[resource.Extension]) > 0 {
  625. maybeRDF = true
  626. }
  627. if !maybeRDF && magicType == "text/plain" {
  628. maybeRDF = true
  629. }
  630. s.debug.Println("Setting CType to:", magicType)
  631. status = 200
  632. if req.Method == "GET" && strings.Contains(contentType, "text/html") {
  633. // delete ETag to force load the app
  634. w.Header().Del("ETag")
  635. w.Header().Set("Link", brack(resource.MetaURI)+"; rel=\"meta\", "+brack(resource.AclURI)+"; rel=\"acl\"")
  636. if maybeRDF {
  637. w.Header().Set(HCType, contentType)
  638. s.debug.Println("Rendering data app")
  639. app, err := apps.DataApp()
  640. if err != nil {
  641. return r.respond(500, "")
  642. }
  643. return r.respond(200, app)
  644. }
  645. w.Header().Set(HCType, magicType)
  646. w.WriteHeader(200)
  647. f, err := os.Open(resource.File)
  648. if err == nil {
  649. defer func() {
  650. if err := f.Close(); err != nil {
  651. s.debug.Println("GET os.Open err: " + err.Error())
  652. }
  653. }()
  654. io.Copy(w, f)
  655. }
  656. return
  657. }
  658. }
  659. if status != 200 {
  660. return r.respond(status)
  661. }
  662. if req.Method == "HEAD" {
  663. w.Header().Set(HCType, contentType)
  664. return r.respond(status)
  665. }
  666. if !maybeRDF && len(magicType) > 0 {
  667. w.Header().Set(HCType, magicType)
  668. if status == 200 {
  669. f, err := os.Open(resource.File)
  670. if err == nil {
  671. defer func() {
  672. if err := f.Close(); err != nil {
  673. s.debug.Println("GET f.Close err:" + err.Error())
  674. }
  675. }()
  676. io.Copy(w, f)
  677. }
  678. } else {
  679. w.WriteHeader(status)
  680. }
  681. return
  682. }
  683. if maybeRDF {
  684. g.ReadFile(resource.File)
  685. w.Header().Set(HCType, contentType)
  686. }
  687. data := ""
  688. if Streaming {
  689. errCh := make(chan error, 8)
  690. go func() {
  691. rf, wf, err := os.Pipe()
  692. if err != nil {
  693. errCh <- err
  694. return
  695. }
  696. go func() {
  697. defer wf.Close()
  698. err := g.WriteFile(wf, contentType)
  699. if err != nil {
  700. errCh <- err
  701. }
  702. }()
  703. go func() {
  704. defer rf.Close()
  705. _, err := io.Copy(w, rf)
  706. if err != nil {
  707. errCh <- err
  708. } else {
  709. errCh <- nil
  710. }
  711. }()
  712. }()
  713. err = <-errCh
  714. } else {
  715. data, err = g.Serialize(contentType)
  716. }
  717. if err != nil {
  718. return r.respond(500, err)
  719. } else if len(data) > 0 {
  720. fmt.Fprint(w, data)
  721. }
  722. return
  723. case "PATCH":
  724. unlock := lock(resource.File)
  725. defer unlock()
  726. // check append first
  727. aclAppend, err := acl.AllowAppend(resource.URI)
  728. if aclAppend > 200 || err != nil {
  729. // check if we can write then
  730. aclWrite, err := acl.AllowWrite(resource.URI)
  731. if aclWrite > 200 || err != nil {
  732. return r.respond(aclWrite, handleStatusText(aclWrite, err))
  733. }
  734. }
  735. etag, _ := NewETag(resource.File)
  736. if !req.ifMatch("\"" + etag + "\"") {
  737. return r.respond(412, "412 - Precondition Failed")
  738. }
  739. if !req.ifNoneMatch("\"" + etag + "\"") {
  740. return r.respond(412, "412 - Precondition Failed")
  741. }
  742. if dataHasParser {
  743. s.debug.Println("Preparing to PATCH resource", resource.URI, "with file", resource.File)
  744. buf, _ := ioutil.ReadAll(req.Body)
  745. body := ioutil.NopCloser(bytes.NewBuffer(buf))
  746. req.Body.Close()
  747. if req.Header.Get("Content-Length") == "0" || len(buf) == 0 {
  748. errmsg := "Could not patch resource. No SPARQL statements found in the request."
  749. s.debug.Println(errmsg)
  750. return r.respond(400, errmsg)
  751. }
  752. g := NewGraph(resource.URI)
  753. g.ReadFile(resource.File)
  754. switch dataMime {
  755. case "application/json":
  756. g.JSONPatch(body)
  757. case "application/sparql-update":
  758. sparql := NewSPARQLUpdate(g.URI())
  759. sparql.Parse(body)
  760. ecode, err := g.SPARQLUpdate(sparql)
  761. if err != nil {
  762. return r.respond(ecode, "Error processing SPARQL Update: "+err.Error())
  763. }
  764. default:
  765. if dataHasParser {
  766. g.Parse(body, dataMime)
  767. }
  768. }
  769. if !resource.Exists {
  770. err = os.MkdirAll(_path.Dir(resource.File), 0755)
  771. if err != nil {
  772. s.debug.Println("PATCH MkdirAll err: " + err.Error())
  773. return r.respond(500, err)
  774. }
  775. }
  776. f, err := os.OpenFile(resource.File, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0664)
  777. if err != nil {
  778. s.debug.Println("PATCH os.OpenFile err: " + err.Error())
  779. return r.respond(500, err)
  780. }
  781. defer f.Close()
  782. err = g.WriteFile(f, "text/turtle")
  783. if err != nil {
  784. s.debug.Println("PATCH g.WriteFile err: " + err.Error())
  785. return r.respond(500, err)
  786. }
  787. s.debug.Println("Succefully PATCHed resource", resource.URI)
  788. onUpdateURI(resource.URI)
  789. onUpdateURI(resource.ParentURI)
  790. return r.respond(200)
  791. }
  792. case "POST":
  793. unlock := lock(resource.File)
  794. defer unlock()
  795. updateURI := resource.URI
  796. // check append first
  797. aclAppend, err := acl.AllowAppend(resource.URI)
  798. if aclAppend > 200 || err != nil {
  799. // check if we can write then
  800. aclWrite, err := acl.AllowWrite(resource.URI)
  801. if aclWrite > 200 || err != nil {
  802. return r.respond(aclWrite, handleStatusText(aclWrite, err))
  803. }
  804. }
  805. err = nil
  806. etag, _ := NewETag(resource.File)
  807. if !req.ifMatch("\"" + etag + "\"") {
  808. return r.respond(412, "412 - Precondition Failed")
  809. }
  810. if !req.ifNoneMatch("\"" + etag + "\"") {
  811. return r.respond(412, "412 - Precondition Failed")
  812. }
  813. // LDP
  814. isNew := false
  815. if resource.IsDir && dataMime != "multipart/form-data" {
  816. link := ParseLinkHeader(req.Header.Get("Link")).MatchRel("type")
  817. slug := req.Header.Get("Slug")
  818. uuid := NewUUID()
  819. uuid = uuid[:6]
  820. if !strings.HasSuffix(resource.Path, "/") {
  821. resource.Path += "/"
  822. }
  823. if len(slug) > 0 {
  824. if strings.HasPrefix(slug, "/") {
  825. slug = strings.TrimLeft(slug, "/")
  826. }
  827. if strings.HasSuffix(slug, "/") {
  828. slug = strings.TrimRight(slug, "/")
  829. }
  830. st, err := os.Stat(resource.File + slug)
  831. //@@TODO append a random string
  832. if st != nil && !os.IsNotExist(err) {
  833. slug += "-" + uuid
  834. }
  835. } else {
  836. slug = uuid
  837. }
  838. resource.Path += slug
  839. if len(link) > 0 && link == "http://www.w3.org/ns/ldp#BasicContainer" {
  840. if !strings.HasSuffix(resource.Path, "/") {
  841. resource.Path += "/"
  842. }
  843. resource, err = req.pathInfo(resource.Base + "/" + resource.Path)
  844. if err != nil {
  845. s.debug.Println("POST LDPC req.pathInfo err: " + err.Error())
  846. return r.respond(500, err)
  847. }
  848. w.Header().Set("Location", resource.URI)
  849. w.Header().Set("Link", brack(resource.MetaURI)+"; rel=\"meta\", "+brack(resource.AclURI)+"; rel=\"acl\"")
  850. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#Resource")+"; rel=\"type\"")
  851. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#BasicContainer")+"; rel=\"type\"")
  852. err = os.MkdirAll(resource.File, 0755)
  853. if err != nil {
  854. s.debug.Println("POST LDPC os.MkdirAll err: " + err.Error())
  855. return r.respond(500, err)
  856. }
  857. s.debug.Println("Created dir " + resource.File)
  858. buf := new(bytes.Buffer)
  859. buf.ReadFrom(req.Body)
  860. if buf.Len() > 0 {
  861. f, err := os.OpenFile(resource.MetaFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  862. if err != nil {
  863. s.debug.Println("POST LDPC os.OpenFile err: " + err.Error())
  864. return r.respond(500, err)
  865. }
  866. defer f.Close()
  867. _, err = io.Copy(f, buf)
  868. if err != nil {
  869. s.debug.Println("POST io.Copy err: " + err.Error())
  870. }
  871. }
  872. w.Header().Set("Location", resource.URI)
  873. onUpdateURI(resource.URI)
  874. onUpdateURI(resource.ParentURI)
  875. return r.respond(201)
  876. }
  877. resource, err = req.pathInfo(resource.Base + "/" + resource.Path)
  878. if err != nil {
  879. s.debug.Println("POST LDPR req.pathInfo err: " + err.Error())
  880. return r.respond(500, err)
  881. }
  882. w.Header().Set("Location", resource.URI)
  883. w.Header().Set("Link", brack(resource.MetaURI)+"; rel=\"meta\", "+brack(resource.AclURI)+"; rel=\"acl\"")
  884. // LDP header
  885. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#Resource")+"; rel=\"type\"")
  886. isNew = true
  887. }
  888. if !resource.Exists {
  889. err = os.MkdirAll(_path.Dir(resource.File), 0755)
  890. if err != nil {
  891. s.debug.Println("POST MkdirAll err: " + err.Error())
  892. return r.respond(500, err)
  893. }
  894. s.debug.Println("Created resource " + _path.Dir(resource.File))
  895. }
  896. if dataMime == "multipart/form-data" {
  897. err := req.ParseMultipartForm(100000)
  898. if err != nil {
  899. s.debug.Println("POST parse multipart data err: " + err.Error())
  900. } else {
  901. m := req.MultipartForm
  902. for elt := range m.File {
  903. files := m.File[elt]
  904. for i := range files {
  905. file, err := files[i].Open()
  906. defer file.Close()
  907. if err != nil {
  908. s.debug.Println("POST multipart/form f.Open err: " + err.Error())
  909. return r.respond(500, err)
  910. }
  911. newFile := ""
  912. if filepath.Base(resource.Path) == files[i].Filename {
  913. newFile = resource.File
  914. } else {
  915. newFile = resource.File + files[i].Filename
  916. }
  917. dst, err := os.OpenFile(newFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  918. defer dst.Close()
  919. if err != nil {
  920. s.debug.Println("POST multipart/form os.Create err: " + err.Error())
  921. return r.respond(500, err)
  922. }
  923. if _, err := io.Copy(dst, file); err != nil {
  924. s.debug.Println("POST multipart/form io.Copy err: " + err.Error())
  925. return r.respond(500, err)
  926. }
  927. location := &url.URL{Path: files[i].Filename}
  928. w.Header().Add("Location", resource.URI+location.String())
  929. }
  930. }
  931. onUpdateURI(resource.URI)
  932. return r.respond(201)
  933. }
  934. } else {
  935. if !resource.Exists {
  936. isNew = true
  937. }
  938. if resource.IsDir {
  939. resource.File = resource.File + "/" + s.Config.MetaSuffix
  940. }
  941. if dataHasParser {
  942. g := NewGraph(resource.URI)
  943. g.ReadFile(resource.File)
  944. switch dataMime {
  945. case "application/json":
  946. g.JSONPatch(req.Body)
  947. case "application/sparql-update":
  948. sparql := NewSPARQLUpdate(g.URI())
  949. sparql.Parse(req.Body)
  950. ecode, err := g.SPARQLUpdate(sparql)
  951. if err != nil {
  952. println(err.Error())
  953. return r.respond(ecode, "Error processing SPARQL Update: "+err.Error())
  954. }
  955. default:
  956. g.Parse(req.Body, dataMime)
  957. }
  958. f, err := os.OpenFile(resource.File, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  959. if err != nil {
  960. s.debug.Println("POST os.OpenFile err: " + err.Error())
  961. return r.respond(500, err.Error())
  962. }
  963. defer f.Close()
  964. if g.Len() > 0 {
  965. err = g.WriteFile(f, "text/turtle")
  966. if err != nil {
  967. s.debug.Println("POST g.WriteFile err: " + err.Error())
  968. } else {
  969. s.debug.Println("Wrote resource file: " + resource.File)
  970. }
  971. }
  972. } else {
  973. f, err := os.OpenFile(resource.File, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  974. if err != nil {
  975. s.debug.Println("POST os.OpenFile err: " + err.Error())
  976. return r.respond(500, err.Error())
  977. }
  978. defer f.Close()
  979. _, err = io.Copy(f, req.Body)
  980. if err != nil {
  981. s.debug.Println("POST os.OpenFile err: " + err.Error())
  982. return r.respond(500, err.Error())
  983. }
  984. }
  985. onUpdateURI(updateURI)
  986. if updateURI != resource.ParentURI {
  987. onUpdateURI(resource.ParentURI)
  988. }
  989. if isNew {
  990. return r.respond(201)
  991. }
  992. return r.respond(200)
  993. }
  994. case "PUT":
  995. unlock := lock(resource.File)
  996. defer unlock()
  997. // LDP header
  998. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#Resource")+"; rel=\"type\"")
  999. // check append first
  1000. aclAppend, err := acl.AllowAppend(resource.URI)
  1001. if aclAppend > 200 || err != nil {
  1002. // check if we can write then
  1003. aclWrite, err := acl.AllowWrite(resource.URI)
  1004. if aclWrite > 200 || err != nil {
  1005. return r.respond(aclWrite, handleStatusText(aclWrite, err))
  1006. }
  1007. }
  1008. etag, _ := NewETag(resource.File)
  1009. if !req.ifMatch("\"" + etag + "\"") {
  1010. return r.respond(412, "412 - Precondition Failed")
  1011. }
  1012. if !req.ifNoneMatch("\"" + etag + "\"") {
  1013. return r.respond(412, "412 - Precondition Failed")
  1014. }
  1015. isNew := true
  1016. if resource.Exists {
  1017. isNew = false
  1018. }
  1019. // LDP PUT should be merged with LDP POST into a common LDP "method" switch
  1020. link := ParseLinkHeader(req.Header.Get("Link")).MatchRel("type")
  1021. if len(link) > 0 && link == "http://www.w3.org/ns/ldp#BasicContainer" {
  1022. err := os.MkdirAll(resource.File, 0755)
  1023. if err != nil {
  1024. s.debug.Println("PUT MkdirAll err: " + err.Error())
  1025. return r.respond(500, err)
  1026. }
  1027. // refresh resource and set the right headers
  1028. resource, err = req.pathInfo(resource.URI)
  1029. w.Header().Set("Link", brack(resource.MetaURI)+"; rel=\"meta\", "+brack(resource.AclURI)+"; rel=\"acl\"")
  1030. // LDP header
  1031. w.Header().Add("Link", brack("http://www.w3.org/ns/ldp#Resource")+"; rel=\"type\"")
  1032. onUpdateURI(resource.URI)
  1033. onUpdateURI(resource.ParentURI)
  1034. return r.respond(201)
  1035. }
  1036. err = os.MkdirAll(_path.Dir(resource.File), 0755)
  1037. if err != nil {
  1038. s.debug.Println("PUT MkdirAll err: " + err.Error())
  1039. return r.respond(500, err)
  1040. }
  1041. f, err := os.OpenFile(resource.File, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  1042. if err != nil {
  1043. s.debug.Println("PUT os.OpenFile err: " + err.Error())
  1044. if resource.IsDir {
  1045. w.Header().Add("Link", brack(resource.URI)+"; rel=\"describedby\"")
  1046. return r.respond(406, "406 - Cannot use PUT on a directory.")
  1047. }
  1048. return r.respond(500, err)
  1049. }
  1050. defer f.Close()
  1051. _, err = io.Copy(f, req.Body)
  1052. if err != nil {
  1053. s.debug.Println("PUT io.Copy err: " + err.Error())
  1054. }
  1055. if err != nil {
  1056. return r.respond(500, err)
  1057. }
  1058. w.Header().Set("Location", resource.URI)
  1059. onUpdateURI(resource.URI)
  1060. onUpdateURI(resource.ParentURI)
  1061. if isNew {
  1062. return r.respond(201)
  1063. }
  1064. return r.respond(200)
  1065. case "DELETE":
  1066. unlock := lock(resource.Path)
  1067. defer unlock()
  1068. aclWrite, err := acl.AllowWrite(resource.URI)
  1069. if aclWrite > 200 || err != nil {
  1070. return r.respond(aclWrite, handleStatusText(aclWrite, err))
  1071. }
  1072. if len(resource.Path) == 0 {
  1073. return r.respond(500, "500 - Cannot DELETE root (/)")
  1074. }
  1075. // remove ACL and meta files first
  1076. if resource.File != resource.AclFile {
  1077. _ = os.Remove(resource.AclFile)
  1078. }
  1079. if resource.File != resource.MetaFile {
  1080. _ = os.Remove(resource.MetaFile)
  1081. }
  1082. err = os.Remove(resource.File)
  1083. if err != nil {
  1084. if os.IsNotExist(err) {
  1085. return r.respondNotFound()
  1086. }
  1087. return r.respond(500, err)
  1088. }
  1089. _, err = os.Stat(resource.File)
  1090. if err == nil {
  1091. return r.respond(409, err)
  1092. }
  1093. onDeleteURI(resource.URI)
  1094. onUpdateURI(resource.ParentURI)
  1095. return
  1096. case "MKCOL":
  1097. unlock := lock(resource.File)
  1098. defer unlock()
  1099. aclWrite, err := acl.AllowWrite(resource.URI)
  1100. if aclWrite > 200 || err != nil {
  1101. return r.respond(aclWrite, handleStatusText(aclWrite, err))
  1102. }
  1103. err = os.MkdirAll(resource.File, 0755)
  1104. if err != nil {
  1105. switch err.(type) {
  1106. case *os.PathError:
  1107. return r.respond(409, err)
  1108. default:
  1109. return r.respond(500, err)
  1110. }
  1111. } else {
  1112. _, err := os.Stat(resource.File)
  1113. if err != nil {
  1114. return r.respond(409, err)
  1115. }
  1116. }
  1117. onUpdateURI(resource.URI)
  1118. onUpdateURI(resource.ParentURI)
  1119. return r.respond(201)
  1120. case "COPY", "MOVE", "LOCK", "UNLOCK":
  1121. aclWrite, err := acl.AllowWrite(resource.URI)
  1122. if aclWrite > 200 || err != nil {
  1123. return r.respond(aclWrite, handleStatusText(aclWrite, err))
  1124. }
  1125. s.webdav.ServeHTTP(w, req.Request)
  1126. default:
  1127. return r.respond(405, "405 - Method Not Allowed:", req.Method)
  1128. }
  1129. return
  1130. }