<commit_before>package termui

import (
	gtermui "github.com/gizak/termui"
)

//Row is a widget for table rows
type Row struct {
	X, Y    int
	Width   int
	Height  int
	Columns []gtermui.GridBufferer
	Table   Table
}

//AddColumn adds the given column to this row
func (row *Row) AddColumn(c gtermui.GridBufferer) {
	row.Columns = append(row.Columns, c)
}

//GetHeight returns this Row heigth
func (row *Row) GetHeight() int {
	return row.Height
}

//SetX sets the x position of this Row
func (row *Row) SetX(x int) {
	row.X = x
}

//SetY sets the y position of this Row
func (row *Row) SetY(y int) {
	if y == row.Y {
		return
	}
	for _, col := range row.Columns {
		col.SetY(y)
	}
	row.Y = y
}

//SetWidth sets the width of this Row
func (row *Row) SetWidth(width int) {
	x := row.X
	//Setting the width of a row does very little, since
	//column widths come from the table
	//It might be worthy to add checks in the case of a width
	//being set that is different than the sum of row.Table.ColumnWidths()
	if row.Table != nil {
		for i, width := range row.Table.ColumnWidths() {
			col := row.Columns[i]
			col.SetX(x)
			col.SetWidth(width)
			x += width + DefaultColumnSpacing
		}
	}
	row.Width = width
}

//Buffer returns this Row data as a gtermui.Buffer
func (row *Row) Buffer() gtermui.Buffer {
	buf := gtermui.NewBuffer()
	for _, col := range row.Columns {
		buf.Merge(col.Buffer())
	}
	return buf
}
<commit_msg>Set columns width from row width if there is no table<commit_after>package termui

import (
	gtermui "github.com/gizak/termui"
)

//Row is a widget for table rows
type Row struct {
	X, Y    int
	Width   int
	Height  int
	Columns []gtermui.GridBufferer
	Table   Table
}

//AddColumn adds the given column to this row
func (row *Row) AddColumn(c gtermui.GridBufferer) {
	row.Columns = append(row.Columns, c)
}

//GetHeight returns this Row heigth
func (row *Row) GetHeight() int {
	return row.Height
}

//SetX sets the x position of this Row
func (row *Row) SetX(x int) {
	row.X = x
}

//SetY sets the y position of this Row
func (row *Row) SetY(y int) {
	if y == row.Y {
		return
	}
	for _, col := range row.Columns {
		col.SetY(y)
	}
	row.Y = y
}

//SetWidth sets the width of this Row
func (row *Row) SetWidth(width int) {
	x := row.X
	//Setting the width of a row does very little, since
	//column widths come from the table
	//It might be worthy to add checks in the case of a width
	//being set that is different than the sum of row.Table.ColumnWidths()
	if row.Table != nil {
		for i, width := range row.Table.ColumnWidths() {
			col := row.Columns[i]
			col.SetX(x)
			col.SetWidth(width)
			x += width + DefaultColumnSpacing
		}
	} else {
		for _, col := range row.Columns {
			col.SetX(x)
			col.SetWidth(width)
			x += width + DefaultColumnSpacing
		}
	}
	row.Width = width
}

//Buffer returns this Row data as a gtermui.Buffer
func (row *Row) Buffer() gtermui.Buffer {
	buf := gtermui.NewBuffer()
	for _, col := range row.Columns {
		buf.Merge(col.Buffer())
	}
	return buf
}
<|endoftext|>


<commit_before>package unused

import (
	"go/ast"
	"go/token"
	"go/types"
	"log"
	"strings"

	"golang.org/x/tools/go/loader"
)

type CheckMode int

const (
	CheckConstants CheckMode = 1 << iota
	CheckFields
	CheckFunctions
	CheckTypes
	CheckVariables
)

type Checker struct {
	Mode    CheckMode
	Fset    *token.FileSet
	Verbose bool
}

func (c *Checker) checkConstants() bool { return (c.Mode & CheckConstants) > 0 }
func (c *Checker) checkFields() bool    { return (c.Mode & CheckFields) > 0 }
func (c *Checker) checkFunctions() bool { return (c.Mode & CheckFunctions) > 0 }
func (c *Checker) checkTypes() bool     { return (c.Mode & CheckTypes) > 0 }
func (c *Checker) checkVariables() bool { return (c.Mode & CheckVariables) > 0 }

func (c *Checker) Check(paths []string) ([]types.Object, error) {
	defs := map[types.Object]bool{}
	var interfaces []*types.Interface
	var unused []types.Object

	conf := loader.Config{}
	if !c.Verbose {
		conf.TypeChecker.Error = func(error) {}
	}
	pkgs := map[string]bool{}
	for _, path := range paths {
		pkgs[path] = true
	}
	conf.TypeCheckFuncBodies = func(s string) bool {
		return pkgs[s]
	}
	for _, path := range paths {
		conf.ImportWithTests(path)
	}
	lprog, err := conf.Load()
	if err != nil {
		return nil, err
	}

	for _, path := range paths {
		pkg := lprog.Package(path)
		if pkg == nil {
			log.Println("Couldn't load package", path)
			continue
		}
		for _, obj := range pkg.Defs {
			if obj == nil {
				continue
			}
			if obj, ok := obj.(*types.Var); ok {
				if typ, ok := obj.Type().(*types.Interface); ok {
					interfaces = append(interfaces, typ)
				}
			}
			if obj, ok := obj.(*types.TypeName); ok {
				if typ, ok := obj.Type().Underlying().(*types.Interface); ok {
					interfaces = append(interfaces, typ)
				}
			}
			if isVariable(obj) && !isPkgScope(obj) && !isField(obj) {
				// Skip variables that aren't package variables or struct fields
				continue
			}
			if _, ok := obj.(*types.PkgName); ok {
				continue
			}
			defs[obj] = false
		}
		for _, obj := range pkg.Uses {
			defs[obj] = true
		}
		for _, file := range pkg.Files {
			var v visitor
			v = func(node ast.Node) ast.Visitor {
				if node, ok := node.(*ast.CompositeLit); ok {
					var obj types.Type
					if _, ok := node.Type.(*ast.StructType); ok {
						obj = pkg.TypeOf(node)
					} else {
						ident, ok := node.Type.(*ast.Ident)
						if !ok {
							return v
						}
						obj, ok = pkg.ObjectOf(ident).Type().(*types.Named)
						if !ok {
							return v
						}
					}
					typ, ok := obj.Underlying().(*types.Struct)
					if !ok {
						return v
					}
					basic := false
					for _, elt := range node.Elts {
						if _, ok := elt.(*ast.BasicLit); ok {
							basic = true
							break
						}
					}
					if basic {
						n := typ.NumFields()
						for i := 0; i < n; i++ {
							field := typ.Field(i)
							defs[field] = true
						}
					}
				}
				return v
			}
			ast.Walk(v, file)
		}
	}
	for obj, used := range defs {
		f := lprog.Fset.Position(obj.Pos()).Filename
		if strings.HasSuffix(f, "_test.go") {
			continue
		}
		if obj.Pkg() == nil {
			continue
		}
		// TODO methods + reflection
		if !c.checkFlags(obj) {
			continue
		}
		if used {
			continue
		}
		if obj.Name() == "_" {
			continue
		}
		if obj.Exported() && (isPkgScope(obj) || isMethod(obj) || isField(obj)) {
			f := lprog.Fset.Position(obj.Pos()).Filename
			if !strings.HasSuffix(f, "_test.go") || strings.HasPrefix(obj.Name(), "Test") || strings.HasPrefix(obj.Name(), "Benchmark") {
				continue
			}
		}
		if isMain(obj) {
			continue
		}
		if isFunction(obj) && !isMethod(obj) && obj.Name() == "init" {
			continue
		}
		if isMethod(obj) && implements(obj, interfaces) {
			continue
		}
		unused = append(unused, obj)
	}
	c.Fset = lprog.Fset
	return unused, nil
}

func Check(paths []string, flags CheckMode) ([]types.Object, error) {
	checker := Checker{Mode: flags}
	return checker.Check(paths)
}

func implements(obj types.Object, ifaces []*types.Interface) bool {
	recvType := obj.(*types.Func).Type().(*types.Signature).Recv().Type()
	for _, iface := range ifaces {
		if !types.Implements(recvType, iface) {
			continue
		}
		n := iface.NumMethods()
		for i := 0; i < n; i++ {
			if iface.Method(i).Name() == obj.Name() {
				return true
			}
		}
	}
	return false
}

func isPkgScope(obj types.Object) bool {
	return obj.Parent() == obj.Pkg().Scope()
}

func isMain(obj types.Object) bool {
	if obj.Pkg().Name() != "main" {
		return false
	}
	if obj.Name() != "main" {
		return false
	}
	if !isPkgScope(obj) {
		return false
	}
	if !isFunction(obj) {
		return false
	}
	if isMethod(obj) {
		return false
	}
	return true
}

func isFunction(obj types.Object) bool {
	_, ok := obj.(*types.Func)
	return ok
}

func isMethod(obj types.Object) bool {
	if !isFunction(obj) {
		return false
	}
	return obj.(*types.Func).Type().(*types.Signature).Recv() != nil
}

func isVariable(obj types.Object) bool {
	_, ok := obj.(*types.Var)
	return ok
}

func isConstant(obj types.Object) bool {
	_, ok := obj.(*types.Const)
	return ok
}

func isType(obj types.Object) bool {
	_, ok := obj.(*types.TypeName)
	return ok
}

func isField(obj types.Object) bool {
	if obj, ok := obj.(*types.Var); ok && obj.IsField() {
		return true
	}
	return false
}

func (c *Checker) checkFlags(obj types.Object) bool {
	if isFunction(obj) && !c.checkFunctions() {
		return false
	}
	if isVariable(obj) && !c.checkVariables() {
		return false
	}
	if isConstant(obj) && !c.checkConstants() {
		return false
	}
	if isType(obj) && !c.checkTypes() {
		return false
	}
	if isField(obj) && !c.checkFields() {
		return false
	}
	return true
}

type visitor func(node ast.Node) ast.Visitor

func (v visitor) Visit(node ast.Node) ast.Visitor {
	return v(node)
}
<commit_msg>Detect all struct initializers<commit_after>package unused

import (
	"go/ast"
	"go/token"
	"go/types"
	"log"
	"strings"

	"golang.org/x/tools/go/loader"
)

type CheckMode int

const (
	CheckConstants CheckMode = 1 << iota
	CheckFields
	CheckFunctions
	CheckTypes
	CheckVariables
)

type Checker struct {
	Mode    CheckMode
	Fset    *token.FileSet
	Verbose bool
}

func (c *Checker) checkConstants() bool { return (c.Mode & CheckConstants) > 0 }
func (c *Checker) checkFields() bool    { return (c.Mode & CheckFields) > 0 }
func (c *Checker) checkFunctions() bool { return (c.Mode & CheckFunctions) > 0 }
func (c *Checker) checkTypes() bool     { return (c.Mode & CheckTypes) > 0 }
func (c *Checker) checkVariables() bool { return (c.Mode & CheckVariables) > 0 }

