<commit_before>package service

import (
	"crypto/rand"
	"encoding/base64"
	"time"

	"github.com/kolide/kolide-ose/server/contexts/viewer"
	"github.com/kolide/kolide-ose/server/kolide"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

func (svc service) NewUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
	err := svc.VerifyInvite(ctx, *p.Email, *p.InviteToken)
	if err != nil {
		return nil, err
	}
	invite, err := svc.ds.InviteByEmail(*p.Email)
	if err != nil {
		return nil, err
	}
	user, err := svc.newUser(p)
	if err != nil {
		return nil, err
	}
	err = svc.ds.DeleteInvite(invite)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (svc service) NewAdminCreatedUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
	return svc.newUser(p)
}

func (svc service) newUser(p kolide.UserPayload) (*kolide.User, error) {
	user, err := p.User(svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
	if err != nil {
		return nil, err
	}
	user, err = svc.ds.NewUser(user)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPayload) (*kolide.User, error) {
	user, err := svc.User(ctx, userID)
	if err != nil {
		return nil, err
	}

	// the method assumes that the correct authorization
	// has been validated higher up the stack
	if p.Username != nil {
		user.Username = *p.Username
	}

	if p.Name != nil {
		user.Name = *p.Name
	}

	if p.Admin != nil {
		user.Admin = *p.Admin
	}

	if p.Email != nil {
		user.Email = *p.Email
	}

	if p.Enabled != nil {
		user.Enabled = *p.Enabled
	}

	if p.Position != nil {
		user.Position = *p.Position
	}

	if p.GravatarURL != nil {
		user.GravatarURL = *p.GravatarURL
	}

	if p.Password != nil {
		err := user.SetPassword(
			*p.Password,
			svc.config.Auth.SaltKeySize,
			svc.config.Auth.BcryptCost,
		)
		if err != nil {
			return nil, err
		}
		user.AdminForcedPasswordReset = false
	}

	err = svc.saveUser(user)
	if err != nil {
		return nil, err
	}

	// https://github.com/kolide/kolide-ose/issues/351
	// Calling this action last, because svc.RequestPasswordReset saves the
	// user separately and we don't want to override the value set there
	if p.AdminForcedPasswordReset != nil && *p.AdminForcedPasswordReset {
		err = svc.RequestPasswordReset(ctx, user.Email)
		if err != nil {
			return nil, err
		}
	}
	return svc.User(ctx, userID)
}

func (svc service) User(ctx context.Context, id uint) (*kolide.User, error) {
	return svc.ds.UserByID(id)
}

func (svc service) AuthenticatedUser(ctx context.Context) (*kolide.User, error) {
	vc, ok := viewer.FromContext(ctx)
	if !ok {
		return nil, errNoContext
	}
	if !vc.IsLoggedIn() {
		return nil, permissionError{}
	}
	return vc.User, nil
}

func (svc service) ListUsers(ctx context.Context, opt kolide.ListOptions) ([]*kolide.User, error) {
	return svc.ds.ListUsers(opt)
}

// setNewPassword is a helper for changing a user's password. It should be
// called to set the new password after proper authorization has been
// performed.
func (svc service) setNewPassword(ctx context.Context, user *kolide.User, password string) error {
	err := user.SetPassword(password, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
	if err != nil {
		return errors.Wrap(err, "setting new password")
	}

	err = svc.saveUser(user)
	if err != nil {
		return errors.Wrap(err, "saving changed password")
	}

	return nil
}

func (svc service) ChangePassword(ctx context.Context, oldPass, newPass string) error {
	vc, ok := viewer.FromContext(ctx)
	if !ok {
		return errNoContext
	}

	if err := vc.User.ValidatePassword(oldPass); err != nil {
		return errors.Wrap(err, "password validation failed")
	}

	return errors.Wrap(svc.setNewPassword(ctx, vc.User, newPass), "setting new password")
}

func (svc service) ResetPassword(ctx context.Context, token, password string) error {
	reset, err := svc.ds.FindPassswordResetByToken(token)
	if err != nil {
		return errors.Wrap(err, "looking up reset by token")
	}
	user, err := svc.User(ctx, reset.UserID)
	if err != nil {
		return errors.Wrap(err, "retrieving user")
	}

	err = svc.setNewPassword(ctx, user, password)
	if err != nil {
		return errors.Wrap(err, "setting new password")
	}

	// delete password reset tokens for user
	if err := svc.ds.DeletePasswordResetRequestsForUser(user.ID); err != nil {
		return errors.Wrap(err, "deleting password reset requests")
	}

	// Clear sessions so that any other browsers will have to log in with
	// the new password
	if err := svc.DeleteSessionsForUser(ctx, user.ID); err != nil {
		return errors.Wrap(err, "deleting user sessions")
	}

	return nil
}

func (svc service) RequestPasswordReset(ctx context.Context, email string) error {
	// the password reset is different depending on whether performed by an
	// admin or a user
	// if an admin requests a password reset, then no token is
	// generated, instead the AdminForcedPasswordReset flag is set
	user, err := svc.ds.UserByEmail(email)
	if err != nil {
		return err
	}
	vc, ok := viewer.FromContext(ctx)
	if ok {
		if vc.IsAdmin() {
			user.AdminForcedPasswordReset = true
			if err := svc.saveUser(user); err != nil {
				return err
			}
			// Sessions should only be cleared if this is an admin
			// forced password reset
			if err := svc.DeleteSessionsForUser(ctx, user.ID); err != nil {
				return err
			}
			return nil
		}
	}

	random, err := kolide.RandomText(svc.config.App.TokenKeySize)
	if err != nil {
		return err
	}
	token := base64.URLEncoding.EncodeToString([]byte(random))

	request := &kolide.PasswordResetRequest{
		ExpiresAt: time.Now().Add(time.Hour * 24),
		UserID:    user.ID,
		Token:     token,
	}
	request, err = svc.ds.NewPasswordResetRequest(request)
	if err != nil {
		return err
	}

	config, err := svc.AppConfig(ctx)
	if err != nil {
		return err
	}

	resetEmail := kolide.Email{
		Subject: "Reset Your Kolide Password",
		To:      []string{user.Email},
		Config:  config,
		Mailer: &kolide.PasswordResetMailer{
			KolideServerURL: config.KolideServerURL,
			Token:           token,
		},
	}

	err = svc.mailService.SendEmail(resetEmail)
	if err != nil {
		return err
	}

	return nil
}

// saves user in datastore.
// doesn't need to be exposed to the transport
// the service should expose actions for modifying a user instead
func (svc service) saveUser(user *kolide.User) error {
	return svc.ds.SaveUser(user)
}

// generateRandomText return a string generated by filling in keySize bytes with
// random data and then base64 encoding those bytes
func generateRandomText(keySize int) (string, error) {
	key := make([]byte, keySize)
	_, err := rand.Read(key)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(key), nil
}
<commit_msg>create user from invite: set admin property from invite. (#675)<commit_after>package service

import (
	"crypto/rand"
	"encoding/base64"
	"time"

	"github.com/kolide/kolide-ose/server/contexts/viewer"
	"github.com/kolide/kolide-ose/server/kolide"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

func (svc service) NewUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
	err := svc.VerifyInvite(ctx, *p.Email, *p.InviteToken)
	if err != nil {
		return nil, err
	}
	invite, err := svc.ds.InviteByEmail(*p.Email)
	if err != nil {
		return nil, err
	}

	// set the payload Admin property based on an existing invite.
	p.Admin = &invite.Admin

	user, err := svc.newUser(p)
	if err != nil {
		return nil, err
	}
	err = svc.ds.DeleteInvite(invite)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (svc service) NewAdminCreatedUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
	return svc.newUser(p)
}

func (svc service) newUser(p kolide.UserPayload) (*kolide.User, error) {
	user, err := p.User(svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
	if err != nil {
		return nil, err
	}
	user, err = svc.ds.NewUser(user)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPayload) (*kolide.User, error) {
	user, err := svc.User(ctx, userID)
	if err != nil {
		return nil, err
	}

	// the method assumes that the correct authorization
	// has been validated higher up the stack
	if p.Username != nil {
		user.Username = *p.Username
	}

	if p.Name != nil {
		user.Name = *p.Name
	}

	if p.Admin != nil {
		user.Admin = *p.Admin
	}

	if p.Email != nil {
		user.Email = *p.Email
	}

	if p.Enabled != nil {
		user.Enabled = *p.Enabled
	}

	if p.Position != nil {
		user.Position = *p.Position
	}

	if p.GravatarURL != nil {
		user.GravatarURL = *p.GravatarURL
	}

	if p.Password != nil {
		err := user.SetPassword(
			*p.Password,
			svc.config.Auth.SaltKeySize,
			svc.config.Auth.BcryptCost,
		)
		if err != nil {
			return nil, err
		}
		user.AdminForcedPasswordReset = false
	}

	err = svc.saveUser(user)
	if err != nil {
		return nil, err
	}

	// https://github.com/kolide/kolide-ose/issues/351
	// Calling this action last, because svc.RequestPasswordReset saves the
	// user separately and we don't want to override the value set there
	if p.AdminForcedPasswordReset != nil && *p.AdminForcedPasswordReset {
		err = svc.RequestPasswordReset(ctx, user.Email)
		if err != nil {
			return nil, err
		}
	}
	return svc.User(ctx, userID)
}

func (svc service) User(ctx context.Context, id uint) (*kolide.User, error) {
	return svc.ds.UserByID(id)
}

func (svc service) AuthenticatedUser(ctx context.Context) (*kolide.User, error) {
	vc, ok := viewer.FromContext(ctx)
	if !ok {
		return nil, errNoContext
	}
	if !vc.IsLoggedIn() {
		return nil, permissionError{}
	}
	return vc.User, nil
}

func (svc service) ListUsers(ctx context.Context, opt kolide.ListOptions) ([]*kolide.User, error) {
	return svc.ds.ListUsers(opt)
}

// setNewPassword is a helper for changing a user's password. It should be
// called to set the new password after proper authorization has been
// performed.
func (svc service) setNewPassword(ctx context.Context, user *kolide.User, password string) error {
	err := user.SetPassword(password, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
	if err != nil {
		return errors.Wrap(err, "setting new password")
	}

	err = svc.saveUser(user)
	if err != nil {
		return errors.Wrap(err, "saving changed password")
	}

	return nil
}

func (svc service) ChangePassword(ctx context.Context, oldPass, newPass string) error {
	vc, ok := viewer.FromContext(ctx)
	if !ok {
		return errNoContext
	}

	if err := vc.User.ValidatePassword(oldPass); err != nil {
		return errors.Wrap(err, "password validation failed")
	}

	return errors.Wrap(svc.setNewPassword(ctx, vc.User, newPass), "setting new password")
}

func (svc service) ResetPassword(ctx context.Context, token, password string) error {
	reset, err := svc.ds.FindPassswordResetByToken(token)
	if err != nil {
		return errors.Wrap(err, "looking up reset by token")
	}
	user, err := svc.User(ctx, reset.UserID)
	if err != nil {
		return errors.Wrap(err, "retrieving user")
	}

	err = svc.setNewPassword(ctx, user, password)
	if err != nil {
		return errors.Wrap(err, "setting new password")
	}

	// delete password reset tokens for user
	if err := svc.ds.DeletePasswordResetRequestsForUser(user.ID); err != nil {
		return errors.Wrap(err, "deleting password reset requests")
	}

	// Clear sessions so that any other browsers will have to log in with
	// the new password
	if err := svc.DeleteSessionsForUser(ctx, user.ID); err != nil {
		return errors.Wrap(err, "deleting user sessions")
	}

	return nil
}

func (svc service) RequestPasswordReset(ctx context.Context, email string) error {
	// the password reset is different depending on whether performed by an
	// admin or a user
	// if an admin requests a password reset, then no token is
	// generated, instead the AdminForcedPasswordReset flag is set
	user, err := svc.ds.UserByEmail(email)
	if err != nil {
		return err
	}
	vc, ok := viewer.FromContext(ctx)
	if ok {
		if vc.IsAdmin() {
			user.AdminForcedPasswordReset = true
			if err := svc.saveUser(user); err != nil {
				return err
			}
			// Sessions should only be cleared if this is an admin
			// forced password reset
			if err := svc.DeleteSessionsForUser(ctx, user.ID); err != nil {
				return err
			}
			return nil
		}
	}

	random, err := kolide.RandomText(svc.config.App.TokenKeySize)
	if err != nil {
		return err
	}
	token := base64.URLEncoding.EncodeToString([]byte(random))

	request := &kolide.PasswordResetRequest{
		ExpiresAt: time.Now().Add(time.Hour * 24),
		UserID:    user.ID,
		Token:     token,
	}
	request, err = svc.ds.NewPasswordResetRequest(request)
	if err != nil {
		return err
	}

	config, err := svc.AppConfig(ctx)
	if err != nil {
		return err
	}

	resetEmail := kolide.Email{
		Subject: "Reset Your Kolide Password",
		To:      []string{user.Email},
		Config:  config,
		Mailer: &kolide.PasswordResetMailer{
			KolideServerURL: config.KolideServerURL,
			Token:           token,
		},
	}

	err = svc.mailService.SendEmail(resetEmail)
	if err != nil {
		return err
	}

	return nil
}

// saves user in datastore.
// doesn't need to be exposed to the transport
// the service should expose actions for modifying a user instead
func (svc service) saveUser(user *kolide.User) error {
	return svc.ds.SaveUser(user)
}

// generateRandomText return a string generated by filling in keySize bytes with
// random data and then base64 encoding those bytes
func generateRandomText(keySize int) (string, error) {
	key := make([]byte, keySize)
	_, err := rand.Read(key)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(key), nil
}
<|endoftext|>


<commit_before>package gannoy

import (
	"math"
)

type Distance interface {
	createSplit([]Node, Random, Node) Node
	distance([]float64, []float64) float64
	side(Node, []float64, Random) int
	margin(Node, []float64) float64
}

type Angular struct {
}

func (a Angular) createSplit(nodes []Node, random Random, n Node) Node {
	bestIv, bestJv := twoMeans(a, nodes, random, true)
	v := make([]float64, len(nodes[0].v))
	for z, _ := range v {
		v[z] = bestIv[z] - bestJv[z]
	}
	n.v = normalize(n.v)
	return n
}

func (a Angular) distance(x, y []float64) float64 {
	var pp, qq, pq float64
	for z, xz := range x {
		pp += xz * xz
		qq += y[z] * y[z]
		pq += xz * y[z]
	}
	ppqq := pp * qq
	if ppqq > 0 {
		return 2.0 - 2.0*pq/math.Sqrt(ppqq)
	}
	return 2.0
}

func (a Angular) side(n Node, y []float64, random Random) int {
	dot := a.margin(n, y)
	if dot != 0.0 {
		if dot > 0 {
			return 1
		} else {
			return 0
		}
	}
	return random.flip()
}

func (a Angular) margin(n Node, y []float64) float64 {
	dot := 0.0
	for z, v := range n.v {
		dot += v * y[z]
	}
	return dot
}
<commit_msg>Fixed a bug that happen panic when do makeTree.<commit_after>package gannoy

import (
	"math"
)

type Distance interface {
	createSplit([]Node, Random, Node) Node
	distance([]float64, []float64) float64
	side(Node, []float64, Random) int
	margin(Node, []float64) float64
}

type Angular struct {
}

func (a Angular) createSplit(nodes []Node, random Random, n Node) Node {
	bestIv, bestJv := twoMeans(a, nodes, random, true)
	v := make([]float64, len(nodes[0].v))
	for z, _ := range v {
		v[z] = bestIv[z] - bestJv[z]
	}
	n.v = normalize(v)
	return n
}

func (a Angular) distance(x, y []float64) float64 {
	var pp, qq, pq float64
	for z, xz := range x {
		pp += xz * xz
		qq += y[z] * y[z]
		pq += xz * y[z]
	}
	ppqq := pp * qq
	if ppqq > 0 {
		return 2.0 - 2.0*pq/math.Sqrt(ppqq)
	}
	return 2.0
}

func (a Angular) side(n Node, y []float64, random Random) int {
	dot := a.margin(n, y)
	if dot != 0.0 {
		if dot > 0 {
			return 1
		} else {
			return 0
		}
	}
	return random.flip()
}

func (a Angular) margin(n Node, y []float64) float64 {
	dot := 0.0
	for z, v := range n.v {
		dot += v * y[z]
	}
	return dot
}
<|endoftext|>


<commit_before>package public

import (
	"fmt"
	"strings"

	"bitbucket.org/ikeikeikeike/antenna/models"
	"bitbucket.org/ikeikeikeike/antenna/models/anime"
	"bitbucket.org/ikeikeikeike/antenna/models/diva"

	"github.com/astaxie/beego/orm"
	"github.com/astaxie/beego/utils/pagination"
	"github.com/ikeikeikeike/gopkg/convert"
	// "github.com/k0kubun/pp"
)

type EntriesController struct {
	BaseController
}

// func (c *EntriesController) NestPrepare() {}

func (c *EntriesController) NestFinish() {
	c.PushInAccessLog()
}

func (c *EntriesController) Home() {
	c.TplNames = "public/entries/home.tpl"

	var (
		divas     []*models.Diva
		animes    []*models.Anime
		entries   []*models.Entry
		summaries []*models.Summary
		pers      = c.DefaultPers
	)

	dqs := c.SetBracup(c.SetBlood(c.SetPrefixLines(diva.StarringDivas().RelatedSel(), ""), ""), "")
	aqs := c.SetPrefixLines(anime.StarringAnimes().RelatedSel(), "")

	v := c.GetString("q")
	if v != "" {
		for _, word := range convert.StrTo(v).MultiWord() {
			c := orm.NewCondition()
			c = c.Or("name__icontains", word)
			c = c.Or("kana__icontains", word)

			dqs = dqs.SetCond(c)
			aqs = aqs.SetCond(c)
		}
	}
	dqs.Limit(4).All(&divas)
	aqs.Limit(4).All(&animes)

	c.SetAdvancedSearch(models.Entries().RelatedSel(), "").Limit(pers).All(&entries)
	c.SetAdvancedSearch(models.Summaries().RelatedSel(), "entry__").RelatedSel().Limit(pers).All(&summaries)

	c.Data["Divas"] = divas
	c.Data["Animes"] = animes
	c.Data["Entries"] = entries
	c.Data["Summaries"] = summaries
}

func (c *EntriesController) News() {
	c.TplNames = "public/entries/news.tpl"

	pers := c.DefaultPers
	qs := c.SetAdvancedSearch(models.Entries().RelatedSel(), "")

	cnt, _ := models.CountObjects(qs)
	pager := pagination.SetPaginator(c.Ctx, pers, cnt)

	qs = qs.Limit(pers, pager.Offset())

	var entries []*models.Entry
	models.ListObjects(qs, &entries)

	c.Data["QURL"] = ""
	c.Data["Entries"] = entries
}

func (c *EntriesController) Hots() {
	c.TplNames = "public/entries/hots.tpl"

	pers := c.DefaultPers
	qs := c.SetAdvancedSearch(models.Summaries().RelatedSel(), "entry__")

	cnt, _ := models.CountObjects(qs)
	pager := pagination.SetPaginator(c.Ctx, pers, cnt)

	qs = qs.Limit(pers, pager.Offset())

	var summaries []*models.Summary
	models.ListObjects(qs, &summaries)

	c.Data["QURL"] = ""
	c.Data["Summaries"] = summaries
}

func (c *EntriesController) Show() {
	c.TplNames = "public/entries/show.tpl"

	id := c.Ctx.Input.Param(":id")
	if id == "" {
		c.Ctx.Abort(404, "404 NotFound")
		return
	}

	uid, _ := convert.StrTo(id).Int64()
	s := &models.Entry{Id: uid}
	s.Read()

	if !s.IsLiving() {
		c.Ctx.Abort(404, "404 NotFound")
		return
	}

	s.Blog.LoadRelated()
	if s.Video != nil {
		s.Video.LoadRelated()
	}
	if s.Picture != nil {
		s.Picture.LoadRelated()
	}

	var (
		in        []string
		summaries []*models.Summary
	)

	for _, t := range s.Tags {
		if t.Name != "" {
			in = append(in, t.Name)
		}
	}
	if len(in) <= 0 {
		in = append(in, "巨乳")
	}

	// models.Summaries().RelatedSel().
	// Filter("entry__tags__tag__name__in", in).
	// Limit(15).All(&summaries)
	//
	// 上記を `DISTINCT` 付きでやっている
	names := fmt.Sprintf("'%s'", strings.Join(in, "','"))
	q := fmt.Sprintf(`
	SELECT DISTINCT s.* FROM summary as s
	LEFT OUTER JOIN entry e ON e.id = s.entry_id
	LEFT OUTER JOIN blog b ON b.id = e.blog_id
	LEFT OUTER JOIN entry_tag et ON et.entry_id = e.id
	LEFT OUTER JOIN tag tag ON tag.id = et.tag_id
	WHERE (tag.name IN (%s) OR e.q like '%%%s%%') AND e.id != '%d'
	ORDER BY s.sort DESC LIMIT 4`, names, names[0], s.Id)
	orm.NewOrm().Raw(q).QueryRows(&summaries)

	c.Data["Summaries"] = summaries
	c.Data["Entry"] = s
}

func (c *EntriesController) Search() {
	c.TplNames = "public/entries/search.tpl"
}
<commit_msg>Reduce the choice blog to 3 items in show page<commit_after>package public

import (
	"fmt"
	"strings"

	"bitbucket.org/ikeikeikeike/antenna/models"
	"bitbucket.org/ikeikeikeike/antenna/models/anime"
	"bitbucket.org/ikeikeikeike/antenna/models/diva"

	"github.com/astaxie/beego/orm"
	"github.com/astaxie/beego/utils/pagination"
	"github.com/ikeikeikeike/gopkg/convert"
	// "github.com/k0kubun/pp"
)

type EntriesController struct {
	BaseController
}

// func (c *EntriesController) NestPrepare() {}

func (c *EntriesController) NestFinish() {
	c.PushInAccessLog()
}

func (c *EntriesController) Home() {
	c.TplNames = "public/entries/home.tpl"

	var (
		divas     []*models.Diva
		animes    []*models.Anime
		entries   []*models.Entry
		summaries []*models.Summary
		pers      = c.DefaultPers
	)

	dqs := c.SetBracup(c.SetBlood(c.SetPrefixLines(diva.StarringDivas().RelatedSel(), ""), ""), "")
	aqs := c.SetPrefixLines(anime.StarringAnimes().RelatedSel(), "")

	v := c.GetString("q")
	if v != "" {
		for _, word := range convert.StrTo(v).MultiWord() {
			c := orm.NewCondition()
			c = c.Or("name__icontains", word)
			c = c.Or("kana__icontains", word)

			dqs = dqs.SetCond(c)
			aqs = aqs.SetCond(c)
		}
	}
	dqs.Limit(4).All(&divas)
	aqs.Limit(4).All(&animes)

	c.SetAdvancedSearch(models.Entries().RelatedSel(), "").Limit(pers).All(&entries)
	c.SetAdvancedSearch(models.Summaries().RelatedSel(), "entry__").RelatedSel().Limit(pers).All(&summaries)

	c.Data["Divas"] = divas
	c.Data["Animes"] = animes
	c.Data["Entries"] = entries
	c.Data["Summaries"] = summaries
}

func (c *EntriesController) News() {
	c.TplNames = "public/entries/news.tpl"

	pers := c.DefaultPers
	qs := c.SetAdvancedSearch(models.Entries().RelatedSel(), "")

	cnt, _ := models.CountObjects(qs)
	pager := pagination.SetPaginator(c.Ctx, pers, cnt)

	qs = qs.Limit(pers, pager.Offset())

	var entries []*models.Entry
	models.ListObjects(qs, &entries)

	c.Data["QURL"] = ""
	c.Data["Entries"] = entries
}

func (c *EntriesController) Hots() {
	c.TplNames = "public/entries/hots.tpl"

	pers := c.DefaultPers
	qs := c.SetAdvancedSearch(models.Summaries().RelatedSel(), "entry__")

	cnt, _ := models.CountObjects(qs)
	pager := pagination.SetPaginator(c.Ctx, pers, cnt)

	qs = qs.Limit(pers, pager.Offset())

	var summaries []*models.Summary
	models.ListObjects(qs, &summaries)

	c.Data["QURL"] = ""
	c.Data["Summaries"] = summaries
}

func (c *EntriesController) Show() {
	c.TplNames = "public/entries/show.tpl"

	id := c.Ctx.Input.Param(":id")
	if id == "" {
		c.Ctx.Abort(404, "404 NotFound")
		return
	}

	uid, _ := convert.StrTo(id).Int64()
	s := &models.Entry{Id: uid}
	s.Read()

	if !s.IsLiving() {
		c.Ctx.Abort(404, "404 NotFound")
		return
	}

	s.Blog.LoadRelated()
	if s.Video != nil {
		s.Video.LoadRelated()
	}
	if s.Picture != nil {
		s.Picture.LoadRelated()
	}

	var (
		in        []string
		summaries []*models.Summary
	)

	for _, t := range s.Tags {
		if t.Name != "" {
			in = append(in, t.Name)
		}
	}
	if len(in) <= 0 {
		in = append(in, "巨乳")
	}

	// models.Summaries().RelatedSel().
	// Filter("entry__tags__tag__name__in", in).
	// Limit(15).All(&summaries)
	//
	// 上記を `DISTINCT` 付きでやっている
	names := fmt.Sprintf("'%s'", strings.Join(in, "','"))
	q := fmt.Sprintf(`
	SELECT DISTINCT s.* FROM summary as s
	LEFT OUTER JOIN entry e ON e.id = s.entry_id
	LEFT OUTER JOIN blog b ON b.id = e.blog_id
	LEFT OUTER JOIN entry_tag et ON et.entry_id = e.id
	LEFT OUTER JOIN tag tag ON tag.id = et.tag_id
	WHERE (tag.name IN (%s) OR e.q like '%%%s%%') AND e.id != '%d'
	ORDER BY s.sort DESC LIMIT 3`, names, names[0], s.Id)
	orm.NewOrm().Raw(q).QueryRows(&summaries)

	c.Data["Summaries"] = summaries
	c.Data["Entry"] = s
}

func (c *EntriesController) Search() {
	c.TplNames = "public/entries/search.tpl"
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	etcdc "github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"github.com/gtfierro/cs262-project/common"
	"sync"
	"time"
)

const LeaderKey = "leader"

type LeaderService struct {
	isLeader          bool
	leaderChangeRev   int64
	leaderLease       etcdc.LeaseID
	leaderLock        sync.RWMutex
	leaderWaitChans   []chan bool
	unleaderWaitChans []chan bool
	etcdConn          *EtcdConnection
	stop              chan bool
	waitGroup         sync.WaitGroup
	ipswitcher        IPSwitcher
}

func NewLeaderService(etcdConn *EtcdConnection, timeout time.Duration, ipswitcher IPSwitcher) *LeaderService {
	cs := new(LeaderService)
	cs.etcdConn = etcdConn
	cs.leaderChangeRev = -1
	cs.leaderWaitChans = []chan bool{}
	cs.unleaderWaitChans = []chan bool{}
	cs.stop = make(chan bool, 1)
	cs.ipswitcher = ipswitcher
	return cs
}

// Doesn't return. Watches for a lack of a leader and if so
// attempts to become the new leader
func (cs *LeaderService) WatchForLeadershipChange() {
	cs.waitGroup.Add(1)
	defer cs.waitGroup.Done()
	var watchResp etcdc.WatchResponse
	watchChan := cs.etcdConn.watcher.Watch(cs.etcdConn.GetCtx(), LeaderKey)
	for {
		select {
		case <-cs.stop:
			return
		case watchResp = <-watchChan:
		}
		if common.IsChanClosed(cs.stop) {
			return
		}
		if watchResp.Canceled {
			watchChan = cs.etcdConn.watcher.Watch(cs.etcdConn.GetCtx(), LeaderKey)
		}
		for _, event := range watchResp.Events {
			if event.Type == mvccpb.DELETE && string(event.Kv.Key) == LeaderKey { // Currently no leader!
				log.WithFields(log.Fields{
					"isCreate": event.IsCreate(), "isModify": event.IsModify(), "version": event.Kv.Version,
				}).Debug("WatchForLeadershipChange detected a deletion event!")
				cs.AttemptToBecomeLeader()
			}
		}
	}
}

func (cs *LeaderService) CancelWatch() {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	for _, waitChan := range cs.unleaderWaitChans {
		close(waitChan)
	}
	for _, waitChan := range cs.leaderWaitChans {
		close(waitChan)
	}
	close(cs.stop)
	cs.waitGroup.Wait()
}

// Maintain the leadership lease; doesn't return
func (cs *LeaderService) MaintainLeaderLease() {
	var waitChan chan bool
	cs.waitGroup.Add(1)
	defer cs.waitGroup.Done()
	for {
		// If we're not a leader, just wait... nothing to be done here
		waitChan = cs.WaitForLeadership()
		select {
		case <-cs.stop:
			return
		case <-waitChan:
			if common.IsChanClosed(cs.stop) {
				return
			}
		}
		waitChan = cs.WaitForNonleadership()

		// Acquire the IP
		cs.leaderLock.RLock()
		thinkIAmLeader := cs.isLeader
		cs.leaderLock.RUnlock()

		if thinkIAmLeader {
			err := cs.ipswitcher.AcquireIP()
			if err != nil {
				log.WithField("Error", err).Error("Could not acquire IP!")
			} else {
				log.Info("Successfully got IP!")
			}
		}

		select {
		case <-cs.stop:
			return
		case <-waitChan:
			if common.IsChanClosed(cs.stop) {
				return
			} else {
				continue
			}
		case <-time.After(3 * time.Second): // to maintain lease
			if common.IsChanClosed(cs.stop) {
				return
			}
			cs.leaderLock.RLock()
			_, err := cs.etcdConn.client.KeepAliveOnce(cs.etcdConn.GetCtx(), cs.leaderLease)
			cs.leaderLock.RUnlock()
			if err == rpctypes.ErrLeaseNotFound {
				log.Info("Lost leadership! Lease expired.")
				// Lost our lease; we are no longer the leader
				cs.AttemptToBecomeLeader()
			} else if err != nil {
				log.WithField("error", err).Error("Error while attempting to renew lease for leader key")
			}
		}
	}
}

func (cs *LeaderService) GetLeadershipChangeRevision() int64 {
	cs.leaderLock.RLock()
	defer cs.leaderLock.RUnlock()
	return cs.leaderChangeRev
}

// return true iff we became the leader which will happen only if there is
// currently no leader
func (cs *LeaderService) AttemptToBecomeLeader() (bool, error) {
	var (
		changeRev int64
		isLeader  bool
	)
	txn := cs.etcdConn.kv.Txn(cs.etcdConn.GetCtx())
	cmp := etcdc.Compare(etcdc.Version(LeaderKey), "=", 0)
	leaseResp, err := cs.etcdConn.client.Grant(cs.etcdConn.GetCtx(), 5)
	if err != nil {
		log.WithField("error", err).Error("Error while attempting to get a lease for a leader key!")
		return false, err
	}
	putKeyOp := etcdc.OpPut(LeaderKey, "", etcdc.WithLease(leaseResp.ID))
	getKeyOp := etcdc.OpGet(LeaderKey)
	txnResp, err := txn.If(cmp).Then(putKeyOp).Else(getKeyOp).Commit()
	if err != nil {
		return false, err
	}
	for _, resp := range txnResp.Responses {
		if r, ok := resp.Response.(*etcdserverpb.ResponseUnion_ResponseRange); ok && !txnResp.Succeeded {
			isLeader = false
			changeRev = r.ResponseRange.Kvs[0].ModRevision
		} else if r, ok := resp.Response.(*etcdserverpb.ResponseUnion_ResponsePut); ok && txnResp.Succeeded {
			isLeader = true
			changeRev = r.ResponsePut.GetHeader().Revision
			cs.leaderLock.Lock()
			cs.leaderLease = leaseResp.ID
			cs.leaderLock.Unlock()
		}
	}
	log.WithField("isLeader", isLeader).Info("Attempted to become leader")
	cs.SetLeader(isLeader, changeRev)
	return isLeader, nil
}

func (cs *LeaderService) IsLeader() bool {
	cs.leaderLock.RLock()
	defer cs.leaderLock.RUnlock()
	return cs.isLeader
}

func (cs *LeaderService) SetLeader(isLeader bool, changeRev int64) {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	cs.leaderChangeRev = changeRev
	cs.isLeader = isLeader
	if isLeader {
		for _, c := range cs.leaderWaitChans {
			close(c)
		}
		cs.leaderWaitChans = []chan bool{}
	} else {
		for _, c := range cs.unleaderWaitChans {
			close(c)
		}
		cs.unleaderWaitChans = []chan bool{}
	}
}

// Returns a channel which will be closed when this is leader
func (cs *LeaderService) WaitForLeadership() chan bool {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	// if these are not buffered channels, then sending on the channel
	// can block indefinitely and deadlock -- GTF
	c := make(chan bool, 1)
	if cs.isLeader {
		close(c)
		return c
	} else {
		cs.leaderWaitChans = append(cs.leaderWaitChans, c)
		return c
	}
}

// Returns a channel which will be closed when this is nonleader
func (cs *LeaderService) WaitForNonleadership() chan bool {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	// if these are not buffered channels, then sending on the channel
	// can block indefinitely and deadlock -- GTF
	c := make(chan bool, 1)
	if !cs.isLeader {
		c <- true
		return c
	} else {
		cs.unleaderWaitChans = append(cs.unleaderWaitChans, c)
		return c
	}
}
<commit_msg>Make the IP acquisition step only happen on actual leadership changes in the hope of being kinder to connection safety<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	etcdc "github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"github.com/gtfierro/cs262-project/common"
	"sync"
	"time"
)

const LeaderKey = "leader"

type LeaderService struct {
	isLeader          bool
	leaderChangeRev   int64
	leaderLease       etcdc.LeaseID
	leaderLock        sync.RWMutex
	leaderWaitChans   []chan bool
	unleaderWaitChans []chan bool
	etcdConn          *EtcdConnection
	stop              chan bool
	waitGroup         sync.WaitGroup
	ipswitcher        IPSwitcher
}

func NewLeaderService(etcdConn *EtcdConnection, timeout time.Duration, ipswitcher IPSwitcher) *LeaderService {
	cs := new(LeaderService)
	cs.etcdConn = etcdConn
	cs.leaderChangeRev = -1
	cs.leaderWaitChans = []chan bool{}
	cs.unleaderWaitChans = []chan bool{}
	cs.stop = make(chan bool, 1)
	cs.ipswitcher = ipswitcher
	return cs
}

// Doesn't return. Watches for a lack of a leader and if so
// attempts to become the new leader
func (cs *LeaderService) WatchForLeadershipChange() {
	cs.waitGroup.Add(1)
	defer cs.waitGroup.Done()
	var watchResp etcdc.WatchResponse
	watchChan := cs.etcdConn.watcher.Watch(cs.etcdConn.GetCtx(), LeaderKey)
	for {
		select {
		case <-cs.stop:
			return
		case watchResp = <-watchChan:
		}
		if common.IsChanClosed(cs.stop) {
			return
		}
		if watchResp.Canceled {
			watchChan = cs.etcdConn.watcher.Watch(cs.etcdConn.GetCtx(), LeaderKey)
		}
		for _, event := range watchResp.Events {
			if event.Type == mvccpb.DELETE && string(event.Kv.Key) == LeaderKey { // Currently no leader!
				log.WithFields(log.Fields{
					"isCreate": event.IsCreate(), "isModify": event.IsModify(), "version": event.Kv.Version,
				}).Debug("WatchForLeadershipChange detected a deletion event!")
				cs.AttemptToBecomeLeader()
			}
		}
	}
}

func (cs *LeaderService) CancelWatch() {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	for _, waitChan := range cs.unleaderWaitChans {
		close(waitChan)
	}
	for _, waitChan := range cs.leaderWaitChans {
		close(waitChan)
	}
	close(cs.stop)
	cs.waitGroup.Wait()
}

// Maintain the leadership lease; doesn't return
func (cs *LeaderService) MaintainLeaderLease() {
	var waitChan chan bool
	cs.waitGroup.Add(1)
	defer cs.waitGroup.Done()
	for {
		// If we're not a leader, just wait... nothing to be done here
		waitChan = cs.WaitForLeadership()
		select {
		case <-cs.stop:
			return
		case <-waitChan:
			if common.IsChanClosed(cs.stop) {
				return
			}
		}
		waitChan = cs.WaitForNonleadership()

		select {
		case <-cs.stop:
			return
		case <-waitChan:
			if common.IsChanClosed(cs.stop) {
				return
			} else {
				continue
			}
		case <-time.After(3 * time.Second): // to maintain lease
			if common.IsChanClosed(cs.stop) {
				return
			}
			cs.leaderLock.RLock()
			_, err := cs.etcdConn.client.KeepAliveOnce(cs.etcdConn.GetCtx(), cs.leaderLease)
			cs.leaderLock.RUnlock()
			if err == rpctypes.ErrLeaseNotFound {
				log.Info("Lost leadership! Lease expired.")
				// Lost our lease; we are no longer the leader
				cs.AttemptToBecomeLeader()
			} else if err != nil {
				log.WithField("error", err).Error("Error while attempting to renew lease for leader key")
			}
		}
	}
}

func (cs *LeaderService) GetLeadershipChangeRevision() int64 {
	cs.leaderLock.RLock()
	defer cs.leaderLock.RUnlock()
	return cs.leaderChangeRev
}

// return true iff we became the leader which will happen only if there is
// currently no leader
func (cs *LeaderService) AttemptToBecomeLeader() (bool, error) {
	var (
		changeRev int64
		isLeader  bool
	)
	txn := cs.etcdConn.kv.Txn(cs.etcdConn.GetCtx())
	cmp := etcdc.Compare(etcdc.Version(LeaderKey), "=", 0)
	leaseResp, err := cs.etcdConn.client.Grant(cs.etcdConn.GetCtx(), 5)
	if err != nil {
		log.WithField("error", err).Error("Error while attempting to get a lease for a leader key!")
		return false, err
	}
	putKeyOp := etcdc.OpPut(LeaderKey, "", etcdc.WithLease(leaseResp.ID))
	getKeyOp := etcdc.OpGet(LeaderKey)
	txnResp, err := txn.If(cmp).Then(putKeyOp).Else(getKeyOp).Commit()
	if err != nil {
		return false, err
	}
	for _, resp := range txnResp.Responses {
		if r, ok := resp.Response.(*etcdserverpb.ResponseUnion_ResponseRange); ok && !txnResp.Succeeded {
			isLeader = false
			changeRev = r.ResponseRange.Kvs[0].ModRevision
		} else if r, ok := resp.Response.(*etcdserverpb.ResponseUnion_ResponsePut); ok && txnResp.Succeeded {
			isLeader = true
			changeRev = r.ResponsePut.GetHeader().Revision
			cs.leaderLock.Lock()
			cs.leaderLease = leaseResp.ID
			cs.leaderLock.Unlock()

			// Acquire the IP
			err := cs.ipswitcher.AcquireIP()
			if err != nil {
				log.WithField("Error", err).Error("Could not acquire IP!")
			} else {
				log.Info("Successfully got IP!")
			}
		}
	}
	log.WithField("isLeader", isLeader).Info("Attempted to become leader")
	cs.SetLeader(isLeader, changeRev)
	return isLeader, nil
}

func (cs *LeaderService) IsLeader() bool {
	cs.leaderLock.RLock()
	defer cs.leaderLock.RUnlock()
	return cs.isLeader
}

func (cs *LeaderService) SetLeader(isLeader bool, changeRev int64) {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	cs.leaderChangeRev = changeRev
	cs.isLeader = isLeader
	if isLeader {
		for _, c := range cs.leaderWaitChans {
			close(c)
		}
		cs.leaderWaitChans = []chan bool{}
	} else {
		for _, c := range cs.unleaderWaitChans {
			close(c)
		}
		cs.unleaderWaitChans = []chan bool{}
	}
}

// Returns a channel which will be closed when this is leader
func (cs *LeaderService) WaitForLeadership() chan bool {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	// if these are not buffered channels, then sending on the channel
	// can block indefinitely and deadlock -- GTF
	c := make(chan bool, 1)
	if cs.isLeader {
		close(c)
		return c
	} else {
		cs.leaderWaitChans = append(cs.leaderWaitChans, c)
		return c
	}
}

// Returns a channel which will be closed when this is nonleader
func (cs *LeaderService) WaitForNonleadership() chan bool {
	cs.leaderLock.Lock()
	defer cs.leaderLock.Unlock()
	// if these are not buffered channels, then sending on the channel
	// can block indefinitely and deadlock -- GTF
	c := make(chan bool, 1)
	if !cs.isLeader {
		c <- true
		return c
	} else {
		cs.unleaderWaitChans = append(cs.unleaderWaitChans, c)
		return c
	}
}
<|endoftext|>


<commit_before><commit_msg>feat(gcs) : add create thumbnail example<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"dfss/dfssc/sign"
)

func signContract(args []string) {
	filename := args[0]
	fmt.Println("You are going to sign the following contract:")
	contract := getContract(filename)
	if contract == nil {
		os.Exit(1)
	}

	var passphrase string
	_ = readPassword(&passphrase, false)

	// Preparation
	manager, err := sign.NewSignatureManager(fca, fcert, fkey, addrPort, passphrase, localPort, contract)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	err = manager.ConnectToPeers()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(3)
	}

	// Confirmation
	var ready string
	readStringParam("Do you REALLY want to sign "+contract.File.Name+"? Type 'yes' to confirm", "", &ready)
	if ready != "yes" {
		os.Exit(4)
	}

	// Ignition
	fmt.Println("Waiting for other signers to be ready...")
	signatureUUID, err := manager.SendReadySign()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(5)
	}

	// TODO Warning, integration tests are checking Stdout
	fmt.Println("Everybody is ready, starting the signature", signatureUUID)

	// Signature
}
<commit_msg>[c] Add Sign() to execution command<commit_after>package main

import (
	"fmt"
	"os"

	"dfss/dfssc/sign"
)

func signContract(args []string) {
	filename := args[0]
	fmt.Println("You are going to sign the following contract:")
	contract := getContract(filename)
	if contract == nil {
		os.Exit(1)
	}

	var passphrase string
	_ = readPassword(&passphrase, false)

	// Preparation
	manager, err := sign.NewSignatureManager(fca, fcert, fkey, addrPort, passphrase, localPort, contract)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	err = manager.ConnectToPeers()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(3)
	}

	// Confirmation
	var ready string
	readStringParam("Do you REALLY want to sign "+contract.File.Name+"? Type 'yes' to confirm", "", &ready)
	if ready != "yes" {
		os.Exit(4)
	}

	// Ignition
	fmt.Println("Waiting for other signers to be ready...")
	signatureUUID, err := manager.SendReadySign()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(5)
	}

	// TODO Warning, integration tests are checking Stdout
	fmt.Println("Everybody is ready, starting the signature", signatureUUID)

	// Signature
	manager.Sign()

	// Persist evidencies, if any
}
<|endoftext|>


