<commit_before>/*Package nmap parses Nmap XML data into a similary formed struct.*/
package nmap

import (
	"encoding/xml"
	"fmt"
	"strconv"
	"time"
)

type Timestamp time.Time

// str2time converts a string containing a UNIX timestamp to to a time.Time.
func (t *Timestamp) str2time(s string) (err error) {
	ts, err := strconv.Atoi(s)
	if err != nil {
		return
	}
	*t = Timestamp(time.Unix(int64(ts), 0))
	return
}

// time2str formats the time.Time value as a UNIX timestamp string.
// XXX these might also need to be changed to pointers. See str2time and UnmarshalXMLAttr.
func (t Timestamp) time2str() string {
	return fmt.Sprint(time.Time(t))
}

func (t Timestamp) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("\"%s\"", t.time2str())), nil
}

func (t Timestamp) UnmarshalJSON(b []byte) error {
	return t.str2time(string(b))
}

func (t *Timestamp) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
	return xml.Attr{Name: name, Value: t.time2str()}, nil
}

func (t *Timestamp) UnmarshalXMLAttr(attr xml.Attr) (err error) {
	return t.str2time(attr.Value)
}

// NmapRun is contains all the data for a single nmap scan.
type NmapRun struct {
	Scanner          string         `xml:"scanner,attr" json:"scanner"`
	Args             string         `xml:"args,attr" json:"args"`
	Start            Timestamp      `xml:"start,attr" json:"start"`
	StartStr         string         `xml:"startstr,attr" json:"startstr"`
	Version          string         `xml:"version,attr" json:"version"`
	ProfileName      string         `xml:"profile_name,attr" json:"profile_name"`
	XMLOutputVersion string         `xml:"xmloutputversion,attr" json:"xmloutputversion"`
	ScanInfo         ScanInfo       `xml:"scaninfo" json:"scaninfo"`
	Verbose          Verbose        `xml:"verbose" json:"verbose"`
	Debugging        Debugging      `xml:"debugging" json:"debugging"`
	TaskBegin        []Task         `xml:"taskbegin" json:"taskbegin"`
	TaskProgress     []TaskProgress `xml:"taskprogress" json:"taskprogress"`
	TaskEnd          []Task         `xml:"taskend" json:"taskend"`
	PreScripts       []Script       `xml:"prescript>script" json:"prescripts"`
	PostScripts      []Script       `xml:"postscript>script" json:"postscripts"`
	Hosts            []Host         `xml:"host" json:"hosts"`
	Targets          []Target       `xml:"target" json:"targets"`
	RunStats         RunStats       `xml:"runstats" json:"runstats"`
}

// ScanInfo contains informational regarding how the scan
// was run.
type ScanInfo struct {
	Type        string `xml:"type,attr" json:"type"`
	Protocol    string `xml:"protocol,attr" json:"protocol"`
	NumServices int    `xml:"numservices,attr" json:"numservices"`
	Services    string `xml:"services,attr" json:"services"`
	ScanFlags   string `xml:"scanflags,attr" json:"scanflags"`
}

// Verbose contains the verbosity level for the Nmap scan.
type Verbose struct {
	Level int `xml:"level,attr" json:"level"`
}

// Debugging contains the debugging level for the Nmap scan.
type Debugging struct {
	Level int `xml:"level,attr" json:"level"`
}

// Task contains information about started and stopped Nmap tasks.
type Task struct {
	Task      string    `xml:"task,attr" json:"task"`
	Time      Timestamp `xml:"time,attr" json:"time"`
	ExtraInfo string    `xml:"extrainfo,attr" json:"extrainfo"`
}

// TaskProgress contains information about the progression of a Task.
type TaskProgress struct {
	Task      string    `xml:"task,attr" json:"task"`
	Time      Timestamp `xml:"time,attr" json:"time"`
	Percent   float32   `xml:"percent,attr" json:"percent"`
	Remaining int       `xml:"remaining,attr" json:"remaining"`
	Etc       Timestamp `xml:"etc,attr" json:"etc"`
}

// Target is found in the Nmap xml spec. I have no idea what it
// actually is.
type Target struct {
	Specification string `xml:"specification,attr" json:"specification"`
	Status        string `xml:"status,attr" json:"status"`
	Reason        string `xml:"reason,attr" json:"reason"`
}

// Host contains all information about a single host.
type Host struct {
	StartTime     Timestamp     `xml:"starttime,attr" json:"starttime"`
	EndTime       Timestamp     `xml:"endtime,attr" json:"endtime"`
	Comment       string        `xml:"comment,attr" json:"comment"`
	Status        Status        `xml:"status" json:"status"`
	Addresses     []Address     `xml:"address" json:"addresses"`
	Hostnames     []Hostname    `xml:"hostnames>hostname" json:"hostnames"`
	Smurfs        []Smurf       `xml:"smurf" json:"smurfs"`
	Ports         []Port        `xml:"ports>port" json:"ports"`
	ExtraPorts    []ExtraPorts  `xml:"ports>extraports" json:"extraports"`
	Os            Os            `xml:"os" json:"os"`
	Distance      Distance      `xml:"distance" json:"distance"`
	Uptime        Uptime        `xml:"uptime" json:"uptime"`
	TcpSequence   TcpSequence   `xml:"tcpsequence" json:"tcpsequence"`
	IpIdSequence  IpIdSequence  `xml:"ipidsequence" json:"ipidsequence"`
	TcpTsSequence TcpTsSequence `xml:"tcptssequence" json:"tcptssequence"`
	HostScripts   []Script      `xml:"hostscript>script" json:"hostscripts"`
	Trace         Trace         `xml:"trace" json:"trace"`
	Times         Times         `xml:"times" json:"times"`
}

// Status is the host's status. Up, down, etc.
type Status struct {
	State     string  `xml:"state,attr" json:"state"`
	Reason    string  `xml:"reason,attr" json:"reason"`
	ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
}

// Address contains a IPv4 or IPv6 address for a Host.
type Address struct {
	Addr     string `xml:"addr,attr" json:"addr"`
	AddrType string `xml:"addrtype,attr" json:"addrtype"`
	Vendor   string `xml:"vendor,attr" json:"vendor"`
}

// Hostname is a single name for a Host.
type Hostname struct {
	Name string `xml:"name,attr" json:"name"`
	Type string `xml:"type,attr" json:"type"`
}

// Smurf contains repsonses from a smurf attack. I think.
// Smurf attacks, really?
type Smurf struct {
	Responses string `xml:"responses,attr" json:"responses"`
}

// ExtraPorts contains the information about the closed|filtered ports.
type ExtraPorts struct {
	State   string   `xml:"state,attr" json:"state"`
	Count   int      `xml:"count,attr" json:"count"`
	Reasons []Reason `xml:"extrareasons" json:"reasons"`
}
type Reason struct {
	Reason string `xml:"reason,attr" json:"reason"`
	Count  int    `xml:"count,attr" json:"count"`
}

// Port contains all the information about a scanned port.
type Port struct {
	Protocol string   `xml:"protocol,attr" json:"protocol"`
	PortId   int      `xml:"portid,attr" json:"id"`
	State    State    `xml:"state" json:"state"`
	Owner    Owner    `xml:"owner" json:"owner"`
	Service  Service  `xml:"service" json:"service"`
	Scripts  []Script `xml:"script" json:"scripts"`
}

// State contains information about a given ports
// status. State will be open, closed, etc.
type State struct {
	State     string  `xml:"state,attr" json:"state"`
	Reason    string  `xml:"reason,attr" json:"reason"`
	ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
	ReasonIP  string  `xml:"reason_ip,attr" json:"reason_ip"`
}

// Owner contains the name of Port.Owner.
type Owner struct {
	Name string `xml:"name,attr" json:"name"`
}

// Service contains detailed information about a Port's
// service details.
type Service struct {
	Name       string `xml:"name,attr" json:"name"`
	Conf       int    `xml:"conf,attr" json:"conf"`
	Method     string `xml:"method,attr" json:"method"`
	Version    string `xml:"version,attr" json:"version"`
	Product    string `xml:"product,attr" json:"product"`
	ExtraInfo  string `xml:"extrainfo,attr" json:"extrainfo"`
	Tunnel     string `xml:"tunnel,attr" json:"tunnel"`
	Proto      string `xml:"proto,attr" json:"proto"`
	Rpcnum     string `xml:"rpcnum,attr" json:"rpcnum"`
	Lowver     string `xml:"lowver,attr" json:"lowver"`
	Highver    string `xml:"hiver,attr" json:"hiver"`
	Hostname   string `xml:"hostname,attr" json:"hostname"`
	OsType     string `xml:"ostype,attr" json:"ostype"`
	DeviceType string `xml:"devicetype,attr" json:"devicetype"`
	ServiceFp  string `xml:"servicefp,attr" json:"servicefp"`
	CPEs       []CPE  `xml:"cpe" json:"cpes"`
}

// CPE (Common Platform Enumeration) is a standardized way to name software
// applications, operating systems, and hardware platforms.
type CPE string

// Script contains information from Nmap Scripting Engine.
type Script struct {
	Id     string  `xml:"id,attr" json:"id"`
	Output string  `xml:"output,attr" json:"output"`
	Tables []Table `xml:"table" json:"tables"`
}

// Table contains the output of the script in a more parse-able form.
// ToDo: This should be a map[string][]string
type Table struct {
	Key      string   `xml:"key,attr" json:"key"`
	Elements []string `xml:"elem" json:"elements"`
}

// Os contains the fingerprinted operating system for a Host.
type Os struct {
	PortsUsed      []PortUsed      `xml:"portused" json:"portsused"`
	OsMatches      []OsMatch       `xml:"osmatch" json:"osmatches"`
	OsFingerprints []OsFingerprint `xml:"osfingerprint" json:"osfingerprints"`
}

// PortsUsed is the port used to fingerprint a Os.
type PortUsed struct {
	State  string `xml:"state,attr" json:"state"`
	Proto  string `xml:"proto,attr" json:"proto"`
	PortId int    `xml:"portid,attr" json:"portid"`
}

// OsClass contains vendor information for an Os.
type OsClass struct {
	Vendor   string `xml:"vendor,attr" json:"vendor"`
	OsGen    string `xml"osgen,attr"`
	Type     string `xml:"type,attr" json:"type"`
	Accuracy string `xml:"accurancy,attr" json:"accurancy"`
	OsFamily string `xml:"osfamily,attr" json:"osfamily"`
	CPEs     []CPE  `xml:"cpe" json:"cpes"`
}

// OsMatch contains detailed information regarding a Os fingerprint.
type OsMatch struct {
	Name      string    `xml:"name,attr" json:"name"`
	Accuracy  string    `xml:"accuracy,attr" json:"accuracy"`
	Line      string    `xml:"line,attr" json:"line"`
	OsClasses []OsClass `xml:"osclass" json:"osclasses"`
}

// OsFingerprint is the actual fingerprint string.
type OsFingerprint struct {
	Fingerprint string `xml:"fingerprint,attr" json:"fingerprint"`
}

// Distance is the amount of hops to a particular host.
type Distance struct {
	Value int `xml:"value,attr" json:"value"`
}

// Uptime is the amount of time the host has been up.
type Uptime struct {
	Seconds  int    `xml:"seconds,attr" json:"seconds"`
	Lastboot string `xml:"lastboot,attr" json:"lastboot"`
}

// TcpSequence contains information regarding the detected tcp sequence.
type TcpSequence struct {
	Index      int    `xml:"index,attr" json:"index"`
	Difficulty string `xml:"difficulty,attr" json:"difficulty"`
	Values     string `xml:"vaules,attr" json:"vaules"`
}

// Sequence contains information regarding the detected X sequence.
type Sequence struct {
	Class  string `xml:"class,attr" json:"class"`
	Values string `xml:"values,attr" json:"values"`
}
type IpIdSequence Sequence
type TcpTsSequence Sequence

// Trace contains the hops to a Host.
type Trace struct {
	Proto string `xml:"proto,attr" json:"proto"`
	Port  int    `xml:"port,attr" json:"port"`
	Hops  []Hop  `xml:"hop" json:"hops"`
}

// Hop is a ip hop to a Host.
type Hop struct {
	TTL    float32 `xml:"ttl,attr" json:"ttl"`
	RTT    float32 `xml:"rtt,attr" json:"rtt"`
	IPAddr string  `xml:"ipaddr,attr" json:"ipaddr"`
	Host   string  `xml:"host,attr" json:"host"`
}

// Times contains time statistics for an Nmap scan.
type Times struct {
	SRTT string `xml:"srtt,attr" json:"srtt"`
	RTT  string `xml:"rttvar,attr" json:"rttv"`
	To   string `xml:"to,attr" json:"to"`
}

// RunStats contains statistics for a
// finished Nmap scan.
type RunStats struct {
	Finished Finished  `xml:"finished" json:"finished"`
	Hosts    HostStats `xml:"hosts" json:"hosts"`
}

// Finished contains detailed statistics regarding
// a finished Nmap scan.
type Finished struct {
	Time     Timestamp `xml:"time,attr" json:"time"`
	TimeStr  string    `xml:"timestr,attr" json:"timestr"`
	Elapsed  float32   `xml:"elapsed,attr" json:"elapsed"`
	Summary  string    `xml:"summary,attr" json:"summary"`
	Exit     string    `xml:"exit,attr" json:"exit"`
	ErrorMsg string    `xml:"errormsg,attr" json:"errormsg"`
}

