<commit_before>package main

import (
	"fmt"
	//"log"
	//"math/rand"
	//"os"
	//"strconv"
	"sort"
	"time"

	"github.com/icza/gowut/gwu"
	"github.com/udhos/jazigo/dev"
)

func newAccPanel(user string) gwu.Panel {
	ap := gwu.NewHorizontalPanel()
	ap.Style().AddClass("account_panel")

	ap.Add(gwu.NewLabel(fmt.Sprintf("%s %s", appName, appVersion)))

	if user == "" {
		// guest user

		// create login button
		b := gwu.NewButton("Login")
		b.AddEHandlerFunc(func(e gwu.Event) {
			e.ReloadWin("login")
		}, gwu.ETypeClick)
		ap.Add(b)

	} else {

		// logged user
		l := gwu.NewLabel(fmt.Sprintf("username=[%s]", user))
		ap.Add(l)
	}
	return ap
}

func accountPanelUpdate(jaz *app, user string) {

	if jaz.winHome != nil {
		if jaz.apHome != nil {
			home := jaz.winHome.ById(jaz.apHome.Id())
			jaz.winHome.Remove(home)
		}
		jaz.apHome = newAccPanel(user)
		if !jaz.winHome.Insert(jaz.apHome, 0) {
			jaz.logf("home win insert accPanel failed")
		}
	}

	if jaz.winAdmin != nil {
		if jaz.apAdmin != nil {
			admin := jaz.winAdmin.ById(jaz.apAdmin.Id())
			jaz.winAdmin.Remove(admin)
		}
		jaz.apAdmin = newAccPanel(user)
		if !jaz.winAdmin.Insert(jaz.apAdmin, 0) {
			jaz.logf("admin win insert accPanel failed")
		}
	}

	if jaz.winLogout != nil {
		if jaz.apLogout != nil {
			logout := jaz.winLogout.ById(jaz.apLogout.Id())
			jaz.winLogout.Remove(logout)
		}
		jaz.apLogout = newAccPanel(user)
		if !jaz.winLogout.Insert(jaz.apLogout, 0) {
			jaz.logf("logout win insert accPanel failed")
		}
	}

}

func accountPanelUpdateEvent(jaz *app, user string, e gwu.Event) {
	accountPanelUpdate(jaz, user)

	if jaz.winHome != nil {
		e.MarkDirty(jaz.winHome)
	}

	if jaz.winAdmin != nil {
		e.MarkDirty(jaz.winAdmin)
	}

	if jaz.winLogout != nil {
		e.MarkDirty(jaz.winLogout)
	}
}

func newWin(jaz *app, path, name string) gwu.Window {
	win := gwu.NewWindow(path, name)
	cssLink := fmt.Sprintf(`<link rel="stylesheet" type="text/css" href="%s">`, jaz.cssPath)
	win.AddHeadHtml(cssLink)
	jaz.logf("window=[%s] attached CSS=[%s]", path, cssLink)
	return win
}

type sortById struct {
	data []*dev.Device
}

func (s sortById) Len() int {
	return len(s.data)
}
func (s sortById) Swap(i, j int) {
	s.data[i], s.data[j] = s.data[j], s.data[i]
}
func (s sortById) Less(i, j int) bool {
	return s.data[i].Id < s.data[j].Id
}

func buildDeviceTable(jaz *app, t gwu.Table) {
	const COLS = 9

	t.Add(gwu.NewLabel("Model"), 0, 0)
	t.Add(gwu.NewLabel("Device"), 0, 1)
	t.Add(gwu.NewLabel("Host"), 0, 2)
	t.Add(gwu.NewLabel("Transport"), 0, 3)
	t.Add(gwu.NewLabel("Last Status"), 0, 4)
	t.Add(gwu.NewLabel("Last Try"), 0, 5)
	t.Add(gwu.NewLabel("Last Success"), 0, 6)
	t.Add(gwu.NewLabel("Holdtime"), 0, 7)
	t.Add(gwu.NewLabel("Run Now"), 0, 8)

	for j := 0; j < COLS; j++ {
		t.CellFmt(0, j).Style().AddClass("device_table_cell")
	}

	devList := jaz.table.ListDevices()
	sort.Sort(sortById{data: devList})

	now := time.Now()

	i := 1
	for _, d := range devList {
		labMod := gwu.NewLabel(d.Model())
		labId := gwu.NewLabel(d.Id)
		labHost := gwu.NewLabel(d.HostPort)
		labTransport := gwu.NewLabel(d.Transports)
		labLastStatus := gwu.NewLabel(fmt.Sprintf("%v", d.LastStatus()))
		labLastTry := gwu.NewLabel(timestampString(d.LastTry()))
		labLastSuccess := gwu.NewLabel(timestampString(d.LastSuccess()))
		h := d.Holdtime(now, jaz.options.Holdtime)
		if h < 0 {
			h = 0
		}
		labHoldtime := gwu.NewLabel(fmt.Sprintf("%v", h))

		buttonRun := gwu.NewButton("Run")
		id := d.Id
		buttonRun.AddEHandlerFunc(func(e gwu.Event) {
			runPriority(jaz, id)
		}, gwu.ETypeClick)

		t.Add(labMod, i, 0)
		t.Add(labId, i, 1)
		t.Add(labHost, i, 2)
		t.Add(labTransport, i, 3)
		t.Add(labLastStatus, i, 4)
		t.Add(labLastTry, i, 5)
		t.Add(labLastSuccess, i, 6)
		t.Add(labHoldtime, i, 7)
		t.Add(buttonRun, i, 8)

		for j := 0; j < COLS; j++ {
			t.CellFmt(i, j).Style().AddClass("device_table_cell")
		}

		i++
	}
}

func runPriority(jaz *app, id string) {
	jaz.logger.Printf("runPriority: device: %s", id)
	jaz.priority <- id
}

func buildHomeWin(jaz *app, s gwu.Session) {

	winName := fmt.Sprintf("%s home", appName)
	win := newWin(jaz, "home", winName)

	win.Add(jaz.apHome)

	l := gwu.NewLabel(winName)
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	win.Add(l)

	t := gwu.NewTable()
	t.Style().AddClass("device_table")

	refresh := func(e gwu.Event) {
		t.Clear() // clear out table contents
		buildDeviceTable(jaz, t)
		e.MarkDirty(t)
	}

	refreshButton := gwu.NewButton("Refresh")
	refreshButton.AddEHandlerFunc(func(e gwu.Event) {
		refresh(e)
	}, gwu.ETypeClick)
	win.Add(refreshButton)

	win.AddEHandlerFunc(func(e gwu.Event) {
		refresh(e)
	}, gwu.ETypeWinLoad)

	buildDeviceTable(jaz, t)

	win.Add(t)

	s.AddWin(win)

	jaz.winHome = win
}

func timestampString(ts time.Time) string {
	if ts.IsZero() {
		return "never"
	}
	return ts.String()
}

func buildLoginWin(jaz *app, s gwu.Session) {

	winName := fmt.Sprintf("%s login", appName)
	win := newWin(jaz, "login", winName)

	win.Style().SetFullSize()
	win.SetAlign(gwu.HACenter, gwu.VAMiddle)

	p := gwu.NewPanel()
	p.SetHAlign(gwu.HACenter)
	p.SetCellPadding(2)

	l := gwu.NewLabel(winName)
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("150%")
	p.Add(l)
	l = gwu.NewLabel("Login")
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	p.Add(l)
	p.CellFmt(l).Style().SetBorder2(1, gwu.BrdStyleDashed, gwu.ClrNavy)
	l = gwu.NewLabel("FIXME: username must be equal to password")
	l.Style().SetFontSize("80%").SetFontStyle(gwu.FontStyleItalic)
	p.Add(l)

	errL := gwu.NewLabel("")
	errL.Style().SetColor(gwu.ClrRed)
	p.Add(errL)

	table := gwu.NewTable()
	table.SetCellPadding(2)
	table.EnsureSize(2, 2)
	table.Add(gwu.NewLabel("Username:"), 0, 0)
	tb := gwu.NewTextBox("")
	tb.AddSyncOnETypes(gwu.ETypeKeyUp) // synchronize values during editing (while you type in characters)

	tb.Style().SetWidthPx(160)
	table.Add(tb, 0, 1)
	table.Add(gwu.NewLabel("Password:"), 1, 0)
	pb := gwu.NewPasswBox("")
	pb.AddSyncOnETypes(gwu.ETypeKeyUp) // synchronize values during editing (while you type in characters)

	pb.Style().SetWidthPx(160)
	table.Add(pb, 1, 1)
	p.Add(table)
	b := gwu.NewButton("OK")

	p.Add(b)
	l = gwu.NewLabel("")
	p.Add(l)
	p.CellFmt(l).Style().SetHeightPx(200)

	loginHandler := func(e gwu.Event) {

		user := tb.Text()
		pass := pb.Text()
		auth := loginAuth(user, pass)

		//jaz.logf("debug login user=[%s] pass=[%s] result=[%v]", user, pass, auth)

		if auth {

			// Clear username/password fields
			tb.SetText("")
			pb.SetText("")

			// Clear error message
			errL.SetText("")
			e.MarkDirty(errL)

			newSession := e.NewSession()
			newSession.SetAttr("username", user)

			remoteAddr := "(remoteAddr?)"
			if hrr, ok := e.(gwu.HasRequestResponse); ok {
				req := hrr.Request()
				remoteAddr = req.RemoteAddr
			}

			buildPrivateWins(jaz, newSession, remoteAddr)

			accountPanelUpdateEvent(jaz, user, e)

			e.ReloadWin("admin")
		} else {
			//e.SetFocusedComp(tb)
			errL.SetText("Invalid user name or password!")
			e.MarkDirty(errL)
		}
	}

	enterHandler := func(e gwu.Event) {
		if e.Type() == gwu.ETypeKeyPress && e.KeyCode() == gwu.KeyEnter {
			// enter key was pressed
			loginHandler(e)
		}
	}

	tb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	pb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	b.AddEHandlerFunc(loginHandler, gwu.ETypeClick)

	win.Add(p)
	win.SetFocusedCompId(tb.Id())

	s.AddWin(win)
}

func loginAuth(user, pass string) bool {
	return user == pass // loginAuth: FIXME WRITEME
}

func buildPrivateWins(jaz *app, s gwu.Session, remoteAddr string) {
	user := s.Attr("username").(string)

	buildLogoutWin(jaz, s, user, remoteAddr)
	buildAdminWin(jaz, s, user, remoteAddr)
}

func buildLogoutWin(jaz *app, s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s logout", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := newWin(jaz, "logout", winName)

	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	win.Add(jaz.apLogout)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	p := gwu.NewPanel()
	p.SetCellPadding(2)

	logoutButton := gwu.NewButton("Logout")

	p.Add(logoutButton)

	win.Add(p)
	s.AddWin(win)

	jaz.winLogout = win
}

func buildAdminWin(jaz *app, s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s admin", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := newWin(jaz, "admin", winName)

	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	win.Add(jaz.apAdmin)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	win.Add(gwu.NewLabel("click on this window to see updates"))

	win.AddEHandlerFunc(func(e gwu.Event) {

		if hrr, ok := e.(gwu.HasRequestResponse); ok {
			req := hrr.Request()
			remoteAddr = req.RemoteAddr
		}

		win.Add(gwu.NewLabel(fmt.Sprintf("click - addr=%v", remoteAddr)))
		e.MarkDirty(win)
	}, gwu.ETypeClick)

	s.AddWin(win)

	jaz.winAdmin = win
}
<commit_msg>Per-device window.<commit_after>package main

import (
	"fmt"
	//"log"
	//"math/rand"
	//"os"
	//"strconv"
	"sort"
	"time"

	"github.com/icza/gowut/gwu"
	"github.com/udhos/jazigo/dev"
)