func (c *Checker) Check(paths []string) ([]types.Object, error) {
	defs := map[types.Object]bool{}
	var interfaces []*types.Interface
	var unused []types.Object

	conf := loader.Config{}
	if !c.Verbose {
		conf.TypeChecker.Error = func(error) {}
	}
	pkgs := map[string]bool{}
	for _, path := range paths {
		pkgs[path] = true
	}
	conf.TypeCheckFuncBodies = func(s string) bool {
		return pkgs[s]
	}
	for _, path := range paths {
		conf.ImportWithTests(path)
	}
	lprog, err := conf.Load()
	if err != nil {
		return nil, err
	}

	for _, path := range paths {
		pkg := lprog.Package(path)
		if pkg == nil {
			log.Println("Couldn't load package", path)
			continue
		}
		for _, obj := range pkg.Defs {
			if obj == nil {
				continue
			}
			if obj, ok := obj.(*types.Var); ok {
				if typ, ok := obj.Type().(*types.Interface); ok {
					interfaces = append(interfaces, typ)
				}
			}
			if obj, ok := obj.(*types.TypeName); ok {
				if typ, ok := obj.Type().Underlying().(*types.Interface); ok {
					interfaces = append(interfaces, typ)
				}
			}
			if isVariable(obj) && !isPkgScope(obj) && !isField(obj) {
				// Skip variables that aren't package variables or struct fields
				continue
			}
			if _, ok := obj.(*types.PkgName); ok {
				continue
			}
			defs[obj] = false
		}
		for _, obj := range pkg.Uses {
			defs[obj] = true
		}
		for _, file := range pkg.Files {
			var v visitor
			v = func(node ast.Node) ast.Visitor {
				if node, ok := node.(*ast.CompositeLit); ok {
					var obj types.Type
					if _, ok := node.Type.(*ast.StructType); ok {
						obj = pkg.TypeOf(node)
					} else {
						ident, ok := node.Type.(*ast.Ident)
						if !ok {
							return v
						}
						obj, ok = pkg.ObjectOf(ident).Type().(*types.Named)
						if !ok {
							return v
						}
					}
					typ, ok := obj.Underlying().(*types.Struct)
					if !ok {
						return v
					}
					basic := false
					for _, elt := range node.Elts {
						if _, ok := elt.(*ast.KeyValueExpr); !ok {
							basic = true
							break
						}
					}
					if basic {
						n := typ.NumFields()
						for i := 0; i < n; i++ {
							field := typ.Field(i)
							defs[field] = true
						}
					}
				}
				return v
			}
			ast.Walk(v, file)
		}
	}
	for obj, used := range defs {
		f := lprog.Fset.Position(obj.Pos()).Filename
		if strings.HasSuffix(f, "_test.go") {
			continue
		}
		if obj.Pkg() == nil {
			continue
		}
		// TODO methods + reflection
		if !c.checkFlags(obj) {
			continue
		}
		if used {
			continue
		}
		if obj.Name() == "_" {
			continue
		}
		if obj.Exported() && (isPkgScope(obj) || isMethod(obj) || isField(obj)) {
			f := lprog.Fset.Position(obj.Pos()).Filename
			if !strings.HasSuffix(f, "_test.go") || strings.HasPrefix(obj.Name(), "Test") || strings.HasPrefix(obj.Name(), "Benchmark") {
				continue
			}
		}
		if isMain(obj) {
			continue
		}
		if isFunction(obj) && !isMethod(obj) && obj.Name() == "init" {
			continue
		}
		if isMethod(obj) && implements(obj, interfaces) {
			continue
		}
		unused = append(unused, obj)
	}
	c.Fset = lprog.Fset
	return unused, nil
}

func Check(paths []string, flags CheckMode) ([]types.Object, error) {
	checker := Checker{Mode: flags}
	return checker.Check(paths)
}

func implements(obj types.Object, ifaces []*types.Interface) bool {
	recvType := obj.(*types.Func).Type().(*types.Signature).Recv().Type()
	for _, iface := range ifaces {
		if !types.Implements(recvType, iface) {
			continue
		}
		n := iface.NumMethods()
		for i := 0; i < n; i++ {
			if iface.Method(i).Name() == obj.Name() {
				return true
			}
		}
	}
	return false
}

func isPkgScope(obj types.Object) bool {
	return obj.Parent() == obj.Pkg().Scope()
}

func isMain(obj types.Object) bool {
	if obj.Pkg().Name() != "main" {
		return false
	}
	if obj.Name() != "main" {
		return false
	}
	if !isPkgScope(obj) {
		return false
	}
	if !isFunction(obj) {
		return false
	}
	if isMethod(obj) {
		return false
	}
	return true
}

func isFunction(obj types.Object) bool {
	_, ok := obj.(*types.Func)
	return ok
}

func isMethod(obj types.Object) bool {
	if !isFunction(obj) {
		return false
	}
	return obj.(*types.Func).Type().(*types.Signature).Recv() != nil
}

func isVariable(obj types.Object) bool {
	_, ok := obj.(*types.Var)
	return ok
}

func isConstant(obj types.Object) bool {
	_, ok := obj.(*types.Const)
	return ok
}

func isType(obj types.Object) bool {
	_, ok := obj.(*types.TypeName)
	return ok
}

func isField(obj types.Object) bool {
	if obj, ok := obj.(*types.Var); ok && obj.IsField() {
		return true
	}
	return false
}

func (c *Checker) checkFlags(obj types.Object) bool {
	if isFunction(obj) && !c.checkFunctions() {
		return false
	}
	if isVariable(obj) && !c.checkVariables() {
		return false
	}
	if isConstant(obj) && !c.checkConstants() {
		return false
	}
	if isType(obj) && !c.checkTypes() {
		return false
	}
	if isField(obj) && !c.checkFields() {
		return false
	}
	return true
}

type visitor func(node ast.Node) ast.Visitor

func (v visitor) Visit(node ast.Node) ast.Visitor {
	return v(node)
}
<|endoftext|>


<commit_before>package viz

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestWriteGEXFDataHappyPath(t *testing.T) {
	buf := bytes.NewBufferString("")
	err := WriteGEXFData(buf, testNodes(), testEdges())
	assert.Nil(t, err)

	output := buf.String()
	assert.Equalf(t, expectedGexfOutput, output, "Expected output to match")
}

const expectedGexfOutput = `<?xml version="1.0" encoding="UTF-8"?>
  <gexf xmlns="http://www.gephi.org/gexf" version="1.3" xmlns:viz="http://www.gephi.org/gexf/viz">
      <meta lastmodifieddate="2021-11-19">
          <creator>OWASP Amass - https://github.com/OWASP/Amass</creator>
          <description>OWASP Amass Network Mapping</description>
      </meta>
      <graph mode="static" defaultedgetype="directed">
          <attributes class="node">
              <attribute id="0" title="Title" type="string"></attribute>
              <attribute id="1" title="Source" type="string"></attribute>
              <attribute id="2" title="Type" type="string"></attribute>
          </attributes>
          <nodes>
              <node id="0" label="owasp.org">
                  <attvalues>
                      <attvalue for="0" value="domain: owasp.org"></attvalue>
                      <attvalue for="1" value="DNS"></attvalue>
                      <attvalue for="2" value="domain"></attvalue>
                  </attvalues>
                  <parents></parents>
                  <viz:color r="242" g="44" b="13"></viz:color>
              </node>
              <node id="1" label="205.251.199.98">
                  <attvalues>
                      <attvalue for="0" value="address: 205.251.199.98"></attvalue>
                      <attvalue for="1" value="DNS"></attvalue>
                      <attvalue for="2" value="address"></attvalue>
                  </attvalues>
                  <parents></parents>
                  <viz:color r="243" g="156" b="18"></viz:color>
              </node>
          </nodes>
          <edges>
              <edge id="0" source="0" target="1">
                  <attvalues></attvalues>
              </edge>
          </edges>
      </graph>
  </gexf>`
<commit_msg>Fixed Gexf test<commit_after>package viz

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestWriteGEXFDataHappyPath(t *testing.T) {
	buf := bytes.NewBufferString("")
	err := WriteGEXFData(buf, testNodes(), testEdges())
	assert.Nil(t, err)

	output := buf.String()
	assert.Contains(t, output, expectedGexfOutput, "Gexf output should contain")
}

const expectedGexfOutput = `<creator>OWASP Amass - https://github.com/OWASP/Amass</creator>
          <description>OWASP Amass Network Mapping</description>
      </meta>
      <graph mode="static" defaultedgetype="directed">
          <attributes class="node">
              <attribute id="0" title="Title" type="string"></attribute>
              <attribute id="1" title="Source" type="string"></attribute>
              <attribute id="2" title="Type" type="string"></attribute>
          </attributes>
          <nodes>
              <node id="0" label="owasp.org">
                  <attvalues>
                      <attvalue for="0" value="domain: owasp.org"></attvalue>
                      <attvalue for="1" value="DNS"></attvalue>
                      <attvalue for="2" value="domain"></attvalue>
                  </attvalues>
                  <parents></parents>
                  <viz:color r="242" g="44" b="13"></viz:color>
              </node>
              <node id="1" label="205.251.199.98">
                  <attvalues>
                      <attvalue for="0" value="address: 205.251.199.98"></attvalue>
                      <attvalue for="1" value="DNS"></attvalue>
                      <attvalue for="2" value="address"></attvalue>
                  </attvalues>
                  <parents></parents>
                  <viz:color r="243" g="156" b="18"></viz:color>
              </node>
          </nodes>
          <edges>
              <edge id="0" source="0" target="1">
                  <attvalues></attvalues>
              </edge>
          </edges>
      </graph>
  </gexf>`
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	"io"
	"time"
	"os"
	"runtime"

	"github.com/daviddengcn/go-colortext"
)

//LogLevel
type Level int

//Logger
type Logger struct {
	// Log level
	Level Level
	// Colorful output, different log levels use different text colors
	Colorful bool
	// Where to write output stream
	Output io.Writer
	// Find which code, which file and which line number, called log functions
	CallStackDepth int
	// Logger name
	name string
}

//LogLevel constants
const (
	LevelDebug = iota
	LevelInfo
	LevelWarning
	LevelError
	LevelFatal
)

const (
	defaultCallStackDepth = 2
)

//global logger
//
// The global logger is named "root" and it is a colorful logger with level DEBUG and log to os.Stdout
var gLogger = Get("root")
var gLoggers = make(map[string]*Logger)
var gLogFileAndLine = true

func init() {
	SetCallStackDepth(3)
}

//debug log
func Debug(fmt string, v ...interface{}) {
	gLogger.Debug(fmt, v...)
}

//info log
func Info(fmt string, v ...interface{}) {
	gLogger.Info(fmt, v...)
}

//warning log
func Warn(fmt string, v ...interface{}) {
	gLogger.Warn(fmt, v...)
}

//error log detailed
func ErrorD(fmt string, v ...interface{}) {
	gLogger.ErrorD(fmt, v...)
}

//error log
func Error(err error) {
	gLogger.Error(err)
}

//fatal error log detailed
func FatalD(fmt string, v ...interface{}) {
	gLogger.FatalD(fmt, v...)
}

//fatal error log
func Fatal(err error) {
	gLogger.Fatal(err)
}

// Convenient log functions,
func DebugLog(v interface{}) {
	gLogger.DebugLog(v)
}

func InfoLog(v interface{}) {
	gLogger.InfoLog(v)
}

func WarningLog(v interface{}) {
	gLogger.WarningLog(v)
}

func ErrorLog(v interface{}) {
	gLogger.ErrorLog(v)
}

func FatalLog(v interface{}) {
	gLogger.FatalLog(v)
}

//set log level of the global logger
func SetLevel(level Level) {
	gLogger.Level = level
}

//set colorful of the global logger
func SetColorful(b bool) {
	gLogger.Colorful = b
}

//set where to write the log text of the global logger
func SetOutput(output io.Writer) {
	gLogger.Output = output
}

//set call stack depth of the global logger
func SetCallStackDepth(depth int) {
	gLogger.SetCallStackDepth(depth)
}

//global switch of file name and line number
//
//production applications should turn off file name and line number output to improve performance
func SetLogFileNameAndLineNumber(b bool) {
	gLogFileAndLine = b
}

//get a logger from logger pool, if cooresponding logger is not found, a simple logger is created and registered then return
//
//it is recommended that libraries call this function to initialize a library-inner-logger and pass library full name
//to the "name" argument
func Get(name string) *Logger {
	l := gLoggers[name]
	if(l == nil) {
		l = NewSimpleLogger(name)
		Register(l)
	}
	return l
}

//register a logger instance to logger pool
func Register(l * Logger)  {
	gLoggers[l.name] = l
}