// HostStats contains the amount of up and down hosts and the total count.
type HostStats struct {
	Up    int `xml:"up,attr" json:"up"`
	Down  int `xml:"down,attr" json:"down"`
	Total int `xml:"total,attr" json:"total"`
}

// Parse takes a byte array of nmap xml data and unmarshals it into an
// NmapRun struct. All elements are returned as strings, it is up to the caller
// to check and cast them to the proper type.
func Parse(content []byte) (*NmapRun, error) {
	r := &NmapRun{}
	err := xml.Unmarshal(content, r)
	return r, err
}
<commit_msg>Fix Marshal producing data Unmarshal can't parse<commit_after>/*Package nmap parses Nmap XML data into a similary formed struct.*/
package nmap

import (
	"encoding/xml"
	"strconv"
	"time"
)

// Timestamp represents time as a UNIX timestamp in seconds.
type Timestamp time.Time

// str2time converts a string containing a UNIX timestamp to to a time.Time.
func (t *Timestamp) str2time(s string) error {
	ts, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return err
	}
	*t = Timestamp(time.Unix(ts, 0))
	return nil
}

// time2str formats the time.Time value as a UNIX timestamp string.
// XXX these might also need to be changed to pointers. See str2time and UnmarshalXMLAttr.
func (t Timestamp) time2str() string {
	return strconv.FormatInt(time.Time(t).Unix(), 10)
}

func (t Timestamp) MarshalJSON() ([]byte, error) {
	return []byte(t.time2str()), nil
}

func (t *Timestamp) UnmarshalJSON(b []byte) error {
	return t.str2time(string(b))
}

func (t Timestamp) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
	return xml.Attr{Name: name, Value: t.time2str()}, nil
}

func (t *Timestamp) UnmarshalXMLAttr(attr xml.Attr) (err error) {
	return t.str2time(attr.Value)
}

// NmapRun is contains all the data for a single nmap scan.
type NmapRun struct {
	Scanner          string         `xml:"scanner,attr" json:"scanner"`
	Args             string         `xml:"args,attr" json:"args"`
	Start            Timestamp      `xml:"start,attr" json:"start"`
	StartStr         string         `xml:"startstr,attr" json:"startstr"`
	Version          string         `xml:"version,attr" json:"version"`
	ProfileName      string         `xml:"profile_name,attr" json:"profile_name"`
	XMLOutputVersion string         `xml:"xmloutputversion,attr" json:"xmloutputversion"`
	ScanInfo         ScanInfo       `xml:"scaninfo" json:"scaninfo"`
	Verbose          Verbose        `xml:"verbose" json:"verbose"`
	Debugging        Debugging      `xml:"debugging" json:"debugging"`
	TaskBegin        []Task         `xml:"taskbegin" json:"taskbegin"`
	TaskProgress     []TaskProgress `xml:"taskprogress" json:"taskprogress"`
	TaskEnd          []Task         `xml:"taskend" json:"taskend"`
	PreScripts       []Script       `xml:"prescript>script" json:"prescripts"`
	PostScripts      []Script       `xml:"postscript>script" json:"postscripts"`
	Hosts            []Host         `xml:"host" json:"hosts"`
	Targets          []Target       `xml:"target" json:"targets"`
	RunStats         RunStats       `xml:"runstats" json:"runstats"`
}

// ScanInfo contains informational regarding how the scan
// was run.
type ScanInfo struct {
	Type        string `xml:"type,attr" json:"type"`
	Protocol    string `xml:"protocol,attr" json:"protocol"`
	NumServices int    `xml:"numservices,attr" json:"numservices"`
	Services    string `xml:"services,attr" json:"services"`
	ScanFlags   string `xml:"scanflags,attr" json:"scanflags"`
}

// Verbose contains the verbosity level for the Nmap scan.
type Verbose struct {
	Level int `xml:"level,attr" json:"level"`
}

// Debugging contains the debugging level for the Nmap scan.
type Debugging struct {
	Level int `xml:"level,attr" json:"level"`
}

// Task contains information about started and stopped Nmap tasks.
type Task struct {
	Task      string    `xml:"task,attr" json:"task"`
	Time      Timestamp `xml:"time,attr" json:"time"`
	ExtraInfo string    `xml:"extrainfo,attr" json:"extrainfo"`
}

// TaskProgress contains information about the progression of a Task.
type TaskProgress struct {
	Task      string    `xml:"task,attr" json:"task"`
	Time      Timestamp `xml:"time,attr" json:"time"`
	Percent   float32   `xml:"percent,attr" json:"percent"`
	Remaining int       `xml:"remaining,attr" json:"remaining"`
	Etc       Timestamp `xml:"etc,attr" json:"etc"`
}

// Target is found in the Nmap xml spec. I have no idea what it
// actually is.
type Target struct {
	Specification string `xml:"specification,attr" json:"specification"`
	Status        string `xml:"status,attr" json:"status"`
	Reason        string `xml:"reason,attr" json:"reason"`
}

// Host contains all information about a single host.
type Host struct {
	StartTime     Timestamp     `xml:"starttime,attr" json:"starttime"`
	EndTime       Timestamp     `xml:"endtime,attr" json:"endtime"`
	Comment       string        `xml:"comment,attr" json:"comment"`
	Status        Status        `xml:"status" json:"status"`
	Addresses     []Address     `xml:"address" json:"addresses"`
	Hostnames     []Hostname    `xml:"hostnames>hostname" json:"hostnames"`
	Smurfs        []Smurf       `xml:"smurf" json:"smurfs"`
	Ports         []Port        `xml:"ports>port" json:"ports"`
	ExtraPorts    []ExtraPorts  `xml:"ports>extraports" json:"extraports"`
	Os            Os            `xml:"os" json:"os"`
	Distance      Distance      `xml:"distance" json:"distance"`
	Uptime        Uptime        `xml:"uptime" json:"uptime"`
	TcpSequence   TcpSequence   `xml:"tcpsequence" json:"tcpsequence"`
	IpIdSequence  IpIdSequence  `xml:"ipidsequence" json:"ipidsequence"`
	TcpTsSequence TcpTsSequence `xml:"tcptssequence" json:"tcptssequence"`
	HostScripts   []Script      `xml:"hostscript>script" json:"hostscripts"`
	Trace         Trace         `xml:"trace" json:"trace"`
	Times         Times         `xml:"times" json:"times"`
}

// Status is the host's status. Up, down, etc.
type Status struct {
	State     string  `xml:"state,attr" json:"state"`
	Reason    string  `xml:"reason,attr" json:"reason"`
	ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
}

// Address contains a IPv4 or IPv6 address for a Host.
type Address struct {
	Addr     string `xml:"addr,attr" json:"addr"`
	AddrType string `xml:"addrtype,attr" json:"addrtype"`
	Vendor   string `xml:"vendor,attr" json:"vendor"`
}

// Hostname is a single name for a Host.
type Hostname struct {
	Name string `xml:"name,attr" json:"name"`
	Type string `xml:"type,attr" json:"type"`
}

// Smurf contains repsonses from a smurf attack. I think.
// Smurf attacks, really?
type Smurf struct {
	Responses string `xml:"responses,attr" json:"responses"`
}

// ExtraPorts contains the information about the closed|filtered ports.
type ExtraPorts struct {
	State   string   `xml:"state,attr" json:"state"`
	Count   int      `xml:"count,attr" json:"count"`
	Reasons []Reason `xml:"extrareasons" json:"reasons"`
}
type Reason struct {
	Reason string `xml:"reason,attr" json:"reason"`
	Count  int    `xml:"count,attr" json:"count"`
}

// Port contains all the information about a scanned port.
type Port struct {
	Protocol string   `xml:"protocol,attr" json:"protocol"`
	PortId   int      `xml:"portid,attr" json:"id"`
	State    State    `xml:"state" json:"state"`
	Owner    Owner    `xml:"owner" json:"owner"`
	Service  Service  `xml:"service" json:"service"`
	Scripts  []Script `xml:"script" json:"scripts"`
}

// State contains information about a given ports
// status. State will be open, closed, etc.
type State struct {
	State     string  `xml:"state,attr" json:"state"`
	Reason    string  `xml:"reason,attr" json:"reason"`
	ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
	ReasonIP  string  `xml:"reason_ip,attr" json:"reason_ip"`
}

// Owner contains the name of Port.Owner.
type Owner struct {
	Name string `xml:"name,attr" json:"name"`
}

// Service contains detailed information about a Port's
// service details.
type Service struct {
	Name       string `xml:"name,attr" json:"name"`
	Conf       int    `xml:"conf,attr" json:"conf"`
	Method     string `xml:"method,attr" json:"method"`
	Version    string `xml:"version,attr" json:"version"`
	Product    string `xml:"product,attr" json:"product"`
	ExtraInfo  string `xml:"extrainfo,attr" json:"extrainfo"`
	Tunnel     string `xml:"tunnel,attr" json:"tunnel"`
	Proto      string `xml:"proto,attr" json:"proto"`
	Rpcnum     string `xml:"rpcnum,attr" json:"rpcnum"`
	Lowver     string `xml:"lowver,attr" json:"lowver"`
	Highver    string `xml:"hiver,attr" json:"hiver"`
	Hostname   string `xml:"hostname,attr" json:"hostname"`
	OsType     string `xml:"ostype,attr" json:"ostype"`
	DeviceType string `xml:"devicetype,attr" json:"devicetype"`
	ServiceFp  string `xml:"servicefp,attr" json:"servicefp"`
	CPEs       []CPE  `xml:"cpe" json:"cpes"`
}

// CPE (Common Platform Enumeration) is a standardized way to name software
// applications, operating systems, and hardware platforms.
type CPE string

// Script contains information from Nmap Scripting Engine.
type Script struct {
	Id     string  `xml:"id,attr" json:"id"`
	Output string  `xml:"output,attr" json:"output"`
	Tables []Table `xml:"table" json:"tables"`
}

// Table contains the output of the script in a more parse-able form.
// ToDo: This should be a map[string][]string
type Table struct {
	Key      string   `xml:"key,attr" json:"key"`
	Elements []string `xml:"elem" json:"elements"`
}

// Os contains the fingerprinted operating system for a Host.
type Os struct {
	PortsUsed      []PortUsed      `xml:"portused" json:"portsused"`
	OsMatches      []OsMatch       `xml:"osmatch" json:"osmatches"`
	OsFingerprints []OsFingerprint `xml:"osfingerprint" json:"osfingerprints"`
}

// PortsUsed is the port used to fingerprint a Os.
type PortUsed struct {
	State  string `xml:"state,attr" json:"state"`
	Proto  string `xml:"proto,attr" json:"proto"`
	PortId int    `xml:"portid,attr" json:"portid"`
}

// OsClass contains vendor information for an Os.
type OsClass struct {
	Vendor   string `xml:"vendor,attr" json:"vendor"`
	OsGen    string `xml"osgen,attr"`
	Type     string `xml:"type,attr" json:"type"`
	Accuracy string `xml:"accurancy,attr" json:"accurancy"`
	OsFamily string `xml:"osfamily,attr" json:"osfamily"`
	CPEs     []CPE  `xml:"cpe" json:"cpes"`
}

// OsMatch contains detailed information regarding a Os fingerprint.
type OsMatch struct {
	Name      string    `xml:"name,attr" json:"name"`
	Accuracy  string    `xml:"accuracy,attr" json:"accuracy"`
	Line      string    `xml:"line,attr" json:"line"`
	OsClasses []OsClass `xml:"osclass" json:"osclasses"`
}

// OsFingerprint is the actual fingerprint string.
type OsFingerprint struct {
	Fingerprint string `xml:"fingerprint,attr" json:"fingerprint"`
}

// Distance is the amount of hops to a particular host.
type Distance struct {
	Value int `xml:"value,attr" json:"value"`
}

// Uptime is the amount of time the host has been up.
type Uptime struct {
	Seconds  int    `xml:"seconds,attr" json:"seconds"`
	Lastboot string `xml:"lastboot,attr" json:"lastboot"`
}

// TcpSequence contains information regarding the detected tcp sequence.
type TcpSequence struct {
	Index      int    `xml:"index,attr" json:"index"`
	Difficulty string `xml:"difficulty,attr" json:"difficulty"`
	Values     string `xml:"vaules,attr" json:"vaules"`
}

// Sequence contains information regarding the detected X sequence.
type Sequence struct {
	Class  string `xml:"class,attr" json:"class"`
	Values string `xml:"values,attr" json:"values"`
}
type IpIdSequence Sequence
type TcpTsSequence Sequence

// Trace contains the hops to a Host.
type Trace struct {
	Proto string `xml:"proto,attr" json:"proto"`
	Port  int    `xml:"port,attr" json:"port"`
	Hops  []Hop  `xml:"hop" json:"hops"`
}

// Hop is a ip hop to a Host.
type Hop struct {
	TTL    float32 `xml:"ttl,attr" json:"ttl"`
	RTT    float32 `xml:"rtt,attr" json:"rtt"`
	IPAddr string  `xml:"ipaddr,attr" json:"ipaddr"`
	Host   string  `xml:"host,attr" json:"host"`
}