<commit_before>package api

import (
	"log"
	"net/http"
	"os"
	"time"
)

func getBaseF12APIUrl() string {
	baseUrl := os.Getenv("F12_METRICS_API_ADDRESS")
	if baseUrl == "" {
		baseUrl = "http://app.force12.io"
	}

	log.Printf("Sending results to %s", baseUrl)
	return baseUrl
}

var baseF12APIUrl string = getBaseF12APIUrl()
var httpClient *http.Client = &http.Client{
	Timeout: 30000 * time.Millisecond,
}
<commit_msg>Hmmm. Probably need to come back to this timeout.<commit_after>package api

import (
	"log"
	"net/http"
	"os"
	"time"
)

func getBaseF12APIUrl() string {
	baseUrl := os.Getenv("F12_METRICS_API_ADDRESS")
	if baseUrl == "" {
		baseUrl = "http://app.force12.io"
	}

	log.Printf("Sending results to %s", baseUrl)
	return baseUrl
}

var baseF12APIUrl string = getBaseF12APIUrl()
var httpClient *http.Client = &http.Client{
	Timeout: 15000 * time.Millisecond,
}
<|endoftext|>


<commit_before>package dispatcher

import (
	"errors"

	"github.com/abhinavdahiya/go-messenger-bot"
)

