<commit_before>package gautomator

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"

	"github.com/gorilla/mux"
)

type Route struct {
	Name        string
	Method      string
	Pattern     string
	HandlerFunc http.HandlerFunc
}

type Routes []Route

func displaySvg(w http.ResponseWriter, r *http.Request, taskStructure *TaskGraphStructure) {
	subProcess := exec.Command("dot", "-Tsvg")

	stdin, err := subProcess.StdinPipe()
	if err != nil {
		fmt.Println(err) //replace with logger, or anything you want
	}
	defer stdin.Close() // the doc says subProcess.Wait will close it, but I'm not sure, so I kept this line

	subProcess.Stdout = w
	subProcess.Stderr = os.Stderr
	if err = subProcess.Start(); err != nil { //Use start, not run
		fmt.Println("An error occured: ", err) //replace with logger, or anything you want

	}
	io.WriteString(stdin, "digraph G {\n")
	io.WriteString(stdin, " a->b\n")
	io.WriteString(stdin, "}\n")
	// Command was successful
	stdin.Close()
	subProcess.Wait()

}

// Courtesy of http://stackoverflow.com/questions/26211954/how-do-i-pass-arguments-to-my-handler
func showTasks(w http.ResponseWriter, r *http.Request, taskStructure *TaskGraphStructure) {
	sigmaStructure := GetSigmaStructure(taskStructure)
	//	jsonOutput, _ := json.Marshal(sigmaStructure)
	json.NewEncoder(w).Encode(sigmaStructure)
}

func NewRouter(taskStruct *TaskGraphStructure) *mux.Router {

	router := mux.NewRouter().StrictSlash(true)
	router.Headers("Content-Type", "application/json", "X-Requested-With", "XMLHttpRequest")
	router.Methods("GET").Path("/svg").Name("TaskIndex").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		displaySvg(w, r, taskStruct)
	})
	router.Methods("GET").Path("/tasks").Name("TaskIndex").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		showTasks(w, r, taskStruct)
	})
	router.PathPrefix("/").Handler(http.FileServer(http.Dir("../htdocs/static/")))

	/*
		for _, route := range routes {
			router.
				Methods(route.Method).
				Path(route.Pattern).
				Name(route.Name).
				Handler(route.HandlerFunc)
		}
	*/
	return router
}

/*
var routes = Routes{
	Route{
		"Index",
		"GET",
		"/",
		Index,
	},
		Route{
			"TaskIndex",
			"GET",
			"/tasks",
			showTasks,
		},
	Route{
		"TaskShow",
		"GET",
		"/tasks/{Name}",
		TaskShow,
	},
}
*/
<commit_msg>If dot is present, the /svg get the dot representation of the task structure and then it is written on the web io.Writer. To be simple: GET /svg display the svg representing the structure<commit_after>package gautomator

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"os/exec"

	"github.com/gorilla/mux"
)

type Route struct {
	Name        string
	Method      string
	Pattern     string
	HandlerFunc http.HandlerFunc
}

type Routes []Route

func displaySvg(w http.ResponseWriter, r *http.Request, taskStructure *TaskGraphStructure) {
	subProcess := exec.Command("dot", "-Tsvg")

	stdin, err := subProcess.StdinPipe()
	if err != nil {
		fmt.Println(err) //replace with logger, or anything you want
	}
	defer stdin.Close() // the doc says subProcess.Wait will close it, but I'm not sure, so I kept this line

	subProcess.Stdout = w
	subProcess.Stderr = os.Stderr
	if err = subProcess.Start(); err != nil { //Use start, not run
		fmt.Println("An error occured: ", err) //replace with logger, or anything you want

	}
	taskStructure.PrintDot(stdin)
	//io.WriteString(stdin, "digraph G {\n")
	//io.WriteString(stdin, " a->b\n")
	//io.WriteString(stdin, "}\n")
	// Command was successful
	stdin.Close()
	subProcess.Wait()

}

// Courtesy of http://stackoverflow.com/questions/26211954/how-do-i-pass-arguments-to-my-handler
func showTasks(w http.ResponseWriter, r *http.Request, taskStructure *TaskGraphStructure) {
	sigmaStructure := GetSigmaStructure(taskStructure)
	//	jsonOutput, _ := json.Marshal(sigmaStructure)
	json.NewEncoder(w).Encode(sigmaStructure)
}

func NewRouter(taskStruct *TaskGraphStructure) *mux.Router {

	router := mux.NewRouter().StrictSlash(true)
	router.Headers("Content-Type", "application/json", "X-Requested-With", "XMLHttpRequest")
	router.Methods("GET").Path("/svg").Name("TaskIndex").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		displaySvg(w, r, taskStruct)
	})
	router.Methods("GET").Path("/tasks").Name("TaskIndex").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		showTasks(w, r, taskStruct)
	})
	router.PathPrefix("/").Handler(http.FileServer(http.Dir("../htdocs/static/")))

	/*
		for _, route := range routes {
			router.
				Methods(route.Method).
				Path(route.Pattern).
				Name(route.Name).
				Handler(route.HandlerFunc)
		}
	*/
	return router
}

/*
var routes = Routes{
	Route{
		"Index",
		"GET",
		"/",
		Index,
	},
		Route{
			"TaskIndex",
			"GET",
			"/tasks",
			showTasks,
		},
	Route{
		"TaskShow",
		"GET",
		"/tasks/{Name}",
		TaskShow,
	},
}
*/
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"io/ioutil"
)

type userProfileReq struct {
	ApiKey string
	Id     string
}

func printError(msg string, rw http.ResponseWriter) {
    rw.WriteHeader(500)
    fmt.Fprintf(rw, "{\"status\":{\"code\":500,\"description\":\"%s\"}}\n", msg)
}

func handleUserProfile(res http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	var u userProfileReq
	err := decoder.Decode(&u)
	if err != nil {
		log.Println(err.Error())
        errorDesc := "Error: unable to parse request"
        printError(errorDesc, res)
        return
	}

	log.Println("apiKey =",u.ApiKey)
	log.Println("id =",u.Id)
	apiKey := u.ApiKey
	id := u.Id
    // TODO: implement isApiKeyValid(apiKey)
	if apiKey != "dd06058e-f32a-4e11-b14b-85a2f98ea523" {
        errorDesc := "Error: invalid apiKey"
        printError(errorDesc, res)
        return
	}

    // TODO: implement lookupUserProfile(id)
    if id != "abcxyz123" {
        errorDesc := "Error: unable to parse request"
        printError(errorDesc, res)
        return
	}

	// no input errors
	buf, err := ioutil.ReadFile("/home/tirto/go/src/github.com/tirto/webserver/userProfile.json")
	if err != nil {
        errorDesc := "Error: unable to lookup userProfile"
        printError(errorDesc, res)
        return
	}
	fmt.Fprintf(res, string(buf))
}