//create a logger
func NewLogger(name string, level Level, colorful bool, output io.Writer) *Logger {
	if(output == nil) {
		output = os.Stdout
	}
	if(name == "") {
		name = "root"
	}
	ret := &Logger{level, colorful, output, defaultCallStackDepth, name}
	return ret
}

//create a named logger
//
//simple logger is a colorful logger with level DEBUG and log to os.Stdout
func NewSimpleLogger(name string) *Logger{
	return NewLogger(name, LevelDebug, true, nil)
}

//debug log
func (logger *Logger) Debug(fmt string, v ...interface{}) {
	if(logger.Level > LevelDebug) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Cyan, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("TRACE:", fmt, v...)
}

//info log
func (logger *Logger) Info(fmt string, v ...interface{}) {
	if(logger.Level > LevelInfo) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Green, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("INFO:", fmt, v...)
}

//warning log
func (logger *Logger) Warn(fmt string, v ...interface{}) {
	if(logger.Level > LevelWarning) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Yellow, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***WARN***:", fmt, v...)
}

//error log detailed
func (logger *Logger) ErrorD(fmt string, v ...interface{}) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***ERROR***:", fmt, v...)
}

//error log, print error directly
func (logger *Logger) Error(err error) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***ERROR***:", err.Error())
}

//fatal error log detailed
func (logger *Logger) FatalD(fmt string, v ...interface{}) {
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***FATAL***:", fmt, v...)
}

//error log, print fatal error directly
func (logger *Logger) Fatal(err error) {
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***FATAL***:", err.Error())
}

//set caller depth, this is used to print the file name and line number where calling log function
//
//set this value to 0 will cause no file name and line number outputting
//range of depth will be restricted from 0 to 10
func (logger *Logger) SetCallStackDepth(depth int) {
	if(depth < 0) {
		depth = 0
	}
	if(depth > 10) {
		depth = 10
	}
	logger.CallStackDepth = depth
}

// Convenient log functions,
func (logger *Logger) DebugLog(v interface{}) {
	if(logger.Level > LevelDebug) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Cyan, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("TRACE:", "%+v", v)
}

func (logger *Logger) InfoLog(v interface{}) {
	if(logger.Level > LevelInfo) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Green, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("INFO:", "%+v", v)
}

func (logger *Logger) WarningLog(v interface{}) {
	if(logger.Level > LevelWarning) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Yellow, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***WARN***:", "%+v", v)
}

func (logger *Logger) ErrorLog(v interface{}) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***ERROR***:", "%+v", v)
}

func (logger *Logger) FatalLog(v interface{}) {
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***FATAL***:", "%+v", v)
}

//******************************************************************************************//
//******************************************************************************************//
func (logger *Logger) logText(levelFlagString, formatString string, v ...interface{}) {
	caller := " "
	if(gLogFileAndLine) {
		if (logger.CallStackDepth > 0) {
			_, file, line, ok := runtime.Caller(logger.CallStackDepth)
			if (ok == true) {
				caller = fmt.Sprintf(" - %s:%d -", file, line)
			}
		}
	}
	t := time.Now()
	s1 := fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d.%03d [%s] %s%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond() / 1000000, logger.name, levelFlagString, caller)
	s2 := fmt.Sprintf(formatString, v...)
	fmt.Fprintf(logger.Output, "%s%s\n", s1, s2)
}<commit_msg>add space to log text<commit_after>package logger

import (
	"fmt"
	"io"
	"time"
	"os"
	"runtime"

	"github.com/daviddengcn/go-colortext"
)

//LogLevel
type Level int

//Logger
type Logger struct {
	// Log level
	Level Level
	// Colorful output, different log levels use different text colors
	Colorful bool
	// Where to write output stream
	Output io.Writer
	// Find which code, which file and which line number, called log functions
	CallStackDepth int
	// Logger name
	name string
}

//LogLevel constants
const (
	LevelDebug = iota
	LevelInfo
	LevelWarning
	LevelError
	LevelFatal
)

const (
	defaultCallStackDepth = 2
)

//global logger
//
// The global logger is named "root" and it is a colorful logger with level DEBUG and log to os.Stdout
var gLogger = Get("root")
var gLoggers = make(map[string]*Logger)
var gLogFileAndLine = true

func init() {
	SetCallStackDepth(3)
}

//debug log
func Debug(fmt string, v ...interface{}) {
	gLogger.Debug(fmt, v...)
}

//info log
func Info(fmt string, v ...interface{}) {
	gLogger.Info(fmt, v...)
}

//warning log
func Warn(fmt string, v ...interface{}) {
	gLogger.Warn(fmt, v...)
}

//error log detailed
func ErrorD(fmt string, v ...interface{}) {
	gLogger.ErrorD(fmt, v...)
}

//error log
func Error(err error) {
	gLogger.Error(err)
}

//fatal error log detailed
func FatalD(fmt string, v ...interface{}) {
	gLogger.FatalD(fmt, v...)
}

//fatal error log
func Fatal(err error) {
	gLogger.Fatal(err)
}

// Convenient log functions,
func DebugLog(v interface{}) {
	gLogger.DebugLog(v)
}

func InfoLog(v interface{}) {
	gLogger.InfoLog(v)
}

func WarningLog(v interface{}) {
	gLogger.WarningLog(v)
}

func ErrorLog(v interface{}) {
	gLogger.ErrorLog(v)
}

func FatalLog(v interface{}) {
	gLogger.FatalLog(v)
}

//set log level of the global logger
func SetLevel(level Level) {
	gLogger.Level = level
}

//set colorful of the global logger
func SetColorful(b bool) {
	gLogger.Colorful = b
}

//set where to write the log text of the global logger
func SetOutput(output io.Writer) {
	gLogger.Output = output
}

//set call stack depth of the global logger
func SetCallStackDepth(depth int) {
	gLogger.SetCallStackDepth(depth)
}

//global switch of file name and line number
//
//production applications should turn off file name and line number output to improve performance
func SetLogFileNameAndLineNumber(b bool) {
	gLogFileAndLine = b
}

//get a logger from logger pool, if cooresponding logger is not found, a simple logger is created and registered then return
//
//it is recommended that libraries call this function to initialize a library-inner-logger and pass library full name
//to the "name" argument
func Get(name string) *Logger {
	l := gLoggers[name]
	if(l == nil) {
		l = NewSimpleLogger(name)
		Register(l)
	}
	return l
}

//register a logger instance to logger pool
func Register(l * Logger)  {
	gLoggers[l.name] = l
}

//create a logger
func NewLogger(name string, level Level, colorful bool, output io.Writer) *Logger {
	if(output == nil) {
		output = os.Stdout
	}
	if(name == "") {
		name = "root"
	}
	ret := &Logger{level, colorful, output, defaultCallStackDepth, name}
	return ret
}

//create a named logger
//
//simple logger is a colorful logger with level DEBUG and log to os.Stdout
func NewSimpleLogger(name string) *Logger{
	return NewLogger(name, LevelDebug, true, nil)
}

//debug log
func (logger *Logger) Debug(fmt string, v ...interface{}) {
	if(logger.Level > LevelDebug) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Cyan, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("TRACE:", fmt, v...)
}

//info log
func (logger *Logger) Info(fmt string, v ...interface{}) {
	if(logger.Level > LevelInfo) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Green, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("INFO:", fmt, v...)
}

//warning log
func (logger *Logger) Warn(fmt string, v ...interface{}) {
	if(logger.Level > LevelWarning) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Yellow, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***WARN***:", fmt, v...)
}

//error log detailed
func (logger *Logger) ErrorD(fmt string, v ...interface{}) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***ERROR***:", fmt, v...)
}

//error log, print error directly
func (logger *Logger) Error(err error) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***ERROR***:", err.Error())
}

//fatal error log detailed
func (logger *Logger) FatalD(fmt string, v ...interface{}) {
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***FATAL***:", fmt, v...)
}

//error log, print fatal error directly
func (logger *Logger) Fatal(err error) {
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***FATAL***:", err.Error())
}

//set caller depth, this is used to print the file name and line number where calling log function
//
//set this value to 0 will cause no file name and line number outputting
//range of depth will be restricted from 0 to 10
func (logger *Logger) SetCallStackDepth(depth int) {
	if(depth < 0) {
		depth = 0
	}
	if(depth > 10) {
		depth = 10
	}
	logger.CallStackDepth = depth
}

// Convenient log functions,
func (logger *Logger) DebugLog(v interface{}) {
	if(logger.Level > LevelDebug) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Cyan, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("TRACE:", "%+v", v)
}

func (logger *Logger) InfoLog(v interface{}) {
	if(logger.Level > LevelInfo) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Green, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("INFO:", "%+v", v)
}

func (logger *Logger) WarningLog(v interface{}) {
	if(logger.Level > LevelWarning) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Yellow, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***WARN***:", "%+v", v)
}

func (logger *Logger) ErrorLog(v interface{}) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***ERROR***:", "%+v", v)
}

func (logger *Logger) FatalLog(v interface{}) {
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	} else {
		ct.ChangeColor(ct.Black, false, ct.None, false)
	}
	logger.logText("***FATAL***:", "%+v", v)
}

//******************************************************************************************//
//******************************************************************************************//
func (logger *Logger) logText(levelFlagString, formatString string, v ...interface{}) {
	caller := " "
	if(gLogFileAndLine) {
		if (logger.CallStackDepth > 0) {
			_, file, line, ok := runtime.Caller(logger.CallStackDepth)
			if (ok == true) {
				caller = fmt.Sprintf(" - %s:%d - ", file, line)
			}
		}
	}
	t := time.Now()
	s1 := fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d.%03d [%s] %s%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond() / 1000000, logger.name, levelFlagString, caller)
	s2 := fmt.Sprintf(formatString, v...)
	fmt.Fprintf(logger.Output, "%s%s\n", s1, s2)
}<|endoftext|>


<commit_before>package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"time"
)

// Logger stuct contains the log details
type Logger struct {
	Date   time.Time
	Level  string
	Src    Source
	Output *string
}

// Source struct contains the source details
type Source struct {
	Func string
	Line int
}

var log = new(Logger)

func (l *Logger) caller() {
	var ok bool
	var pc uintptr
	pc, _, l.Src.Line, ok = runtime.Caller(3)
	if !ok {
		l.Src.Func = "???"
		l.Src.Line = 0
	} else {
		l.Src.Func = runtime.FuncForPC(pc).Name()
	}

}

func (l *Logger) message(format string, args []interface{}) *string {
	m := fmt.Sprintf(format, args...)
	return &m
}

func (l *Logger) now() {
	l.Date = time.Now()
}

func (l *Logger) print(level string, format string, args ...interface{}) {
	l.now()
	l.caller()
	l.Output = l.message(format, args)
	l.Level = level

	data, err := json.Marshal(l)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(data))
}

// Info function logs a message using INFO as log level
func Info(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("info", "%s", s)
}

// Warning function logs a message using WARNING as log level
func Warning(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("warning", "%s", s)
}

// Fatal function logs a message using FATAL as log level followed by a call to os.Exit(1)
func Fatal(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("fatal", "%s", s)
	os.Exit(1)
}

// Infof function logs a message with arguments using INFO as log level
func Infof(format string, args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("info", format, s)
}

// Warningf function logs a message with arguments using WARNING as log level
func Warningf(format string, args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("warning", format, s)
}

// Fatalf function logs a message with arguments using FATAL as log level followed by a call to os.Exit(1)
func Fatalf(format string, args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("fatal", format, s)
	os.Exit(1)
}
<commit_msg>Fix bug when logging with format string<commit_after>package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"time"
)

// Logger stuct contains the log details
type Logger struct {
	Date   time.Time
	Level  string
	Src    Source
	Output *string
}

// Source struct contains the source details
type Source struct {
	Func string
	Line int
}

var log = new(Logger)

func (l *Logger) caller() {
	var ok bool
	var pc uintptr
	pc, _, l.Src.Line, ok = runtime.Caller(3)
	if !ok {
		l.Src.Func = "???"
		l.Src.Line = 0
	} else {
		l.Src.Func = runtime.FuncForPC(pc).Name()
	}

}

