<commit_before>package main

import (
	"fmt"
	"gopkg.in/telegram-bot-api.v4"
	"time"
	//"os/user"
)

func regpi(msg *tgbotapi.Message, update tgbotapi.Update) {
	var reply tgbotapi.MessageConfig
	var user User

	userId := msg.From.ID
	groupId := msg.Chat.ID

	gdb.Where("userId = ? AND groupId = ?", userId, groupId).First(&user)

	if len(msg.From.UserName) == 0 {
		reply = tgbotapi.NewMessage(msg.Chat.ID, "A girl has no name.")
	} else if user.Id == 0 {
		user.Username = "@" + msg.From.UserName
		user.UserId = msg.From.ID
		user.GroupID = int(groupId)
		user.Score = 0
		user.Usernick = fmt.Sprintf("%s %s", msg.From.FirstName, msg.From.LastName)
		user.Quota = 6
		gdb.Create(&user)
		gdb.Model(&user).Update(User{Quota: 6})
		reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Ты регнулся, [%s](tg://user?id=%d)\n", user.Usernick, user.UserId))
		reply.ParseMode = tgbotapi.ModeMarkdown
	} else {
		reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprint("Эй, ты уже в игре!"))
	}

	reply.ReplyToMessageID = update.Message.MessageID
	bot.Send(reply)
}

func showpid(msg *tgbotapi.Message) {
	var users []User

	groupId := msg.Chat.ID

	gdb.Where("groupId = ?", groupId).Find(&users)

	if len(users) != 0 {
		output := "Кандидаты в пидоры дня:\n"
		for _, i := range users {
			if len(i.Usernick) > 0 {
				output += fmt.Sprintf("[%s](tg://user?id=%d)\n", i.Usernick, i.UserId)
			} else {
				output += fmt.Sprintf("[%s](tg://user?id=%d)\n", i.Username[1:], i.UserId)
			}
		}
		output += "Хочешь себя увидеть тут?\nЖми /regpi"
		reply := tgbotapi.NewMessage(msg.Chat.ID, output)
		reply.ParseMode = tgbotapi.ModeMarkdown
		bot.Send(reply)
	} else {
		output := "Пидоров нет! Будь первым! Жми /regpi"
		bot.Send(tgbotapi.NewMessage(msg.Chat.ID, output))
	}
}

func pidorStat(msg *tgbotapi.Message) {
	titles := map[int]string{
		1 : "Пидоратор",
		2 : "Пидороль",
		3 : "Герцопидор",
		4 : "Пиркиз",
		5 : "Пидорон",
	}

	var users []User
	var reply tgbotapi.MessageConfig
	var flag bool
	var currentUserName string

	groupId := msg.Chat.ID
	counter := 0
	var titlesCounter int

	gdb.Where("groupId = ?", groupId).Order("score desc").Find(&users)

	output := "Статистика (первые 5):\n"
	titlesCounter = 1
	for _, i := range users {
		if i.Score != 0 {
			if len(i.Usernick) > 0 {
				currentUserName = i.Usernick
			} else {
				currentUserName = i.Username[1:]
			}

			output += fmt.Sprintf("[%s](tg://user?id=%d) - %d (%s)\n", currentUserName, i.UserId, i.Score, titles[titlesCounter])
			titlesCounter++
			flag = true

			if counter == 5 {
				break
			} else {
				counter++
			}
		}
	}

	if flag {
		reply = tgbotapi.NewMessage(msg.Chat.ID, output)
		reply.ParseMode = tgbotapi.ModeMarkdown
	} else {
		reply = tgbotapi.NewMessage(msg.Chat.ID, "Пидор дня еще ни разу не был выбран! Жми /pidor")
	}

	bot.Send(reply)
}

func startQuiz(msg *tgbotapi.Message) {
	firstPhrases := []string {
		"Инициализирую поиск пидора дня...",
		"Внимание, ищу пидора!",
		"Ну-ка дай-ка...",
		"Такс, кто тут у нас мало каши ел?",
		"Инициализация.Поиск.",
	}

	secondPhrases := []string {
		"Кажется я что-то вижу!",
		"Не может быть!",
		"Пожалуй препроверю...",
		"Найден!",
		"Прям по Бабичу!",
		"Как предсказал Великий Мейстер...",
	}

	var reply tgbotapi.MessageConfig
	var theUser User
	var users []User
	var randomUser int
	var currentUserName string
	var winner User
	var winnerScore int
	var available Available

	groupId := msg.Chat.ID

	gdb.Where("groupId = ?", groupId).Find(&users)
	gdb.Where("groupId = ?", groupId).First(&available)

	rowsCounted := len(users)
	if rowsCounted == 0 {
		reply = tgbotapi.NewMessage(msg.Chat.ID, "Нет участников! Жми /regpi")
		bot.Send(reply)
	} else {
		if available.Flag {
			lenOfCurrentUsers := len(users)
			if lenOfCurrentUsers == 1 {
				randomUser = 0
			} else {
				randomUser = random(0, lenOfCurrentUsers - 1)
			}

			gdb.Where("id = ?", users[randomUser].Id).First(&winner)

			reply = tgbotapi.NewMessage(msg.Chat.ID, firstPhrases[random(0, len(firstPhrases) - 1)])
			bot.Send(reply)
			time.Sleep(time.Second * 2)
			reply = tgbotapi.NewMessage(msg.Chat.ID, secondPhrases[random(0, len(secondPhrases) - 1)])
			bot.Send(reply)
			gdb.Where("id = ? and groupId = ?", theUser, groupId).First(&winner)
			winnerScore = winner.Score + 1
			gdb.Model(&users).Where("id = ?", winner.Id).UpdateColumn("score", winnerScore)
			time.Sleep(time.Second * 2)
			if len(winner.Usernick) > 0 {
				currentUserName = winner.Usernick
			} else {
				currentUserName = winner.Username
			}
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Ага! 🎉🎉🎉 Сегодня пидор - [%s](tg://user?id=%d)", currentUserName, winner.UserId))
			reply.ParseMode = tgbotapi.ModeMarkdown
			bot.Send(reply)
			gdb.Model(&available).Where("groupId = ?", groupId).Update("flag", false)
			gdb.Model(&available).Where("groupId = ?", groupId).Update("userId", winner.Id)
		} else {
			var currentUser User
			gdb.Where("id = ?", available.UserId).First(&currentUser)
			if len(currentUser.Usernick) > 0 {
				currentUserName = currentUser.Usernick
			} else {
				currentUserName = currentUser.Username[1:]
			}
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("🎉Сегодня у нас уже есть победитель - [%s](tg://user?id=%d)🎉", currentUserName, currentUser.UserId))
			reply.ParseMode = tgbotapi.ModeMarkdown
			bot.Send(reply)
		}
	}
}

func kekogen(msg *tgbotapi.Message) {
	var reply tgbotapi.MessageConfig
	var user User

	userId := msg.From.ID
	groupId := msg.Chat.ID

	gdb.Where("userId = ? and groupId = ?", userId, groupId).First(&user)
	currentQuota := user.Quota

	if user.Id > 0 {
		if currentQuota > 1 {
			vowels := []string {
				"а", "о", "и", "е", "у",  "я",
			}
			consonants := []string {
				"в", "д", "к", "л", "м", "н", "п", "р", "с", "т", "ф", "х", "ш",
			}
			result := "кек"

			for x:= 0; x < 5; x++ {
				if x % 2 == 0 {
					result += vowels[random(0, len(vowels) - 1)]
				} else {
					result += consonants[random(0, len(consonants) - 1)]
				}
			}

			gdb.Model(&user).Update(User{Quota: currentQuota - 1})
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf(result))
		} else {
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Твои кеки на сегодня кончились!"))
		}
	} else {
		reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Жми /regpi чтобы зарегистрироваться и получить свои кеки!"))
	}

	bot.Send(reply)
}<commit_msg>fix stat<commit_after>package main

import (
	"fmt"
	"gopkg.in/telegram-bot-api.v4"
	"time"
	//"os/user"
)

func regpi(msg *tgbotapi.Message, update tgbotapi.Update) {
	var reply tgbotapi.MessageConfig
	var user User

	userId := msg.From.ID
	groupId := msg.Chat.ID

	gdb.Where("userId = ? AND groupId = ?", userId, groupId).First(&user)

	if len(msg.From.UserName) == 0 {
		reply = tgbotapi.NewMessage(msg.Chat.ID, "A girl has no name.")
	} else if user.Id == 0 {
		user.Username = "@" + msg.From.UserName
		user.UserId = msg.From.ID
		user.GroupID = int(groupId)
		user.Score = 0
		user.Usernick = fmt.Sprintf("%s %s", msg.From.FirstName, msg.From.LastName)
		user.Quota = 6
		gdb.Create(&user)
		gdb.Model(&user).Update(User{Quota: 6})
		reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Ты регнулся, [%s](tg://user?id=%d)\n", user.Usernick, user.UserId))
		reply.ParseMode = tgbotapi.ModeMarkdown
	} else {
		reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprint("Эй, ты уже в игре!"))
	}

	reply.ReplyToMessageID = update.Message.MessageID
	bot.Send(reply)
}

func showpid(msg *tgbotapi.Message) {
	var users []User

	groupId := msg.Chat.ID

	gdb.Where("groupId = ?", groupId).Find(&users)

	if len(users) != 0 {
		output := "Кандидаты в пидоры дня:\n"
		for _, i := range users {
			if len(i.Usernick) > 0 {
				output += fmt.Sprintf("[%s](tg://user?id=%d)\n", i.Usernick, i.UserId)
			} else {
				output += fmt.Sprintf("[%s](tg://user?id=%d)\n", i.Username[1:], i.UserId)
			}
		}
		output += "Хочешь себя увидеть тут?\nЖми /regpi"
		reply := tgbotapi.NewMessage(msg.Chat.ID, output)
		reply.ParseMode = tgbotapi.ModeMarkdown
		bot.Send(reply)
	} else {
		output := "Пидоров нет! Будь первым! Жми /regpi"
		bot.Send(tgbotapi.NewMessage(msg.Chat.ID, output))
	}
}

