<commit_before>// Copyright (c) 2014 Salsita Software
// Copyright (C) 2015 Scott Devoid
// Use of this source code is governed by the MIT License.
// The license can be found in the LICENSE file.

package pivotal

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

// Number of items to fetch at once when getting paginated response.
const PageLimit = 10

const (
	StoryTypeFeature = "feature"
	StoryTypeBug     = "bug"
	StoryTypeChore   = "chore"
	StoryTypeRelease = "release"
)

const (
	StoryStateUnscheduled = "unscheduled"
	StoryStatePlanned     = "planned"
	StoryStateUnstarted   = "unstarted"
	StoryStateStarted     = "started"
	StoryStateFinished    = "finished"
	StoryStateDelivered   = "delivered"
	StoryStateAccepted    = "accepted"
	StoryStateRejected    = "rejected"
)

type Story struct {
	Id            int        `json:"id,omitempty"`
	ProjectId     int        `json:"project_id,omitempty"`
	Name          string     `json:"name,omitempty"`
	Description   string     `json:"description,omitempty"`
	Type          string     `json:"story_type,omitempty"`
	State         string     `json:"current_state,omitempty"`
	Estimate      *float64   `json:"estimate,omitempty"`
	AcceptedAt    *time.Time `json:"accepted_at,omitempty"`
	Deadline      *time.Time `json:"deadline,omitempty"`
	RequestedById int        `json:"requested_by_id,omitempty"`
	OwnerIds      []int      `json:"owner_ids,omitempty"`
	LabelIds      []int      `json:"label_ids,omitempty"`
	Labels        []*Label   `json:"labels,omitempty"`
	TaskIds       []int      `json:"task_ids,omitempty"`
	Tasks         []int      `json:"tasks,omitempty"`
	FollowerIds   []int      `json:"follower_ids,omitempty"`
	CommentIds    []int      `json:"comment_ids,omitempty"`
	CreatedAt     *time.Time `json:"created_at,omitempty"`
	UpdatedAt     *time.Time `json:"updated_at,omitempty"`
	IntegrationId int        `json:"integration_id,omitempty"`
	ExternalId    string     `json:"external_id,omitempty"`
	URL           string     `json:"url,omitempty"`
}

type StoryRequest struct {
	Name        string    `json:"name,omitempty"`
	Description string    `json:"description,omitempty"`
	Type        string    `json:"story_type,omitempty"`
	State       string    `json:"current_state,omitempty"`
	Estimate    *float64  `json:"estimate,omitempty"`
	OwnerIds    *[]int    `json:"owner_ids,omitempty"`
	LabelIds    *[]int    `json:"label_ids,omitempty"`
	Labels      *[]*Label `json:"labels,omitempty"`
	TaskIds     *[]int    `json:"task_ids,omitempty"`
	Tasks       *[]int    `json:"tasks,omitempty"`
	FollowerIds *[]int    `json:"follower_ids,omitempty"`
	CommentIds  *[]int    `json:"comment_ids,omitempty"`
}

type Label struct {
	Id        int        `json:"id,omitempty"`
	ProjectId int        `json:"project_id,omitempty"`
	Name      string     `json:"name,omitempty"`
	CreatedAt *time.Time `json:"created_at,omitempty"`
	UpdatedAt *time.Time `json:"updated_at,omitempty"`
	Kind      string     `json:"kind,omitempty"`
}

type Task struct {
	Id          int        `json:"id,omitempty"`
	StoryId     int        `json:"story_id,omitempty"`
	Description string     `json:"description,omitempty"`
	Position    int        `json:"position,omitempty"`
	Complete    bool       `json:"complete,omitempty"`
	CreatedAt   *time.Time `json:"created_at,omitempty"`
	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
}

type Person struct {
	Id       int    `json:"id,omitempty"`
	Name     string `json:"name,omitempty"`
	Email    string `json:"email,omitempty"`
	Initials string `json:"initials,omitempty"`
	Username string `json:"username,omitempty"`
	Kind     string `json:"kind,omitempty"`
}

type Comment struct {
	Id                  int        `json:"id,omitempty"`
	StoryId             int        `json:"story_id,omitempty"`
	EpicId              int        `json:"epic_id,omitempty"`
	PersonId            int        `json:"person_id,omitempty"`
	Text                string     `json:"text,omitempty"`
	FileAttachmentIds   []int      `json:"file_attachment_ids,omitempty"`
	GoogleAttachmentIds []int      `json:"google_attachment_ids,omitempty"`
	CommitType          string     `json:"commit_type,omitempty"`
	CommitIdentifier    string     `json:"commit_identifier,omitempty"`
	CreatedAt           *time.Time `json:"created_at,omitempty"`
	UpdatedAt           *time.Time `json:"updated_at,omitempty"`
}

type StoryService struct {
	client *Client
}

func newStoryService(client *Client) *StoryService {
	return &StoryService{client}
}

// List returns all stories matching the filter in case the filter is specified.
//
// List actually sends 2 HTTP requests - one to get the total number of stories,
// another to retrieve the stories using the right pagination setup. The reason
// for this is that the filter might require to fetch all the stories at once
// to get the right results. Since the response as generated by Pivotal Tracker
// is not always sorted when using a filter, this approach is required to get
// the right data. Not sure whether this is a bug or a feature.
func (service *StoryService) List(projectId int, filter string) ([]*Story, error) {
	reqFunc := newStoriesRequestFunc(service.client, projectId, filter)
	cursor, err := newCursor(service.client, reqFunc, 0)
	if err != nil {
		return nil, err
	}

	var stories []*Story
	if err := cursor.all(&stories); err != nil {
		return nil, err
	}
	return stories, nil
}

func newStoriesRequestFunc(client *Client, projectId int, filter string) func() *http.Request {
	return func() *http.Request {
		u := fmt.Sprintf("projects/%v/stories", projectId)
		if filter != "" {
			u += "?filter=" + url.QueryEscape(filter)
		}
		req, _ := client.NewRequest("GET", u, nil)
		return req
	}
}

type StoryCursor struct {
	*cursor
	buff []*Story
}

// Next returns the next story.
//
// In case there are no more stories, io.EOF is returned as an error.
func (c *StoryCursor) Next() (s *Story, err error) {
	if len(c.buff) == 0 {
		_, err = c.next(&c.buff)
		if err != nil {
			return nil, err
		}
	}

	if len(c.buff) == 0 {
		err = io.EOF
	} else {
		s, c.buff = c.buff[0], c.buff[1:]
	}
	return s, err
}

// Iterate returns a cursor that can be used to iterate over the stories specified
// by the filter. More stories are fetched on demand as needed.
func (service *StoryService) Iterate(projectId int, filter string) (c *StoryCursor, err error) {
	reqFunc := newStoriesRequestFunc(service.client, projectId, filter)
	cursor, err := newCursor(service.client, reqFunc, PageLimit)
	if err != nil {
		return nil, err
	}
	return &StoryCursor{cursor, make([]*Story, 0)}, nil
}

func (service *StoryService) Create(projectId int, story *StoryRequest) (*Story, *http.Response, error) {
	if projectId == 0 {
		return nil, nil, &ErrFieldNotSet{"project_id"}
	}

	if story.Name == "" {
		return nil, nil, &ErrFieldNotSet{"name"}
	}

	u := fmt.Sprintf("projects/%v/stories", projectId)
	req, err := service.client.NewRequest("POST", u, story)
	if err != nil {
		return nil, nil, err
	}

	var newStory Story

	resp, err := service.client.Do(req, &newStory)
	if err != nil {
		return nil, resp, err
	}

	return &newStory, resp, nil
}

func (service *StoryService) Get(projectId, storyId int) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var story Story
	resp, err := service.client.Do(req, &story)
	if err != nil {
		return nil, resp, err
	}

	return &story, resp, err
}

func (service *StoryService) Update(projectId, storyId int, story *StoryRequest) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("PUT", u, story)
	if err != nil {
		return nil, nil, err
	}

	var bodyStory Story
	resp, err := service.client.Do(req, &bodyStory)
	if err != nil {
		return nil, resp, err
	}

	return &bodyStory, resp, err

}

func (service *StoryService) ListTasks(projectId, storyId int) ([]*Task, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*Task
	resp, err := service.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, err
}

func (service *StoryService) AddTask(projectId, storyId int, task *Task) (*http.Response, error) {
	if task.Description == "" {
		return nil, &ErrFieldNotSet{"description"}
	}

	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, task)
	if err != nil {
		return nil, err
	}

	return service.client.Do(req, nil)
}