// Times contains time statistics for an Nmap scan.
type Times struct {
	SRTT string `xml:"srtt,attr" json:"srtt"`
	RTT  string `xml:"rttvar,attr" json:"rttv"`
	To   string `xml:"to,attr" json:"to"`
}

// RunStats contains statistics for a
// finished Nmap scan.
type RunStats struct {
	Finished Finished  `xml:"finished" json:"finished"`
	Hosts    HostStats `xml:"hosts" json:"hosts"`
}

// Finished contains detailed statistics regarding
// a finished Nmap scan.
type Finished struct {
	Time     Timestamp `xml:"time,attr" json:"time"`
	TimeStr  string    `xml:"timestr,attr" json:"timestr"`
	Elapsed  float32   `xml:"elapsed,attr" json:"elapsed"`
	Summary  string    `xml:"summary,attr" json:"summary"`
	Exit     string    `xml:"exit,attr" json:"exit"`
	ErrorMsg string    `xml:"errormsg,attr" json:"errormsg"`
}

// HostStats contains the amount of up and down hosts and the total count.
type HostStats struct {
	Up    int `xml:"up,attr" json:"up"`
	Down  int `xml:"down,attr" json:"down"`
	Total int `xml:"total,attr" json:"total"`
}

// Parse takes a byte array of nmap xml data and unmarshals it into an
// NmapRun struct. All elements are returned as strings, it is up to the caller
// to check and cast them to the proper type.
func Parse(content []byte) (*NmapRun, error) {
	r := &NmapRun{}
	err := xml.Unmarshal(content, r)
	return r, err
}
<|endoftext|>


<commit_before>package nnmc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/html/charset"
)

// NNMc values:
// client http.Client with cookie
type NNMc struct {
	client http.Client
}

// Topic from forum
type Topic struct {
	Href    string
	Name    string
	Year    string
	Quality string
}

// Film all values
// ID            id
// Name          Название
// EngName       Английское название
// Href          Ссылка
// Year          Год
// Genre         Жанр
// Country       Производство
// Director      Режиссер
// Producer      Продюсер
// Actors        Актеры
// Description   Описание
// Age           Возраст
// ReleaseDate   Дата мировой премьеры
// RussianDate   Дата премьеры в России
// Duration      Продолжительность
// Quality       Качество видео
// Translation   Перевод
// SubtitlesType Вид субтитров
// Subtitles     Субтитры
// Video         Видео
// Audio         Аудио
// Kinopoisk     Рейтинг кинопоиска
// Imdb          Рейтинг IMDb
// NNM           Рейтинг nnm-club
// Sound         Звук
// Size          Размер
// DateCreate    Дата создания раздачи
// Torrent       Ссылка на torrent
// Poster        Ссылка на постер
// UpdatedAt     Дата обновления записи БД
// CreatedAt     Дата создания записи БД
type Film struct {
	ID            int64     `gorm:"column:id"             db:"id"             sql:"AUTO_INCREMENT"`
	Name          string    `gorm:"column:name"           db:"name"           sql:"type:text"`
	EngName       string    `gorm:"column:eng_name"       db:"eng_name"       sql:"type:text"`
	Href          string    `gorm:"column:href"           db:"href"           sql:"type:text"`
	Year          int64     `gorm:"column:year"           db:"year"`
	Genre         string    `gorm:"column:genre"          db:"genre"          sql:"type:text"`
	Country       string    `gorm:"column:country"        db:"country"        sql:"type:text"`
	Director      string    `gorm:"column:director"       db:"director"       sql:"type:text"`
	Producer      string    `gorm:"column:producer"       db:"producer"       sql:"type:text"`
	Actors        string    `gorm:"column:actors"         db:"actors"         sql:"type:text"`
	Description   string    `gorm:"column:description"    db:"description"    sql:"type:text"`
	Age           string    `gorm:"column:age"            db:"age"            sql:"type:text"`
	ReleaseDate   string    `gorm:"column:release_date"   db:"release_date"   sql:"type:text"`
	RussianDate   string    `gorm:"column:russian_date"   db:"russian_date"   sql:"type:text"`
	Duration      int64     `gorm:"column:duration"       db:"duration"`
	Quality       string    `gorm:"column:quality"        db:"quality"        sql:"type:text"`
	Translation   string    `gorm:"column:translation"    db:"translation"    sql:"type:text"`
	SubtitlesType string    `gorm:"column:subtitles_type" db:"subtitles_type" sql:"type:text"`
	Subtitles     string    `gorm:"column:subtitles"      db:"subtitles"      sql:"type:text"`
	Video         string    `gorm:"column:video"          db:"video"          sql:"type:text"`
	Audio         string    `gorm:"column:audio"          db:"audio"          sql:"type:text"`
	Kinopoisk     float64   `gorm:"column:kinopoisk"      db:"kinopoisk"`
	IMDb          float64   `gorm:"column:imdb"           db:"imdb"`
	NNM           float64   `gorm:"column:nnm"            db:"nnm"`
	Sound         string    `gorm:"column:sound"          db:"sound"          sql:"type:text"`
	Size          int64     `gorm:"column:size"           db:"size"`
	DateCreate    string    `gorm:"column:date_create"    db:"date_create"`
	Torrent       string    `gorm:"column:torrent"        db:"torrent"`
	Poster        string    `gorm:"column:poster"         db:"poster"`
	UpdatedAt     time.Time `gorm:"column:updated_at"     db:"updated_at"`
	CreatedAt     time.Time `gorm:"column:created_at"     db:"created_at"`
}