func pidorStat(msg *tgbotapi.Message) {
	titles := map[int]string{
		1 : "Пидоратор",
		2 : "Пидороль",
		3 : "Герцопидор",
		4 : "Пиркиз",
		5 : "Пидорон",
	}

	var users []User
	var reply tgbotapi.MessageConfig
	var flag bool
	var currentUserName string

	groupId := msg.Chat.ID
	counter := 0
	var titlesCounter int

	gdb.Where("groupId = ?", groupId).Order("score desc").Find(&users)

	output := "Статистика (первые 5):\n"
	titlesCounter = 1
	for _, i := range users {
		if i.Score != 0 {
			if len(i.Usernick) > 0 {
				currentUserName = i.Usernick
			} else {
				currentUserName = i.Username[1:]
			}

			output += fmt.Sprintf("[%s](tg://user?id=%d) - %d (%s)\n", currentUserName, i.UserId, i.Score, titles[titlesCounter])
			titlesCounter++
			flag = true

			if counter > 5 {
				break
			} else {
				counter++
			}
		}
	}

	if flag {
		reply = tgbotapi.NewMessage(msg.Chat.ID, output)
		reply.ParseMode = tgbotapi.ModeMarkdown
	} else {
		reply = tgbotapi.NewMessage(msg.Chat.ID, "Пидор дня еще ни разу не был выбран! Жми /pidor")
	}

	bot.Send(reply)
}

func startQuiz(msg *tgbotapi.Message) {
	firstPhrases := []string {
		"Инициализирую поиск пидора дня...",
		"Внимание, ищу пидора!",
		"Ну-ка дай-ка...",
		"Такс, кто тут у нас мало каши ел?",
		"Инициализация.Поиск.",
	}

	secondPhrases := []string {
		"Кажется я что-то вижу!",
		"Не может быть!",
		"Пожалуй препроверю...",
		"Найден!",
		"Прям по Бабичу!",
		"Как предсказал Великий Мейстер...",
	}

	var reply tgbotapi.MessageConfig
	var theUser User
	var users []User
	var randomUser int
	var currentUserName string
	var winner User
	var winnerScore int
	var available Available

	groupId := msg.Chat.ID

	gdb.Where("groupId = ?", groupId).Find(&users)
	gdb.Where("groupId = ?", groupId).First(&available)

	rowsCounted := len(users)
	if rowsCounted == 0 {
		reply = tgbotapi.NewMessage(msg.Chat.ID, "Нет участников! Жми /regpi")
		bot.Send(reply)
	} else {
		if available.Flag {
			lenOfCurrentUsers := len(users)
			if lenOfCurrentUsers == 1 {
				randomUser = 0
			} else {
				randomUser = random(0, lenOfCurrentUsers - 1)
			}

			gdb.Where("id = ?", users[randomUser].Id).First(&winner)

			reply = tgbotapi.NewMessage(msg.Chat.ID, firstPhrases[random(0, len(firstPhrases) - 1)])
			bot.Send(reply)
			time.Sleep(time.Second * 2)
			reply = tgbotapi.NewMessage(msg.Chat.ID, secondPhrases[random(0, len(secondPhrases) - 1)])
			bot.Send(reply)
			gdb.Where("id = ? and groupId = ?", theUser, groupId).First(&winner)
			winnerScore = winner.Score + 1
			gdb.Model(&users).Where("id = ?", winner.Id).UpdateColumn("score", winnerScore)
			time.Sleep(time.Second * 2)
			if len(winner.Usernick) > 0 {
				currentUserName = winner.Usernick
			} else {
				currentUserName = winner.Username
			}
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Ага! 🎉🎉🎉 Сегодня пидор - [%s](tg://user?id=%d)", currentUserName, winner.UserId))
			reply.ParseMode = tgbotapi.ModeMarkdown
			bot.Send(reply)
			gdb.Model(&available).Where("groupId = ?", groupId).Update("flag", false)
			gdb.Model(&available).Where("groupId = ?", groupId).Update("userId", winner.Id)
		} else {
			var currentUser User
			gdb.Where("id = ?", available.UserId).First(&currentUser)
			if len(currentUser.Usernick) > 0 {
				currentUserName = currentUser.Usernick
			} else {
				currentUserName = currentUser.Username[1:]
			}
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("🎉Сегодня у нас уже есть победитель - [%s](tg://user?id=%d)🎉", currentUserName, currentUser.UserId))
			reply.ParseMode = tgbotapi.ModeMarkdown
			bot.Send(reply)
		}
	}
}

func kekogen(msg *tgbotapi.Message) {
	var reply tgbotapi.MessageConfig
	var user User

	userId := msg.From.ID
	groupId := msg.Chat.ID

	gdb.Where("userId = ? and groupId = ?", userId, groupId).First(&user)
	currentQuota := user.Quota

	if user.Id > 0 {
		if currentQuota > 1 {
			vowels := []string {
				"а", "о", "и", "е", "у",  "я",
			}
			consonants := []string {
				"в", "д", "к", "л", "м", "н", "п", "р", "с", "т", "ф", "х", "ш",
			}
			result := "кек"

			for x:= 0; x < 5; x++ {
				if x % 2 == 0 {
					result += vowels[random(0, len(vowels) - 1)]
				} else {
					result += consonants[random(0, len(consonants) - 1)]
				}
			}

			gdb.Model(&user).Update(User{Quota: currentQuota - 1})
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf(result))
		} else {
			reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Твои кеки на сегодня кончились!"))
		}
	} else {
		reply = tgbotapi.NewMessage(msg.Chat.ID, fmt.Sprintf("Жми /regpi чтобы зарегистрироваться и получить свои кеки!"))
	}

	bot.Send(reply)
}<|endoftext|>


<commit_before>package repository

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/pkg/errors"
	"github.com/stormcat24/protodep/dependency"
	"github.com/stormcat24/protodep/helper"
	"github.com/stormcat24/protodep/logger"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
)

type GitRepository interface {
	Open() (*OpenedRepository, error)
	ProtoRootDir() string
}

type GitHubRepository struct {
	protodepDir  string
	dep          dependency.ProtoDepDependency
	authProvider helper.AuthProvider
}

func NewGitRepository(protodepDir string, dep dependency.ProtoDepDependency, authProvider helper.AuthProvider) GitRepository {
	return &GitHubRepository{
		protodepDir:  protodepDir,
		dep:          dep,
		authProvider: authProvider,
	}
}

type OpenedRepository struct {
	Repository *git.Repository
	Dep        dependency.ProtoDepDependency
	Hash       string
}

func (r *GitHubRepository) Open() (*OpenedRepository, error) {

	branch := "master"
	if r.dep.Branch != "" {
		branch = r.dep.Branch
	}

	revision := r.dep.Revision

	reponame := r.dep.Repository()
	repopath := filepath.Join(r.protodepDir, reponame)

	auth, err := r.authProvider.AuthMethod()
	if err != nil {
		return nil, err
	}

	var rep *git.Repository

	if stat, err := os.Stat(repopath); err == nil && stat.IsDir() {
		spinner := logger.InfoWithSpinner("Getting %s ", reponame)

		rep, err = git.PlainOpen(repopath)
		if err != nil {
			return nil, errors.Wrap(err, "open repository is failed")
		}
		spinner.Stop()

		fetchOpts := &git.FetchOptions{
			Auth: auth,
		}

		// TODO: Validate remote setting.
		// TODO: If .protodep cache remains with SSH, change remote target to HTTPS.

		if err := rep.Fetch(fetchOpts); err != nil {
			if err != git.NoErrAlreadyUpToDate {
				return nil, errors.Wrap(err, "fetch repository is failed")
			}
		}
		spinner.Finish()

	} else {
		spinner := logger.InfoWithSpinner("Getting %s ", reponame)
		// IDEA: Is it better to register both ssh and HTTP?
		rep, err = git.PlainClone(repopath, false, &git.CloneOptions{
			Auth: auth,
			URL:  r.authProvider.GetRepositoryURL(reponame),
		})
		if err != nil {
			return nil, errors.Wrap(err, "clone repository is failed")
		}
		spinner.Finish()
	}

	wt, err := rep.Worktree()
	if err != nil {
		return nil, errors.Wrap(err, "get worktree is failed")
	}

	if revision == "" {
		target, err := rep.Storer.Reference(plumbing.ReferenceName(fmt.Sprintf("refs/remotes/origin/%s", branch)))
		if err != nil {
			return nil, errors.Wrapf(err, "change branch to %s is failed", branch)
		}

		if err := wt.Checkout(&git.CheckoutOptions{Hash: target.Hash()}); err != nil {
			return nil, errors.Wrapf(err, "checkout to %s is failed", revision)
		}

		head := plumbing.NewHashReference(plumbing.HEAD, target.Hash())
		if err := rep.Storer.SetReference(head); err != nil {
			return nil, errors.Wrapf(err, "set head to %s is failed", branch)
		}
	} else {
		hash := plumbing.NewHash(revision)
		if err := wt.Checkout(&git.CheckoutOptions{Hash: hash}); err != nil {
			return nil, errors.Wrapf(err, "checkout to %s is failed", revision)
		}

		tag := plumbing.NewTagReferenceName(revision)
		ref, err := rep.Reference(tag, false)
		if err != nil && err != plumbing.ErrReferenceNotFound {
			return nil, errors.Wrapf(err, "tag = %s", tag)
		}

		if ref != nil {
			hash = ref.Hash()
		}

		head := plumbing.NewHashReference(plumbing.HEAD, hash)
		if err := rep.Storer.SetReference(head); err != nil {
			return nil, errors.Wrapf(err, "set head to %s is failed", revision)
		}
	}

	commiter, err := rep.Log(&git.LogOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "get commit is failed")
	}

	current, err := commiter.Next()
	if err != nil {
		return nil, errors.Wrap(err, "get commit current is failed")
	}

	return &OpenedRepository{
		Repository: rep,
		Dep:        r.dep,
		Hash:       current.Hash.String(),
	}, nil
}

func (r *GitHubRepository) ProtoRootDir() string {
	return filepath.Join(r.protodepDir, r.dep.Target)
}
<commit_msg>Fix checkout by tag in revision (#64)<commit_after>package repository

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/pkg/errors"
	"github.com/stormcat24/protodep/dependency"
	"github.com/stormcat24/protodep/helper"
	"github.com/stormcat24/protodep/logger"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
)

type GitRepository interface {
	Open() (*OpenedRepository, error)
	ProtoRootDir() string
}

type GitHubRepository struct {
	protodepDir  string
	dep          dependency.ProtoDepDependency
	authProvider helper.AuthProvider
}

func NewGitRepository(protodepDir string, dep dependency.ProtoDepDependency, authProvider helper.AuthProvider) GitRepository {
	return &GitHubRepository{
		protodepDir:  protodepDir,
		dep:          dep,
		authProvider: authProvider,
	}
}