func (l *Logger) message(format string, args []interface{}) *string {
	m := fmt.Sprintf(format, args...)
	return &m
}

func (l *Logger) now() {
	l.Date = time.Now()
}

func (l *Logger) print(level string, format string, args ...interface{}) {
	l.now()
	l.caller()
	l.Output = l.message(format, args)
	l.Level = level

	data, err := json.Marshal(l)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(data))
}

// Info function logs a message using INFO as log level
func Info(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("info", "%s", s)
}

// Warning function logs a message using WARNING as log level
func Warning(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("warning", "%s", s)
}

// Fatal function logs a message using FATAL as log level followed by a call to os.Exit(1)
func Fatal(args ...interface{}) {
	s := fmt.Sprint(args...)
	log.print("fatal", "%s", s)
	os.Exit(1)
}

// Infof function logs a message with arguments using INFO as log level
func Infof(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("info", s)
}

// Warningf function logs a message with arguments using WARNING as log level
func Warningf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("warning", s)
}

// Fatalf function logs a message with arguments using FATAL as log level followed by a call to os.Exit(1)
func Fatalf(format string, args ...interface{}) {
	s := fmt.Sprintf(format, args...)
	log.print("fatal", s)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package log

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

var Stderr io.Writer = os.Stderr
var Discard io.Writer = ioutil.Discard

var topics map[string]struct{} = make(map[string]struct{})

func Println(topic string, v ...interface{}) bool {
	if IsOn(topic) {
		log.Println(v...)
		return true
	}
	return false
}

func SetOutput(w io.Writer) { log.SetOutput(w) }

func On(topic string) {
	for _, t := range strings.Split(topic, " ") {
		topics[t] = struct{}{}
	}
}

func Off(topic string) {
	delete(topics, topic)
}

func IsOn(topic string) bool {
	_, ok := topics[topic]
	return ok
}
<commit_msg>Panic added<commit_after>package log

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

var Stderr io.Writer = os.Stderr
var Discard io.Writer = ioutil.Discard

var topics map[string]struct{} = make(map[string]struct{})

func Println(topic string, v ...interface{}) bool {
	if IsOn(topic) {
		log.Println(v...)
		return true
	}
	return false
}

func Panic(v ...interface{}) bool {
	s := fmt.Sprint(v...)
	log.Println(s)
	panic(s)
}

func SetOutput(w io.Writer) { log.SetOutput(w) }

func On(topic string) {
	for _, t := range strings.Split(topic, " ") {
		topics[t] = struct{}{}
	}
}

func Off(topic string) {
	delete(topics, topic)
}

func IsOn(topic string) bool {
	_, ok := topics[topic]
	return ok
}
<|endoftext|>


<commit_before>package utils

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"strconv"

	e "github.com/techjanitor/pram-post/errors"
)

var nRandBytes = 32

// creates a new random session id with user id
func NewSession(userid, groupid uint) (cookieToken string, err error) {

	// convert userid to string
	uid := strconv.Itoa(int(userid))
	gid := strconv.Itoa(int(groupid))

	// Initialize cache handle
	cache := RedisCache

	// make slice for token, with user + semicolon
	token := make([]byte, nRandBytes+len(uid)+1)
	// copy key into token
	copy(token, []byte(uid))
	// add semicolon
	token[len(uid)] = ';'

	// read in random bytes
	_, err = rand.Read(token[len(uid)+1:])
	if err != nil {
		return
	}

	// goes to redis
	sum := md5.Sum(token)
	storageToken := base64.StdEncoding.EncodeToString(sum[:])

	// user hash is like user:100
	user_key := fmt.Sprintf("user:%d", userid)

	// check to see if session exists already
	result, err := cache.HGet(user_key, "session")
	if err != nil && err != ErrCacheMiss {
		return
	}

	old_session_key := fmt.Sprintf("session:%s", result)

	// delete keys
	err = cache.Delete(user_key, old_session_key)
	if err != nil {
		return
	}

	// set in user hash
	err = cache.HMSet(user_key, "session", []byte(storageToken))
	if err != nil {
		return
	}

	// set user group
	err = cache.HMSet(user_key, "group", []byte(gid))
	if err != nil {
		return
	}

	new_session_key := fmt.Sprintf("session:%s", storageToken)

	// set key in redis
	err = cache.SetEx(new_session_key, 31556952, []byte(uid))
	if err != nil {
		return
	}

	// goes in the cookie
	cookieToken = base64.URLEncoding.EncodeToString(token)

	return

}

// validate compares provided session id to redis
func ValidateSession(key []byte) (uid, gid uint, err error) {

	// Initialize cache handle
	cache := RedisCache

	// decode key
	token, err := base64.URLEncoding.DecodeString(string(key))
	if err != nil {
		return
	}

	// get uid
	index := bytes.IndexByte(token, ';')

	// check to see if user is there
	if index < 0 {
		return e.ErrInvalidSession
	}

	// get given uid
	cookie_uid := string(token[:index])

	// hash token
	sum := md5.Sum(token)

	// base64 encode sum
	providedHash := base64.StdEncoding.EncodeToString(sum[:])

	// session key in redis
	session_key := fmt.Sprintf("session:%s", providedHash)

	// check for match in user session hash
	userid, err := cache.Get(session_key)
	if err != nil {
		return
	}

	// check if uid matches
	if cookie_uid != string(userid) {
		return e.ErrInvalidSession
	}

	// user hash is like user:100
	user_key := fmt.Sprintf("user:%s", cookie_uid)

	// get group id from user session hash
	groupid, err := cache.HGet(user_key, "group")
	if err != nil {
		return
	}

	// parse user to uint
	uid, err = strconv.ParseUint(cookie_uid, 10, 0)
	if err != nil {
		return
	}

	// parse group to uint
	gid, err = strconv.ParseUint(groupid, 10, 0)
	if err != nil {
		return
	}

	return

}
<commit_msg>add session cookie validation<commit_after>package utils

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"strconv"

	e "github.com/techjanitor/pram-post/errors"
)

var nRandBytes = 32

// creates a new random session id with user id
func NewSession(userid, groupid uint) (cookieToken string, err error) {

	// convert userid to string
	uid := strconv.Itoa(int(userid))
	gid := strconv.Itoa(int(groupid))

	// Initialize cache handle
	cache := RedisCache

	// make slice for token, with user + semicolon
	token := make([]byte, nRandBytes+len(uid)+1)
	// copy key into token
	copy(token, []byte(uid))
	// add semicolon
	token[len(uid)] = ';'

	// read in random bytes
	_, err = rand.Read(token[len(uid)+1:])
	if err != nil {
		return
	}

	// goes to redis
	sum := md5.Sum(token)
	storageToken := base64.StdEncoding.EncodeToString(sum[:])

	// user hash is like user:100
	user_key := fmt.Sprintf("user:%d", userid)

	// check to see if session exists already
	result, err := cache.HGet(user_key, "session")
	if err != nil && err != ErrCacheMiss {
		return
	}

	old_session_key := fmt.Sprintf("session:%s", result)

	// delete keys
	err = cache.Delete(user_key, old_session_key)
	if err != nil {
		return
	}

	// set in user hash
	err = cache.HMSet(user_key, "session", []byte(storageToken))
	if err != nil {
		return
	}

	// set user group
	err = cache.HMSet(user_key, "group", []byte(gid))
	if err != nil {
		return
	}

	new_session_key := fmt.Sprintf("session:%s", storageToken)

	// set key in redis
	err = cache.SetEx(new_session_key, 31556952, []byte(uid))
	if err != nil {
		return
	}

	// goes in the cookie
	cookieToken = base64.URLEncoding.EncodeToString(token)

	return

}

// validate compares provided session id to redis
func ValidateSession(key []byte) (uid, gid uint, err error) {

	// Initialize cache handle
	cache := RedisCache

	// decode key
	token, err := base64.URLEncoding.DecodeString(string(key))
	if err != nil {
		return
	}

	// get uid
	index := bytes.IndexByte(token, ';')

	// check to see if user is there
	if index < 0 {
		return
	}

	// get given uid
	cookie_uid := string(token[:index])

	// hash token
	sum := md5.Sum(token)

	// base64 encode sum
	providedHash := base64.StdEncoding.EncodeToString(sum[:])

	// session key in redis
	session_key := fmt.Sprintf("session:%s", providedHash)

	// check for match in user session hash
	userid, err := cache.Get(session_key)
	if err != nil {
		return
	}

	// check if uid matches
	if cookie_uid != string(userid) {
		return
	}

	// user hash is like user:100
	user_key := fmt.Sprintf("user:%s", cookie_uid)

	// get group id from user session hash
	groupid, err := cache.HGet(user_key, "group")
	if err != nil {
		return
	}

	// parse user to uint
	puid, err := strconv.ParseUint(cookie_uid, 10, 0)
	if err != nil {
		return
	}

	// parse group to uint
	pgid, err := strconv.ParseUint(string(groupid), 10, 0)
	if err != nil {
		return
	}

	uid = uint(puid)
	gid = uint(pgid)

	return

}
<|endoftext|>


<commit_before>package colonycore