// Init nnmc with login password
func Init(login string, password string) (*NNMc, error) {
	var client http.Client
	cookieJar, _ := cookiejar.New(nil)
	client.Jar = cookieJar
	urlPost := "http://nnm-club.me/forum/login.php"
	form := url.Values{}
	form.Set("username", login)
	form.Add("password", password)
	form.Add("redirect", "")
	form.Add("login", "âõîä")
	req, _ := http.NewRequest("POST", urlPost, bytes.NewBufferString(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
	_, err := client.Do(req)
	return &NNMc{client: client}, err
}

// getHTML get body from url
func getHTML(url string, n *NNMc) ([]byte, error) {
	resp, err := n.client.Get(url)
	if err != nil {
		log.Println("client Get error:", err)
		return nil, err
	}
	defer resp.Body.Close()
	utf8body, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		log.Println("Encoding error:", err)
		return nil, err
	}
	doc, err := ioutil.ReadAll(utf8body)
	if err != nil {
		log.Println("ioutil.ReadAll error:", err)
	}
	doc = replaceAll(doc, " ", " ")
	doc = replaceAll(doc, "&", "&")
	return doc, nil
}

// ParseForumTree get topics from forumTree
func (n *NNMc) ParseForumTree(url string) ([]Topic, error) {
	var (
		topics []Topic
		reTree = regexp.MustCompile(`<a href="(viewtopic.php\?t=\d+)"class="topictitle">(.+?)\s\((\d{4})\)\s(.+?)</a>`)
	)
	body, err := getHTML(url, n)
	if err != nil {
		return topics, err
	}
	if reTree.Match(body) == false {
		return topics, fmt.Errorf("No topic in body")
	}
	findResult := reTree.FindAllSubmatch(body, -1)
	for _, v := range findResult {
		var t Topic
		t.Href = "http://nnm-club.me/forum/" + string(v[1])
		t.Name = string(v[2])
		t.Year = string(v[3])
		t.Quality = string(v[4])
		topics = append(topics, t)
	}
	return topics, nil
}

// ParseTopic get film from topic
func (n *NNMc) ParseTopic(topic Topic) (Film, error) {
	var (
		film     Film
		reTopic  = regexp.MustCompile(`(Производство|Жанр|Режиссер|Продюсер|Актеры|Описание фильма|Описание|Возраст|Дата мировой премьеры|Дата премьеры в России|Дата Российской премьеры|Дата российской премьеры|Продолжительность|Качество видео|Качество|Перевод|Вид субтитров|Субтитры|Видео|Аудио):\s*<\/span>(.+?)<`)
		reDate   = regexp.MustCompile(`> (\d{1,2} .{3} \d{4}).{9}<`)
		reSize   = regexp.MustCompile(`Размер блока: \d.+?B"> (\d{1,2},\d{1,2}|\d{3,4}|\d{1,2})\s`)
		reRating = regexp.MustCompile(`>(\d,\d|\d)<\/span>.+?\(Голосов:`)
		reDl     = regexp.MustCompile(`<a href="download\.php\?id=(\d{5,7})" rel="nofollow">Скачать<`)
		reImg    = regexp.MustCompile(`"postImg postImgAligned img-right" title="http:\/\/assets\.nnm-club\.ws\/forum\/image\.php\?link=(.+?jpe{0,1}g)`)
	)
	name := strings.Split(topic.Name, " / ")
	switch len(name) {
	case 1:
		film.Name = strings.Trim(name[0], " ")
	case 2:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	case 3:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	}
	film.Href = topic.Href
	if year64, err := strconv.ParseInt(topic.Year, 10, 64); err == nil {
		film.Year = year64
	}
	body, err := getHTML(film.Href, n)
	if err != nil {
		return film, err
	}
	// var reFn = regexp.MustCompile(`(\d{6})`)
	// filename := string(reFn.Find([]byte(film.Href)))
	// _ = ioutil.WriteFile(filename+".html", body, 0644)
	if reTopic.Match(body) == false {
		return film, fmt.Errorf("No topic in body")
	}
	findAttrs := reTopic.FindAllSubmatch(body, -1)
	for _, v := range findAttrs {
		one := strings.Trim(string(v[1]), " ")
		two := strings.Replace(string(v[2]), "
", "", -1)
		two = strings.Trim(two, " ")
		switch one {
		case "Производство":
			film.Country = two
		case "Жанр":
			film.Genre = strings.ToLower(two)
		case "Режиссер":
			film.Director = two
		case "Продюсер":
			film.Producer = two
		case "Актеры":
			film.Actors = two
		case "Описание фильма", "Описание":
			film.Description = two
		case "Возраст":
			film.Age = two
		case "Дата мировой премьеры":
			film.ReleaseDate = two
		case "Дата премьеры в России", "Дата российской премьеры", "Дата Российской премьеры":
			film.RussianDate = two
		case "Продолжительность":
			if i64, err := strconv.ParseInt(two, 10, 64); err == nil {
				film.Duration = i64
			}
		case "Качество видео", "Качество":
			film.Quality = two
		case "Перевод":
			if caseInsensitiveContains(two, "не требуется") == false {
				film.Translation = two
			} else {
				film.Translation = "Не требуется"
			}
		case "Вид субтитров":
			film.SubtitlesType = two
		case "Субтитры":
			film.Subtitles = two
		case "Видео":
			film.Video = two
		case "Аудио":
			film.Audio = two
		}
	}
	// if reDl.Match(body) == false {
	// 	return film, fmt.Errorf("No torrent url in body")
	// }
	findDl := reDl.FindAllSubmatch(body, -1)
	film.Torrent = "http://nnm-club.me/forum/download.php?id=" + string(findDl[0][1])
	if reDate.Match(body) == true {
		film.DateCreate = replaceDate(string(reDate.FindSubmatch(body)[1]))
	}
	if reSize.Match(body) == true {
		size := string(reSize.FindSubmatch(body)[1])
		size = strings.Replace(size, ",", ".", -1)
		if s64, err := strconv.ParseFloat(size, 64); err == nil {
			if s64 < 100 {
				s64 = s64 * 1000
			}
			film.Size = int64(s64)
		}
	}
	if reRating.Match(body) == true {
		rating := string(reRating.FindSubmatch(body)[1])
		rating = strings.Replace(rating, ",", ".", -1)
		if r64, err := strconv.ParseFloat(rating, 64); err == nil {
			film.NNM = r64
		}
	}
	if reImg.Match(body) == true {
		film.Poster = string(reImg.FindSubmatch(body)[1])
	}
	return film, nil
}

func replaceAll(body []byte, from string, to string) []byte {
	var reStr = regexp.MustCompile(from)
	result := reStr.ReplaceAll(body, []byte(to))
	return result
}

func replaceDate(s string) string {
	s = strings.Replace(s, " Янв ", ".01.", -1)
	s = strings.Replace(s, " Фев ", ".02.", -1)
	s = strings.Replace(s, " Мар ", ".03.", -1)
	s = strings.Replace(s, " Апр ", ".04.", -1)
	s = strings.Replace(s, " Май ", ".05.", -1)
	s = strings.Replace(s, " Июн ", ".06.", -1)
	s = strings.Replace(s, " Июл ", ".07.", -1)
	s = strings.Replace(s, " Авг ", ".08.", -1)
	s = strings.Replace(s, " Сен ", ".09.", -1)
	s = strings.Replace(s, " Окт ", ".10.", -1)
	s = strings.Replace(s, " Ноя ", ".11.", -1)
	s = strings.Replace(s, " Дек ", ".12.", -1)
	return s
}

func caseInsensitiveContains(s, substr string) bool {
	s, substr = strings.ToUpper(s), strings.ToUpper(substr)
	return strings.Contains(s, substr)
}
<commit_msg>update Film struct, move parse topic to file<commit_after>package nnmc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/html/charset"
)

// NNMc values:
// client http.Client with cookie
type NNMc struct {
	client http.Client
}

// Topic from forum
type Topic struct {
	Href    string
	Name    string
	Year    string
	Quality string
	Body    []byte
}

// Film all values
// ID            id
// Name          Название
// EngName       Английское название
// Href          Ссылка
// Year          Год
// Genre         Жанр
// Country       Производство
// Director      Режиссер
// Producer      Продюсер
// Actors        Актеры
// Description   Описание
// Age           Возраст
// ReleaseDate   Дата мировой премьеры
// RussianDate   Дата премьеры в России
// Duration      Продолжительность
// Quality       Качество видео
// Translation   Перевод
// SubtitlesType Вид субтитров
// Subtitles     Субтитры
// Video         Видео
// Resolution    Разрешение видео
// Audio         Аудио
// Kinopoisk     Рейтинг кинопоиска
// Imdb          Рейтинг IMDb
// NNM           Рейтинг nnm-club
// Sound         Звук
// Size          Размер
// DateCreate    Дата создания раздачи
// Torrent       Ссылка на torrent
// Poster        Ссылка на постер
// Seeders       Количество раздающих
// Leechers      Количество скачивающих
// UpdatedAt     Дата обновления записи БД
// CreatedAt     Дата создания записи БД
type Film struct {
	ID            int64     `gorm:"column:id"             db:"id"             sql:"AUTO_INCREMENT"`
	Name          string    `gorm:"column:name"           db:"name"           sql:"type:text"`
	EngName       string    `gorm:"column:eng_name"       db:"eng_name"       sql:"type:text"`
	Href          string    `gorm:"column:href"           db:"href"           sql:"type:text"`
	Year          int64     `gorm:"column:year"           db:"year"`
	Genre         string    `gorm:"column:genre"          db:"genre"          sql:"type:text"`
	Country       string    `gorm:"column:country"        db:"country"        sql:"type:text"`
	Director      string    `gorm:"column:director"       db:"director"       sql:"type:text"`
	Producer      string    `gorm:"column:producer"       db:"producer"       sql:"type:text"`
	Actors        string    `gorm:"column:actors"         db:"actors"         sql:"type:text"`
	Description   string    `gorm:"column:description"    db:"description"    sql:"type:text"`
	Age           string    `gorm:"column:age"            db:"age"            sql:"type:text"`
	ReleaseDate   string    `gorm:"column:release_date"   db:"release_date"   sql:"type:text"`
	RussianDate   string    `gorm:"column:russian_date"   db:"russian_date"   sql:"type:text"`
	Duration      int64     `gorm:"column:duration"       db:"duration"`
	Quality       string    `gorm:"column:quality"        db:"quality"        sql:"type:text"`
	Translation   string    `gorm:"column:translation"    db:"translation"    sql:"type:text"`
	SubtitlesType string    `gorm:"column:subtitles_type" db:"subtitles_type" sql:"type:text"`
	Subtitles     string    `gorm:"column:subtitles"      db:"subtitles"      sql:"type:text"`
	Video         string    `gorm:"column:video"          db:"video"          sql:"type:text"`
	Resolution    string    `gorm:"column:resolution"     db:"resolution"     sql:"type:text"`
	Audio         string    `gorm:"column:audio"          db:"audio"          sql:"type:text"`
	Kinopoisk     float64   `gorm:"column:kinopoisk"      db:"kinopoisk"`
	IMDb          float64   `gorm:"column:imdb"           db:"imdb"`
	NNM           float64   `gorm:"column:nnm"            db:"nnm"`
	Sound         string    `gorm:"column:sound"          db:"sound"          sql:"type:text"`
	Size          int64     `gorm:"column:size"           db:"size"`
	DateCreate    string    `gorm:"column:date_create"    db:"date_create"`
	Torrent       string    `gorm:"column:torrent"        db:"torrent"`
	Poster        string    `gorm:"column:poster"         db:"poster"`
	Seeders       int64     `gorm:"column:seeders"        db:"seeders"`
	Leechers      int64     `gorm:"column:leechers"       db:"leechers"`
	UpdatedAt     time.Time `gorm:"column:updated_at"     db:"updated_at"`
	CreatedAt     time.Time `gorm:"column:created_at"     db:"created_at"`
}

// Init nnmc with login password
func Init(login string, password string) (*NNMc, error) {
	var client http.Client
	cookieJar, _ := cookiejar.New(nil)
	client.Jar = cookieJar
	urlPost := "http://nnm-club.me/forum/login.php"
	form := url.Values{}
	form.Set("username", login)
	form.Add("password", password)
	form.Add("redirect", "")
	form.Add("login", "âõîä")
	req, _ := http.NewRequest("POST", urlPost, bytes.NewBufferString(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
	_, err := client.Do(req)
	return &NNMc{client: client}, err
}

// getHTML get body from url
func getHTML(url string, n *NNMc) ([]byte, error) {
	resp, err := n.client.Get(url)
	if err != nil {
		log.Println("client Get error:", err)
		return nil, err
	}
	defer resp.Body.Close()
	utf8body, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		log.Println("Encoding error:", err)
		return nil, err
	}
	doc, err := ioutil.ReadAll(utf8body)
	if err != nil {
		log.Println("ioutil.ReadAll error:", err)
	}
	doc = replaceAll(doc, " ", " ")
	doc = replaceAll(doc, "&", "&")
	return doc, nil
}

// ParseForumTree get topics from forumTree
func (n *NNMc) ParseForumTree(url string) ([]Topic, error) {
	var (
		topics []Topic
		reTree = regexp.MustCompile(`<a href="(viewtopic.php\?t=\d+)"class="topictitle">(.+?)\s\((\d{4})\)\s(.+?)</a>`)
	)
	body, err := getHTML(url, n)
	if err != nil {
		return topics, err
	}
	if reTree.Match(body) == false {
		return topics, fmt.Errorf("No topic in body")
	}
	findResult := reTree.FindAllSubmatch(body, -1)
	for _, v := range findResult {
		var t Topic
		t.Href = "http://nnm-club.me/forum/" + string(v[1])
		t.Name = string(v[2])
		t.Year = string(v[3])
		t.Quality = string(v[4])
		topics = append(topics, t)
	}
	return topics, nil
}

// ParseTopic get film from topic
func (n *NNMc) ParseTopic(topic Topic) (Film, error) {
	var (
		film Film
	)
	name := strings.Split(topic.Name, " / ")
	switch len(name) {
	case 1:
		film.Name = strings.Trim(name[0], " ")
	case 2:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	case 3:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	}
	film.Href = topic.Href
	if year64, err := strconv.ParseInt(topic.Year, 10, 64); err == nil {
		film.Year = year64
	}
	body, err := getHTML(film.Href, n)
	if err != nil {
		return film, err
	}
	topic.Body = body
	film.Country = topic.getCountry()
	film.Genre = topic.getGenre()
	film.Director = topic.getDirector()
	film.Producer = topic.getProducer()
	film.Actors = topic.getActors()
	film.Description = topic.getDescription()
	film.Age = topic.getAge()
	film.ReleaseDate = topic.getReleaseDate()
	film.RussianDate = topic.getRussianDate()
	film.Duration = topic.getDuration()
	film.Quality = topic.getQuality()
	film.Translation = topic.getTranslation()
	film.SubtitlesType = topic.getSubtitlesType()
	film.Subtitles = topic.getSubtitles()
	film.Video = topic.getVideo()
	film.Resolution = getResolution(film.Video)
	film.Audio = topic.getAudio()
	film.Torrent = topic.getTorrent()
	film.DateCreate = topic.getDate()
	film.Size = topic.getSize()
	film.NNM = topic.getRating()
	film.Poster = topic.getPoster()
	film.Seeders = topic.getSeeds()
	film.Leechers = topic.getLeechs()
	return film, nil
}

func replaceAll(body []byte, from string, to string) []byte {
	var reStr = regexp.MustCompile(from)
	result := reStr.ReplaceAll(body, []byte(to))
	return result
}

func replaceDate(s string) string {
	s = strings.Replace(s, " Янв ", ".01.", -1)
	s = strings.Replace(s, " Фев ", ".02.", -1)
	s = strings.Replace(s, " Мар ", ".03.", -1)
	s = strings.Replace(s, " Апр ", ".04.", -1)
	s = strings.Replace(s, " Май ", ".05.", -1)
	s = strings.Replace(s, " Июн ", ".06.", -1)
	s = strings.Replace(s, " Июл ", ".07.", -1)
	s = strings.Replace(s, " Авг ", ".08.", -1)
	s = strings.Replace(s, " Сен ", ".09.", -1)
	s = strings.Replace(s, " Окт ", ".10.", -1)
	s = strings.Replace(s, " Ноя ", ".11.", -1)
	s = strings.Replace(s, " Дек ", ".12.", -1)
	return s
}

func caseInsensitiveContains(s, substr string) bool {
	s, substr = strings.ToUpper(s), strings.ToUpper(substr)
	return strings.Contains(s, substr)
}

func cleanStr(str string) string {
	var reSpan = regexp.MustCompile("")
	str = strings.Replace(str, "
", "", -1)
	str = reSpan.ReplaceAllString(str, "")
	str = strings.Trim(str, " ")
	return str
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const (
	defaultEnv      = "NOTI_DEFAULT"
	pushbulletEnv   = "NOTI_PUSHBULLET_TOK"
	slackChannelEnv = "NOTI_SLACK_DEST"
	slackEnv        = "NOTI_SLACK_TOK"
	soundEnv        = "NOTI_SOUND"
	voiceEnv        = "NOTI_VOICE"

	version = "2.0.0-rc2"
)

var (
	title       = flag.String("t", "noti", "")
	message     = flag.String("m", "Done!", "")
	showVersion = flag.Bool("v", false, "")
	showHelp    = flag.Bool("h", false, "")

	// Notifications
	banner     = flag.Bool("b", false, "")
	pushbullet = flag.Bool("p", false, "")
	speech     = flag.Bool("s", false, "")
	slack      = flag.Bool("k", false, "")
)

func init() {
	flag.StringVar(title, "title", "noti", "")
	flag.StringVar(message, "message", "Done!", "")
	flag.BoolVar(showVersion, "version", false, "")
	flag.BoolVar(showHelp, "help", false, "")

	// Notifications
	flag.BoolVar(banner, "banner", false, "")
	flag.BoolVar(speech, "speech", false, "")
	flag.BoolVar(pushbullet, "pushbullet", false, "")
	flag.BoolVar(slack, "slack", false, "")
}

func main() {
	log.SetFlags(0)
	flag.Parse()

	if *showVersion {
		fmt.Printf("noti version %s\n", version)
		return
	}
	if *showHelp {
		flag.Usage()
		return
	}

	runUtility()

	if defs := strings.TrimSpace(os.Getenv(defaultEnv)); defs != "" {
		*banner = strings.Contains(defs, "banner")
		*speech = strings.Contains(defs, "speech")
		*pushbullet = strings.Contains(defs, "pushbullet")
		*slack = strings.Contains(defs, "slack")
	} else {
		var explicitSet bool
		var val bool

		flag.Visit(func(f *flag.Flag) {
			if f.Name == "b" || f.Name == "banner" {
				explicitSet = true
				// Ignoring error, false on error is fine.
				val, _ = strconv.ParseBool(f.Value.String())
			}
		})

		// If the user explicitly set -banner, then use the value that the user
		// set, but if no banner flag was set, then the default is true.
		if explicitSet {
			*banner = val
		} else {
			*banner = true
		}
	}

	if *banner {
		bannerNotify()
	}
	if *speech {
		speechNotify()
	}
	if *pushbullet {
		pushbulletNotify()
	}
	if *slack {
		slackNotify()
	}
}

func runUtility() {
	var cmd *exec.Cmd

	args := flag.Args()
	if len(args) < 1 {
		return
	}

	cmd = exec.Command(args[0], args[1:]...)
	*title = args[0]

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		*title = *title + " failed"
		*message = err.Error()
	}
}
<commit_msg>Handle utility fail better<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const (
	version = "2.0.0-rc2"

	defaultEnv      = "NOTI_DEFAULT"
	pushbulletEnv   = "NOTI_PUSHBULLET_TOK"
	slackChannelEnv = "NOTI_SLACK_DEST"
	slackEnv        = "NOTI_SLACK_TOK"
	soundEnv        = "NOTI_SOUND"
	voiceEnv        = "NOTI_VOICE"
)

var (
	title       = flag.String("t", "noti", "")
	message     = flag.String("m", "Done!", "")
	showVersion = flag.Bool("v", false, "")
	showHelp    = flag.Bool("h", false, "")

	// Notifications
	banner     = flag.Bool("b", false, "")
	pushbullet = flag.Bool("p", false, "")
	speech     = flag.Bool("s", false, "")
	slack      = flag.Bool("k", false, "")
)

func init() {
	flag.StringVar(title, "title", "noti", "")
	flag.StringVar(message, "message", "Done!", "")
	flag.BoolVar(showVersion, "version", false, "")
	flag.BoolVar(showHelp, "help", false, "")

	// Notifications
	flag.BoolVar(banner, "banner", false, "")
	flag.BoolVar(speech, "speech", false, "")
	flag.BoolVar(pushbullet, "pushbullet", false, "")
	flag.BoolVar(slack, "slack", false, "")
}

func main() {
	log.SetFlags(0)
	flag.Parse()

	if *showVersion {
		fmt.Println("noti version", version)
		return
	}
	if *showHelp {
		flag.Usage()
		return
	}

	runUtility()

	if defs := strings.TrimSpace(os.Getenv(defaultEnv)); defs != "" {
		*banner = strings.Contains(defs, "banner")
		*speech = strings.Contains(defs, "speech")
		*pushbullet = strings.Contains(defs, "pushbullet")
		*slack = strings.Contains(defs, "slack")
	} else {
		var explicitSet bool
		var val bool

		flag.Visit(func(f *flag.Flag) {
			if f.Name == "b" || f.Name == "banner" {
				explicitSet = true
				// Ignoring error, false on error is fine.
				val, _ = strconv.ParseBool(f.Value.String())
			}
		})

		// If the user explicitly set -banner, then use the value that the user
		// set, but if no banner flag was set, then the default is true.
		if explicitSet {
			*banner = val
		} else {
			*banner = true
		}
	}

	if *banner {
		bannerNotify()
	}
	if *speech {
		speechNotify()
	}
	if *pushbullet {
		pushbulletNotify()
	}
	if *slack {
		slackNotify()
	}
}

func runUtility() {
	var cmd *exec.Cmd

	args := flag.Args()
	if len(args) < 1 {
		return
	}

	cmd = exec.Command(args[0], args[1:]...)
	*title = args[0]

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if exerr, is := err.(*exec.ExitError); is {
		if !exerr.Success() {
			*title = *title + " failed"
		}
	}
	if err != nil {
		*message = err.Error()
	}
}
<|endoftext|>


<commit_before>// obfs4 connection wrappers

package gost

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sync"
	"time"

	"github.com/go-log/log"

	pt "git.torproject.org/pluggable-transports/goptlib.git"
	"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
	"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4"
)

type obfsHTTPTransporter struct {
	tcpTransporter
}

// ObfsHTTPTransporter creates a Transporter that is used by HTTP obfuscating tunnel client.
func ObfsHTTPTransporter() Transporter {
	return &obfsHTTPTransporter{}
}

func (tr *obfsHTTPTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
	opts := &HandshakeOptions{}
	for _, option := range options {
		option(opts)
	}
	return &obfsHTTPConn{Conn: conn, host: opts.Host}, nil
}

type obfsHTTPListener struct {
	net.Listener
}

// ObfsHTTPListener creates a Listener for HTTP obfuscating tunnel server.
func ObfsHTTPListener(addr string) (Listener, error) {
	laddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	ln, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return nil, err
	}
	return &obfsHTTPListener{Listener: tcpKeepAliveListener{ln}}, nil
}

func (l *obfsHTTPListener) Accept() (net.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}

	return &obfsHTTPConn{Conn: conn, isServer: true}, nil
}