func main() {
	http.HandleFunc("/AudService/v1/user/profile", handleUserProfile)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>formatted<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

type userProfileReq struct {
	ApiKey string
	Id     string
}

func printError(msg string, rw http.ResponseWriter) {
	rw.WriteHeader(500)
	fmt.Fprintf(rw, "{\"status\":{\"code\":500,\"description\":\"%s\"}}\n", msg)
}

func handleUserProfile(res http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	var u userProfileReq
	err := decoder.Decode(&u)
	if err != nil {
		log.Println(err.Error())
		errorDesc := "Error: unable to parse request"
		printError(errorDesc, res)
		return
	}

	log.Println("apiKey =", u.ApiKey)
	log.Println("id =", u.Id)
	apiKey := u.ApiKey
	id := u.Id
	// TODO: implement isApiKeyValid(apiKey)
	if apiKey != "dd06058e-f32a-4e11-b14b-85a2f98ea523" {
		errorDesc := "Error: invalid apiKey"
		printError(errorDesc, res)
		return
	}

	// TODO: implement lookupUserProfile(id)
	if id != "abcxyz123" {
		errorDesc := "Error: unable to parse request"
		printError(errorDesc, res)
		return
	}

	// no input errors
	buf, err := ioutil.ReadFile("/home/tirto/go/src/github.com/tirto/webserver/userProfile.json")
	if err != nil {
		errorDesc := "Error: unable to lookup userProfile"
		printError(errorDesc, res)
		return
	}
	fmt.Fprintf(res, string(buf))
}

func main() {
	http.HandleFunc("/AudService/v1/user/profile", handleUserProfile)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


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

package storage

import (
	"fmt"

	"github.com/google/logger"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

// Volume represents a MSFT_Volume object.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume
type Volume struct {
	DriveLetter     string
	Path            string
	HealthStatus    int32
	FileSystem      string
	FileSystemLabel string
	FileSystemType  int32
	Size            uint64
	SizeRemaining   uint64
	DriveType       int32
	DedupMode       int32

	handle *ole.IDispatch
}

// Close releases the handle to the volume.
func (v *Volume) Close() {
	if v.handle != nil {
		v.handle.Release()
	}
}

// Flush flushes the cached data in the volume's file system to disk.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume-flush
func (v *Volume) Flush() error {
	res, err := oleutil.CallMethod(v.handle, "Flush")
	if err != nil {
		return fmt.Errorf("Flush: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return fmt.Errorf("error code returned during flush: %d", val)
	}
	return nil
}

// FormatFAT32 is a helper for calling Format using only the options supported by FAT32.
func (v *Volume) FormatFAT32(label string, allocationUnitSize int32, full, force bool) (Volume, ExtendedStatus, error) {
	return v.Format("FAT32", label, allocationUnitSize, full, force, nil, nil, nil, nil, nil)
}

// FormatNTFS is a helper for calling Format using only the options supported by NTFS.
func (v *Volume) FormatNTFS(label string, allocationUnitSize int32, full, force, compress, shortFileNameSupport, useLargeFRS, disableHeatGathering bool) (Volume, ExtendedStatus, error) {
	return v.Format("NTFS", label, allocationUnitSize, full, force, compress, shortFileNameSupport, nil, useLargeFRS, disableHeatGathering)
}

// FormatReFS is a helper for calling Format using only the options supported by ReFS.
func (v *Volume) FormatReFS(label string, allocationUnitSize int32, full, force, setIntegrityStreams, disableHeatGathering bool) (Volume, ExtendedStatus, error) {
	return v.Format("ReFS", label, allocationUnitSize, full, force, nil, nil, setIntegrityStreams, nil, disableHeatGathering)
}

// Format formats a volume.
//
// You may want to use one of the filesystem-specific helpers instead of calling this directly.
//
// fs can be one of "ExFAT", "FAT", "FAT32", "NTFS", "ReFS". Set allocationUnitSize to 0 for default.
//
// Note: The Windows API requires any parameters not supported by a given filesystem to be nil (NOT zero value).
// To enable this here, any non-universal parameters are implemented as interfaces and must be passed as either the
// correct type for the underlying API field or nil. Attempting to pass a field to a filesystem that doesn't
// support it will result in a vague and unhelpful code 1 (unsupported) from the API.
//
// If successful, the formatted volume is returned as a new Volume object. Close() must be called on the new Volume.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/format-msft-volume
func (v *Volume) Format(
	fs string,
	fsLabel string,
	allocationUnitSize int32,
	full bool,
	force bool,
	compress interface{},
	shortFileNameSupport interface{},
	setIntegrityStreams interface{},
	useLargeFRS interface{},
	disableHeatGathering interface{}) (Volume, ExtendedStatus, error) {
	vol := Volume{}
	stat := ExtendedStatus{}

	var extendedStatus ole.VARIANT
	ole.VariantInit(&extendedStatus)
	var formattedVolume ole.VARIANT
	ole.VariantInit(&formattedVolume)

	var ialloc interface{}
	if allocationUnitSize != 0 {
		ialloc = allocationUnitSize
	} else {
		ialloc = nil
	}

	res, err := oleutil.CallMethod(v.handle, "Format",
		fs,
		fsLabel,
		ialloc,
		full,
		force,
		compress,
		shortFileNameSupport,
		setIntegrityStreams,
		useLargeFRS,
		disableHeatGathering,
		&formattedVolume, &extendedStatus) // outputs
	if err != nil {
		return vol, stat, fmt.Errorf("Format: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return vol, stat, fmt.Errorf("error code returned during formatting: %d", val)
	}

	// TODO(mattl): figure out why this handle is invalid
	vol.handle = formattedVolume.ToIDispatch()

	return vol, stat, nil
}

// Optimize optimizes the volume.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/optimize-msft-volume
func (v *Volume) Optimize(reTrim, analyze, defrag, slabConslidate, tierOptimize bool) (ExtendedStatus, error) {
	stat := ExtendedStatus{}
	var extendedStatus ole.VARIANT
	ole.VariantInit(&extendedStatus)

	res, err := oleutil.CallMethod(v.handle, "Optimize", reTrim, analyze, defrag, slabConslidate, tierOptimize, &extendedStatus)
	if err != nil {
		return stat, fmt.Errorf("Optimize: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return stat, fmt.Errorf("error code returned during optimization: %d", val)
	}
	return stat, nil
}

// SetFileSystemLabel Sets the file system label for the volume.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume-setfilesystemlabel
func (v *Volume) SetFileSystemLabel(fileSystemLabel string) (ExtendedStatus, error) {
	stat := ExtendedStatus{}
	var extendedStatus ole.VARIANT
	ole.VariantInit(&extendedStatus)

	res, err := oleutil.CallMethod(v.handle, "SetFileSystemLabel", fileSystemLabel, &extendedStatus)
	if err != nil {
		return stat, fmt.Errorf("SetFileSystemLabel: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return stat, fmt.Errorf("error code returned during setting file system label: %d", val)
	}
	return stat, nil
}

// Query reads and populates the volume state.
func (v *Volume) Query() error {
	if v.handle == nil {
		return fmt.Errorf("invalid handle")
	}

	// DriveLetter
	p, err := oleutil.GetProperty(v.handle, "DriveLetter")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(DriveLetter): %w", err)
	}
	// DriveLetter is represented as Char16 (Ascii)
	v.DriveLetter = string(rune(p.Val))

	// Path
	p, err = oleutil.GetProperty(v.handle, "Path")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(Path): %w", err)
	}
	v.Path = p.ToString()

	// FileSystem
	p, err = oleutil.GetProperty(v.handle, "FileSystem")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(FileSystem): %w", err)
	}
	v.FileSystem = p.ToString()

	// FileSystemLabel
	p, err = oleutil.GetProperty(v.handle, "FileSystemLabel")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(FileSystemLabel): %w", err)
	}
	v.FileSystemLabel = p.ToString()

	// All the non-strings
	for _, p := range [][]interface{}{
		[]interface{}{"HealthStatus", &v.HealthStatus},
		[]interface{}{"FileSystemType", &v.FileSystemType},
		[]interface{}{"Size", &v.Size},
		[]interface{}{"SizeRemaining", &v.SizeRemaining},
		[]interface{}{"DriveType", &v.DriveType},
		[]interface{}{"DedupMode", &v.DedupMode},
	} {
		prop, err := oleutil.GetProperty(v.handle, p[0].(string))
		if err != nil {
			return fmt.Errorf("oleutil.GetProperty(%s): %w", p[0].(string), err)
		}
		if err := assignVariant(prop.Value(), p[1]); err != nil {
			logger.Warningf("assignVariant(%s): %v", p[0].(string), err)
		}
	}
	return nil
}

// A VolumeSet contains one or more Volumes.
type VolumeSet struct {
	Volumes []Volume
}

// Close releases all Volume handles inside a VolumeSet.
func (s *VolumeSet) Close() {
	for _, v := range s.Volumes {
		v.Close()
	}
}

// GetVolumes queries for local volumes.
//
// Close() must be called on the resulting VolumeSet to ensure all volumes are released.
//
// Get all volumes:
//		svc.GetVolumes("")
//
// To get specific volumes, provide a valid WMI query filter string, for example:
//		svc.GetVolumes("WHERE DriveLetter=D")
func (svc Service) GetVolumes(filter string) (VolumeSet, error) {
	vset := VolumeSet{}
	query := "SELECT * FROM MSFT_Volume"
	if filter != "" {
		query = fmt.Sprintf("%s %s", query, filter)
	}

	logger.V(1).Info(query)
	raw, err := oleutil.CallMethod(svc.wmiSvc, "ExecQuery", query)
	if err != nil {
		return vset, fmt.Errorf("ExecQuery(%s): %w", query, err)
	}
	result := raw.ToIDispatch()
	defer result.Release()

	countVar, err := oleutil.GetProperty(result, "Count")
	if err != nil {
		return vset, fmt.Errorf("oleutil.GetProperty(Count): %w", err)
	}
	count := int(countVar.Val)

	for i := 0; i < count; i++ {
		v := Volume{}
		itemRaw, err := oleutil.CallMethod(result, "ItemIndex", i)
		if err != nil {
			return vset, fmt.Errorf("oleutil.CallMethod(ItemIndex, %d): %w", i, err)
		}
		v.handle = itemRaw.ToIDispatch()

		if err := v.Query(); err != nil {
			return vset, err
		}

		vset.Volumes = append(vset.Volumes, v)
	}

	return vset, nil
}
<commit_msg>Add a table for DriveTypes ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
	"fmt"

	"github.com/google/logger"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

// Volume represents a MSFT_Volume object.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume
type Volume struct {
	DriveLetter     string
	Path            string
	HealthStatus    int32
	FileSystem      string
	FileSystemLabel string
	FileSystemType  int32
	Size            uint64
	SizeRemaining   uint64
	DriveType       int32
	DedupMode       int32

	handle *ole.IDispatch
}

// Close releases the handle to the volume.
func (v *Volume) Close() {
	if v.handle != nil {
		v.handle.Release()
	}
}

// Flush flushes the cached data in the volume's file system to disk.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume-flush
func (v *Volume) Flush() error {
	res, err := oleutil.CallMethod(v.handle, "Flush")
	if err != nil {
		return fmt.Errorf("Flush: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return fmt.Errorf("error code returned during flush: %d", val)
	}
	return nil
}

// FormatFAT32 is a helper for calling Format using only the options supported by FAT32.
func (v *Volume) FormatFAT32(label string, allocationUnitSize int32, full, force bool) (Volume, ExtendedStatus, error) {
	return v.Format("FAT32", label, allocationUnitSize, full, force, nil, nil, nil, nil, nil)
}

// FormatNTFS is a helper for calling Format using only the options supported by NTFS.
func (v *Volume) FormatNTFS(label string, allocationUnitSize int32, full, force, compress, shortFileNameSupport, useLargeFRS, disableHeatGathering bool) (Volume, ExtendedStatus, error) {
	return v.Format("NTFS", label, allocationUnitSize, full, force, compress, shortFileNameSupport, nil, useLargeFRS, disableHeatGathering)
}

// FormatReFS is a helper for calling Format using only the options supported by ReFS.
func (v *Volume) FormatReFS(label string, allocationUnitSize int32, full, force, setIntegrityStreams, disableHeatGathering bool) (Volume, ExtendedStatus, error) {
	return v.Format("ReFS", label, allocationUnitSize, full, force, nil, nil, setIntegrityStreams, nil, disableHeatGathering)
}

// Format formats a volume.
//
// You may want to use one of the filesystem-specific helpers instead of calling this directly.
//
// fs can be one of "ExFAT", "FAT", "FAT32", "NTFS", "ReFS". Set allocationUnitSize to 0 for default.
//
// Note: The Windows API requires any parameters not supported by a given filesystem to be nil (NOT zero value).
// To enable this here, any non-universal parameters are implemented as interfaces and must be passed as either the
// correct type for the underlying API field or nil. Attempting to pass a field to a filesystem that doesn't
// support it will result in a vague and unhelpful code 1 (unsupported) from the API.
//
// If successful, the formatted volume is returned as a new Volume object. Close() must be called on the new Volume.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/format-msft-volume
func (v *Volume) Format(
	fs string,
	fsLabel string,
	allocationUnitSize int32,
	full bool,
	force bool,
	compress interface{},
	shortFileNameSupport interface{},
	setIntegrityStreams interface{},
	useLargeFRS interface{},
	disableHeatGathering interface{}) (Volume, ExtendedStatus, error) {
	vol := Volume{}
	stat := ExtendedStatus{}

	var extendedStatus ole.VARIANT
	ole.VariantInit(&extendedStatus)
	var formattedVolume ole.VARIANT
	ole.VariantInit(&formattedVolume)

	var ialloc interface{}
	if allocationUnitSize != 0 {
		ialloc = allocationUnitSize
	} else {
		ialloc = nil
	}

	res, err := oleutil.CallMethod(v.handle, "Format",
		fs,
		fsLabel,
		ialloc,
		full,
		force,
		compress,
		shortFileNameSupport,
		setIntegrityStreams,
		useLargeFRS,
		disableHeatGathering,
		&formattedVolume, &extendedStatus) // outputs
	if err != nil {
		return vol, stat, fmt.Errorf("Format: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return vol, stat, fmt.Errorf("error code returned during formatting: %d", val)
	}

	// TODO(mattl): figure out why this handle is invalid
	vol.handle = formattedVolume.ToIDispatch()

	return vol, stat, nil
}

// Optimize optimizes the volume.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/optimize-msft-volume
func (v *Volume) Optimize(reTrim, analyze, defrag, slabConslidate, tierOptimize bool) (ExtendedStatus, error) {
	stat := ExtendedStatus{}
	var extendedStatus ole.VARIANT
	ole.VariantInit(&extendedStatus)

	res, err := oleutil.CallMethod(v.handle, "Optimize", reTrim, analyze, defrag, slabConslidate, tierOptimize, &extendedStatus)
	if err != nil {
		return stat, fmt.Errorf("Optimize: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return stat, fmt.Errorf("error code returned during optimization: %d", val)
	}
	return stat, nil
}

// SetFileSystemLabel Sets the file system label for the volume.
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume-setfilesystemlabel
func (v *Volume) SetFileSystemLabel(fileSystemLabel string) (ExtendedStatus, error) {
	stat := ExtendedStatus{}
	var extendedStatus ole.VARIANT
	ole.VariantInit(&extendedStatus)

	res, err := oleutil.CallMethod(v.handle, "SetFileSystemLabel", fileSystemLabel, &extendedStatus)
	if err != nil {
		return stat, fmt.Errorf("SetFileSystemLabel: %w", err)
	} else if val, ok := res.Value().(int32); val != 0 || !ok {
		return stat, fmt.Errorf("error code returned during setting file system label: %d", val)
	}
	return stat, nil
}

// Query reads and populates the volume state.
func (v *Volume) Query() error {
	if v.handle == nil {
		return fmt.Errorf("invalid handle")
	}

	// DriveLetter
	p, err := oleutil.GetProperty(v.handle, "DriveLetter")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(DriveLetter): %w", err)
	}
	// DriveLetter is represented as Char16 (Ascii)
	v.DriveLetter = string(rune(p.Val))

	// Path
	p, err = oleutil.GetProperty(v.handle, "Path")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(Path): %w", err)
	}
	v.Path = p.ToString()

	// FileSystem
	p, err = oleutil.GetProperty(v.handle, "FileSystem")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(FileSystem): %w", err)
	}
	v.FileSystem = p.ToString()

	// FileSystemLabel
	p, err = oleutil.GetProperty(v.handle, "FileSystemLabel")
	if err != nil {
		return fmt.Errorf("oleutil.GetProperty(FileSystemLabel): %w", err)
	}
	v.FileSystemLabel = p.ToString()

	// All the non-strings
	for _, p := range [][]interface{}{
		[]interface{}{"HealthStatus", &v.HealthStatus},
		[]interface{}{"FileSystemType", &v.FileSystemType},
		[]interface{}{"Size", &v.Size},
		[]interface{}{"SizeRemaining", &v.SizeRemaining},
		[]interface{}{"DriveType", &v.DriveType},
		[]interface{}{"DedupMode", &v.DedupMode},
	} {
		prop, err := oleutil.GetProperty(v.handle, p[0].(string))
		if err != nil {
			return fmt.Errorf("oleutil.GetProperty(%s): %w", p[0].(string), err)
		}
		if err := assignVariant(prop.Value(), p[1]); err != nil {
			logger.Warningf("assignVariant(%s): %v", p[0].(string), err)
		}
	}
	return nil
}

// DriveType describes a Drive Type
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-volume
type DriveType int

const (
	// UnknownDriveType is a type of Drive Type
	UnknownDriveType DriveType = iota
	// Invalid is a type of Drive Type
	Invalid
	// Removable is a type of Drive Type
	Removable
	// Fixed is a type of Drive Type
	Fixed
	// Remote is a type of Drive Type
	Remote
	// CDROM is a type of Drive Type
	CDROM
	// RAM is a type of Drive Type
	RAM
)

// A VolumeSet contains one or more Volumes.
type VolumeSet struct {
	Volumes []Volume
}

// Close releases all Volume handles inside a VolumeSet.
func (s *VolumeSet) Close() {
	for _, v := range s.Volumes {
		v.Close()
	}
}

// GetVolumes queries for local volumes.
//
// Close() must be called on the resulting VolumeSet to ensure all volumes are released.
//
// Get all volumes:
//		svc.GetVolumes("")
//
// To get specific volumes, provide a valid WMI query filter string, for example:
//		svc.GetVolumes("WHERE DriveLetter=D")
func (svc Service) GetVolumes(filter string) (VolumeSet, error) {
	vset := VolumeSet{}
	query := "SELECT * FROM MSFT_Volume"
	if filter != "" {
		query = fmt.Sprintf("%s %s", query, filter)
	}

	logger.V(1).Info(query)
	raw, err := oleutil.CallMethod(svc.wmiSvc, "ExecQuery", query)
	if err != nil {
		return vset, fmt.Errorf("ExecQuery(%s): %w", query, err)
	}
	result := raw.ToIDispatch()
	defer result.Release()

	countVar, err := oleutil.GetProperty(result, "Count")
	if err != nil {
		return vset, fmt.Errorf("oleutil.GetProperty(Count): %w", err)
	}
	count := int(countVar.Val)

	for i := 0; i < count; i++ {
		v := Volume{}
		itemRaw, err := oleutil.CallMethod(result, "ItemIndex", i)
		if err != nil {
			return vset, fmt.Errorf("oleutil.CallMethod(ItemIndex, %d): %w", i, err)
		}
		v.handle = itemRaw.ToIDispatch()

		if err := v.Query(); err != nil {
			return vset, err
		}

		vset.Volumes = append(vset.Volumes, v)
	}

	return vset, nil
}
<|endoftext|>


<commit_before>package gopherjs_http

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	pathpkg "path"
	"strings"
	"time"
)

func NewFS(source http.FileSystem) http.FileSystem {
	return &gopherJSFS{source: source}
}

type gopherJSFS struct {
	source http.FileSystem
}

func (fs *gopherJSFS) Open(path string) (http.File, error) {
	switch dir, file := pathpkg.Split(path); {
	case file == pathpkg.Base(dir)+".js":
		return fs.compileGoPackage(dir)
	default:
		return fs.openSource(path)
	}
}

func (fs *gopherJSFS) openSource(path string) (http.File, error) {
	f, err := fs.source.Open(path)
	if err != nil {
		return nil, err
	}

	fi, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, err
	}

	switch {
	// Files with .go and ".inc.js" extensions are consumed and no longer exist
	// in output filesystem.
	case !fi.IsDir() && pathpkg.Ext(fi.Name()) == ".go":
		fallthrough
	case !fi.IsDir() && strings.HasSuffix(fi.Name(), ".inc.js"):
		f.Close()
		return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
	case !fi.IsDir():
		return f, nil
	}
	defer f.Close()

	fis, err := f.Readdir(0)
	if err != nil {
		return nil, err
	}

	// Include all subfolders, non-.go files.
	var entries []os.FileInfo
	var haveGo []os.FileInfo
	for _, fi := range fis {
		switch {
		case !fi.IsDir() && pathpkg.Ext(fi.Name()) == ".go":
			haveGo = append(haveGo, fi)
		case !fi.IsDir() && strings.HasSuffix(fi.Name(), ".inc.js"):
			// TODO: Handle ".inc.js" files correctly.
			entries = append(entries, fi)
		default:
			entries = append(entries, fi)
		}
	}

	// If it has any .go files, present the Go package compiled with GopherJS as an additional virtual file.
	if len(haveGo) > 0 {
		entries = append(entries, &file{
			name:    fi.Name() + ".js",
			size:    0,           // TODO.
			modTime: time.Time{}, // TODO.
		})
	}

	return &dir{
		name:    fi.Name(),
		entries: entries,
		modTime: fi.ModTime(),
	}, nil
}

func (fs *gopherJSFS) compileGoPackage(dir string) (http.File, error) {
	f, err := fs.source.Open(dir)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	if !fi.IsDir() {
		return nil, fmt.Errorf("%s is not a dir", dir)
	}

	fis, err := f.Readdir(0)
	if err != nil {
		return nil, err
	}

	var goFiles []os.FileInfo
	for _, f := range fis {
		if !f.IsDir() && pathpkg.Ext(f.Name()) == ".go" {
			goFiles = append(goFiles, f)
		}
	}
	if len(goFiles) == 0 {
		return nil, fmt.Errorf("%s has no .go files", dir)
	}

	// TODO: Clean this up.
	{
		name := pathpkg.Base(dir) + ".js"

		var names []string
		var goReaders []io.Reader
		var goClosers []io.Closer
		for _, goFile := range goFiles {
			file, err := fs.source.Open(pathpkg.Join(dir, goFile.Name()))
			if err != nil {
				return nil, err
			}
			names = append(names, goFile.Name())
			goReaders = append(goReaders, file)
			goClosers = append(goClosers, file)
		}

		fmt.Printf("REBUILDING SOURCE for: %s using %+v\n", name, names)
		content := []byte(handleJsError(goReadersToJs(names, goReaders)))

		for _, closer := range goClosers {
			closer.Close()
		}

		return &file{
			name:    name,
			size:    int64(len(content)),
			modTime: time.Now(),
			Reader:  bytes.NewReader(content),
		}, nil
	}
}

// file is an opened file instance.
type file struct {
	name    string
	modTime time.Time
	size    int64
	*bytes.Reader
}

func (f *file) Readdir(count int) ([]os.FileInfo, error) {
	return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
}
func (f *file) Stat() (os.FileInfo, error) { return f, nil }

func (f *file) Name() string       { return f.name }
func (f *file) Size() int64        { return f.size }
func (f *file) Mode() os.FileMode  { return 0444 }
func (f *file) ModTime() time.Time { return f.modTime }
func (f *file) IsDir() bool        { return false }
func (f *file) Sys() interface{}   { return nil }

func (f *file) Close() error {
	return nil
}

// dir is an opened dir instance.
type dir struct {
	name    string
	modTime time.Time
	entries []os.FileInfo
	pos     int // Position within entries for Seek and Readdir.
}

func (d *dir) Read([]byte) (int, error) {
	return 0, fmt.Errorf("cannot Read from directory %s", d.name)
}
func (d *dir) Close() error               { return nil }
func (d *dir) Stat() (os.FileInfo, error) { return d, nil }

func (d *dir) Name() string       { return d.name }
func (d *dir) Size() int64        { return 0 }
func (d *dir) Mode() os.FileMode  { return 0755 | os.ModeDir }
func (d *dir) ModTime() time.Time { return d.modTime }
func (d *dir) IsDir() bool        { return true }
func (d *dir) Sys() interface{}   { return nil }

func (d *dir) Seek(offset int64, whence int) (int64, error) {
	if offset == 0 && whence == os.SEEK_SET {
		d.pos = 0
		return 0, nil
	}
	return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
}

func (d *dir) Readdir(count int) ([]os.FileInfo, error) {
	if d.pos >= len(d.entries) && count > 0 {
		return nil, io.EOF
	}
	if count <= 0 || count > len(d.entries)-d.pos {
		count = len(d.entries) - d.pos
	}
	e := d.entries[d.pos : d.pos+count]
	d.pos += count
	return e, nil
}
<commit_msg>gopherjs_http: Document NewFS.<commit_after>package gopherjs_http

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	pathpkg "path"
	"strings"
	"time"
)