func (service *StoryService) ListOwners(projectId, storyId int) ([]*Person, *http.Response, error) {
	u := fmt.Sprintf("projects/%d/stories/%d/owners", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var owners []*Person
	resp, err := service.client.Do(req, &owners)
	if err != nil {
		return nil, resp, err
	}

	return owners, resp, err
}

func (service *StoryService) AddComment(
	projectId int,
	storyId int,
	comment *Comment,
) (*Comment, *http.Response, error) {

	u := fmt.Sprintf("projects/%v/stories/%v/comments", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, comment)
	if err != nil {
		return nil, nil, err
	}

	var newComment Comment
	resp, err := service.client.Do(req, &newComment)
	if err != nil {
		return nil, resp, err
	}

	return &newComment, resp, err
}

// ListComments returns the list of Comments in a Story.
func (service *StoryService) ListComments(
	projectId int,
	storyId int,
) ([]*Comment, *http.Response, error) {

	u := fmt.Sprintf("projects/%v/stories/%v/comments", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var comments []*Comment
	resp, err := service.client.Do(req, &comments)
	if err != nil {
		return nil, resp, err
	}

	return comments, resp, nil
}
<commit_msg>Add listing and creating blockers<commit_after>// Copyright (c) 2014 Salsita Software
// Copyright (C) 2015 Scott Devoid
// Use of this source code is governed by the MIT License.
// The license can be found in the LICENSE file.

package pivotal

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

// Number of items to fetch at once when getting paginated response.
const PageLimit = 10

const (
	StoryTypeFeature = "feature"
	StoryTypeBug     = "bug"
	StoryTypeChore   = "chore"
	StoryTypeRelease = "release"
)

const (
	StoryStateUnscheduled = "unscheduled"
	StoryStatePlanned     = "planned"
	StoryStateUnstarted   = "unstarted"
	StoryStateStarted     = "started"
	StoryStateFinished    = "finished"
	StoryStateDelivered   = "delivered"
	StoryStateAccepted    = "accepted"
	StoryStateRejected    = "rejected"
)

type Story struct {
	Id            int        `json:"id,omitempty"`
	ProjectId     int        `json:"project_id,omitempty"`
	Name          string     `json:"name,omitempty"`
	Description   string     `json:"description,omitempty"`
	Type          string     `json:"story_type,omitempty"`
	State         string     `json:"current_state,omitempty"`
	Estimate      *float64   `json:"estimate,omitempty"`
	AcceptedAt    *time.Time `json:"accepted_at,omitempty"`
	Deadline      *time.Time `json:"deadline,omitempty"`
	RequestedById int        `json:"requested_by_id,omitempty"`
	OwnerIds      []int      `json:"owner_ids,omitempty"`
	LabelIds      []int      `json:"label_ids,omitempty"`
	Labels        []*Label   `json:"labels,omitempty"`
	TaskIds       []int      `json:"task_ids,omitempty"`
	Tasks         []int      `json:"tasks,omitempty"`
	FollowerIds   []int      `json:"follower_ids,omitempty"`
	CommentIds    []int      `json:"comment_ids,omitempty"`
	CreatedAt     *time.Time `json:"created_at,omitempty"`
	UpdatedAt     *time.Time `json:"updated_at,omitempty"`
	IntegrationId int        `json:"integration_id,omitempty"`
	ExternalId    string     `json:"external_id,omitempty"`
	URL           string     `json:"url,omitempty"`
}

type StoryRequest struct {
	Name        string    `json:"name,omitempty"`
	Description string    `json:"description,omitempty"`
	Type        string    `json:"story_type,omitempty"`
	State       string    `json:"current_state,omitempty"`
	Estimate    *float64  `json:"estimate,omitempty"`
	OwnerIds    *[]int    `json:"owner_ids,omitempty"`
	LabelIds    *[]int    `json:"label_ids,omitempty"`
	Labels      *[]*Label `json:"labels,omitempty"`
	TaskIds     *[]int    `json:"task_ids,omitempty"`
	Tasks       *[]int    `json:"tasks,omitempty"`
	FollowerIds *[]int    `json:"follower_ids,omitempty"`
	CommentIds  *[]int    `json:"comment_ids,omitempty"`
}

type Label struct {
	Id        int        `json:"id,omitempty"`
	ProjectId int        `json:"project_id,omitempty"`
	Name      string     `json:"name,omitempty"`
	CreatedAt *time.Time `json:"created_at,omitempty"`
	UpdatedAt *time.Time `json:"updated_at,omitempty"`
	Kind      string     `json:"kind,omitempty"`
}

type Task struct {
	Id          int        `json:"id,omitempty"`
	StoryId     int        `json:"story_id,omitempty"`
	Description string     `json:"description,omitempty"`
	Position    int        `json:"position,omitempty"`
	Complete    bool       `json:"complete,omitempty"`
	CreatedAt   *time.Time `json:"created_at,omitempty"`
	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
}

type Person struct {
	Id       int    `json:"id,omitempty"`
	Name     string `json:"name,omitempty"`
	Email    string `json:"email,omitempty"`
	Initials string `json:"initials,omitempty"`
	Username string `json:"username,omitempty"`
	Kind     string `json:"kind,omitempty"`
}

type Comment struct {
	Id                  int        `json:"id,omitempty"`
	StoryId             int        `json:"story_id,omitempty"`
	EpicId              int        `json:"epic_id,omitempty"`
	PersonId            int        `json:"person_id,omitempty"`
	Text                string     `json:"text,omitempty"`
	FileAttachmentIds   []int      `json:"file_attachment_ids,omitempty"`
	GoogleAttachmentIds []int      `json:"google_attachment_ids,omitempty"`
	CommitType          string     `json:"commit_type,omitempty"`
	CommitIdentifier    string     `json:"commit_identifier,omitempty"`
	CreatedAt           *time.Time `json:"created_at,omitempty"`
	UpdatedAt           *time.Time `json:"updated_at,omitempty"`
}

type Blocker struct {
	Id          int        `json:"id,omitempty"`
	StoryId     int        `json:"story_id,omitempty"`
	PersonId    int        `json:"person_id,omitempty"`
	Description string     `json:"description,omitempty"`
	Resolved    bool       `json:"resolved,omitempty"`
	CreatedAt   *time.Time `json:"created_at,omitempty"`
	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
}

type BlockerRequest struct {
	Description string `json:"description,omitempty"`
	Resolved    *bool  `json:"resolved,omitempty"`
}

type StoryService struct {
	client *Client
}

func newStoryService(client *Client) *StoryService {
	return &StoryService{client}
}

// List returns all stories matching the filter in case the filter is specified.
//
// List actually sends 2 HTTP requests - one to get the total number of stories,
// another to retrieve the stories using the right pagination setup. The reason
// for this is that the filter might require to fetch all the stories at once
// to get the right results. Since the response as generated by Pivotal Tracker
// is not always sorted when using a filter, this approach is required to get
// the right data. Not sure whether this is a bug or a feature.
func (service *StoryService) List(projectId int, filter string) ([]*Story, error) {
	reqFunc := newStoriesRequestFunc(service.client, projectId, filter)
	cursor, err := newCursor(service.client, reqFunc, 0)
	if err != nil {
		return nil, err
	}

	var stories []*Story
	if err := cursor.all(&stories); err != nil {
		return nil, err
	}
	return stories, nil
}

func newStoriesRequestFunc(client *Client, projectId int, filter string) func() *http.Request {
	return func() *http.Request {
		u := fmt.Sprintf("projects/%v/stories", projectId)
		if filter != "" {
			u += "?filter=" + url.QueryEscape(filter)
		}
		req, _ := client.NewRequest("GET", u, nil)
		return req
	}
}

type StoryCursor struct {
	*cursor
	buff []*Story
}

// Next returns the next story.
//
// In case there are no more stories, io.EOF is returned as an error.
func (c *StoryCursor) Next() (s *Story, err error) {
	if len(c.buff) == 0 {
		_, err = c.next(&c.buff)
		if err != nil {
			return nil, err
		}
	}

	if len(c.buff) == 0 {
		err = io.EOF
	} else {
		s, c.buff = c.buff[0], c.buff[1:]
	}
	return s, err
}

// Iterate returns a cursor that can be used to iterate over the stories specified
// by the filter. More stories are fetched on demand as needed.
func (service *StoryService) Iterate(projectId int, filter string) (c *StoryCursor, err error) {
	reqFunc := newStoriesRequestFunc(service.client, projectId, filter)
	cursor, err := newCursor(service.client, reqFunc, PageLimit)
	if err != nil {
		return nil, err
	}
	return &StoryCursor{cursor, make([]*Story, 0)}, nil
}

func (service *StoryService) Create(projectId int, story *StoryRequest) (*Story, *http.Response, error) {
	if projectId == 0 {
		return nil, nil, &ErrFieldNotSet{"project_id"}
	}

	if story.Name == "" {
		return nil, nil, &ErrFieldNotSet{"name"}
	}

	u := fmt.Sprintf("projects/%v/stories", projectId)
	req, err := service.client.NewRequest("POST", u, story)
	if err != nil {
		return nil, nil, err
	}

	var newStory Story

	resp, err := service.client.Do(req, &newStory)
	if err != nil {
		return nil, resp, err
	}

	return &newStory, resp, nil
}

func (service *StoryService) Get(projectId, storyId int) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var story Story
	resp, err := service.client.Do(req, &story)
	if err != nil {
		return nil, resp, err
	}

	return &story, resp, err
}

func (service *StoryService) Update(projectId, storyId int, story *StoryRequest) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("PUT", u, story)
	if err != nil {
		return nil, nil, err
	}

	var bodyStory Story
	resp, err := service.client.Do(req, &bodyStory)
	if err != nil {
		return nil, resp, err
	}

	return &bodyStory, resp, err

}

func (service *StoryService) ListTasks(projectId, storyId int) ([]*Task, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*Task
	resp, err := service.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, err
}

func (service *StoryService) AddTask(projectId, storyId int, task *Task) (*http.Response, error) {
	if task.Description == "" {
		return nil, &ErrFieldNotSet{"description"}
	}

	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, task)
	if err != nil {
		return nil, err
	}

	return service.client.Do(req, nil)
}

func (service *StoryService) ListOwners(projectId, storyId int) ([]*Person, *http.Response, error) {
	u := fmt.Sprintf("projects/%d/stories/%d/owners", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var owners []*Person
	resp, err := service.client.Do(req, &owners)
	if err != nil {
		return nil, resp, err
	}

	return owners, resp, err
}

func (service *StoryService) AddComment(
	projectId int,
	storyId int,
	comment *Comment,
) (*Comment, *http.Response, error) {

	u := fmt.Sprintf("projects/%v/stories/%v/comments", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, comment)
	if err != nil {
		return nil, nil, err
	}

	var newComment Comment
	resp, err := service.client.Do(req, &newComment)
	if err != nil {
		return nil, resp, err
	}

	return &newComment, resp, err
}

// ListComments returns the list of Comments in a Story.
func (service *StoryService) ListComments(
	projectId int,
	storyId int,
) ([]*Comment, *http.Response, error) {

	u := fmt.Sprintf("projects/%v/stories/%v/comments", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var comments []*Comment
	resp, err := service.client.Do(req, &comments)
	if err != nil {
		return nil, resp, err
	}

	return comments, resp, nil
}

// ListBlockers returns the list of Blockers in a Story.
func (service *StoryService) ListBlockers(
	projectId int,
	storyId int,
) ([]*Blocker, *http.Response, error) {

	u := fmt.Sprintf("projects/%v/stories/%v/blockers", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var blockers []*Blocker
	resp, err := service.client.Do(req, &blockers)
	if err != nil {
		return nil, resp, err
	}

	return blockers, resp, nil
}

func (service *StoryService) AddBlocker(projectId int, storyId int, description string) (*Blocker, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v/blockers", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, BlockerRequest{
		Description: description,
	})
	if err != nil {
		return nil, nil, err
	}

	var blocker Blocker
	resp, err := service.client.Do(req, &blocker)
	if err != nil {
		return nil, resp, err
	}

	return &blocker, resp, nil
}

func (service *StoryService) UpdateBlocker(projectId, stroyId, blockerId int, blocker *BlockerRequest) (*Blocker, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v/blockers/%v", projectId, stroyId, blockerId)
	req, err := service.client.NewRequest("PUT", u, blocker)
	if err != nil {
		return nil, nil, err
	}

	var blockerResp Blocker
	resp, err := service.client.Do(req, &blockerResp)
	if err != nil {
		return nil, resp, err
	}

	return &blockerResp, resp, nil
}
<|endoftext|>


<commit_before>// Package scan TCP ports
package scan

import (
	"context"
	"fmt"
	"math/rand"
	"net"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/olekukonko/tablewriter"
)

// Scan represents the scan parameters
type Scan struct {
	minPort int
	maxPort int
	target  string
	lport   int
	rport   int
	laddr   net.IP
	raddr   net.IP
	ifName  string
	network string
	forceV4 bool
	forceV6 bool
	synScan bool
	ip      gopacket.NetworkLayer
}

// NewScan creats scan object
func NewScan(args string, cfg cli.Config) (Scan, error) {
	var (
		scan Scan
		flag map[string]interface{}
		err  error
	)

	args, flag = cli.Flag(args)
	// help
	if _, ok := flag["help"]; ok || args == "" {
		help(cfg)
		return scan, fmt.Errorf("")
	}

	scan.forceV4 = cli.SetFlag(flag, "4", true).(bool)
	scan.forceV6 = cli.SetFlag(flag, "6", false).(bool)
	scan.synScan = cli.SetFlag(flag, "s", false).(bool)

	pRange := cli.SetFlag(flag, "p", cfg.Scan.Port).(string)

	re := regexp.MustCompile(`(\d+)(\-{0,1}(\d*))`)
	f := re.FindStringSubmatch(pRange)

	if len(f) != 4 {
		return scan, fmt.Errorf("error! please try scan help")
	}

	scan.target = args
	if len(f) == 4 && f[2] != "" {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[3])
	} else {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[1])
	}

	if err != nil {
		return scan, err
	}

	if scan.IsCIDR() {
		return scan, fmt.Errorf("it doesn't support CIDR")
	}

	scan.setIP()

	return scan, nil
}