type OpenedRepository struct {
	Repository *git.Repository
	Dep        dependency.ProtoDepDependency
	Hash       string
}

func (r *GitHubRepository) Open() (*OpenedRepository, error) {

	branch := "master"
	if r.dep.Branch != "" {
		branch = r.dep.Branch
	}

	revision := r.dep.Revision

	reponame := r.dep.Repository()
	repopath := filepath.Join(r.protodepDir, reponame)

	auth, err := r.authProvider.AuthMethod()
	if err != nil {
		return nil, err
	}

	var rep *git.Repository

	if stat, err := os.Stat(repopath); err == nil && stat.IsDir() {
		spinner := logger.InfoWithSpinner("Getting %s ", reponame)

		rep, err = git.PlainOpen(repopath)
		if err != nil {
			return nil, errors.Wrap(err, "open repository is failed")
		}
		spinner.Stop()

		fetchOpts := &git.FetchOptions{
			Auth: auth,
		}

		// TODO: Validate remote setting.
		// TODO: If .protodep cache remains with SSH, change remote target to HTTPS.

		if err := rep.Fetch(fetchOpts); err != nil {
			if err != git.NoErrAlreadyUpToDate {
				return nil, errors.Wrap(err, "fetch repository is failed")
			}
		}
		spinner.Finish()

	} else {
		spinner := logger.InfoWithSpinner("Getting %s ", reponame)
		// IDEA: Is it better to register both ssh and HTTP?
		rep, err = git.PlainClone(repopath, false, &git.CloneOptions{
			Auth: auth,
			URL:  r.authProvider.GetRepositoryURL(reponame),
		})
		if err != nil {
			return nil, errors.Wrap(err, "clone repository is failed")
		}
		spinner.Finish()
	}

	wt, err := rep.Worktree()
	if err != nil {
		return nil, errors.Wrap(err, "get worktree is failed")
	}

	if revision == "" {
		target, err := rep.Storer.Reference(plumbing.ReferenceName(fmt.Sprintf("refs/remotes/origin/%s", branch)))
		if err != nil {
			return nil, errors.Wrapf(err, "change branch to %s is failed", branch)
		}

		if err := wt.Checkout(&git.CheckoutOptions{Hash: target.Hash()}); err != nil {
			return nil, errors.Wrapf(err, "checkout to %s is failed", revision)
		}

		head := plumbing.NewHashReference(plumbing.HEAD, target.Hash())
		if err := rep.Storer.SetReference(head); err != nil {
			return nil, errors.Wrapf(err, "set head to %s is failed", branch)
		}
	} else {
		var opts git.CheckoutOptions

		tag := plumbing.NewTagReferenceName(revision)
		_, err := rep.Reference(tag, false)
		if err != nil && err != plumbing.ErrReferenceNotFound {
			return nil, errors.Wrapf(err, "tag = %s", tag)
		} else {
			if err != nil {
				// Tag not found, revision must be a hash
				logger.Info("%s is not a tag, checking out by hash", revision)
				hash := plumbing.NewHash(revision)
				opts = git.CheckoutOptions{Hash: hash}
			} else {
				logger.Info("%s is a tag, checking out by tag", revision)
				opts = git.CheckoutOptions{Branch: tag}
			}
		}

		if err := wt.Checkout(&opts); err != nil {
			return nil, errors.Wrapf(err, "checkout to %s is failed", revision)
		}
	}

	commiter, err := rep.Log(&git.LogOptions{})
	if err != nil {
		return nil, errors.Wrap(err, "get commit is failed")
	}

	current, err := commiter.Next()
	if err != nil {
		return nil, errors.Wrap(err, "get commit current is failed")
	}

	return &OpenedRepository{
		Repository: rep,
		Dep:        r.dep,
		Hash:       current.Hash.String(),
	}, nil
}

func (r *GitHubRepository) ProtoRootDir() string {
	return filepath.Join(r.protodepDir, r.dep.Target)
}
<|endoftext|>


<commit_before><commit_msg>Fixing up the in memory L1<commit_after><|endoftext|>


<commit_before>package rest

import (
	"net/http"

	"github.com/leanovate/microtools/routing"
)

type Resource interface {
	BeforeFilter(resp http.ResponseWriter, req *http.Request) bool
	Self() Link
	Get(request *http.Request) (interface{}, error)
	Patch(request *http.Request) (interface{}, error)
	Update(request *http.Request) (interface{}, error)
	Delete(request *http.Request) (interface{}, error)

	SubResources() routing.Matcher
}

type ResourceBase struct{}

func (ResourceBase) BeforeFilter(resp http.ResponseWriter, req *http.Request) bool {
	return true
}