func newAccPanel(user string) gwu.Panel {
	ap := gwu.NewHorizontalPanel()
	ap.Style().AddClass("account_panel")

	ap.Add(gwu.NewLabel(fmt.Sprintf("%s %s", appName, appVersion)))

	if user == "" {
		// guest user

		// create login button
		b := gwu.NewButton("Login")
		b.AddEHandlerFunc(func(e gwu.Event) {
			e.ReloadWin("login")
		}, gwu.ETypeClick)
		ap.Add(b)

	} else {

		// logged user
		l := gwu.NewLabel(fmt.Sprintf("username=[%s]", user))
		ap.Add(l)
	}
	return ap
}

func accountPanelUpdate(jaz *app, user string) {

	if jaz.winHome != nil {
		if jaz.apHome != nil {
			home := jaz.winHome.ById(jaz.apHome.Id())
			jaz.winHome.Remove(home)
		}
		jaz.apHome = newAccPanel(user)
		if !jaz.winHome.Insert(jaz.apHome, 0) {
			jaz.logf("home win insert accPanel failed")
		}
	}

	if jaz.winAdmin != nil {
		if jaz.apAdmin != nil {
			admin := jaz.winAdmin.ById(jaz.apAdmin.Id())
			jaz.winAdmin.Remove(admin)
		}
		jaz.apAdmin = newAccPanel(user)
		if !jaz.winAdmin.Insert(jaz.apAdmin, 0) {
			jaz.logf("admin win insert accPanel failed")
		}
	}

	if jaz.winLogout != nil {
		if jaz.apLogout != nil {
			logout := jaz.winLogout.ById(jaz.apLogout.Id())
			jaz.winLogout.Remove(logout)
		}
		jaz.apLogout = newAccPanel(user)
		if !jaz.winLogout.Insert(jaz.apLogout, 0) {
			jaz.logf("logout win insert accPanel failed")
		}
	}

}

func accountPanelUpdateEvent(jaz *app, user string, e gwu.Event) {
	accountPanelUpdate(jaz, user)

	if jaz.winHome != nil {
		e.MarkDirty(jaz.winHome)
	}

	if jaz.winAdmin != nil {
		e.MarkDirty(jaz.winAdmin)
	}

	if jaz.winLogout != nil {
		e.MarkDirty(jaz.winLogout)
	}
}

func newWin(jaz *app, path, name string) gwu.Window {
	win := gwu.NewWindow(path, name)
	cssLink := fmt.Sprintf(`<link rel="stylesheet" type="text/css" href="%s">`, jaz.cssPath)
	win.AddHeadHtml(cssLink)
	jaz.logf("window=[%s] attached CSS=[%s]", path, cssLink)
	return win
}

type sortById struct {
	data []*dev.Device
}

func (s sortById) Len() int {
	return len(s.data)
}
func (s sortById) Swap(i, j int) {
	s.data[i], s.data[j] = s.data[j], s.data[i]
}
func (s sortById) Less(i, j int) bool {
	return s.data[i].Id < s.data[j].Id
}

func buildDeviceTable(jaz *app, s gwu.Session, t gwu.Table) {
	const COLS = 9

	t.Add(gwu.NewLabel("Model"), 0, 0)
	t.Add(gwu.NewLabel("Device"), 0, 1)
	t.Add(gwu.NewLabel("Host"), 0, 2)
	t.Add(gwu.NewLabel("Transport"), 0, 3)
	t.Add(gwu.NewLabel("Last Status"), 0, 4)
	t.Add(gwu.NewLabel("Last Try"), 0, 5)
	t.Add(gwu.NewLabel("Last Success"), 0, 6)
	t.Add(gwu.NewLabel("Holdtime"), 0, 7)
	t.Add(gwu.NewLabel("Run Now"), 0, 8)

	for j := 0; j < COLS; j++ {
		t.CellFmt(0, j).Style().AddClass("device_table_cell")
	}

	devList := jaz.table.ListDevices()
	sort.Sort(sortById{data: devList})

	now := time.Now()

	i := 1
	for _, d := range devList {
		labMod := gwu.NewLabel(d.Model())

		devWin := "device-" + d.Id
		labId := gwu.NewLink(d.Id, "/"+appName+"/"+devWin)
		labId.AddEHandlerFunc(func(e gwu.Event) {
			win := s.WinByName(devWin)
			if win == nil {
				win = newWin(jaz, devWin, "Device "+d.Id)
				win.Add(gwu.NewLabel("Device: " + d.Id))

				panel := gwu.NewTabPanel()

				panel.Add(gwu.NewLabel("Files"), gwu.NewLabel("Files"))
				panel.Add(gwu.NewLabel("Properties"), gwu.NewLabel("Properties"))
				panel.Add(gwu.NewLabel("Error Log"), gwu.NewLabel("Error Log"))

				win.Add(panel)
				s.AddWin(win)
				//jaz.logger.Printf("buildDeviceTable: win=[%s] created", devWin)
			}
		}, gwu.ETypeClick)

		labHost := gwu.NewLabel(d.HostPort)
		labTransport := gwu.NewLabel(d.Transports)
		labLastStatus := gwu.NewLabel(fmt.Sprintf("%v", d.LastStatus()))
		labLastTry := gwu.NewLabel(timestampString(d.LastTry()))
		labLastSuccess := gwu.NewLabel(timestampString(d.LastSuccess()))
		h := d.Holdtime(now, jaz.options.Holdtime)
		if h < 0 {
			h = 0
		}
		labHoldtime := gwu.NewLabel(fmt.Sprintf("%v", h))

		buttonRun := gwu.NewButton("Run")
		id := d.Id
		buttonRun.AddEHandlerFunc(func(e gwu.Event) {
			runPriority(jaz, id)
		}, gwu.ETypeClick)

		t.Add(labMod, i, 0)
		t.Add(labId, i, 1)
		t.Add(labHost, i, 2)
		t.Add(labTransport, i, 3)
		t.Add(labLastStatus, i, 4)
		t.Add(labLastTry, i, 5)
		t.Add(labLastSuccess, i, 6)
		t.Add(labHoldtime, i, 7)
		t.Add(buttonRun, i, 8)

		for j := 0; j < COLS; j++ {
			t.CellFmt(i, j).Style().AddClass("device_table_cell")
		}

		i++
	}
}

func runPriority(jaz *app, id string) {
	jaz.logger.Printf("runPriority: device: %s", id)
	jaz.priority <- id
}

func buildHomeWin(jaz *app, s gwu.Session) {

	winName := fmt.Sprintf("%s home", appName)
	win := newWin(jaz, "home", winName)

	win.Add(jaz.apHome)

	l := gwu.NewLabel(winName)
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	win.Add(l)

	t := gwu.NewTable()
	t.Style().AddClass("device_table")

	refresh := func(e gwu.Event) {
		t.Clear() // clear out table contents
		buildDeviceTable(jaz, s, t)
		e.MarkDirty(t)
	}

	refreshButton := gwu.NewButton("Refresh")
	refreshButton.AddEHandlerFunc(func(e gwu.Event) {
		refresh(e)
	}, gwu.ETypeClick)
	win.Add(refreshButton)

	win.AddEHandlerFunc(func(e gwu.Event) {
		refresh(e)
	}, gwu.ETypeWinLoad)

	buildDeviceTable(jaz, s, t)

	win.Add(t)

	s.AddWin(win)

	jaz.winHome = win
}

func timestampString(ts time.Time) string {
	if ts.IsZero() {
		return "never"
	}
	return ts.String()
}

func buildLoginWin(jaz *app, s gwu.Session) {

	winName := fmt.Sprintf("%s login", appName)
	win := newWin(jaz, "login", winName)

	win.Style().SetFullSize()
	win.SetAlign(gwu.HACenter, gwu.VAMiddle)

	p := gwu.NewPanel()
	p.SetHAlign(gwu.HACenter)
	p.SetCellPadding(2)

	l := gwu.NewLabel(winName)
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("150%")
	p.Add(l)
	l = gwu.NewLabel("Login")
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	p.Add(l)
	p.CellFmt(l).Style().SetBorder2(1, gwu.BrdStyleDashed, gwu.ClrNavy)
	l = gwu.NewLabel("FIXME: username must be equal to password")
	l.Style().SetFontSize("80%").SetFontStyle(gwu.FontStyleItalic)
	p.Add(l)

	errL := gwu.NewLabel("")
	errL.Style().SetColor(gwu.ClrRed)
	p.Add(errL)

	table := gwu.NewTable()
	table.SetCellPadding(2)
	table.EnsureSize(2, 2)
	table.Add(gwu.NewLabel("Username:"), 0, 0)
	tb := gwu.NewTextBox("")
	tb.AddSyncOnETypes(gwu.ETypeKeyUp) // synchronize values during editing (while you type in characters)

	tb.Style().SetWidthPx(160)
	table.Add(tb, 0, 1)
	table.Add(gwu.NewLabel("Password:"), 1, 0)
	pb := gwu.NewPasswBox("")
	pb.AddSyncOnETypes(gwu.ETypeKeyUp) // synchronize values during editing (while you type in characters)

	pb.Style().SetWidthPx(160)
	table.Add(pb, 1, 1)
	p.Add(table)
	b := gwu.NewButton("OK")

	p.Add(b)
	l = gwu.NewLabel("")
	p.Add(l)
	p.CellFmt(l).Style().SetHeightPx(200)

	loginHandler := func(e gwu.Event) {

		user := tb.Text()
		pass := pb.Text()
		auth := loginAuth(user, pass)

		//jaz.logf("debug login user=[%s] pass=[%s] result=[%v]", user, pass, auth)

		if auth {

			// Clear username/password fields
			tb.SetText("")
			pb.SetText("")

			// Clear error message
			errL.SetText("")
			e.MarkDirty(errL)

			newSession := e.NewSession()
			newSession.SetAttr("username", user)

			remoteAddr := "(remoteAddr?)"
			if hrr, ok := e.(gwu.HasRequestResponse); ok {
				req := hrr.Request()
				remoteAddr = req.RemoteAddr
			}

			buildPrivateWins(jaz, newSession, remoteAddr)

			accountPanelUpdateEvent(jaz, user, e)

			e.ReloadWin("admin")
		} else {
			//e.SetFocusedComp(tb)
			errL.SetText("Invalid user name or password!")
			e.MarkDirty(errL)
		}
	}

	enterHandler := func(e gwu.Event) {
		if e.Type() == gwu.ETypeKeyPress && e.KeyCode() == gwu.KeyEnter {
			// enter key was pressed
			loginHandler(e)
		}
	}

	tb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	pb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	b.AddEHandlerFunc(loginHandler, gwu.ETypeClick)

	win.Add(p)
	win.SetFocusedCompId(tb.Id())

	s.AddWin(win)
}

func loginAuth(user, pass string) bool {
	return user == pass // loginAuth: FIXME WRITEME
}

func buildPrivateWins(jaz *app, s gwu.Session, remoteAddr string) {
	user := s.Attr("username").(string)

	buildLogoutWin(jaz, s, user, remoteAddr)
	buildAdminWin(jaz, s, user, remoteAddr)
}

func buildLogoutWin(jaz *app, s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s logout", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := newWin(jaz, "logout", winName)

	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	win.Add(jaz.apLogout)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	p := gwu.NewPanel()
	p.SetCellPadding(2)

	logoutButton := gwu.NewButton("Logout")

	p.Add(logoutButton)

	win.Add(p)
	s.AddWin(win)

	jaz.winLogout = win
}

func buildAdminWin(jaz *app, s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s admin", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := newWin(jaz, "admin", winName)

	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	win.Add(jaz.apAdmin)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	win.Add(gwu.NewLabel("click on this window to see updates"))

	win.AddEHandlerFunc(func(e gwu.Event) {

		if hrr, ok := e.(gwu.HasRequestResponse); ok {
			req := hrr.Request()
			remoteAddr = req.RemoteAddr
		}

		win.Add(gwu.NewLabel(fmt.Sprintf("click - addr=%v", remoteAddr)))
		e.MarkDirty(win)
	}, gwu.ETypeClick)

	s.AddWin(win)

	jaz.winAdmin = win
}
<|endoftext|>