// IsCIDR checks the target if it's CIDR
func (s *Scan) IsCIDR() bool {
	_, _, err := net.ParseCIDR(s.target)
	if err != nil {
		return false
	}
	return true
}

func isIPv4(ip net.IP) bool {
	return len(ip.To4()) == net.IPv4len
}

func isIPv6(ip net.IP) bool {
	if r := strings.Index(ip.String(), ":"); r != -1 {
		return true
	}
	return false
}

func (s *Scan) setIP() error {
	ips, err := net.LookupIP(s.target)
	if err != nil {
		return err
	}
	for _, ip := range ips {
		if isIPv4(ip) && !s.forceV6 {
			s.raddr = ip
			s.network = "ip4"
			break
		} else if isIPv6(ip) && !s.forceV4 {
			s.raddr = ip
			s.network = "ip6"
			break
		}
	}
	return nil
}

// Run tries to scan wide range ports (TCP)
func (s *Scan) Run() {
	var (
		openPorts []int
		err       error
	)

	if s.minPort != s.maxPort {
		fmt.Printf("Scan %s (%s) TCP ports %d-%d\n", s.target, s.raddr, s.minPort, s.maxPort)
	} else {
		fmt.Printf("Scan %s (%s) TCP port %d\n", s.target, s.raddr, s.minPort)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Protocol", "Port", "Status"})

	tStart := time.Now()
	if !s.synScan {
		openPorts = s.tcpConnScan()
	} else {
		openPorts, err = s.tcpSYNScan()
	}

	if err != nil {
		println(err.Error())
		return
	}

	for _, p := range openPorts {
		table.Append([]string{"TCP", fmt.Sprintf("%d", p), "Open"})
	}

	if len(openPorts) == 0 {
		println("there isn't any opened port")
	} else {
		table.Render()
		elapsed := fmt.Sprintf("%.3f seconds", time.Since(tStart).Seconds())
		println("Scan done:", len(openPorts), "opened port(s) found in", elapsed)
	}

}

func (s *Scan) packetDataTCP(rport int) (error, []byte) {
	tcp := &layers.TCP{
		SrcPort: layers.TCPPort(s.lport),
		DstPort: layers.TCPPort(rport),
		Seq:     1,
		SYN:     true,
		Window:  15000,
	}

	tcp.SetNetworkLayerForChecksum(s.ip)
	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{
		ComputeChecksums: true,
		FixLengths:       true,
	}

	if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil {
		return err, []byte{}
	}

	return nil, buf.Bytes()
}

func (s *Scan) setProto(proto string) error {
	var netProto = fmt.Sprintf("%s:%s", s.network, proto)

	switch netProto {
	case "ip4:tcp":
		s.ip = &layers.IPv4{
			SrcIP:    s.laddr,
			DstIP:    s.raddr,
			Protocol: layers.IPProtocolTCP,
		}
	case "ip4:udp":
		s.ip = &layers.IPv4{
			SrcIP:    s.laddr,
			DstIP:    s.raddr,
			Protocol: layers.IPProtocolUDP,
		}
	case "ip6:tcp":
		s.ip = &layers.IPv6{
			SrcIP:      s.laddr,
			DstIP:      s.raddr,
			NextHeader: layers.IPProtocolTCP,
		}
	case "ip6:udp":
		s.ip = &layers.IPv6{
			SrcIP:      s.laddr,
			DstIP:      s.raddr,
			NextHeader: layers.IPProtocolUDP,
		}

	}

	return nil
}

func (s *Scan) setLocalNet() error {
	conn, err := net.Dial("udp", net.JoinHostPort(s.raddr.String(), "80"))
	if err != nil {
		return err
	}
	defer conn.Close()

	if lAddr, ok := conn.LocalAddr().(*net.UDPAddr); ok {
		s.laddr = lAddr.IP
		s.lport = lAddr.Port
	} else {
		return fmt.Errorf("can not find local address/port")
	}

	ifs, _ := net.Interfaces()
	for _, i := range ifs {
		addrs, _ := i.Addrs()
		for _, addr := range addrs {
			ip, _, _ := net.ParseCIDR(addr.String())
			if ip.String() == s.laddr.String() {
				s.ifName = i.Name
				break
			}
		}
	}

	return nil
}

func (s *Scan) tcpSYNScan() ([]int, error) {
	var err error

	ctx, cancel := context.WithCancel(context.Background())

	if err = s.setLocalNet(); err != nil {
		println(err.Error())
	}
	if err = s.setProto("tcp"); err != nil {
		println(err.Error())
	}
	go func() {
		if err := s.sendTCPSYN(); err != nil {
			println(err.Error())
		}
		cancel()
	}()

	openPorts, err := s.pCapture(ctx)
	return openPorts, err
}

func (s *Scan) pCapture(ctx context.Context) ([]int, error) {
	var (
		tcp       *layers.TCP
		timeout   = 100 * time.Nanosecond
		openPorts []int
		ok        bool
	)
	handle, err := pcap.OpenLive(s.ifName, 6*1024, false, timeout)
	if err != nil {
		return nil, err
	}
	defer handle.Close()

	filter := "tcp and src host " + s.raddr.String()
	if err := handle.SetBPFFilter(filter); err != nil {
		return nil, err
	}

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
LOOP:
	for {
		select {
		case <-ctx.Done():
			break LOOP
		case packet := <-packetSource.Packets():
			tcpLayer := packet.Layer(layers.LayerTypeTCP)
			if tcpLayer != nil {
				if tcp, ok = tcpLayer.(*layers.TCP); !ok {
					continue
				}
				if tcp.SYN && tcp.ACK {
					p, _ := strconv.Atoi(fmt.Sprintf("%d", tcp.SrcPort))
					openPorts = append(openPorts, p)
				}
				continue
			}
		}
	}
	openPorts = uniqSlice(openPorts)
	sort.Ints(openPorts)
	return openPorts, nil
}

func (s *Scan) sendTCPSYN() error {
	var (
		buf  []byte
		err  error
		conn net.PacketConn
	)
	if s.network != "ip6" {
		conn, err = net.ListenPacket(s.network+":tcp", "0.0.0.0")
	} else {
		conn, err = net.ListenPacket(s.network+":tcp", "[::]")
	}
	if err != nil {
		return err
	}
	for i := s.minPort; i <= s.maxPort; i++ {
		if err, buf = s.packetDataTCP(i); err != nil {
			return err
		}
		if _, err := conn.WriteTo(buf, &net.IPAddr{IP: s.raddr}); err != nil {
			println(err.Error())
		}
		time.Sleep(5 * time.Millisecond)
	}
	time.Sleep(1 * time.Second)
	return nil
}

// tcpConnScan tries to scan a single host
func (s *Scan) tcpConnScan() []int {
	var wg sync.WaitGroup

	var ports []int
	for i := s.minPort; i <= s.maxPort; i++ {
		wg.Add(1)
		go func(i int) {
			for {
				host := net.JoinHostPort(s.raddr.String(), fmt.Sprintf("%d", i))
				conn, err := net.DialTimeout("tcp", host, 2*time.Second)
				if err != nil {
					if strings.Contains(err.Error(), "too many open files") {
						// random back-off
						time.Sleep(time.Duration(10+rand.Int31n(30)) * time.Millisecond)
						continue
					}
					wg.Done()
					return
				}
				conn.Close()
				break
			}
			ports = append(ports, i)
			wg.Done()
		}(i)
	}

	wg.Wait()
	sort.Ints(ports)
	return ports
}

func uniqSlice(s []int) []int {
	m := map[int]bool{}
	r := []int{}
	for _, v := range s {
		if _, ok := m[v]; !ok {
			m[v] = true
			r = append(r, v)
		}
	}
	return r
}

// help represents guide to user
func help(cfg cli.Config) {
	fmt.Printf(`
    usage:
          scan ip/host [option]
    options:
          -p port-range or port number      specified range or port number (default is %s)
    example:
          scan 8.8.8.8 -p 53
          scan www.google.com -p 1-500
	`,
		cfg.Scan.Port)
}
<commit_msg>set tcp syn as default, add help fixed #46<commit_after>// Package scan TCP ports
package scan

import (
	"context"
	"fmt"
	"math/rand"
	"net"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/olekukonko/tablewriter"
)

// Scan represents the scan parameters
type Scan struct {
	minPort  int
	maxPort  int
	target   string
	lport    int
	rport    int
	laddr    net.IP
	raddr    net.IP
	ifName   string
	network  string
	forceV4  bool
	forceV6  bool
	connScan bool
	ip       gopacket.NetworkLayer
}

// NewScan creats scan object
func NewScan(args string, cfg cli.Config) (Scan, error) {
	var (
		scan Scan
		flag map[string]interface{}
		err  error
	)

	args, flag = cli.Flag(args)
	// help
	if _, ok := flag["help"]; ok || args == "" {
		help(cfg)
		return scan, fmt.Errorf("")
	}

	scan.forceV4 = cli.SetFlag(flag, "4", false).(bool)
	scan.forceV6 = cli.SetFlag(flag, "6", false).(bool)
	scan.connScan = cli.SetFlag(flag, "c", false).(bool)

	pRange := cli.SetFlag(flag, "p", cfg.Scan.Port).(string)

	re := regexp.MustCompile(`(\d+)(\-{0,1}(\d*))`)
	f := re.FindStringSubmatch(pRange)

	if len(f) != 4 {
		return scan, fmt.Errorf("error! please try scan help")
	}

	scan.target = args
	if len(f) == 4 && f[2] != "" {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[3])
	} else {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[1])
	}

	if err != nil {
		return scan, err
	}

	if scan.IsCIDR() {
		return scan, fmt.Errorf("it doesn't support CIDR")
	}

	scan.setIP()

	return scan, nil
}

// IsCIDR checks the target if it's CIDR
func (s *Scan) IsCIDR() bool {
	_, _, err := net.ParseCIDR(s.target)
	if err != nil {
		return false
	}
	return true
}

func isIPv4(ip net.IP) bool {
	return len(ip.To4()) == net.IPv4len
}

func isIPv6(ip net.IP) bool {
	if r := strings.Index(ip.String(), ":"); r != -1 {
		return true
	}
	return false
}

func (s *Scan) setIP() error {
	ips, err := net.LookupIP(s.target)
	if err != nil {
		return err
	}
	for _, ip := range ips {
		if isIPv4(ip) && !s.forceV6 {
			s.raddr = ip
			s.network = "ip4"
			break
		} else if isIPv6(ip) && !s.forceV4 {
			s.raddr = ip
			s.network = "ip6"
			break
		}
	}
	return nil
}