import (
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

type WebGrabber struct {
	orm.ModelBase
	ID                string            `json:"nameid",bson:"nameid"`
	CallType          string            `json:"calltype",bson:"calltype"`
	SourceType        string            `json:"sourcetype",bson:"sourcetype"`
	IntervalType      string            `json:"intervaltype",bson:"intervaltype"`
	GrabInterval      int32             `json:"grabinterval",bson:"grabinterval"`
	TimeoutInterval   int32             `json:"timeoutinterval",bson:"timeoutinterval"`
	URL               string            `json:"url",bson:"url"`
	LogConfiguration  *LogConfiguration `json:"logconf",bson:"logconf"`
	DataSettings      []*DataSetting    `json:"datasettings",bson:"datasettings"`
	GrabConfiguration toolkit.M         `json:"grabconf",bson:"grabconf"`
}

func (ds *WebGrabber) TableName() string {
	return "webgrabbers"
}

func (ds *WebGrabber) RecordID() interface{} {
	return ds.ID
}

type LogConfiguration struct {
	FileName    string `json:"filename",bson:"filename"`
	FilePattern string `json:"filepattern",bson:"filepattern"`
	LogPath     string `json:"logpath",bson:"logpath"`
}

type ConnectionInfo struct {
	Host       string    `json:"host",bson:"host"`
	Database   string    `json:"database",bson:"database"`
	UserName   string    `json:"username",bson:"username"`
	Password   string    `json:"password",bson:"password"`
	Settings   toolkit.M `json:"settings",bson:"settings"`
	Collection string    `json:"collection",bson:"collection"`
}

type DataSetting struct {
	RowSelector         string           `json:"rowselector",bson:"rowselector"`
	ColumnSettings      []*ColumnSetting `json:"columnsettings",bson:"columnsettings"`
	RowDeleteCondition  toolkit.M        `json:"rowdeletecond",bson:"rowdeletecond"`
	RowIncludeCondition toolkit.M        `json:"rowincludecond",bson:"rowincludecond"`
	ConnectionInfo      *ConnectionInfo  `json:"connectioninfo",bson:"connectioninfo"`
	DestinationType     string           `json:"desttype",bson:"desttype"`
	Name                string           `json:"name",bson:"name"`
}

type ColumnSetting struct {
	Alias     string `json:"alias",bson:"alias"`
	Index     int    `json:"index",bson:"index"`
	Selector  string `json:"selector",bson:"selector"`
	ValueType string `json:"valuetype",bson:"valuetype"`
}
<commit_msg>no message<commit_after>package colonycore

import (
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

type WebGrabber struct {
	orm.ModelBase
	ID                string            `json:"_id",bson:"_id"`
	IDBackup          string            `json:"nameid",bson:"nameid"`
	CallType          string            `json:"calltype",bson:"calltype"`
	SourceType        string            `json:"sourcetype",bson:"sourcetype"`
	IntervalType      string            `json:"intervaltype",bson:"intervaltype"`
	GrabInterval      int32             `json:"grabinterval",bson:"grabinterval"`
	TimeoutInterval   int32             `json:"timeoutinterval",bson:"timeoutinterval"`
	URL               string            `json:"url",bson:"url"`
	LogConfiguration  *LogConfiguration `json:"logconf",bson:"logconf"`
	DataSettings      []*DataSetting    `json:"datasettings",bson:"datasettings"`
	GrabConfiguration toolkit.M         `json:"grabconf",bson:"grabconf"`
}

func (ds *WebGrabber) TableName() string {
	return "webgrabbers"
}

func (ds *WebGrabber) RecordID() interface{} {
	return ds.ID
}

type LogConfiguration struct {
	FileName    string `json:"filename",bson:"filename"`
	FilePattern string `json:"filepattern",bson:"filepattern"`
	LogPath     string `json:"logpath",bson:"logpath"`
}

type ConnectionInfo struct {
	Host       string    `json:"host",bson:"host"`
	Database   string    `json:"database",bson:"database"`
	UserName   string    `json:"username",bson:"username"`
	Password   string    `json:"password",bson:"password"`
	Settings   toolkit.M `json:"settings",bson:"settings"`
	Collection string    `json:"collection",bson:"collection"`
}

type DataSetting struct {
	RowSelector         string           `json:"rowselector",bson:"rowselector"`
	ColumnSettings      []*ColumnSetting `json:"columnsettings",bson:"columnsettings"`
	RowDeleteCondition  toolkit.M        `json:"rowdeletecond",bson:"rowdeletecond"`
	RowIncludeCondition toolkit.M        `json:"rowincludecond",bson:"rowincludecond"`
	ConnectionInfo      *ConnectionInfo  `json:"connectioninfo",bson:"connectioninfo"`
	DestinationType     string           `json:"desttype",bson:"desttype"`
	Name                string           `json:"name",bson:"name"`
}

type ColumnSetting struct {
	Alias     string `json:"alias",bson:"alias"`
	Index     int    `json:"index",bson:"index"`
	Selector  string `json:"selector",bson:"selector"`
	ValueType string `json:"valuetype",bson:"valuetype"`
}
<|endoftext|>


<commit_before>package upload

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/subutai-io/agent/log"
	"github.com/subutai-io/gorjun/config"
	"github.com/subutai-io/gorjun/db"
)

type share struct {
	Token  string   `json:"token"`
	Id     string   `json:"id"`
	Add    []string `json:"add"`
	Remove []string `json:"remove"`
}

//Handler function works with income upload requests, makes sanity checks, etc
func Handler(w http.ResponseWriter, r *http.Request) (hash, owner string) {
	r.ParseMultipartForm(32 << 20)
	if len(r.MultipartForm.Value["token"]) == 0 || len(db.CheckToken(r.MultipartForm.Value["token"][0])) == 0 {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte("Not authorized"))
		log.Warn(r.RemoteAddr + " - rejecting unauthorized upload request")
		return
	}

	owner = db.CheckToken(r.MultipartForm.Value["token"][0])

	file, header, err := r.FormFile("file")
	if log.Check(log.WarnLevel, "Failed to parse POST form", err) {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Cannot get file from request"))
		return
	}
	defer file.Close()

	l := сheckLength(owner, r.Header.Get("Content-Length"))
	if !l {
		w.WriteHeader(http.StatusNotAcceptable)
		w.Write([]byte("Storage quota exceeded"))
		log.Warn("User " + owner + " exceeded storage quota, rejecting upload")
		return
	}

	out, err := os.Create(config.Storage.Path + header.Filename)
	if log.Check(log.WarnLevel, "Unable to create the file for writing", err) {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Cannot create file"))
		return
	}
	defer out.Close()

	limit := int64(db.QuotaLeft(owner))
	f := io.Reader(file)
	if limit != -1 {
		f = io.LimitReader(file, limit)
	}

	// write the content from POST to the file
	if copied, err := io.Copy(out, f); limit != -1 && (copied == limit || err != nil) {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Failed to write file or storage quota exceeded"))
		log.Warn("User " + owner + " exceeded storage quota, removing file")
		os.Remove(config.Storage.Path + header.Filename)
		return
	}

	hash = Hash(config.Storage.Path + header.Filename)
	if len(hash) == 0 {
		log.Warn("Failed to calculate hash for " + header.Filename)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Failed to calculate hash"))
		return
	}

	os.Rename(config.Storage.Path+header.Filename, config.Storage.Path+hash)
	log.Info("File received: " + header.Filename + "(" + hash + ")")

	return hash, owner
}

func Hash(file string, algo ...string) string {
	f, err := os.Open(file)
	log.Check(log.WarnLevel, "Opening file "+file, err)
	defer f.Close()

	hash := md5.New()
	if len(algo) != 0 {
		switch algo[0] {
		case "sha512":
			hash = sha512.New()
		case "sha256":
			hash = sha256.New()
		case "sha1":
			hash = sha1.New()
		}
	}
	if _, err := io.Copy(hash, f); err != nil {
		return ""
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func Delete(w http.ResponseWriter, r *http.Request) string {
	hash := r.URL.Query().Get("id")
	token := r.URL.Query().Get("token")
	if len(hash) == 0 {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Empty file id"))
		log.Warn(r.RemoteAddr + " - empty file id")
		return ""
	}
	if len(token) == 0 {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte("Empty token"))
		log.Warn(r.RemoteAddr + " - empty token")
		return ""
	}
	user := db.CheckToken(token)
	info := db.Info(hash)
	if len(info) == 0 {
		log.Warn("File not found by hash")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("File not found"))
		return ""
	}

	repo := strings.Split(r.URL.EscapedPath(), "/")
	if len(repo) < 4 {
		log.Warn(r.URL.EscapedPath() + " - bad deletion request")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Bad request"))
		return ""
	}

	if db.CheckRepo(user, repo[3], hash) == 0 {
		log.Warn("File " + info["name"] + "(" + hash + ") in " + repo[3] + " repo is not owned by " + user + ", rejecting deletion request")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("File " + info["name"] + " not found or it has different owner"))
		return ""
	}

	f, err := os.Stat(config.Storage.Path + hash)
	if !log.Check(log.WarnLevel, "Reading file stats", err) {
		db.QuotaUsageSet(user, -int(f.Size()))
	}

	if db.Delete(user, repo[3], hash) == 0 {
		log.Warn("Removing " + hash + " from disk")
		// torrent.Delete(hash)
		if log.Check(log.WarnLevel, "Removing "+info["name"]+"from disk", os.Remove(config.Storage.Path+hash)) {
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Failed to remove file"))
			return ""
		}
	}

	log.Info("Removing " + info["name"] + " from " + repo[3] + " repo")
	return hash
}

func Share(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		if len(r.FormValue("json")) == 0 {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Empty json"))
			log.Warn("Share request: empty json, nothing to do")
			return
		}
		var data share
		if log.Check(log.WarnLevel, "Parsing share request json", json.Unmarshal([]byte(r.FormValue("json")), &data)) {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Failed to parse json body"))
			return
		}
		if len(data.Token) == 0 || len(db.CheckToken(data.Token)) == 0 {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Not authorized"))
			log.Warn("Empty or invalid token, rejecting share request")
			return
		}
		if len(data.Id) == 0 {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Empty file id"))
			log.Warn("Empty file id, rejecting share request")
			return
		}
		owner := db.CheckToken(data.Token)
		repo := strings.Split(r.URL.EscapedPath(), "/")
		if len(repo) < 4 {
			log.Warn(r.URL.EscapedPath() + " - bad share request")
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Bad request"))
			return
		}
		if db.CheckRepo(owner, repo[3], data.Id) == 0 {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("File is not owned by authorized user"))
			log.Warn("User tried to share another's file, rejecting")
			return
		}
		for _, v := range data.Add {
			db.ShareWith(data.Id, owner, v)
		}
		for _, v := range data.Remove {
			db.UnshareWith(data.Id, owner, v)
		}
	} else if r.Method == "GET" {
		id := r.URL.Query().Get("id")
		if len(id) == 0 {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Empty file id"))
			return
		}
		token := r.URL.Query().Get("token")
		if len(token) == 0 || len(db.CheckToken(token)) == 0 {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Not authorized"))
			return
		}
		owner := db.CheckToken(token)
		repo := strings.Split(r.URL.EscapedPath(), "/")
		if len(repo) < 4 {
			log.Warn(r.URL.EscapedPath() + " - bad deletion request")
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Bad request"))
			return
		}
		if db.CheckRepo(owner, repo[3], id) == 0 {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("File is not owned by authorized user"))
			log.Warn("User tried to request scope of another's file, rejecting")
			return
		}
		js, _ := json.Marshal(db.GetScope(id, owner))
		w.Write(js)
	}
}

func сheckLength(user, length string) bool {
	l, err := strconv.Atoi(length)
	if err != nil || len(length) == 0 {
		log.Warn("Empty or invalid content length")
		return true
	}

	if l < db.QuotaLeft(user) || db.QuotaLeft(user) == -1 {
		db.QuotaUsageSet(user, l)
		return true
	}
	return false
}

func Quota(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		user := r.URL.Query().Get("user")
		token := r.URL.Query().Get("token")

		if len(token) == 0 || len(db.CheckToken(token)) == 0 || db.CheckToken(token) != "Hub" && db.CheckToken(token) != "2129bb4fb65b27ff68a21c678d461db2e2c20bb7" && db.CheckToken(token) != user {
			w.Write([]byte("Forbidden"))
			w.WriteHeader(http.StatusForbidden)
			return
		}

		if len(user) != 0 {
			q, _ := json.Marshal(map[string]int{
				"quota": db.QuotaGet(user),
				"used":  db.QuotaUsageGet(user),
				"left":  db.QuotaLeft(user)})
			w.Write([]byte(q))
		}

	} else if r.Method == "POST" {
		user := r.FormValue("user")
		quota := r.FormValue("quota")
		token := r.FormValue("token")

		if len(token) == 0 || len(db.CheckToken(token)) == 0 || db.CheckToken(token) != "Hub" && db.CheckToken(token) != "2129bb4fb65b27ff68a21c678d461db2e2c20bb7" {
			w.Write([]byte("Forbidden"))
			w.WriteHeader(http.StatusForbidden)
			return
		}

		if len(user) == 0 || len(quota) == 0 {
			w.Write([]byte("Please specify username and quota value"))
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		if q, err := strconv.Atoi(quota); err != nil || q < -1 {
			w.Write([]byte("Invalid quota value"))
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		db.QuotaSet(user, quota)
		log.Info("New quota for " + user + " is " + quota)
		w.Write([]byte("Ok"))
		w.WriteHeader(http.StatusOK)
	}
}
<commit_msg>Replaced hardcoded user in quota.<commit_after>package upload

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/subutai-io/agent/log"
	"github.com/subutai-io/gorjun/config"
	"github.com/subutai-io/gorjun/db"
)

type share struct {
	Token  string   `json:"token"`
	Id     string   `json:"id"`
	Add    []string `json:"add"`
	Remove []string `json:"remove"`
}