func (ResourceBase) Get(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Patch(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Update(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Delete(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) SubResources() routing.Matcher {
	return HttpErrorMatcher(NotFound)
}

type LimitedResource struct {
	ResourceBase
	RequestSizeLimit int64
}

func (r LimitedResource) BeforeFilter(resp http.ResponseWriter, req *http.Request) bool {
	if req.Body != nil {
		req.Body = http.MaxBytesReader(resp, req.Body, r.RequestSizeLimit)
	}
	return true
}

func ResourceMatcher(resource Resource) routing.Matcher {
	return routing.Sequence(
		routing.EndSeq(
			routing.GET(restHandler{before: resource.BeforeFilter, handler: resource.Get}),
			routing.PUT(restHandler{before: resource.BeforeFilter, handler: resource.Update}),
			routing.PATCH(restHandler{before: resource.BeforeFilter, handler: resource.Patch}),
			routing.DELETE(restHandler{before: resource.BeforeFilter, handler: resource.Delete}),
			HttpErrorMatcher(MethodNotAllowed),
		),
		resource.SubResources(),
	)
}
<commit_msg>Add Post to resource<commit_after>package rest

import (
	"net/http"

	"github.com/leanovate/microtools/routing"
)

type Resource interface {
	BeforeFilter(resp http.ResponseWriter, req *http.Request) bool
	Self() Link
	Get(request *http.Request) (interface{}, error)
	Post(request *http.Request) (interface{}, error)
	Patch(request *http.Request) (interface{}, error)
	Update(request *http.Request) (interface{}, error)
	Delete(request *http.Request) (interface{}, error)

	SubResources() routing.Matcher
}

type ResourceBase struct{}

func (ResourceBase) BeforeFilter(resp http.ResponseWriter, req *http.Request) bool {
	return true
}

func (ResourceBase) Get(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Post(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Patch(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Update(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) Delete(request *http.Request) (interface{}, error) {
	return nil, MethodNotAllowed
}

func (ResourceBase) SubResources() routing.Matcher {
	return HttpErrorMatcher(NotFound)
}

type LimitedResource struct {
	ResourceBase
	RequestSizeLimit int64
}

func (r LimitedResource) BeforeFilter(resp http.ResponseWriter, req *http.Request) bool {
	if req.Body != nil {
		req.Body = http.MaxBytesReader(resp, req.Body, r.RequestSizeLimit)
	}
	return true
}

func ResourceMatcher(resource Resource) routing.Matcher {
	return routing.Sequence(
		routing.EndSeq(
			routing.GET(restHandler{before: resource.BeforeFilter, handler: resource.Get}),
			routing.POST(restHandler{before: resource.BeforeFilter, handler: resource.Post}),
			routing.PUT(restHandler{before: resource.BeforeFilter, handler: resource.Update}),
			routing.PATCH(restHandler{before: resource.BeforeFilter, handler: resource.Patch}),
			routing.DELETE(restHandler{before: resource.BeforeFilter, handler: resource.Delete}),
			HttpErrorMatcher(MethodNotAllowed),
		),
		resource.SubResources(),
	)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"go/build"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func operationEmbedGo(pkg *build.Package) {

	boxMap := findBoxes(pkg)

	// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
	if len(boxMap) == 0 {
		fmt.Println("no calls to rice.FindBox() found")
		return
	}

	verbosef("\n")

	for boxname := range boxMap {
		// find path and filename for this box
		boxPath := filepath.Join(pkg.Dir, boxname)
		boxFilename := strings.Replace(boxname, "/", "-", -1)
		boxFilename = strings.Replace(boxFilename, "..", "back", -1)
		boxFilename = boxFilename + `.rice-box.go`

		// verbose info
		verbosef("embedding box '%s'\n", boxname)
		verbosef("\tto file %s\n", boxFilename)

		// create box datastructure (used by template)
		box := &boxDataType{
			Package: pkg.Name,
			BoxName: boxname,
			UnixNow: time.Now().Unix(),
			Files:   make([]*fileDataType, 0),
			Dirs:    make(map[string]*dirDataType),
		}

		// fill box datastructure with file data
		filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				fmt.Printf("error walking box: %s\n", err)
				os.Exit(1)
			}

			filename := strings.TrimPrefix(path, boxPath)
			filename = strings.Replace(filename, "\\", "/", -1)
			filename = strings.TrimPrefix(filename, "/")
			if info.IsDir() {
				dirData := &dirDataType{
					Identifier: "dir_" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
					ChildFiles: make([]*fileDataType, 0),
					ChildDirs:  make([]*dirDataType, 0),
				}
				verbosef("\tincludes dir: '%s'\n", dirData.FileName)
				box.Dirs[dirData.FileName] = dirData

				// add tree entry (skip for root, it'll create a recursion)
				if dirData.FileName != "" {
					pathParts := strings.Split(dirData.FileName, "/")
					parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
					parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
				}
			} else {
				fileData := &fileDataType{
					Identifier: "file_" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
				}
				verbosef("\tincludes file: '%s'\n", fileData.FileName)
				fileData.Content, err = ioutil.ReadFile(path)
				if err != nil {
					fmt.Printf("error reading file content while walking box: %s\n", err)
					os.Exit(1)
				}
				box.Files = append(box.Files, fileData)

				// add tree entry
				pathParts := strings.Split(fileData.FileName, "/")
				parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
				parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
			}
			return nil
		})

		embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))

		// execute template to buffer
		err := tmplEmbeddedBox.Execute(embedSourceUnformated, box)
		if err != nil {
			log.Printf("error writing embedded box to file (template execute): %s\n", err)
			os.Exit(1)
		}

		// format the source code
		embedSource, err := format.Source(embedSourceUnformated.Bytes())
		if err != nil {
			log.Printf("error formatting embedSource: %s\n", err)
			os.Exit(1)
		}

		// create go file for box
		boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
		if err != nil {
			log.Printf("error creating embedded box file: %s\n", err)
			os.Exit(1)
		}
		defer boxFile.Close()

		// write source to file
		_, err = io.Copy(boxFile, bytes.NewBuffer(embedSource))
		if err != nil {
			log.Printf("error writing embedSource to file: %s\n", err)
			os.Exit(1)
		}
	}
}
<commit_msg>Make golint happy about generated code.<commit_after>package main

import (
	"bytes"
	"fmt"
	"go/build"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func operationEmbedGo(pkg *build.Package) {

	boxMap := findBoxes(pkg)

	// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
	if len(boxMap) == 0 {
		fmt.Println("no calls to rice.FindBox() found")
		return
	}

	verbosef("\n")

	for boxname := range boxMap {
		// find path and filename for this box
		boxPath := filepath.Join(pkg.Dir, boxname)
		boxFilename := strings.Replace(boxname, "/", "-", -1)
		boxFilename = strings.Replace(boxFilename, "..", "back", -1)
		boxFilename = boxFilename + `.rice-box.go`

		// verbose info
		verbosef("embedding box '%s'\n", boxname)
		verbosef("\tto file %s\n", boxFilename)

		// create box datastructure (used by template)
		box := &boxDataType{
			Package: pkg.Name,
			BoxName: boxname,
			UnixNow: time.Now().Unix(),
			Files:   make([]*fileDataType, 0),
			Dirs:    make(map[string]*dirDataType),
		}

		// fill box datastructure with file data
		filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				fmt.Printf("error walking box: %s\n", err)
				os.Exit(1)
			}

			filename := strings.TrimPrefix(path, boxPath)
			filename = strings.Replace(filename, "\\", "/", -1)
			filename = strings.TrimPrefix(filename, "/")
			if info.IsDir() {
				dirData := &dirDataType{
					Identifier: "dir" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
					ChildFiles: make([]*fileDataType, 0),
					ChildDirs:  make([]*dirDataType, 0),
				}
				verbosef("\tincludes dir: '%s'\n", dirData.FileName)
				box.Dirs[dirData.FileName] = dirData

				// add tree entry (skip for root, it'll create a recursion)
				if dirData.FileName != "" {
					pathParts := strings.Split(dirData.FileName, "/")
					parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
					parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
				}
			} else {
				fileData := &fileDataType{
					Identifier: "file" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
				}
				verbosef("\tincludes file: '%s'\n", fileData.FileName)
				fileData.Content, err = ioutil.ReadFile(path)
				if err != nil {
					fmt.Printf("error reading file content while walking box: %s\n", err)
					os.Exit(1)
				}
				box.Files = append(box.Files, fileData)

				// add tree entry
				pathParts := strings.Split(fileData.FileName, "/")
				parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
				parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
			}
			return nil
		})

		embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))

		// execute template to buffer
		err := tmplEmbeddedBox.Execute(embedSourceUnformated, box)
		if err != nil {
			log.Printf("error writing embedded box to file (template execute): %s\n", err)
			os.Exit(1)
		}

		// format the source code
		embedSource, err := format.Source(embedSourceUnformated.Bytes())
		if err != nil {
			log.Printf("error formatting embedSource: %s\n", err)
			os.Exit(1)
		}

		// create go file for box
		boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
		if err != nil {
			log.Printf("error creating embedded box file: %s\n", err)
			os.Exit(1)
		}
		defer boxFile.Close()

		// write source to file
		_, err = io.Copy(boxFile, bytes.NewBuffer(embedSource))
		if err != nil {
			log.Printf("error writing embedSource to file: %s\n", err)
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>// chris 090115 Unix removable lock file.

// TODO Note how Close calls errors are not handled.

package rmlock

import (
	"os"

	"golang.org/x/sys/unix"
)

type LockContext struct {
	globalname string

	local *os.File
}

func globalCtx(globalname string, inner func() error) error {
	f, err := os.OpenFile(globalname, os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
		return err
	}

	if err := inner(); err != nil {
		return err
	}

	if err = unix.Flock(int(f.Fd()), unix.LOCK_UN); err != nil {
		return err
	}

	return nil
}

func Lock(globalname, localname string) (*LockContext, error) {
	var lc *LockContext

	err := globalCtx(globalname, func() error {
		f, err := os.Openfile(localname, os.O_CREATE, 0666)
		if err != nil {
			return err
		}

		err = unix.Flock(int(f.Fd()), unix.LOCK_EX | unix.LOCK_NB)
		if err != nil {
			f.Close()
			return err
		}

		lc = &LockContext{
			globalname: globalname,
			local: f,
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return lc, nil

}

func (lc *LockContext) Unlock() error {
	return globalCtx(lc.globalname, func() error {
		lc.local.Close()
		return os.Remove(lc.local.Name())
	})
}
<commit_msg>rmlock: Refactors mode to be a const.<commit_after>// chris 090115 Unix removable lock file.

// TODO Note how Close calls errors are not handled.

package rmlock

import (
	"os"

	"golang.org/x/sys/unix"
)

const mode = 0666

type LockContext struct {
	globalname string

	local *os.File
}

func globalCtx(globalname string, inner func() error) error {
	f, err := os.OpenFile(globalname, os.O_CREATE, mode)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
		return err
	}

	if err := inner(); err != nil {
		return err
	}

	if err = unix.Flock(int(f.Fd()), unix.LOCK_UN); err != nil {
		return err
	}

	return nil
}

func Lock(globalname, localname string) (*LockContext, error) {
	var lc *LockContext

	err := globalCtx(globalname, func() error {
		f, err := os.OpenFile(localname, os.O_CREATE, mode)
		if err != nil {
			return err
		}

		err = unix.Flock(int(f.Fd()), unix.LOCK_EX | unix.LOCK_NB)
		if err != nil {
			f.Close()
			return err
		}

		lc = &LockContext{
			globalname: globalname,
			local: f,
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return lc, nil

}

func (lc *LockContext) Unlock() error {
	return globalCtx(lc.globalname, func() error {
		lc.local.Close()
		return os.Remove(lc.local.Name())
	})
}
<|endoftext|>


<commit_before>package radius

import (
	"os"
	"os/exec"
	"strings"
	"testing"
)

func TestRFCBuild(t *testing.T) {
	t.Parallel()

	var packages []string

	f, err := os.Open(".")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	entries, err := f.Readdir(0)
	if err != nil {
		t.Fatal(err)
	}
	for _, entry := range entries {
		if entry.IsDir() && strings.HasPrefix(entry.Name(), "rfc") {
			packages = append(packages, entry.Name())
		}
	}

	for _, pkg := range packages {
		func(pkg string) {
			t.Run(pkg, func(t *testing.T) {
				t.Parallel()

				cmd := exec.Command("go", "build", "layeh.com/radius/"+pkg)
				output, err := cmd.CombinedOutput()
				if err != nil {
					t.Errorf("%s: %s\n", err, output)
				}
			})
		}(pkg)
	}
}
<commit_msg>remove useless test<commit_after><|endoftext|>


<commit_before>package sftp

import (
	"fmt"
	"io"
	"path/filepath"
	"reflect"
	"sync"
	"syscall"
)

var maxTxPacket uint32 = 1 << 15

type handleHandler func(string) string

type Handlers struct {
	FileGet  FileReader
	FilePut  FileWriter
	FileCmd  FileCmder
	FileInfo FileInfoer
}

// Server that abstracts the sftp protocol for a http request-like protocol
type RequestServer struct {
	serverConn
	Handlers        Handlers
	pktChan         chan packet
	openRequests    map[string]*Request
	openRequestLock sync.RWMutex
}

// simple factory function
// one server per user-session
func NewRequestServer(rwc io.ReadWriteCloser, h Handlers) (*RequestServer, error) {
	s := &RequestServer{
		serverConn: serverConn{
			conn: conn{
				Reader:      rwc,
				WriteCloser: rwc,
			},
		},
		Handlers:     h,
		pktChan:      make(chan packet, sftpServerWorkerCount),
		openRequests: make(map[string]*Request),
	}

	return s, nil
}

func (rs *RequestServer) nextRequest(r *Request) string {
	rs.openRequestLock.Lock()
	defer rs.openRequestLock.Unlock()
	rs.openRequests[r.Filepath] = r
	return r.Filepath
}

func (rs *RequestServer) getRequest(handle string) (*Request, bool) {
	rs.openRequestLock.Lock()
	defer rs.openRequestLock.Unlock()
	r, ok := rs.openRequests[handle]
	return r, ok
}

func (rs *RequestServer) closeRequest(handle string) {
	rs.openRequestLock.Lock()
	defer rs.openRequestLock.Unlock()
	if _, ok := rs.openRequests[handle]; ok {
		delete(rs.openRequests, handle)
	}
}

// close the read/write/closer to trigger exiting the main server loop
func (rs *RequestServer) Close() error { return rs.conn.Close() }

// start serving requests from user session
func (rs *RequestServer) Serve() error {
	var wg sync.WaitGroup
	wg.Add(sftpServerWorkerCount)
	for i := 0; i < sftpServerWorkerCount; i++ {
		go func() {
			defer wg.Done()
			if err := rs.packetWorker(); err != nil {
				rs.conn.Close() // shuts down recvPacket
			}
		}()
	}

	var err error
	var pktType uint8
	var pktBytes []byte
	for {
		pktType, pktBytes, err = rs.recvPacket()
		if err != nil {
			break
		}
		pkt, err := makePacket(rxPacket{fxp(pktType), pktBytes})
		if err != nil {
			break
		}
		rs.pktChan <- pkt
	}

	close(rs.pktChan) // shuts down sftpServerWorkers
	wg.Wait()         // wait for all workers to exit
	return err
}

func (rs *RequestServer) packetWorker() error {
	for pkt := range rs.pktChan {
		fmt.Println("Incoming Packet: ", pkt, reflect.TypeOf(pkt))
		var handle string
		var rpkt resp_packet
		var err error
		switch pkt := pkt.(type) {
		case *sshFxInitPacket:
			rpkt = sshFxVersionPacket{sftpProtocolVersion, nil}
		case *sshFxpClosePacket:
			handle = pkt.getHandle()
			rs.closeRequest(handle)
			rpkt = statusFromError(pkt, nil)
		case *sshFxpRealpathPacket:
			rpkt = cleanPath(pkt)
		case isOpener:
			handle = rs.nextRequest(newRequest(pkt.getPath()))
			rpkt = sshFxpHandlePacket{pkt.id(), handle}
		case hasPath:
			handle = rs.nextRequest(newRequest(pkt.getPath()))
			rpkt = rs.request(handle, pkt)
		case hasHandle:
			handle = pkt.getHandle()
			rpkt = rs.request(handle, pkt)
		}

		fmt.Println("Reply Packet: ", rpkt, reflect.TypeOf(rpkt))
		err = rs.sendPacket(rpkt)
		if err != nil {
			return err
		}
	}
	return nil
}

func cleanPath(pkt *sshFxpRealpathPacket) resp_packet {
	path := pkt.getPath()
	if !filepath.IsAbs(path) {
		path = "/" + path
	} // all paths are absolute

	cleaned_path := filepath.Clean(path)
	return &sshFxpNamePacket{
		ID: pkt.id(),
		NameAttrs: []sshFxpNameAttr{{
			Name:     cleaned_path,
			LongName: cleaned_path,
			Attrs:    emptyFileStat,
		}},
	}
}

func (rs *RequestServer) request(handle string, pkt packet) resp_packet {
	var rpkt resp_packet
	var err error
	if request, ok := rs.getRequest(handle); ok {
		// called here to keep packet handling out of request for testing
		request.populate(pkt)
		fmt.Println("Request Method: ", request.Method)
		rpkt, err = request.handle(rs.Handlers)
		if err != nil {
			rpkt = statusFromError(pkt, err)
		}
	} else {
		rpkt = statusFromError(pkt, syscall.EBADF)
	}
	return rpkt
}
<commit_msg>os.ErrorNotExist should convert to ssh_FX_NO_SUCH_FILE<commit_after>package sftp

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"sync"
	"syscall"
)

var maxTxPacket uint32 = 1 << 15

type handleHandler func(string) string

type Handlers struct {
	FileGet  FileReader
	FilePut  FileWriter
	FileCmd  FileCmder
	FileInfo FileInfoer
}

// Server that abstracts the sftp protocol for a http request-like protocol
type RequestServer struct {
	serverConn
	Handlers        Handlers
	pktChan         chan packet
	openRequests    map[string]*Request
	openRequestLock sync.RWMutex
}

// simple factory function
// one server per user-session
func NewRequestServer(rwc io.ReadWriteCloser, h Handlers) (*RequestServer, error) {
	s := &RequestServer{
		serverConn: serverConn{
			conn: conn{
				Reader:      rwc,
				WriteCloser: rwc,
			},
		},
		Handlers:     h,
		pktChan:      make(chan packet, sftpServerWorkerCount),
		openRequests: make(map[string]*Request),
	}

	return s, nil
}

func (rs *RequestServer) nextRequest(r *Request) string {
	rs.openRequestLock.Lock()
	defer rs.openRequestLock.Unlock()
	rs.openRequests[r.Filepath] = r
	return r.Filepath
}

func (rs *RequestServer) getRequest(handle string) (*Request, bool) {
	rs.openRequestLock.Lock()
	defer rs.openRequestLock.Unlock()
	r, ok := rs.openRequests[handle]
	return r, ok
}

func (rs *RequestServer) closeRequest(handle string) {
	rs.openRequestLock.Lock()
	defer rs.openRequestLock.Unlock()
	if _, ok := rs.openRequests[handle]; ok {
		delete(rs.openRequests, handle)
	}
}

// close the read/write/closer to trigger exiting the main server loop
func (rs *RequestServer) Close() error { return rs.conn.Close() }

// start serving requests from user session
func (rs *RequestServer) Serve() error {
	var wg sync.WaitGroup
	wg.Add(sftpServerWorkerCount)
	for i := 0; i < sftpServerWorkerCount; i++ {
		go func() {
			defer wg.Done()
			if err := rs.packetWorker(); err != nil {
				rs.conn.Close() // shuts down recvPacket
			}
		}()
	}

	var err error
	var pktType uint8
	var pktBytes []byte
	for {
		pktType, pktBytes, err = rs.recvPacket()
		if err != nil {
			break
		}
		pkt, err := makePacket(rxPacket{fxp(pktType), pktBytes})
		if err != nil {
			break
		}
		rs.pktChan <- pkt
	}

	close(rs.pktChan) // shuts down sftpServerWorkers
	wg.Wait()         // wait for all workers to exit
	return err
}

func (rs *RequestServer) packetWorker() error {
	for pkt := range rs.pktChan {
		fmt.Println("Incoming Packet: ", pkt, reflect.TypeOf(pkt))
		var handle string
		var rpkt resp_packet
		var err error
		switch pkt := pkt.(type) {
		case *sshFxInitPacket:
			rpkt = sshFxVersionPacket{sftpProtocolVersion, nil}
		case *sshFxpClosePacket:
			handle = pkt.getHandle()
			rs.closeRequest(handle)
			rpkt = statusFromError(pkt, nil)
		case *sshFxpRealpathPacket:
			rpkt = cleanPath(pkt)
		case isOpener:
			handle = rs.nextRequest(newRequest(pkt.getPath()))
			rpkt = sshFxpHandlePacket{pkt.id(), handle}
		case hasPath:
			handle = rs.nextRequest(newRequest(pkt.getPath()))
			rpkt = rs.request(handle, pkt)
		case hasHandle:
			handle = pkt.getHandle()
			rpkt = rs.request(handle, pkt)
		}

		fmt.Println("Reply Packet: ", rpkt, reflect.TypeOf(rpkt))
		err = rs.sendPacket(rpkt)
		if err != nil {
			return err
		}
	}
	return nil
}

func cleanPath(pkt *sshFxpRealpathPacket) resp_packet {
	path := pkt.getPath()
	if !filepath.IsAbs(path) {
		path = "/" + path
	} // all paths are absolute

	cleaned_path := filepath.Clean(path)
	return &sshFxpNamePacket{
		ID: pkt.id(),
		NameAttrs: []sshFxpNameAttr{{
			Name:     cleaned_path,
			LongName: cleaned_path,
			Attrs:    emptyFileStat,
		}},
	}
}

func (rs *RequestServer) request(handle string, pkt packet) resp_packet {
	var rpkt resp_packet
	var err error
	if request, ok := rs.getRequest(handle); ok {
		// called here to keep packet handling out of request for testing
		request.populate(pkt)
		fmt.Println("Request Method: ", request.Method)
		rpkt, err = request.handle(rs.Handlers)
		if err != nil {
			err = errorAdapter(err)
			rpkt = statusFromError(pkt, err)
		}
	} else {
		rpkt = statusFromError(pkt, syscall.EBADF)
	}
	return rpkt
}

// os.ErrNotExist should convert to ssh_FX_NO_SUCH_FILE, but is not recognized
// by statusFromError. So we convert to syscall.ENOENT which it does.
func errorAdapter(err error) error {
	if err == os.ErrNotExist {
		return syscall.ENOENT
	}
	return err
}
<|endoftext|>


<commit_before>// Code generated by protoc-gen-grpc-gateway
// source: zvelo/msg/api.proto
// DO NOT EDIT!

/*
Package msg is a reverse proxy.

It translates gRPC into RESTful JSON APIs.
*/
package msg

import (
	"io"
	"net/http"

	"github.com/golang/protobuf/proto"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/grpc-ecosystem/grpc-gateway/utilities"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/status"
)

var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray

func request_API_QueryV1_0(ctx context.Context, marshaler runtime.Marshaler, client APIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq QueryRequests
	var metadata runtime.ServerMetadata

	if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := client.QueryV1(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

func request_API_QueryResultV1_0(ctx context.Context, marshaler runtime.Marshaler, client APIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq QueryPollRequest
	var metadata runtime.ServerMetadata

	var (
		val string
		ok  bool
		err error
		_   = err
	)

	val, ok = pathParams["request_id"]
	if !ok {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "request_id")
	}

	protoReq.RequestId, err = runtime.String(val)

	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "request_id", err)
	}

	msg, err := client.QueryResultV1(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

// RegisterAPIHandlerFromEndpoint is same as RegisterAPIHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAPIHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
	conn, err := grpc.Dial(endpoint, opts...)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			if cerr := conn.Close(); cerr != nil {
				grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
			}
			return
		}
		go func() {
			<-ctx.Done()
			if cerr := conn.Close(); cerr != nil {
				grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
			}
		}()
	}()

	return RegisterAPIHandler(ctx, mux, conn)
}

// RegisterAPIHandler registers the http handlers for service API to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterAPIHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
	client := NewAPIClient(conn)

	mux.Handle("POST", pattern_API_QueryV1_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		if cn, ok := w.(http.CloseNotifier); ok {
			go func(done <-chan struct{}, closed <-chan bool) {
				select {
				case <-done:
				case <-closed:
					cancel()
				}
			}(ctx.Done(), cn.CloseNotify())
		}
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_API_QueryV1_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_API_QueryV1_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("GET", pattern_API_QueryResultV1_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		if cn, ok := w.(http.CloseNotifier); ok {
			go func(done <-chan struct{}, closed <-chan bool) {
				select {
				case <-done:
				case <-closed:
					cancel()
				}
			}(ctx.Done(), cn.CloseNotify())
		}
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_API_QueryResultV1_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_API_QueryResultV1_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	return nil
}

var (
	pattern_API_QueryV1_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "query"}, ""))

	pattern_API_QueryResultV1_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "query", "request_id"}, ""))
)