// Run tries to scan wide range ports (TCP)
func (s *Scan) Run() {
	var (
		openPorts []int
		err       error
	)

	if s.minPort != s.maxPort {
		fmt.Printf("Scan %s (%s) TCP ports %d-%d\n", s.target, s.raddr, s.minPort, s.maxPort)
	} else {
		fmt.Printf("Scan %s (%s) TCP port %d\n", s.target, s.raddr, s.minPort)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Protocol", "Port", "Status"})

	tStart := time.Now()
	if s.connScan {
		openPorts = s.tcpConnScan()
	} else {
		openPorts, err = s.tcpSYNScan()
	}

	if err != nil {
		println(err.Error())
		return
	}

	for _, p := range openPorts {
		table.Append([]string{"TCP", fmt.Sprintf("%d", p), "Open"})
	}

	if len(openPorts) == 0 {
		println("there isn't any opened port")
	} else {
		table.Render()
		elapsed := fmt.Sprintf("%.3f seconds", time.Since(tStart).Seconds())
		println("Scan done:", len(openPorts), "opened port(s) found in", elapsed)
	}

}

func (s *Scan) packetDataTCP(rport int) (error, []byte) {
	tcp := &layers.TCP{
		SrcPort: layers.TCPPort(s.lport),
		DstPort: layers.TCPPort(rport),
		Seq:     1,
		SYN:     true,
		Window:  15000,
	}

	tcp.SetNetworkLayerForChecksum(s.ip)
	buf := gopacket.NewSerializeBuffer()
	opts := gopacket.SerializeOptions{
		ComputeChecksums: true,
		FixLengths:       true,
	}

	if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil {
		return err, []byte{}
	}

	return nil, buf.Bytes()
}

func (s *Scan) setProto(proto string) error {
	var netProto = fmt.Sprintf("%s:%s", s.network, proto)

	switch netProto {
	case "ip4:tcp":
		s.ip = &layers.IPv4{
			SrcIP:    s.laddr,
			DstIP:    s.raddr,
			Protocol: layers.IPProtocolTCP,
		}
	case "ip4:udp":
		s.ip = &layers.IPv4{
			SrcIP:    s.laddr,
			DstIP:    s.raddr,
			Protocol: layers.IPProtocolUDP,
		}
	case "ip6:tcp":
		s.ip = &layers.IPv6{
			SrcIP:      s.laddr,
			DstIP:      s.raddr,
			NextHeader: layers.IPProtocolTCP,
		}
	case "ip6:udp":
		s.ip = &layers.IPv6{
			SrcIP:      s.laddr,
			DstIP:      s.raddr,
			NextHeader: layers.IPProtocolUDP,
		}

	}

	return nil
}

func (s *Scan) setLocalNet() error {
	conn, err := net.Dial("udp", net.JoinHostPort(s.raddr.String(), "80"))
	if err != nil {
		return err
	}
	defer conn.Close()

	if lAddr, ok := conn.LocalAddr().(*net.UDPAddr); ok {
		s.laddr = lAddr.IP
		s.lport = lAddr.Port
	} else {
		return fmt.Errorf("can not find local address/port")
	}

	ifs, _ := net.Interfaces()
	for _, i := range ifs {
		addrs, _ := i.Addrs()
		for _, addr := range addrs {
			ip, _, _ := net.ParseCIDR(addr.String())
			if ip.String() == s.laddr.String() {
				s.ifName = i.Name
				break
			}
		}
	}

	return nil
}

func (s *Scan) tcpSYNScan() ([]int, error) {
	var err error

	ctx, cancel := context.WithCancel(context.Background())

	if err = s.setLocalNet(); err != nil {
		return []int{}, fmt.Errorf("source IP address not configured")
	}
	if err = s.setProto("tcp"); err != nil {
		println(err.Error())
	}
	go func() {
		if err := s.sendTCPSYN(); err != nil {
			println(err.Error())
		}
		cancel()
	}()

	openPorts, err := s.pCapture(ctx)
	return openPorts, err
}

func (s *Scan) pCapture(ctx context.Context) ([]int, error) {
	var (
		tcp       *layers.TCP
		timeout   = 100 * time.Nanosecond
		openPorts []int
		ok        bool
	)
	handle, err := pcap.OpenLive(s.ifName, 6*1024, false, timeout)
	if err != nil {
		return nil, err
	}
	defer handle.Close()

	filter := "tcp and src host " + s.raddr.String()
	if err := handle.SetBPFFilter(filter); err != nil {
		return nil, err
	}

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
LOOP:
	for {
		select {
		case <-ctx.Done():
			break LOOP
		case packet := <-packetSource.Packets():
			tcpLayer := packet.Layer(layers.LayerTypeTCP)
			if tcpLayer != nil {
				if tcp, ok = tcpLayer.(*layers.TCP); !ok {
					continue
				}
				if tcp.SYN && tcp.ACK {
					p, _ := strconv.Atoi(fmt.Sprintf("%d", tcp.SrcPort))
					openPorts = append(openPorts, p)
				}
				continue
			}
		}
	}
	openPorts = uniqSlice(openPorts)
	sort.Ints(openPorts)
	return openPorts, nil
}

func (s *Scan) sendTCPSYN() error {
	var (
		buf  []byte
		err  error
		conn net.PacketConn
	)
	if s.network != "ip6" {
		conn, err = net.ListenPacket(s.network+":tcp", "0.0.0.0")
	} else {
		conn, err = net.ListenPacket(s.network+":tcp", "[::]")
	}
	if err != nil {
		return err
	}
	for i := s.minPort; i <= s.maxPort; i++ {
		if err, buf = s.packetDataTCP(i); err != nil {
			return err
		}
		if _, err := conn.WriteTo(buf, &net.IPAddr{IP: s.raddr}); err != nil {
			println(err.Error())
		}
		time.Sleep(5 * time.Millisecond)
	}
	time.Sleep(1 * time.Second)
	return nil
}

// tcpConnScan tries to scan a single host
func (s *Scan) tcpConnScan() []int {
	var wg sync.WaitGroup

	var ports []int
	for i := s.minPort; i <= s.maxPort; i++ {
		wg.Add(1)
		go func(i int) {
			for {
				host := net.JoinHostPort(s.raddr.String(), fmt.Sprintf("%d", i))
				conn, err := net.DialTimeout("tcp", host, 2*time.Second)
				if err != nil {
					if strings.Contains(err.Error(), "too many open files") {
						// random back-off
						time.Sleep(time.Duration(10+rand.Int31n(30)) * time.Millisecond)
						continue
					}
					wg.Done()
					return
				}
				conn.Close()
				break
			}
			ports = append(ports, i)
			wg.Done()
		}(i)
	}

	wg.Wait()
	sort.Ints(ports)
	return ports
}

func uniqSlice(s []int) []int {
	m := map[int]bool{}
	r := []int{}
	for _, v := range s {
		if _, ok := m[v]; !ok {
			m[v] = true
			r = append(r, v)
		}
	}
	return r
}

// help represents guide to user
func help(cfg cli.Config) {
	fmt.Printf(`
    usage:
          scan ip/host [option]
    options:
          -p port-range or port number      Specified range or port number (default is %s)
          -c                                TCP connect scan (default is TCP SYN scan)
          -4                                Force IPv4
          -6                                Force IPv6
    example:
          scan 8.8.8.8 -p 53
          scan www.google.com -p 1-500
          scan freebsd.org -6
	`,
		cfg.Scan.Port)
}
<|endoftext|>


<commit_before>package recurrent

import (
	"time"

	"github.com/efritz/glock"
)

type (
	// Scheduler periodically executes the a target function. A scheduler can
	// be signaled to execute the function immediately as often as the user likes
	// via the Signal method (if the scheduler is configured to allow it).
	Scheduler interface {
		// Start the scheduler in a goroutine.
		Start()

		// Stop the scheduler. No additional signals are meaningful. This method
		// must not be called twice.
		Stop()

		// Signal the scheduler to execute the function immediately. This method is
		// always non-blocking, and may be ignored depending on if the scheduler is
		// throttling signals or not. This method must not be called after Stop.
		Signal()
	}

	scheduler struct {
		target   func()
		interval time.Duration
		clock    glock.Clock
		withChan func(f func(chan struct{}))
		quit     chan struct{}
		signal   chan struct{}
	}

	SchedulerConfig func(*scheduler)
)

func NewScheduler(target func(), configs ...SchedulerConfig) Scheduler {
	withChan := func(f func(chan struct{})) {
		quit := make(chan struct{})
		defer close(quit)

		f(hammer(quit))
	}

	scheduler := &scheduler{
		target:   target,
		interval: time.Second,
		clock:    glock.NewRealClock(),
		withChan: withChan,
		quit:     make(chan struct{}),
		signal:   make(chan struct{}, 1),
	}

	for _, config := range configs {
		config(scheduler)
	}

	return scheduler
}

func WithInterval(interval time.Duration) SchedulerConfig {
	return func(s *scheduler) {
		s.interval = interval
	}
}

func WithThrottle(minInterval time.Duration) SchedulerConfig {
	return func(s *scheduler) {
		s.withChan = func(f func(chan struct{})) {
			ticker := s.clock.NewTicker(minInterval)
			defer ticker.Stop()

			f(convert(ticker.Chan()))
		}
	}
}

func withClock(clock glock.Clock) SchedulerConfig {
	return func(s *scheduler) { s.clock = clock }
}

func (s *scheduler) Start() {
	go func() {
		defer close(s.signal)

		s.withChan(func(c chan struct{}) {
			t := throttle(c, s.signal)

			for {
				select {
				case <-t:
					s.target()

				case <-s.clock.After(s.interval):
					s.Signal()

				case <-s.quit:
					return
				}
			}
		})
	}()
}

func (s *scheduler) Stop() {
	close(s.quit)
}

func (s *scheduler) Signal() {
	select {
	case s.signal <- struct{}{}:
	default:
	}
}
<commit_msg> (Closes #3) Rename config funcs.<commit_after>package recurrent

import (
	"time"

	"github.com/efritz/glock"
)

type (
	// Scheduler periodically executes the a target function. A scheduler can
	// be signaled to execute the function immediately as often as the user likes
	// via the Signal method (if the scheduler is configured to allow it).
	Scheduler interface {
		// Start the scheduler in a goroutine.
		Start()

		// Stop the scheduler. No additional signals are meaningful. This method
		// must not be called twice.
		Stop()

		// Signal the scheduler to execute the function immediately. This method is
		// always non-blocking, and may be ignored depending on if the scheduler is
		// throttling signals or not. This method must not be called after Stop.
		Signal()
	}

	scheduler struct {
		target   func()
		interval time.Duration
		clock    glock.Clock
		withChan func(f func(chan struct{}))
		quit     chan struct{}
		signal   chan struct{}
	}

	// ConfigFunc is a function used to initialize a new scheduler.
	ConfigFunc func(*scheduler)
)

// NewScheduler creates a new scheduler that will invoke the target function.
func NewScheduler(target func(), configs ...ConfigFunc) Scheduler {
	withChan := func(f func(chan struct{})) {
		quit := make(chan struct{})
		defer close(quit)

		f(hammer(quit))
	}

	scheduler := &scheduler{
		target:   target,
		interval: time.Second,
		clock:    glock.NewRealClock(),
		withChan: withChan,
		quit:     make(chan struct{}),
		signal:   make(chan struct{}, 1),
	}

	for _, config := range configs {
		config(scheduler)
	}

	return scheduler
}

// WithInterval sets the interval at which the scheduler will invoke the
// scheduled function (default is one second).
func WithInterval(interval time.Duration) ConfigFunc {
	return func(s *scheduler) {
		s.interval = interval
	}
}

// WithThrottle sets the minimum duration between two invocations of the
// scheduled function (there is no default minimum).
func WithThrottle(minInterval time.Duration) ConfigFunc {
	return func(s *scheduler) {
		s.withChan = func(f func(chan struct{})) {
			ticker := s.clock.NewTicker(minInterval)
			defer ticker.Stop()

			f(convert(ticker.Chan()))
		}
	}
}

func withClock(clock glock.Clock) ConfigFunc {
	return func(s *scheduler) { s.clock = clock }
}

func (s *scheduler) Start() {
	go func() {
		defer close(s.signal)

		s.withChan(func(c chan struct{}) {
			t := throttle(c, s.signal)

			for {
				select {
				case <-t:
					s.target()

				case <-s.clock.After(s.interval):
					s.Signal()

				case <-s.quit:
					return
				}
			}
		})
	}()
}

func (s *scheduler) Stop() {
	close(s.quit)
}

func (s *scheduler) Signal() {
	select {
	case s.signal <- struct{}{}:
	default:
	}
}
<|endoftext|>


<commit_before>package sparse

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"testing"

	"time"

	"strconv"

	"github.com/rancher/sparse-tools/log"
)

type TestFileInterval struct {
	FileInterval
	dataMask byte // XORed with other generated data bytes
}

func (i TestFileInterval) String() string {
	return fmt.Sprintf("{%v %2X}", i.FileInterval, i.dataMask)
}

func TestRandomLayout10MB(t *testing.T) {
	const seed = 0
	const size = 10 /*MB*/ << 20
	prefix := "ssync"
	name := tempFilePath(prefix)
	defer fileCleanup(name)

	layoutStream := generateLayout(prefix, size, seed)
	layout1, layout2 := teeLayout(layoutStream)

	done := createTestSparseFileLayout(name, size, layout1)
	layoutTmp := unstreamLayout(layout2)
	<-done
	log.Info("Done writing layout of ", len(layoutTmp), "items")

	layout := streamLayout(layoutTmp)
	err := checkTestSparseFileLayout(name, layout)
	if err != nil {
		t.Fatal(err)
	}
}

func TestRandomLayout100MB(t *testing.T) {
	const seed = 0
	const size = 100 /*MB*/ << 20
	prefix := "ssync"
	name := tempFilePath(prefix)
	defer fileCleanup(name)

	layoutStream := generateLayout(prefix, size, seed)
	layout1, layout2 := teeLayout(layoutStream)

	done := createTestSparseFileLayout(name, size, layout1)
	layoutTmp := unstreamLayout(layout2)
	<-done
	log.Info("Done writing layout of ", len(layoutTmp), "items")

	layout := streamLayout(layoutTmp)
	err := checkTestSparseFileLayout(name, layout)
	if err != nil {
		t.Fatal(err)
	}
}

const srcPrefix = "ssync-src"
const dstPrefix = "ssync-dst"

func TestRandomSync100MB(t *testing.T) {
	const seed = 1
	const size = 100 /*MB*/ << 20
	srcName := tempFilePath(srcPrefix)
	dstName := tempFilePath(dstPrefix)
	RandomSync(t, size, seed, srcName, dstName, true /*create dstFile*/)
}

func TestRandomSyncNoDst100MB(t *testing.T) {
	const seed = 2
	const size = 100 /*MB*/ << 20
	srcName := tempFilePath(srcPrefix)
	dstName := tempFilePath(dstPrefix)
	RandomSync(t, size, seed, srcName, dstName, false /*no dstFile*/)
}

func TestRandomSyncCustomGB(t *testing.T) {
	if testing.Short() {
		t.Skip("skipped custom random sync")
	}

	// random seed
	seed := time.Now().UnixNano()
	log.LevelPush(log.LevelInfo)
	defer log.LevelPop()
	log.Info("seed=", seed)

	// default size
	var size = int64(100) /*MB*/ << 20
	arg := os.Args[len(os.Args)-1]
	sizeGB, err := strconv.Atoi(arg)
	if err != nil {
		log.Info("")
		log.Info("Using default 100MB size for random seed test")
		log.Info("For alternative size in GB and in current dir(vs tmp) use -timeout 10m -args <GB>")
		log.Info("Increase the optional -timeout value for 20GB and larger sizes")
		log.Info("")
		srcName := tempFilePath(srcPrefix)
		dstName := tempFilePath(dstPrefix)
		RandomSync(t, size, seed, srcName, dstName, true /*create dstFile*/)
	} else {
		log.Info("Using ", sizeGB, "(GB) size for random seed test")
		size = int64(sizeGB) << 30
		srcName := tempBigFilePath(srcPrefix)
		dstName := tempBigFilePath(dstPrefix)
		RandomSync(t, size, seed, srcName, dstName, true /*create dstFile*/)
	}
}

func RandomSync(t *testing.T, size, seed int64, srcPath, dstPath string, dstCreate bool) {
	const localhost = "127.0.0.1"
	const timeout = 10 //seconds
	var remoteAddr = TCPEndPoint{localhost, 5000}

	defer filesCleanup(srcPath, dstPath)

	srcLayoutStream1, srcLayoutStream2 := teeLayout(generateLayout(srcPrefix, size, seed))
	dstLayoutStream := generateLayout(dstPrefix, size, seed+1)

	srcDone := createTestSparseFileLayout(srcPath, size, srcLayoutStream1)
	srcLayout := unstreamLayout(srcLayoutStream2)
	if dstCreate {
		// Create destination with some data
		dstDone := createTestSparseFileLayout(dstPath, size, dstLayoutStream)
		<-dstDone
	}
	<-srcDone
	log.Info("Done writing layout of ", len(srcLayout), "items")

	log.Info("Syncing...")

	go TestServer(remoteAddr, timeout)
	_, err := SyncFile(srcPath, remoteAddr, dstPath, timeout)

	if err != nil {
		t.Fatal("sync error")
	}
	log.Info("...syncing done")

	log.Info("Checking...")
	layoutStream := streamLayout(srcLayout)
	err = checkTestSparseFileLayout(dstPath, layoutStream)
	if err != nil {
		t.Fatal(err)
	}
}

func unstreamLayout(in <-chan TestFileInterval) []TestFileInterval {
	layout := make([]TestFileInterval, 0, 4096)
	for i := range in {
		log.Trace("unstream", i)
		layout = append(layout, i)
	}
	return layout
}

func streamLayout(in []TestFileInterval) (out chan TestFileInterval) {
	out = make(chan TestFileInterval, 128)

	go func() {
		for _, i := range in {
			log.Trace("stream", i)
			out <- i
		}
		close(out)
	}()

	return out
}

func teeLayout(in <-chan TestFileInterval) (out1 chan TestFileInterval, out2 chan TestFileInterval) {
	out1 = make(chan TestFileInterval, 128)
	out2 = make(chan TestFileInterval, 128)

	go func() {
		for i := range in {
			log.Trace("Tee1...")
			out1 <- i
			log.Trace("Tee2...")
			out2 <- i
		}
		close(out1)
		close(out2)
	}()

	return out1, out2
}

func generateLayout(prefix string, size, seed int64) <-chan TestFileInterval {
	const maxInterval = 256 // Blocks
	layoutStream := make(chan TestFileInterval, 128)
	r := rand.New(rand.NewSource(seed))

	go func() {
		offset := int64(0)
		for offset < size {
			blocks := int64(r.Intn(maxInterval)) + 1 // 1..maxInterval
			length := blocks * Blocks
			if offset+length > size {
				// don't overshoot size
				length = size - offset
			}

			interval := Interval{offset, offset + length}
			offset += interval.Len()

			kind := SparseHole
			var mask byte
			if r.Intn(2) == 0 {
				// Data
				kind = SparseData
				mask = 0xAA * byte(r.Intn(10)/9) // 10%
			}
			t := TestFileInterval{FileInterval{kind, interval}, mask}
			log.Debug(prefix, t)
			layoutStream <- t
		}
		close(layoutStream)
	}()

	return layoutStream
}

func makeIntervalData(interval TestFileInterval) []byte {
	data := make([]byte, interval.Len())
	if SparseData == interval.Kind {
		for i := range data {
			value := byte((interval.Begin + int64(i)) / Blocks)
			data[i] = interval.dataMask ^ value
		}
	}
	return data
}

func createTestSparseFileLayout(name string, fileSize int64, layout <-chan TestFileInterval) (done chan struct{}) {
	done = make(chan struct{})

	// Fill up file with layout data
	go func() {
		f, err := os.Create(name)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		err = f.Truncate(fileSize)
		if err != nil {
			log.Fatal(err)
		}

		for interval := range layout {
			log.Debug("writing...", interval)
			if SparseData == interval.Kind {
				size := batch * Blocks
				for offset := interval.Begin; offset < interval.End; {
					if offset+size > interval.End {
						size = interval.End - offset
					}
					chunkInterval := TestFileInterval{FileInterval{SparseData, Interval{offset, offset + size}}, interval.dataMask}
					data := makeIntervalData(chunkInterval)
					_, err = f.WriteAt(data, offset)
					if err != nil {
						log.Fatal(err)
					}
					offset += size
				}
			}
		}
		f.Sync()
		close(done)
	}()

	return done
}

func checkTestSparseFileLayout(name string, layout <-chan TestFileInterval) error {
	f, err := os.Open(name)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	// Read and check data
	for interval := range layout {
		log.Debug("checking...", interval)
		if SparseData == interval.Kind {
			size := batch * Blocks
			for offset := interval.Begin; offset < interval.End; {
				if offset+size > interval.End {
					size = interval.End - offset
				}
				dataModel := makeIntervalData(TestFileInterval{FileInterval{SparseData, Interval{offset, offset + size}}, interval.dataMask})
				data := make([]byte, size)
				f.ReadAt(data, offset)
				offset += size

				if !bytes.Equal(data, dataModel) {
					return errors.New(fmt.Sprint("data equality check failure at", interval))
				}
			}
		} else if SparseHole == interval.Kind {
			layoutActual, err := RetrieveLayout(f, interval.Interval)
			if err != nil {
				return errors.New(fmt.Sprint("hole retrieval failure at", interval, err))
			}
			if len(layoutActual) != 1 {
				return errors.New(fmt.Sprint("hole check failure at", interval))
			}
			if layoutActual[0] != interval.FileInterval {
				return errors.New(fmt.Sprint("hole equality check failure at", interval))
			}
		}
	}
	return nil // success
}
<commit_msg>'test: fixed minor test race - prevented occasional open file failure upon ssync start because the file was not closed prior to indicating completion @createTestSparseFileLayout'<commit_after>package sparse

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"testing"

	"time"

	"strconv"

	"github.com/rancher/sparse-tools/log"
)