//Handler function works with income upload requests, makes sanity checks, etc
func Handler(w http.ResponseWriter, r *http.Request) (hash, owner string) {
	r.ParseMultipartForm(32 << 20)
	if len(r.MultipartForm.Value["token"]) == 0 || len(db.CheckToken(r.MultipartForm.Value["token"][0])) == 0 {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte("Not authorized"))
		log.Warn(r.RemoteAddr + " - rejecting unauthorized upload request")
		return
	}

	owner = db.CheckToken(r.MultipartForm.Value["token"][0])

	file, header, err := r.FormFile("file")
	if log.Check(log.WarnLevel, "Failed to parse POST form", err) {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Cannot get file from request"))
		return
	}
	defer file.Close()

	l := сheckLength(owner, r.Header.Get("Content-Length"))
	if !l {
		w.WriteHeader(http.StatusNotAcceptable)
		w.Write([]byte("Storage quota exceeded"))
		log.Warn("User " + owner + " exceeded storage quota, rejecting upload")
		return
	}

	out, err := os.Create(config.Storage.Path + header.Filename)
	if log.Check(log.WarnLevel, "Unable to create the file for writing", err) {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Cannot create file"))
		return
	}
	defer out.Close()

	limit := int64(db.QuotaLeft(owner))
	f := io.Reader(file)
	if limit != -1 {
		f = io.LimitReader(file, limit)
	}

	// write the content from POST to the file
	if copied, err := io.Copy(out, f); limit != -1 && (copied == limit || err != nil) {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Failed to write file or storage quota exceeded"))
		log.Warn("User " + owner + " exceeded storage quota, removing file")
		os.Remove(config.Storage.Path + header.Filename)
		return
	}

	hash = Hash(config.Storage.Path + header.Filename)
	if len(hash) == 0 {
		log.Warn("Failed to calculate hash for " + header.Filename)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Failed to calculate hash"))
		return
	}

	os.Rename(config.Storage.Path+header.Filename, config.Storage.Path+hash)
	log.Info("File received: " + header.Filename + "(" + hash + ")")

	return hash, owner
}

func Hash(file string, algo ...string) string {
	f, err := os.Open(file)
	log.Check(log.WarnLevel, "Opening file "+file, err)
	defer f.Close()

	hash := md5.New()
	if len(algo) != 0 {
		switch algo[0] {
		case "sha512":
			hash = sha512.New()
		case "sha256":
			hash = sha256.New()
		case "sha1":
			hash = sha1.New()
		}
	}
	if _, err := io.Copy(hash, f); err != nil {
		return ""
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func Delete(w http.ResponseWriter, r *http.Request) string {
	hash := r.URL.Query().Get("id")
	token := r.URL.Query().Get("token")
	if len(hash) == 0 {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Empty file id"))
		log.Warn(r.RemoteAddr + " - empty file id")
		return ""
	}
	if len(token) == 0 {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte("Empty token"))
		log.Warn(r.RemoteAddr + " - empty token")
		return ""
	}
	user := db.CheckToken(token)
	info := db.Info(hash)
	if len(info) == 0 {
		log.Warn("File not found by hash")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("File not found"))
		return ""
	}

	repo := strings.Split(r.URL.EscapedPath(), "/")
	if len(repo) < 4 {
		log.Warn(r.URL.EscapedPath() + " - bad deletion request")
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Bad request"))
		return ""
	}

	if db.CheckRepo(user, repo[3], hash) == 0 {
		log.Warn("File " + info["name"] + "(" + hash + ") in " + repo[3] + " repo is not owned by " + user + ", rejecting deletion request")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("File " + info["name"] + " not found or it has different owner"))
		return ""
	}

	f, err := os.Stat(config.Storage.Path + hash)
	if !log.Check(log.WarnLevel, "Reading file stats", err) {
		db.QuotaUsageSet(user, -int(f.Size()))
	}

	if db.Delete(user, repo[3], hash) == 0 {
		log.Warn("Removing " + hash + " from disk")
		// torrent.Delete(hash)
		if log.Check(log.WarnLevel, "Removing "+info["name"]+"from disk", os.Remove(config.Storage.Path+hash)) {
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("Failed to remove file"))
			return ""
		}
	}

	log.Info("Removing " + info["name"] + " from " + repo[3] + " repo")
	return hash
}

func Share(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		if len(r.FormValue("json")) == 0 {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Empty json"))
			log.Warn("Share request: empty json, nothing to do")
			return
		}
		var data share
		if log.Check(log.WarnLevel, "Parsing share request json", json.Unmarshal([]byte(r.FormValue("json")), &data)) {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Failed to parse json body"))
			return
		}
		if len(data.Token) == 0 || len(db.CheckToken(data.Token)) == 0 {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Not authorized"))
			log.Warn("Empty or invalid token, rejecting share request")
			return
		}
		if len(data.Id) == 0 {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Empty file id"))
			log.Warn("Empty file id, rejecting share request")
			return
		}
		owner := db.CheckToken(data.Token)
		repo := strings.Split(r.URL.EscapedPath(), "/")
		if len(repo) < 4 {
			log.Warn(r.URL.EscapedPath() + " - bad share request")
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Bad request"))
			return
		}
		if db.CheckRepo(owner, repo[3], data.Id) == 0 {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("File is not owned by authorized user"))
			log.Warn("User tried to share another's file, rejecting")
			return
		}
		for _, v := range data.Add {
			db.ShareWith(data.Id, owner, v)
		}
		for _, v := range data.Remove {
			db.UnshareWith(data.Id, owner, v)
		}
	} else if r.Method == "GET" {
		id := r.URL.Query().Get("id")
		if len(id) == 0 {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Empty file id"))
			return
		}
		token := r.URL.Query().Get("token")
		if len(token) == 0 || len(db.CheckToken(token)) == 0 {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Not authorized"))
			return
		}
		owner := db.CheckToken(token)
		repo := strings.Split(r.URL.EscapedPath(), "/")
		if len(repo) < 4 {
			log.Warn(r.URL.EscapedPath() + " - bad deletion request")
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte("Bad request"))
			return
		}
		if db.CheckRepo(owner, repo[3], id) == 0 {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("File is not owned by authorized user"))
			log.Warn("User tried to request scope of another's file, rejecting")
			return
		}
		js, _ := json.Marshal(db.GetScope(id, owner))
		w.Write(js)
	}
}

func сheckLength(user, length string) bool {
	l, err := strconv.Atoi(length)
	if err != nil || len(length) == 0 {
		log.Warn("Empty or invalid content length")
		return true
	}

	if l < db.QuotaLeft(user) || db.QuotaLeft(user) == -1 {
		db.QuotaUsageSet(user, l)
		return true
	}
	return false
}

func Quota(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		user := r.URL.Query().Get("user")
		token := r.URL.Query().Get("token")

		if len(token) == 0 || len(db.CheckToken(token)) == 0 || db.CheckToken(token) != "Hub" && db.CheckToken(token) != "subutai" && db.CheckToken(token) != user {
			w.Write([]byte("Forbidden"))
			w.WriteHeader(http.StatusForbidden)
			return
		}

		if len(user) != 0 {
			q, _ := json.Marshal(map[string]int{
				"quota": db.QuotaGet(user),
				"used":  db.QuotaUsageGet(user),
				"left":  db.QuotaLeft(user)})
			w.Write([]byte(q))
		}

	} else if r.Method == "POST" {
		user := r.FormValue("user")
		quota := r.FormValue("quota")
		token := r.FormValue("token")

		if len(token) == 0 || len(db.CheckToken(token)) == 0 || db.CheckToken(token) != "Hub" && db.CheckToken(token) != "subutai" {
			w.Write([]byte("Forbidden"))
			w.WriteHeader(http.StatusForbidden)
			return
		}

		if len(user) == 0 || len(quota) == 0 {
			w.Write([]byte("Please specify username and quota value"))
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		if q, err := strconv.Atoi(quota); err != nil || q < -1 {
			w.Write([]byte("Invalid quota value"))
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		db.QuotaSet(user, quota)
		log.Info("New quota for " + user + " is " + quota)
		w.Write([]byte("Ok"))
		w.WriteHeader(http.StatusOK)
	}
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"fmt"
	"strconv"
	"strings"

	k8s "github.com/YakLabs/k8s-client"
	"github.com/pulcy/j2/jobs"
	pkg "github.com/pulcy/j2/pkg/kubernetes"
)

// createServices creates all services needed for the given task group.
func createServices(tg *jobs.TaskGroup, pod pod, ctx generatorContext) ([]k8s.Service, error) {
	var services []k8s.Service
	for _, t := range pod.tasks {
		if len(t.Ports) == 0 && len(t.PublicFrontEnds) == 0 && len(t.PrivateFrontEnds) == 0 {
			continue
		}
		// Normal service for the task
		d := newService(ctx.Namespace, taskServiceName(t))
		setTaskGroupLabelsAnnotations(&d.ObjectMeta, tg)
		d.Spec.Selector = createPodSelector(d.Spec.Selector, pod)

		ports := collectPorts(t)
		for _, p := range ports {
			pp, err := p.Parse()
			if err != nil {
				return nil, maskAny(err)
			}
			protocol := pp.ProtocolString()
			servicePort := k8s.ServicePort{
				Name:       strings.ToLower(fmt.Sprintf("%d-%s", pp.ContainerPort, protocol)),
				Port:       int32(pp.ContainerPort),
				Protocol:   k8s.Protocol(protocol),
				TargetPort: pkg.FromInt(int32(pp.ContainerPort)),
			}
			d.Spec.Ports = append(d.Spec.Ports, servicePort)
		}
		services = append(services, *d)

		// Host mapped ports (a service with all ports mapped to all hosts using a NodePort)
		if !t.Network.IsHost() {
			hmPorts := getHostMappedPorts(t.Ports)
			if len(hmPorts) > 0 {
				d := newService(ctx.Namespace, taskServiceName(t)+"-host")
				setTaskGroupLabelsAnnotations(&d.ObjectMeta, tg)
				d.Spec.Type = k8s.ServiceTypeNodePort
				d.Spec.Selector = createPodSelector(d.Spec.Selector, pod)

				for _, p := range hmPorts {
					pp, err := p.Parse()
					if err != nil {
						return nil, maskAny(err)
					}
					protocol := pp.ProtocolString()
					hostPort := pp.ContainerPort
					if pp.HasHostPort() {
						hostPort = pp.HostPort
					}
					servicePort := k8s.ServicePort{
						Name:       strings.ToLower(fmt.Sprintf("%d-%s", pp.ContainerPort, protocol)),
						Port:       int32(pp.ContainerPort),
						Protocol:   k8s.Protocol(protocol),
						TargetPort: pkg.FromInt(int32(pp.ContainerPort)),
						NodePort:   int32(hostPort),
					}
					d.Spec.Ports = append(d.Spec.Ports, servicePort)
				}
				services = append(services, *d)
			}
		}
	}

	return services, nil
}

func collectPorts(t *jobs.Task) []jobs.PortMapping {
	ports := t.Ports
	portFound := func(containerPort int) bool {
		for _, p := range ports {
			pm, _ := p.Parse()
			if pm.ContainerPort == containerPort {
				return true
			}
		}
		return false
	}
	for _, f := range t.PublicFrontEnds {
		if f.Port != 0 {
			if !portFound(f.Port) {
				port := strconv.Itoa(f.Port)
				ports = append(ports, jobs.PortMapping(port))
			}
		}
	}
	for _, f := range t.PrivateFrontEnds {
		if f.Port != 0 {
			if !portFound(f.Port) {
				port := strconv.Itoa(f.Port)
				ports = append(ports, jobs.PortMapping(port))
			}
		}
	}
	return ports
}

// getHostMappedPorts returns all port mappings from the given list that have their HostIP set to '0.0.0.0'.
func getHostMappedPorts(mappings []jobs.PortMapping) []jobs.PortMapping {
	var result []jobs.PortMapping
	for _, m := range mappings {
		if pm, err := m.Parse(); err == nil && pm.HostIP == "0.0.0.0" {
			result = append(result, m)
		}
	}
	return result
}

func newService(namespace, name string) *k8s.Service {
	s := k8s.NewService(namespace, name)
	s.Spec.Type = k8s.ServiceTypeClusterIP
	s.Spec.SessionAffinity = "None"
	return s
}
<commit_msg>Prevent service creation for proxy tasks<commit_after>package kubernetes

import (
	"fmt"
	"strconv"
	"strings"

	k8s "github.com/YakLabs/k8s-client"
	"github.com/pulcy/j2/jobs"
	pkg "github.com/pulcy/j2/pkg/kubernetes"
)

// createServices creates all services needed for the given task group.
func createServices(tg *jobs.TaskGroup, pod pod, ctx generatorContext) ([]k8s.Service, error) {
	var services []k8s.Service
	for _, t := range pod.tasks {
		if t.Type.IsProxy() {
			continue
		}
		if len(t.Ports) == 0 && len(t.PublicFrontEnds) == 0 && len(t.PrivateFrontEnds) == 0 {
			continue
		}
		// Normal service for the task
		d := newService(ctx.Namespace, taskServiceName(t))
		setTaskGroupLabelsAnnotations(&d.ObjectMeta, tg)
		d.Spec.Selector = createPodSelector(d.Spec.Selector, pod)

		ports := collectPorts(t)
		for _, p := range ports {
			pp, err := p.Parse()
			if err != nil {
				return nil, maskAny(err)
			}
			protocol := pp.ProtocolString()
			servicePort := k8s.ServicePort{
				Name:       strings.ToLower(fmt.Sprintf("%d-%s", pp.ContainerPort, protocol)),
				Port:       int32(pp.ContainerPort),
				Protocol:   k8s.Protocol(protocol),
				TargetPort: pkg.FromInt(int32(pp.ContainerPort)),
			}
			d.Spec.Ports = append(d.Spec.Ports, servicePort)
		}
		services = append(services, *d)

		// Host mapped ports (a service with all ports mapped to all hosts using a NodePort)
		if !t.Network.IsHost() {
			hmPorts := getHostMappedPorts(t.Ports)
			if len(hmPorts) > 0 {
				d := newService(ctx.Namespace, taskServiceName(t)+"-host")
				setTaskGroupLabelsAnnotations(&d.ObjectMeta, tg)
				d.Spec.Type = k8s.ServiceTypeNodePort
				d.Spec.Selector = createPodSelector(d.Spec.Selector, pod)

				for _, p := range hmPorts {
					pp, err := p.Parse()
					if err != nil {
						return nil, maskAny(err)
					}
					protocol := pp.ProtocolString()
					hostPort := pp.ContainerPort
					if pp.HasHostPort() {
						hostPort = pp.HostPort
					}
					servicePort := k8s.ServicePort{
						Name:       strings.ToLower(fmt.Sprintf("%d-%s", pp.ContainerPort, protocol)),
						Port:       int32(pp.ContainerPort),
						Protocol:   k8s.Protocol(protocol),
						TargetPort: pkg.FromInt(int32(pp.ContainerPort)),
						NodePort:   int32(hostPort),
					}
					d.Spec.Ports = append(d.Spec.Ports, servicePort)
				}
				services = append(services, *d)
			}
		}
	}

	return services, nil
}

func collectPorts(t *jobs.Task) []jobs.PortMapping {
	ports := t.Ports
	portFound := func(containerPort int) bool {
		for _, p := range ports {
			pm, _ := p.Parse()
			if pm.ContainerPort == containerPort {
				return true
			}
		}
		return false
	}
	for _, f := range t.PublicFrontEnds {
		if f.Port != 0 {
			if !portFound(f.Port) {
				port := strconv.Itoa(f.Port)
				ports = append(ports, jobs.PortMapping(port))
			}
		}
	}
	for _, f := range t.PrivateFrontEnds {
		if f.Port != 0 {
			if !portFound(f.Port) {
				port := strconv.Itoa(f.Port)
				ports = append(ports, jobs.PortMapping(port))
			}
		}
	}
	return ports
}

// getHostMappedPorts returns all port mappings from the given list that have their HostIP set to '0.0.0.0'.
func getHostMappedPorts(mappings []jobs.PortMapping) []jobs.PortMapping {
	var result []jobs.PortMapping
	for _, m := range mappings {
		if pm, err := m.Parse(); err == nil && pm.HostIP == "0.0.0.0" {
			result = append(result, m)
		}
	}
	return result
}

func newService(namespace, name string) *k8s.Service {
	s := k8s.NewService(namespace, name)
	s.Spec.Type = k8s.ServiceTypeClusterIP
	s.Spec.SessionAffinity = "None"
	return s
}
<|endoftext|>


<commit_before>package volume

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

	mounttypes "github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/stringid"
	"github.com/opencontainers/runc/libcontainer/label"
	"github.com/pkg/errors"
)

