package repo import ( "bufio" "bytes" "fmt" "io" "net/http" "os" "path" "path/filepath" "strings" "github.com/G-Node/gogs/internal/conf" "github.com/G-Node/gogs/internal/context" "github.com/G-Node/gogs/internal/tool" "github.com/G-Node/libgin/libgin" "github.com/gogs/git-module" log "gopkg.in/clog.v1" "gopkg.in/yaml.v2" ) func serveAnnexedData(ctx *context.Context, name string, buf []byte) error { keyparts := strings.Split(strings.TrimSpace(string(buf)), "/") key := keyparts[len(keyparts)-1] contentPath, err := git.NewCommand("annex", "contentlocation", key).RunInDir(ctx.Repo.Repository.RepoPath()) if err != nil { log.Error(2, "Failed to find content location for file %q with key %q", name, key) return err } // always trim space from output for git command contentPath = bytes.TrimSpace(contentPath) return serveAnnexedKey(ctx, name, string(contentPath)) } func serveAnnexedKey(ctx *context.Context, name string, contentPath string) error { fullContentPath := filepath.Join(ctx.Repo.Repository.RepoPath(), contentPath) annexfp, err := os.Open(fullContentPath) if err != nil { log.Error(2, "Failed to open annex file at %q: %s", fullContentPath, err.Error()) return err } defer annexfp.Close() annexReader := bufio.NewReader(annexfp) info, err := annexfp.Stat() if err != nil { log.Error(2, "Failed to stat file at %q: %s", fullContentPath, err.Error()) return err } buf, _ := annexReader.Peek(1024) ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", info.Size())) if !tool.IsTextFile(buf) { if !tool.IsImageFile(buf) { ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"") ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") } } else if !conf.Repository.EnableRawFileRenderMode || !ctx.QueryBool("render") { ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8") } log.Trace("Serving annex content for %q: %q", name, contentPath) if ctx.Req.Method == http.MethodHead { // Skip content copy when request method is HEAD log.Trace("Returning header: %+v", ctx.Resp.Header()) return nil } _, err = io.Copy(ctx.Resp, annexReader) return err } func readDataciteFile(c *context.Context) { log.Trace("Reading datacite.yml file") entry, err := c.Repo.Commit.Blob("/datacite.yml") if err != nil || entry == nil { log.Error(2, "datacite.yml blob could not be retrieved: %v", err) c.Data["HasDataCite"] = false return } buf, err := entry.Bytes() if err != nil { log.Error(2, "datacite.yml data could not be read: %v", err) c.Data["HasDataCite"] = false return } doiInfo := libgin.DOIRegInfo{} err = yaml.Unmarshal(buf, &doiInfo) if err != nil { log.Error(2, "datacite.yml data could not be unmarshalled: %v", err) c.Data["HasDataCite"] = false return } c.Data["DOIInfo"] = &doiInfo } // resolveAnnexedContent takes a buffer with the contents of a git-annex // pointer file and an io.Reader for the underlying file and returns the // corresponding buffer and a bufio.Reader for the underlying content file. // The returned byte slice and bufio.Reader can be used to replace the buffer // and io.Reader sent in through the caller so that any existing code can use // the two variables without modifications. // Any errors that occur during processing are stored in the provided context. // The FileSize of the annexed content is also saved in the context (c.Data["FileSize"]). func resolveAnnexedContent(c *context.Context, buf []byte) ([]byte, error) { if !tool.IsAnnexedFile(buf) { // not an annex pointer file; return as is return buf, nil } log.Trace("Annexed file requested: Resolving content for %q", bytes.TrimSpace(buf)) keyparts := strings.Split(strings.TrimSpace(string(buf)), "/") key := keyparts[len(keyparts)-1] contentPath, err := git.NewCommand("annex", "contentlocation", key).RunInDir(c.Repo.Repository.RepoPath()) if err != nil { log.Error(2, "Failed to find content location for key %q", key) c.Data["IsAnnexedFile"] = true return buf, err } // always trim space from output for git command contentPath = bytes.TrimSpace(contentPath) afp, err := os.Open(filepath.Join(c.Repo.Repository.RepoPath(), string(contentPath))) if err != nil { log.Trace("Could not open annex file: %v", err) c.Data["IsAnnexedFile"] = true return buf, err } info, err := afp.Stat() if err != nil { log.Trace("Could not stat annex file: %v", err) c.Data["IsAnnexedFile"] = true return buf, err } annexDataReader := bufio.NewReader(afp) annexBuf := make([]byte, 1024) n, _ := annexDataReader.Read(annexBuf) annexBuf = annexBuf[:n] c.Data["FileSize"] = info.Size() log.Trace("Annexed file size: %d B", info.Size()) return annexBuf, nil } func GitConfig(c *context.Context) { log.Trace("RepoPath: %+v", c.Repo.Repository.RepoPath()) configFilePath := path.Join(c.Repo.Repository.RepoPath(), "config") log.Trace("Serving file %q", configFilePath) if _, err := os.Stat(configFilePath); err != nil { c.ServerError("GitConfig", err) return } c.ServeFileContent(configFilePath, "config") } func AnnexGetKey(c *context.Context) { filename := c.Params(":keyfile") key := c.Params(":key") contentPath := filepath.Join("annex/objects", c.Params(":hashdira"), c.Params(":hashdirb"), key, filename) log.Trace("Git annex requested key %q: %q", key, contentPath) err := serveAnnexedKey(c, filename, contentPath) if err != nil { c.ServerError("AnnexGetKey", err) } }