// Walk inside the source/reference repository func (s *scan) walkSource(path string, f os.FileInfo, err error) error { if f == nil || f.IsDir() || f.Mode()&os.ModeSymlink != 0 { return nil } d := new(filedata) d.path = path[len(GetConfig().Repository):] d.size = f.Size() d.modTime = f.ModTime() // Get the previous file properties properties, err := redis.Strings(s.walkRedisConn.Do("HMGET", fmt.Sprintf("FILE_%s", d.path), "size", "modTime", "sha1", "sha256", "md5")) if err != nil && err != redis.ErrNil { return err } else if len(properties) < 5 { // This will force a rehash properties = make([]string, 5) } size, _ := strconv.ParseInt(properties[0], 10, 64) modTime, _ := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", properties[1]) sha1 := properties[2] sha256 := properties[3] md5 := properties[4] rehash := (GetConfig().Hashes.SHA1 && len(sha1) == 0) || (GetConfig().Hashes.SHA256 && len(sha256) == 0) || (GetConfig().Hashes.MD5 && len(md5) == 0) if rehash || size != d.size || !modTime.Equal(d.modTime) { h, err := filesystem.HashFile(GetConfig().Repository + d.path) if err != nil { log.Warningf("%s: hashing failed: %s", d.path, err.Error()) } else { d.sha1 = h.Sha1 d.sha256 = h.Sha256 d.md5 = h.Md5 if len(d.sha1) > 0 { log.Infof("%s: SHA1 %s", d.path, d.sha1) } if len(d.sha256) > 0 { log.Infof("%s: SHA256 %s", d.path, d.sha256) } if len(d.md5) > 0 { log.Infof("%s: MD5 %s", d.path, d.md5) } } } else { d.sha1 = sha1 d.sha256 = sha256 d.md5 = md5 } s.walkSourceFiles = append(s.walkSourceFiles, d) return nil }
func (c *cli) CmdEdit(args ...string) error { cmd := SubCmd("edit", "[IDENTIFIER]", "Edit a mirror") if err := cmd.Parse(args); err != nil { return nil } if cmd.NArg() != 1 { cmd.Usage() return nil } // Find the editor to use editor := os.Getenv("EDITOR") if editor == "" { log.Fatal("Environment variable $EDITOR not set") } // Guess which mirror to use list, err := c.matchMirror(cmd.Arg(0)) if err != nil { return err } if len(list) == 0 { fmt.Fprintf(os.Stderr, "No match for %s\n", cmd.Arg(0)) return nil } else if len(list) > 1 { for _, e := range list { fmt.Fprintf(os.Stderr, "%s\n", e) } return nil } id := list[0] // Connect to the database r := database.NewRedis() conn, err := r.Connect() if err != nil { log.Fatal("Redis: ", err) } defer conn.Close() // Get the mirror information key := fmt.Sprintf("MIRROR_%s", id) m, err := redis.Values(conn.Do("HGETALL", key)) if err != nil { fmt.Fprintf(os.Stderr, "Cannot fetch mirror details: %s\n", err) return err } var mirror mirrors.Mirror err = redis.ScanStruct(m, &mirror) if err != nil { return err } // Generate a yaml configuration string from the struct out, err := yaml.Marshal(mirror) // Open a temporary file f, err := ioutil.TempFile(os.TempDir(), "edit") if err != nil { log.Fatal("Cannot create temporary file:", err) } defer os.Remove(f.Name()) f.WriteString("# You can now edit this mirror configuration.\n" + "# Just save and quit when you're done.\n\n") f.WriteString(string(out)) f.WriteString(fmt.Sprintf("\n%s\n\n%s\n", commentSeparator, mirror.Comment)) f.Close() // Checksum the original file chk, _ := filesystem.HashFile(f.Name()) reopen: // Launch the editor with the filename as first parameter exe := exec.Command(editor, f.Name()) exe.Stdin = os.Stdin exe.Stdout = os.Stdout exe.Stderr = os.Stderr err = exe.Run() if err != nil { log.Fatal(err) } // Read the file back out, err = ioutil.ReadFile(f.Name()) if err != nil { log.Fatal("Cannot read file", f.Name()) } // Checksum the file back and compare chk2, _ := filesystem.HashFile(f.Name()) if chk == chk2 { fmt.Println("Aborted") return nil } var ( yamlstr string = string(out) comment string ) commentIndex := strings.Index(yamlstr, commentSeparator) if commentIndex > 0 { comment = strings.TrimSpace(yamlstr[commentIndex+len(commentSeparator):]) yamlstr = yamlstr[:commentIndex] } // Fill the struct from the yaml err = yaml.Unmarshal([]byte(yamlstr), &mirror) if err != nil { eagain: fmt.Printf("%s\nRetry? [Y/n]", err.Error()) reader := bufio.NewReader(os.Stdin) s, _ := reader.ReadString('\n') switch s[0] { case 'y', 'Y', 10: goto reopen case 'n', 'N': fmt.Println("Aborted") return nil default: goto eagain } } // Reformat contry codes mirror.CountryCodes = strings.Replace(mirror.CountryCodes, ",", " ", -1) ccodes := strings.Fields(mirror.CountryCodes) mirror.CountryCodes = "" for _, c := range ccodes { mirror.CountryCodes += strings.ToUpper(c) + " " } mirror.CountryCodes = strings.TrimRight(mirror.CountryCodes, " ") // Reformat continent code //FIXME sanitize mirror.ContinentCode = strings.ToUpper(mirror.ContinentCode) // Normalize URLs if mirror.HttpURL != "" { mirror.HttpURL = utils.NormalizeURL(mirror.HttpURL) } if mirror.RsyncURL != "" { mirror.RsyncURL = utils.NormalizeURL(mirror.RsyncURL) } if mirror.FtpURL != "" { mirror.FtpURL = utils.NormalizeURL(mirror.FtpURL) } mirror.Comment = comment // Save the values back into redis _, err = conn.Do("HMSET", key, "ID", id, "http", mirror.HttpURL, "rsync", mirror.RsyncURL, "ftp", mirror.FtpURL, "sponsorName", mirror.SponsorName, "sponsorURL", mirror.SponsorURL, "sponsorLogo", mirror.SponsorLogoURL, "adminName", mirror.AdminName, "adminEmail", mirror.AdminEmail, "customData", mirror.CustomData, "continentOnly", mirror.ContinentOnly, "countryOnly", mirror.CountryOnly, "asOnly", mirror.ASOnly, "score", mirror.Score, "latitude", mirror.Latitude, "longitude", mirror.Longitude, "continentCode", mirror.ContinentCode, "countryCodes", mirror.CountryCodes, "asnum", mirror.Asnum, "comment", mirror.Comment, "enabled", mirror.Enabled) if err != nil { log.Fatal("Couldn't save the configuration into redis:", err) } // Publish update database.Publish(conn, database.MIRROR_UPDATE, id) fmt.Println("Mirror edited successfully") return nil }