var (
	forward_API_QueryV1_0 = runtime.ForwardResponseMessage

	forward_API_QueryResultV1_0 = runtime.ForwardResponseMessage
)
<commit_msg>rebuild<commit_after>// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: zvelo/msg/api.proto

/*
Package msg is a reverse proxy.

It translates gRPC into RESTful JSON APIs.
*/
package msg

import (
	"io"
	"net/http"

	"github.com/golang/protobuf/proto"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/grpc-ecosystem/grpc-gateway/utilities"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/status"
)

var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray

func request_API_QueryV1_0(ctx context.Context, marshaler runtime.Marshaler, client APIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq QueryRequests
	var metadata runtime.ServerMetadata

	if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := client.QueryV1(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

func request_API_QueryResultV1_0(ctx context.Context, marshaler runtime.Marshaler, client APIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq QueryPollRequest
	var metadata runtime.ServerMetadata

	var (
		val string
		ok  bool
		err error
		_   = err
	)

	val, ok = pathParams["request_id"]
	if !ok {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "request_id")
	}

	protoReq.RequestId, err = runtime.String(val)

	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "request_id", err)
	}

	msg, err := client.QueryResultV1(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

// RegisterAPIHandlerFromEndpoint is same as RegisterAPIHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAPIHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
	conn, err := grpc.Dial(endpoint, opts...)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			if cerr := conn.Close(); cerr != nil {
				grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
			}
			return
		}
		go func() {
			<-ctx.Done()
			if cerr := conn.Close(); cerr != nil {
				grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
			}
		}()
	}()

	return RegisterAPIHandler(ctx, mux, conn)
}