// NewFS returns an http.FileSystem that is exactly like source, except all Go packages are compiled to JavaScript with GopherJS.
//
// For example:
//
// 	/mypkg/foo.go
// 	/mypkg/bar.go
//
// Become replaced with:
//
// 	/mypkg/mypkg.js
//
// Where mypkg.js is the result of building mypkg with GopherJS.
func NewFS(source http.FileSystem) http.FileSystem {
	return &gopherJSFS{source: source}
}

type gopherJSFS struct {
	source http.FileSystem
}

func (fs *gopherJSFS) Open(path string) (http.File, error) {
	switch dir, file := pathpkg.Split(path); {
	case file == pathpkg.Base(dir)+".js":
		return fs.compileGoPackage(dir)
	default:
		return fs.openSource(path)
	}
}

func (fs *gopherJSFS) openSource(path string) (http.File, error) {
	f, err := fs.source.Open(path)
	if err != nil {
		return nil, err
	}

	fi, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, err
	}

	switch {
	// Files with .go and ".inc.js" extensions are consumed and no longer exist
	// in output filesystem.
	case !fi.IsDir() && pathpkg.Ext(fi.Name()) == ".go":
		fallthrough
	case !fi.IsDir() && strings.HasSuffix(fi.Name(), ".inc.js"):
		f.Close()
		return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
	case !fi.IsDir():
		return f, nil
	}
	defer f.Close()

	fis, err := f.Readdir(0)
	if err != nil {
		return nil, err
	}

	// Include all subfolders, non-.go files.
	var entries []os.FileInfo
	var haveGo []os.FileInfo
	for _, fi := range fis {
		switch {
		case !fi.IsDir() && pathpkg.Ext(fi.Name()) == ".go":
			haveGo = append(haveGo, fi)
		case !fi.IsDir() && strings.HasSuffix(fi.Name(), ".inc.js"):
			// TODO: Handle ".inc.js" files correctly.
			entries = append(entries, fi)
		default:
			entries = append(entries, fi)
		}
	}

	// If it has any .go files, present the Go package compiled with GopherJS as an additional virtual file.
	if len(haveGo) > 0 {
		entries = append(entries, &file{
			name:    fi.Name() + ".js",
			size:    0,           // TODO.
			modTime: time.Time{}, // TODO.
		})
	}

	return &dir{
		name:    fi.Name(),
		entries: entries,
		modTime: fi.ModTime(),
	}, nil
}