type Storage interface {
	StoreState(mbotapi.User, State) error
	FetchState(mbotapi.User) (State, error)
}

type Dispatcher struct {
	States map[string]State
	Storage
	InitState string
}

func NewDispatcher() *Dispatcher {
	return &Dispatcher{
		States: make(map[string]State),
		Storage: &InMemory{
			Store: make(map[mbotapi.User]State),
		},
		InitState: "start",
	}
}

func (d *Dispatcher) AddState(s State) {
	d.States[s.Name()] = s
}

func (d *Dispatcher) LoadState(name string) (State, error) {
	if s, ok := d.States[name]; ok {
		return s, nil
	}
	return nil, errors.New("Unknown State.")
}

func (d *Dispatcher) Process(c mbotapi.Callback, bot *mbotapi.BotAPI) error {
	// fetch the current state of the user
	curr, err := d.FetchState(c.Sender)
	if err != nil {
		// If no state found, initialize user to init state
		tmp := d.States[d.InitState]
		d.StoreState(c.Sender, tmp)
	}
	var cLeave Action
	_, cLeave = curr.Actions()

	// exec Leave action for the state
	if cLeave != nil {
		err := cLeave(c, bot)
		if err != nil {
			return err
		}
	}

	// load next state
	curr.Transitor(c)
	ns := curr.Next()

	var next State
	next, err = d.LoadState(ns)
	var nEnter Action
	nEnter, _ = next.Actions()

	// load the next state
	ctx := Get(&curr)
	Set(&next, ctx)
	if nEnter != nil {
		err = nEnter(c, bot)
		if err != nil {
			return err
		}
	}

	d.StoreState(c.Sender, next)
	return nil
}
<commit_msg>Flush new state<commit_after>package dispatcher