// RegisterAPIHandler registers the http handlers for service API to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterAPIHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
	return RegisterAPIHandlerClient(ctx, mux, NewAPIClient(conn))
}

// RegisterAPIHandler registers the http handlers for service API to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "APIClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "APIClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "APIClient" to call the correct interceptors.
func RegisterAPIHandlerClient(ctx context.Context, mux *runtime.ServeMux, client APIClient) error {

	mux.Handle("POST", pattern_API_QueryV1_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		if cn, ok := w.(http.CloseNotifier); ok {
			go func(done <-chan struct{}, closed <-chan bool) {
				select {
				case <-done:
				case <-closed:
					cancel()
				}
			}(ctx.Done(), cn.CloseNotify())
		}
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_API_QueryV1_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_API_QueryV1_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	mux.Handle("GET", pattern_API_QueryResultV1_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		if cn, ok := w.(http.CloseNotifier); ok {
			go func(done <-chan struct{}, closed <-chan bool) {
				select {
				case <-done:
				case <-closed:
					cancel()
				}
			}(ctx.Done(), cn.CloseNotify())
		}
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_API_QueryResultV1_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_API_QueryResultV1_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	return nil
}

var (
	pattern_API_QueryV1_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "query"}, ""))

	pattern_API_QueryResultV1_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "query", "request_id"}, ""))
)

var (
	forward_API_QueryV1_0 = runtime.ForwardResponseMessage

	forward_API_QueryResultV1_0 = runtime.ForwardResponseMessage
)
<|endoftext|>


<commit_before>package resource

import (
	"time"

	"github.com/gbl08ma/disturbancesmlx/dataobjects"
	"github.com/heetch/sqalx"
	"github.com/yarf-framework/yarf"
)

// Stats composites resource
type Stats struct {
	resource
	calculator StatsCalculator
}

type apiStats struct {
	LineStats       map[string]apiLineStats `msgpack:"lineStats" json:"lineStats"`
	LastDisturbance time.Time               `msgpack:"lastDisturbance" json:"lastDisturbance"`
}

type apiLineStats struct {
	Availability               float64              `msgpack:"availability" json:"availability"`
	AverageDisturbanceDuration dataobjects.Duration `msgpack:"avgDistDuration" json:"avgDistDuration"`
}

type StatsCalculator interface {
	Availability(node sqalx.Node, line *dataobjects.Line, startTime time.Time, endTime time.Time) (float64, time.Duration, error)
}

func (r *Stats) WithNode(node sqalx.Node) *Stats {
	r.node = node
	return r
}

func (r *Stats) WithCalculator(calculator StatsCalculator) *Stats {
	r.calculator = calculator
	return r
}

func (r *Stats) Get(c *yarf.Context) error {
	tx, err := r.Beginx()
	if err != nil {
		return err
	}
	defer tx.Commit() // read-only tx

	start := c.Request.URL.Query().Get("start")
	startTime := time.Now().AddDate(0, 0, -7)
	if start != "" {
		startTime, err = time.Parse(time.RFC3339, start)
		if err != nil {
			return err
		}
	}

	end := c.Request.URL.Query().Get("end")
	endTime := time.Now()
	if end != "" {
		endTime, err = time.Parse(time.RFC3339, end)
		if err != nil {
			return err
		}
	}

	if c.Param("id") != "" {
		network, err := dataobjects.GetNetwork(tx, c.Param("id"))
		if err != nil {
			return err
		}

		stats, err := r.getStatsForNetwork(tx, network, startTime, endTime)
		if err != nil {
			return err
		}

		RenderData(c, stats)
	} else {
		statsMap := make(map[string]apiStats)
		networks, err := dataobjects.GetNetworks(tx)
		if err != nil {
			return err
		}

		for _, network := range networks {
			stats, err := r.getStatsForNetwork(tx, network, startTime, endTime)
			if err != nil {
				return err
			}
			statsMap[network.ID] = stats
		}
		RenderData(c, statsMap)
	}
	return nil
}

func (r *Stats) getStatsForNetwork(node sqalx.Node, network *dataobjects.Network, startTime time.Time, endTime time.Time) (apiStats, error) {
	tx, err := r.Beginx()
	if err != nil {
		return apiStats{}, err
	}
	defer tx.Commit() // read-only tx

	lastDist, err := r.getLastDisturbanceTimeForNetwork(tx, network)

	stats := apiStats{
		LastDisturbance: lastDist,
		LineStats:       make(map[string]apiLineStats),
	}

	lines, err := network.Lines(tx)
	if err != nil {
		return apiStats{}, err
	}

	for _, line := range lines {
		availability, avgDuration, err := r.calculator.Availability(tx, line, startTime, endTime)
		if err != nil {
			return apiStats{}, err
		}
		stats.LineStats[line.ID] = apiLineStats{
			Availability:               availability,
			AverageDisturbanceDuration: dataobjects.Duration(avgDuration),
		}
	}
	return stats, nil
}

func (r *Stats) getLastDisturbanceTimeForNetwork(node sqalx.Node, network *dataobjects.Network) (time.Time, error) {
	tx, err := r.Beginx()
	if err != nil {
		return time.Now().UTC(), err
	}
	defer tx.Commit() // read-only tx

	d, err := network.LastDisturbance(tx)
	if err != nil {
		return time.Now().UTC(), err
	}

	if !d.Ended {
		return time.Now().UTC(), nil
	}
	return d.EndTime, nil
}
<commit_msg>Add client input validation<commit_after>package resource

import (
	"net/http"
	"time"

	"github.com/gbl08ma/disturbancesmlx/dataobjects"
	"github.com/heetch/sqalx"
	"github.com/yarf-framework/yarf"
)

// Stats composites resource
type Stats struct {
	resource
	calculator StatsCalculator
}

type apiStats struct {
	LineStats       map[string]apiLineStats `msgpack:"lineStats" json:"lineStats"`
	LastDisturbance time.Time               `msgpack:"lastDisturbance" json:"lastDisturbance"`
}

type apiLineStats struct {
	Availability               float64              `msgpack:"availability" json:"availability"`
	AverageDisturbanceDuration dataobjects.Duration `msgpack:"avgDistDuration" json:"avgDistDuration"`
}

type StatsCalculator interface {
	Availability(node sqalx.Node, line *dataobjects.Line, startTime time.Time, endTime time.Time) (float64, time.Duration, error)
}

func (r *Stats) WithNode(node sqalx.Node) *Stats {
	r.node = node
	return r
}

func (r *Stats) WithCalculator(calculator StatsCalculator) *Stats {
	r.calculator = calculator
	return r
}

func (r *Stats) Get(c *yarf.Context) error {
	tx, err := r.Beginx()
	if err != nil {
		return err
	}
	defer tx.Commit() // read-only tx

	start := c.Request.URL.Query().Get("start")
	startTime := time.Now().AddDate(0, 0, -7)
	if start != "" {
		startTime, err = time.Parse(time.RFC3339, start)
		if err != nil {
			return err
		}
	}

	end := c.Request.URL.Query().Get("end")
	endTime := time.Now()
	if end != "" {
		endTime, err = time.Parse(time.RFC3339, end)
		if err != nil {
			return err
		}
	}

	if !endTime.After(startTime) {
		return &yarf.CustomError{
			HTTPCode:  http.StatusBadRequest,
			ErrorMsg:  "End time must be after start time",
			ErrorBody: "End time must be after start time",
		}
	}

	if c.Param("id") != "" {
		network, err := dataobjects.GetNetwork(tx, c.Param("id"))
		if err != nil {
			return err
		}

		stats, err := r.getStatsForNetwork(tx, network, startTime, endTime)
		if err != nil {
			return err
		}

		RenderData(c, stats)
	} else {
		statsMap := make(map[string]apiStats)
		networks, err := dataobjects.GetNetworks(tx)
		if err != nil {
			return err
		}

		for _, network := range networks {
			stats, err := r.getStatsForNetwork(tx, network, startTime, endTime)
			if err != nil {
				return err
			}
			statsMap[network.ID] = stats
		}
		RenderData(c, statsMap)
	}
	return nil
}

func (r *Stats) getStatsForNetwork(node sqalx.Node, network *dataobjects.Network, startTime time.Time, endTime time.Time) (apiStats, error) {
	tx, err := r.Beginx()
	if err != nil {
		return apiStats{}, err
	}
	defer tx.Commit() // read-only tx

	lastDist, err := r.getLastDisturbanceTimeForNetwork(tx, network)

	stats := apiStats{
		LastDisturbance: lastDist,
		LineStats:       make(map[string]apiLineStats),
	}

	lines, err := network.Lines(tx)
	if err != nil {
		return apiStats{}, err
	}

	for _, line := range lines {
		availability, avgDuration, err := r.calculator.Availability(tx, line, startTime, endTime)
		if err != nil {
			return apiStats{}, err
		}
		stats.LineStats[line.ID] = apiLineStats{
			Availability:               availability,
			AverageDisturbanceDuration: dataobjects.Duration(avgDuration),
		}
	}
	return stats, nil
}

func (r *Stats) getLastDisturbanceTimeForNetwork(node sqalx.Node, network *dataobjects.Network) (time.Time, error) {
	tx, err := r.Beginx()
	if err != nil {
		return time.Now().UTC(), err
	}
	defer tx.Commit() // read-only tx

	d, err := network.LastDisturbance(tx)
	if err != nil {
		return time.Now().UTC(), err
	}

	if !d.Ended {
		return time.Now().UTC(), nil
	}
	return d.EndTime, nil
}
<|endoftext|>


<commit_before>package wikidump

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
)

type loggingWriter struct {
	w           io.WriteCloser
	done, total int64
	threshold   float32
}

func (w *loggingWriter) Write(p []byte) (n int, err error) {
	n, err = w.w.Write(p)
	w.done += int64(n)
	if float32(w.done) > w.threshold*float32(w.total) {
		log.Printf("%3d%% done (%d of %d)",
			int(100*w.threshold), w.done, w.total)
		w.threshold += .05
	}
	return
}

func (w loggingWriter) Close() error {
	return w.w.Close()
}

func nullLogger(string, ...interface{}) {
}

// Download database dump for wikiname (e.g., "en", "sco", "nds_nl") from
// WikiMedia.
//
// Returns the local file path of the dump, derived from the URL.
//
// Logs its progress on the standard log if logProgress is true.
func Download(wikiname string, logProgress bool) (filepath string, err error) {
	return download(wikiname, ".", logProgress, http.DefaultClient)
}

