package forge
import (
"errors"
"html/template"
"net/http"
"os"
"path/filepath"
"strings"
)
const renameSchema = `
create table if not exists renames (
old text primary key,
user integer not null references users(id) on delete cascade
);
`
func (s *Store) RenameUser(id int64, old, name string) error {
if err := checkName(name); err != nil {
return err
}
tx, err := s.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Exec(`update users set name = ? where id = ?`, name, id); err != nil {
if strings.Contains(err.Error(), "UNIQUE") {
return errors.New("That name is taken")
}
return err
}
tx.Exec(`delete from renames where old = ?`, name)
if _, err := tx.Exec(`insert into renames(old, user) values (?, ?) on conflict(old) do update set user = excluded.user`, old, id); err != nil {
return err
}
return tx.Commit()
}
func (s *Store) Renamed(old string) (User, bool) {
var id int64
if s.db.QueryRow(`select r.user from renames r where r.old = ? and not exists (select 1 from users where name = r.old)`, old).Scan(&id) != nil {
return User{}, false
}
u, err := s.userByID(id)
return u, err == nil
}
func (s *Server) rename(w http.ResponseWriter, r *http.Request) {
u := s.require(w, r)
if u == nil {
return
}
fail := func(msg string) {
http.Redirect(w, r, "/settings?error="+template.URLQueryEscaper(msg)+"#username", http.StatusSeeOther)
}
name := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(r.FormValue("name")), "~")))
if name == u.Name {
http.Redirect(w, r, "/settings", http.StatusSeeOther)
return
}
if _, ok := s.store.CheckPassword(u.Name, r.FormValue("password")); !ok {
s.limiter.fail(clientIP(r))
fail("Password is wrong")
return
}
if err := s.store.RenameUser(u.ID, u.Name, name); err != nil {
fail(err.Error())
return
}
from, to := filepath.Join(s.root, "repos", u.Name), filepath.Join(s.root, "repos", name)
os.Remove(to)
if err := os.Rename(from, to); err != nil && !os.IsNotExist(err) {
s.store.RenameUser(u.ID, name, u.Name)
fail("Could not move your repositories")
return
}
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}