func (fs *gopherJSFS) compileGoPackage(dir string) (http.File, error) {
	f, err := fs.source.Open(dir)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	if !fi.IsDir() {
		return nil, fmt.Errorf("%s is not a dir", dir)
	}

	fis, err := f.Readdir(0)
	if err != nil {
		return nil, err
	}

	var goFiles []os.FileInfo
	for _, f := range fis {
		if !f.IsDir() && pathpkg.Ext(f.Name()) == ".go" {
			goFiles = append(goFiles, f)
		}
	}
	if len(goFiles) == 0 {
		return nil, fmt.Errorf("%s has no .go files", dir)
	}

	// TODO: Clean this up.
	{
		name := pathpkg.Base(dir) + ".js"

		var names []string
		var goReaders []io.Reader
		var goClosers []io.Closer
		for _, goFile := range goFiles {
			file, err := fs.source.Open(pathpkg.Join(dir, goFile.Name()))
			if err != nil {
				return nil, err
			}
			names = append(names, goFile.Name())
			goReaders = append(goReaders, file)
			goClosers = append(goClosers, file)
		}

		fmt.Printf("REBUILDING SOURCE for: %s using %+v\n", name, names)
		content := []byte(handleJsError(goReadersToJs(names, goReaders)))

		for _, closer := range goClosers {
			closer.Close()
		}

		return &file{
			name:    name,
			size:    int64(len(content)),
			modTime: time.Now(),
			Reader:  bytes.NewReader(content),
		}, nil
	}
}

// file is an opened file instance.
type file struct {
	name    string
	modTime time.Time
	size    int64
	*bytes.Reader
}

func (f *file) Readdir(count int) ([]os.FileInfo, error) {
	return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
}
func (f *file) Stat() (os.FileInfo, error) { return f, nil }

func (f *file) Name() string       { return f.name }
func (f *file) Size() int64        { return f.size }
func (f *file) Mode() os.FileMode  { return 0444 }
func (f *file) ModTime() time.Time { return f.modTime }
func (f *file) IsDir() bool        { return false }
func (f *file) Sys() interface{}   { return nil }

func (f *file) Close() error {
	return nil
}

// dir is an opened dir instance.
type dir struct {
	name    string
	modTime time.Time
	entries []os.FileInfo
	pos     int // Position within entries for Seek and Readdir.
}

func (d *dir) Read([]byte) (int, error) {
	return 0, fmt.Errorf("cannot Read from directory %s", d.name)
}
func (d *dir) Close() error               { return nil }
func (d *dir) Stat() (os.FileInfo, error) { return d, nil }

func (d *dir) Name() string       { return d.name }
func (d *dir) Size() int64        { return 0 }
func (d *dir) Mode() os.FileMode  { return 0755 | os.ModeDir }
func (d *dir) ModTime() time.Time { return d.modTime }
func (d *dir) IsDir() bool        { return true }
func (d *dir) Sys() interface{}   { return nil }

func (d *dir) Seek(offset int64, whence int) (int64, error) {
	if offset == 0 && whence == os.SEEK_SET {
		d.pos = 0
		return 0, nil
	}
	return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
}

func (d *dir) Readdir(count int) ([]os.FileInfo, error) {
	if d.pos >= len(d.entries) && count > 0 {
		return nil, io.EOF
	}
	if count <= 0 || count > len(d.entries)-d.pos {
		count = len(d.entries) - d.pos
	}
	e := d.entries[d.pos : d.pos+count]
	d.pos += count
	return e, nil
}
<|endoftext|>


<commit_before>package udp

import (
	"net"
	"testing"
	"time"
)

var (
	testAddr = ":8126"
)

func setup(t *testing.T) net.Conn {
	udpClient, err := net.DialTimeout("udp", testAddr, time.Second)
	if err != nil {
		t.Fatal(err)
	}

	SetAddr(testAddr)

	return udpClient
}

func TestAll(t *testing.T) {
	udpClient := setup(t)

	testValues := [][]interface{}{
		[]interface{}{"foo", "foo", true, true},
		[]interface{}{"foo", "bar", false, false},
		[]interface{}{"foo", "foobar", false, true},
		[]interface{}{"foo", "", false, false},
		[]interface{}{"", "", true, true},
	}

	for _, values := range testValues {
		shouldGet := values[0].(string)
		sendString := values[1].(string)
		shouldEquals := values[2].(bool)
		shouldContains := values[3].(bool)

		got, equals, contains := get(t, shouldGet, func() {
			udpClient.Write([]byte(sendString))
		})

		if got != sendString {
			t.Errorf("Should've got %#v but got %#v", sendString, got)
		}
		if equals != shouldEquals {
			t.Errorf("Equals should've been %#v but was %#v", shouldEquals, equals)
		}
		if contains != shouldContains {
			t.Errorf("Contains should've been %#v but was %#v", shouldContains, contains)
		}
	}

	ShouldReceiveOnly(t, "foo", func() {
		udpClient.Write([]byte("foo"))
	})

	ShouldNotReceiveOnly(t, "bar", func() {
		udpClient.Write([]byte("foo"))
	})

	ShouldReceive(t, "foo", func() {
		udpClient.Write([]byte("barfoo"))
	})

	ShouldNotReceive(t, "bar", func() {
		udpClient.Write([]byte("fooba"))
	})

	ShouldReceiveAll(t, []string{"foo", "bar"}, func() {
		udpClient.Write([]byte("foobizbar"))
	})

	ShouldNotReceiveAny(t, []string{"fooby", "bars"}, func() {
		udpClient.Write([]byte("foobizbar"))
	})

	ShouldReceiveAllAndNotReceiveAny(t, []string{"foo", "bar"}, []string{"fooby", "bars"}, func() {
		udpClient.Write([]byte("foo"))
		udpClient.Write([]byte("biz"))
		udpClient.Write([]byte("bar"))
	})

	// This should fail, but it also shouldn't stall out
	// ShouldReceive(t, "foo", func() {})
}

func TestRaceConditionInReadingResults(t *testing.T) {
	udpClient := setup(t)

	ShouldReceiveAllAndNotReceiveAny(t, []string{"foo", "bar", "biz"}, []string{"fooby", "bars"}, func() {
		time.Sleep(time.Millisecond * 100)
		udpClient.Write([]byte("foo"))
		time.Sleep(time.Millisecond * 200)
		udpClient.Write([]byte("biz"))
		time.Sleep(time.Millisecond * 500)
		udpClient.Write([]byte("bar"))
	})
}
<commit_msg>remove duplicate code<commit_after>package udp

import (
	"net"
	"testing"
	"time"
)

var (
	testAddr = ":8126"
)

func setup(t *testing.T) net.Conn {
	udpClient, err := net.DialTimeout("udp", testAddr, time.Second)
	if err != nil {
		t.Fatal(err)
	}

	SetAddr(testAddr)

	return udpClient
}

func TestAll(t *testing.T) {
	udpClient := setup(t)

	testValues := [][]interface{}{
		{"foo", "foo", true, true},
		{"foo", "bar", false, false},
		{"foo", "foobar", false, true},
		{"foo", "", false, false},
		{"", "", true, true},
	}

	for _, values := range testValues {
		shouldGet := values[0].(string)
		sendString := values[1].(string)
		shouldEquals := values[2].(bool)
		shouldContains := values[3].(bool)

		got, equals, contains := get(t, shouldGet, func() {
			udpClient.Write([]byte(sendString))
		})

		if got != sendString {
			t.Errorf("Should've got %#v but got %#v", sendString, got)
		}
		if equals != shouldEquals {
			t.Errorf("Equals should've been %#v but was %#v", shouldEquals, equals)
		}
		if contains != shouldContains {
			t.Errorf("Contains should've been %#v but was %#v", shouldContains, contains)
		}
	}

	ShouldReceiveOnly(t, "foo", func() {
		udpClient.Write([]byte("foo"))
	})

	ShouldNotReceiveOnly(t, "bar", func() {
		udpClient.Write([]byte("foo"))
	})

	ShouldReceive(t, "foo", func() {
		udpClient.Write([]byte("barfoo"))
	})

	ShouldNotReceive(t, "bar", func() {
		udpClient.Write([]byte("fooba"))
	})

	ShouldReceiveAll(t, []string{"foo", "bar"}, func() {
		udpClient.Write([]byte("foobizbar"))
	})

	ShouldNotReceiveAny(t, []string{"fooby", "bars"}, func() {
		udpClient.Write([]byte("foobizbar"))
	})

	ShouldReceiveAllAndNotReceiveAny(t, []string{"foo", "bar"}, []string{"fooby", "bars"}, func() {
		udpClient.Write([]byte("foo"))
		udpClient.Write([]byte("biz"))
		udpClient.Write([]byte("bar"))
	})

	// This should fail, but it also shouldn't stall out
	// ShouldReceive(t, "foo", func() {})
}