func download(wikiname, directory string, logProgress bool,
	client *http.Client) (filepath string, err error) {

	logprint := nullLogger
	if logProgress {
		logprint = log.Printf
	}

	urlstr := fmt.Sprintf(
		"https://dumps.wikimedia.org/%s/latest/%s-latest-pages-articles.xml.bz2",
		wikiname, wikiname)
	resp, err := client.Get(urlstr)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("HTTP error %d for %s", resp.StatusCode, urlstr)
	}

	u, err := url.Parse(urlstr)
	if err != nil {
		return
	}
	filepath = path.Base(u.Path)

	var out io.WriteCloser
	out, err = os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		return
	}
	defer out.Close()

	logprint("downloading from %s to %s", urlstr, filepath)
	if logProgress && resp.ContentLength >= 0 {
		out = &loggingWriter{
			w:     out,
			total: resp.ContentLength,
		}
	}
	_, err = io.Copy(out, resp.Body)
	logprint("download of %s done", urlstr)
	return
}
<commit_msg>progress bar in downloader<commit_after>package wikidump

import (
	"fmt"
	"github.com/cheggaaa/pb"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
)

func nullLogger(string, ...interface{}) {
}

// Writer with progressbar.
type pbWriter struct {
	w   io.WriteCloser
	bar *pb.ProgressBar
}

func newPbWriter(w io.WriteCloser, total int64) *pbWriter {
	pbw := &pbWriter{w, pb.New64(total)}
	pbw.bar.Start()
	return pbw
}

func (w *pbWriter) Close() error {
	w.bar.Finish()
	return w.w.Close()
}

func (w *pbWriter) Write(p []byte) (n int, err error) {
	n, err = w.w.Write(p)
	w.bar.Add(n)
	return
}

// Download database dump for wikiname (e.g., "en", "sco", "nds_nl") from
// WikiMedia.
//
// Returns the local file path of the dump, derived from the URL.
//
// Logs its progress on the standard log if logProgress is true.
func Download(wikiname string, logProgress bool) (filepath string, err error) {
	return download(wikiname, ".", logProgress, http.DefaultClient)
}

func download(wikiname, directory string, logProgress bool,
	client *http.Client) (filepath string, err error) {

	logprint := nullLogger
	if logProgress {
		logprint = log.Printf
	}

	urlstr := fmt.Sprintf(
		"https://dumps.wikimedia.org/%s/latest/%s-latest-pages-articles.xml.bz2",
		wikiname, wikiname)
	resp, err := client.Get(urlstr)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("HTTP error %d for %s", resp.StatusCode, urlstr)
	}

	u, err := url.Parse(urlstr)
	if err != nil {
		return
	}
	filepath = path.Base(u.Path)

	var out io.WriteCloser
	out, err = os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		return
	}
	defer out.Close()

	logprint("downloading from %s to %s", urlstr, filepath)
	if logProgress && resp.ContentLength >= 0 {
		out = newPbWriter(out, resp.ContentLength)
	}
	_, err = io.Copy(out, resp.Body)
	logprint("download of %s done", urlstr)
	return
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package model

import (
	"strconv"
	"time"

	"github.com/webx-top/com"
	"github.com/webx-top/db"
	"github.com/webx-top/echo"

	"github.com/admpub/nging/v3/application/dbschema"
	"github.com/admpub/nging/v3/application/library/common"
	"github.com/admpub/nging/v3/application/library/ip2region"
	"github.com/admpub/nging/v3/application/model/base"
)

func NewLoginLog(ctx echo.Context) *LoginLog {
	m := &LoginLog{
		NgingLoginLog: &dbschema.NgingLoginLog{},
		base:          base.New(ctx),
	}
	m.SetContext(ctx)
	return m
}

type LoginLog struct {
	*dbschema.NgingLoginLog
	base *base.Base
}

func (s *LoginLog) check() error {
	if !echo.Bool(`backend.Anonymous`) {
		s.IpAddress = s.base.RealIP()
		ipInfo, err := ip2region.IPInfo(s.IpAddress)
		if err != nil {
			return err
		}
		s.IpLocation = ip2region.Stringify(ipInfo)
		s.UserAgent = s.base.Request().UserAgent()
	}
	s.Success = common.GetBoolFlag(s.Success)
	s.Errpwd = com.MaskString(s.Errpwd)
	day, _ := strconv.Atoi(time.Now().Format(`20060102`))
	s.Day = uint(day)
	return nil
}

func (s *LoginLog) Add() (pk interface{}, err error) {
	if err = s.check(); err != nil {
		return nil, err
	}
	return s.NgingLoginLog.Add()
}

func (s *LoginLog) Edit(mw func(db.Result) db.Result, args ...interface{}) (err error) {
	if err = s.check(); err != nil {
		return err
	}
	return s.NgingLoginLog.Edit(mw, args...)
}

func (s *LoginLog) ListPage(cond *db.Compounds, sorts ...interface{}) ([]*dbschema.NgingLoginLog, error) {
	_, err := common.NewLister(s, nil, func(r db.Result) db.Result {
		return r.OrderBy(sorts...)
	}, cond.And()).Paging(s.base.Context)
	return s.Objects(), err
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package model

import (
	"strconv"
	"time"

	"github.com/webx-top/com"
	"github.com/webx-top/db"
	"github.com/webx-top/echo"

	"github.com/admpub/nging/v3/application/dbschema"
	"github.com/admpub/nging/v3/application/library/common"
	"github.com/admpub/nging/v3/application/library/ip2region"
	"github.com/admpub/nging/v3/application/model/base"
)

func NewLoginLog(ctx echo.Context) *LoginLog {
	m := &LoginLog{
		NgingLoginLog: &dbschema.NgingLoginLog{},
		base:          base.New(ctx),
	}
	m.SetContext(ctx)
	return m
}

type LoginLog struct {
	*dbschema.NgingLoginLog
	base *base.Base
}

func (s *LoginLog) check() error {
	k := `backend.Anonymous`
	if s.OwnerType != `user` {
		k = `frontend.Anonymous`
	}
	if !echo.Bool(k) {
		s.IpAddress = s.base.RealIP()
		ipInfo, err := ip2region.IPInfo(s.IpAddress)
		if err != nil {
			return err
		}
		s.IpLocation = ip2region.Stringify(ipInfo)
		s.UserAgent = s.base.Request().UserAgent()
	}
	s.Success = common.GetBoolFlag(s.Success)
	s.Errpwd = com.MaskString(s.Errpwd)
	day, _ := strconv.Atoi(time.Now().Format(`20060102`))
	s.Day = uint(day)
	return nil
}

func (s *LoginLog) Add() (pk interface{}, err error) {
	if err = s.check(); err != nil {
		return nil, err
	}
	return s.NgingLoginLog.Add()
}

func (s *LoginLog) Edit(mw func(db.Result) db.Result, args ...interface{}) (err error) {
	if err = s.check(); err != nil {
		return err
	}
	return s.NgingLoginLog.Edit(mw, args...)
}

func (s *LoginLog) ListPage(cond *db.Compounds, sorts ...interface{}) ([]*dbschema.NgingLoginLog, error) {
	_, err := common.NewLister(s, nil, func(r db.Result) db.Result {
		return r.OrderBy(sorts...)
	}, cond.And()).Paging(s.base.Context)
	return s.Objects(), err
}
<|endoftext|>


<commit_before>// package roundrobin implements dynamic weighted round robin load balancer http handler
package roundrobin

import (
	"fmt"
	"net/http"
	"net/url"
	"sync"

	"github.com/vulcand/oxy/utils"
)

// Weight is an optional functional argument that sets weight of the server
func Weight(w int) ServerOption {
	return func(s *server) error {
		if w < 0 {
			return fmt.Errorf("Weight should be >= 0")
		}
		s.weight = w
		return nil
	}
}

// ErrorHandler is a functional argument that sets error handler of the server
func ErrorHandler(h utils.ErrorHandler) LBOption {
	return func(s *RoundRobin) error {
		s.errHandler = h
		return nil
	}
}

type RoundRobin struct {
	mutex      *sync.Mutex
	next       http.Handler
	errHandler utils.ErrorHandler
	// Current index (starts from -1)
	index         int
	servers       []*server
	currentWeight int
}

func New(next http.Handler, opts ...LBOption) (*RoundRobin, error) {
	rr := &RoundRobin{
		next:    next,
		index:   -1,
		mutex:   &sync.Mutex{},
		servers: []*server{},
	}
	for _, o := range opts {
		if err := o(rr); err != nil {
			return nil, err
		}
	}
	if rr.errHandler == nil {
		rr.errHandler = utils.DefaultHandler
	}
	return rr, nil
}

func (r *RoundRobin) Next() http.Handler {
	return r.next
}

func (r *RoundRobin) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	url, err := r.NextServer()
	if err != nil {
		r.errHandler.ServeHTTP(w, req, err)
		return
	}
	// make shallow copy of request before chaning anything to avoid side effects
	newReq := *req
	newReq.URL = url
	r.next.ServeHTTP(w, &newReq)
}

func (r *RoundRobin) NextServer() (*url.URL, error) {
	srv, err := r.nextServer()
	if err != nil {
		return nil, err
	}
	return utils.CopyURL(srv.url), nil
}

func (r *RoundRobin) nextServer() (*server, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if len(r.servers) == 0 {
		return nil, fmt.Errorf("no servers in the pool")
	}

	// The algo below may look messy, but is actually very simple
	// it calculates the GCD  and subtracts it on every iteration, what interleaves servers
	// and allows us not to build an iterator every time we readjust weights

	// GCD across all enabled servers
	gcd := r.weightGcd()
	// Maximum weight across all enabled servers
	max := r.maxWeight()

	for {
		r.index = (r.index + 1) % len(r.servers)
		if r.index == 0 {
			r.currentWeight = r.currentWeight - gcd
			if r.currentWeight <= 0 {
				r.currentWeight = max
				if r.currentWeight == 0 {
					return nil, fmt.Errorf("all servers have 0 weight")
				}
			}
		}
		srv := r.servers[r.index]
		if srv.weight >= r.currentWeight {
			return srv, nil
		}
	}
	// We did full circle and found no available servers
	return nil, fmt.Errorf("no available servers")
}

func (r *RoundRobin) RemoveServer(u *url.URL) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	e, index := r.findServerByURL(u)
	if e == nil {
		return fmt.Errorf("server not found")
	}
	r.servers = append(r.servers[:index], r.servers[index+1:]...)
	r.resetState()
	return nil
}