<commit_before>// Copyright 2013 Akeda Bagus <admin@gedex.web.id>. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package inflector

import (
	"testing"
)

type inflectorTest struct {
	in  string
	out string
}

var pluralTests = []inflectorTest{
	{"categoria", "categorias"},
	{"house", "houses"},
	{"powerhouse", "powerhouses"},
	{"Bus", "Buses"},
	{"bus", "buses"},
	{"menu", "menus"},
	{"news", "news"},
	{"food_menu", "food_menus"},
	{"Menu", "Menus"},
	{"FoodMenu", "FoodMenus"},
	{"quiz", "quizzes"},
	{"matrix_row", "matrix_rows"},
	{"matrix", "matrices"},
	{"vertex", "vertices"},
	{"index", "indices"},
	{"Alias", "Aliases"},
	{"Aliases", "Aliases"},
	{"Media", "Media"},
	{"NodeMedia", "NodeMedia"},
	{"alumnus", "alumni"},
	{"bacillus", "bacilli"},
	{"cactus", "cacti"},
	{"focus", "foci"},
	{"fungus", "fungi"},
	{"nucleus", "nuclei"},
	{"octopus", "octopuses"},
	{"radius", "radii"},
	{"stimulus", "stimuli"},
	{"syllabus", "syllabi"},
	{"terminus", "termini"},
	{"virus", "viri"},
	{"person", "people"},
	{"people", "people"},
	{"glove", "gloves"},
	{"crisis", "crises"},
	{"tax", "taxes"},
	{"wave", "waves"},
	{"bureau", "bureaus"},
	{"cafe", "cafes"},
	{"roof", "roofs"},
	{"foe", "foes"},
	{"cookie", "cookies"},
	{"wolf", "wolves"},
	{"thief", "thieves"},
	{"potato", "potatoes"},
	{"hero", "heroes"},
	{"buffalo", "buffalo"},
	{"tooth", "teeth"},
	{"goose", "geese"},
	{"foot", "feet"},
	{"objective", "objectives"},
	{"", ""},
}

var singularTests = []inflectorTest{
	{"categorias", "categoria"},
	{"menus", "menu"},
	{"news", "news"},
	{"food_menus", "food_menu"},
	{"Menus", "Menu"},
	{"FoodMenus", "FoodMenu"},
	{"houses", "house"},
	{"powerhouses", "powerhouse"},
	{"quizzes", "quiz"},
	{"Buses", "Bus"},
	{"buses", "bus"},
	{"matrix_rows", "matrix_row"},
	{"matrices", "matrix"},
	{"vertices", "vertex"},
	{"indices", "index"},
	{"Aliases", "Alias"},
	{"Alias", "Alias"},
	{"Media", "Media"},
	{"NodeMedia", "NodeMedia"},
	{"alumni", "alumnus"},
	{"bacilli", "bacillus"},
	{"cacti", "cactus"},
	{"foci", "focus"},
	{"fungi", "fungus"},
	{"nuclei", "nucleus"},
	{"octopuses", "octopus"},
	{"radii", "radius"},
	{"stimuli", "stimulus"},
	{"syllabi", "syllabus"},
	{"termini", "terminus"},
	{"viri", "virus"},
	{"people", "person"},
	{"gloves", "glove"},
	{"doves", "dove"},
	{"lives", "life"},
	{"knives", "knife"},
	{"wolves", "wolf"},
	{"slaves", "slave"},
	{"shelves", "shelf"},
	{"taxis", "taxi"},
	{"taxes", "tax"},
	{"Taxes", "Tax"},
	{"AwesomeTaxes", "AwesomeTax"},
	{"faxes", "fax"},
	{"waxes", "wax"},
	{"niches", "niche"},
	{"waves", "wave"},
	{"bureaus", "bureau"},
	{"genetic_analyses", "genetic_analysis"},
	{"doctor_diagnoses", "doctor_diagnosis"},
	{"parantheses", "paranthesis"},
	{"Causes", "Cause"},
	{"colossuses", "colossus"},
	{"diagnoses", "diagnosis"},
	{"bases", "basis"},
	{"analyses", "analysis"},
	{"curves", "curve"},
	{"cafes", "cafe"},
	{"roofs", "roof"},
	{"foes", "foe"},
	{"databases", "database"},
	{"cookies", "cookie"},
	{"thieves", "thief"},
	{"potatoes", "potato"},
	{"heroes", "hero"},
	{"buffalos", "buffalo"},
	{"babies", "baby"},
	{"teeth", "tooth"},
	{"geese", "goose"},
	{"feet", "foot"},
	{"objectives", "objective"},
	{"", ""},
}

func TestPluralize(t *testing.T) {
	for i := 0; i < len(pluralTests); i++ {
		pt := pluralTests[i]
		s := Pluralize(pt.in)
		if s != pt.out {
			t.Fatalf("Pluralize(%s) = %s want %s", pt.in, s, pt.out)
		}
		// Second retrieval should returns the same result.
		// This is also tests the cache
		s = Pluralize(pt.in)
		if s != pt.out {
			t.Fatalf("Pluralize(%s) = %s want %s", pt.in, s, pt.out)
		}
	}
}

func TestSingularize(t *testing.T) {
	for i := 0; i < len(singularTests); i++ {
		pt := singularTests[i]
		s := Singularize(pt.in)
		if s != pt.out {
			t.Fatalf("Singularize(%s) = %s want %s", pt.in, s, pt.out)
		}
		// Second retrieval should returns the same result.
		// This is also tests the cache
		s = Singularize(pt.in)
		if s != pt.out {
			t.Fatalf("Singularize(%s) = %s want %s", pt.in, s, pt.out)
		}
	}
}
<commit_msg>Remove code duplication on Unit test<commit_after>// Copyright 2013 Akeda Bagus <admin@gedex.web.id>. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package inflector

import (
	"testing"
)

type inflector func(string) string

type inflectorTest struct {
	in  string
	out string
}

var pluralTests = []inflectorTest{
	{"categoria", "categorias"},
	{"house", "houses"},
	{"powerhouse", "powerhouses"},
	{"Bus", "Buses"},
	{"bus", "buses"},
	{"menu", "menus"},
	{"news", "news"},
	{"food_menu", "food_menus"},
	{"Menu", "Menus"},
	{"FoodMenu", "FoodMenus"},
	{"quiz", "quizzes"},
	{"matrix_row", "matrix_rows"},
	{"matrix", "matrices"},
	{"vertex", "vertices"},
	{"index", "indices"},
	{"Alias", "Aliases"},
	{"Aliases", "Aliases"},
	{"Media", "Media"},
	{"NodeMedia", "NodeMedia"},
	{"alumnus", "alumni"},
	{"bacillus", "bacilli"},
	{"cactus", "cacti"},
	{"focus", "foci"},
	{"fungus", "fungi"},
	{"nucleus", "nuclei"},
	{"octopus", "octopuses"},
	{"radius", "radii"},
	{"stimulus", "stimuli"},
	{"syllabus", "syllabi"},
	{"terminus", "termini"},
	{"virus", "viri"},
	{"person", "people"},
	{"people", "people"},
	{"glove", "gloves"},
	{"crisis", "crises"},
	{"tax", "taxes"},
	{"wave", "waves"},
	{"bureau", "bureaus"},
	{"cafe", "cafes"},
	{"roof", "roofs"},
	{"foe", "foes"},
	{"cookie", "cookies"},
	{"wolf", "wolves"},
	{"thief", "thieves"},
	{"potato", "potatoes"},
	{"hero", "heroes"},
	{"buffalo", "buffalo"},
	{"tooth", "teeth"},
	{"goose", "geese"},
	{"foot", "feet"},
	{"objective", "objectives"},
	{"", ""},
}

var singularTests = []inflectorTest{
	{"categorias", "categoria"},
	{"menus", "menu"},
	{"news", "news"},
	{"food_menus", "food_menu"},
	{"Menus", "Menu"},
	{"FoodMenus", "FoodMenu"},
	{"houses", "house"},
	{"powerhouses", "powerhouse"},
	{"quizzes", "quiz"},
	{"Buses", "Bus"},
	{"buses", "bus"},
	{"matrix_rows", "matrix_row"},
	{"matrices", "matrix"},
	{"vertices", "vertex"},
	{"indices", "index"},
	{"Aliases", "Alias"},
	{"Alias", "Alias"},
	{"Media", "Media"},
	{"NodeMedia", "NodeMedia"},
	{"alumni", "alumnus"},
	{"bacilli", "bacillus"},
	{"cacti", "cactus"},
	{"foci", "focus"},
	{"fungi", "fungus"},
	{"nuclei", "nucleus"},
	{"octopuses", "octopus"},
	{"radii", "radius"},
	{"stimuli", "stimulus"},
	{"syllabi", "syllabus"},
	{"termini", "terminus"},
	{"viri", "virus"},
	{"people", "person"},
	{"gloves", "glove"},
	{"doves", "dove"},
	{"lives", "life"},
	{"knives", "knife"},
	{"wolves", "wolf"},
	{"slaves", "slave"},
	{"shelves", "shelf"},
	{"taxis", "taxi"},
	{"taxes", "tax"},
	{"Taxes", "Tax"},
	{"AwesomeTaxes", "AwesomeTax"},
	{"faxes", "fax"},
	{"waxes", "wax"},
	{"niches", "niche"},
	{"waves", "wave"},
	{"bureaus", "bureau"},
	{"genetic_analyses", "genetic_analysis"},
	{"doctor_diagnoses", "doctor_diagnosis"},
	{"parantheses", "paranthesis"},
	{"Causes", "Cause"},
	{"colossuses", "colossus"},
	{"diagnoses", "diagnosis"},
	{"bases", "basis"},
	{"analyses", "analysis"},
	{"curves", "curve"},
	{"cafes", "cafe"},
	{"roofs", "roof"},
	{"foes", "foe"},
	{"databases", "database"},
	{"cookies", "cookie"},
	{"thieves", "thief"},
	{"potatoes", "potato"},
	{"heroes", "hero"},
	{"buffalos", "buffalo"},
	{"babies", "baby"},
	{"teeth", "tooth"},
	{"geese", "goose"},
	{"feet", "foot"},
	{"objectives", "objective"},
	{"", ""},
}

func checker(t *testing.T, name string, fn inflector, tests []inflectorTest) {
	for _, n := range tests {
		s := fn(n.in)
		if s != n.out {
			t.Fatalf("%s(%s) = %s want %s", name, n.in, s, n.out)
		}
		// Second retrieval should return the same result.
		// This also test the cache
		s = fn(n.in)
		if s != n.out {
			t.Fatalf("%s(%s) = %s want %s", name, n.in, s, n.out)
		}
	}
}

func TestPluralize(t *testing.T) {
	checker(t, "Pluralize", Pluralize, pluralTests)
}

func TestSingularize(t *testing.T) {
	checker(t, "Singularize", Singularize, singularTests)
}
<|endoftext|>


<commit_before>package generator

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

const (
	multilinePrefix = "(?m)"
	orPipe          = "|"
)

// GenHeuristics generates language identification heuristics in Go.
// It is of generator.File type.
func GenHeuristics(fileToParse, _, outPath, tmplPath, tmplName, commit string) error {
	heuristicsYaml, err := parseYaml(fileToParse)
	if err != nil {
		return err
	}

	langPatterns, err := loadHeuristics(heuristicsYaml)
	if err != nil {
		return err
	}

	buf := &bytes.Buffer{}
	err = executeTemplate(buf, tmplName, tmplPath, commit, nil, langPatterns)
	if err != nil {
		return err
	}

	return formatedWrite(outPath, buf.Bytes())
}