func TestRaceConditionInReadingResults(t *testing.T) {
	udpClient := setup(t)

	ShouldReceiveAllAndNotReceiveAny(t, []string{"foo", "bar", "biz"}, []string{"fooby", "bars"}, func() {
		time.Sleep(time.Millisecond * 100)
		udpClient.Write([]byte("foo"))
		time.Sleep(time.Millisecond * 200)
		udpClient.Write([]byte("biz"))
		time.Sleep(time.Millisecond * 500)
		udpClient.Write([]byte("bar"))
	})
}
<|endoftext|>


<commit_before>package ui

import(
	"encoding/json"
	"fmt"
	"net/http"

	"golang.org/x/net/context"
	"google.golang.org/appengine/user"

	"github.com/skypies/flightdb2/fgae"
)

func init() {
	http.HandleFunc("/fdb/debug", UIOptionsHandler(debugHandler)) // should rename at some point ...
	http.HandleFunc("/fdb/debug/user", UIOptionsHandler(debugUserHandler))
}

// {{{ debugUserHandler

func debugUserHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	user := user.Current(ctx)
	json,_ := json.MarshalIndent(user, "", "  ")

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte("OK\n--\n"+string(json)))
}

// }}}
// {{{ debugHandler

func debugHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	opt,_ := GetUIOptions(ctx)
	db := fgae.FlightDB{C:ctx}
	str := ""

	//str += fmt.Sprintf("** Idspecs:-\n%#v\n\n", opt.IdSpecs())

	idspecs,err := opt.IdSpecs()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	for _,idspec := range idspecs {
		str += fmt.Sprintf("*** %s [%v]\n", idspec, idspec)

		results,err := db.LookupAll(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		for i,result := range results {
			//s,e := result.Times()
			//str += fmt.Sprintf("  * [%02d] %s,%s  %s\n", i, s,e, result.IdentityString())
			str += fmt.Sprintf("  * [%02d] %s\n", i, result)
		}
		str += "\n\n\n\n"

		for i,f := range results {
			str += fmt.Sprintf("----------{result %02d }-----------\n\n", i)

			if f == nil {
				http.Error(w, fmt.Sprintf("idspec %s[%#v] not found", idspec, idspec), http.StatusInternalServerError)
				return
			}

			str += fmt.Sprintf("    %s\n", f.IdSpec())
			str += fmt.Sprintf("    %s\n", f.FullString())
			str += fmt.Sprintf("    airframe: %s\n", f.Airframe.String())
			str += fmt.Sprintf("    %s\n\n", f)
			str += fmt.Sprintf("    index tags: %v\n", f.IndexTagList())
			str += fmt.Sprintf("    /batch/flights/flight?flightkey=%s&job=retag\n", f.GetDatastoreKey())

			t := f.AnyTrack()
			str += fmt.Sprintf("---- Anytrack: %s\n", t)

			/*
			gr := geo.LatlongBoxRestrictor{LatlongBox: sfo.KFixes["ZORSA"].Box(1,1) }
			satisfies,intersection,deb := f.SatisfiesGeoRestriction(gr, []string{"ADSB","MLAT"})
			str += fmt.Sprintf("\n\n---- ZORSA@1KM intersection result:-\n * %v\n * %s\n----\n%s",
				satisfies, intersection, deb)*/

			/* pos := sfo.KFixes["BRIXX"]
		gr := geo.LatlongBoxRestrictor{LatlongBox: pos.Box(1,1) }
		isects,debug := t.AllIntersectsGeoRestriction(gr)
		str += fmt.Sprintf("---- Intersections\n")
		for _,isect := range isects { str += fmt.Sprintf("  -- %s\n", isect) }
		str += fmt.Sprintf("\n%s", debug) */

			for k,v := range f.Tracks {
				str += fmt.Sprintf("  -- [%-7.7s] %s\n", k, v)
				if r.FormValue("v") != "" {
					for n,tp := range *v {
						str += fmt.Sprintf("    - [%3d] %s\n", n, tp)
					}
				}
			}

			str += fmt.Sprintf("\n--- DebugLog:-\n%s\n", f.DebugLog)
		}
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte(fmt.Sprintf("OK\n\n%s", str)))
}

// }}}

// {{{ -------------------------={ E N D }=----------------------------------

// Local variables:
// folded-file: t
// end:

// }}}
<commit_msg>Helpers for frag analysis<commit_after>package ui

import(
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/appengine/user"

	"github.com/skypies/adsb"
	fdb "github.com/skypies/flightdb2"
	"github.com/skypies/flightdb2/fgae"
)

func init() {
	http.HandleFunc("/fdb/debug", UIOptionsHandler(debugHandler)) // should rename at some point ...
	//http.HandleFunc("/fdb/debug/frags", UIOptionsHandler(debugFragsHandler))
	http.HandleFunc("/fdb/debug/user", UIOptionsHandler(debugUserHandler))
}

// {{{ debugUserHandler

func debugUserHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	user := user.Current(ctx)
	json,_ := json.MarshalIndent(user, "", "  ")

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte("OK\n--\n"+string(json)))
}

// }}}
// {{{ debugHandler

func debugHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	opt,_ := GetUIOptions(ctx)
	db := fgae.FlightDB{C:ctx}
	str := ""

	//str += fmt.Sprintf("** Idspecs:-\n%#v\n\n", opt.IdSpecs())

	idspecs,err := opt.IdSpecs()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	nPts := 0

	for _,idspec := range idspecs {
		str += fmt.Sprintf("*** %s [%v]\n", idspec, idspec)

		results,err := db.LookupAll(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		for i,result := range results {
			//s,e := result.Times()
			//str += fmt.Sprintf("  * [%02d] %s,%s  %s\n", i, s,e, result.IdentityString())
			str += fmt.Sprintf("  * [%02d] %s\n", i, result)
			nPts += len(result.AnyTrack())
		}
		str += fmt.Sprintf("*** Total pts: %d\n", nPts)
		str += "\n\n\n\n"

		for i,f := range results {
			str += fmt.Sprintf("----------{result %02d }-----------\n\n", i)

			if f == nil {
				http.Error(w, fmt.Sprintf("idspec %s[%#v] not found", idspec, idspec), http.StatusInternalServerError)
				return
			}

			str += fmt.Sprintf("    %s\n", f.IdSpec())
			str += fmt.Sprintf("    %s\n", f.FullString())
			str += fmt.Sprintf("    airframe: %s\n", f.Airframe.String())
			str += fmt.Sprintf("    %s\n\n", f)
			str += fmt.Sprintf("    index tags: %v\n", f.IndexTagList())
			str += fmt.Sprintf("    /batch/flights/flight?flightkey=%s&job=retag\n", f.GetDatastoreKey())

			t := f.AnyTrack()
			str += fmt.Sprintf("\n---- Anytrack: %s\n", t)

			/*
			gr := geo.LatlongBoxRestrictor{LatlongBox: sfo.KFixes["ZORSA"].Box(1,1) }
			satisfies,intersection,deb := f.SatisfiesGeoRestriction(gr, []string{"ADSB","MLAT"})
			str += fmt.Sprintf("\n\n---- ZORSA@1KM intersection result:-\n * %v\n * %s\n----\n%s",
				satisfies, intersection, deb)*/

			/* pos := sfo.KFixes["BRIXX"]
		gr := geo.LatlongBoxRestrictor{LatlongBox: pos.Box(1,1) }
		isects,debug := t.AllIntersectsGeoRestriction(gr)
		str += fmt.Sprintf("---- Intersections\n")
		for _,isect := range isects { str += fmt.Sprintf("  -- %s\n", isect) }
		str += fmt.Sprintf("\n%s", debug) */

			for k,v := range f.Tracks {
				str += fmt.Sprintf("  -- [%-7.7s] %s\n", k, v)
				if r.FormValue("v") != "" {
					for n,tp := range *v {
						str += fmt.Sprintf("    - [%3d] %s\n", n, tp)
					}
				}
			}
			str += "\n"
			str += fmt.Sprintf("--- DebugLog:-\n%s\n", f.DebugLog)
		}
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte(fmt.Sprintf("OK\n\n%s", str)))
}

// }}}

// {{{ AddFragEvent{}

type AddFragEvent struct {
	TrackFragment fdb.TrackFragment
	Time time.Time
	I int `json:"-"`
	N int `json:"-"`  // Added N trackpoints starting at index I
	J int `json:"-"` // result number
}

func (ev AddFragEvent)String() string {
	// Delay from when message was received, to when frag was written to datastore
	delay := ev.Time.Sub(ev.TrackFragment.Track[0].TimestampUTC).Seconds()
	return fmt.Sprintf("{{%2d}} % 4d + %2d : %s (total delay %6.0fs) %s",
		ev.J, ev.I, ev.N,
		ev.TrackFragment.Track[0].TimestampUTC.Format("15:04:05"),
		delay, ev.TrackFragment)
}

type ByTimeOfDBWrite []AddFragEvent

func (a ByTimeOfDBWrite) Len() int           { return len(a) }
func (a ByTimeOfDBWrite) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByTimeOfDBWrite) Less(i, j int) bool { return a[i].Time.Before(a[j].Time) }

// }}}
// {{{ AddNewEvent

func AddNewEvent(events *[]AddFragEvent, instance int, t fdb.Track, id,callsign string, start,length int, tm time.Time) {
	ev := AddFragEvent{
		TrackFragment: track2frag(t, id, callsign, start, length),
		Time:tm,
		J:instance,
		I:start,
		N:length,
	}

	*events = append(*events, ev)
}

func track2frag(t fdb.Track, id,callsign string, start,length int) fdb.TrackFragment {
	if len(t) == 0 || start+length > len(t) {
		return fdb.TrackFragment{}
	}

	frag := fdb.TrackFragment{
		IcaoId: adsb.IcaoId(id),
		Callsign: callsign,
		Track: t[start:(start+length)],
		DataSystem: fdb.DSADSB,
	}

	if t[0].DataSource != "ADSB" {
		frag.DataSystem = fdb.DSMLAT
	}

	return frag
}