import (
	"errors"

	"github.com/abhinavdahiya/go-messenger-bot"
)

type Storage interface {
	StoreState(mbotapi.User, State) error
	FetchState(mbotapi.User) (State, error)
}

type Dispatcher struct {
	States map[string]State
	Storage
	InitState string
}

func NewDispatcher() *Dispatcher {
	return &Dispatcher{
		States: make(map[string]State),
		Storage: &InMemory{
			Store: make(map[mbotapi.User]State),
		},
		InitState: "start",
	}
}

func (d *Dispatcher) AddState(s State) {
	d.States[s.Name()] = s
}

func (d *Dispatcher) LoadState(name string) (State, error) {
	if s, ok := d.States[name]; ok {
		return s, nil
	}
	return nil, errors.New("Unknown State.")
}

func (d *Dispatcher) Process(c mbotapi.Callback, bot *mbotapi.BotAPI) error {
	// fetch the current state of the user
	curr, err := d.FetchState(c.Sender)
	if err != nil {
		// If no state found, initialize user to init state
		tmp := d.States[d.InitState]
		d.StoreState(c.Sender, tmp)
	}
	var cLeave Action
	_, cLeave = curr.Actions()

	// exec Leave action for the state
	if cLeave != nil {
		err := cLeave(c, bot)
		if err != nil {
			return err
		}
	}

	// load next state
	curr.Transitor(c)
	ns := curr.Next()

	var next State
	next, err = d.LoadState(ns)
	var nEnter Action
	nEnter, _ = next.Actions()

	// load the next state
	ctx := Get(&curr)
	Set(&next, ctx)
	next.Flush()
	if nEnter != nil {
		err = nEnter(c, bot)
		if err != nil {
			return err
		}
	}

	d.StoreState(c.Sender, next)
	return nil
}
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
 * All rights reserved
 *
 * Use of this source code is governed by a 2-clause BSD license
 * that can be found in the LICENSE file.
 */