type obfsHTTPConn struct {
	net.Conn
	host           string
	buf            []byte
	isServer       bool
	handshaked     bool
	handshakeMutex sync.Mutex
}

func (c *obfsHTTPConn) Handshake() (err error) {
	c.handshakeMutex.Lock()
	defer c.handshakeMutex.Unlock()

	if c.handshaked {
		return nil
	}

	if c.isServer {
		err = c.serverHandshake()
	} else {
		err = c.clientHandshake()
	}
	if err != nil {
		return
	}

	c.handshaked = true
	return nil
}

func (c *obfsHTTPConn) serverHandshake() (err error) {
	br := bufio.NewReader(c.Conn)
	r, err := http.ReadRequest(br)
	if err != nil {
		return
	}
	if Debug {
		dump, _ := httputil.DumpRequest(r, false)
		log.Logf("[ohttp] %s -> %s\n%s", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), string(dump))
	}

	if r.ContentLength > 0 {
		c.buf, err = ioutil.ReadAll(r.Body)
	} else {
		c.buf, err = br.Peek(br.Buffered())
	}

	if err != nil {
		log.Logf("[ohttp] %s -> %s : %v", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), err)
		return
	}

	b := bytes.Buffer{}
	if r.Header.Get("Upgrade") == "websocket" {
		b.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
		b.WriteString("Server: nginx/1.10.0\r\n")
		b.WriteString("Date: " + time.Now().Format(time.RFC1123) + "\r\n")
		b.WriteString("Connection: Upgrade\r\n")
		b.WriteString("Upgrade: websocket\r\n")
		b.WriteString(fmt.Sprintf("Sec-WebSocket-Accept: %s\r\n", computeAcceptKey(r.Header.Get("Sec-WebSocket-Key"))))
		b.WriteString("\r\n")
	} else {
		b.WriteString("HTTP/1.1 200 OK\r\n")
		b.WriteString("Server: nginx/1.10.0\r\n")
		b.WriteString("Date: " + time.Now().Format(time.RFC1123) + "\r\n")
		b.WriteString("Content-Type: application/octet-stream\r\n")
		b.WriteString("Connection: keep-alive\r\n")
		b.WriteString("Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n")
		b.WriteString("Pragma: no-cache\r\n")
		b.WriteString("\r\n")
	}
	if Debug {
		log.Logf("[ohttp] %s <- %s\n%s", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), b.String())
	}
	_, err = b.WriteTo(c.Conn)
	return
}

func (c *obfsHTTPConn) clientHandshake() (err error) {
	r := &http.Request{
		Method:     http.MethodGet,
		ProtoMajor: 1,
		ProtoMinor: 1,
		URL:        &url.URL{Scheme: "http", Host: c.host},
		Header:     make(http.Header),
	}
	r.Header.Set("User-Agent", "curl/7.49.1")
	r.Header.Set("Connection", "Upgrade")
	r.Header.Set("Upgrade", "websocket")
	key, _ := generateChallengeKey()
	r.Header.Set("Sec-WebSocket-Key", key)

	if err = r.Write(c.Conn); err != nil {
		return
	}
	if Debug {
		dump, _ := httputil.DumpRequest(r, false)
		log.Logf("[ohttp] %s -> %s\n%s", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), string(dump))
	}
	var resp *http.Response
	resp, err = http.ReadResponse(bufio.NewReader(c.Conn), r)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Logf("[ohttp] %s <- %s\n%s", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), string(dump))
	}

	return nil
}

func (c *obfsHTTPConn) Read(b []byte) (n int, err error) {
	if err = c.Handshake(); err != nil {
		return
	}
	if len(c.buf) > 0 {
		n = copy(b, c.buf)
		c.buf = c.buf[n:]
		return
	}
	return c.Conn.Read(b)
}

func (c *obfsHTTPConn) Write(b []byte) (n int, err error) {
	if err = c.Handshake(); err != nil {
		return
	}
	return c.Conn.Write(b)
}

type obfs4Context struct {
	cf    base.ClientFactory
	cargs interface{} // type obfs4ClientArgs
	sf    base.ServerFactory
	sargs *pt.Args
}

var obfs4Map = make(map[string]obfs4Context)

// Obfs4Init initializes the obfs client or server based on isServeNode
func Obfs4Init(node Node, isServeNode bool) error {
	if _, ok := obfs4Map[node.Addr]; ok {
		return fmt.Errorf("obfs4 context already inited")
	}

	t := new(obfs4.Transport)

	stateDir := node.Values.Get("state-dir")
	if stateDir == "" {
		stateDir = "."
	}

	ptArgs := pt.Args(node.Values)

	if !isServeNode {
		cf, err := t.ClientFactory(stateDir)
		if err != nil {
			return err
		}

		cargs, err := cf.ParseArgs(&ptArgs)
		if err != nil {
			return err
		}

		obfs4Map[node.Addr] = obfs4Context{cf: cf, cargs: cargs}
	} else {
		sf, err := t.ServerFactory(stateDir, &ptArgs)
		if err != nil {
			return err
		}

		sargs := sf.Args()

		obfs4Map[node.Addr] = obfs4Context{sf: sf, sargs: sargs}

		log.Log("[obfs4] server inited:", obfs4ServerURL(node))
	}

	return nil
}

func obfs4GetContext(addr string) (obfs4Context, error) {
	ctx, ok := obfs4Map[addr]
	if !ok {
		return obfs4Context{}, fmt.Errorf("obfs4 context not inited")
	}
	return ctx, nil
}

func obfs4ServerURL(node Node) string {
	ctx, err := obfs4GetContext(node.Addr)
	if err != nil {
		return ""
	}

	values := (*url.Values)(ctx.sargs)
	query := values.Encode()
	return fmt.Sprintf(
		"%s+%s://%s/?%s", //obfs4-cert=%s&iat-mode=%s",
		node.Protocol,
		node.Transport,
		node.Addr,
		query,
	)
}

func obfs4ClientConn(addr string, conn net.Conn) (net.Conn, error) {
	ctx, err := obfs4GetContext(addr)
	if err != nil {
		return nil, err
	}

	pseudoDial := func(a, b string) (net.Conn, error) { return conn, nil }
	return ctx.cf.Dial("tcp", "", pseudoDial, ctx.cargs)
}

func obfs4ServerConn(addr string, conn net.Conn) (net.Conn, error) {
	ctx, err := obfs4GetContext(addr)
	if err != nil {
		return nil, err
	}

	return ctx.sf.WrapConn(conn)
}

type obfs4Transporter struct {
	tcpTransporter
}

// Obfs4Transporter creates a Transporter that is used by obfs4 client.
func Obfs4Transporter() Transporter {
	return &obfs4Transporter{}
}

func (tr *obfs4Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
	opts := &HandshakeOptions{}
	for _, option := range options {
		option(opts)
	}

	timeout := opts.Timeout
	if timeout <= 0 {
		timeout = HandshakeTimeout
	}
	conn.SetDeadline(time.Now().Add(timeout))
	defer conn.SetDeadline(time.Time{})

	return obfs4ClientConn(opts.Addr, conn)
}

type obfs4Listener struct {
	addr string
	net.Listener
}

// Obfs4Listener creates a Listener for obfs4 server.
func Obfs4Listener(addr string) (Listener, error) {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	l := &obfs4Listener{
		addr:     addr,
		Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
	}
	return l, nil
}

func (l *obfs4Listener) Accept() (net.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	cc, err := obfs4ServerConn(l.addr, conn)
	if err != nil {
		conn.Close()
		return nil, err
	}
	return cc, nil
}
<commit_msg>fix obfs-http server<commit_after>// obfs4 connection wrappers

package gost

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sync"
	"time"

	"github.com/go-log/log"

	pt "git.torproject.org/pluggable-transports/goptlib.git"
	"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
	"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4"
)

type obfsHTTPTransporter struct {
	tcpTransporter
}

// ObfsHTTPTransporter creates a Transporter that is used by HTTP obfuscating tunnel client.
func ObfsHTTPTransporter() Transporter {
	return &obfsHTTPTransporter{}
}

func (tr *obfsHTTPTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
	opts := &HandshakeOptions{}
	for _, option := range options {
		option(opts)
	}
	return &obfsHTTPConn{Conn: conn, host: opts.Host}, nil
}

type obfsHTTPListener struct {
	net.Listener
}

// ObfsHTTPListener creates a Listener for HTTP obfuscating tunnel server.
func ObfsHTTPListener(addr string) (Listener, error) {
	laddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	ln, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return nil, err
	}
	return &obfsHTTPListener{Listener: tcpKeepAliveListener{ln}}, nil
}

func (l *obfsHTTPListener) Accept() (net.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}

	return &obfsHTTPConn{Conn: conn, isServer: true}, nil
}

type obfsHTTPConn struct {
	net.Conn
	host           string
	rbuf           bytes.Buffer
	wbuf           bytes.Buffer
	isServer       bool
	handshaked     bool
	handshakeMutex sync.Mutex
}

func (c *obfsHTTPConn) Handshake() (err error) {
	c.handshakeMutex.Lock()
	defer c.handshakeMutex.Unlock()

	if c.handshaked {
		return nil
	}

	if c.isServer {
		err = c.serverHandshake()
	} else {
		err = c.clientHandshake()
	}
	if err != nil {
		return
	}

	c.handshaked = true
	return nil
}

func (c *obfsHTTPConn) serverHandshake() (err error) {
	br := bufio.NewReader(c.Conn)
	r, err := http.ReadRequest(br)
	if err != nil {
		return
	}
	if Debug {
		dump, _ := httputil.DumpRequest(r, false)
		log.Logf("[ohttp] %s -> %s\n%s", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), string(dump))
	}

	if r.ContentLength > 0 {
		_, err = io.Copy(&c.rbuf, r.Body)
	} else {
		var b []byte
		b, err = br.Peek(br.Buffered())
		if len(b) > 0 {
			_, err = c.rbuf.Write(b)
		}
	}
	if err != nil {
		log.Logf("[ohttp] %s -> %s : %v", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), err)
		return
	}

	b := bytes.Buffer{}
	if r.Header.Get("Upgrade") == "websocket" {
		b.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
		b.WriteString("Server: nginx/1.10.0\r\n")
		b.WriteString("Date: " + time.Now().Format(time.RFC1123) + "\r\n")
		b.WriteString("Connection: Upgrade\r\n")
		b.WriteString("Upgrade: websocket\r\n")
		b.WriteString(fmt.Sprintf("Sec-WebSocket-Accept: %s\r\n", computeAcceptKey(r.Header.Get("Sec-WebSocket-Key"))))
		b.WriteString("\r\n")
	} else {
		b.WriteString("HTTP/1.1 200 OK\r\n")
		b.WriteString("Server: nginx/1.10.0\r\n")
		b.WriteString("Date: " + time.Now().Format(time.RFC1123) + "\r\n")
		b.WriteString("Content-Type: application/octet-stream\r\n")
		b.WriteString("Connection: keep-alive\r\n")
		b.WriteString("Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n")
		b.WriteString("Pragma: no-cache\r\n")
		b.WriteString("\r\n")
	}
	if Debug {
		log.Logf("[ohttp] %s <- %s\n%s", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), b.String())
	}

	if c.rbuf.Len() > 0 {
		c.wbuf = b // cache the response header if there are extra data in the request body.
		return
	}

	_, err = b.WriteTo(c.Conn)
	return
}