// }}}
// {{{ debugFragsHandler

// This handler reconstructs the series of TrackFragments that generated the set of
// flights matching the idpsec (use a range idspec to get multiple instances of the same IcaoID).
// It parses the debug log to figure out which trackpoints got added when, so it's brittle.
func debugFragsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	opt,_ := GetUIOptions(ctx)
	db := fgae.FlightDB{C:ctx}
	str := ""

	idspecs,err := opt.IdSpecs()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	events := []AddFragEvent{}
	nPts := 0

	for _,idspec := range idspecs {
		str += fmt.Sprintf("*** %s [%v]\n", idspec, idspec)

		results,err := db.LookupAll(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		for i,result := range results {
			str += fmt.Sprintf("  * [%02d] %s\n", i, result)
			nPts += len(result.AnyTrack())
		}
		str += fmt.Sprintf("*** Total pts: %d\n\n\n", nPts)

		for instance,f := range results {
			var tStart time.Time
			var track fdb.Track
			firstHandled := false

			// What to write the fragments as
			newIcao := r.FormValue("as")
			if newIcao == "" { newIcao = f.IcaoId }

			for _,line := range strings.Split(f.DebugLog, "\n") {
				exp := "-- AddFrag.*\\]([AM]) (\\d{4}-.* UTC): "+
					"(was not plausible|new IcaoID|extending \\(adding (\\d+) to (\\d+))"
				reg := regexp.MustCompile(exp).FindStringSubmatch(line)
				if reg != nil {
					t,_ := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", reg[2])
					length,_ := strconv.Atoi(reg[4])
					index,_ := strconv.Atoi(reg[5])

					if length == 0 { // AKA undef; must be the initial fragment; we don't know how long it is
						tStart = t
						switch (fdb.DataSystem(reg[1])) {
						case fdb.DSADSB: track = *(f.Tracks["ADSB"])
						default: track = *(f.Tracks["MLAT"])
						}

					} else {
						if !firstHandled {
							// Now we know how long the first frag was, add it in
							if 0+index > len(track) {
								http.Error(w, fmt.Sprintf("0+%d > %d !", index, len(track)),
									http.StatusInternalServerError)
								return
							}
							AddNewEvent(&events, instance, track, newIcao, f.Callsign, 0, index, tStart)
							firstHandled = true
						}
						if index+length > len(track) {
							http.Error(w, fmt.Sprintf("%d+%d > %d !", index, length, len(track)),
								http.StatusInternalServerError)
							return
						}
						AddNewEvent(&events, instance, track, newIcao, f.Callsign, index, length, t)
					}
				}
			}

			if !firstHandled {
				// We only ever had one frag - so its length is that of the whole track
				AddNewEvent(&events, instance, track, newIcao, f.Callsign, 0, len(track), tStart)
			}
		}
	}

	//fgae.Debug = true

	sort.Sort(ByTimeOfDBWrite(events)) // Restore the original insertion order

	if false && len(events) == 865 {
		// The original list of 864 frags is too long to use with test datastore. Prune out these
		// uninteresting serial ones: [776,864] [300,758] [143,260]
		events = events[:777]
		events = append(events[:300], events[759:]...)
		events = append(events[:143], events[261:]...)
	}

	for i,ev := range events {
		relativeT := ev.Time.Sub(events[0].Time).Seconds() // Time from when first frag was written

		if r.FormValue("as") != "" {
			// We've been asked to reinsert these frags, in their orig order, with a new IcaoID
			time.Sleep(1000 * time.Millisecond) // the test datastore gets confused if we go any quicker
			err := db.AddTrackFragment(&ev.TrackFragment)
			str += fmt.Sprintf("%08.3fs %s added [[%v]]\n", relativeT, ev, err)
		} else if r.FormValue("json") == "" {
			str += fmt.Sprintf("[% 5d] %08.3fs %s\n", i, relativeT, ev)
		}
	}

	if r.FormValue("json") != "" {
		frags := []fdb.TrackFragment{}
		for _,ev := range events {
			frags = append(frags, ev.TrackFragment)
		}
		json,_ := json.MarshalIndent(frags, "", "  ")
		str = "OK\n--\n"+string(json)
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte(fmt.Sprintf("OK\n\n%s", str)))
}

// }}}

// {{{ -------------------------={ E N D }=----------------------------------

// Local variables:
// folded-file: t
// end:

// }}}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strconv"

	"github.com/miekg/dns"
)

// Answer crafts a response to the DNS Question
func (s *RedisDNSServer) Answer(msg dns.Question) []dns.RR {
	var answers []dns.RR
	record := s.Get(msg.Name)

	// Bail out early if the record isn't found in the key store
	if record == nil {
		log.Printf("Error no record found for %s\n", msg.Name)
		return nil
	}

	switch msg.Qtype {
	case dns.TypeNS:
		answers = append(answers, NS(msg.Name, record)...)
	case dns.TypeSOA:
		if len(record.NameServers) > 0 {
			answers = append(answers, SOA(msg.Name, record, s.getSerialNumber()))
		}
	case dns.TypeA:
		answers = append(answers, A(msg.Name, record)...)
	case dns.TypeAAAA:
		answers = append(answers, AAAA(msg.Name, record)...)
	case dns.TypeCNAME:
		answers = append(answers, CNAME(msg.Name, record)...)
	case dns.TypeMX:
		answers = append(answers, MX(msg.Name, record)...)
	default:
		answers = append(answers, s.Host(msg.Name, record)...)
	}
	return answers
}

func (s *RedisDNSServer) getSerialNumber() uint32 {
	sn, err := s.redisClient.Get(serialNumberKey)
	if err != nil {
		log.Printf("Error reading SerialNumber: %s\n", err)
		return uint32(0)
	}

	x, _ := strconv.Atoi(string(sn))
	log.Printf("Found SerialNumber %d\n", uint32(x))
	return uint32(x)
}

// SOA returns the Server of Authority record response
func SOA(name string, record *Record, serialNumber uint32) dns.RR {
	return &dns.SOA{
		Hdr:     dns.RR_Header{Name: name, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 60},
		Ns:      dns.Fqdn(record.NameServers[0]),
		Mbox:    record.MBox,
		Serial:  serialNumber,
		Refresh: 86400,
		Retry:   7200,
		Expire:  3600, // RFC1912 suggests 2-4 weeks 1209600-2419200
		Minttl:  60,
	}
}

// NS returns the Name Servers
func NS(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.NameServers); i++ {
		r := new(dns.NS)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: record.TTL}
		r.Ns = dns.Fqdn(record.NameServers[i])
		answers = append(answers, r)
	}
	return answers
}

// A returns A records
func A(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.IPv4PublicIPs); i++ {
		r := new(dns.A)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: record.TTL}
		r.A = record.IPv4PublicIPs[i]
		answers = append(answers, r)
	}
	return answers
}

// AAAA returns IPv6 records
func AAAA(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.IPv4PublicIPs); i++ {
		r := new(dns.AAAA)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: record.TTL}
		r.AAAA = record.IPv4PublicIPs[i]
		answers = append(answers, r)
	}
	return answers
}

// CNAME returns canonical names
func CNAME(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.CNames); i++ {
		r := new(dns.CNAME)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: record.TTL}
		r.Target = dns.Fqdn(record.CNames[i])
		answers = append(answers, r)
	}
	return answers
}

// MX returns mail records
func MX(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.MXServers); i++ {
		r := new(dns.MX)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: record.TTL}
		r.Mx = record.MXServers[i]
		answers = append(answers, r)
	}
	return answers

}

// Host returns cname, a, and aaaa in that order
func (s *RedisDNSServer) Host(name string, record *Record) []dns.RR {
	var answers []dns.RR

	answers = append(answers, CNAME(name, record)...)
	answers = append(answers, A(name, record)...)
	answers = append(answers, AAAA(name, record)...)

	return answers
}
<commit_msg>Handle A requests that only have Cnames<commit_after>package main

import (
	"log"
	"strconv"

	"github.com/miekg/dns"
)

// Answer crafts a response to the DNS Question
func (s *RedisDNSServer) Answer(msg dns.Question) []dns.RR {
	var answers []dns.RR
	record := s.Get(msg.Name)

	// Bail out early if the record isn't found in the key store
	if record == nil {
		log.Printf("Error no record found for %s\n", msg.Name)
		return nil
	}

	switch msg.Qtype {
	case dns.TypeNS:
		answers = append(answers, NS(msg.Name, record)...)
	case dns.TypeSOA:
		if len(record.NameServers) > 0 {
			answers = append(answers, SOA(msg.Name, record, s.getSerialNumber()))
		}
	case dns.TypeA:
		answers = append(answers, A(msg.Name, record)...)
	case dns.TypeAAAA:
		answers = append(answers, AAAA(msg.Name, record)...)
	case dns.TypeCNAME:
		answers = append(answers, CNAME(msg.Name, record)...)
	case dns.TypeMX:
		answers = append(answers, MX(msg.Name, record)...)
	default:
		answers = append(answers, s.Host(msg.Name, record)...)
	}
	return answers
}

func (s *RedisDNSServer) getSerialNumber() uint32 {
	sn, err := s.redisClient.Get(serialNumberKey)
	if err != nil {
		log.Printf("Error reading SerialNumber: %s\n", err)
		return uint32(0)
	}

	x, _ := strconv.Atoi(string(sn))
	log.Printf("Found SerialNumber %d\n", uint32(x))
	return uint32(x)
}

// SOA returns the Server of Authority record response
func SOA(name string, record *Record, serialNumber uint32) dns.RR {
	return &dns.SOA{
		Hdr:     dns.RR_Header{Name: name, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 60},
		Ns:      dns.Fqdn(record.NameServers[0]),
		Mbox:    record.MBox,
		Serial:  serialNumber,
		Refresh: 86400,
		Retry:   7200,
		Expire:  3600, // RFC1912 suggests 2-4 weeks 1209600-2419200
		Minttl:  60,
	}
}

// NS returns the Name Servers
func NS(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.NameServers); i++ {
		r := new(dns.NS)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: record.TTL}
		r.Ns = dns.Fqdn(record.NameServers[i])
		answers = append(answers, r)
	}
	return answers
}

// A returns A records
func A(name string, record *Record) []dns.RR {
	var answers []dns.RR
	// handle A requests that have no A records

	if len(record.IPv4PublicIPs) == 0 && len(record.CNames) > 0 {
		return CNAME(name, record)
	}

	for i := 0; i < len(record.IPv4PublicIPs); i++ {
		r := new(dns.A)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: record.TTL}
		r.A = record.IPv4PublicIPs[i]
		answers = append(answers, r)
	}
	return answers
}

// AAAA returns IPv6 records
func AAAA(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.IPv4PublicIPs); i++ {
		r := new(dns.AAAA)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: record.TTL}
		r.AAAA = record.IPv4PublicIPs[i]
		answers = append(answers, r)
	}
	return answers
}