// loadHeuristics transforms parsed YAML to map[".ext"]->IR for code generation.
func loadHeuristics(yaml *Heuristics) (map[string][]*LanguagePattern, error) {
	var patterns = make(map[string][]*LanguagePattern)
	for _, disambiguation := range yaml.Disambiguations {
		var rules []*LanguagePattern
		for _, rule := range disambiguation.Rules {
			langPattern := loadRule(yaml.NamedPatterns, rule)
			if langPattern != nil {
				rules = append(rules, langPattern)
			}
		}
		// unroll to a single map
		for _, ext := range disambiguation.Extensions {
			if _, ok := patterns[ext]; ok {
				return nil, fmt.Errorf("cannt add extension '%s', it already exists for %+v", ext, patterns[ext])
			}
			patterns[ext] = rules
		}

	}
	return patterns, nil
}

// loadRule transforms single rule from parsed YAML to IR for code generation.
// For OrPattern case, it always combines multiple patterns into a single one.
func loadRule(namedPatterns map[string]StringArray, rule *Rule) *LanguagePattern {
	var result *LanguagePattern
	if len(rule.And) != 0 { // AndPattern
		var subPatterns []*LanguagePattern
		for _, r := range rule.And {
			subp := loadRule(namedPatterns, r)
			subPatterns = append(subPatterns, subp)
		}
		result = &LanguagePattern{"And", rule.Languages, "", subPatterns}
	} else if len(rule.Pattern) != 0 { // OrPattern
		conjunction := strings.Join(rule.Pattern, orPipe)
		pattern := convertToValidRegexp(conjunction)
		result = &LanguagePattern{"Or", rule.Languages, pattern, nil}
	} else if rule.NegativePattern != "" { // NotPattern
		pattern := convertToValidRegexp(rule.NegativePattern)
		result = &LanguagePattern{"Not", rule.Languages, pattern, nil}
	} else if rule.NamedPattern != "" { // Named OrPattern
		conjunction := strings.Join(namedPatterns[rule.NamedPattern], orPipe)
		pattern := convertToValidRegexp(conjunction)
		result = &LanguagePattern{"Or", rule.Languages, pattern, nil}
	} else { // AlwaysPattern
		result = &LanguagePattern{"Always", rule.Languages, "", nil}
	}

	if isUnsupportedRegexpSyntax(result.Pattern) {
		log.Printf("skipping rule: language:'%q', rule:'%q'\n", rule.Languages, result.Pattern)
		return nil
	}
	return result
}

// LanguagePattern is an IR of parsed Rule suitable for code generations.
// Strings are used as this is to be be consumed by text/template.
type LanguagePattern struct {
	Op      string
	Langs   []string
	Pattern string
	Rules   []*LanguagePattern
}

type Heuristics struct {
	Disambiguations []*Disambiguation
	NamedPatterns   map[string]StringArray `yaml:"named_patterns"`
}

type Disambiguation struct {
	Extensions []string `yaml:"extensions,flow"`
	Rules      []*Rule  `yaml:"rules"`
}

type Rule struct {
	Patterns  `yaml:",inline"`
	Languages StringArray `yaml:"language"`
	And       []*Rule
}

type Patterns struct {
	Pattern         StringArray `yaml:"pattern,omitempty"`
	NamedPattern    string      `yaml:"named_pattern,omitempty"`
	NegativePattern string      `yaml:"negative_pattern,omitempty"`
}

// StringArray is workaround for parsing named_pattern,
// wich is sometimes arry and sometimes not.
// See https://github.com/go-yaml/yaml/issues/100
type StringArray []string

// UnmarshalYAML allowes to parse element always as a []string
func (sa *StringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var multi []string
	if err := unmarshal(&multi); err != nil {
		var single string
		if err := unmarshal(&single); err != nil {
			return err
		}
		*sa = []string{single}
	} else {
		*sa = multi
	}
	return nil
}

func parseYaml(file string) (*Heuristics, error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	h := &Heuristics{}
	if err := yaml.Unmarshal(data, &h); err != nil {
		return nil, err
	}

	return h, nil
}

// isUnsupportedRegexpSyntax filters regexp syntax that is not supported by RE2.
// In particular, we stumbled up on usage of next cases:
// - named & numbered capturing group/after text matching
// - backreference
// For referece on supported syntax see https://github.com/google/re2/wiki/Syntax
func isUnsupportedRegexpSyntax(reg string) bool {
	return strings.Contains(reg, `(?<`) || strings.Contains(reg, `\1`) ||
		// See https://github.com/github/linguist/pull/4243#discussion_r246105067
		(strings.HasPrefix(reg, multilinePrefix+`/`) && strings.HasSuffix(reg, `/`))
}

// convertToValidRegexp converts Ruby regexp syntaxt to RE2 equivalent.
// Does not work with Ruby regexp literals.
func convertToValidRegexp(rubyRegexp string) string {
	return multilinePrefix + rubyRegexp
}
<commit_msg>address review feedback - tixing a fypo<commit_after>package generator

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	yaml "gopkg.in/yaml.v2"
)

const (
	multilinePrefix = "(?m)"
	orPipe          = "|"
)

// GenHeuristics generates language identification heuristics in Go.
// It is of generator.File type.
func GenHeuristics(fileToParse, _, outPath, tmplPath, tmplName, commit string) error {
	heuristicsYaml, err := parseYaml(fileToParse)
	if err != nil {
		return err
	}

	langPatterns, err := loadHeuristics(heuristicsYaml)
	if err != nil {
		return err
	}

	buf := &bytes.Buffer{}
	err = executeTemplate(buf, tmplName, tmplPath, commit, nil, langPatterns)
	if err != nil {
		return err
	}

	return formatedWrite(outPath, buf.Bytes())
}

// loadHeuristics transforms parsed YAML to map[".ext"]->IR for code generation.
func loadHeuristics(yaml *Heuristics) (map[string][]*LanguagePattern, error) {
	var patterns = make(map[string][]*LanguagePattern)
	for _, disambiguation := range yaml.Disambiguations {
		var rules []*LanguagePattern
		for _, rule := range disambiguation.Rules {
			langPattern := loadRule(yaml.NamedPatterns, rule)
			if langPattern != nil {
				rules = append(rules, langPattern)
			}
		}
		// unroll to a single map
		for _, ext := range disambiguation.Extensions {
			if _, ok := patterns[ext]; ok {
				return nil, fmt.Errorf("cannot add extension '%s', it already exists for %+v", ext, patterns[ext])
			}
			patterns[ext] = rules
		}

	}
	return patterns, nil
}

// loadRule transforms single rule from parsed YAML to IR for code generation.
// For OrPattern case, it always combines multiple patterns into a single one.
func loadRule(namedPatterns map[string]StringArray, rule *Rule) *LanguagePattern {
	var result *LanguagePattern
	if len(rule.And) != 0 { // AndPattern
		var subPatterns []*LanguagePattern
		for _, r := range rule.And {
			subp := loadRule(namedPatterns, r)
			subPatterns = append(subPatterns, subp)
		}
		result = &LanguagePattern{"And", rule.Languages, "", subPatterns}
	} else if len(rule.Pattern) != 0 { // OrPattern
		conjunction := strings.Join(rule.Pattern, orPipe)
		pattern := convertToValidRegexp(conjunction)
		result = &LanguagePattern{"Or", rule.Languages, pattern, nil}
	} else if rule.NegativePattern != "" { // NotPattern
		pattern := convertToValidRegexp(rule.NegativePattern)
		result = &LanguagePattern{"Not", rule.Languages, pattern, nil}
	} else if rule.NamedPattern != "" { // Named OrPattern
		conjunction := strings.Join(namedPatterns[rule.NamedPattern], orPipe)
		pattern := convertToValidRegexp(conjunction)
		result = &LanguagePattern{"Or", rule.Languages, pattern, nil}
	} else { // AlwaysPattern
		result = &LanguagePattern{"Always", rule.Languages, "", nil}
	}

	if isUnsupportedRegexpSyntax(result.Pattern) {
		log.Printf("skipping rule: language:'%q', rule:'%q'\n", rule.Languages, result.Pattern)
		return nil
	}
	return result
}

// LanguagePattern is an IR of parsed Rule suitable for code generations.
// Strings are used as this is to be be consumed by text/template.
type LanguagePattern struct {
	Op      string
	Langs   []string
	Pattern string
	Rules   []*LanguagePattern
}

type Heuristics struct {
	Disambiguations []*Disambiguation
	NamedPatterns   map[string]StringArray `yaml:"named_patterns"`
}

type Disambiguation struct {
	Extensions []string `yaml:"extensions,flow"`
	Rules      []*Rule  `yaml:"rules"`
}

type Rule struct {
	Patterns  `yaml:",inline"`
	Languages StringArray `yaml:"language"`
	And       []*Rule
}

type Patterns struct {
	Pattern         StringArray `yaml:"pattern,omitempty"`
	NamedPattern    string      `yaml:"named_pattern,omitempty"`
	NegativePattern string      `yaml:"negative_pattern,omitempty"`
}

// StringArray is workaround for parsing named_pattern,
// wich is sometimes arry and sometimes not.
// See https://github.com/go-yaml/yaml/issues/100
type StringArray []string

// UnmarshalYAML allowes to parse element always as a []string
func (sa *StringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var multi []string
	if err := unmarshal(&multi); err != nil {
		var single string
		if err := unmarshal(&single); err != nil {
			return err
		}
		*sa = []string{single}
	} else {
		*sa = multi
	}
	return nil
}

func parseYaml(file string) (*Heuristics, error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	h := &Heuristics{}
	if err := yaml.Unmarshal(data, &h); err != nil {
		return nil, err
	}

	return h, nil
}

// isUnsupportedRegexpSyntax filters regexp syntax that is not supported by RE2.
// In particular, we stumbled up on usage of next cases:
// - named & numbered capturing group/after text matching
// - backreference
// For referece on supported syntax see https://github.com/google/re2/wiki/Syntax
func isUnsupportedRegexpSyntax(reg string) bool {
	return strings.Contains(reg, `(?<`) || strings.Contains(reg, `\1`) ||
		// See https://github.com/github/linguist/pull/4243#discussion_r246105067
		(strings.HasPrefix(reg, multilinePrefix+`/`) && strings.HasSuffix(reg, `/`))
}

// convertToValidRegexp converts Ruby regexp syntaxt to RE2 equivalent.
// Does not work with Ruby regexp literals.
func convertToValidRegexp(rubyRegexp string) string {
	return multilinePrefix + rubyRegexp
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'cert.pem' and 'key.pem' and will overwrite existing files.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"flag"
	"fmt"
	"log"
	"math/big"
	"net"
	"os"
	"strings"
	"time"
)

var (
	host       = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
	validFrom  = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
	validFor   = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
	isCA       = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
	rsaBits    = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
	ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
)

func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	case *ecdsa.PrivateKey:
		return &k.PublicKey
	default:
		return nil
	}
}

func pemBlockForKey(priv interface{}) *pem.Block {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
			os.Exit(2)
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
	default:
		return nil
	}
}

func main() {
	flag.Parse()

	if len(*host) == 0 {
		log.Fatalf("Missing required --host parameter")
	}

	var priv interface{}
	var err error
	switch *ecdsaCurve {
	case "":
		priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
	case "P224":
		priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
	case "P256":
		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	case "P384":
		priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
	case "P521":
		priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	default:
		fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve)
		os.Exit(1)
	}
	if err != nil {
		log.Fatalf("failed to generate private key: %s", err)
	}

	var notBefore time.Time
	if len(*validFrom) == 0 {
		notBefore = time.Now()
	} else {
		notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
			os.Exit(1)
		}
	}

	notAfter := notBefore.Add(*validFor)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := strings.Split(*host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	if *isCA {
		template.IsCA = true
		template.KeyUsage |= x509.KeyUsageCertSign
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
	}

	certOut, err := os.Create("cert.pem")
	if err != nil {
		log.Fatalf("failed to open cert.pem for writing: %s", err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()
	log.Print("written cert.pem\n")

	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Print("failed to open key.pem for writing:", err)
		return
	}
	pem.Encode(keyOut, pemBlockForKey(priv))
	keyOut.Close()
	log.Print("written key.pem\n")
}
<commit_msg>crypto/tls: handle errors in generate_cert.go<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'cert.pem' and 'key.pem' and will overwrite existing files.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"flag"
	"fmt"
	"log"
	"math/big"
	"net"
	"os"
	"strings"
	"time"
)