type TestFileInterval struct {
	FileInterval
	dataMask byte // XORed with other generated data bytes
}

func (i TestFileInterval) String() string {
	return fmt.Sprintf("{%v %2X}", i.FileInterval, i.dataMask)
}

func TestRandomLayout10MB(t *testing.T) {
	const seed = 0
	const size = 10 /*MB*/ << 20
	prefix := "ssync"
	name := tempFilePath(prefix)
	defer fileCleanup(name)

	layoutStream := generateLayout(prefix, size, seed)
	layout1, layout2 := teeLayout(layoutStream)

	done := createTestSparseFileLayout(name, size, layout1)
	layoutTmp := unstreamLayout(layout2)
	<-done
	log.Info("Done writing layout of ", len(layoutTmp), "items")

	layout := streamLayout(layoutTmp)
	err := checkTestSparseFileLayout(name, layout)
	if err != nil {
		t.Fatal(err)
	}
}

func TestRandomLayout100MB(t *testing.T) {
	const seed = 0
	const size = 100 /*MB*/ << 20
	prefix := "ssync"
	name := tempFilePath(prefix)
	defer fileCleanup(name)

	layoutStream := generateLayout(prefix, size, seed)
	layout1, layout2 := teeLayout(layoutStream)

	done := createTestSparseFileLayout(name, size, layout1)
	layoutTmp := unstreamLayout(layout2)
	<-done
	log.Info("Done writing layout of ", len(layoutTmp), "items")

	layout := streamLayout(layoutTmp)
	err := checkTestSparseFileLayout(name, layout)
	if err != nil {
		t.Fatal(err)
	}
}