// CNAME returns canonical names
func CNAME(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.CNames); i++ {
		r := new(dns.CNAME)

		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: record.TTL}
		r.Target = dns.Fqdn(record.CNames[i])
		answers = append(answers, r)
	}
	return answers
}

// MX returns mail records
func MX(name string, record *Record) []dns.RR {
	var answers []dns.RR
	for i := 0; i < len(record.MXServers); i++ {
		r := new(dns.MX)
		r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: record.TTL}
		r.Mx = record.MXServers[i]
		answers = append(answers, r)
	}
	return answers

}

// Host returns cname, a, and aaaa in that order
func (s *RedisDNSServer) Host(name string, record *Record) []dns.RR {
	var answers []dns.RR

	answers = append(answers, CNAME(name, record)...)
	answers = append(answers, A(name, record)...)
	answers = append(answers, AAAA(name, record)...)

	return answers
}
<|endoftext|>


<commit_before>package air

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
)

// Response is an HTTP response.
type Response struct {
	StatusCode int
	Headers    map[string]string
	Cookies    []*Cookie
	Size       int
	Written    bool

	request       *Request
	writer        http.ResponseWriter
	flusher       http.Flusher
	hijacker      http.Hijacker
	closeNotifier http.CloseNotifier
	pusher        http.Pusher
}

// newResponse returns a new instance of the `Response`.
func newResponse(r *Request, writer http.ResponseWriter) *Response {
	flusher, _ := writer.(http.Flusher)
	hijacker, _ := writer.(http.Hijacker)
	closeNotifier, _ := writer.(http.CloseNotifier)
	pusher, _ := writer.(http.Pusher)

	return &Response{
		StatusCode: 200,
		Headers:    map[string]string{},

		request:       r,
		writer:        writer,
		flusher:       flusher,
		hijacker:      hijacker,
		closeNotifier: closeNotifier,
		pusher:        pusher,
	}
}

// write writes the b to the client.
func (r *Response) write(b []byte) error {
	if !r.Written {
		for k, v := range r.Headers {
			r.writer.Header().Set(k, v)
		}
		for _, c := range r.Cookies {
			if v := c.String(); v != "" {
				r.writer.Header().Add("Set-Cookie", v)
			}
		}
		r.writer.WriteHeader(r.StatusCode)
		r.Written = true
	}
	n, err := r.writer.Write(b)
	r.Size += n
	return err
}

// NoContent responds to the client with no content.
func (r *Response) NoContent() error {
	return r.write(nil)
}

// Redirect responds to the client with a redirection to the url.
func (r *Response) Redirect(url string) error {
	r.Headers["Location"] = url
	return r.write(nil)
}

// Blob responds to the client with the contentType content b.
func (r *Response) Blob(contentType string, b []byte) error {
	var err error
	if b, err = theMinifier.minify(contentType, b); err != nil {
		return err
	}
	r.Headers["Content-Type"] = contentType
	return r.write(b)
}

// String responds to the client with the "text/plain" content s.
func (r *Response) String(s string) error {
	return r.Blob("text/plain; charset=utf-8", []byte(s))
}

// JSON responds to the client with the "application/json" content v.
func (r *Response) JSON(v interface{}) error {
	b, err := json.Marshal(v)
	if DebugMode {
		b, err = json.MarshalIndent(v, "", "\t")
	}
	if err != nil {
		return err
	}
	return r.Blob("application/json; charset=utf-8", b)
}

// XML responds to the client with the "application/xml" content v.
func (r *Response) XML(v interface{}) error {
	b, err := xml.Marshal(v)
	if DebugMode {
		b, err = xml.MarshalIndent(v, "", "\t")
	}
	if err != nil {
		return err
	}
	b = append([]byte(xml.Header), b...)
	return r.Blob("application/xml; charset=utf-8", b)
}

// HTML responds to the client with the "text/html" content html.
func (r *Response) HTML(html string) error {
	return r.Blob("text/html; charset=utf-8", []byte(html))
}

// Render renders one or more HTML templates with the m and responds to the
// client with the "text/html" content. The results rendered by the former can
// be inherited by accessing the `m["InheritedHTML"]`.
func (r *Response) Render(m map[string]interface{}, templates ...string) error {
	buf := &bytes.Buffer{}
	for _, t := range templates {
		m["InheritedHTML"] = template.HTML(buf.String())
		buf.Reset()
		if err := theRenderer.render(buf, t, m); err != nil {
			return err
		}
	}
	return r.HTML(buf.String())
}

// Stream responds to the client with the contentType streaming content reader.
func (r *Response) Stream(contentType string, reader io.Reader) error {
	if err := r.Blob(contentType, nil); err != nil {
		return err
	}
	_, err := io.Copy(r.writer, reader)
	return err
}

// File responds to the client with a file content with the file.
func (r *Response) File(file string) error {
	file, err := filepath.Abs(file)
	if err != nil {
		return err
	} else if fi, err := os.Stat(file); err != nil {
		return err
	} else if fi.IsDir() {
		if p := r.request.request.URL.Path; p[len(p)-1] != '/' {
			p = path.Base(p) + "/"
			if q := r.request.request.URL.RawQuery; q != "" {
				p += "?" + q
			}
			r.StatusCode = 301
			return r.Redirect(p)
		}
		file += "index.html"
	}

	c := []byte{}
	if a, err := theCoffer.asset(file); err != nil {
		return err
	} else if a != nil {
		c = a.content
	} else if c, err = ioutil.ReadFile(file); err != nil {
		return err
	}

	r.Headers["ETag"] = fmt.Sprintf(`"%x"`, md5.Sum(c))

	for k, v := range r.Headers {
		r.writer.Header().Set(k, v)
	}

	for _, c := range r.Cookies {
		if v := c.String(); v != "" {
			r.writer.Header().Add("Set-Cookie", v)
		}
	}

	http.ServeContent(
		r.writer,
		r.request.request,
		file,
		time.Time{},
		bytes.NewReader(c),
	)

	r.Written = true

	return nil
}

// Flush flushes buffered data to the client.
func (r *Response) Flush() {
	r.flusher.Flush()
}

// Hijack took over the connection from the server.
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return r.hijacker.Hijack()
}

// CloseNotify returns a channel that receives at most a single value when the
// connection has gone away.
func (r *Response) CloseNotify() <-chan bool {
	return r.closeNotifier.CloseNotify()
}

// Push initiates an HTTP/2 server push. This constructs a synthetic request
// using the given target and pos, serializes that request into a PUSH_PROMISE
// frame, then dispatches that request using the server's request handler. If
// pos is nil, default options are used.
func (r *Response) Push(target string, pos *http.PushOptions) error {
	return r.pusher.Push(target, pos)
}
<commit_msg>fix: `Response#File()` covers the ETag header<commit_after>package air

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
)

// Response is an HTTP response.
type Response struct {
	StatusCode int
	Headers    map[string]string
	Cookies    []*Cookie
	Size       int
	Written    bool

	request       *Request
	writer        http.ResponseWriter
	flusher       http.Flusher
	hijacker      http.Hijacker
	closeNotifier http.CloseNotifier
	pusher        http.Pusher
}

// newResponse returns a new instance of the `Response`.
func newResponse(r *Request, writer http.ResponseWriter) *Response {
	flusher, _ := writer.(http.Flusher)
	hijacker, _ := writer.(http.Hijacker)
	closeNotifier, _ := writer.(http.CloseNotifier)
	pusher, _ := writer.(http.Pusher)

	return &Response{
		StatusCode: 200,
		Headers:    map[string]string{},

		request:       r,
		writer:        writer,
		flusher:       flusher,
		hijacker:      hijacker,
		closeNotifier: closeNotifier,
		pusher:        pusher,
	}
}

// write writes the b to the client.
func (r *Response) write(b []byte) error {
	if !r.Written {
		for k, v := range r.Headers {
			r.writer.Header().Set(k, v)
		}
		for _, c := range r.Cookies {
			if v := c.String(); v != "" {
				r.writer.Header().Add("Set-Cookie", v)
			}
		}
		r.writer.WriteHeader(r.StatusCode)
		r.Written = true
	}
	n, err := r.writer.Write(b)
	r.Size += n
	return err
}

// NoContent responds to the client with no content.
func (r *Response) NoContent() error {
	return r.write(nil)
}

// Redirect responds to the client with a redirection to the url.
func (r *Response) Redirect(url string) error {
	r.Headers["Location"] = url
	return r.write(nil)
}

// Blob responds to the client with the contentType content b.
func (r *Response) Blob(contentType string, b []byte) error {
	var err error
	if b, err = theMinifier.minify(contentType, b); err != nil {
		return err
	}
	r.Headers["Content-Type"] = contentType
	return r.write(b)
}

// String responds to the client with the "text/plain" content s.
func (r *Response) String(s string) error {
	return r.Blob("text/plain; charset=utf-8", []byte(s))
}

// JSON responds to the client with the "application/json" content v.
func (r *Response) JSON(v interface{}) error {
	b, err := json.Marshal(v)
	if DebugMode {
		b, err = json.MarshalIndent(v, "", "\t")
	}
	if err != nil {
		return err
	}
	return r.Blob("application/json; charset=utf-8", b)
}

// XML responds to the client with the "application/xml" content v.
func (r *Response) XML(v interface{}) error {
	b, err := xml.Marshal(v)
	if DebugMode {
		b, err = xml.MarshalIndent(v, "", "\t")
	}
	if err != nil {
		return err
	}
	b = append([]byte(xml.Header), b...)
	return r.Blob("application/xml; charset=utf-8", b)
}

// HTML responds to the client with the "text/html" content html.
func (r *Response) HTML(html string) error {
	return r.Blob("text/html; charset=utf-8", []byte(html))
}

// Render renders one or more HTML templates with the m and responds to the
// client with the "text/html" content. The results rendered by the former can
// be inherited by accessing the `m["InheritedHTML"]`.
func (r *Response) Render(m map[string]interface{}, templates ...string) error {
	buf := &bytes.Buffer{}
	for _, t := range templates {
		m["InheritedHTML"] = template.HTML(buf.String())
		buf.Reset()
		if err := theRenderer.render(buf, t, m); err != nil {
			return err
		}
	}
	return r.HTML(buf.String())
}

// Stream responds to the client with the contentType streaming content reader.
func (r *Response) Stream(contentType string, reader io.Reader) error {
	if err := r.Blob(contentType, nil); err != nil {
		return err
	}
	_, err := io.Copy(r.writer, reader)
	return err
}