var (
	host       = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
	validFrom  = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
	validFor   = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
	isCA       = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
	rsaBits    = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
	ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
)

func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	case *ecdsa.PrivateKey:
		return &k.PublicKey
	default:
		return nil
	}
}

func pemBlockForKey(priv interface{}) *pem.Block {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
			os.Exit(2)
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
	default:
		return nil
	}
}

func main() {
	flag.Parse()

	if len(*host) == 0 {
		log.Fatalf("Missing required --host parameter")
	}

	var priv interface{}
	var err error
	switch *ecdsaCurve {
	case "":
		priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
	case "P224":
		priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
	case "P256":
		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	case "P384":
		priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
	case "P521":
		priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	default:
		fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve)
		os.Exit(1)
	}
	if err != nil {
		log.Fatalf("failed to generate private key: %s", err)
	}

	var notBefore time.Time
	if len(*validFrom) == 0 {
		notBefore = time.Now()
	} else {
		notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
			os.Exit(1)
		}
	}

	notAfter := notBefore.Add(*validFor)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := strings.Split(*host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	if *isCA {
		template.IsCA = true
		template.KeyUsage |= x509.KeyUsageCertSign
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
	}

	certOut, err := os.Create("cert.pem")
	if err != nil {
		log.Fatalf("failed to open cert.pem for writing: %s", err)
	}
	if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
		log.Fatalf("failed to write data to cert.pem: %s", err)
	}
	if err := certOut.Close(); err != nil {
		log.Fatalf("error closing cert.pem: %s", err)
	}
	log.Print("wrote cert.pem\n")

	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Print("failed to open key.pem for writing:", err)
		return
	}
	if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil {
		log.Fatalf("failed to write data to key.pem: %s", err)
	}
	if err := keyOut.Close(); err != nil {
		log.Fatalf("error closing key.pem: %s", err)
	}
	log.Print("wrote key.pem\n")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

       _ "github.com/go-sql-driver/mysql"
        "database/sql"

	"github.com/ethereum/ethash"
	"github.com/ethereum/go-ethereum/common"

	"github.com/gorilla/mux"

)

var currWork *ResponseArray = nil

var pendingBlockNumber uint64 = 0
var pendingBlockDifficulty *big.Int

var invalidRequest = `{
  "id":64,
  "jsonrpc": "2.0",
  "result": false,
  "error": "invalid request"
}`

var okRequest = `{
  "id":64,
  "jsonrpc": "2.0",
  "result": true
}`

var pow256 = common.BigPow(2, 256)

var hasher = ethash.New()

var poolPort = "5082"
var ethereumPort = "8545" //8545 = geth, 8080 = eth (requires dev branch when using eth client)

var logInfo *log.Logger
var logError *log.Logger

type ResponseArray struct {
	Id      int           `json:"id"`
	Jsonrpc string        `json:"jsonrpc"`
	Result  []interface{} `json:"result"`
}

type ResponseJSON struct {
	Id      int                    `json:"id"`
	Jsonrpc string                 `json:"jsonrpc"`
	Result  map[string]interface{} `json:"result"`
}

type ResponseBool struct {
	Id      int    `json:"id"`
	Jsonrpc string `json:"jsonrpc"`
	Result  bool   `json:"result"`
}

type Request struct {
	Id      int           `json:"id"`
	Jsonrpc string        `json:"jsonrpc"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
}

type block struct {
	difficulty  *big.Int
	hashNoNonce common.Hash
	nonce       uint64
	mixDigest   common.Hash
	number      uint64
}

func (b block) Difficulty() *big.Int     { return b.difficulty }
func (b block) HashNoNonce() common.Hash { return b.hashNoNonce }
func (b block) Nonce() uint64            { return b.nonce }
func (b block) MixDigest() common.Hash   { return b.mixDigest }
func (b block) NumberU64() uint64        { return b.number }

func main() {
	// Set up logging
	logInfo = log.New(os.Stderr, "INFO: ", log.Ldate|log.Ltime)
	logError = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime)
	logInfo.Println("Welcome to ethpool 2.0")
	logInfo.Println("Pool port is", poolPort)
	logInfo.Println("Point your miners to: http://<ip>:" + poolPort + "/{miner}/{difficulty}")

	go updateWork()
	go updatePendingBlock()

	r := mux.NewRouter()
	r.HandleFunc("/{miner}/{difficulty}", handleMiner)
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe(":5082", nil))
}

func handleMiner(rw http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

        if vars["difficulty"] == "0" {
            vars["difficulty"] = "5"
        }

	minerDifficulty, err := strconv.ParseFloat(vars["difficulty"], 64)
	if err != nil {
		logError.Println("Invalid difficulty provided: " + vars["difficulty"])
		minerDifficulty = 5 // Set a fixed difficulty (5MH/s) in this case
		// fmt.Fprint(rw, getErrorResponse("Invalid difficulty provided: "+vars["difficulty"]))
		// return
	}
	minerAdjustedDifficulty := int64(minerDifficulty * 1000000 * 60)

	miner := vars["miner"]
	worker := "default"

	decoder := json.NewDecoder(req.Body)
	var t Request
	err = decoder.Decode(&t)
	if err != nil {
		logError.Println("Invalid JSON request: ", err)
		fmt.Fprint(rw, getErrorResponse("Invalid JSON request"))
		return
	}

	if t.Method == "eth_getWork" {
		difficulty := big.NewInt(minerAdjustedDifficulty)
		// Send the response
		fmt.Fprint(rw, getWorkPackage(difficulty))
	} else if t.Method == "eth_submitHashrate" {
		fmt.Fprint(rw, okRequest)
	} else if t.Method == "eth_submitWork" {
		paramsOrig := t.Params[:]

		hashNoNonce := t.Params[1].(string)
		nonce, err := strconv.ParseUint(strings.Replace(t.Params[0].(string), "0x", "", -1), 16, 64)
		if err != nil {
			logError.Println("Invalid nonce provided: ", err)
			fmt.Fprint(rw, getErrorResponse("Invalid nonce provided"))
			return
		}

		mixDigest := t.Params[2].(string)

		myBlock := block{
			number:      pendingBlockNumber,
			hashNoNonce: common.HexToHash(hashNoNonce),
			difficulty:  big.NewInt(minerAdjustedDifficulty),
			nonce:       nonce,
			mixDigest:   common.HexToHash(mixDigest),
		}

		myBlockRealDiff := block{
			number:      pendingBlockNumber,
			hashNoNonce: common.HexToHash(hashNoNonce),
			difficulty:  pendingBlockDifficulty,
			nonce:       nonce,
			mixDigest:   common.HexToHash(mixDigest),
		}

    oures := "Y"
    upres := "N"

		if hasher.Verify(myBlock) {
			//fmt.Println("Share is valid")
			if hasher.Verify(myBlockRealDiff) {
				submitWork(paramsOrig)
				logInfo.Println(" -=###########################################################################=-")
				logInfo.Println("--=############################# BLOCK FOUND !!! #############################=--")
				logInfo.Println(" -=###########################################################################=-")
    upres = "Y"
			}

			logInfo.Println("Miner", miner, ".", worker, "found valid share for Block: ", myBlock.number, " (Diff:", minerAdjustedDifficulty, "WantDiff:", minerDifficulty, "Mix:", mixDigest, "Hash:", hashNoNonce, "Nonce:", nonce, ")")
		} else {
			logError.Println("Miner", miner, "provided invalid share")
			fmt.Fprint(rw, getErrorResponse("Provided PoW solution is invalid!"))
    oures = "N"
		}

    remoteipa := strings.Split(req.RemoteAddr, ":")
    remoteip := remoteipa[0]

    mysqldiff := minerDifficulty * 4

    db, err := sql.Open("mysql", "ethdb_user:ethdb_pass@/ethdb?charset=utf8")  // of course you have to enter your credentials here !
    checkErr(err)

    stmt, err := db.Prepare("INSERT INTO shares (time, rem_host, username, our_result, upstream_result, difficulty, reason, solution) VALUES ( NOW(), ?, ?, ?, ?, ?, NULL, ?) ")
    checkErr(err)

    mysqlmixDigest := strings.Replace(mixDigest, "0x", "", -1)
    res, err := stmt.Exec(remoteip, miner, oures, upres, mysqldiff, strings.ToLower(mysqlmixDigest))
    checkErr(err)

    id, err := res.LastInsertId()
    checkErr(err)

    logInfo.Println(id)

	// We've found a Block, we have to insert it

	if (upres == "Y") {

		stmt2, err2 := db.Prepare("INSERT INTO blocks (time, height, blockhash, confirmations, accounted) VALUES ( UNIX_TIMESTAMP(NOW()), ?, ?, '0', '0')")
		checkErr(err2)

		res2, err2 := stmt2.Exec(myBlock.number, strings.ToLower(mysqlmixDigest))
		checkErr(err2)

		id2, err2 := res2.LastInsertId()
		checkErr(err2)

		logInfo.Println(id2)

	}

    db.Close()

		fmt.Fprint(rw, okRequest)
	} else {
		logError.Println("Method " + t.Method + " not implemented!")
		fmt.Fprint(rw, getErrorResponse("Method "+t.Method+" not implemented!"))
	}
}

func getWorkPackage(difficulty *big.Int) string {

	if currWork == nil {
		return getErrorResponse("Current work unavailable")
	}

	// Our response object
	response := &ResponseArray{
		Id:      currWork.Id,
		Jsonrpc: currWork.Jsonrpc,
		Result:  currWork.Result[:],
	}

	// Calculte requested difficulty
	diff := new(big.Int).Div(pow256, difficulty)
	diffBytes := string(common.ToHex(diff.Bytes()))

	// Adjust the difficulty for the miner
	response.Result[2] = diffBytes

	// Convert respone object to JSON
	b, _ := json.Marshal(response)

	return string(b)

}

func updateWork() {
	for true {
		currWorkNew, err := callArray("eth_getWork", []interface{}{})

		if err == nil {
			currWork = currWorkNew
		} else {
			currWork = nil
		}

		// fmt.Println("Current work", currWork.Result[0])
		time.Sleep(time.Millisecond * 100)
	}
}

func submitWork(params []interface{}) {
	result, err := callBool("eth_submitWork", params)
	if err == nil {
		logInfo.Println(result.Result)
	}
}

func updatePendingBlock() {
	params := []interface{}{"pending", false}

	for true {
		block, err := callJSON("eth_getBlockByNumber", params)
		if err == nil {
			blockNbr, err := strconv.ParseUint(strings.Replace(block.Result["number"].(string), "0x", "", -1), 16, 64)
			if err == nil {
				pendingBlockNumber = blockNbr
			}

			blockDiff, err := strconv.ParseInt(strings.Replace(block.Result["difficulty"].(string), "0x", "", -1), 16, 64)
			if err == nil {
				pendingBlockDifficulty = big.NewInt(blockDiff)
				// logInfo.Println("Pending block difficulty:", pendingBlockDifficulty)
			}
		}
		time.Sleep(time.Millisecond * 100)
	}
}

func callArray(method string, params []interface{}) (*ResponseArray, error) {
	url := "http://localhost:" + ethereumPort
	jsonReq := &Request{
		Id:      1,
		Jsonrpc: "2.0",
		Method:  method,
		Params:  params,
	}
	reqJSON, _ := json.Marshal(jsonReq)
	// fmt.Println(string(reqJSON))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqJSON))

	if err != nil {
		logError.Println("Could not create POST request", err)
		return nil, errors.New("Could not create POST request")
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logError.Println("Could not send POST request to Ethereum client", err)
		return nil, errors.New("Could not send POST request to Ethereum client")
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	// fmt.Println(string(body))
	res := &ResponseArray{}

	if err := json.Unmarshal(body, res); err != nil {
		logError.Println("Ethereum client returned unexpected data", err)
		return nil, errors.New("Ethereum client returned unexpected data")
	}

	// fmt.Println("done")
	return res, nil
}

func callBool(method string, params []interface{}) (*ResponseBool, error) {
	url := "http://localhost:" + ethereumPort
	jsonReq := &Request{
		Id:      1,
		Jsonrpc: "2.0",
		Method:  method,
		Params:  params,
	}
	reqJSON, _ := json.Marshal(jsonReq)
	// fmt.Println(string(reqJSON))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqJSON))

	if err != nil {
		logError.Println("Could not create POST request", err)
		return nil, errors.New("Could not create POST request")
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logError.Println("Could not send POST request to Ethereum client", err)
		return nil, errors.New("Could not send POST request to Ethereum client")
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	// fmt.Println(string(body))
	res := &ResponseBool{}

	if err := json.Unmarshal(body, res); err != nil {
		logError.Println("Ethereum client returned unexpected data", err)
		return nil, errors.New("Ethereum client returned unexpected data")
	}

	// fmt.Println("done")
	return res, nil
}

func callJSON(method string, params []interface{}) (*ResponseJSON, error) {
	url := "http://localhost:" + ethereumPort
	jsonReq := &Request{
		Id:      1,
		Jsonrpc: "2.0",
		Method:  method,
		Params:  params,
	}
	reqJSON, _ := json.Marshal(jsonReq)
	// fmt.Println(string(reqJSON))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqJSON))

	if err != nil {
		logError.Println("Could not create POST request", err)
		return nil, errors.New("Could not create POST request")
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logError.Println("Could not send POST request to Ethereum client", err)
		return nil, errors.New("Could not send POST request to Ethereum client")
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	// fmt.Println(string(body))
	res := &ResponseJSON{}

	if err := json.Unmarshal(body, res); err != nil {
		logError.Println("Ethereum client returned unexpected data", err)
		return nil, errors.New("Ethereum client returned unexpected data")
	}

	// fmt.Println("done")
	return res, nil
}

func getErrorResponse(errorMsg string) string {
	return `{
    "id":64,
    "jsonrpc": "2.0",
    "result": false,
    "error": "` + errorMsg + `"
  }`
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
<commit_msg>defer close mysql<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

       _ "github.com/go-sql-driver/mysql"
        "database/sql"

	"github.com/ethereum/ethash"
	"github.com/ethereum/go-ethereum/common"

	"github.com/gorilla/mux"

)

var currWork *ResponseArray = nil

var pendingBlockNumber uint64 = 0
var pendingBlockDifficulty *big.Int

var invalidRequest = `{
  "id":64,
  "jsonrpc": "2.0",
  "result": false,
  "error": "invalid request"
}`

var okRequest = `{
  "id":64,
  "jsonrpc": "2.0",
  "result": true
}`

var pow256 = common.BigPow(2, 256)

var hasher = ethash.New()

var poolPort = "5082"
var ethereumPort = "8545" //8545 = geth, 8080 = eth (requires dev branch when using eth client)

var logInfo *log.Logger
var logError *log.Logger

type ResponseArray struct {
	Id      int           `json:"id"`
	Jsonrpc string        `json:"jsonrpc"`
	Result  []interface{} `json:"result"`
}

type ResponseJSON struct {
	Id      int                    `json:"id"`
	Jsonrpc string                 `json:"jsonrpc"`
	Result  map[string]interface{} `json:"result"`
}

type ResponseBool struct {
	Id      int    `json:"id"`
	Jsonrpc string `json:"jsonrpc"`
	Result  bool   `json:"result"`
}

type Request struct {
	Id      int           `json:"id"`
	Jsonrpc string        `json:"jsonrpc"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
}