const srcPrefix = "ssync-src"
const dstPrefix = "ssync-dst"

func TestRandomSync100MB(t *testing.T) {
	const seed = 1
	const size = 100 /*MB*/ << 20
	srcName := tempFilePath(srcPrefix)
	dstName := tempFilePath(dstPrefix)
	RandomSync(t, size, seed, srcName, dstName, true /*create dstFile*/)
}

func TestRandomSyncNoDst100MB(t *testing.T) {
	const seed = 2
	const size = 100 /*MB*/ << 20
	srcName := tempFilePath(srcPrefix)
	dstName := tempFilePath(dstPrefix)
	RandomSync(t, size, seed, srcName, dstName, false /*no dstFile*/)
}

func TestRandomSyncCustomGB(t *testing.T) {
	if testing.Short() {
		t.Skip("skipped custom random sync")
	}

	// random seed
	seed := time.Now().UnixNano()
	log.LevelPush(log.LevelInfo)
	defer log.LevelPop()
	log.Info("seed=", seed)

	// default size
	var size = int64(100) /*MB*/ << 20
	arg := os.Args[len(os.Args)-1]
	sizeGB, err := strconv.Atoi(arg)
	if err != nil {
		log.Info("")
		log.Info("Using default 100MB size for random seed test")
		log.Info("For alternative size in GB and in current dir(vs tmp) use -timeout 10m -args <GB>")
		log.Info("Increase the optional -timeout value for 20GB and larger sizes")
		log.Info("")
		srcName := tempFilePath(srcPrefix)
		dstName := tempFilePath(dstPrefix)
		RandomSync(t, size, seed, srcName, dstName, true /*create dstFile*/)
	} else {
		log.Info("Using ", sizeGB, "(GB) size for random seed test")
		size = int64(sizeGB) << 30
		srcName := tempBigFilePath(srcPrefix)
		dstName := tempBigFilePath(dstPrefix)
		RandomSync(t, size, seed, srcName, dstName, true /*create dstFile*/)
	}
}

func RandomSync(t *testing.T, size, seed int64, srcPath, dstPath string, dstCreate bool) {
	const localhost = "127.0.0.1"
	const timeout = 10 //seconds
	var remoteAddr = TCPEndPoint{localhost, 5000}

	defer filesCleanup(srcPath, dstPath)

	srcLayoutStream1, srcLayoutStream2 := teeLayout(generateLayout(srcPrefix, size, seed))
	dstLayoutStream := generateLayout(dstPrefix, size, seed+1)

	srcDone := createTestSparseFileLayout(srcPath, size, srcLayoutStream1)
	srcLayout := unstreamLayout(srcLayoutStream2)
	if dstCreate {
		// Create destination with some data
		dstDone := createTestSparseFileLayout(dstPath, size, dstLayoutStream)
		<-dstDone
	}
	<-srcDone
	log.Info("Done writing layout of ", len(srcLayout), "items")

	log.Info("Syncing...")

	go TestServer(remoteAddr, timeout)
	_, err := SyncFile(srcPath, remoteAddr, dstPath, timeout)

	if err != nil {
		t.Fatal("sync error")
	}
	log.Info("...syncing done")

	log.Info("Checking...")
	layoutStream := streamLayout(srcLayout)
	err = checkTestSparseFileLayout(dstPath, layoutStream)
	if err != nil {
		t.Fatal(err)
	}
}

func unstreamLayout(in <-chan TestFileInterval) []TestFileInterval {
	layout := make([]TestFileInterval, 0, 4096)
	for i := range in {
		log.Trace("unstream", i)
		layout = append(layout, i)
	}
	return layout
}

func streamLayout(in []TestFileInterval) (out chan TestFileInterval) {
	out = make(chan TestFileInterval, 128)

	go func() {
		for _, i := range in {
			log.Trace("stream", i)
			out <- i
		}
		close(out)
	}()

	return out
}

func teeLayout(in <-chan TestFileInterval) (out1 chan TestFileInterval, out2 chan TestFileInterval) {
	out1 = make(chan TestFileInterval, 128)
	out2 = make(chan TestFileInterval, 128)

	go func() {
		for i := range in {
			log.Trace("Tee1...")
			out1 <- i
			log.Trace("Tee2...")
			out2 <- i
		}
		close(out1)
		close(out2)
	}()

	return out1, out2
}

func generateLayout(prefix string, size, seed int64) <-chan TestFileInterval {
	const maxInterval = 256 // Blocks
	layoutStream := make(chan TestFileInterval, 128)
	r := rand.New(rand.NewSource(seed))

	go func() {
		offset := int64(0)
		for offset < size {
			blocks := int64(r.Intn(maxInterval)) + 1 // 1..maxInterval
			length := blocks * Blocks
			if offset+length > size {
				// don't overshoot size
				length = size - offset
			}

			interval := Interval{offset, offset + length}
			offset += interval.Len()

			kind := SparseHole
			var mask byte
			if r.Intn(2) == 0 {
				// Data
				kind = SparseData
				mask = 0xAA * byte(r.Intn(10)/9) // 10%
			}
			t := TestFileInterval{FileInterval{kind, interval}, mask}
			log.Debug(prefix, t)
			layoutStream <- t
		}
		close(layoutStream)
	}()

	return layoutStream
}

func makeIntervalData(interval TestFileInterval) []byte {
	data := make([]byte, interval.Len())
	if SparseData == interval.Kind {
		for i := range data {
			value := byte((interval.Begin + int64(i)) / Blocks)
			data[i] = interval.dataMask ^ value
		}
	}
	return data
}

func createTestSparseFileLayout(name string, fileSize int64, layout <-chan TestFileInterval) (done chan struct{}) {
	done = make(chan struct{})

	// Fill up file with layout data
	go func() {
		f, err := os.Create(name)
		if err != nil {
			log.Fatal(err)
		}
		err = f.Truncate(fileSize)
		if err != nil {
			log.Fatal(err)
		}

		for interval := range layout {
			log.Debug("writing...", interval)
			if SparseData == interval.Kind {
				size := batch * Blocks
				for offset := interval.Begin; offset < interval.End; {
					if offset+size > interval.End {
						size = interval.End - offset
					}
					chunkInterval := TestFileInterval{FileInterval{SparseData, Interval{offset, offset + size}}, interval.dataMask}
					data := makeIntervalData(chunkInterval)
					_, err = f.WriteAt(data, offset)
					if err != nil {
						log.Fatal(err)
					}
					offset += size
				}
			}
		}
		f.Sync()
		f.Close()
		close(done)
	}()

	return done
}

func checkTestSparseFileLayout(name string, layout <-chan TestFileInterval) error {
	f, err := os.Open(name)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	// Read and check data
	for interval := range layout {
		log.Debug("checking...", interval)
		if SparseData == interval.Kind {
			size := batch * Blocks
			for offset := interval.Begin; offset < interval.End; {
				if offset+size > interval.End {
					size = interval.End - offset
				}
				dataModel := makeIntervalData(TestFileInterval{FileInterval{SparseData, Interval{offset, offset + size}}, interval.dataMask})
				data := make([]byte, size)
				f.ReadAt(data, offset)
				offset += size

				if !bytes.Equal(data, dataModel) {
					return errors.New(fmt.Sprint("data equality check failure at", interval))
				}
			}
		} else if SparseHole == interval.Kind {
			layoutActual, err := RetrieveLayout(f, interval.Interval)
			if err != nil {
				return errors.New(fmt.Sprint("hole retrieval failure at", interval, err))
			}
			if len(layoutActual) != 1 {
				return errors.New(fmt.Sprint("hole check failure at", interval))
			}
			if layoutActual[0] != interval.FileInterval {
				return errors.New(fmt.Sprint("hole equality check failure at", interval))
			}
		}
	}
	return nil // success
}
<|endoftext|>


<commit_before>// +build linux,amd64 darwin,amd64

package types

type TMessage struct {
	Msg        Cardinal
	_UnusedMsg Cardinal
	WParam     WAPRAM
	LParam     LPARAM
	Result     LRESULT
}
<commit_msg>Update build constraints.<commit_after>// +build !windows
// +build amd64

package types

type TMessage struct {
	Msg        Cardinal
	_UnusedMsg Cardinal
	WParam     WAPRAM
	LParam     LPARAM
	Result     LRESULT
}
<|endoftext|>


<commit_before>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

import (
    "errors"
    "strings"
    "unicode"

    "sippy/utils"
)

func extract_to_next_token(s string, match string, invert bool) (string, string) {
    i := 0
    for i < len(s) {
        if (! invert && strings.IndexByte(match, s[i]) == -1) || (invert && strings.IndexByte(match, s[i]) != -1) {
            break
        }
        i++
    }
    if i == 0 {
        return "", s
    }
    if i == len(s) {
        return s, ""
    }
    return s[:i], s[i:]
}

type UpdateLookupOpts struct {
    DestinationIP   string
    LocalIP         string
    Codecs          []string
    Otherparams     string
    RemoteIP        string
    RemotePort      string
    FromTag         string
    ToTag           string
    NotifySocket    string
    NotifyTag       string
}