package msg

type Supervisor struct {
	Verdict    uint16
	RemoteAddr string
	KexId      string
	Data       []byte
	Kex        auth.Kex
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Add BasicAuth fields to msg.Supervisor<commit_after>/*-
 * Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
 * All rights reserved
 *
 * Use of this source code is governed by a 2-clause BSD license
 * that can be found in the LICENSE file.
 */

package msg

type Supervisor struct {
	Verdict        uint16
	RemoteAddr     string
	KexId          string
	Data           []byte
	Kex            auth.Kex
	BasicAuthUser  string
	BasicAuthToken string
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	found, err := nsenterdetect()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cound not detect if nsenter was installed: %s\n", err)
		return 1
	}
	if !found {
		fmt.Fprintf(os.Stderr, "nsenter is not installed\n")
		fmt.Fprintf(os.Stderr, "run boot2docker ssh 'docker run --rm -v /var/lib/boot2docker/:/target bobtfish/nsenter'\n")
		return 1
	}
	/* Woo! We found nsenter, now to move onto more interesting things */
	pid, err, out := dockerpid("juliank_shell")
	if err != nil {
		pid, err, out = dockerstart("juliank_shell", "busybox")
		if err != nil {
			fmt.Fprintf(os.Stderr, "cound not start container: %s: %s\n", err, out)
			return 1
		}
	}
	nsenterexec(pid)
	return 0
}
<commit_msg>Get the username of the current user rather than hard coding<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	found, err := nsenterdetect()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cound not detect if nsenter was installed: %s\n", err)
		return 1
	}
	if !found {
		fmt.Fprintf(os.Stderr, "nsenter is not installed\n")
		fmt.Fprintf(os.Stderr, "run boot2docker ssh 'docker run --rm -v /var/lib/boot2docker/:/target bobtfish/nsenter'\n")
		return 1
	}
	/* Woo! We found nsenter, now to move onto more interesting things */
	u, err2 := user.Current()
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Current: %v", err2)
	}
	if u.HomeDir == "" {
		fmt.Fprintf(os.Stderr, "didn't get a HomeDir")
	}
	if u.Username == "" {
		fmt.Fprintf(os.Stderr, "didn't get a username")
	}

	var container_name = fmt.Sprintf("%s_dockersh", u.Username)

	pid, err, out := dockerpid(container_name)
	if err != nil {
		pid, err, out = dockerstart(container_name, "busybox")
		if err != nil {
			fmt.Fprintf(os.Stderr, "cound not start container: %s: %s\n", err, out)
			return 1
		}
	}
	nsenterexec(pid)
	return 0
}
<|endoftext|>


<commit_before>package corehttp

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	core "github.com/ipfs/go-ipfs/core"
	coreunix "github.com/ipfs/go-ipfs/core/coreunix"
	namesys "github.com/ipfs/go-ipfs/namesys"
	ci "github.com/ipfs/go-ipfs/p2p/crypto"
	path "github.com/ipfs/go-ipfs/path"
	repo "github.com/ipfs/go-ipfs/repo"
	config "github.com/ipfs/go-ipfs/repo/config"
	testutil "github.com/ipfs/go-ipfs/util/testutil"
)

type mockNamesys map[string]path.Path

func (m mockNamesys) Resolve(ctx context.Context, name string) (value path.Path, err error) {
	return m.ResolveN(ctx, name, namesys.DefaultDepthLimit)
}

func (m mockNamesys) ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error) {
	p, ok := m[name]
	if !ok {
		return "", namesys.ErrResolveFailed
	}
	return p, nil
}

func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
	return errors.New("not implemented for mockNamesys")
}

func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, _ time.Time) error {
	return errors.New("not implemented for mockNamesys")
}

func newNodeWithMockNamesys(ns mockNamesys) (*core.IpfsNode, error) {
	c := config.Config{
		Identity: config.Identity{
			PeerID: "Qmfoo", // required by offline node
		},
	}
	r := &repo.Mock{
		C: c,
		D: testutil.ThreadSafeCloserMapDatastore(),
	}
	n, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r})
	if err != nil {
		return nil, err
	}
	n.Namesys = ns
	return n, nil
}

type delegatedHandler struct {
	http.Handler
}

func (dh *delegatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	dh.Handler.ServeHTTP(w, r)
}

func doWithoutRedirect(req *http.Request) (*http.Response, error) {
	tag := "without-redirect"
	c := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return errors.New(tag)
		},
	}
	res, err := c.Do(req)
	if err != nil && !strings.Contains(err.Error(), tag) {
		return nil, err
	}
	return res, nil
}

func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *core.IpfsNode) {
	n, err := newNodeWithMockNamesys(ns)
	if err != nil {
		t.Fatal(err)
	}

	// need this variable here since we need to construct handler with
	// listener, and server with handler. yay cycles.
	dh := &delegatedHandler{}
	ts := httptest.NewServer(dh)

	dh.Handler, err = makeHandler(n,
		ts.Listener,
		IPNSHostnameOption(),
		GatewayOption(false),
	)
	if err != nil {
		t.Fatal(err)
	}

	return ts, n
}

func TestGatewayGet(t *testing.T) {
	ns := mockNamesys{}
	ts, n := newTestServerAndNode(t, ns)
	defer ts.Close()

	k, err := coreunix.Add(n, strings.NewReader("fnord"))
	if err != nil {
		t.Fatal(err)
	}
	ns["/ipns/example.com"] = path.FromString("/ipfs/" + k)

	t.Log(ts.URL)
	for _, test := range []struct {
		host   string
		path   string
		status int
		text   string
	}{
		{"localhost:5001", "/", http.StatusNotFound, "404 page not found\n"},
		{"localhost:5001", "/" + k, http.StatusNotFound, "404 page not found\n"},
		{"localhost:5001", "/ipfs/" + k, http.StatusOK, "fnord"},
		{"localhost:5001", "/ipns/nxdomain.example.com", http.StatusBadRequest, "Path Resolve error: " + namesys.ErrResolveFailed.Error()},
		{"localhost:5001", "/ipns/example.com", http.StatusOK, "fnord"},
		{"example.com", "/", http.StatusOK, "fnord"},
	} {
		var c http.Client
		r, err := http.NewRequest("GET", ts.URL+test.path, nil)
		if err != nil {
			t.Fatal(err)
		}
		r.Host = test.host
		resp, err := c.Do(r)

		urlstr := "http://" + test.host + test.path
		if err != nil {
			t.Errorf("error requesting %s: %s", urlstr, err)
			continue
		}
		defer resp.Body.Close()
		if resp.StatusCode != test.status {
			t.Errorf("got %d, expected %d from %s", resp.StatusCode, test.status, urlstr)
			continue
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("error reading response from %s: %s", urlstr, err)
		}
		if string(body) != test.text {
			t.Errorf("unexpected response body from %s: expected %q; got %q", urlstr, test.text, body)
			continue
		}
	}
}