// File responds to the client with a file content with the file.
func (r *Response) File(file string) error {
	file, err := filepath.Abs(file)
	if err != nil {
		return err
	} else if fi, err := os.Stat(file); err != nil {
		return err
	} else if fi.IsDir() {
		if p := r.request.request.URL.Path; p[len(p)-1] != '/' {
			p = path.Base(p) + "/"
			if q := r.request.request.URL.RawQuery; q != "" {
				p += "?" + q
			}
			r.StatusCode = 301
			return r.Redirect(p)
		}
		file += "index.html"
	}

	c := []byte{}
	if a, err := theCoffer.asset(file); err != nil {
		return err
	} else if a != nil {
		c = a.content
	} else if c, err = ioutil.ReadFile(file); err != nil {
		return err
	}

	if _, ok := r.Headers["ETag"]; !ok {
		r.Headers["ETag"] = fmt.Sprintf(`"%x"`, md5.Sum(c))
	}

	for k, v := range r.Headers {
		r.writer.Header().Set(k, v)
	}

	for _, c := range r.Cookies {
		if v := c.String(); v != "" {
			r.writer.Header().Add("Set-Cookie", v)
		}
	}

	http.ServeContent(
		r.writer,
		r.request.request,
		file,
		time.Time{},
		bytes.NewReader(c),
	)

	r.Written = true

	return nil
}

// Flush flushes buffered data to the client.
func (r *Response) Flush() {
	r.flusher.Flush()
}

// Hijack took over the connection from the server.
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return r.hijacker.Hijack()
}

// CloseNotify returns a channel that receives at most a single value when the
// connection has gone away.
func (r *Response) CloseNotify() <-chan bool {
	return r.closeNotifier.CloseNotify()
}

// Push initiates an HTTP/2 server push. This constructs a synthetic request
// using the given target and pos, serializes that request into a PUSH_PROMISE
// frame, then dispatches that request using the server's request handler. If
// pos is nil, default options are used.
func (r *Response) Push(target string, pos *http.PushOptions) error {
	return r.pusher.Push(target, pos)
}
<|endoftext|>


<commit_before>package parser

import "bytes"

type RedirectT struct {
	Path     string
	IsAppend bool
}

type StatementT struct {
	Argv     []string
	Redirect [3]RedirectT
	IsAppend [3]bool
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

func (this StatementT) String() string {
	var buffer bytes.Buffer
	for _, arg := range this.Argv {
		buffer.WriteRune('[')
		buffer.WriteString(arg)
		buffer.WriteRune(']')
	}
	for i := 0; i < len(prefix); i++ {
		if len(this.Redirect[i].Path) > 0 {
			buffer.WriteString(prefix[i])
			buffer.WriteString("[")
			buffer.WriteString(this.Redirect[i].Path)
			buffer.WriteString("]")
		}
	}
	buffer.WriteString(" ")
	buffer.WriteString(this.Term)
	return buffer.String()
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := '\000'
	quote := false
	for {
		ch, _, ok := source.ReadRune()
		if ok != nil {
			break
		}
		if ch == '"' {
			quote = !quote
			if lastchar == '"' && quote {
				buffer.WriteRune('"')
				lastchar = '\000'
			}
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	return buffer.String()
}

func terminate(statements *[]StatementT,
	nextword *int,
	redirect *[3]RedirectT,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {
	var statement1 StatementT
	if buffer.Len() > 0 {
		if *nextword == WORD_ARGV {
			statement1.Argv = append(*argv, dequote(buffer))
		} else {
			statement1.Argv = *argv
			(*redirect)[*nextword].Path = dequote(buffer)
			*nextword = WORD_ARGV
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect[0] = redirect[0]
	statement1.Redirect[1] = redirect[1]
	statement1.Redirect[2] = redirect[2]
	redirect[0].Path = ""
	redirect[0].IsAppend = false
	redirect[1].Path = ""
	redirect[1].IsAppend = false
	redirect[2].Path = ""
	redirect[2].IsAppend = false
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

const (
	WORD_ARGV   = -1
	WORD_STDIN  = 0
	WORD_STDOUT = 1
	WORD_STDERR = 2
)

func Parse1(text string) []StatementT {
	isQuoted := false
	statements := make([]StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	lastredirected := -1
	var buffer bytes.Buffer
	nextword := WORD_ARGV
	var redirect [3]RedirectT
	for _, ch := range text {
		if ch == '"' {
			isQuoted = !isQuoted
		}
		if isQuoted {
			buffer.WriteRune(ch)
		} else {
			if ch == ' ' {
				if buffer.Len() > 0 {
					if nextword == WORD_ARGV {
						argv = append(argv, dequote(&buffer))
					} else {
						redirect[nextword].Path = dequote(&buffer)
					}
					buffer.Reset()
					nextword = WORD_ARGV
				}
			} else if lastchar == ' ' && ch == ';' {
				terminate(&statements, &nextword, &redirect, &buffer, &argv, ";")
			} else if ch == '|' {
				if lastchar == '|' {
					statements[len(statements)-1].Term = "||"
				} else {
					terminate(&statements, &nextword, &redirect, &buffer, &argv, "|")
				}
			} else if ch == '&' {
				if lastchar == '&' {
					statements[len(statements)-1].Term = "&&"
				} else {
					terminate(&statements, &nextword, &redirect, &buffer, &argv, "&")
				}
			} else if ch == '>' {
				if lastchar == '1' {
					chomp(&buffer)
					nextword = WORD_STDOUT
					redirect[1].IsAppend = false
					lastredirected = 1
				} else if lastchar == '2' {
					chomp(&buffer)
					nextword = WORD_STDERR
					redirect[2].IsAppend = false
					lastredirected = 2
				} else if lastchar == '>' && lastredirected >= 0 {
					redirect[lastredirected].IsAppend = true
				} else {
					nextword = WORD_STDOUT
					lastredirected = 1
				}
			} else if ch == '<' {
				nextword = WORD_STDIN
				redirect[0].IsAppend = false
				lastredirected = 0
			} else {
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	terminate(&statements, &nextword, &redirect, &buffer, &argv, " ")
	return statements
}

func Parse2(statements []StatementT) [][]StatementT {
	result := make([][]StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		if statement1.Term != "|" {
			result = append(result, make([]StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) [][]StatementT {
	result1 := Parse1(text)
	result2 := Parse2(result1)
	return result2
}
<commit_msg>expand %ENV%<commit_after>package parser

import "bytes"
import "os"

type RedirectT struct {
	Path     string
	IsAppend bool
}

type StatementT struct {
	Argv     []string
	Redirect [3]RedirectT
	IsAppend [3]bool
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

func (this StatementT) String() string {
	var buffer bytes.Buffer
	for _, arg := range this.Argv {
		buffer.WriteRune('[')
		buffer.WriteString(arg)
		buffer.WriteRune(']')
	}
	for i := 0; i < len(prefix); i++ {
		if len(this.Redirect[i].Path) > 0 {
			buffer.WriteString(prefix[i])
			buffer.WriteString("[")
			buffer.WriteString(this.Redirect[i].Path)
			buffer.WriteString("]")
		}
	}
	buffer.WriteString(" ")
	buffer.WriteString(this.Term)
	return buffer.String()
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := '\000'
	quote := false
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '%' {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					break
				}
				nameBuf.WriteRune(ch)
			}
			nameStr := nameBuf.String()
			value := os.Getenv(nameStr)
			if value != "" {
				buffer.WriteString(value)
			}else{
				buffer.WriteRune('%')
				buffer.WriteString(nameStr)
				buffer.WriteRune('%')
			}
			continue
		}
		if ch == '"' {
			quote = !quote
			if lastchar == '"' && quote {
				buffer.WriteRune('"')
				lastchar = '\000'
			}
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	return buffer.String()
}

func terminate(statements *[]StatementT,
	nextword *int,
	redirect *[3]RedirectT,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {
	var statement1 StatementT
	if buffer.Len() > 0 {
		if *nextword == WORD_ARGV {
			statement1.Argv = append(*argv, dequote(buffer))
		} else {
			statement1.Argv = *argv
			(*redirect)[*nextword].Path = dequote(buffer)
			*nextword = WORD_ARGV
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect[0] = redirect[0]
	statement1.Redirect[1] = redirect[1]
	statement1.Redirect[2] = redirect[2]
	redirect[0].Path = ""
	redirect[0].IsAppend = false
	redirect[1].Path = ""
	redirect[1].IsAppend = false
	redirect[2].Path = ""
	redirect[2].IsAppend = false
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

const (
	WORD_ARGV   = -1
	WORD_STDIN  = 0
	WORD_STDOUT = 1
	WORD_STDERR = 2
)

func Parse1(text string) []StatementT {
	isQuoted := false
	statements := make([]StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	lastredirected := -1
	var buffer bytes.Buffer
	nextword := WORD_ARGV
	var redirect [3]RedirectT
	for _, ch := range text {
		if ch == '"' {
			isQuoted = !isQuoted
		}
		if isQuoted {
			buffer.WriteRune(ch)
		} else {
			if ch == ' ' {
				if buffer.Len() > 0 {
					if nextword == WORD_ARGV {
						argv = append(argv, dequote(&buffer))
					} else {
						redirect[nextword].Path = dequote(&buffer)
					}
					buffer.Reset()
					nextword = WORD_ARGV
				}
			} else if lastchar == ' ' && ch == ';' {
				terminate(&statements, &nextword, &redirect, &buffer, &argv, ";")
			} else if ch == '|' {
				if lastchar == '|' {
					statements[len(statements)-1].Term = "||"
				} else {
					terminate(&statements, &nextword, &redirect, &buffer, &argv, "|")
				}
			} else if ch == '&' {
				if lastchar == '&' {
					statements[len(statements)-1].Term = "&&"
				} else {
					terminate(&statements, &nextword, &redirect, &buffer, &argv, "&")
				}
			} else if ch == '>' {
				if lastchar == '1' {
					chomp(&buffer)
					nextword = WORD_STDOUT
					redirect[1].IsAppend = false
					lastredirected = 1
				} else if lastchar == '2' {
					chomp(&buffer)
					nextword = WORD_STDERR
					redirect[2].IsAppend = false
					lastredirected = 2
				} else if lastchar == '>' && lastredirected >= 0 {
					redirect[lastredirected].IsAppend = true
				} else {
					nextword = WORD_STDOUT
					lastredirected = 1
				}
			} else if ch == '<' {
				nextword = WORD_STDIN
				redirect[0].IsAppend = false
				lastredirected = 0
			} else {
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	terminate(&statements, &nextword, &redirect, &buffer, &argv, " ")
	return statements
}

func Parse2(statements []StatementT) [][]StatementT {
	result := make([][]StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		if statement1.Term != "|" {
			result = append(result, make([]StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) [][]StatementT {
	result1 := Parse1(text)
	result2 := Parse2(result1)
	return result2
}
<|endoftext|>