type block struct {
	difficulty  *big.Int
	hashNoNonce common.Hash
	nonce       uint64
	mixDigest   common.Hash
	number      uint64
}

func (b block) Difficulty() *big.Int     { return b.difficulty }
func (b block) HashNoNonce() common.Hash { return b.hashNoNonce }
func (b block) Nonce() uint64            { return b.nonce }
func (b block) MixDigest() common.Hash   { return b.mixDigest }
func (b block) NumberU64() uint64        { return b.number }

func main() {
	// Set up logging
	logInfo = log.New(os.Stderr, "INFO: ", log.Ldate|log.Ltime)
	logError = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime)
	logInfo.Println("Welcome to ethpool 2.0")
	logInfo.Println("Pool port is", poolPort)
	logInfo.Println("Point your miners to: http://<ip>:" + poolPort + "/{miner}/{difficulty}")

	go updateWork()
	go updatePendingBlock()

	r := mux.NewRouter()
	r.HandleFunc("/{miner}/{difficulty}", handleMiner)
	http.Handle("/", r)
	log.Fatal(http.ListenAndServe(":5082", nil))
}

func handleMiner(rw http.ResponseWriter, req *http.Request) {

	vars := mux.Vars(req)

        if vars["difficulty"] == "0" {
            vars["difficulty"] = "5"
        }

	minerDifficulty, err := strconv.ParseFloat(vars["difficulty"], 64)
	if err != nil {
		logError.Println("Invalid difficulty provided: " + vars["difficulty"])
		minerDifficulty = 5 // Set a fixed difficulty (5MH/s) in this case
		// fmt.Fprint(rw, getErrorResponse("Invalid difficulty provided: "+vars["difficulty"]))
		// return
	}
	minerAdjustedDifficulty := int64(minerDifficulty * 1000000 * 60)

	miner := vars["miner"]
	worker := "default"

	decoder := json.NewDecoder(req.Body)
	var t Request
	err = decoder.Decode(&t)
	if err != nil {
		logError.Println("Invalid JSON request: ", err)
		fmt.Fprint(rw, getErrorResponse("Invalid JSON request"))
		return
	}

	if t.Method == "eth_getWork" {
		difficulty := big.NewInt(minerAdjustedDifficulty)
		// Send the response
		fmt.Fprint(rw, getWorkPackage(difficulty))
	} else if t.Method == "eth_submitHashrate" {
		fmt.Fprint(rw, okRequest)
	} else if t.Method == "eth_submitWork" {
		paramsOrig := t.Params[:]

		hashNoNonce := t.Params[1].(string)
		nonce, err := strconv.ParseUint(strings.Replace(t.Params[0].(string), "0x", "", -1), 16, 64)
		if err != nil {
			logError.Println("Invalid nonce provided: ", err)
			fmt.Fprint(rw, getErrorResponse("Invalid nonce provided"))
			return
		}

		mixDigest := t.Params[2].(string)

		myBlock := block{
			number:      pendingBlockNumber,
			hashNoNonce: common.HexToHash(hashNoNonce),
			difficulty:  big.NewInt(minerAdjustedDifficulty),
			nonce:       nonce,
			mixDigest:   common.HexToHash(mixDigest),
		}

		myBlockRealDiff := block{
			number:      pendingBlockNumber,
			hashNoNonce: common.HexToHash(hashNoNonce),
			difficulty:  pendingBlockDifficulty,
			nonce:       nonce,
			mixDigest:   common.HexToHash(mixDigest),
		}

    oures := "Y"
    upres := "N"

		if hasher.Verify(myBlock) {
			//fmt.Println("Share is valid")
			if hasher.Verify(myBlockRealDiff) {
				submitWork(paramsOrig)
				logInfo.Println(" -=###########################################################################=-")
				logInfo.Println("--=############################# BLOCK FOUND !!! #############################=--")
				logInfo.Println(" -=###########################################################################=-")
    upres = "Y"
			}

			logInfo.Println("Miner", miner, ".", worker, "found valid share for Block: ", myBlock.number, " (Diff:", minerAdjustedDifficulty, "WantDiff:", minerDifficulty, "Mix:", mixDigest, "Hash:", hashNoNonce, "Nonce:", nonce, ")")
		} else {
			logError.Println("Miner", miner, "provided invalid share")
			fmt.Fprint(rw, getErrorResponse("Provided PoW solution is invalid!"))
    oures = "N"
		}

    remoteipa := strings.Split(req.RemoteAddr, ":")
    remoteip := remoteipa[0]

    mysqldiff := minerDifficulty * 4

    db, err := sql.Open("mysql", "ethdb_user:ethdb_pass@/ethdb?charset=utf8")  // of course you have to enter your credentials here !
    checkErr(err)
    defer db.Close()

    stmt, err := db.Prepare("INSERT INTO shares (time, rem_host, username, our_result, upstream_result, difficulty, reason, solution) VALUES ( NOW(), ?, ?, ?, ?, ?, NULL, ?) ")
    checkErr(err)

    mysqlmixDigest := strings.Replace(mixDigest, "0x", "", -1)
    res, err := stmt.Exec(remoteip, miner, oures, upres, mysqldiff, strings.ToLower(mysqlmixDigest))
    checkErr(err)

    id, err := res.LastInsertId()
    checkErr(err)

    logInfo.Println(id)

	// We've found a Block, we have to insert it

	if (upres == "Y") {

		stmt2, err2 := db.Prepare("INSERT INTO blocks (time, height, blockhash, confirmations, accounted) VALUES ( UNIX_TIMESTAMP(NOW()), ?, ?, '0', '0')")
		checkErr(err2)

		res2, err2 := stmt2.Exec(myBlock.number, strings.ToLower(mysqlmixDigest))
		checkErr(err2)

		id2, err2 := res2.LastInsertId()
		checkErr(err2)

		logInfo.Println(id2)

	}

		fmt.Fprint(rw, okRequest)
	} else {
		logError.Println("Method " + t.Method + " not implemented!")
		fmt.Fprint(rw, getErrorResponse("Method "+t.Method+" not implemented!"))
	}
}

func getWorkPackage(difficulty *big.Int) string {

	if currWork == nil {
		return getErrorResponse("Current work unavailable")
	}

	// Our response object
	response := &ResponseArray{
		Id:      currWork.Id,
		Jsonrpc: currWork.Jsonrpc,
		Result:  currWork.Result[:],
	}

	// Calculte requested difficulty
	diff := new(big.Int).Div(pow256, difficulty)
	diffBytes := string(common.ToHex(diff.Bytes()))

	// Adjust the difficulty for the miner
	response.Result[2] = diffBytes

	// Convert respone object to JSON
	b, _ := json.Marshal(response)

	return string(b)

}

func updateWork() {
	for true {
		currWorkNew, err := callArray("eth_getWork", []interface{}{})

		if err == nil {
			currWork = currWorkNew
		} else {
			currWork = nil
		}

		// fmt.Println("Current work", currWork.Result[0])
		time.Sleep(time.Millisecond * 100)
	}
}

func submitWork(params []interface{}) {
	result, err := callBool("eth_submitWork", params)
	if err == nil {
		logInfo.Println(result.Result)
	}
}

func updatePendingBlock() {
	params := []interface{}{"pending", false}

	for true {
		block, err := callJSON("eth_getBlockByNumber", params)
		if err == nil {
			blockNbr, err := strconv.ParseUint(strings.Replace(block.Result["number"].(string), "0x", "", -1), 16, 64)
			if err == nil {
				pendingBlockNumber = blockNbr
			}

			blockDiff, err := strconv.ParseInt(strings.Replace(block.Result["difficulty"].(string), "0x", "", -1), 16, 64)
			if err == nil {
				pendingBlockDifficulty = big.NewInt(blockDiff)
				// logInfo.Println("Pending block difficulty:", pendingBlockDifficulty)
			}
		}
		time.Sleep(time.Millisecond * 100)
	}
}