func (c *obfsHTTPConn) clientHandshake() (err error) {
	r := &http.Request{
		Method:     http.MethodGet,
		ProtoMajor: 1,
		ProtoMinor: 1,
		URL:        &url.URL{Scheme: "http", Host: c.host},
		Header:     make(http.Header),
	}
	r.Header.Set("User-Agent", "curl/7.49.1")
	r.Header.Set("Connection", "Upgrade")
	r.Header.Set("Upgrade", "websocket")
	key, _ := generateChallengeKey()
	r.Header.Set("Sec-WebSocket-Key", key)

	if err = r.Write(c.Conn); err != nil {
		return
	}
	if Debug {
		dump, _ := httputil.DumpRequest(r, false)
		log.Logf("[ohttp] %s -> %s\n%s", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), string(dump))
	}
	var resp *http.Response
	resp, err = http.ReadResponse(bufio.NewReader(c.Conn), r)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Logf("[ohttp] %s <- %s\n%s", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), string(dump))
	}

	return nil
}

func (c *obfsHTTPConn) Read(b []byte) (n int, err error) {
	if err = c.Handshake(); err != nil {
		return
	}
	if c.rbuf.Len() > 0 {
		return c.rbuf.Read(b)
	}
	return c.Conn.Read(b)
}

func (c *obfsHTTPConn) Write(b []byte) (n int, err error) {
	if err = c.Handshake(); err != nil {
		return
	}

	if c.wbuf.Len() > 0 {
		c.wbuf.Write(b) // append the data to the cached header
		_, err = c.wbuf.WriteTo(c.Conn)
		n = len(b) // exclude the header length
		return
	}
	return c.Conn.Write(b)
}

type obfs4Context struct {
	cf    base.ClientFactory
	cargs interface{} // type obfs4ClientArgs
	sf    base.ServerFactory
	sargs *pt.Args
}

var obfs4Map = make(map[string]obfs4Context)

// Obfs4Init initializes the obfs client or server based on isServeNode
func Obfs4Init(node Node, isServeNode bool) error {
	if _, ok := obfs4Map[node.Addr]; ok {
		return fmt.Errorf("obfs4 context already inited")
	}

	t := new(obfs4.Transport)

	stateDir := node.Values.Get("state-dir")
	if stateDir == "" {
		stateDir = "."
	}

	ptArgs := pt.Args(node.Values)

	if !isServeNode {
		cf, err := t.ClientFactory(stateDir)
		if err != nil {
			return err
		}

		cargs, err := cf.ParseArgs(&ptArgs)
		if err != nil {
			return err
		}

		obfs4Map[node.Addr] = obfs4Context{cf: cf, cargs: cargs}
	} else {
		sf, err := t.ServerFactory(stateDir, &ptArgs)
		if err != nil {
			return err
		}

		sargs := sf.Args()

		obfs4Map[node.Addr] = obfs4Context{sf: sf, sargs: sargs}

		log.Log("[obfs4] server inited:", obfs4ServerURL(node))
	}

	return nil
}

func obfs4GetContext(addr string) (obfs4Context, error) {
	ctx, ok := obfs4Map[addr]
	if !ok {
		return obfs4Context{}, fmt.Errorf("obfs4 context not inited")
	}
	return ctx, nil
}

func obfs4ServerURL(node Node) string {
	ctx, err := obfs4GetContext(node.Addr)
	if err != nil {
		return ""
	}

	values := (*url.Values)(ctx.sargs)
	query := values.Encode()
	return fmt.Sprintf(
		"%s+%s://%s/?%s", //obfs4-cert=%s&iat-mode=%s",
		node.Protocol,
		node.Transport,
		node.Addr,
		query,
	)
}

func obfs4ClientConn(addr string, conn net.Conn) (net.Conn, error) {
	ctx, err := obfs4GetContext(addr)
	if err != nil {
		return nil, err
	}

	pseudoDial := func(a, b string) (net.Conn, error) { return conn, nil }
	return ctx.cf.Dial("tcp", "", pseudoDial, ctx.cargs)
}

func obfs4ServerConn(addr string, conn net.Conn) (net.Conn, error) {
	ctx, err := obfs4GetContext(addr)
	if err != nil {
		return nil, err
	}

	return ctx.sf.WrapConn(conn)
}

type obfs4Transporter struct {
	tcpTransporter
}

// Obfs4Transporter creates a Transporter that is used by obfs4 client.
func Obfs4Transporter() Transporter {
	return &obfs4Transporter{}
}

func (tr *obfs4Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
	opts := &HandshakeOptions{}
	for _, option := range options {
		option(opts)
	}

	timeout := opts.Timeout
	if timeout <= 0 {
		timeout = HandshakeTimeout
	}
	conn.SetDeadline(time.Now().Add(timeout))
	defer conn.SetDeadline(time.Time{})

	return obfs4ClientConn(opts.Addr, conn)
}

type obfs4Listener struct {
	addr string
	net.Listener
}

// Obfs4Listener creates a Listener for obfs4 server.
func Obfs4Listener(addr string) (Listener, error) {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	l := &obfs4Listener{
		addr:     addr,
		Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
	}
	return l, nil
}

func (l *obfs4Listener) Accept() (net.Conn, error) {
	conn, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	cc, err := obfs4ServerConn(l.addr, conn)
	if err != nil {
		conn.Close()
		return nil, err
	}
	return cc, nil
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013-2015 Oryx(ossrs)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// The os defines:
//      bsd: darwin dragonfly freebsd nacl netbsd openbsd solaris
//      unix: bsd linux
//      server: unix plan9
//      posix: bsd linux windows
// All os by go:
//      server windows
//      posix plan9

package main

import (
	"flag"
	"fmt"
	"github.com/ossrs/go-oryx/app"
	"github.com/ossrs/go-oryx/core"
	"os"
)

func serve(svr *app.Server) int {
	if err := svr.PrepareLogger(); err != nil {
		core.Error.Println("prepare logger failed, err is", err)
		return -1
	}

	oryxMain(svr)

	core.Trace.Println(core.OryxSigServer())
	core.Trace.Println(core.OryxSigCopyright)
	core.Trace.Println(core.OryxSigProduct)

	if err := svr.Initialize(); err != nil {
		core.Error.Println("initialize server failed, err is", err)
		return -1
	}

	if err := svr.Run(); err != nil {
		core.Error.Println("run server failed, err is", err)
		return -1
	}

	return 0
}

func main() {
	// the args format:
	//          -c conf/oryx.json
	//          --c conf/oryx.json
	//          -c=conf/oryx.json
	//          --c=conf/oryx.json
	//          --conf=conf/oryx.json
	var confFile string
	if true {
		dv := ""
		ua := "the config file"
		flag.StringVar(&confFile, "c", dv, ua)
		flag.StringVar(&confFile, "conf", dv, ua)
	}

	var showHelp bool
	if true {
		dv := false
		ua := "print version"
		flag.BoolVar(&showHelp, "v", dv, ua)
		flag.BoolVar(&showHelp, "V", dv, ua)
		flag.BoolVar(&showHelp, "version", dv, ua)
	}

	var showSignature bool
	if true {
		dv := false
		ua := "print signature"
		flag.BoolVar(&showSignature, "g", dv, ua)
		flag.BoolVar(&showSignature, "signature", dv, ua)
	}

	flag.Usage = func() {
		fmt.Println(fmt.Sprintf("Usage: %v [-c|--conf <filename>] [-?|-h|--help] [-v|-V|--version] [-g|--signature]", os.Args[0]))
		fmt.Println(fmt.Sprintf("	-c, --conf filename     : the config file path"))
		fmt.Println(fmt.Sprintf("	-?, -h, --help          : show this help and exit"))
		fmt.Println(fmt.Sprintf("	-v, -V, --version       : print version and exit"))
		fmt.Println(fmt.Sprintf("	-g, --signature         : print server signature and exit"))
		fmt.Println(fmt.Sprintf("For example:"))
		fmt.Println(fmt.Sprintf("	%v -c conf/oryx.json", os.Args[0]))
	}
	flag.Parse()

	if showHelp {
		fmt.Fprintln(os.Stderr, core.Version())
		os.Exit(0)
	}

	if showSignature {
		fmt.Fprintln(os.Stderr, core.OryxSigServer())
		os.Exit(0)
	}

	if len(confFile) == 0 {
		flag.Usage()
		os.Exit(0)
	}

	fmt.Println(fmt.Sprintf("%v signature is %v", core.OryxSigName, core.OryxSigServer()))
	ret := func() int {
		svr := app.NewServer()
		defer svr.Close()

		if err := svr.ParseConfig(confFile); err != nil {
			core.Error.Println("parse config from", confFile, "failed, err is", err)
			return -1
		}

		return run(svr)
	}()

	os.Exit(ret)
}
<commit_msg>refine code.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013-2015 Oryx(ossrs)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// The os defines:
//      bsd: darwin dragonfly freebsd nacl netbsd openbsd solaris
//      unix: bsd linux
//      server: unix plan9
//      posix: bsd linux windows
// All os by go:
//      server windows
//      posix plan9

package main

import (
	"flag"
	"fmt"
	"github.com/ossrs/go-oryx/app"
	"github.com/ossrs/go-oryx/core"
	"os"
)

func serve(svr *app.Server) int {
	if err := svr.PrepareLogger(); err != nil {
		core.Error.Println("prepare logger failed, err is", err)
		return -1
	}

	oryxMain(svr)

	core.Trace.Println(core.OryxSigServer())
	core.Trace.Println(core.OryxSigCopyright)
	core.Trace.Println(core.OryxSigProduct)

	if err := svr.Initialize(); err != nil {
		core.Error.Println("initialize server failed, err is", err)
		return -1
	}

	if err := svr.Run(); err != nil {
		core.Error.Println("run server failed, err is", err)
		return -1
	}

	return 0
}

func main() {
	// the args format:
	//          -c conf/oryx.json
	//          --c conf/oryx.json
	//          -c=conf/oryx.json
	//          --c=conf/oryx.json
	//          --conf=conf/oryx.json
	var confFile string
	if true {
		dv := ""
		ua := "the config file"
		flag.StringVar(&confFile, "c", dv, ua)
		flag.StringVar(&confFile, "conf", dv, ua)
	}

	var showHelp bool
	if true {
		dv := false
		ua := "print version"
		flag.BoolVar(&showHelp, "v", dv, ua)
		flag.BoolVar(&showHelp, "V", dv, ua)
		flag.BoolVar(&showHelp, "version", dv, ua)
	}

	var showSignature bool
	if true {
		dv := false
		ua := "print signature"
		flag.BoolVar(&showSignature, "g", dv, ua)
		flag.BoolVar(&showSignature, "signature", dv, ua)
	}

	flag.Usage = func() {
		fmt.Println(fmt.Sprintf("Usage: %v [-c|--conf <filename>] [-?|-h|--help] [-v|-V|--version] [-g|--signature]", os.Args[0]))
		fmt.Println(fmt.Sprintf("	    -c, --conf filename     : the config file path"))
		fmt.Println(fmt.Sprintf("	    -?, -h, --help          : show this help and exit"))
		fmt.Println(fmt.Sprintf("	    -v, -V, --version       : print version and exit"))
		fmt.Println(fmt.Sprintf("	    -g, --signature         : print server signature and exit"))
		fmt.Println(fmt.Sprintf("For example:"))
		fmt.Println(fmt.Sprintf("	    %v -c conf/oryx.json", os.Args[0]))
	}
	flag.Parse()

	if showHelp {
		fmt.Fprintln(os.Stderr, core.Version())
		os.Exit(0)
	}

	if showSignature {
		fmt.Fprintln(os.Stderr, core.OryxSigServer())
		os.Exit(0)
	}

	if len(confFile) == 0 {
		flag.Usage()
		os.Exit(0)
	}

	fmt.Println(fmt.Sprintf("%v signature is %v", core.OryxSigName, core.OryxSigServer()))
	ret := func() int {
		svr := app.NewServer()
		defer svr.Close()

		if err := svr.ParseConfig(confFile); err != nil {
			core.Error.Println("parse config from", confFile, "failed, err is", err)
			return -1
		}

		return run(svr)
	}()

	os.Exit(ret)
}
<|endoftext|>


<commit_before>package intarr

import (
	"bytes"
	"compress/gzip"
	"encoding/binary"
	"fmt"
	"github.com/golang/snappy"
	"github.com/reducedb/encoding/cursor"
	"github.com/zhenjl/encoding/delta/bp32"
	"io"
)

func Pack(in []int32) (r []byte, e error) {
	Sort(in)
	w := bytes.NewBuffer(nil)
	if len(in) <= 48 {
		_, e = w.Write([]byte{'n'})
		if e != nil {
			return
		}
		for _, v := range in {
			e = binary.Write(w, binary.BigEndian, v)
			if e != nil {
				return
			}
		}
		return w.Bytes(), nil
	} else {
		_, e = w.Write([]byte{'p'})
		if e != nil {
			return
		}
		b, e := encode(in)
		if e != nil {
			return nil, e
		}
		_, e = w.Write(b)
		return w.Bytes(), nil
	}
}

func Unpack(data []byte) (arr []int32, e error) {
	if len(data) < 5 {
		return nil, fmt.Errorf("eror len is %d", len(data))
	}
	r := bytes.NewReader(data)
	var t = make([]byte, 1)
	_, e = r.Read(t)
	if e != nil {
		return
	}
	switch t[0] {
	case 'p':
		return decode(data[1:])
	case 'n':
		l := len(data[1:]) / 4
		for i := 0; i < l; i++ {
			var num int32
			e = binary.Read(r, binary.BigEndian, &num)
			if e != nil {
				return
			}
			arr = append(arr, num)
		}
		return
	default:
		e = fmt.Errorf("Unknow encoding type")
		return
	}

}

func compress(in []int32) (arr []int32, e error) {
	l := len(in)
	if len(in)%128 != 0 {
		l = l + (128 - len(in)%128)
	}

	arr = make([]int32, l)
	copy(arr, in)

	compdata := make([]int32, 2*l)
	inpos := cursor.New()
	outpos := cursor.New()
	codec := bp32.New()
	e = codec.Compress(arr, inpos, l, compdata, outpos)
	if e != nil {
		return nil, e
	}

	return compdata[:outpos.Get()+1], e
}

func encode(in []int32) ([]byte, error) {

	arr, e := compress(in)
	if e != nil {
		return nil, e
	}

	w := bytes.NewBuffer(nil)
	for _, v := range arr {
		e = binary.Write(w, binary.BigEndian, v)
		if e != nil {
			return nil, e
		}
	}
	return w.Bytes(), nil
}

func uncompress(arr []int32) ([]int32, error) {
	newinpos := cursor.New()
	newoutpos := cursor.New()

	recov := make([]int32, 10000)

	codec := bp32.New()
	e := codec.Uncompress(arr, newinpos, len(arr)-1, recov, newoutpos)
	l := 0
	for i, v := range recov[:newoutpos.Get()] {
		if v != 0 {
			l = i
		}
	}
	return recov[:l+1], e
}

func decode(data []byte) ([]int32, error) {
	r := bytes.NewReader(data)

	var arr []int32
	var num int32
	for e := binary.Read(r, binary.BigEndian, &num); e == nil; e = binary.Read(r, binary.BigEndian, &num) {
		arr = append(arr, num)
	}
	return uncompress(arr)
}

func is2b(s Slice) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	for _, v := range s {
		e := binary.Write(w, binary.BigEndian, v)
		if e != nil {
			return nil, e
		}
	}
	return w.Bytes(), nil
}