func TestIPNSHostnameRedirect(t *testing.T) {
	ns := mockNamesys{}
	ts, n := newTestServerAndNode(t, ns)
	t.Logf("test server url: %s", ts.URL)
	defer ts.Close()

	// create /ipns/example.net/foo/index.html
	_, dagn1, err := coreunix.AddWrapped(n, strings.NewReader("_"), "_")
	if err != nil {
		t.Fatal(err)
	}
	_, dagn2, err := coreunix.AddWrapped(n, strings.NewReader("_"), "index.html")
	if err != nil {
		t.Fatal(err)
	}
	dagn1.AddNodeLink("foo", dagn2)
	if err != nil {
		t.Fatal(err)
	}

	err = n.DAG.AddRecursive(dagn1)
	if err != nil {
		t.Fatal(err)
	}

	k, err := dagn1.Key()
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("k: %s\n", k)
	ns["/ipns/example.net"] = path.FromString("/ipfs/" + k.String())

	// make request to directory containing index.html
	req, err := http.NewRequest("GET", ts.URL+"/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err := doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect 302 redirect to same path, but with trailing slash
	if res.StatusCode != 302 {
		t.Errorf("status is %d, expected 302", res.StatusCode)
	}
	hdr := res.Header["Location"]
	if len(hdr) < 1 {
		t.Errorf("location header not present")
	} else if hdr[0] != "/foo/" {
		t.Errorf("location header is %v, expected /foo/", hdr[0])
	}

	// make request with prefix to directory containing index.html
	req, err = http.NewRequest("GET", ts.URL+"/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"
	req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect 302 redirect to same path, but with prefix and trailing slash
	if res.StatusCode != 302 {
		t.Errorf("status is %d, expected 302", res.StatusCode)
	}
	hdr = res.Header["Location"]
	if len(hdr) < 1 {
		t.Errorf("location header not present")
	} else if hdr[0] != "/prefix/foo/" {
		t.Errorf("location header is %v, expected /prefix/foo/", hdr[0])
	}
}

func TestIPNSHostnameBacklinks(t *testing.T) {
	ns := mockNamesys{}
	ts, n := newTestServerAndNode(t, ns)
	t.Logf("test server url: %s", ts.URL)
	defer ts.Close()

	// create /ipns/example.net/foo/
	_, dagn1, err := coreunix.AddWrapped(n, strings.NewReader("1"), "file.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, dagn2, err := coreunix.AddWrapped(n, strings.NewReader("2"), "file.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, dagn3, err := coreunix.AddWrapped(n, strings.NewReader("3"), "file.txt")
	if err != nil {
		t.Fatal(err)
	}
	dagn2.AddNodeLink("bar", dagn3)
	dagn1.AddNodeLink("foo", dagn2)
	if err != nil {
		t.Fatal(err)
	}

	err = n.DAG.AddRecursive(dagn1)
	if err != nil {
		t.Fatal(err)
	}

	k, err := dagn1.Key()
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("k: %s\n", k)
	ns["/ipns/example.net"] = path.FromString("/ipfs/" + k.String())

	// make request to directory listing
	req, err := http.NewRequest("GET", ts.URL+"/foo/", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err := doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s := string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /foo/") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}

	// make request to directory listing at root
	req, err = http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks at root
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s = string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}

	// make request to directory listing
	req, err = http.NewRequest("GET", ts.URL+"/foo/bar/", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s = string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /foo/bar/") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}

	// make request to directory listing with prefix
	req, err = http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"
	req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks with prefix
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s = string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /prefix") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}
}
<commit_msg>gateway: add tests for /version<commit_after>package corehttp

import (
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	core "github.com/ipfs/go-ipfs/core"
	coreunix "github.com/ipfs/go-ipfs/core/coreunix"
	namesys "github.com/ipfs/go-ipfs/namesys"
	ci "github.com/ipfs/go-ipfs/p2p/crypto"
	id "github.com/ipfs/go-ipfs/p2p/protocol/identify"
	path "github.com/ipfs/go-ipfs/path"
	repo "github.com/ipfs/go-ipfs/repo"
	config "github.com/ipfs/go-ipfs/repo/config"
	testutil "github.com/ipfs/go-ipfs/util/testutil"
)

type mockNamesys map[string]path.Path

func (m mockNamesys) Resolve(ctx context.Context, name string) (value path.Path, err error) {
	return m.ResolveN(ctx, name, namesys.DefaultDepthLimit)
}

func (m mockNamesys) ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error) {
	p, ok := m[name]
	if !ok {
		return "", namesys.ErrResolveFailed
	}
	return p, nil
}

func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
	return errors.New("not implemented for mockNamesys")
}

func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, _ time.Time) error {
	return errors.New("not implemented for mockNamesys")
}

func newNodeWithMockNamesys(ns mockNamesys) (*core.IpfsNode, error) {
	c := config.Config{
		Identity: config.Identity{
			PeerID: "Qmfoo", // required by offline node
		},
	}
	r := &repo.Mock{
		C: c,
		D: testutil.ThreadSafeCloserMapDatastore(),
	}
	n, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r})
	if err != nil {
		return nil, err
	}
	n.Namesys = ns
	return n, nil
}

type delegatedHandler struct {
	http.Handler
}

func (dh *delegatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	dh.Handler.ServeHTTP(w, r)
}

func doWithoutRedirect(req *http.Request) (*http.Response, error) {
	tag := "without-redirect"
	c := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return errors.New(tag)
		},
	}
	res, err := c.Do(req)
	if err != nil && !strings.Contains(err.Error(), tag) {
		return nil, err
	}
	return res, nil
}

func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, *core.IpfsNode) {
	n, err := newNodeWithMockNamesys(ns)
	if err != nil {
		t.Fatal(err)
	}

	// need this variable here since we need to construct handler with
	// listener, and server with handler. yay cycles.
	dh := &delegatedHandler{}
	ts := httptest.NewServer(dh)

	dh.Handler, err = makeHandler(n,
		ts.Listener,
		VersionOption(),
		IPNSHostnameOption(),
		GatewayOption(false),
	)
	if err != nil {
		t.Fatal(err)
	}

	return ts, n
}

func TestGatewayGet(t *testing.T) {
	ns := mockNamesys{}
	ts, n := newTestServerAndNode(t, ns)
	defer ts.Close()

	k, err := coreunix.Add(n, strings.NewReader("fnord"))
	if err != nil {
		t.Fatal(err)
	}
	ns["/ipns/example.com"] = path.FromString("/ipfs/" + k)

	t.Log(ts.URL)
	for _, test := range []struct {
		host   string
		path   string
		status int
		text   string
	}{
		{"localhost:5001", "/", http.StatusNotFound, "404 page not found\n"},
		{"localhost:5001", "/" + k, http.StatusNotFound, "404 page not found\n"},
		{"localhost:5001", "/ipfs/" + k, http.StatusOK, "fnord"},
		{"localhost:5001", "/ipns/nxdomain.example.com", http.StatusBadRequest, "Path Resolve error: " + namesys.ErrResolveFailed.Error()},
		{"localhost:5001", "/ipns/example.com", http.StatusOK, "fnord"},
		{"example.com", "/", http.StatusOK, "fnord"},
	} {
		var c http.Client
		r, err := http.NewRequest("GET", ts.URL+test.path, nil)
		if err != nil {
			t.Fatal(err)
		}
		r.Host = test.host
		resp, err := c.Do(r)

		urlstr := "http://" + test.host + test.path
		if err != nil {
			t.Errorf("error requesting %s: %s", urlstr, err)
			continue
		}
		defer resp.Body.Close()
		if resp.StatusCode != test.status {
			t.Errorf("got %d, expected %d from %s", resp.StatusCode, test.status, urlstr)
			continue
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("error reading response from %s: %s", urlstr, err)
		}
		if string(body) != test.text {
			t.Errorf("unexpected response body from %s: expected %q; got %q", urlstr, test.text, body)
			continue
		}
	}
}

func TestIPNSHostnameRedirect(t *testing.T) {
	ns := mockNamesys{}
	ts, n := newTestServerAndNode(t, ns)
	t.Logf("test server url: %s", ts.URL)
	defer ts.Close()

	// create /ipns/example.net/foo/index.html
	_, dagn1, err := coreunix.AddWrapped(n, strings.NewReader("_"), "_")
	if err != nil {
		t.Fatal(err)
	}
	_, dagn2, err := coreunix.AddWrapped(n, strings.NewReader("_"), "index.html")
	if err != nil {
		t.Fatal(err)
	}
	dagn1.AddNodeLink("foo", dagn2)
	if err != nil {
		t.Fatal(err)
	}

	err = n.DAG.AddRecursive(dagn1)
	if err != nil {
		t.Fatal(err)
	}

	k, err := dagn1.Key()
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("k: %s\n", k)
	ns["/ipns/example.net"] = path.FromString("/ipfs/" + k.String())

	// make request to directory containing index.html
	req, err := http.NewRequest("GET", ts.URL+"/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err := doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect 302 redirect to same path, but with trailing slash
	if res.StatusCode != 302 {
		t.Errorf("status is %d, expected 302", res.StatusCode)
	}
	hdr := res.Header["Location"]
	if len(hdr) < 1 {
		t.Errorf("location header not present")
	} else if hdr[0] != "/foo/" {
		t.Errorf("location header is %v, expected /foo/", hdr[0])
	}

	// make request with prefix to directory containing index.html
	req, err = http.NewRequest("GET", ts.URL+"/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"
	req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect 302 redirect to same path, but with prefix and trailing slash
	if res.StatusCode != 302 {
		t.Errorf("status is %d, expected 302", res.StatusCode)
	}
	hdr = res.Header["Location"]
	if len(hdr) < 1 {
		t.Errorf("location header not present")
	} else if hdr[0] != "/prefix/foo/" {
		t.Errorf("location header is %v, expected /prefix/foo/", hdr[0])
	}
}

func TestIPNSHostnameBacklinks(t *testing.T) {
	ns := mockNamesys{}
	ts, n := newTestServerAndNode(t, ns)
	t.Logf("test server url: %s", ts.URL)
	defer ts.Close()

	// create /ipns/example.net/foo/
	_, dagn1, err := coreunix.AddWrapped(n, strings.NewReader("1"), "file.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, dagn2, err := coreunix.AddWrapped(n, strings.NewReader("2"), "file.txt")
	if err != nil {
		t.Fatal(err)
	}
	_, dagn3, err := coreunix.AddWrapped(n, strings.NewReader("3"), "file.txt")
	if err != nil {
		t.Fatal(err)
	}
	dagn2.AddNodeLink("bar", dagn3)
	dagn1.AddNodeLink("foo", dagn2)
	if err != nil {
		t.Fatal(err)
	}

	err = n.DAG.AddRecursive(dagn1)
	if err != nil {
		t.Fatal(err)
	}

	k, err := dagn1.Key()
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("k: %s\n", k)
	ns["/ipns/example.net"] = path.FromString("/ipfs/" + k.String())

	// make request to directory listing
	req, err := http.NewRequest("GET", ts.URL+"/foo/", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err := doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s := string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /foo/") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}

	// make request to directory listing at root
	req, err = http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks at root
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s = string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}

	// make request to directory listing
	req, err = http.NewRequest("GET", ts.URL+"/foo/bar/", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s = string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /foo/bar/") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}

	// make request to directory listing with prefix
	req, err = http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Host = "example.net"
	req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")

	res, err = doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}

	// expect correct backlinks with prefix
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s = string(body)
	t.Logf("body: %s\n", string(body))

	if !strings.Contains(s, "Index of /prefix") {
		t.Fatalf("expected a path in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected backlink in directory listing")
	}
	if !strings.Contains(s, "") {
		t.Fatalf("expected file in directory listing")
	}
}

func TestVersion(t *testing.T) {
	ns := mockNamesys{}
	ts, _ := newTestServerAndNode(t, ns)
	t.Logf("test server url: %s", ts.URL)
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL+"/version", nil)
	if err != nil {
		t.Fatal(err)
	}

	res, err := doWithoutRedirect(req)
	if err != nil {
		t.Fatal(err)
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("error reading response: %s", err)
	}
	s := string(body)

	if !strings.Contains(s, "Client Version: "+id.ClientVersion) {
		t.Fatalf("response doesn't contain client version:\n%s", s)
	}

	if !strings.Contains(s, "Protocol Version: "+id.IpfsVersion) {
		t.Fatalf("response doesn't contain protocol version:\n%s", s)
	}
}
<|endoftext|>


<commit_before>package x86_16

import (
	"fmt"
	"strings"
	"syscall"

	"github.com/lunixbochs/argjoy"
	"github.com/pkg/errors"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/models"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
)

const (
	STACK_BASE = 0x8000
	STACK_SIZE = 0x1000
	NUM_FDS    = 256

	// Registers
	AH    = uc.X86_REG_AH
	AL    = uc.X86_REG_AL
	AX    = uc.X86_REG_AX
	BH    = uc.X86_REG_BH
	BL    = uc.X86_REG_BL
	BP    = uc.X86_REG_BP
	BX    = uc.X86_REG_BX
	CH    = uc.X86_REG_CH
	CL    = uc.X86_REG_CL
	CS    = uc.X86_REG_CS
	CX    = uc.X86_REG_CX
	DH    = uc.X86_REG_DH
	DI    = uc.X86_REG_DI
	DL    = uc.X86_REG_DL
	DS    = uc.X86_REG_DS
	DX    = uc.X86_REG_DX
	ES    = uc.X86_REG_ES
	FS    = uc.X86_REG_FS
	GS    = uc.X86_REG_GS
	IP    = uc.X86_REG_IP
	SI    = uc.X86_REG_SI
	SP    = uc.X86_REG_SP
	SS    = uc.X86_REG_SS
	FLAGS = uc.X86_REG_EFLAGS
)

func (k *DosKernel) reg16(enum int) uint16 {
	r, _ := k.U.RegRead(enum)
	return uint16(r)
}
func (k *DosKernel) reg8(enum int) uint8 {
	r, _ := k.U.RegRead(enum)
	return uint8(r)
}
func (k *DosKernel) wreg16(enum int, val uint16) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) wreg8(enum int, val uint8) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) setFlagC(set bool) {
	// TODO: Write setFlagX with enum for each flag
	// Unicorn doesn't have the non-extended FLAGS register, so we're
	// dealing with 32 bits here
	flags, _ := k.U.RegRead(FLAGS)
	if set {
		flags |= 1 // CF = 1
	} else {
		flags &= 0xfffffffe // CF = 0
	}
	k.U.RegWrite(FLAGS, flags)
}