func NewUpdateLookupOpts(s, args string) (*UpdateLookupOpts, error) {
    arr := sippy_utils.FieldsN(args, 3)
    if len(arr) != 3 {
        return nil, errors.New("The lookup opts must have at least three arguments")
    }
    self := &UpdateLookupOpts{
        RemoteIP        : arr[0],
        RemotePort      : arr[1],
    }
    arr = sippy_utils.FieldsN(arr[2], 2)
    self.FromTag = arr[0]
    if len(arr) > 1 {
        arr2 := sippy_utils.FieldsN(arr[1], 3)
        switch len(arr2) {
        case 1:
            self.ToTag = arr2[0]
        case 2:
            self.NotifySocket, self.NotifyTag = arr2[0], arr2[1]
        default:
            self.ToTag, self.NotifySocket, self.NotifyTag = arr2[0], arr2[1], arr2[2]
        }
    }
    for len(s) > 0 {
        var val string
        if s[0] == 'R' {
            val, s = extract_to_next_token(s[1:], "1234567890.", false)
            val = strings.TrimSpace(val)
            if len(val) > 0 {
                self.DestinationIP = val
            }
        }
        switch s[0] {
        case 'L':
            val, s = extract_to_next_token(s[1:], "1234567890.", false)
            val = strings.TrimSpace(val)
            if len(val) > 0 {
                self.LocalIP = val
            }
        case 'c':
            val, s = extract_to_next_token(s[1:], "1234567890,", false)
            val = strings.TrimSpace(val)
            if len(val) > 0 {
                self.Codecs = strings.Split(val, ",")
            }
        default:
            val, s = extract_to_next_token(s, "cR", true)
            if len(val) > 0 {
                self.Otherparams += val
            }
        }
    }
    return self, nil
}

func (self *UpdateLookupOpts) Getstr(call_id string/*, swaptags bool*/) string {
    s := ""
    if self.DestinationIP != "" {
        s += "R" + self.DestinationIP
    }
    if self.LocalIP != "" {
        s += "L" + self.LocalIP
    }
    if self.Codecs != nil {
        s += "c" + strings.Join(self.Codecs, ",")
    }
    s += self.Otherparams
    s += " " + call_id
    if self.RemoteIP != "" {
        s += " " + self.RemoteIP
    }
    if self.RemotePort != "" {
        s += " " + self.RemotePort
    }
    /*
    from_tag, to_tag := self.FromTag, self.to_tag
    if swaptags {
        if self.to_tag == "" {
            return "", errors.New('UpdateLookupOpts::Getstr(swaptags = True): to_tag is not set')
        }
        to_tag, from_tag = self.FromTag, self.to_tag
    }
    */
    if self.FromTag != "" {
        s += " " + self.FromTag
    }
    if self.ToTag != "" {
        s += " " + self.ToTag
    }
    if self.NotifySocket != "" {
        s += " " + self.NotifySocket
    }
    if self.NotifyTag != "" {
        s += " " + self.NotifyTag
    }
    return s
}

type Rtp_proxy_cmd struct {
    Type            byte
    ULOpts          *UpdateLookupOpts
    CommandOpts     string
    CallId          string
    args            string
    //nretr = None
}

func NewRtp_proxy_cmd(cmd string) (*Rtp_proxy_cmd, error) {
    self := &Rtp_proxy_cmd{
        Type    : strings.ToUpper(cmd[:1])[0],
    }
    switch self.Type {
    case 'U': fallthrough
    case 'L': fallthrough
    case 'D': fallthrough
    case 'P': fallthrough
    case 'S': fallthrough
    case 'R': fallthrough
    case 'C': fallthrough
    case 'Q':
        var command_opts, args string
        arr := sippy_utils.FieldsN(cmd, 3)
        if len(arr) != 3 {
            return nil, errors.New("The command must have at least three parts")
        }
        command_opts, self.CallId, args = arr[0], arr[1], arr[2]
        switch self.Type {
        case 'U': fallthrough
        case 'L':
            var err error
            self.ULOpts, err = NewUpdateLookupOpts(command_opts[1:], args)
            if err != nil {
                return nil, err
            }
        default:
            self.args = args
            self.CommandOpts = command_opts[1:]
        }
    case 'G':
        if ! unicode.IsSpace([]rune(cmd)[1]) {
            cparts := sippy_utils.FieldsN(cmd[1:], 2)
            if len(cparts) > 1 {
                self.CommandOpts, self.args = cparts[0], cparts[1]
            } else {
                self.CommandOpts = cparts[0]
            }
        } else {
            self.args = strings.TrimSpace(cmd[1:])
        }
    default:
        self.CommandOpts = cmd[1:]
    }
    return self, nil
}

func (self *Rtp_proxy_cmd) String() string {
    s := string([]byte{ self.Type })
    if self.ULOpts != nil {
        s += self.ULOpts.Getstr(self.CallId)
    } else {
        if self.CommandOpts != "" {
            s += self.CommandOpts
        }
        if self.CallId != "" {
            s += " " + self.CallId
        }
    }
    if self.args != "" {
        s += " " + self.args
    }
    return s
}

/*
class Rtpp_stats(object):
    spookyprefix = ''
    verbose = False

    def __init__(self, snames):
        all_types = []
        for sname in snames:
            if sname != 'total_duration':
                stype = int
            else:
                stype = float
            self.__dict__[self.spookyprefix + sname] = stype()
            all_types.append(stype)
        self.all_names = tuple(snames)
        self.all_types = tuple(all_types)

    def __iadd__(self, other):
        for sname in self.all_names:
            aname = self.spookyprefix + sname
            self.__dict__[aname] += other.__dict__[aname]
        return self

    def parseAndAdd(self, rstr):
        rparts = rstr.split(None, len(self.all_names) - 1)
        for i in range(0, len(self.all_names)):
            stype = self.all_types[i]
            rval = stype(rparts[i])
            aname = self.spookyprefix + self.all_names[i]
            self.__dict__[aname] += rval

    def __str__(self):
        aname = self.spookyprefix + self.all_names[0]
        if self.verbose:
            rval = '%s=%s' % (self.all_names[0], str(self.__dict__[aname]))
        else:
            rval = str(self.__dict__[aname])
        for sname in self.all_names[1:]:
            aname = self.spookyprefix + sname
            if self.verbose:
                rval += ' %s=%s' % (sname, str(self.__dict__[aname]))
            else:
                rval += ' %s' % str(self.__dict__[aname])
        return rval

if __name__ == '__main__':
    rc = Rtp_proxy_cmd('G nsess_created total_duration')
    print(rc)
    print(rc.args)
    print(rc.command_opts)
    rc = Rtp_proxy_cmd('Gv nsess_created total_duration')
    print(rc)
    print(rc.args)
    print(rc.command_opts)
*/
<commit_msg>Add Rtpp_stats object and export more fields.<commit_after>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

import (
    "errors"
    "strings"
    "unicode"

    "sippy/utils"
)

func extract_to_next_token(s string, match string, invert bool) (string, string) {
    i := 0
    for i < len(s) {
        if (! invert && strings.IndexByte(match, s[i]) == -1) || (invert && strings.IndexByte(match, s[i]) != -1) {
            break
        }
        i++
    }
    if i == 0 {
        return "", s
    }
    if i == len(s) {
        return s, ""
    }
    return s[:i], s[i:]
}

type UpdateLookupOpts struct {
    DestinationIP   string
    LocalIP         string
    Codecs          []string
    Otherparams     string
    RemoteIP        string
    RemotePort      string
    FromTag         string
    ToTag           string
    NotifySocket    string
    NotifyTag       string
}

func NewUpdateLookupOpts(s, args string) (*UpdateLookupOpts, error) {
    arr := sippy_utils.FieldsN(args, 3)
    if len(arr) != 3 {
        return nil, errors.New("The lookup opts must have at least three arguments")
    }
    self := &UpdateLookupOpts{
        RemoteIP        : arr[0],
        RemotePort      : arr[1],
    }
    arr = sippy_utils.FieldsN(arr[2], 2)
    self.FromTag = arr[0]
    if len(arr) > 1 {
        arr2 := sippy_utils.FieldsN(arr[1], 3)
        switch len(arr2) {
        case 1:
            self.ToTag = arr2[0]
        case 2:
            self.NotifySocket, self.NotifyTag = arr2[0], arr2[1]
        default:
            self.ToTag, self.NotifySocket, self.NotifyTag = arr2[0], arr2[1], arr2[2]
        }
    }
    for len(s) > 0 {
        var val string
        if s[0] == 'R' {
            val, s = extract_to_next_token(s[1:], "1234567890.", false)
            val = strings.TrimSpace(val)
            if len(val) > 0 {
                self.DestinationIP = val
            }
        }
        switch s[0] {
        case 'L':
            val, s = extract_to_next_token(s[1:], "1234567890.", false)
            val = strings.TrimSpace(val)
            if len(val) > 0 {
                self.LocalIP = val
            }
        case 'c':
            val, s = extract_to_next_token(s[1:], "1234567890,", false)
            val = strings.TrimSpace(val)
            if len(val) > 0 {
                self.Codecs = strings.Split(val, ",")
            }
        default:
            val, s = extract_to_next_token(s, "cR", true)
            if len(val) > 0 {
                self.Otherparams += val
            }
        }
    }
    return self, nil
}

func (self *UpdateLookupOpts) Getstr(call_id string/*, swaptags bool*/) string {
    s := ""
    if self.DestinationIP != "" {
        s += "R" + self.DestinationIP
    }
    if self.LocalIP != "" {
        s += "L" + self.LocalIP
    }
    if self.Codecs != nil {
        s += "c" + strings.Join(self.Codecs, ",")
    }
    s += self.Otherparams
    s += " " + call_id
    if self.RemoteIP != "" {
        s += " " + self.RemoteIP
    }
    if self.RemotePort != "" {
        s += " " + self.RemotePort
    }
    /*
    from_tag, to_tag := self.FromTag, self.to_tag
    if swaptags {
        if self.to_tag == "" {
            return "", errors.New('UpdateLookupOpts::Getstr(swaptags = True): to_tag is not set')
        }
        to_tag, from_tag = self.FromTag, self.to_tag
    }
    */
    if self.FromTag != "" {
        s += " " + self.FromTag
    }
    if self.ToTag != "" {
        s += " " + self.ToTag
    }
    if self.NotifySocket != "" {
        s += " " + self.NotifySocket
    }
    if self.NotifyTag != "" {
        s += " " + self.NotifyTag
    }
    return s
}

type Rtp_proxy_cmd struct {
    Type            byte
    ULOpts          *UpdateLookupOpts
    CommandOpts     string
    CallId          string
    Args            string
    Nretr           int
}