func b2is(b []byte) (s Slice, e error) {
	r := bytes.NewReader(b)
	var i int32
	for e = binary.Read(r, binary.BigEndian, &i); e == nil; e = binary.Read(r, binary.BigEndian, &i) {
		s = append(s, i)
	}
	if e == io.EOF {
		e = nil
	}
	return
}

func gz(data []byte) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	gw, e := gzip.NewWriterLevel(w, gzip.BestCompression)
	if e != nil {
		return nil, e
	}
	_, e = gw.Write(data)
	if e != nil {
		return nil, e
	}
	e = gw.Close()
	return w.Bytes(), e
}

func PackGzip(s Slice) ([]byte, error) {
	b, e := is2b(s)
	if e != nil {
		return nil, e
	}
	return gz(b)
}

func snap(arr []byte) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	sw := snappy.NewWriter(w)
	_, e := sw.Write(arr)
	if e != nil {
		return nil, e
	}
	return w.Bytes(), nil
}

func PackSnappy(s Slice) ([]byte, error) {
	b, e := is2b(s)
	if e != nil {
		return nil, e
	}
	return snap(b)
}
<commit_msg>ungizp<commit_after>package intarr

import (
	"bytes"
	"compress/gzip"
	"encoding/binary"
	"fmt"
	"github.com/golang/snappy"
	"github.com/reducedb/encoding/cursor"
	"github.com/zhenjl/encoding/delta/bp32"
	"io"
)

func Pack(in []int32) (r []byte, e error) {
	Sort(in)
	w := bytes.NewBuffer(nil)
	if len(in) <= 48 {
		_, e = w.Write([]byte{'n'})
		if e != nil {
			return
		}
		for _, v := range in {
			e = binary.Write(w, binary.BigEndian, v)
			if e != nil {
				return
			}
		}
		return w.Bytes(), nil
	} else {
		_, e = w.Write([]byte{'p'})
		if e != nil {
			return
		}
		b, e := encode(in)
		if e != nil {
			return nil, e
		}
		_, e = w.Write(b)
		return w.Bytes(), nil
	}
}

func Unpack(data []byte) (arr []int32, e error) {
	if len(data) < 5 {
		return nil, fmt.Errorf("eror len is %d", len(data))
	}
	r := bytes.NewReader(data)
	var t = make([]byte, 1)
	_, e = r.Read(t)
	if e != nil {
		return
	}
	switch t[0] {
	case 'p':
		return decode(data[1:])
	case 'n':
		l := len(data[1:]) / 4
		for i := 0; i < l; i++ {
			var num int32
			e = binary.Read(r, binary.BigEndian, &num)
			if e != nil {
				return
			}
			arr = append(arr, num)
		}
		return
	default:
		e = fmt.Errorf("Unknow encoding type")
		return
	}

}

func compress(in []int32) (arr []int32, e error) {
	l := len(in)
	if len(in)%128 != 0 {
		l = l + (128 - len(in)%128)
	}

	arr = make([]int32, l)
	copy(arr, in)

	compdata := make([]int32, 2*l)
	inpos := cursor.New()
	outpos := cursor.New()
	codec := bp32.New()
	e = codec.Compress(arr, inpos, l, compdata, outpos)
	if e != nil {
		return nil, e
	}

	return compdata[:outpos.Get()+1], e
}

func encode(in []int32) ([]byte, error) {

	arr, e := compress(in)
	if e != nil {
		return nil, e
	}

	w := bytes.NewBuffer(nil)
	for _, v := range arr {
		e = binary.Write(w, binary.BigEndian, v)
		if e != nil {
			return nil, e
		}
	}
	return w.Bytes(), nil
}

func uncompress(arr []int32) ([]int32, error) {
	newinpos := cursor.New()
	newoutpos := cursor.New()

	recov := make([]int32, 10000)

	codec := bp32.New()
	e := codec.Uncompress(arr, newinpos, len(arr)-1, recov, newoutpos)
	l := 0
	for i, v := range recov[:newoutpos.Get()] {
		if v != 0 {
			l = i
		}
	}
	return recov[:l+1], e
}

func decode(data []byte) ([]int32, error) {
	r := bytes.NewReader(data)

	var arr []int32
	var num int32
	for e := binary.Read(r, binary.BigEndian, &num); e == nil; e = binary.Read(r, binary.BigEndian, &num) {
		arr = append(arr, num)
	}
	return uncompress(arr)
}

func is2b(s Slice) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	for _, v := range s {
		e := binary.Write(w, binary.BigEndian, v)
		if e != nil {
			return nil, e
		}
	}
	return w.Bytes(), nil
}

func b2is(b []byte) (s Slice, e error) {
	r := bytes.NewReader(b)
	var i int32
	for e = binary.Read(r, binary.BigEndian, &i); e == nil; e = binary.Read(r, binary.BigEndian, &i) {
		s = append(s, i)
	}
	if e == io.EOF {
		e = nil
	}
	return
}

func gz(data []byte) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	gw, e := gzip.NewWriterLevel(w, gzip.BestSpeed)
	if e != nil {
		return nil, e
	}
	_, e = gw.Write(data)
	if e != nil {
		return nil, e
	}
	e = gw.Close()
	return w.Bytes(), e
}

func ungz(data []byte) ([]byte, error) {
	r := bytes.NewReader(data)
	gr, e := gzip.NewReader(r)
	if e != nil {
		return nil, e
	}
	var res []byte
	buf := bytes.NewBuffer(res)
	_, e = io.Copy(buf, gr)
	return res, e
}

func PackGzip(s Slice) ([]byte, error) {
	b, e := is2b(s)
	if e != nil {
		return nil, e
	}
	return gz(b)
}

func UnpackGzip(data []byte) (Slice, error) {
	bts, e := ungz(data)
	if e != nil {
		return Slice{}, e
	}
	return b2is(bts)
}

func snap(arr []byte) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	sw := snappy.NewWriter(w)
	_, e := sw.Write(arr)
	if e != nil {
		return nil, e
	}
	return w.Bytes(), nil
}

func PackSnappy(s Slice) ([]byte, error) {
	b, e := is2b(s)
	if e != nil {
		return nil, e
	}
	return snap(b)
}
<|endoftext|>


<commit_before>package terminfo

import (
	"bytes"
	"io"
	"strconv"
	"sync"
)

type stack []interface{}

func (stk *stack) push(v interface{}) {
	*stk = append(*stk, v)
}

func (stk *stack) pop() interface{} {
	if len(*stk) == 0 {
		return nil
	}
	v := (*stk)[len(*stk)-1]
	*stk = (*stk)[:len(*stk)-1]
	return v
}

func (stk *stack) popInt() int {
	if ai, ok := stk.pop().(int); ok {
		return ai
	}
	return 0
}

func (stk *stack) popBool() bool {
	if ab, ok := stk.pop().(bool); ok {
		return ab
	}
	return false
}

func (stk *stack) popByte() byte {
	if ab, ok := stk.pop().(byte); ok {
		return ab
	}
	return 0
}

func (stk *stack) popString() string {
	if as, ok := stk.pop().(string); ok {
		return as
	}
	return ""
}

// parametizer represents the scanners state.
type parametizer struct {
	s        string          // terminfo string
	pos      int             // position in s
	nest     int             // nesting level of if statements
	stk      stack           // terminfo var stack
	skipElse bool            // controls which fuction skipText returns
	buf      *bytes.Buffer   // result buffer
	params   [9]interface{}  // paramters
	dvars    [26]interface{} // dynamic vars
}

// static vars
var svars [26]interface{}

var parametizerPool = sync.Pool{
	New: func() interface{} {
		pz := new(parametizer)
		pz.buf = bytes.NewBuffer(make([]byte, 0, 45))
		return pz
	},
}

// getparametizer returns a new initialized parametizer from the pool.
func getParametizer(s string) *parametizer {
	pz := parametizerPool.Get().(*parametizer)
	pz.s = s
	return pz
}

// free resets the parametizer.
func (pz *parametizer) free() {
	pz.pos = 0
	pz.nest = 0
	pz.stk = pz.stk[:0]
	pz.buf.Reset()
	pz.params = [9]interface{}{}
	pz.dvars = [26]interface{}{}
	parametizerPool.Put(pz)
}

// Parm evaluates a terminfo parameterized string, such as cap.SetAForeground,
// and returns the result.
func Parm(s string, p ...interface{}) string {
	pz := getParametizer(s)
	defer pz.free()
	// make sure we always have 9 parameters -- makes it easier
	// later to skip checks and its faster
	for i := 0; i < len(pz.params) && i < len(p); i++ {
		pz.params[i] = p[i]
	}
	return pz.run()
}

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*parametizer) stateFn

func (pz *parametizer) run() string {
	for state := scanText; state != nil; {
		state = state(pz)
	}
	return pz.buf.String()
}

// get returns the current byte.
func (pz *parametizer) get() (byte, error) {
	if pz.pos >= len(pz.s) {
		return 0, io.EOF
	}
	return pz.s[pz.pos], nil
}

// writeFrom writes the characters from ppos to pos to the buffer.
func (pz *parametizer) writeFrom(ppos int) {
	if pz.pos > ppos {
		// Append remaining characters.
		pz.buf.WriteString(pz.s[ppos:pz.pos])
	}
}

// scanText scans until the next code.
func scanText(pz *parametizer) stateFn {
	ppos := pz.pos
	for {
		ch, err := pz.get()
		if err != nil {
			pz.writeFrom(ppos)
			return nil
		}
		if ch == '%' {
			pz.writeFrom(ppos)
			pz.pos++
			return scanCode
		}
		pz.pos++
	}
}