var dosSysNum = map[int]string{
	0x00: "terminate",
	0x01: "char_in",
	0x02: "char_out",
	0x09: "display",
	0x30: "get_dos_version",
	0x3C: "create_or_truncate",
	0x3D: "open",
	0x3E: "close",
	0x3F: "read",
	0x40: "write",
	0x41: "unlink",
	0x4C: "terminate_with_code",
}

// TODO: Create a reverse map of this for conciseness
var abiMap = map[int][]int{
	0x00: {},
	0x01: {DX},
	0x02: {DX},
	0x09: {DX},
	0x30: {},
	0x3C: {DX, CX},
	0x3D: {DX, AL},
	0x3E: {BX},
	0x3F: {BX, DX, CX},
	0x40: {BX, DX, CX},
	0x41: {DX, CX},
	0x4C: {AL},
}

type PSP struct {
	CPMExit                     [2]byte
	FirstFreeSegment            uint16
	Reserved1                   uint8
	CPMCall5Compat              [5]byte
	OldTSRAddress               uint32
	OldBreakAddress             uint32
	CriticalErrorHandlerAddress uint32
	CallerPSPSegment            uint16
	JobFileTable                [20]byte
	EnvironmentSegment          uint16
	INT21SSSP                   uint32
	JobFileTableSize            uint16
	JobFileTablePointer         uint32
	PreviousPSP                 uint32
	Reserved2                   uint32
	DOSVersion                  uint16
	Reserved3                   [14]byte
	DOSFarCall                  [3]byte
	Reserved4                   uint16
	ExtendedFCB1                [7]byte
	FCB1                        [16]byte
	FCB2                        [20]byte
	CommandLineLength           uint8
	CommandLine                 [127]byte
}

type DosKernel struct {
	*co.KernelBase
	fds [NUM_FDS]int
}

func initPsp(argc int, argv []string) *PSP {
	psp := &PSP{
		CPMExit:    [2]byte{0xcd, 0x20},       // int 0x20
		DOSFarCall: [3]byte{0xcd, 0x21, 0xcd}, // int 0x21 + retf
	}

	psp.FCB1[0] = 0x01
	psp.FCB1[1] = 0x20

	// Combine all args into one string
	commandline := strings.Join(argv, " ")
	copy(psp.CommandLine[:126], commandline)
	if len(commandline) > 126 {
		psp.CommandLineLength = 126
	} else {
		psp.CommandLineLength = uint8(len(commandline))
	}

	return psp
}

func (k *DosKernel) readUntilChar(addr uint64, c byte) []byte {
	var mem []byte
	var i uint64
	var char byte = 0

	// TODO: Read ahead? This'll be slow
	for i = 1; char != c || i == 1; i++ {
		mem, _ = k.U.MemRead(addr, i)
		char = mem[i-1]
	}
	return mem[:i-2]
}

func (k *DosKernel) getFd(fd int) (uint16, error) {
	for i := uint16(0); i < NUM_FDS; i++ {
		if k.fds[i] == -1 {
			k.fds[i] = fd
			return i, nil
		}
	}
	return 0xFFFF, errors.New("DOS FD table exhausted")
}

func (k *DosKernel) freeFd(fd int) (int, error) {
	realfd := k.fds[fd]
	if realfd == -1 {
		return 0xFFFF, errors.New("FD not found in FD table")
	}
	k.fds[fd] = -1
	return realfd, nil
}

func (k *DosKernel) Terminate() {
	k.U.Exit(models.ExitStatus(0))
}

func (k *DosKernel) CharIn(buf co.Buf) byte {
	var char byte
	fmt.Scanf("%c", &char)
	k.U.MemWrite(buf.Addr, []byte{char})
	return char
}

func (k *DosKernel) CharOut(char uint16) byte {
	fmt.Printf("%c", byte(char&0xFF))
	return byte(char & 0xFF)
}

func (k *DosKernel) Display(buf co.Buf) int {
	mem := k.readUntilChar(buf.Addr, '$')

	syscall.Write(1, mem)
	k.wreg8(AL, 0x24)
	return 0x24
}

func (k *DosKernel) GetDosVersion() int {
	k.wreg16(AX, 0x7)
	return 0x7
}

func (k *DosKernel) openFile(filename string, mode int) co.Fd {
	realfd, err := syscall.Open(filename, mode, 0666)
	if err != nil {
		k.wreg16(AX, 0xFFFF)
		k.setFlagC(true)
		return 0xFFFF
	}

	// Find an internal fd number
	dosfd, err := k.getFd(realfd)
	if err != nil {
		k.wreg16(AX, dosfd)
		k.setFlagC(true)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, dosfd)
	return co.Fd(dosfd)
}

func (k *DosKernel) CreateOrTruncate(buf co.Buf, attr int) co.Fd {
	filename := string(k.readUntilChar(buf.Addr, '$'))
	return k.openFile(filename, syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR)
}

func (k *DosKernel) Open(filename string, mode int) co.Fd {
	return k.openFile(filename, mode)
}

func (k *DosKernel) Close(fd co.Fd) {
	// Find and free the internal fd
	realfd, _ := k.freeFd(int(fd))
	err := syscall.Close(realfd)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
}

func (k *DosKernel) Read(fd co.Fd, buf co.Obuf, len co.Len) int {
	mem := make([]byte, len)
	n, err := syscall.Read(int(fd), mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.U.MemWrite(buf.Addr, mem)
	k.setFlagC(false)
	k.wreg16(AX, uint16(n))
	return n
}

func (k *DosKernel) Write(fd co.Fd, buf co.Buf, n co.Len) int {
	mem, _ := k.U.MemRead(buf.Addr, uint64(n))
	written, err := syscall.Write(k.fds[fd], mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, uint16(written))
	return written
}

func (k *DosKernel) Unlink(filename string, attr int) int {
	err := syscall.Unlink(filename)
	if err != nil {
		k.setFlagC(true)
		k.wreg16(AX, 0xFFFF)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
	return 0
}

func (k *DosKernel) TerminateWithCode(code int) {
	k.U.Exit(models.ExitStatus(code))
}

func NewKernel() *DosKernel {
	k := &DosKernel{
		KernelBase: &co.KernelBase{},
	}

	// Init FDs
	for i := 0; i < NUM_FDS; i++ {
		k.fds[i] = -1
	}
	k.fds[0] = 0
	k.fds[1] = 1
	k.fds[2] = 2

	k.Argjoy.Register(k.getDosArgCodec())
	return k
}

func DosInit(u models.Usercorn, args, env []string) error {
	// Setup PSP
	// TODO: Setup args
	psp := initPsp(0, nil)
	u.StrucAt(0).Pack(psp)

	u.SetEntry(0x100)
	return u.MapStack(STACK_BASE, STACK_SIZE, false)
}

func DosSyscall(u models.Usercorn) {
	num, _ := u.RegRead(AH)
	name, _ := dosSysNum[int(num)]
	// TODO: How are registers numbered from here?
	u.Syscall(int(num), name, dosArgs(u, int(num)))
	// TODO: Set error
}

func (k *DosKernel) getDosArgCodec() func(interface{}, []interface{}) error {
	return func(arg interface{}, vals []interface{}) error {
		// DOS takes address as DS+DX
		if reg, ok := vals[0].(uint64); ok && len(vals) > 1 {
			ds, _ := k.U.RegRead(DS)
			reg += ds
			switch v := arg.(type) {
			case *co.Buf:
				*v = co.NewBuf(k, reg)
			case *co.Obuf:
				*v = co.Obuf{co.NewBuf(k, reg)}
			case *co.Ptr:
				*v = co.Ptr(reg)
			case *string:
				s, err := k.U.Mem().ReadStrAt(reg)
				if err != nil {
					return errors.Wrapf(err, "ReadStrAt(%#x) failed", reg)
				}
				*v = s
			default:
				return argjoy.NoMatch
			}
			return nil
		}
		return argjoy.NoMatch
	}
}

func dosArgs(u models.Usercorn, num int) func(int) ([]uint64, error) {
	return co.RegArgs(u, abiMap[num])
}

func DosInterrupt(u models.Usercorn, cause uint32) {
	intno := cause & 0xFF
	if intno == 0x21 {
		DosSyscall(u)
	} else if intno == 0x20 {
		u.Syscall(0, "terminate", func(int) ([]uint64, error) { return []uint64{}, nil })
	} else {
		panic(fmt.Sprintf("unhandled X86 interrupt %#X", intno))
	}
}
func DosKernels(u models.Usercorn) []interface{} {
	return []interface{}{NewKernel()}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "DOS",
		Init:      DosInit,
		Interrupt: DosInterrupt,
		Kernels:   DosKernels,
	})
}
<commit_msg>DOS: Invert ABI map for conciseness<commit_after>package x86_16