// DefaultDriverName is the driver name used for the driver
// implemented in the local package.
const DefaultDriverName = "local"

// Scopes define if a volume has is cluster-wide (global) or local only.
// Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume
const (
	LocalScope  = "local"
	GlobalScope = "global"
)

// Driver is for creating and removing volumes.
type Driver interface {
	// Name returns the name of the volume driver.
	Name() string
	// Create makes a new volume with the given name.
	Create(name string, opts map[string]string) (Volume, error)
	// Remove deletes the volume.
	Remove(vol Volume) (err error)
	// List lists all the volumes the driver has
	List() ([]Volume, error)
	// Get retrieves the volume with the requested name
	Get(name string) (Volume, error)
	// Scope returns the scope of the driver (e.g. `global` or `local`).
	// Scope determines how the driver is handled at a cluster level
	Scope() string
}

// Capability defines a set of capabilities that a driver is able to handle.
type Capability struct {
	// Scope is the scope of the driver, `global` or `local`
	// A `global` scope indicates that the driver manages volumes across the cluster
	// A `local` scope indicates that the driver only manages volumes resources local to the host
	// Scope is declared by the driver
	Scope string
}

// Volume is a place to store data. It is backed by a specific driver, and can be mounted.
type Volume interface {
	// Name returns the name of the volume
	Name() string
	// DriverName returns the name of the driver which owns this volume.
	DriverName() string
	// Path returns the absolute path to the volume.
	Path() string
	// Mount mounts the volume and returns the absolute path to
	// where it can be consumed.
	Mount(id string) (string, error)
	// Unmount unmounts the volume when it is no longer in use.
	Unmount(id string) error
	// Status returns low-level status information about a volume
	Status() map[string]interface{}
}

// DetailedVolume wraps a Volume with user-defined labels, options, and cluster scope (e.g., `local` or `global`)
type DetailedVolume interface {
	Labels() map[string]string
	Options() map[string]string
	Scope() string
	Volume
}