func scanCode(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	switch ch {
	case '%':
		pz.buf.WriteByte('%')
	case 'i':
		pz.params[0] = pz.params[0].(int) + 1
		pz.params[1] = pz.params[1].(int) + 1
	case 'c':
		pz.buf.WriteByte(pz.stk.popByte())
	case 's':
		pz.buf.WriteString(pz.stk.popString())
	case 'd':
		pz.buf.WriteString(strconv.Itoa(pz.stk.popInt()))
	case ':':
		// TODO implement
	case 'p':
		pz.pos++
		return pushParam
	case 'P':
		pz.pos++
		return setDSVar
	case 'g':
		pz.pos++
		return getDSVar
	case '\'':
		pz.pos++
		ch, err = pz.get()
		if err != nil {
			return nil
		}
		pz.stk.push(ch)
		// skip the '\''
		pz.pos++
	case '{':
		pz.pos++
		return pushInt
	case 'l':
		pz.stk.push(len(pz.stk.popString()))
	case '+':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai + bi)
	case '-':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai - bi)
	case '*':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai * bi)
	case '/':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		if bi != 0 {
			pz.stk.push(ai / bi)
		} else {
			pz.stk.push(0)
		}
	case 'm':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		if bi != 0 {
			pz.stk.push(ai % bi)
		} else {
			pz.stk.push(0)
		}
	case '&':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai & bi)
	case '|':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai | bi)
	case '^':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai ^ bi)
	case '~':
		pz.stk.push(pz.stk.popInt() ^ -1)
	case '!':
		pz.stk.push(pz.stk.popInt() != 0)
	case '=':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai == bi)
	case '>':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai > bi)
	case '<':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai < bi)
	case '?':
	case ';':
	case 't':
		return scanThen
	case 'e':
		pz.skipElse = true
		return skipText
	}
	pz.pos++
	return scanText
}

func pushParam(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	if ai := int(ch - '1'); ai >= 0 && ai < len(pz.params) {
		pz.stk.push(pz.params[ai])
	} else {
		pz.stk.push(0)
	}
	// skip the '}'
	pz.pos++
	return scanText
}

func setDSVar(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	if ch >= 'A' && ch <= 'Z' {
		svars[int(ch-'A')] = pz.stk.pop()
	} else if ch >= 'a' && ch <= 'z' {
		pz.dvars[int(ch-'a')] = pz.stk.pop()
	}
	pz.pos++
	return scanText
}

func getDSVar(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	if ch >= 'A' && ch <= 'Z' {
		pz.stk.push(svars[int(ch-'A')])
	} else if ch >= 'a' && ch <= 'z' {
		pz.stk.push(svars[int(ch-'a')])
	}
	pz.pos++
	return scanText
}

func pushInt(pz *parametizer) stateFn {
	var ai int
	for {
		ch, err := pz.get()
		if err != nil {
			return nil
		}
		pz.pos++
		if ch < '0' || ch > '9' {
			pz.stk.push(ai)
			return scanText
		}
		ai = (ai * 10) + int(ch-'0')
	}
}

func scanThen(pz *parametizer) stateFn {
	pz.pos++
	if pz.stk.popBool() {
		return scanText
	}
	pz.skipElse = false
	return skipText
}

func skipText(pz *parametizer) stateFn {
	for {
		ch, err := pz.get()
		if err != nil {
			return nil
		}
		pz.pos++
		if ch == '%' {
			break
		}
	}
	if pz.skipElse {
		return skipElse
	}
	return skipThen
}

func skipThen(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	pz.pos++
	switch ch {
	case ';':
		if pz.nest == 0 {
			return scanText
		}
		pz.nest--
	case '?':
		pz.nest++
	case 'e':
		if pz.nest == 0 {
			return scanText
		}
	}
	return skipText
}

func skipElse(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	pz.pos++
	switch ch {
	case ';':
		if pz.nest == 0 {
			return scanText
		}
		pz.nest--
	case '?':
		pz.nest++
	}
	return skipText
}
<commit_msg>fixed renaming mistake in comment<commit_after>package terminfo

import (
	"bytes"
	"io"
	"strconv"
	"sync"
)

type stack []interface{}

func (stk *stack) push(v interface{}) {
	*stk = append(*stk, v)
}

func (stk *stack) pop() interface{} {
	if len(*stk) == 0 {
		return nil
	}
	v := (*stk)[len(*stk)-1]
	*stk = (*stk)[:len(*stk)-1]
	return v
}

func (stk *stack) popInt() int {
	if ai, ok := stk.pop().(int); ok {
		return ai
	}
	return 0
}

func (stk *stack) popBool() bool {
	if ab, ok := stk.pop().(bool); ok {
		return ab
	}
	return false
}

func (stk *stack) popByte() byte {
	if ab, ok := stk.pop().(byte); ok {
		return ab
	}
	return 0
}

func (stk *stack) popString() string {
	if as, ok := stk.pop().(string); ok {
		return as
	}
	return ""
}

// parametizer represents the scanners state.
type parametizer struct {
	s        string          // terminfo string
	pos      int             // position in s
	nest     int             // nesting level of if statements
	stk      stack           // terminfo var stack
	skipElse bool            // controls which fuction skipText returns
	buf      *bytes.Buffer   // result buffer
	params   [9]interface{}  // paramters
	dvars    [26]interface{} // dynamic vars
}

// static vars
var svars [26]interface{}

var parametizerPool = sync.Pool{
	New: func() interface{} {
		pz := new(parametizer)
		pz.buf = bytes.NewBuffer(make([]byte, 0, 45))
		return pz
	},
}

// getparametizer returns a new initialized parametizer from the pool.
func getParametizer(s string) *parametizer {
	pz := parametizerPool.Get().(*parametizer)
	pz.s = s
	return pz
}

// free resets the parametizer.
func (pz *parametizer) free() {
	pz.pos = 0
	pz.nest = 0
	pz.stk = pz.stk[:0]
	pz.buf.Reset()
	pz.params = [9]interface{}{}
	pz.dvars = [26]interface{}{}
	parametizerPool.Put(pz)
}

// Parm evaluates a terminfo parameterized string, such as caps.SetAForeground,
// and returns the result.
func Parm(s string, p ...interface{}) string {
	pz := getParametizer(s)
	defer pz.free()
	// make sure we always have 9 parameters -- makes it easier
	// later to skip checks and its faster
	for i := 0; i < len(pz.params) && i < len(p); i++ {
		pz.params[i] = p[i]
	}
	return pz.run()
}

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*parametizer) stateFn

func (pz *parametizer) run() string {
	for state := scanText; state != nil; {
		state = state(pz)
	}
	return pz.buf.String()
}

// get returns the current byte.
func (pz *parametizer) get() (byte, error) {
	if pz.pos >= len(pz.s) {
		return 0, io.EOF
	}
	return pz.s[pz.pos], nil
}

// writeFrom writes the characters from ppos to pos to the buffer.
func (pz *parametizer) writeFrom(ppos int) {
	if pz.pos > ppos {
		// Append remaining characters.
		pz.buf.WriteString(pz.s[ppos:pz.pos])
	}
}

// scanText scans until the next code.
func scanText(pz *parametizer) stateFn {
	ppos := pz.pos
	for {
		ch, err := pz.get()
		if err != nil {
			pz.writeFrom(ppos)
			return nil
		}
		if ch == '%' {
			pz.writeFrom(ppos)
			pz.pos++
			return scanCode
		}
		pz.pos++
	}
}

func scanCode(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	switch ch {
	case '%':
		pz.buf.WriteByte('%')
	case 'i':
		pz.params[0] = pz.params[0].(int) + 1
		pz.params[1] = pz.params[1].(int) + 1
	case 'c':
		pz.buf.WriteByte(pz.stk.popByte())
	case 's':
		pz.buf.WriteString(pz.stk.popString())
	case 'd':
		pz.buf.WriteString(strconv.Itoa(pz.stk.popInt()))
	case ':':
		// TODO implement
	case 'p':
		pz.pos++
		return pushParam
	case 'P':
		pz.pos++
		return setDSVar
	case 'g':
		pz.pos++
		return getDSVar
	case '\'':
		pz.pos++
		ch, err = pz.get()
		if err != nil {
			return nil
		}
		pz.stk.push(ch)
		// skip the '\''
		pz.pos++
	case '{':
		pz.pos++
		return pushInt
	case 'l':
		pz.stk.push(len(pz.stk.popString()))
	case '+':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai + bi)
	case '-':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai - bi)
	case '*':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai * bi)
	case '/':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		if bi != 0 {
			pz.stk.push(ai / bi)
		} else {
			pz.stk.push(0)
		}
	case 'm':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		if bi != 0 {
			pz.stk.push(ai % bi)
		} else {
			pz.stk.push(0)
		}
	case '&':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai & bi)
	case '|':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai | bi)
	case '^':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai ^ bi)
	case '~':
		pz.stk.push(pz.stk.popInt() ^ -1)
	case '!':
		pz.stk.push(pz.stk.popInt() != 0)
	case '=':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai == bi)
	case '>':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai > bi)
	case '<':
		bi, ai := pz.stk.popInt(), pz.stk.popInt()
		pz.stk.push(ai < bi)
	case '?':
	case ';':
	case 't':
		return scanThen
	case 'e':
		pz.skipElse = true
		return skipText
	}
	pz.pos++
	return scanText
}

func pushParam(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	if ai := int(ch - '1'); ai >= 0 && ai < len(pz.params) {
		pz.stk.push(pz.params[ai])
	} else {
		pz.stk.push(0)
	}
	// skip the '}'
	pz.pos++
	return scanText
}

func setDSVar(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	if ch >= 'A' && ch <= 'Z' {
		svars[int(ch-'A')] = pz.stk.pop()
	} else if ch >= 'a' && ch <= 'z' {
		pz.dvars[int(ch-'a')] = pz.stk.pop()
	}
	pz.pos++
	return scanText
}

func getDSVar(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	if ch >= 'A' && ch <= 'Z' {
		pz.stk.push(svars[int(ch-'A')])
	} else if ch >= 'a' && ch <= 'z' {
		pz.stk.push(svars[int(ch-'a')])
	}
	pz.pos++
	return scanText
}

func pushInt(pz *parametizer) stateFn {
	var ai int
	for {
		ch, err := pz.get()
		if err != nil {
			return nil
		}
		pz.pos++
		if ch < '0' || ch > '9' {
			pz.stk.push(ai)
			return scanText
		}
		ai = (ai * 10) + int(ch-'0')
	}
}

func scanThen(pz *parametizer) stateFn {
	pz.pos++
	if pz.stk.popBool() {
		return scanText
	}
	pz.skipElse = false
	return skipText
}

func skipText(pz *parametizer) stateFn {
	for {
		ch, err := pz.get()
		if err != nil {
			return nil
		}
		pz.pos++
		if ch == '%' {
			break
		}
	}
	if pz.skipElse {
		return skipElse
	}
	return skipThen
}

func skipThen(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	pz.pos++
	switch ch {
	case ';':
		if pz.nest == 0 {
			return scanText
		}
		pz.nest--
	case '?':
		pz.nest++
	case 'e':
		if pz.nest == 0 {
			return scanText
		}
	}
	return skipText
}

func skipElse(pz *parametizer) stateFn {
	ch, err := pz.get()
	if err != nil {
		return nil
	}
	pz.pos++
	switch ch {
	case ';':
		if pz.nest == 0 {
			return scanText
		}
		pz.nest--
	case '?':
		pz.nest++
	}
	return skipText
}
<|endoftext|>


<commit_before>package gopass

import (
	"os"
)

// getPasswd returns the input read from terminal.
// If masked is true, typing will be matched by asterisks on the screen.
// Otherwise, typing will echo nothing.
func getPasswd(masked bool) []byte {
	var pass, bs, mask []byte
	if masked {
		bs = []byte("\b \b")
		mask = []byte("*")
	}

	for {
		if v := getch(); v == 127 || v == 8 {
			if l := len(pass); l > 0 {
				pass = pass[:l-1]
				os.Stdout.Write(bs)
			}
		} else if v == 13 || v == 10 {
			break
		} else if v != 0 {
			pass = append(pass, v)
			os.Stdout.Write(mask)
		}
	}
	println()
	return pass
}

// GetPasswd returns the password read from the terminal without echoing input.
// The returned byte array does not include end-of-line characters.
func GetPasswd() []byte {
	return getPasswd(false)
}

// GetPasswdMasked returns the password read from the terminal, echoing asterisks.
// The returned byte array does not include end-of-line characters.
func GetPasswdMasked() []byte {
	return getPasswd(true)
}
<commit_msg>added ctrl-c handling, and ErrInterrupted<commit_after>package gopass

import (
	"errors"
	"os"
)

var ErrInterrupted = errors.New("Interrupted")

// getPasswd returns the input read from terminal.
// If masked is true, typing will be matched by asterisks on the screen.
// Otherwise, typing will echo nothing.
func getPasswd(masked bool) ([]byte, error) {
	var pass, bs, mask []byte
	if masked {
		bs = []byte("\b \b")
		mask = []byte("*")
	}

	var err error
	for {
		if v := getch(); v == 127 || v == 8 {
			if l := len(pass); l > 0 {
				pass = pass[:l-1]
				os.Stdout.Write(bs)
			}
		} else if v == 13 || v == 10 {
			break
		} else if v == 3 {
			err = ErrInterrupted
			break
		} else if v != 0 {
			pass = append(pass, v)
			os.Stdout.Write(mask)
		}
	}
	println()
	if err != nil {
		return nil, err
	}
	return pass, nil
}

// GetPasswd returns the password read from the terminal without echoing input.
// The returned byte array does not include end-of-line characters.
func GetPasswd() ([]byte, error) {
	return getPasswd(false)
}

// GetPasswdMasked returns the password read from the terminal, echoing asterisks.
// The returned byte array does not include end-of-line characters.
func GetPasswdMasked() ([]byte, error) {
	return getPasswd(true)
}
<|endoftext|>