func (rr *RoundRobin) Servers() []*url.URL {
	rr.mutex.Lock()
	defer rr.mutex.Unlock()

	out := make([]*url.URL, len(rr.servers))
	for i, srv := range rr.servers {
		out[i] = srv.url
	}
	return out
}

func (rr *RoundRobin) ServerWeight(u *url.URL) (int, bool) {
	rr.mutex.Lock()
	defer rr.mutex.Unlock()

	if s, _ := rr.findServerByURL(u); s != nil {
		return s.weight, true
	}
	return -1, false
}

// In case if server is already present in the load balancer, returns error
func (rr *RoundRobin) UpsertServer(u *url.URL, options ...ServerOption) error {
	rr.mutex.Lock()
	defer rr.mutex.Unlock()

	if u == nil {
		return fmt.Errorf("server URL can't be nil")
	}

	if s, _ := rr.findServerByURL(u); s != nil {
		for _, o := range options {
			if err := o(s); err != nil {
				return err
			}
		}
		rr.resetState()
		return nil
	}

	srv := &server{url: utils.CopyURL(u)}
	for _, o := range options {
		if err := o(srv); err != nil {
			return err
		}
	}

	if srv.weight == 0 {
		srv.weight = defaultWeight
	}

	rr.servers = append(rr.servers, srv)
	rr.resetState()
	return nil
}

func (r *RoundRobin) resetIterator() {
	r.index = -1
	r.currentWeight = 0
}

func (r *RoundRobin) resetState() {
	r.resetIterator()
}

func (r *RoundRobin) findServerByURL(u *url.URL) (*server, int) {
	if len(r.servers) == 0 {
		return nil, -1
	}
	for i, s := range r.servers {
		if sameURL(u, s.url) {
			return s, i
		}
	}
	return nil, -1
}

func (rr *RoundRobin) maxWeight() int {
	max := -1
	for _, s := range rr.servers {
		if s.weight > max {
			max = s.weight
		}
	}
	return max
}

func (rr *RoundRobin) weightGcd() int {
	divisor := -1
	for _, s := range rr.servers {
		if divisor == -1 {
			divisor = s.weight
		} else {
			divisor = gcd(divisor, s.weight)
		}
	}
	return divisor
}

func gcd(a, b int) int {
	for b != 0 {
		a, b = b, a%b
	}
	return a
}

// ServerOption provides various options for server, e.g. weight
type ServerOption func(*server) error

// LBOption provides options for load balancer
type LBOption func(*RoundRobin) error

// Set additional parameters for the server can be supplied when adding server
type server struct {
	url *url.URL
	// Relative weight for the enpoint to other enpoints in the load balancer
	weight int
}

const defaultWeight = 1

func sameURL(a, b *url.URL) bool {
	return a.Path == b.Path && a.Host == b.Host && a.Scheme == b.Scheme
}

type balancerHandler interface {
	Servers() []*url.URL
	ServeHTTP(w http.ResponseWriter, req *http.Request)
	ServerWeight(u *url.URL) (int, bool)
	RemoveServer(u *url.URL) error
	UpsertServer(u *url.URL, options ...ServerOption) error
	NextServer() (*url.URL, error)
	Next() http.Handler
}
<commit_msg>qnd cookie hacking<commit_after>// package roundrobin implements dynamic weighted round robin load balancer http handler
package roundrobin

import (
	"fmt"
	"net/http"
	"net/url"
	"sync"

	"github.com/vulcand/oxy/utils"
)

// Weight is an optional functional argument that sets weight of the server
func Weight(w int) ServerOption {
	return func(s *server) error {
		if w < 0 {
			return fmt.Errorf("Weight should be >= 0")
		}
		s.weight = w
		return nil
	}
}

// ErrorHandler is a functional argument that sets error handler of the server
func ErrorHandler(h utils.ErrorHandler) LBOption {
	return func(s *RoundRobin) error {
		s.errHandler = h
		return nil
	}
}

type RoundRobin struct {
	mutex      *sync.Mutex
	next       http.Handler
	errHandler utils.ErrorHandler
	// Current index (starts from -1)
	index         int
	servers       []*server
	currentWeight int
}

func New(next http.Handler, opts ...LBOption) (*RoundRobin, error) {
	rr := &RoundRobin{
		next:    next,
		index:   -1,
		mutex:   &sync.Mutex{},
		servers: []*server{},
	}
	for _, o := range opts {
		if err := o(rr); err != nil {
			return nil, err
		}
	}
	if rr.errHandler == nil {
		rr.errHandler = utils.DefaultHandler
	}
	return rr, nil
}

func (r *RoundRobin) Next() http.Handler {
	return r.next
}

func (r *RoundRobin) getCookieVal(req *http.Request) (*url.URL, bool, error) {
	// Before we move on to a new server, peek at our req cookie to see if we are bound.
	// If so, serve to them.
	cookie, err := req.Cookie("__STICKY_SVR")
	switch err {
	case nil:
	case http.ErrNoCookie:
		return nil, false, nil
	default:
		return nil, false, err

	}

	s_url, err := url.Parse(cookie.Value)
	if err != nil {
		return nil, false, err
	}

	s, i := r.findServerByURL(s_url)
	if i != -1 {
		return s.url, true, nil
	} else {
		return nil, false, nil
	}

}
func (r *RoundRobin) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// make shallow copy of request before chaning anything to avoid side effects
	newReq := *req
	cookie_url, present, err := r.getCookieVal(&newReq)

	if err != nil {
		r.errHandler.ServeHTTP(w, req, err)
		return
	}
	if present {
		newReq.URL = cookie_url
	} else {
		url, err := r.NextServer()
		if err != nil {
			r.errHandler.ServeHTTP(w, req, err)
			return
		}
		newReq.URL = url
	}
	r.next.ServeHTTP(w, &newReq)
}

func (r *RoundRobin) NextServer() (*url.URL, error) {
	srv, err := r.nextServer()
	if err != nil {
		return nil, err
	}
	return utils.CopyURL(srv.url), nil
}

func (r *RoundRobin) nextServer() (*server, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if len(r.servers) == 0 {
		return nil, fmt.Errorf("no servers in the pool")
	}

	// The algo below may look messy, but is actually very simple
	// it calculates the GCD  and subtracts it on every iteration, what interleaves servers
	// and allows us not to build an iterator every time we readjust weights

	// GCD across all enabled servers
	gcd := r.weightGcd()
	// Maximum weight across all enabled servers
	max := r.maxWeight()

	for {
		r.index = (r.index + 1) % len(r.servers)
		if r.index == 0 {
			r.currentWeight = r.currentWeight - gcd
			if r.currentWeight <= 0 {
				r.currentWeight = max
				if r.currentWeight == 0 {
					return nil, fmt.Errorf("all servers have 0 weight")
				}
			}
		}
		srv := r.servers[r.index]
		if srv.weight >= r.currentWeight {
			return srv, nil
		}
	}
	// We did full circle and found no available servers
	return nil, fmt.Errorf("no available servers")
}

func (r *RoundRobin) RemoveServer(u *url.URL) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	e, index := r.findServerByURL(u)
	if e == nil {
		return fmt.Errorf("server not found")
	}
	r.servers = append(r.servers[:index], r.servers[index+1:]...)
	r.resetState()
	return nil
}

func (rr *RoundRobin) Servers() []*url.URL {
	rr.mutex.Lock()
	defer rr.mutex.Unlock()

	out := make([]*url.URL, len(rr.servers))
	for i, srv := range rr.servers {
		out[i] = srv.url
	}
	return out
}

func (rr *RoundRobin) ServerWeight(u *url.URL) (int, bool) {
	rr.mutex.Lock()
	defer rr.mutex.Unlock()

	if s, _ := rr.findServerByURL(u); s != nil {
		return s.weight, true
	}
	return -1, false
}

// In case if server is already present in the load balancer, returns error
func (rr *RoundRobin) UpsertServer(u *url.URL, options ...ServerOption) error {
	rr.mutex.Lock()
	defer rr.mutex.Unlock()

	if u == nil {
		return fmt.Errorf("server URL can't be nil")
	}

	if s, _ := rr.findServerByURL(u); s != nil {
		for _, o := range options {
			if err := o(s); err != nil {
				return err
			}
		}
		rr.resetState()
		return nil
	}

	srv := &server{url: utils.CopyURL(u)}
	for _, o := range options {
		if err := o(srv); err != nil {
			return err
		}
	}

	if srv.weight == 0 {
		srv.weight = defaultWeight
	}

	rr.servers = append(rr.servers, srv)
	rr.resetState()
	return nil
}

func (r *RoundRobin) resetIterator() {
	r.index = -1
	r.currentWeight = 0
}

func (r *RoundRobin) resetState() {
	r.resetIterator()
}

func (r *RoundRobin) findServerByURL(u *url.URL) (*server, int) {
	if len(r.servers) == 0 {
		return nil, -1
	}
	for i, s := range r.servers {
		if sameURL(u, s.url) {
			return s, i
		}
	}
	return nil, -1
}

func (rr *RoundRobin) maxWeight() int {
	max := -1
	for _, s := range rr.servers {
		if s.weight > max {
			max = s.weight
		}
	}
	return max
}

func (rr *RoundRobin) weightGcd() int {
	divisor := -1
	for _, s := range rr.servers {
		if divisor == -1 {
			divisor = s.weight
		} else {
			divisor = gcd(divisor, s.weight)
		}
	}
	return divisor
}

func gcd(a, b int) int {
	for b != 0 {
		a, b = b, a%b
	}
	return a
}

// ServerOption provides various options for server, e.g. weight
type ServerOption func(*server) error

// LBOption provides options for load balancer
type LBOption func(*RoundRobin) error

// Set additional parameters for the server can be supplied when adding server
type server struct {
	url *url.URL
	// Relative weight for the enpoint to other enpoints in the load balancer
	weight int
}

const defaultWeight = 1

func sameURL(a, b *url.URL) bool {
	return a.Path == b.Path && a.Host == b.Host && a.Scheme == b.Scheme
}

type balancerHandler interface {
	Servers() []*url.URL
	ServeHTTP(w http.ResponseWriter, req *http.Request)
	ServerWeight(u *url.URL) (int, bool)
	RemoveServer(u *url.URL) error
	UpsertServer(u *url.URL, options ...ServerOption) error
	NextServer() (*url.URL, error)
	Next() http.Handler
}
<|endoftext|>