// MountPoint is the intersection point between a volume and a container. It
// specifies which volume is to be used and where inside a container it should
// be mounted.
type MountPoint struct {
	// Source is the source path of the mount.
	// E.g. `mount --bind /foo /bar`, `/foo` is the `Source`.
	Source string
	// Destination is the path relative to the container root (`/`) to the mount point
	// It is where the `Source` is mounted to
	Destination string
	// RW is set to true when the mountpoint should be mounted as read-write
	RW bool
	// Name is the name reference to the underlying data defined by `Source`
	// e.g., the volume name
	Name string
	// Driver is the volume driver used to create the volume (if it is a volume)
	Driver string
	// Type of mount to use, see `Type<foo>` definitions in github.com/docker/docker/api/types/mount
	Type mounttypes.Type `json:",omitempty"`
	// Volume is the volume providing data to this mountpoint.
	// This is nil unless `Type` is set to `TypeVolume`
	Volume Volume `json:"-"`

	// Mode is the comma separated list of options supplied by the user when creating
	// the bind/volume mount.
	// Note Mode is not used on Windows
	Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`"

	// Propagation describes how the mounts are propagated from the host into the
	// mount point, and vice-versa.
	// See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
	// Note Propagation is not used on Windows
	Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string

	// Specifies if data should be copied from the container before the first mount
	// Use a pointer here so we can tell if the user set this value explicitly
	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
	CopyData bool `json:"-"`
	// ID is the opaque ID used to pass to the volume driver.
	// This should be set by calls to `Mount` and unset by calls to `Unmount`
	ID string `json:",omitempty"`

	// Sepc is a copy of the API request that created this mount.
	Spec mounttypes.Mount
}

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (string, error) {
	if m.Volume != nil {
		id := m.ID
		if id == "" {
			id = stringid.GenerateNonCryptoID()
		}
		path, err := m.Volume.Mount(id)
		if err != nil {
			return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
		}
		m.ID = id
		return path, nil
	}
	if len(m.Source) == 0 {
		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
	}
	// system.MkdirAll() produces an error if m.Source exists and is a file (not a directory),
	if m.Type == mounttypes.TypeBind {
		// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
		// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
		if err := idtools.MkdirAllNewAs(m.Source, 0755, rootUID, rootGID); err != nil {
			if perr, ok := err.(*os.PathError); ok {
				if perr.Err != syscall.ENOTDIR {
					return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
				}
			}
		}
	}
	if label.RelabelNeeded(m.Mode) {
		if err := label.Relabel(m.Source, mountLabel, label.IsShared(m.Mode)); err != nil {
			return "", errors.Wrapf(err, "error setting label on mount source '%s'", m.Source)
		}
	}
	return m.Source, nil
}

// Path returns the path of a volume in a mount point.
func (m *MountPoint) Path() string {
	if m.Volume != nil {
		return m.Volume.Path()
	}
	return m.Source
}

// ParseVolumesFrom ensures that the supplied volumes-from is valid.
func ParseVolumesFrom(spec string) (string, string, error) {
	if len(spec) == 0 {
		return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
	}

	specParts := strings.SplitN(spec, ":", 2)
	id := specParts[0]
	mode := "rw"

	if len(specParts) == 2 {
		mode = specParts[1]
		if !ValidMountMode(mode) {
			return "", "", errInvalidMode(mode)
		}
		// For now don't allow propagation properties while importing
		// volumes from data container. These volumes will inherit
		// the same propagation property as of the original volume
		// in data container. This probably can be relaxed in future.
		if HasPropagation(mode) {
			return "", "", errInvalidMode(mode)
		}
		// Do not allow copy modes on volumes-from
		if _, isSet := getCopyMode(mode); isSet {
			return "", "", errInvalidMode(mode)
		}
	}
	return id, mode, nil
}

// ParseMountRaw parses a raw volume spec (e.g. `-v /foo:/bar:shared`) into a
// structured spec. Once the raw spec is parsed it relies on `ParseMountSpec` to
// validate the spec and create a MountPoint
func ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
	arr, err := splitRawSpec(convertSlash(raw))
	if err != nil {
		return nil, err
	}

	var spec mounttypes.Mount
	var mode string
	switch len(arr) {
	case 1:
		// Just a destination path in the container
		spec.Target = arr[0]
	case 2:
		if ValidMountMode(arr[1]) {
			// Destination + Mode is not a valid volume - volumes
			// cannot include a mode. e.g. /foo:rw
			return nil, errInvalidSpec(raw)
		}
		// Host Source Path or Name + Destination
		spec.Source = arr[0]
		spec.Target = arr[1]
	case 3:
		// HostSourcePath+DestinationPath+Mode
		spec.Source = arr[0]
		spec.Target = arr[1]
		mode = arr[2]
	default:
		return nil, errInvalidSpec(raw)
	}

	if !ValidMountMode(mode) {
		return nil, errInvalidMode(mode)
	}

	if filepath.IsAbs(spec.Source) {
		spec.Type = mounttypes.TypeBind
	} else {
		spec.Type = mounttypes.TypeVolume
	}

	spec.ReadOnly = !ReadWrite(mode)

	// cannot assume that if a volume driver is passed in that we should set it
	if volumeDriver != "" && spec.Type == mounttypes.TypeVolume {
		spec.VolumeOptions = &mounttypes.VolumeOptions{
			DriverConfig: &mounttypes.Driver{Name: volumeDriver},
		}
	}

	if copyData, isSet := getCopyMode(mode); isSet {
		if spec.VolumeOptions == nil {
			spec.VolumeOptions = &mounttypes.VolumeOptions{}
		}
		spec.VolumeOptions.NoCopy = !copyData
	}
	if HasPropagation(mode) {
		spec.BindOptions = &mounttypes.BindOptions{
			Propagation: GetPropagation(mode),
		}
	}

	mp, err := ParseMountSpec(spec, platformRawValidationOpts...)
	if mp != nil {
		mp.Mode = mode
	}
	if err != nil {
		err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err)
	}
	return mp, err
}

// ParseMountSpec reads a mount config, validates it, and configures a mountpoint from it.
func ParseMountSpec(cfg mounttypes.Mount, options ...func(*validateOpts)) (*MountPoint, error) {
	if err := validateMountConfig(&cfg, options...); err != nil {
		return nil, err
	}
	mp := &MountPoint{
		RW:          !cfg.ReadOnly,
		Destination: clean(convertSlash(cfg.Target)),
		Type:        cfg.Type,
		Spec:        cfg,
	}

	switch cfg.Type {
	case mounttypes.TypeVolume:
		if cfg.Source == "" {
			mp.Name = stringid.GenerateNonCryptoID()
		} else {
			mp.Name = cfg.Source
		}
		mp.CopyData = DefaultCopyMode

		if cfg.VolumeOptions != nil {
			if cfg.VolumeOptions.DriverConfig != nil {
				mp.Driver = cfg.VolumeOptions.DriverConfig.Name
			}
			if cfg.VolumeOptions.NoCopy {
				mp.CopyData = false
			}
		}
	case mounttypes.TypeBind:
		mp.Source = clean(convertSlash(cfg.Source))
		if cfg.BindOptions != nil {
			if len(cfg.BindOptions.Propagation) > 0 {
				mp.Propagation = cfg.BindOptions.Propagation
			}
		}
	case mounttypes.TypeTmpfs:
		// NOP
	}
	return mp, nil
}

func errInvalidMode(mode string) error {
	return fmt.Errorf("invalid mode: %v", mode)
}

func errInvalidSpec(spec string) error {
	return fmt.Errorf("invalid volume specification: '%s'", spec)
}
<commit_msg>We need to fix labels if the user requests on volumes<commit_after>package volume

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

	mounttypes "github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/stringid"
	"github.com/opencontainers/runc/libcontainer/label"
	"github.com/pkg/errors"
)

// DefaultDriverName is the driver name used for the driver
// implemented in the local package.
const DefaultDriverName = "local"

// Scopes define if a volume has is cluster-wide (global) or local only.
// Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume
const (
	LocalScope  = "local"
	GlobalScope = "global"
)

// Driver is for creating and removing volumes.
type Driver interface {
	// Name returns the name of the volume driver.
	Name() string
	// Create makes a new volume with the given name.
	Create(name string, opts map[string]string) (Volume, error)
	// Remove deletes the volume.
	Remove(vol Volume) (err error)
	// List lists all the volumes the driver has
	List() ([]Volume, error)
	// Get retrieves the volume with the requested name
	Get(name string) (Volume, error)
	// Scope returns the scope of the driver (e.g. `global` or `local`).
	// Scope determines how the driver is handled at a cluster level
	Scope() string
}

// Capability defines a set of capabilities that a driver is able to handle.
type Capability struct {
	// Scope is the scope of the driver, `global` or `local`
	// A `global` scope indicates that the driver manages volumes across the cluster
	// A `local` scope indicates that the driver only manages volumes resources local to the host
	// Scope is declared by the driver
	Scope string
}

// Volume is a place to store data. It is backed by a specific driver, and can be mounted.
type Volume interface {
	// Name returns the name of the volume
	Name() string
	// DriverName returns the name of the driver which owns this volume.
	DriverName() string
	// Path returns the absolute path to the volume.
	Path() string
	// Mount mounts the volume and returns the absolute path to
	// where it can be consumed.
	Mount(id string) (string, error)
	// Unmount unmounts the volume when it is no longer in use.
	Unmount(id string) error
	// Status returns low-level status information about a volume
	Status() map[string]interface{}
}

// DetailedVolume wraps a Volume with user-defined labels, options, and cluster scope (e.g., `local` or `global`)
type DetailedVolume interface {
	Labels() map[string]string
	Options() map[string]string
	Scope() string
	Volume
}

// MountPoint is the intersection point between a volume and a container. It
// specifies which volume is to be used and where inside a container it should
// be mounted.
type MountPoint struct {
	// Source is the source path of the mount.
	// E.g. `mount --bind /foo /bar`, `/foo` is the `Source`.
	Source string
	// Destination is the path relative to the container root (`/`) to the mount point
	// It is where the `Source` is mounted to
	Destination string
	// RW is set to true when the mountpoint should be mounted as read-write
	RW bool
	// Name is the name reference to the underlying data defined by `Source`
	// e.g., the volume name
	Name string
	// Driver is the volume driver used to create the volume (if it is a volume)
	Driver string
	// Type of mount to use, see `Type<foo>` definitions in github.com/docker/docker/api/types/mount
	Type mounttypes.Type `json:",omitempty"`
	// Volume is the volume providing data to this mountpoint.
	// This is nil unless `Type` is set to `TypeVolume`
	Volume Volume `json:"-"`

	// Mode is the comma separated list of options supplied by the user when creating
	// the bind/volume mount.
	// Note Mode is not used on Windows
	Mode string `json:"Relabel,omitempty"` // Originally field was `Relabel`"

	// Propagation describes how the mounts are propagated from the host into the
	// mount point, and vice-versa.
	// See https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
	// Note Propagation is not used on Windows
	Propagation mounttypes.Propagation `json:",omitempty"` // Mount propagation string

	// Specifies if data should be copied from the container before the first mount
	// Use a pointer here so we can tell if the user set this value explicitly
	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
	CopyData bool `json:"-"`
	// ID is the opaque ID used to pass to the volume driver.
	// This should be set by calls to `Mount` and unset by calls to `Unmount`
	ID string `json:",omitempty"`

	// Sepc is a copy of the API request that created this mount.
	Spec mounttypes.Mount
}

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *MountPoint) Setup(mountLabel string, rootUID, rootGID int) (path string, err error) {
	defer func() {
		if err == nil {
			if label.RelabelNeeded(m.Mode) {
				if err = label.Relabel(m.Source, mountLabel, label.IsShared(m.Mode)); err != nil {
					path = ""
					err = errors.Wrapf(err, "error setting label on mount source '%s'", m.Source)
					return
				}
			}
		}
		return
	}()

	if m.Volume != nil {
		id := m.ID
		if id == "" {
			id = stringid.GenerateNonCryptoID()
		}
		path, err := m.Volume.Mount(id)
		if err != nil {
			return "", errors.Wrapf(err, "error while mounting volume '%s'", m.Source)
		}
		m.ID = id
		return path, nil
	}
	if len(m.Source) == 0 {
		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
	}
	// system.MkdirAll() produces an error if m.Source exists and is a file (not a directory),
	if m.Type == mounttypes.TypeBind {
		// idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory)
		// also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it
		if err := idtools.MkdirAllNewAs(m.Source, 0755, rootUID, rootGID); err != nil {
			if perr, ok := err.(*os.PathError); ok {
				if perr.Err != syscall.ENOTDIR {
					return "", errors.Wrapf(err, "error while creating mount source path '%s'", m.Source)
				}
			}
		}
	}
	return m.Source, nil
}

// Path returns the path of a volume in a mount point.
func (m *MountPoint) Path() string {
	if m.Volume != nil {
		return m.Volume.Path()
	}
	return m.Source
}

// ParseVolumesFrom ensures that the supplied volumes-from is valid.
func ParseVolumesFrom(spec string) (string, string, error) {
	if len(spec) == 0 {
		return "", "", fmt.Errorf("volumes-from specification cannot be an empty string")
	}

	specParts := strings.SplitN(spec, ":", 2)
	id := specParts[0]
	mode := "rw"

	if len(specParts) == 2 {
		mode = specParts[1]
		if !ValidMountMode(mode) {
			return "", "", errInvalidMode(mode)
		}
		// For now don't allow propagation properties while importing
		// volumes from data container. These volumes will inherit
		// the same propagation property as of the original volume
		// in data container. This probably can be relaxed in future.
		if HasPropagation(mode) {
			return "", "", errInvalidMode(mode)
		}
		// Do not allow copy modes on volumes-from
		if _, isSet := getCopyMode(mode); isSet {
			return "", "", errInvalidMode(mode)
		}
	}
	return id, mode, nil
}

// ParseMountRaw parses a raw volume spec (e.g. `-v /foo:/bar:shared`) into a
// structured spec. Once the raw spec is parsed it relies on `ParseMountSpec` to
// validate the spec and create a MountPoint
func ParseMountRaw(raw, volumeDriver string) (*MountPoint, error) {
	arr, err := splitRawSpec(convertSlash(raw))
	if err != nil {
		return nil, err
	}

	var spec mounttypes.Mount
	var mode string
	switch len(arr) {
	case 1:
		// Just a destination path in the container
		spec.Target = arr[0]
	case 2:
		if ValidMountMode(arr[1]) {
			// Destination + Mode is not a valid volume - volumes
			// cannot include a mode. e.g. /foo:rw
			return nil, errInvalidSpec(raw)
		}
		// Host Source Path or Name + Destination
		spec.Source = arr[0]
		spec.Target = arr[1]
	case 3:
		// HostSourcePath+DestinationPath+Mode
		spec.Source = arr[0]
		spec.Target = arr[1]
		mode = arr[2]
	default:
		return nil, errInvalidSpec(raw)
	}

	if !ValidMountMode(mode) {
		return nil, errInvalidMode(mode)
	}

	if filepath.IsAbs(spec.Source) {
		spec.Type = mounttypes.TypeBind
	} else {
		spec.Type = mounttypes.TypeVolume
	}

	spec.ReadOnly = !ReadWrite(mode)

	// cannot assume that if a volume driver is passed in that we should set it
	if volumeDriver != "" && spec.Type == mounttypes.TypeVolume {
		spec.VolumeOptions = &mounttypes.VolumeOptions{
			DriverConfig: &mounttypes.Driver{Name: volumeDriver},
		}
	}

	if copyData, isSet := getCopyMode(mode); isSet {
		if spec.VolumeOptions == nil {
			spec.VolumeOptions = &mounttypes.VolumeOptions{}
		}
		spec.VolumeOptions.NoCopy = !copyData
	}
	if HasPropagation(mode) {
		spec.BindOptions = &mounttypes.BindOptions{
			Propagation: GetPropagation(mode),
		}
	}

	mp, err := ParseMountSpec(spec, platformRawValidationOpts...)
	if mp != nil {
		mp.Mode = mode
	}
	if err != nil {
		err = fmt.Errorf("%v: %v", errInvalidSpec(raw), err)
	}
	return mp, err
}

// ParseMountSpec reads a mount config, validates it, and configures a mountpoint from it.
func ParseMountSpec(cfg mounttypes.Mount, options ...func(*validateOpts)) (*MountPoint, error) {
	if err := validateMountConfig(&cfg, options...); err != nil {
		return nil, err
	}
	mp := &MountPoint{
		RW:          !cfg.ReadOnly,
		Destination: clean(convertSlash(cfg.Target)),
		Type:        cfg.Type,
		Spec:        cfg,
	}

	switch cfg.Type {
	case mounttypes.TypeVolume:
		if cfg.Source == "" {
			mp.Name = stringid.GenerateNonCryptoID()
		} else {
			mp.Name = cfg.Source
		}
		mp.CopyData = DefaultCopyMode

		if cfg.VolumeOptions != nil {
			if cfg.VolumeOptions.DriverConfig != nil {
				mp.Driver = cfg.VolumeOptions.DriverConfig.Name
			}
			if cfg.VolumeOptions.NoCopy {
				mp.CopyData = false
			}
		}
	case mounttypes.TypeBind:
		mp.Source = clean(convertSlash(cfg.Source))
		if cfg.BindOptions != nil {
			if len(cfg.BindOptions.Propagation) > 0 {
				mp.Propagation = cfg.BindOptions.Propagation
			}
		}
	case mounttypes.TypeTmpfs:
		// NOP
	}
	return mp, nil
}

func errInvalidMode(mode string) error {
	return fmt.Errorf("invalid mode: %v", mode)
}

func errInvalidSpec(spec string) error {
	return fmt.Errorf("invalid volume specification: '%s'", spec)
}
<|endoftext|>