import (
	"fmt"
	"strings"
	"syscall"

	"github.com/lunixbochs/argjoy"
	"github.com/pkg/errors"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/models"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
)

const (
	STACK_BASE = 0x8000
	STACK_SIZE = 0x1000
	NUM_FDS    = 256

	// Registers
	AH    = uc.X86_REG_AH
	AL    = uc.X86_REG_AL
	AX    = uc.X86_REG_AX
	BH    = uc.X86_REG_BH
	BL    = uc.X86_REG_BL
	BP    = uc.X86_REG_BP
	BX    = uc.X86_REG_BX
	CH    = uc.X86_REG_CH
	CL    = uc.X86_REG_CL
	CS    = uc.X86_REG_CS
	CX    = uc.X86_REG_CX
	DH    = uc.X86_REG_DH
	DI    = uc.X86_REG_DI
	DL    = uc.X86_REG_DL
	DS    = uc.X86_REG_DS
	DX    = uc.X86_REG_DX
	ES    = uc.X86_REG_ES
	FS    = uc.X86_REG_FS
	GS    = uc.X86_REG_GS
	IP    = uc.X86_REG_IP
	SI    = uc.X86_REG_SI
	SP    = uc.X86_REG_SP
	SS    = uc.X86_REG_SS
	FLAGS = uc.X86_REG_EFLAGS
)

func (k *DosKernel) reg16(enum int) uint16 {
	r, _ := k.U.RegRead(enum)
	return uint16(r)
}
func (k *DosKernel) reg8(enum int) uint8 {
	r, _ := k.U.RegRead(enum)
	return uint8(r)
}
func (k *DosKernel) wreg16(enum int, val uint16) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) wreg8(enum int, val uint8) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) setFlagC(set bool) {
	// TODO: Write setFlagX with enum for each flag
	// Unicorn doesn't have the non-extended FLAGS register, so we're
	// dealing with 32 bits here
	flags, _ := k.U.RegRead(FLAGS)
	if set {
		flags |= 1 // CF = 1
	} else {
		flags &= 0xfffffffe // CF = 0
	}
	k.U.RegWrite(FLAGS, flags)
}

var dosSysNum = map[int]string{
	0x00: "terminate",
	0x01: "char_in",
	0x02: "char_out",
	0x09: "display",
	0x30: "get_dos_version",
	0x3C: "create_or_truncate",
	0x3D: "open",
	0x3E: "close",
	0x3F: "read",
	0x40: "write",
	0x41: "unlink",
	0x4C: "terminate_with_code",
}

type abi []int

// ABI to syscall number mapping
var abiMap = map[*abi][]int{
	&abi{BX, DX, CX}: {0x00, 0x30, 0x3E, 0x3F, 0x40},
	&abi{DX, CX}:     {0x01, 0x02, 0x09, 0x3C, 0x41},
	&abi{DX, AL}:     {0x3D},
	&abi{AL}:         {0x4C},
}

var syscallAbis = map[int][]int{}

type PSP struct {
	CPMExit                     [2]byte
	FirstFreeSegment            uint16
	Reserved1                   uint8
	CPMCall5Compat              [5]byte
	OldTSRAddress               uint32
	OldBreakAddress             uint32
	CriticalErrorHandlerAddress uint32
	CallerPSPSegment            uint16
	JobFileTable                [20]byte
	EnvironmentSegment          uint16
	INT21SSSP                   uint32
	JobFileTableSize            uint16
	JobFileTablePointer         uint32
	PreviousPSP                 uint32
	Reserved2                   uint32
	DOSVersion                  uint16
	Reserved3                   [14]byte
	DOSFarCall                  [3]byte
	Reserved4                   uint16
	ExtendedFCB1                [7]byte
	FCB1                        [16]byte
	FCB2                        [20]byte
	CommandLineLength           uint8
	CommandLine                 [127]byte
}

type DosKernel struct {
	*co.KernelBase
	fds [NUM_FDS]int
}

func initPsp(argc int, argv []string) *PSP {
	psp := &PSP{
		CPMExit:    [2]byte{0xcd, 0x20},       // int 0x20
		DOSFarCall: [3]byte{0xcd, 0x21, 0xcd}, // int 0x21 + retf
	}

	psp.FCB1[0] = 0x01
	psp.FCB1[1] = 0x20

	// Combine all args into one string
	commandline := strings.Join(argv, " ")
	copy(psp.CommandLine[:126], commandline)
	if len(commandline) > 126 {
		psp.CommandLineLength = 126
	} else {
		psp.CommandLineLength = uint8(len(commandline))
	}

	return psp
}

func (k *DosKernel) readUntilChar(addr uint64, c byte) []byte {
	var mem []byte
	var i uint64
	var char byte = 0

	// TODO: Read ahead? This'll be slow
	for i = 1; char != c || i == 1; i++ {
		mem, _ = k.U.MemRead(addr, i)
		char = mem[i-1]
	}
	return mem[:i-2]
}

func (k *DosKernel) getFd(fd int) (uint16, error) {
	for i := uint16(0); i < NUM_FDS; i++ {
		if k.fds[i] == -1 {
			k.fds[i] = fd
			return i, nil
		}
	}
	return 0xFFFF, errors.New("DOS FD table exhausted")
}

func (k *DosKernel) freeFd(fd int) (int, error) {
	realfd := k.fds[fd]
	if realfd == -1 {
		return 0xFFFF, errors.New("FD not found in FD table")
	}
	k.fds[fd] = -1
	return realfd, nil
}

func (k *DosKernel) Terminate() {
	k.U.Exit(models.ExitStatus(0))
}

func (k *DosKernel) CharIn(buf co.Buf) byte {
	var char byte
	fmt.Scanf("%c", &char)
	k.U.MemWrite(buf.Addr, []byte{char})
	return char
}

func (k *DosKernel) CharOut(char uint16) byte {
	fmt.Printf("%c", byte(char&0xFF))
	return byte(char & 0xFF)
}

func (k *DosKernel) Display(buf co.Buf) int {
	mem := k.readUntilChar(buf.Addr, '$')

	syscall.Write(1, mem)
	k.wreg8(AL, 0x24)
	return 0x24
}

func (k *DosKernel) GetDosVersion() int {
	k.wreg16(AX, 0x7)
	return 0x7
}

func (k *DosKernel) openFile(filename string, mode int) co.Fd {
	realfd, err := syscall.Open(filename, mode, 0666)
	if err != nil {
		k.wreg16(AX, 0xFFFF)
		k.setFlagC(true)
		return 0xFFFF
	}

	// Find an internal fd number
	dosfd, err := k.getFd(realfd)
	if err != nil {
		k.wreg16(AX, dosfd)
		k.setFlagC(true)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, dosfd)
	return co.Fd(dosfd)
}

func (k *DosKernel) CreateOrTruncate(buf co.Buf, attr int) co.Fd {
	filename := string(k.readUntilChar(buf.Addr, '$'))
	return k.openFile(filename, syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR)
}

func (k *DosKernel) Open(filename string, mode int) co.Fd {
	return k.openFile(filename, mode)
}

func (k *DosKernel) Close(fd co.Fd) {
	// Find and free the internal fd
	realfd, _ := k.freeFd(int(fd))
	err := syscall.Close(realfd)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
}

func (k *DosKernel) Read(fd co.Fd, buf co.Obuf, len co.Len) int {
	mem := make([]byte, len)
	n, err := syscall.Read(int(fd), mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.U.MemWrite(buf.Addr, mem)
	k.setFlagC(false)
	k.wreg16(AX, uint16(n))
	return n
}

func (k *DosKernel) Write(fd co.Fd, buf co.Buf, n co.Len) int {
	mem, _ := k.U.MemRead(buf.Addr, uint64(n))
	written, err := syscall.Write(k.fds[fd], mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, uint16(written))
	return written
}

func (k *DosKernel) Unlink(filename string, attr int) int {
	err := syscall.Unlink(filename)
	if err != nil {
		k.setFlagC(true)
		k.wreg16(AX, 0xFFFF)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
	return 0
}

func (k *DosKernel) TerminateWithCode(code int) {
	k.U.Exit(models.ExitStatus(code))
}

func NewKernel() *DosKernel {
	k := &DosKernel{
		KernelBase: &co.KernelBase{},
	}

	// Init FDs
	for i := 0; i < NUM_FDS; i++ {
		k.fds[i] = -1
	}
	k.fds[0] = 0
	k.fds[1] = 1
	k.fds[2] = 2

	// Invert the syscall map
	for abi, syscalls := range abiMap {
		for _, syscall := range syscalls {
			syscallAbis[syscall] = *abi
		}
	}

	k.Argjoy.Register(k.getDosArgCodec())
	return k
}

func DosInit(u models.Usercorn, args, env []string) error {
	// Setup PSP
	// TODO: Setup args
	psp := initPsp(0, nil)
	u.StrucAt(0).Pack(psp)

	u.SetEntry(0x100)
	return u.MapStack(STACK_BASE, STACK_SIZE, false)
}

func DosSyscall(u models.Usercorn) {
	num, _ := u.RegRead(AH)
	name, _ := dosSysNum[int(num)]
	// TODO: How are registers numbered from here?
	u.Syscall(int(num), name, dosArgs(u, int(num)))
	// TODO: Set error
}

func (k *DosKernel) getDosArgCodec() func(interface{}, []interface{}) error {
	return func(arg interface{}, vals []interface{}) error {
		// DOS takes address as DS+DX
		if reg, ok := vals[0].(uint64); ok && len(vals) > 1 {
			ds, _ := k.U.RegRead(DS)
			reg += ds
			switch v := arg.(type) {
			case *co.Buf:
				*v = co.NewBuf(k, reg)
			case *co.Obuf:
				*v = co.Obuf{co.NewBuf(k, reg)}
			case *co.Ptr:
				*v = co.Ptr(reg)
			case *string:
				s, err := k.U.Mem().ReadStrAt(reg)
				if err != nil {
					return errors.Wrapf(err, "ReadStrAt(%#x) failed", reg)
				}
				*v = s
			default:
				return argjoy.NoMatch
			}
			return nil
		}
		return argjoy.NoMatch
	}
}

func dosArgs(u models.Usercorn, num int) func(int) ([]uint64, error) {
	// Return a closure over the correct arglist based on the syscall number
	return co.RegArgs(u, syscallAbis[num])
}

func DosInterrupt(u models.Usercorn, cause uint32) {
	intno := cause & 0xFF
	if intno == 0x21 {
		DosSyscall(u)
	} else if intno == 0x20 {
		u.Syscall(0, "terminate", func(int) ([]uint64, error) { return []uint64{}, nil })
	} else {
		panic(fmt.Sprintf("unhandled X86 interrupt %#X", intno))
	}
}
func DosKernels(u models.Usercorn) []interface{} {
	return []interface{}{NewKernel()}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "DOS",
		Init:      DosInit,
		Interrupt: DosInterrupt,
		Kernels:   DosKernels,
	})
}
<|endoftext|>