func NewRtp_proxy_cmd(cmd string) (*Rtp_proxy_cmd, error) {
    self := &Rtp_proxy_cmd{
        Type    : strings.ToUpper(cmd[:1])[0],
    }
    switch self.Type {
    case 'U': fallthrough
    case 'L': fallthrough
    case 'D': fallthrough
    case 'P': fallthrough
    case 'S': fallthrough
    case 'R': fallthrough
    case 'C': fallthrough
    case 'Q':
        var command_opts, args string
        arr := sippy_utils.FieldsN(cmd, 3)
        if len(arr) != 3 {
            return nil, errors.New("The command must have at least three parts")
        }
        command_opts, self.CallId, args = arr[0], arr[1], arr[2]
        switch self.Type {
        case 'U': fallthrough
        case 'L':
            var err error
            self.ULOpts, err = NewUpdateLookupOpts(command_opts[1:], args)
            if err != nil {
                return nil, err
            }
        default:
            self.Args = args
            self.CommandOpts = command_opts[1:]
        }
    case 'G':
        if ! unicode.IsSpace([]rune(cmd)[1]) {
            cparts := sippy_utils.FieldsN(cmd[1:], 2)
            if len(cparts) > 1 {
                self.CommandOpts, self.Args = cparts[0], cparts[1]
            } else {
                self.CommandOpts = cparts[0]
            }
        } else {
            self.Args = strings.TrimSpace(cmd[1:])
        }
    default:
        self.CommandOpts = cmd[1:]
    }
    return self, nil
}

func (self *Rtp_proxy_cmd) String() string {
    s := string([]byte{ self.Type })
    if self.ULOpts != nil {
        s += self.ULOpts.Getstr(self.CallId)
    } else {
        if self.CommandOpts != "" {
            s += self.CommandOpts
        }
        if self.CallId != "" {
            s += " " + self.CallId
        }
    }
    if self.Args != "" {
        s += " " + self.Args
    }
    return s
}

type Rtpp_stats struct {
    spookyprefix    string
    Verbose         bool
    dict            map[string]int64
    total_duration  float64
}

func NewRtpp_stats(snames []string) *Rtpp_stats {
    self := &Rtpp_stats{
        Verbose         : false,
        spookyprefix    : "",
        dict            : make(map[string]int64),
    }
    for _, sname := range snames {
        if sname != "total_duration" {
            self.dict[self.spookyprefix + sname] = 0
        }
    }
    return self
}
/*
    def __iadd__(self, other):
        for sname in self.all_names:
            aname = self.spookyprefix + sname
            self.__dict__[aname] += other.__dict__[aname]
        return self

    def parseAndAdd(self, rstr):
        rparts = rstr.split(None, len(self.all_names) - 1)
        for i in range(0, len(self.all_names)):
            stype = self.all_types[i]
            rval = stype(rparts[i])
            aname = self.spookyprefix + self.all_names[i]
            self.__dict__[aname] += rval

    def __str__(self):
        aname = self.spookyprefix + self.all_names[0]
        if self.verbose:
            rval = '%s=%s' % (self.all_names[0], str(self.__dict__[aname]))
        else:
            rval = str(self.__dict__[aname])
        for sname in self.all_names[1:]:
            aname = self.spookyprefix + sname
            if self.verbose:
                rval += ' %s=%s' % (sname, str(self.__dict__[aname]))
            else:
                rval += ' %s' % str(self.__dict__[aname])
        return rval

if __name__ == '__main__':
    rc = Rtp_proxy_cmd('G nsess_created total_duration')
    print(rc)
    print(rc.args)
    print(rc.command_opts)
    rc = Rtp_proxy_cmd('Gv nsess_created total_duration')
    print(rc)
    print(rc.args)
    print(rc.command_opts)
*/
<|endoftext|>


<commit_before>// Package graphite implements sending metrics to Graphite via the Carbon text API
//
// This can be done by sending metrics one at a time or by buffering them through a channel.
package graphite

import (
	"fmt"
	"log"
	"net"
	"time"
)

// Interface type for Graphite.
type Graphite interface {
	Connect()
	SendMetric()
	Sendall()
	chanRecv()
	chanSend()
	sendMetric()
}

// Graphite is how we interface with the library. At minimum you must set
// Host and Port fields.
type GraphiteServer struct {
	Host    string
	Port    uint16
	Timeout time.Duration
	conn    net.Conn
}

// Metric is contains fields used for sending metrics to Graphite. If Timestamp is not set,
// one is generated when sent.
type Metric struct {
	Name      string
	Value     string
	Timestamp int64
}

// defaultTimeout is the default connection timeout used by DialTimeout.
const (
	defaultTimeout = 30
)

var doneSending = false

// connect is the unexported function used for connecting
func connect(host string, port uint16, timeout time.Duration) (net.Conn, error) {
	connectAddr := fmt.Sprintf("%s:%d", host, port)

	return net.DialTimeout("tcp", connectAddr, timeout)
}

// Connect wraps the unexported connect function.
// Sets a connection timeout if unset.
func (g *GraphiteServer) Connect() {
	var (
		err     error
		timeout time.Duration
	)

	if g.Timeout == 0 {
		g.Timeout = defaultTimeout
	}

	timeout = g.Timeout * time.Second

	g.conn, err = connect(g.Host, g.Port, timeout)
	for err != nil {
		log.Printf(err.Error())
		log.Printf(fmt.Sprintf("error connecting, retrying in %d seconds", int(timeout.Seconds())))
		time.Sleep(timeout)

		timeout = (g.Timeout + 5) * time.Second
		g.conn, err = connect(g.Host, g.Port, timeout)
	}
}

// SendMetric is used to send a single metric to Graphite.
// Sets metric.Timestamp to current Unix time if necessary.
func (g *GraphiteServer) SendMetric(metric Metric) {
	if metric.Timestamp == 0 {
		metric.Timestamp = time.Now().Unix()
	}

	g.sendMetric(metric)
}

// Sendall is used to buffered metrics to Graphite via a channel and go routines.
func (g *GraphiteServer) Sendall(buf []Metric) {
	doneSending = false

	ch := make(chan Metric, len(buf))
	go g.chanSend(ch, buf)
	go g.chanRecv(ch, len(buf))

	for doneSending == false {
		time.Sleep(1 * time.Second)
	}
}

// chanRecvMetrics reads `bufsz` numbered metrics off of the given channel and
// sends them to Graphite.
func (g *GraphiteServer) chanRecv(ch chan Metric, bufsz int) {
	for i := 0; i < bufsz; i++ {
		item := <-ch
		g.sendMetric(item)
	}

	doneSending = true
}

// chanSendMetrics sends a Metric slice to the given channel
func (g *GraphiteServer) chanSend(ch chan Metric, buffer []Metric) {
	for _, item := range buffer {
		if len(item.Name) > 0 {
			log.Printf("buffering %s", item.Name)
			ch <- item
		}
	}
}

// sendMetric is the unexported function to send a single metric to Graphite.
func (g *GraphiteServer) sendMetric(metric Metric) {
	log.Printf("sending %s", metric.Name)
	fmt.Fprintf(g.conn, "%s %s %d\n", metric.Name, metric.Value, metric.Timestamp)
}
<commit_msg>big refactor<commit_after>// Package graphite implements sending metrics to Graphite via the Carbon text API
//
// This can be done by sending metrics one at a time or by buffering them through a channel.
package graphite

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"time"
)

// Graphite is the main interface to the library
type Graphite interface {
	Connect()
	SendMetric()
	Sendall()
	chanRecv()
	chanSend()
	sendMetric()
}

// GraphiteServer is used to store the Graphite parameters
type GraphiteServer struct {
	Host    string
	Port    uint16
	Timeout time.Duration
	conn    net.Conn
}

// Metric contains fields used for sending metrics to Graphite. If Timestamp is not set,
// one is generated when sent.
type Metric struct {
	Name      string
	Value     string
	Timestamp int64
}

// defaultTimeout is the default connection timeout used by DialTimeout.
const (
	defaultTimeout  = 30
	initialWaitTime = 30
)

var doneSending = false

// Connect is a factory function for Graphite connection
func Connect(graphite GraphiteServer) GraphiteServer {
	return graphite.getconn()
}

// getconn is the unexported function used to connect to Graphite.
// If there is a problem with the connection it retries with an incremental
// sleep time.
// Returns a pointer to the GraphiteServer struct.
func (g *GraphiteServer) getconn() GraphiteServer {
	var (
		err      error
		waitTime time.Duration
	)

	connectAddr := fmt.Sprintf("%s:%d", g.Host, g.Port)

	if g.Timeout == 0 {
		g.Timeout = defaultTimeout * time.Second
	}

	waitTime = initialWaitTime
	g.conn, err = net.DialTimeout("tcp", connectAddr, g.Timeout)
	for err != nil {
		log.Printf(err.Error())
		log.Printf(fmt.Sprintf("error123 connecting, retrying in %d seconds", waitTime))
		time.Sleep(waitTime * time.Second)

		waitTime += 5
		g.conn, err = net.DialTimeout("tcp", connectAddr, g.Timeout)
	}

	return *g
}

// SendMetric is used to send a single metric to Graphite.
// Sets metric.Timestamp to current Unix time if necessary.
func (g *GraphiteServer) SendMetric(metric Metric) {
	if metric.Timestamp == 0 {
		metric.Timestamp = time.Now().Unix()
	}

	g.sendMetric(metric)
}

// Sendall is used to buffered metrics to Graphite via a channel and go routines.
func (g *GraphiteServer) Sendall(buf []Metric) {
	doneSending = false

	ch := make(chan Metric, len(buf))
	go g.chanSend(ch, buf)
	go g.chanRecv(ch, len(buf))

	for doneSending == false {
		time.Sleep(1 * time.Second)
	}
}

// chanRecvMetrics reads `bufsz` numbered metrics off of the given channel and
// sends them to Graphite.
// If a panic() is received within the stack, log the error and stop
// receiving metrics on the channel (preferably to can try and recover).
func (g *GraphiteServer) chanRecv(ch chan Metric, bufsz int) {
	defer func() {
		if r := recover(); r != nil {
			log.Println(r)
			doneSending = true
			return
		}
	}()

	for i := 0; i < bufsz; i++ {
		item := <-ch
		g.sendMetric(item)
	}

	doneSending = true
}

// chanSendMetrics sends a Metric slice to the given channel
func (g *GraphiteServer) chanSend(ch chan Metric, buffer []Metric) {
	for _, item := range buffer {
		if len(item.Name) > 0 {
			log.Printf("buffering %s", item.Name)
			ch <- item
		}
	}
}

// sendMetric is the unexported function to send a single metric to Graphite.
// If there is a problem, call panic(). If the problem occurs during a Write() call,
// try to reconnect to Graphite.
func (g *GraphiteServer) sendMetric(metric Metric) error {
	if g.conn == nil {
		panic("no graphite connection?")
	}

	if metric.HasEmptyField() {
		panic("badly formed metric")
	}

	buf := bytes.NewBufferString(fmt.Sprintf("%s %s %d\n", metric.Name, metric.Value, metric.Timestamp))

	log.Printf("sending %s", metric.Name)
	_, err := g.conn.Write(buf.Bytes())
	for err != nil {
		log.Printf("trouble writing metric; retrying: %s", err)
		g.getconn()
		err = g.sendMetric(metric)
	}

	return nil
}

// HasEmptyField is a helper function to determine if any Metric fields are empty.
func (m *Metric) HasEmptyField() bool {
	if len(m.Name) == 0 {
		return true
	} else if len(m.Value) == 0 {
		return true
	}

	return false
}
<|endoftext|>