func callArray(method string, params []interface{}) (*ResponseArray, error) {
	url := "http://localhost:" + ethereumPort
	jsonReq := &Request{
		Id:      1,
		Jsonrpc: "2.0",
		Method:  method,
		Params:  params,
	}
	reqJSON, _ := json.Marshal(jsonReq)
	// fmt.Println(string(reqJSON))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqJSON))

	if err != nil {
		logError.Println("Could not create POST request", err)
		return nil, errors.New("Could not create POST request")
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logError.Println("Could not send POST request to Ethereum client", err)
		return nil, errors.New("Could not send POST request to Ethereum client")
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	// fmt.Println(string(body))
	res := &ResponseArray{}

	if err := json.Unmarshal(body, res); err != nil {
		logError.Println("Ethereum client returned unexpected data", err)
		return nil, errors.New("Ethereum client returned unexpected data")
	}

	// fmt.Println("done")
	return res, nil
}

func callBool(method string, params []interface{}) (*ResponseBool, error) {
	url := "http://localhost:" + ethereumPort
	jsonReq := &Request{
		Id:      1,
		Jsonrpc: "2.0",
		Method:  method,
		Params:  params,
	}
	reqJSON, _ := json.Marshal(jsonReq)
	// fmt.Println(string(reqJSON))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqJSON))

	if err != nil {
		logError.Println("Could not create POST request", err)
		return nil, errors.New("Could not create POST request")
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logError.Println("Could not send POST request to Ethereum client", err)
		return nil, errors.New("Could not send POST request to Ethereum client")
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	// fmt.Println(string(body))
	res := &ResponseBool{}

	if err := json.Unmarshal(body, res); err != nil {
		logError.Println("Ethereum client returned unexpected data", err)
		return nil, errors.New("Ethereum client returned unexpected data")
	}

	// fmt.Println("done")
	return res, nil
}

func callJSON(method string, params []interface{}) (*ResponseJSON, error) {
	url := "http://localhost:" + ethereumPort
	jsonReq := &Request{
		Id:      1,
		Jsonrpc: "2.0",
		Method:  method,
		Params:  params,
	}
	reqJSON, _ := json.Marshal(jsonReq)
	// fmt.Println(string(reqJSON))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqJSON))

	if err != nil {
		logError.Println("Could not create POST request", err)
		return nil, errors.New("Could not create POST request")
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logError.Println("Could not send POST request to Ethereum client", err)
		return nil, errors.New("Could not send POST request to Ethereum client")
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	// fmt.Println(string(body))
	res := &ResponseJSON{}

	if err := json.Unmarshal(body, res); err != nil {
		logError.Println("Ethereum client returned unexpected data", err)
		return nil, errors.New("Ethereum client returned unexpected data")
	}

	// fmt.Println("done")
	return res, nil
}

func getErrorResponse(errorMsg string) string {
	return `{
    "id":64,
    "jsonrpc": "2.0",
    "result": false,
    "error": "` + errorMsg + `"
  }`
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
<|endoftext|>


<commit_before>//  Copyright Istio Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package kube

import (
	"github.com/pkg/errors"
	kubeApiAdmissionv1 "k8s.io/api/admission/v1"
	kubeApiAdmissionv1beta1 "k8s.io/api/admission/v1beta1"
	authenticationv1 "k8s.io/api/authentication/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
)

const (
	// APIVersion constants
	admissionAPIV1      = "admission.k8s.io/v1"
	admissionAPIV1beta1 = "admission.k8s.io/v1beta1"

	// Operation constants
	Create  string = "CREATE"
	Update  string = "UPDATE"
	Delete  string = "DELETE"
	Connect string = "CONNECT"
)

// AdmissionReview describes an admission review request/response.
type AdmissionReview struct {
	// TypeMeta describes an individual object in an API response or request
	// with strings representing the type of the object and its API schema version.
	// Structures that are versioned or persisted should inline TypeMeta.
	metav1.TypeMeta `json:",inline"`

	// Request describes the attributes for the admission request.
	Request *AdmissionRequest `json:"request,omitempty"`

	// Response describes the attributes for the admission response.
	Response *AdmissionResponse `json:"response,omitempty"`
}

// AdmissionRequest describes the admission.Attributes for the admission request.
type AdmissionRequest struct {

	// UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
	// otherwise identical (parallel requests, requests when earlier requests did not modify etc)
	// The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
	// It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
	UID types.UID `json:"uid"`

	// Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale)
	Kind metav1.GroupVersionKind `json:"kind"`

	// Resource is the fully-qualified resource being requested (for example, v1.pods)
	Resource metav1.GroupVersionResource `json:"resource"`

	// SubResource is the subresource being requested, if any (for example, "status" or "scale")
	SubResource string `json:"subResource,omitempty"`
	// RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
	// If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
	//
	// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
	// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
	// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
	// with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
	// and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
	//
	RequestKind *metav1.GroupVersionKind `json:"requestKind,omitempty"`

	// RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
	// If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
	//
	// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
	// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
	// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
	// with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
	// and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
	//
	RequestResource *metav1.GroupVersionResource `json:"requestResource,omitempty"`

	// RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
	// If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
	RequestSubResource string `json:"requestSubResource,omitempty"`

	// UserInfo is information about the requesting user
	UserInfo authenticationv1.UserInfo `json:"userInfo"`

	// Name is the name of the object as presented in the request.  On a CREATE operation, the client may omit name and
	// rely on the server to generate the name.  If that is the case, this field will contain an empty string.
	Name string `json:"name,omitempty"`

	// Namespace is the namespace associated with the request (if any).
	Namespace string `json:"namespace,omitempty"`

	// Operation is the operation being performed. This may be different than the operation
	// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
	Operation string `json:"operation"`

	// Object is the object from the incoming request.
	Object runtime.RawExtension `json:"object,omitempty"`

	// OldObject is the existing object. Only populated for DELETE and UPDATE requests.
	OldObject runtime.RawExtension `json:"oldObject,omitempty"`

	// DryRun indicates that modifications will definitely not be persisted for this request.
	// Defaults to false.
	DryRun *bool `json:"dryRun,omitempty"`

	// Options is the operation option structure of the operation being performed.
	// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
	// different than the options the caller provided. e.g. for a patch request the performed
	// Operation might be a CREATE, in which case the Options will a
	// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
	Options runtime.RawExtension `json:"options,omitempty"`
}

// AdmissionResponse describes an admission response.
type AdmissionResponse struct {

	// UID is an identifier for the individual request/response.
	// This should be copied over from the corresponding AdmissionRequest.
	UID types.UID `json:"uid"`

	// Allowed indicates whether or not the admission request was permitted.
	Allowed bool `json:"allowed"`

	// Result contains extra details into why an admission request was denied.
	// This field IS NOT consulted in any way if "Allowed" is "true".
	Result *metav1.Status `json:"status,omitempty"`

	// The patch body. Currently we only support "JSONPatch" which implements RFC 6902.
	Patch []byte `json:"patch,omitempty"`

	// The type of Patch. Currently we only allow "JSONPatch".
	PatchType *string `json:"patchType,omitempty"`

	// AuditAnnotations is an unstructured key value map set by remote admission controller (e.g. error=image-blacklisted).
	// MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controller will prefix the keys with
	// admission webhook name (e.g. imagepolicy.example.com/error=image-blacklisted). AuditAnnotations will be provided by
	// the admission webhook to add additional context to the audit log for this request.
	AuditAnnotations map[string]string `json:"auditAnnotations,omitempty"`
}

func AdmissionReviewKubeToAdapter(object runtime.Object) (*AdmissionReview, error) {
	var typeMeta metav1.TypeMeta
	var req *AdmissionRequest
	var resp *AdmissionResponse
	switch obj := object.(type) {
	case *kubeApiAdmissionv1beta1.AdmissionReview:
		typeMeta = obj.TypeMeta
		arv1beta1Response := obj.Response
		arv1beta1Request := obj.Request
		if arv1beta1Response != nil {
			resp = &AdmissionResponse{
				UID:     arv1beta1Response.UID,
				Allowed: arv1beta1Response.Allowed,
				Result:  arv1beta1Response.Result,
				Patch:   arv1beta1Response.Patch,
			}
			if arv1beta1Response.PatchType != nil {
				patchType := string(*arv1beta1Response.PatchType)
				resp.PatchType = &patchType
			}
		}
		if arv1beta1Request != nil {
			req = &AdmissionRequest{
				UID:       arv1beta1Request.UID,
				Kind:      arv1beta1Request.Kind,
				Resource:  arv1beta1Request.Resource,
				UserInfo:  arv1beta1Request.UserInfo,
				Name:      arv1beta1Request.Name,
				Namespace: arv1beta1Request.Namespace,
				Operation: string(arv1beta1Request.Operation),
				Object:    arv1beta1Request.Object,
				OldObject: arv1beta1Request.OldObject,
			}
		}

	case *kubeApiAdmissionv1.AdmissionReview:
		typeMeta = obj.TypeMeta
		arv1Response := obj.Response
		arv1Request := obj.Request
		if arv1Response != nil {
			resp = &AdmissionResponse{
				UID:     arv1Response.UID,
				Allowed: arv1Response.Allowed,
				Result:  arv1Response.Result,
				Patch:   arv1Response.Patch,
			}
			if arv1Response.PatchType != nil {
				patchType := string(*arv1Response.PatchType)
				resp.PatchType = &patchType
			}
		}

		if arv1Request != nil {
			req = &AdmissionRequest{
				UID:       arv1Request.UID,
				Kind:      arv1Request.Kind,
				Resource:  arv1Request.Resource,
				UserInfo:  arv1Request.UserInfo,
				Name:      arv1Request.Name,
				Namespace: arv1Request.Namespace,
				Operation: string(arv1Request.Operation),
				Object:    arv1Request.Object,
				OldObject: arv1Request.OldObject,
			}
		}

	default:
		return nil, errors.Errorf("unsupported type :%v", object.GetObjectKind())
	}

	return &AdmissionReview{
		TypeMeta: typeMeta,
		Request:  req,
		Response: resp,
	}, nil
}

func AdmissionReviewAdapterToKube(ar *AdmissionReview, apiVersion string) runtime.Object {
	var res runtime.Object
	arRequest := ar.Request
	arResponse := ar.Response
	if apiVersion == "" {
		apiVersion = admissionAPIV1beta1
	}
	switch apiVersion {
	case admissionAPIV1beta1:
		arv1beta1 := kubeApiAdmissionv1beta1.AdmissionReview{}
		if arRequest != nil {
			arv1beta1.Request = &kubeApiAdmissionv1beta1.AdmissionRequest{
				UID:                arRequest.UID,
				Kind:               arRequest.Kind,
				Resource:           arRequest.Resource,
				SubResource:        arRequest.SubResource,
				Name:               arRequest.Name,
				Namespace:          arRequest.Namespace,
				RequestKind:        arRequest.RequestKind,
				RequestResource:    arRequest.RequestResource,
				RequestSubResource: arRequest.RequestSubResource,
				Operation:          kubeApiAdmissionv1beta1.Operation(arRequest.Operation),
				UserInfo:           arRequest.UserInfo,
				Object:             arRequest.Object,
				OldObject:          arRequest.OldObject,
				DryRun:             arRequest.DryRun,
				Options:            arRequest.Options,
			}
		}
		if arResponse != nil {
			var patchType kubeApiAdmissionv1beta1.PatchType
			if arResponse.PatchType != nil {
				patchType = kubeApiAdmissionv1beta1.PatchType(*arResponse.PatchType)
			}
			arv1beta1.Response = &kubeApiAdmissionv1beta1.AdmissionResponse{
				UID:              arResponse.UID,
				Allowed:          arResponse.Allowed,
				Result:           arResponse.Result,
				Patch:            arResponse.Patch,
				PatchType:        &patchType,
				AuditAnnotations: arResponse.AuditAnnotations,
			}
		}
		arv1beta1.TypeMeta = ar.TypeMeta
		res = &arv1beta1
	case admissionAPIV1:
		arv1 := kubeApiAdmissionv1.AdmissionReview{}
		if arRequest != nil {
			arv1.Request = &kubeApiAdmissionv1.AdmissionRequest{
				UID:                arRequest.UID,
				Kind:               arRequest.Kind,
				Resource:           arRequest.Resource,
				SubResource:        arRequest.SubResource,
				Name:               arRequest.Name,
				Namespace:          arRequest.Namespace,
				RequestKind:        arRequest.RequestKind,
				RequestResource:    arRequest.RequestResource,
				RequestSubResource: arRequest.RequestSubResource,
				Operation:          kubeApiAdmissionv1.Operation(arRequest.Operation),
				UserInfo:           arRequest.UserInfo,
				Object:             arRequest.Object,
				OldObject:          arRequest.OldObject,
				DryRun:             arRequest.DryRun,
				Options:            arRequest.Options,
			}
		}
		if arResponse != nil {
			var patchType kubeApiAdmissionv1.PatchType
			if arResponse.PatchType != nil {
				patchType = kubeApiAdmissionv1.PatchType(*arResponse.PatchType)
			}
			arv1.Response = &kubeApiAdmissionv1.AdmissionResponse{
				UID:              arResponse.UID,
				Allowed:          arResponse.Allowed,
				Result:           arResponse.Result,
				Patch:            arResponse.Patch,
				PatchType:        &patchType,
				AuditAnnotations: arResponse.AuditAnnotations,
			}
		}
		arv1.TypeMeta = ar.TypeMeta
		res = &arv1
	}
	return res
}
<commit_msg>Admission webhook fix (#26880)<commit_after>//  Copyright Istio Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package kube

import (
	"github.com/pkg/errors"
	kubeApiAdmissionv1 "k8s.io/api/admission/v1"
	kubeApiAdmissionv1beta1 "k8s.io/api/admission/v1beta1"
	authenticationv1 "k8s.io/api/authentication/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
)

const (
	// APIVersion constants
	admissionAPIV1      = "admission.k8s.io/v1"
	admissionAPIV1beta1 = "admission.k8s.io/v1beta1"

	// Operation constants
	Create  string = "CREATE"
	Update  string = "UPDATE"
	Delete  string = "DELETE"
	Connect string = "CONNECT"
)

// AdmissionReview describes an admission review request/response.
type AdmissionReview struct {
	// TypeMeta describes an individual object in an API response or request
	// with strings representing the type of the object and its API schema version.
	// Structures that are versioned or persisted should inline TypeMeta.
	metav1.TypeMeta `json:",inline"`

	// Request describes the attributes for the admission request.
	Request *AdmissionRequest `json:"request,omitempty"`

	// Response describes the attributes for the admission response.
	Response *AdmissionResponse `json:"response,omitempty"`
}

// AdmissionRequest describes the admission.Attributes for the admission request.
type AdmissionRequest struct {

	// UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
	// otherwise identical (parallel requests, requests when earlier requests did not modify etc)
	// The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
	// It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
	UID types.UID `json:"uid"`

	// Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale)
	Kind metav1.GroupVersionKind `json:"kind"`

	// Resource is the fully-qualified resource being requested (for example, v1.pods)
	Resource metav1.GroupVersionResource `json:"resource"`

	// SubResource is the subresource being requested, if any (for example, "status" or "scale")
	SubResource string `json:"subResource,omitempty"`
	// RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
	// If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
	//
	// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
	// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
	// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
	// with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
	// and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
	//
	RequestKind *metav1.GroupVersionKind `json:"requestKind,omitempty"`

	// RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
	// If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
	//
	// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
	// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
	// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
	// with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
	// and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
	//
	RequestResource *metav1.GroupVersionResource `json:"requestResource,omitempty"`

	// RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
	// If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
	RequestSubResource string `json:"requestSubResource,omitempty"`

	// UserInfo is information about the requesting user
	UserInfo authenticationv1.UserInfo `json:"userInfo"`

	// Name is the name of the object as presented in the request.  On a CREATE operation, the client may omit name and
	// rely on the server to generate the name.  If that is the case, this field will contain an empty string.
	Name string `json:"name,omitempty"`

	// Namespace is the namespace associated with the request (if any).
	Namespace string `json:"namespace,omitempty"`

	// Operation is the operation being performed. This may be different than the operation
	// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
	Operation string `json:"operation"`

	// Object is the object from the incoming request.
	Object runtime.RawExtension `json:"object,omitempty"`

	// OldObject is the existing object. Only populated for DELETE and UPDATE requests.
	OldObject runtime.RawExtension `json:"oldObject,omitempty"`

	// DryRun indicates that modifications will definitely not be persisted for this request.
	// Defaults to false.
	DryRun *bool `json:"dryRun,omitempty"`

	// Options is the operation option structure of the operation being performed.
	// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
	// different than the options the caller provided. e.g. for a patch request the performed
	// Operation might be a CREATE, in which case the Options will a
	// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
	Options runtime.RawExtension `json:"options,omitempty"`
}

// AdmissionResponse describes an admission response.
type AdmissionResponse struct {

	// UID is an identifier for the individual request/response.
	// This should be copied over from the corresponding AdmissionRequest.
	UID types.UID `json:"uid"`

	// Allowed indicates whether or not the admission request was permitted.
	Allowed bool `json:"allowed"`

	// Result contains extra details into why an admission request was denied.
	// This field IS NOT consulted in any way if "Allowed" is "true".
	Result *metav1.Status `json:"status,omitempty"`

	// The patch body. Currently we only support "JSONPatch" which implements RFC 6902.
	Patch []byte `json:"patch,omitempty"`

	// The type of Patch. Currently we only allow "JSONPatch".
	PatchType *string `json:"patchType,omitempty"`

	// AuditAnnotations is an unstructured key value map set by remote admission controller (e.g. error=image-blacklisted).
	// MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controller will prefix the keys with
	// admission webhook name (e.g. imagepolicy.example.com/error=image-blacklisted). AuditAnnotations will be provided by
	// the admission webhook to add additional context to the audit log for this request.
	AuditAnnotations map[string]string `json:"auditAnnotations,omitempty"`
}

func AdmissionReviewKubeToAdapter(object runtime.Object) (*AdmissionReview, error) {
	var typeMeta metav1.TypeMeta
	var req *AdmissionRequest
	var resp *AdmissionResponse
	switch obj := object.(type) {
	case *kubeApiAdmissionv1beta1.AdmissionReview:
		typeMeta = obj.TypeMeta
		arv1beta1Response := obj.Response
		arv1beta1Request := obj.Request
		if arv1beta1Response != nil {
			resp = &AdmissionResponse{
				UID:     arv1beta1Response.UID,
				Allowed: arv1beta1Response.Allowed,
				Result:  arv1beta1Response.Result,
				Patch:   arv1beta1Response.Patch,
			}
			if arv1beta1Response.PatchType != nil {
				patchType := string(*arv1beta1Response.PatchType)
				resp.PatchType = &patchType
			}
		}
		if arv1beta1Request != nil {
			req = &AdmissionRequest{
				UID:       arv1beta1Request.UID,
				Kind:      arv1beta1Request.Kind,
				Resource:  arv1beta1Request.Resource,
				UserInfo:  arv1beta1Request.UserInfo,
				Name:      arv1beta1Request.Name,
				Namespace: arv1beta1Request.Namespace,
				Operation: string(arv1beta1Request.Operation),
				Object:    arv1beta1Request.Object,
				OldObject: arv1beta1Request.OldObject,
			}
		}

	case *kubeApiAdmissionv1.AdmissionReview:
		typeMeta = obj.TypeMeta
		arv1Response := obj.Response
		arv1Request := obj.Request
		if arv1Response != nil {
			resp = &AdmissionResponse{
				UID:     arv1Response.UID,
				Allowed: arv1Response.Allowed,
				Result:  arv1Response.Result,
				Patch:   arv1Response.Patch,
			}
			if arv1Response.PatchType != nil {
				patchType := string(*arv1Response.PatchType)
				resp.PatchType = &patchType
			}
		}

		if arv1Request != nil {
			req = &AdmissionRequest{
				UID:       arv1Request.UID,
				Kind:      arv1Request.Kind,
				Resource:  arv1Request.Resource,
				UserInfo:  arv1Request.UserInfo,
				Name:      arv1Request.Name,
				Namespace: arv1Request.Namespace,
				Operation: string(arv1Request.Operation),
				Object:    arv1Request.Object,
				OldObject: arv1Request.OldObject,
			}
		}

	default:
		return nil, errors.Errorf("unsupported type :%v", object.GetObjectKind())
	}

	return &AdmissionReview{
		TypeMeta: typeMeta,
		Request:  req,
		Response: resp,
	}, nil
}

func AdmissionReviewAdapterToKube(ar *AdmissionReview, apiVersion string) runtime.Object {
	var res runtime.Object
	arRequest := ar.Request
	arResponse := ar.Response
	if apiVersion == "" {
		apiVersion = admissionAPIV1beta1
	}
	switch apiVersion {
	case admissionAPIV1beta1:
		arv1beta1 := kubeApiAdmissionv1beta1.AdmissionReview{}
		if arRequest != nil {
			arv1beta1.Request = &kubeApiAdmissionv1beta1.AdmissionRequest{
				UID:                arRequest.UID,
				Kind:               arRequest.Kind,
				Resource:           arRequest.Resource,
				SubResource:        arRequest.SubResource,
				Name:               arRequest.Name,
				Namespace:          arRequest.Namespace,
				RequestKind:        arRequest.RequestKind,
				RequestResource:    arRequest.RequestResource,
				RequestSubResource: arRequest.RequestSubResource,
				Operation:          kubeApiAdmissionv1beta1.Operation(arRequest.Operation),
				UserInfo:           arRequest.UserInfo,
				Object:             arRequest.Object,
				OldObject:          arRequest.OldObject,
				DryRun:             arRequest.DryRun,
				Options:            arRequest.Options,
			}
		}
		if arResponse != nil {
			var patchType *kubeApiAdmissionv1beta1.PatchType
			if arResponse.PatchType != nil {
				patchType = (*kubeApiAdmissionv1beta1.PatchType)(arResponse.PatchType)
			}
			arv1beta1.Response = &kubeApiAdmissionv1beta1.AdmissionResponse{
				UID:              arResponse.UID,
				Allowed:          arResponse.Allowed,
				Result:           arResponse.Result,
				Patch:            arResponse.Patch,
				PatchType:        patchType,
				AuditAnnotations: arResponse.AuditAnnotations,
			}
		}
		arv1beta1.TypeMeta = ar.TypeMeta
		res = &arv1beta1
	case admissionAPIV1:
		arv1 := kubeApiAdmissionv1.AdmissionReview{}
		if arRequest != nil {
			arv1.Request = &kubeApiAdmissionv1.AdmissionRequest{
				UID:                arRequest.UID,
				Kind:               arRequest.Kind,
				Resource:           arRequest.Resource,
				SubResource:        arRequest.SubResource,
				Name:               arRequest.Name,
				Namespace:          arRequest.Namespace,
				RequestKind:        arRequest.RequestKind,
				RequestResource:    arRequest.RequestResource,
				RequestSubResource: arRequest.RequestSubResource,
				Operation:          kubeApiAdmissionv1.Operation(arRequest.Operation),
				UserInfo:           arRequest.UserInfo,
				Object:             arRequest.Object,
				OldObject:          arRequest.OldObject,
				DryRun:             arRequest.DryRun,
				Options:            arRequest.Options,
			}
		}
		if arResponse != nil {
			var patchType *kubeApiAdmissionv1.PatchType
			if arResponse.PatchType != nil {
				patchType = (*kubeApiAdmissionv1.PatchType)(arResponse.PatchType)
			}
			arv1.Response = &kubeApiAdmissionv1.AdmissionResponse{
				UID:              arResponse.UID,
				Allowed:          arResponse.Allowed,
				Result:           arResponse.Result,
				Patch:            arResponse.Patch,
				PatchType:        patchType,
				AuditAnnotations: arResponse.AuditAnnotations,
			}
		}
		arv1.TypeMeta = ar.TypeMeta
		res = &arv1
	}
	return res
}
<|endoftext|>