<commit_before>package fastping

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

func TestSource(t *testing.T) {
	for i, tt := range []struct {
		firstAddr  string
		secondAddr string
		invalid    bool
	}{
		{firstAddr: "192.0.2.10", secondAddr: "192.0.2.20", invalid: false},
		{firstAddr: "2001:0DB8::10", secondAddr: "2001:0DB8::20", invalid: false},
		{firstAddr: "192.0.2", invalid: true},
	} {
		p := NewPinger()

		origSource, err := p.Source(tt.firstAddr)
		if tt.invalid {
			if err == nil {
				t.Errorf("[%d] Source should return an error but nothing: %v", i)
			}
			continue
		}
		if err != nil {
			t.Errorf("[%d] Source address failed: %v", i, err)
		}
		if origSource != "" {
			t.Errorf("[%d] Source returned an unexpected value: got %q, expected %q", i, origSource, "")
		}

		origSource, err = p.Source(tt.secondAddr)
		if err != nil {
			t.Errorf("[%d] Source address failed: %v", i, err)
		}
		if origSource != tt.firstAddr {
			t.Errorf("[%d] Source returned an unexpected value: got %q, expected %q", i, origSource, tt.firstAddr)
		}
	}

	v4Addr := "192.0.2.10"
	v6Addr := "2001:0DB8::10"

	p := NewPinger()
	_, err := p.Source(v4Addr)
	if err != nil {
		t.Errorf("Source address failed: %v", err)
	}
	_, err = p.Source(v6Addr)
	if err != nil {
		t.Errorf("Source address failed: %v", err)
	}
	origSource, err := p.Source("")
	if err != nil {
		t.Errorf("Source address failed: %v", err)
	}
	if origSource != v4Addr {
		t.Errorf("Source returned an unexpected value: got %q, expected %q", origSource, v4Addr)
	}
}

func TestAddIP(t *testing.T) {
	addIPTests := []struct {
		host   string
		addr   *net.IPAddr
		expect bool
	}{
		{host: "127.0.0.1", addr: &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, expect: true},
		{host: "localhost", addr: &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, expect: false},
	}

	p := NewPinger()

	for _, tt := range addIPTests {
		if ok := p.AddIP(tt.host); ok != nil {
			if tt.expect != false {
				t.Errorf("AddIP failed: got %v, expected %v", ok, tt.expect)
			}
		}
	}
	for _, tt := range addIPTests {
		if tt.expect {
			if !p.addrs[tt.host].IP.Equal(tt.addr.IP) {
				t.Errorf("AddIP didn't save IPAddr: %v", tt.host)
			}
		}
	}
}

func TestRemoveIP(t *testing.T) {
	p := NewPinger()

	if err := p.AddIP("127.0.0.1"); err != nil {
		t.Fatalf("AddIP failed: %v", err)
	}
	if len(p.addrs) != 1 {
		t.Fatalf("AddIP length check failed")
	}

	if err := p.RemoveIP("127.0"); err == nil {
		t.Fatal("RemoveIP, invalid IP should fail")
	}

	if err := p.RemoveIP("127.0.0.1"); err != nil {
		t.Fatalf("RemoveIP failed: %v", err)
	}
	if len(p.addrs) != 0 {
		t.Fatalf("RemoveIP length check failed")
	}
}

func TestRemoveIPAddr(t *testing.T) {
	p := NewPinger()

	if err := p.AddIP("127.0.0.1"); err != nil {
		t.Fatalf("AddIP failed: %v", err)
	}
	if len(p.addrs) != 1 {
		t.Fatalf("AddIP length check failed")
	}

	p.RemoveIPAddr(&net.IPAddr{IP: net.IPv4(127, 0, 0, 1)})
	if len(p.addrs) != 0 {
		t.Fatalf("RemoveIPAddr length check failed")
	}
}

func TestRun(t *testing.T) {
	for _, network := range []string{"ip", "udp"} {
		p := NewPinger()
		p.Network(network)

		if err := p.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}

		if err := p.AddIP("127.0.0.100"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}

		if err := p.AddIP("::1"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}

		found1, found100, foundv6 := false, false, false
		called, idle := false, false
		p.OnRecv = func(ip *net.IPAddr, d time.Duration) {
			called = true
			if ip.String() == "127.0.0.1" {
				found1 = true
			} else if ip.String() == "127.0.0.100" {
				found100 = true
			} else if ip.String() == "::1" {
				foundv6 = true
			}
		}

		p.OnIdle = func() {
			idle = true
		}

		err := p.Run()
		if err != nil {
			t.Fatalf("Pinger returns error: %v", err)
		}
		if !called {
			t.Fatalf("Pinger didn't get any responses")
		}
		if !idle {
			t.Fatalf("Pinger didn't call OnIdle function")
		}
		if !found1 {
			t.Fatalf("Pinger `127.0.0.1` didn't respond")
		}
		if found100 {
			t.Fatalf("Pinger `127.0.0.100` responded")
		}
		if !foundv6 {
			t.Fatalf("Pinger `::1` didn't responded")
		}
	}
}

func TestMultiRun(t *testing.T) {
	for _, network := range []string{"ip", "udp"} {
		p1 := NewPinger()
		p1.Network(network)
		p2 := NewPinger()
		p2.Network(network)

		if err := p1.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP 1 failed: %v", err)
		}

		if err := p2.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP 2 failed: %v", err)
		}

		var mu sync.Mutex
		res1 := 0
		p1.OnRecv = func(*net.IPAddr, time.Duration) {
			mu.Lock()
			res1++
			mu.Unlock()
		}

		res2 := 0
		p2.OnRecv = func(*net.IPAddr, time.Duration) {
			mu.Lock()
			res2++
			mu.Unlock()
		}

		p1.MaxRTT, p2.MaxRTT = time.Millisecond*100, time.Millisecond*100

		if err := p1.Run(); err != nil {
			t.Fatalf("Pinger 1 returns error: %v", err)
		}
		if res1 == 0 {
			t.Fatalf("Pinger 1 didn't get any responses")
		}
		if res2 > 0 {
			t.Fatalf("Pinger 2 got response")
		}

		res1, res2 = 0, 0
		if err := p2.Run(); err != nil {
			t.Fatalf("Pinger 2 returns error: %v", err)
		}
		if res1 > 0 {
			t.Fatalf("Pinger 1 got response")
		}
		if res2 == 0 {
			t.Fatalf("Pinger 2 didn't get any responses")
		}

		res1, res2 = 0, 0
		errch1, errch2 := make(chan error), make(chan error)
		go func(ch chan error) {
			err := p1.Run()
			if err != nil {
				ch <- err
			}
		}(errch1)
		go func(ch chan error) {
			err := p2.Run()
			if err != nil {
				ch <- err
			}
		}(errch2)
		ticker := time.NewTicker(time.Millisecond * 200)
		select {
		case err := <-errch1:
			t.Fatalf("Pinger 1 returns error: %v", err)
		case err := <-errch2:
			t.Fatalf("Pinger 2 returns error: %v", err)
		case <-ticker.C:
			break
		}
		mu.Lock()
		defer mu.Unlock()
		if res1 != 1 {
			t.Fatalf("Pinger 1 didn't get correct response")
		}
		if res2 != 1 {
			t.Fatalf("Pinger 2 didn't get correct response")
		}
	}
}

func TestRunLoop(t *testing.T) {
	for _, network := range []string{"ip", "udp"} {
		p := NewPinger()
		p.Network(network)

		if err := p.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}
		p.MaxRTT = time.Millisecond * 100

		recvCount, idleCount := 0, 0
		p.OnRecv = func(*net.IPAddr, time.Duration) {
			recvCount++
		}

		p.OnIdle = func() {
			idleCount++
		}

		var err error
		p.RunLoop()
		ticker := time.NewTicker(time.Millisecond * 250)
		select {
		case <-p.Done():
			if err = p.Err(); err != nil {
				t.Fatalf("Pinger returns error %v", err)
			}
		case <-ticker.C:
			break
		}
		ticker.Stop()
		p.Stop()

		if recvCount < 2 {
			t.Fatalf("Pinger receive count less than 2")
		}
		if idleCount < 2 {
			t.Fatalf("Pinger idle count less than 2")
		}
	}
}

func TestTimeToBytes(t *testing.T) {
	// 2009-11-10 23:00:00 +0000 UTC = 1257894000000000000
	expect := []byte{0x11, 0x74, 0xef, 0xed, 0xab, 0x18, 0x60, 0x00}
	tm, err := time.Parse(time.RFC3339, "2009-11-10T23:00:00Z")
	if err != nil {
		t.Errorf("time.Parse failed: %v", err)
	}
	b := timeToBytes(tm)
	for i := 0; i < 8; i++ {
		if b[i] != expect[i] {
			t.Errorf("timeToBytes failed: got %v, expected: %v", b, expect)
			break
		}
	}
}

func TestBytesToTime(t *testing.T) {
	// 2009-11-10 23:00:00 +0000 UTC = 1257894000000000000
	b := []byte{0x11, 0x74, 0xef, 0xed, 0xab, 0x18, 0x60, 0x00}
	expect, err := time.Parse(time.RFC3339, "2009-11-10T23:00:00Z")
	if err != nil {
		t.Errorf("time.Parse failed: %v", err)
	}
	tm := bytesToTime(b)
	if !tm.Equal(expect) {
		t.Errorf("bytesToTime failed: got %v, expected: %v", tm.UTC(), expect.UTC())
	}
}

func TestTimeToBytesToTime(t *testing.T) {
	tm, err := time.Parse(time.RFC3339, "2009-11-10T23:00:00Z")
	if err != nil {
		t.Errorf("time.Parse failed: %v", err)
	}
	b := timeToBytes(tm)
	tm2 := bytesToTime(b)
	if !tm.Equal(tm2) {
		t.Errorf("bytesToTime failed: got %v, expected: %v", tm2.UTC(), tm.UTC())
	}
}

func TestPayloadSizeDefault(t *testing.T) {
	s := timeToBytes(time.Now())
	d := append(s, byteSliceOfSize(8-TimeSliceLength)...)

	if len(d) != 8 {
		t.Errorf("Payload size incorrect: got %d, expected: %d", len(d), 8)
	}
}

func TestPayloadSizeCustom(t *testing.T) {
	s := timeToBytes(time.Now())
	d := append(s, byteSliceOfSize(64-TimeSliceLength)...)

	if len(d) != 64 {
		t.Errorf("Payload size incorrect: got %d, expected: %d", len(d), 64)
	}
}
<commit_msg>Add AddIPAddr function's test<commit_after>package fastping

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

func TestSource(t *testing.T) {
	for i, tt := range []struct {
		firstAddr  string
		secondAddr string
		invalid    bool
	}{
		{firstAddr: "192.0.2.10", secondAddr: "192.0.2.20", invalid: false},
		{firstAddr: "2001:0DB8::10", secondAddr: "2001:0DB8::20", invalid: false},
		{firstAddr: "192.0.2", invalid: true},
	} {
		p := NewPinger()

		origSource, err := p.Source(tt.firstAddr)
		if tt.invalid {
			if err == nil {
				t.Errorf("[%d] Source should return an error but nothing: %v", i)
			}
			continue
		}
		if err != nil {
			t.Errorf("[%d] Source address failed: %v", i, err)
		}
		if origSource != "" {
			t.Errorf("[%d] Source returned an unexpected value: got %q, expected %q", i, origSource, "")
		}

		origSource, err = p.Source(tt.secondAddr)
		if err != nil {
			t.Errorf("[%d] Source address failed: %v", i, err)
		}
		if origSource != tt.firstAddr {
			t.Errorf("[%d] Source returned an unexpected value: got %q, expected %q", i, origSource, tt.firstAddr)
		}
	}

	v4Addr := "192.0.2.10"
	v6Addr := "2001:0DB8::10"

	p := NewPinger()
	_, err := p.Source(v4Addr)
	if err != nil {
		t.Errorf("Source address failed: %v", err)
	}
	_, err = p.Source(v6Addr)
	if err != nil {
		t.Errorf("Source address failed: %v", err)
	}
	origSource, err := p.Source("")
	if err != nil {
		t.Errorf("Source address failed: %v", err)
	}
	if origSource != v4Addr {
		t.Errorf("Source returned an unexpected value: got %q, expected %q", origSource, v4Addr)
	}
}

func TestAddIP(t *testing.T) {
	addIPTests := []struct {
		host   string
		addr   *net.IPAddr
		expect bool
	}{
		{host: "127.0.0.1", addr: &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, expect: true},
		{host: "localhost", addr: &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}, expect: false},
	}

	p := NewPinger()

	for _, tt := range addIPTests {
		if ok := p.AddIP(tt.host); ok != nil {
			if tt.expect != false {
				t.Errorf("AddIP failed: got %v, expected %v", ok, tt.expect)
			}
		}
	}
	for _, tt := range addIPTests {
		if tt.expect {
			if !p.addrs[tt.host].IP.Equal(tt.addr.IP) {
				t.Errorf("AddIP didn't save IPAddr: %v", tt.host)
			}
		}
	}
}

func TestAddIPAddr(t *testing.T) {
	addIPAddrTests := []*net.IPAddr{
		{IP: net.IPv4(192, 0, 2, 10)},
		{IP: net.IP{0x20, 0x01, 0x0D, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}},
	}

	p := NewPinger()

	for i, tt := range addIPAddrTests {
		p.AddIPAddr(tt)
		if !p.addrs[tt.String()].IP.Equal(tt.IP) {
			t.Errorf("[%d] AddIPAddr didn't save IPAddr: %v", i, tt.IP)
		}
		if len(tt.IP.To4()) == net.IPv4len {
			if p.hasIPv4 != true {
				t.Errorf("[%d] AddIPAddr didn't save IPAddr type: got %v, expected %v", i, p.hasIPv4, true)
			}
		} else if len(tt.IP) == net.IPv6len {
			if p.hasIPv6 != true {
				t.Errorf("[%d] AddIPAddr didn't save IPAddr type: got %v, expected %v", i, p.hasIPv6, true)
			}
		} else {
			t.Errorf("[%d] AddIPAddr encounted an unexpected error", i)
		}
	}
}

func TestRemoveIP(t *testing.T) {
	p := NewPinger()

	if err := p.AddIP("127.0.0.1"); err != nil {
		t.Fatalf("AddIP failed: %v", err)
	}
	if len(p.addrs) != 1 {
		t.Fatalf("AddIP length check failed")
	}

	if err := p.RemoveIP("127.0"); err == nil {
		t.Fatal("RemoveIP, invalid IP should fail")
	}

	if err := p.RemoveIP("127.0.0.1"); err != nil {
		t.Fatalf("RemoveIP failed: %v", err)
	}
	if len(p.addrs) != 0 {
		t.Fatalf("RemoveIP length check failed")
	}
}

func TestRemoveIPAddr(t *testing.T) {
	p := NewPinger()

	if err := p.AddIP("127.0.0.1"); err != nil {
		t.Fatalf("AddIP failed: %v", err)
	}
	if len(p.addrs) != 1 {
		t.Fatalf("AddIP length check failed")
	}

	p.RemoveIPAddr(&net.IPAddr{IP: net.IPv4(127, 0, 0, 1)})
	if len(p.addrs) != 0 {
		t.Fatalf("RemoveIPAddr length check failed")
	}
}

func TestRun(t *testing.T) {
	for _, network := range []string{"ip", "udp"} {
		p := NewPinger()
		p.Network(network)

		if err := p.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}

		if err := p.AddIP("127.0.0.100"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}

		if err := p.AddIP("::1"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}

		found1, found100, foundv6 := false, false, false
		called, idle := false, false
		p.OnRecv = func(ip *net.IPAddr, d time.Duration) {
			called = true
			if ip.String() == "127.0.0.1" {
				found1 = true
			} else if ip.String() == "127.0.0.100" {
				found100 = true
			} else if ip.String() == "::1" {
				foundv6 = true
			}
		}

		p.OnIdle = func() {
			idle = true
		}

		err := p.Run()
		if err != nil {
			t.Fatalf("Pinger returns error: %v", err)
		}
		if !called {
			t.Fatalf("Pinger didn't get any responses")
		}
		if !idle {
			t.Fatalf("Pinger didn't call OnIdle function")
		}
		if !found1 {
			t.Fatalf("Pinger `127.0.0.1` didn't respond")
		}
		if found100 {
			t.Fatalf("Pinger `127.0.0.100` responded")
		}
		if !foundv6 {
			t.Fatalf("Pinger `::1` didn't responded")
		}
	}
}

func TestMultiRun(t *testing.T) {
	for _, network := range []string{"ip", "udp"} {
		p1 := NewPinger()
		p1.Network(network)
		p2 := NewPinger()
		p2.Network(network)

		if err := p1.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP 1 failed: %v", err)
		}

		if err := p2.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP 2 failed: %v", err)
		}

		var mu sync.Mutex
		res1 := 0
		p1.OnRecv = func(*net.IPAddr, time.Duration) {
			mu.Lock()
			res1++
			mu.Unlock()
		}

		res2 := 0
		p2.OnRecv = func(*net.IPAddr, time.Duration) {
			mu.Lock()
			res2++
			mu.Unlock()
		}

		p1.MaxRTT, p2.MaxRTT = time.Millisecond*100, time.Millisecond*100

		if err := p1.Run(); err != nil {
			t.Fatalf("Pinger 1 returns error: %v", err)
		}
		if res1 == 0 {
			t.Fatalf("Pinger 1 didn't get any responses")
		}
		if res2 > 0 {
			t.Fatalf("Pinger 2 got response")
		}

		res1, res2 = 0, 0
		if err := p2.Run(); err != nil {
			t.Fatalf("Pinger 2 returns error: %v", err)
		}
		if res1 > 0 {
			t.Fatalf("Pinger 1 got response")
		}
		if res2 == 0 {
			t.Fatalf("Pinger 2 didn't get any responses")
		}

		res1, res2 = 0, 0
		errch1, errch2 := make(chan error), make(chan error)
		go func(ch chan error) {
			err := p1.Run()
			if err != nil {
				ch <- err
			}
		}(errch1)
		go func(ch chan error) {
			err := p2.Run()
			if err != nil {
				ch <- err
			}
		}(errch2)
		ticker := time.NewTicker(time.Millisecond * 200)
		select {
		case err := <-errch1:
			t.Fatalf("Pinger 1 returns error: %v", err)
		case err := <-errch2:
			t.Fatalf("Pinger 2 returns error: %v", err)
		case <-ticker.C:
			break
		}
		mu.Lock()
		defer mu.Unlock()
		if res1 != 1 {
			t.Fatalf("Pinger 1 didn't get correct response")
		}
		if res2 != 1 {
			t.Fatalf("Pinger 2 didn't get correct response")
		}
	}
}

func TestRunLoop(t *testing.T) {
	for _, network := range []string{"ip", "udp"} {
		p := NewPinger()
		p.Network(network)

		if err := p.AddIP("127.0.0.1"); err != nil {
			t.Fatalf("AddIP failed: %v", err)
		}
		p.MaxRTT = time.Millisecond * 100

		recvCount, idleCount := 0, 0
		p.OnRecv = func(*net.IPAddr, time.Duration) {
			recvCount++
		}

		p.OnIdle = func() {
			idleCount++
		}

		var err error
		p.RunLoop()
		ticker := time.NewTicker(time.Millisecond * 250)
		select {
		case <-p.Done():
			if err = p.Err(); err != nil {
				t.Fatalf("Pinger returns error %v", err)
			}
		case <-ticker.C:
			break
		}
		ticker.Stop()
		p.Stop()

		if recvCount < 2 {
			t.Fatalf("Pinger receive count less than 2")
		}
		if idleCount < 2 {
			t.Fatalf("Pinger idle count less than 2")
		}
	}
}

func TestTimeToBytes(t *testing.T) {
	// 2009-11-10 23:00:00 +0000 UTC = 1257894000000000000
	expect := []byte{0x11, 0x74, 0xef, 0xed, 0xab, 0x18, 0x60, 0x00}
	tm, err := time.Parse(time.RFC3339, "2009-11-10T23:00:00Z")
	if err != nil {
		t.Errorf("time.Parse failed: %v", err)
	}
	b := timeToBytes(tm)
	for i := 0; i < 8; i++ {
		if b[i] != expect[i] {
			t.Errorf("timeToBytes failed: got %v, expected: %v", b, expect)
			break
		}
	}
}

func TestBytesToTime(t *testing.T) {
	// 2009-11-10 23:00:00 +0000 UTC = 1257894000000000000
	b := []byte{0x11, 0x74, 0xef, 0xed, 0xab, 0x18, 0x60, 0x00}
	expect, err := time.Parse(time.RFC3339, "2009-11-10T23:00:00Z")
	if err != nil {
		t.Errorf("time.Parse failed: %v", err)
	}
	tm := bytesToTime(b)
	if !tm.Equal(expect) {
		t.Errorf("bytesToTime failed: got %v, expected: %v", tm.UTC(), expect.UTC())
	}
}

func TestTimeToBytesToTime(t *testing.T) {
	tm, err := time.Parse(time.RFC3339, "2009-11-10T23:00:00Z")
	if err != nil {
		t.Errorf("time.Parse failed: %v", err)
	}
	b := timeToBytes(tm)
	tm2 := bytesToTime(b)
	if !tm.Equal(tm2) {
		t.Errorf("bytesToTime failed: got %v, expected: %v", tm2.UTC(), tm.UTC())
	}
}

func TestPayloadSizeDefault(t *testing.T) {
	s := timeToBytes(time.Now())
	d := append(s, byteSliceOfSize(8-TimeSliceLength)...)

	if len(d) != 8 {
		t.Errorf("Payload size incorrect: got %d, expected: %d", len(d), 8)
	}
}

func TestPayloadSizeCustom(t *testing.T) {
	s := timeToBytes(time.Now())
	d := append(s, byteSliceOfSize(64-TimeSliceLength)...)

	if len(d) != 64 {
		t.Errorf("Payload size incorrect: got %d, expected: %d", len(d), 64)
	}
}
<|endoftext|>


<commit_before>// 事前に以下の事をしておくこと。
// go getコマンドでパッケージを取得しておくこと。
// go get code.google.com/p/goauth2/oauth
// go get code.google.com/p/google-api-go-client/calendar/v3
//
// Cloud ConsoleのCredentialsでClient IDを作成しておく。
// Cloud ConsoleのAPIsでAPIをONにしておくこと。
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/calendar/v3"
)

var (
	cachefile = "cache.json"

	scope = "https://www.googleapis.com/auth/calendar"
	// request_urlは使用するAPIのURLを指定して下さい。（この例ではCalendarList）
	request_url       = "https://www.googleapis.com/calendar/v3/users/me/calendarList"
	request_token_url = "https://accounts.google.com/o/oauth2/auth"
	auth_token_url    = "https://accounts.google.com/o/oauth2/token"

	// clientID、secret、はDevelopers ConsoleのCredentialsからコピー＆ペーストして下さい。
	clientId     = ""
	clientSecret = ""
	//
	redirectURL = "http://localhost"
)

func main() {
	runtime.GOMAXPROCS(2)

	var err error
	var port int

	flag.IntVar(&port, "p", 16061, "local port")

	flag.Parse()

	if port <= 1024 {
		fmt.Fprintf(os.Stderr, "Usage: \n")
		flag.PrintDefaults()
		os.Exit(2)
	}

	fmt.Println("Start Execute API")

	// 認証コードを引数で受け取る。
	code := flag.Arg(0)

	//
	checkClientIDandSecret()

	config := &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		RedirectURL:  fmt.Sprintf("%s:%d", redirectURL, port),
		Scope:        scope,
		AuthURL:      request_token_url,
		TokenURL:     auth_token_url,
		TokenCache:   oauth.CacheFile(cachefile),
	}

	transport := &oauth.Transport{Config: config}

	// キャッシュからトークンファイルを取得
	_, err = config.TokenCache.Token()
	if err != nil {
		// キャッシュなし
		if code == "" {
			code, err = getAuthCode(config, port)

			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				os.Exit(1)
			}
		}

		// 認証トークンを取得する。（取得後、キャッシュへ）
		_, err = transport.Exchange(code)
		if err != nil {
			fmt.Printf("Exchange Error: %v\n", err)
			os.Exit(1)
		}
	}

	//
	var svc *calendar.Service
	var cl *calendar.CalendarList

	svc, err = calendar.New(transport.Client())

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	cl, err = svc.CalendarList.List().Do()

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	fmt.Printf("--- Your calendars ---\n")

	for _, item := range cl.Items {
		fmt.Printf("%v, %v\n", item.Summary, item.Description)
	}
}

func getAuthCode(config *oauth.Config, port int) (string, error) {
	url := config.AuthCodeURL("")

	var cmd *exec.Cmd

	switch runtime.GOOS {
	case "windows":
		url = strings.Replace(url, "&", `^&`, -1)
		cmd = exec.Command("cmd", "/c", "start", url)

	case "darwin":
		url = strings.Replace(url, "&", `\&`, -1)
		cmd = exec.Command("open", url)

	default:
		return "", fmt.Errorf("ブラウザで以下のURLにアクセスし、認証して下さい。\n%s\n", url)
	}

	redirectResult := make(chan RedirectResult, 1)
	serverStarted := make(chan bool, 1)
	//
	go func(rr chan<- RedirectResult, ss chan<- bool, p int) {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			code := r.URL.Query().Get("code")

			if code == "" {
				rr <- RedirectResult{Err: fmt.Errorf("codeを取得できませんでした。")}
			}

			fmt.Fprintf(w, `<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body onload="window.open('about:blank','_self').close();">
ブラウザが自動で閉じない場合は手動で閉じてください。
</body>
</html>
`)
			rr <- RedirectResult{Code: code}
		})

		host := fmt.Sprintf("localhost:%d", p)

		fmt.Printf("Start Listen: %s\n", host)
		ss <- true

		err := http.ListenAndServe(host, nil)

		if err != nil {
			rr <- RedirectResult{Err: err}
		}
	}(redirectResult, serverStarted, port)

	<-serverStarted

	// set redirect timeout 20sec
	tch := time.After(20 * time.Second)

	fmt.Println("Start your browser after 2sec.")

	time.Sleep(2 * time.Second)

	if err := cmd.Start(); err != nil {
		return "", fmt.Errorf("Browser Start Error: %v\n", err)
	}

	var rr RedirectResult

	select {
	case rr = <-redirectResult:
	case <-tch:
		return "", fmt.Errorf("Timeout: waiting redirect.")
	}

	if rr.Err != nil {
		return "", fmt.Errorf("Redirect Error: %v\n", rr.Err)
	}

	fmt.Printf("Got code.\n")

	return rr.Code, nil
}

type RedirectResult struct {
	Code string
	Err  error
}

func checkClientIDandSecret() {
	if _, err := os.Stat(cachefile); err == nil {
		return
	}

	if clientId == "" {
		fmt.Println("Input ClientID")
		fmt.Scanf("%s\n", &clientId)
	}

	if clientSecret == "" {
		fmt.Println("Input ClientSecret")
		fmt.Scanf("%s\n", &clientSecret)
	}
}
<commit_msg>change timeout to 30sec.<commit_after>// 事前に以下の事をしておくこと。
// go getコマンドでパッケージを取得しておくこと。
// go get code.google.com/p/goauth2/oauth
// go get code.google.com/p/google-api-go-client/calendar/v3
//
// Cloud ConsoleのCredentialsでClient IDを作成しておく。
// Cloud ConsoleのAPIsでAPIをONにしておくこと。
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/google-api-go-client/calendar/v3"
)

var (
	cachefile = "cache.json"

	scope = "https://www.googleapis.com/auth/calendar"
	// request_urlは使用するAPIのURLを指定して下さい。（この例ではCalendarList）
	request_url       = "https://www.googleapis.com/calendar/v3/users/me/calendarList"
	request_token_url = "https://accounts.google.com/o/oauth2/auth"
	auth_token_url    = "https://accounts.google.com/o/oauth2/token"

	// clientID、secret、はDevelopers ConsoleのCredentialsからコピー＆ペーストして下さい。
	clientId     = ""
	clientSecret = ""
	//
	redirectURL = "http://localhost"
)

func main() {
	runtime.GOMAXPROCS(2)

	var err error
	var port int

	flag.IntVar(&port, "p", 16061, "local port")

	flag.Parse()

	if port <= 1024 {
		fmt.Fprintf(os.Stderr, "Usage: \n")
		flag.PrintDefaults()
		os.Exit(2)
	}

	fmt.Println("Start Execute API")

	// 認証コードを引数で受け取る。
	code := flag.Arg(0)

	//
	checkClientIDandSecret()

	config := &oauth.Config{
		ClientId:     clientId,
		ClientSecret: clientSecret,
		RedirectURL:  fmt.Sprintf("%s:%d", redirectURL, port),
		Scope:        scope,
		AuthURL:      request_token_url,
		TokenURL:     auth_token_url,
		TokenCache:   oauth.CacheFile(cachefile),
	}

	transport := &oauth.Transport{Config: config}

	// キャッシュからトークンファイルを取得
	_, err = config.TokenCache.Token()
	if err != nil {
		// キャッシュなし
		if code == "" {
			code, err = getAuthCode(config, port)

			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				os.Exit(1)
			}
		}

		// 認証トークンを取得する。（取得後、キャッシュへ）
		_, err = transport.Exchange(code)
		if err != nil {
			fmt.Printf("Exchange Error: %v\n", err)
			os.Exit(1)
		}
	}

	//
	var svc *calendar.Service
	var cl *calendar.CalendarList

	svc, err = calendar.New(transport.Client())

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	cl, err = svc.CalendarList.List().Do()

	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	fmt.Printf("--- Your calendars ---\n")

	for _, item := range cl.Items {
		fmt.Printf("%v, %v\n", item.Summary, item.Description)
	}
}

func getAuthCode(config *oauth.Config, port int) (string, error) {
	url := config.AuthCodeURL("")

	var cmd *exec.Cmd

	switch runtime.GOOS {
	case "windows":
		url = strings.Replace(url, "&", `^&`, -1)
		cmd = exec.Command("cmd", "/c", "start", url)

	case "darwin":
		url = strings.Replace(url, "&", `\&`, -1)
		cmd = exec.Command("open", url)

	default:
		return "", fmt.Errorf("ブラウザで以下のURLにアクセスし、認証して下さい。\n%s\n", url)
	}

	redirectResult := make(chan RedirectResult, 1)
	serverStarted := make(chan bool, 1)
	//
	go func(rr chan<- RedirectResult, ss chan<- bool, p int) {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			code := r.URL.Query().Get("code")

			if code == "" {
				rr <- RedirectResult{Err: fmt.Errorf("codeを取得できませんでした。")}
			}

			fmt.Fprintf(w, `<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body onload="window.open('about:blank','_self').close();">
ブラウザが自動で閉じない場合は手動で閉じてください。
</body>
</html>
`)
			rr <- RedirectResult{Code: code}
		})

		host := fmt.Sprintf("localhost:%d", p)

		fmt.Printf("Start Listen: %s\n", host)
		ss <- true

		err := http.ListenAndServe(host, nil)

		if err != nil {
			rr <- RedirectResult{Err: err}
		}
	}(redirectResult, serverStarted, port)

	<-serverStarted

	// set redirect timeout 30sec
	tch := time.After(30 * time.Second)

	fmt.Println("Start your browser after 2sec.")

	time.Sleep(2 * time.Second)

	if err := cmd.Start(); err != nil {
		return "", fmt.Errorf("Browser Start Error: %v\n", err)
	}

	var rr RedirectResult

	select {
	case rr = <-redirectResult:
	case <-tch:
		return "", fmt.Errorf("Timeout: waiting redirect.")
	}

	if rr.Err != nil {
		return "", fmt.Errorf("Redirect Error: %v\n", rr.Err)
	}

	fmt.Printf("Got code.\n")

	return rr.Code, nil
}

type RedirectResult struct {
	Code string
	Err  error
}

func checkClientIDandSecret() {
	if _, err := os.Stat(cachefile); err == nil {
		return
	}

	if clientId == "" {
		fmt.Println("Input ClientID")
		fmt.Scanf("%s\n", &clientId)
	}

	if clientSecret == "" {
		fmt.Println("Input ClientSecret")
		fmt.Scanf("%s\n", &clientSecret)
	}
}
<|endoftext|>


<commit_before>// +build linux darwin
// run

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

// Test that if a slice access causes a fault, a deferred func
// sees the most recent value of the variables it accesses.
// This is true today; the role of the test is to ensure it stays true.
//
// In the test, memcopy is the function that will fault, during dst[i] = src[i].
// The deferred func recovers from the error and returns, making memcopy
// return the current value of n. If n is not being flushed to memory
// after each modification, the result will be a stale value of n.
//
// The test is set up by mmapping a 64 kB block of memory and then
// unmapping a 16 kB hole in the middle of it. Running memcopy
// on the resulting slice will fault when it reaches the hole.

package main

import (
	"log"
	"runtime/debug"
	"syscall"
	"unsafe"
)

func memcopy(dst, src []byte) (n int, err error) {
	defer func() {
		err = recover().(error)
	}()

	for i := 0; i < len(dst) && i < len(src); i++ {
		dst[i] = src[i]
		n++
	}
	return
}

func main() {
	// Turn the eventual fault into a panic, not a program crash,
	// so that memcopy can recover.
	debug.SetPanicOnFault(true)

	// Map 64 kB block of data with 16 kB hole in middle.
	data, err := syscall.Mmap(-1, 0, 64*1024, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
	if err != nil {
		log.Fatalf("mmap: %v", err)
	}

	// Note: Cannot call syscall.Munmap, because Munmap checks
	// that you are unmapping a whole region returned by Mmap.
	// We are trying to unmap just a hole in the middle.
	if _, _, err := syscall.Syscall(syscall.SYS_MUNMAP, uintptr(unsafe.Pointer(&data[32*1024])), 16*1024, 0); err != 0 {
		log.Fatalf("munmap: %v", err)
	}

	other := make([]byte, 64*1024)

	// Check that memcopy returns the actual amount copied
	// before the fault (32kB - 5, the offset we skip in the argument).
	n, err := memcopy(data[5:], other)
	if err == nil {
		log.Fatal("no error from memcopy across memory hole")
	}
	if n != 32*1024-5 {
		log.Fatal("memcopy returned %d, want %d", n, 32*1024-5)
	}
}
<commit_msg>test: fix recover4 test on 64kb systems<commit_after>// +build linux darwin
// run

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

// Test that if a slice access causes a fault, a deferred func
// sees the most recent value of the variables it accesses.
// This is true today; the role of the test is to ensure it stays true.
//
// In the test, memcopy is the function that will fault, during dst[i] = src[i].
// The deferred func recovers from the error and returns, making memcopy
// return the current value of n. If n is not being flushed to memory
// after each modification, the result will be a stale value of n.
//
// The test is set up by mmapping a 64 kB block of memory and then
// unmapping a 16 kB hole in the middle of it. Running memcopy
// on the resulting slice will fault when it reaches the hole.

package main

import (
	"log"
	"runtime/debug"
	"syscall"
	"unsafe"
)

func memcopy(dst, src []byte) (n int, err error) {
	defer func() {
		err = recover().(error)
	}()

	for i := 0; i < len(dst) && i < len(src); i++ {
		dst[i] = src[i]
		n++
	}
	return
}

func main() {
	// Turn the eventual fault into a panic, not a program crash,
	// so that memcopy can recover.
	debug.SetPanicOnFault(true)

	size := syscall.Getpagesize()

	// Map 16 pages of data with a 4-page hole in the middle.
	data, err := syscall.Mmap(-1, 0, 16*size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
	if err != nil {
		log.Fatalf("mmap: %v", err)
	}

	// Note: Cannot call syscall.Munmap, because Munmap checks
	// that you are unmapping a whole region returned by Mmap.
	// We are trying to unmap just a hole in the middle.
	if _, _, err := syscall.Syscall(syscall.SYS_MUNMAP, uintptr(unsafe.Pointer(&data[8*size])), uintptr(4*size), 0); err != 0 {
		log.Fatalf("munmap: %v", err)
	}

	other := make([]byte, 16*size)

	// Check that memcopy returns the actual amount copied
	// before the fault (8*size - 5, the offset we skip in the argument).
	n, err := memcopy(data[5:], other)
	if err == nil {
		log.Fatal("no error from memcopy across memory hole")
	}
	if n != 8*size-5 {
		log.Fatal("memcopy returned %d, want %d", n, 8*size-5)
	}
}
<|endoftext|>


<commit_before>package material

import (
	"encoding/json"
	"errors"
	"fmt"

	"github.com/silenceper/wechat/v2/officialaccount/context"
	"github.com/silenceper/wechat/v2/util"
)

const (
	addNewsURL          = "https://api.weixin.qq.com/cgi-bin/material/add_news"
	addMaterialURL      = "https://api.weixin.qq.com/cgi-bin/material/add_material"
	delMaterialURL      = "https://api.weixin.qq.com/cgi-bin/material/del_material"
	getMaterialURL      = "https://api.weixin.qq.com/cgi-bin/material/get_material"
	getMaterialCountURL = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=%s"
	batchGetMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"
)

//PermanentMaterialType 永久素材类型
type PermanentMaterialType string

const (
	//PermanentMaterialTypeImage 永久素材图片类型（image）
	PermanentMaterialTypeImage PermanentMaterialType = "image"
	//PermanentMaterialTypeVideo 永久素材视频类型（video）
	PermanentMaterialTypeVideo PermanentMaterialType = "video"
	//PermanentMaterialTypeVoice 永久素材语音类型 （voice）
	PermanentMaterialTypeVoice PermanentMaterialType = "voice"
	//PermanentMaterialTypeNews 永久素材图文类型（news）
	PermanentMaterialTypeNews PermanentMaterialType = "news"
)

//Material 素材管理
type Material struct {
	*context.Context
}

//NewMaterial init
func NewMaterial(context *context.Context) *Material {
	material := new(Material)
	material.Context = context
	return material
}

//Article 永久图文素材
type Article struct {
	Title            string `json:"title"`
	ThumbMediaID     string `json:"thumb_media_id"`
	ThumbURL         string `json:"thumb_url"`
	Author           string `json:"author"`
	Digest           string `json:"digest"`
	ShowCoverPic     int    `json:"show_cover_pic"`
	Content          string `json:"content"`
	ContentSourceURL string `json:"content_source_url"`
	URL              string `json:"url"`
	DownURL          string `json:"down_url"`
}

// GetNews 获取/下载永久素材
func (material *Material) GetNews(id string) ([]*Article, error) {
	accessToken, err := material.GetAccessToken()
	if err != nil {
		return nil, err
	}
	uri := fmt.Sprintf("%s?access_token=%s", getMaterialURL, accessToken)

	var req struct {
		MediaID string `json:"media_id"`
	}
	req.MediaID = id
	responseBytes, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, err
	}

	var res struct {
		NewsItem []*Article `json:"news_item"`
	}
	err = json.Unmarshal(responseBytes, &res)
	if err != nil {
		return nil, err
	}

	return res.NewsItem, nil
}

//reqArticles 永久性图文素材请求信息
type reqArticles struct {
	Articles []*Article `json:"articles"`
}

//resArticles 永久性图文素材返回结果
type resArticles struct {
	util.CommonError

	MediaID string `json:"media_id"`
}

//AddNews 新增永久图文素材
func (material *Material) AddNews(articles []*Article) (mediaID string, err error) {
	req := &reqArticles{articles}

	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s", addNewsURL, accessToken)
	responseBytes, err := util.PostJSON(uri, req)
	if err != nil {
		return
	}
	var res resArticles
	err = json.Unmarshal(responseBytes, &res)
	if err != nil {
		return
	}
	mediaID = res.MediaID
	return
}

//resAddMaterial 永久性素材上传返回的结果
type resAddMaterial struct {
	util.CommonError

	MediaID string `json:"media_id"`
	URL     string `json:"url"`
}

//AddMaterial 上传永久性素材（处理视频需要单独上传）
func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) {
	if mediaType == MediaTypeVideo {
		err = errors.New("永久视频素材上传使用 AddVideo 方法")
		return
	}
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s&type=%s", addMaterialURL, accessToken, mediaType)
	var response []byte
	response, err = util.PostFile("media", filename, uri)
	if err != nil {
		return
	}
	var resMaterial resAddMaterial
	err = json.Unmarshal(response, &resMaterial)
	if err != nil {
		return
	}
	if resMaterial.ErrCode != 0 {
		err = fmt.Errorf("AddMaterial error : errcode=%v , errmsg=%v", resMaterial.ErrCode, resMaterial.ErrMsg)
		return
	}
	mediaID = resMaterial.MediaID
	url = resMaterial.URL
	return
}

type reqVideo struct {
	Title        string `json:"title"`
	Introduction string `json:"introduction"`
}

//AddVideo 永久视频素材文件上传
func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) {
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s&type=video", addMaterialURL, accessToken)

	videoDesc := &reqVideo{
		Title:        title,
		Introduction: introduction,
	}
	var fieldValue []byte
	fieldValue, err = json.Marshal(videoDesc)
	if err != nil {
		return
	}

	fields := []util.MultipartFormField{
		{
			IsFile:    true,
			Fieldname: "media",
			Filename:  filename,
		},
		{
			IsFile:    false,
			Fieldname: "description",
			Value:     fieldValue,
		},
	}

	var response []byte
	response, err = util.PostMultipartForm(fields, uri)
	if err != nil {
		return
	}

	var resMaterial resAddMaterial
	err = json.Unmarshal(response, &resMaterial)
	if err != nil {
		return
	}
	if resMaterial.ErrCode != 0 {
		err = fmt.Errorf("AddMaterial error : errcode=%v , errmsg=%v", resMaterial.ErrCode, resMaterial.ErrMsg)
		return
	}
	mediaID = resMaterial.MediaID
	url = resMaterial.URL
	return
}

type reqDeleteMaterial struct {
	MediaID string `json:"media_id"`
}

//DeleteMaterial 删除永久素材
func (material *Material) DeleteMaterial(mediaID string) error {
	accessToken, err := material.GetAccessToken()
	if err != nil {
		return err
	}

	uri := fmt.Sprintf("%s?access_token=%s", delMaterialURL, accessToken)
	response, err := util.PostJSON(uri, reqDeleteMaterial{mediaID})
	if err != nil {
		return err
	}

	return util.DecodeWithCommonError(response, "DeleteMaterial")
}

//ArticleList 永久素材列表
type ArticleList struct {
	TotalCount int64             `json:"total_count"`
	ItemCount  int64             `json:"item_count"`
	Item       []ArticleListItem `json:"item"`
}

//ArticleListItem 用于ArticleList的item节点
type ArticleListItem struct {
	MediaID    string             `json:"media_id"`
	Content    ArticleListContent `json:"content"`
	Name       string             `json:"name"`
	URL        string             `json:"url"`
	UpdateTime int64              `json:"update_time"`
}

//ArticleListContent 用于ArticleListItem的content节点
type ArticleListContent struct {
	NewsItem   []Article `json:"news_item"`
	UpdateTime int64     `json:"update_time"`
	CreateTime int64     `json:"create_time"`
}

//reqBatchGetMaterial BatchGetMaterial请求参数
type reqBatchGetMaterial struct {
	Type   PermanentMaterialType `json:"type"`
	Count  int64                 `json:"count"`
	Offset int64                 `json:"offset"`
}

// BatchGetMaterial 批量获取永久素材
//reference:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
func (material *Material) BatchGetMaterial(permanentMaterialType PermanentMaterialType, offset, count int64) (list ArticleList, err error) {
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}
	uri := fmt.Sprintf("%s?access_token=%s", batchGetMaterialURL, accessToken)

	req := reqBatchGetMaterial{
		Type:   permanentMaterialType,
		Offset: offset,
		Count:  count,
	}

	var response []byte
	response, err = util.PostJSON(uri, req)
	if err != nil {
		return
	}

	err = util.DecodeWithError(response, &list, "BatchGetMaterial")
	return
}

// ResMaterialCount 素材总数
type ResMaterialCount struct {
	util.CommonError
	VoiceCount int64 `json:"voice_count"` // 语音总数量
	VideoCount int64 `json:"video_count"` // 视频总数量
	ImageCount int64 `json:"image_count"` // 图片总数量
	NewsCount  int64 `json:"news_count"`  // 图文总数量
}

// GetMaterialCount 获取素材总数.
func (material *Material) GetMaterialCount() (res ResMaterialCount, err error) {
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}
	uri := fmt.Sprintf("%s?access_token=%s", getMaterialCountURL, accessToken)
	var response []byte
	response, err = util.HTTPGet(uri)
	if err != nil {
		return
	}
	err = util.DecodeWithError(response, &res, "GetMaterialCount")
	return
}
<commit_msg>feat:add material news update (#295)<commit_after>package material

import (
	"encoding/json"
	"errors"
	"fmt"

	"github.com/silenceper/wechat/v2/officialaccount/context"
	"github.com/silenceper/wechat/v2/util"
)

const (
	addNewsURL          = "https://api.weixin.qq.com/cgi-bin/material/add_news"
	updateNewsURL       = "https://api.weixin.qq.com/cgi-bin/material/update_news?access_token=%s"
	addMaterialURL      = "https://api.weixin.qq.com/cgi-bin/material/add_material"
	delMaterialURL      = "https://api.weixin.qq.com/cgi-bin/material/del_material"
	getMaterialURL      = "https://api.weixin.qq.com/cgi-bin/material/get_material"
	getMaterialCountURL = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=%s"
	batchGetMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"
)

//PermanentMaterialType 永久素材类型
type PermanentMaterialType string

const (
	//PermanentMaterialTypeImage 永久素材图片类型（image）
	PermanentMaterialTypeImage PermanentMaterialType = "image"
	//PermanentMaterialTypeVideo 永久素材视频类型（video）
	PermanentMaterialTypeVideo PermanentMaterialType = "video"
	//PermanentMaterialTypeVoice 永久素材语音类型 （voice）
	PermanentMaterialTypeVoice PermanentMaterialType = "voice"
	//PermanentMaterialTypeNews 永久素材图文类型（news）
	PermanentMaterialTypeNews PermanentMaterialType = "news"
)

//Material 素材管理
type Material struct {
	*context.Context
}

//NewMaterial init
func NewMaterial(context *context.Context) *Material {
	material := new(Material)
	material.Context = context
	return material
}

//Article 永久图文素材
type Article struct {
	Title            string `json:"title"`
	ThumbMediaID     string `json:"thumb_media_id"`
	ThumbURL         string `json:"thumb_url"`
	Author           string `json:"author"`
	Digest           string `json:"digest"`
	ShowCoverPic     int    `json:"show_cover_pic"`
	Content          string `json:"content"`
	ContentSourceURL string `json:"content_source_url"`
	URL              string `json:"url"`
	DownURL          string `json:"down_url"`
}

// GetNews 获取/下载永久素材
func (material *Material) GetNews(id string) ([]*Article, error) {
	accessToken, err := material.GetAccessToken()
	if err != nil {
		return nil, err
	}
	uri := fmt.Sprintf("%s?access_token=%s", getMaterialURL, accessToken)

	var req struct {
		MediaID string `json:"media_id"`
	}
	req.MediaID = id
	responseBytes, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, err
	}

	var res struct {
		NewsItem []*Article `json:"news_item"`
	}
	err = json.Unmarshal(responseBytes, &res)
	if err != nil {
		return nil, err
	}

	return res.NewsItem, nil
}

//reqArticles 永久性图文素材请求信息
type reqArticles struct {
	Articles []*Article `json:"articles"`
}

//resArticles 永久性图文素材返回结果
type resArticles struct {
	util.CommonError

	MediaID string `json:"media_id"`
}

//AddNews 新增永久图文素材
func (material *Material) AddNews(articles []*Article) (mediaID string, err error) {
	req := &reqArticles{articles}

	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s", addNewsURL, accessToken)
	responseBytes, err := util.PostJSON(uri, req)
	if err != nil {
		return
	}
	var res resArticles
	err = json.Unmarshal(responseBytes, &res)
	if err != nil {
		return
	}
	mediaID = res.MediaID
	return
}

//reqUpdateArticle 更新永久性图文素材请求信息
type reqUpdateArticle struct {
	MediaID  string   `json:"media_id"`
	Index    int64    `json:"index"`
	Articles *Article `json:"articles"`
}

// UpdateNews 更新永久图文素材
func (material *Material) UpdateNews(article *Article, mediaID string, index int64) (err error) {
	req := &reqUpdateArticle{mediaID, index, article}

	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s", updateNewsURL, accessToken)
	var response []byte
	response, err = util.PostJSON(uri, req)
	if err != nil {
		return
	}
	return util.DecodeWithCommonError(response, "UpdateNews")
}

//resAddMaterial 永久性素材上传返回的结果
type resAddMaterial struct {
	util.CommonError

	MediaID string `json:"media_id"`
	URL     string `json:"url"`
}

//AddMaterial 上传永久性素材（处理视频需要单独上传）
func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) {
	if mediaType == MediaTypeVideo {
		err = errors.New("永久视频素材上传使用 AddVideo 方法")
		return
	}
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s&type=%s", addMaterialURL, accessToken, mediaType)
	var response []byte
	response, err = util.PostFile("media", filename, uri)
	if err != nil {
		return
	}
	var resMaterial resAddMaterial
	err = json.Unmarshal(response, &resMaterial)
	if err != nil {
		return
	}
	if resMaterial.ErrCode != 0 {
		err = fmt.Errorf("AddMaterial error : errcode=%v , errmsg=%v", resMaterial.ErrCode, resMaterial.ErrMsg)
		return
	}
	mediaID = resMaterial.MediaID
	url = resMaterial.URL
	return
}

type reqVideo struct {
	Title        string `json:"title"`
	Introduction string `json:"introduction"`
}

//AddVideo 永久视频素材文件上传
func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) {
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf("%s?access_token=%s&type=video", addMaterialURL, accessToken)

	videoDesc := &reqVideo{
		Title:        title,
		Introduction: introduction,
	}
	var fieldValue []byte
	fieldValue, err = json.Marshal(videoDesc)
	if err != nil {
		return
	}

	fields := []util.MultipartFormField{
		{
			IsFile:    true,
			Fieldname: "media",
			Filename:  filename,
		},
		{
			IsFile:    false,
			Fieldname: "description",
			Value:     fieldValue,
		},
	}

	var response []byte
	response, err = util.PostMultipartForm(fields, uri)
	if err != nil {
		return
	}

	var resMaterial resAddMaterial
	err = json.Unmarshal(response, &resMaterial)
	if err != nil {
		return
	}
	if resMaterial.ErrCode != 0 {
		err = fmt.Errorf("AddMaterial error : errcode=%v , errmsg=%v", resMaterial.ErrCode, resMaterial.ErrMsg)
		return
	}
	mediaID = resMaterial.MediaID
	url = resMaterial.URL
	return
}

type reqDeleteMaterial struct {
	MediaID string `json:"media_id"`
}

//DeleteMaterial 删除永久素材
func (material *Material) DeleteMaterial(mediaID string) error {
	accessToken, err := material.GetAccessToken()
	if err != nil {
		return err
	}

	uri := fmt.Sprintf("%s?access_token=%s", delMaterialURL, accessToken)
	response, err := util.PostJSON(uri, reqDeleteMaterial{mediaID})
	if err != nil {
		return err
	}

	return util.DecodeWithCommonError(response, "DeleteMaterial")
}

//ArticleList 永久素材列表
type ArticleList struct {
	TotalCount int64             `json:"total_count"`
	ItemCount  int64             `json:"item_count"`
	Item       []ArticleListItem `json:"item"`
}

//ArticleListItem 用于ArticleList的item节点
type ArticleListItem struct {
	MediaID    string             `json:"media_id"`
	Content    ArticleListContent `json:"content"`
	Name       string             `json:"name"`
	URL        string             `json:"url"`
	UpdateTime int64              `json:"update_time"`
}

//ArticleListContent 用于ArticleListItem的content节点
type ArticleListContent struct {
	NewsItem   []Article `json:"news_item"`
	UpdateTime int64     `json:"update_time"`
	CreateTime int64     `json:"create_time"`
}

//reqBatchGetMaterial BatchGetMaterial请求参数
type reqBatchGetMaterial struct {
	Type   PermanentMaterialType `json:"type"`
	Count  int64                 `json:"count"`
	Offset int64                 `json:"offset"`
}

// BatchGetMaterial 批量获取永久素材
//reference:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
func (material *Material) BatchGetMaterial(permanentMaterialType PermanentMaterialType, offset, count int64) (list ArticleList, err error) {
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}
	uri := fmt.Sprintf("%s?access_token=%s", batchGetMaterialURL, accessToken)

	req := reqBatchGetMaterial{
		Type:   permanentMaterialType,
		Offset: offset,
		Count:  count,
	}

	var response []byte
	response, err = util.PostJSON(uri, req)
	if err != nil {
		return
	}

	err = util.DecodeWithError(response, &list, "BatchGetMaterial")
	return
}

// ResMaterialCount 素材总数
type ResMaterialCount struct {
	util.CommonError
	VoiceCount int64 `json:"voice_count"` // 语音总数量
	VideoCount int64 `json:"video_count"` // 视频总数量
	ImageCount int64 `json:"image_count"` // 图片总数量
	NewsCount  int64 `json:"news_count"`  // 图文总数量
}

// GetMaterialCount 获取素材总数.
func (material *Material) GetMaterialCount() (res ResMaterialCount, err error) {
	var accessToken string
	accessToken, err = material.GetAccessToken()
	if err != nil {
		return
	}
	uri := fmt.Sprintf("%s?access_token=%s", getMaterialCountURL, accessToken)
	var response []byte
	response, err = util.HTTPGet(uri)
	if err != nil {
		return
	}
	err = util.DecodeWithError(response, &res, "GetMaterialCount")
	return
}
<|endoftext|>


<commit_before>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

package helper

import (
	"fmt"

	"github.com/spf13/viper"
	"golang.org/x/net/context"

	"github.com/openblockchain/obc-peer/openchain/chaincode"
	"github.com/openblockchain/obc-peer/openchain/consensus"
	"github.com/openblockchain/obc-peer/openchain/ledger"
	"github.com/openblockchain/obc-peer/openchain/peer"
	pb "github.com/openblockchain/obc-peer/protos"
)

// =============================================================================
// Structure definitions go here
// =============================================================================

// Helper contains the reference to coordinator for broadcasts/unicasts.
type Helper struct {
	coordinator peer.MessageHandlerCoordinator
}

// =============================================================================
// Constructors go here
// =============================================================================

// NewHelper constructs the consensus helper object.
func NewHelper(mhc peer.MessageHandlerCoordinator) consensus.CPI {
	return &Helper{coordinator: mhc}
}

// =============================================================================
// Stack-facing implementation goes here
// =============================================================================

// GetReplicaHash returns the crypto IDs of the current replica and the whole network
// TODO func (h *Helper) GetReplicaHash() (self []byte, network [][]byte, err error) {
// ATTN: Until the crypto package is integrated, this functions returns
// the <IP:port>s of the current replica and the whole network instead
func (h *Helper) GetReplicaHash() (self string, network []string, err error) {
	// v, _ := h.coordinator.GetValidator()
	// self = v.GetID()
	peer, err := peer.GetPeerEndpoint()
	if err != nil {
		return "", nil, err
	}
	self = peer.Address

	config := viper.New()
	config.SetConfigName("openchain")
	config.AddConfigPath("./")
	err = config.ReadInConfig()
	if err != nil {
		err = fmt.Errorf("Fatal error reading root config: %s", err)
		return self, nil, err
	}

	// encodedHashes := config.GetStringSlice("peer.validator.replicas.hashes")
	/* network = make([][]byte, len(encodedHashes))
	for i, v := range encodedHashes {
		network[i], _ = base64.StdEncoding.DecodeString(v)
	} */
	network = config.GetStringSlice("peer.validator.replicas.ips")

	return self, network, nil
}

// GetReplicaID returns the uint handle corresponding to a replica address
// TODO func (h *Helper) GetReplicaID(hash []byte) (id uint64, err error) {
func (h *Helper) GetReplicaID(addr string) (id uint64, err error) {
	_, network, err := h.GetReplicaHash()
	if err != nil {
		return uint64(0), err
	}
	for i, v := range network {
		// if bytes.Equal(v, hash) {
		if v == addr {
			return uint64(i), nil
		}
	}

	//err = fmt.Errorf("Couldn't find crypto ID in list of VP IDs given in config")
	err = fmt.Errorf("Couldn't find IP:port in list of VP addresses given in config")
	return uint64(0), err
}

// Broadcast sends a message to all validating peers.
func (h *Helper) Broadcast(msg *pb.OpenchainMessage) error {
	_ = h.coordinator.Broadcast(msg) // TODO process the errors
	return nil
}

// Unicast sends a message to a specified receiver.
func (h *Helper) Unicast(msgPayload []byte, receiver string) error {
	// TODO Call a function in the comms layer; wait for Jeff's implementation.
	return nil
}

// BeginTxBatch gets invoked when the next round of transaction-batch
// execution begins.
func (h *Helper) BeginTxBatch(id interface{}) error {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return fmt.Errorf("Failed to get the ledger: %v", err)
	}
	if err := ledger.BeginTxBatch(id); err != nil {
		return fmt.Errorf("Failed to begin transaction with the ledger: %v", err)
	}
	return nil
}

// ExecTXs executes all the transactions listed in the txs array
// one-by-one. If all the executions are successful, it returns
// the candidate global state hash, and nil error array.
func (h *Helper) ExecTXs(txs []*pb.Transaction) ([]byte, []error) {
	return chaincode.ExecuteTransactions(context.Background(), chaincode.DefaultChain, txs, h.coordinator.GetSecHelper())
}

// CommitTxBatch gets invoked when the current transaction-batch needs
// to be committed. This function returns successfully iff the
// transactions details and state changes (that may have happened
// during execution of this transaction-batch) have been committed to
// permanent storage.
func (h *Helper) CommitTxBatch(id interface{}, transactions []*pb.Transaction, proof []byte) error {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return fmt.Errorf("Failed to get the ledger: %v", err)
	}
	if err := ledger.CommitTxBatch(id, transactions, proof); err != nil {
		return fmt.Errorf("Failed to commit transaction to the ledger: %v", err)
	}
	return nil
}

// RollbackTxBatch discards all the state changes that may have taken
// place during the execution of current transaction-batch.
func (h *Helper) RollbackTxBatch(id interface{}) error {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return fmt.Errorf("Failed to get the ledger: %v", err)
	}
	if err := ledger.RollbackTxBatch(id); err != nil {
		return fmt.Errorf("Failed to rollback transaction with the ledger: %v", err)
	}
	return nil
}

// GetBlock returns a block from the chain
func (h *Helper) GetBlock(blockNumber uint64) (block *pb.Block, err error) {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return nil, fmt.Errorf("Failed to get the ledger :%v", err)
	}
	return ledger.GetBlockByNumber(blockNumber)
}

// GetCurrentStateHash returns the current/temporary state hash
func (h *Helper) GetCurrentStateHash() (stateHash []byte, err error) {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return nil, fmt.Errorf("Failed to get the ledger :%v", err)
	}
	return ledger.GetTempStateHash()
}
<commit_msg>Update CPI so that it uses replica hashes for identification, see #265<commit_after>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

package helper

import (
	"encoding/base64"
	"fmt"

	"github.com/spf13/viper"
	"golang.org/x/net/context"

	"github.com/openblockchain/obc-peer/openchain/chaincode"
	"github.com/openblockchain/obc-peer/openchain/consensus"
	"github.com/openblockchain/obc-peer/openchain/ledger"
	"github.com/openblockchain/obc-peer/openchain/peer"
	pb "github.com/openblockchain/obc-peer/protos"
)

// =============================================================================
// Structure definitions go here
// =============================================================================

// Helper contains the reference to coordinator for broadcasts/unicasts.
type Helper struct {
	coordinator peer.MessageHandlerCoordinator
}

// =============================================================================
// Constructors go here
// =============================================================================

// NewHelper constructs the consensus helper object.
func NewHelper(mhc peer.MessageHandlerCoordinator) consensus.CPI {
	return &Helper{coordinator: mhc}
}

// =============================================================================
// Stack-facing implementation goes here
// =============================================================================

// GetReplicaHash returns the crypto IDs of the current replica and the whole network
func (h *Helper) GetReplicaHash() (self string, network []string, err error) {
	self = base64.StdEncoding.EncodeToString(h.coordinator.GetSecHelper().GetID())

	config := viper.New()
	config.SetConfigName("openchain")
	config.AddConfigPath("./")
	err = config.ReadInConfig()
	if err != nil {
		err = fmt.Errorf("Fatal error reading root config: %s", err)
		return self, nil, err
	}
	network = config.GetStringSlice("peer.validator.replicas.hashes")

	return self, network, nil
}

// GetReplicaID returns the uint handle corresponding to a replica address
func (h *Helper) GetReplicaID(addr string) (id uint64, err error) {
	_, network, err := h.GetReplicaHash()
	if err != nil {
		return uint64(0), err
	}
	for i, v := range network {
		if v == addr {
			return uint64(i), nil
		}
	}

	err = fmt.Errorf("Couldn't find crypto ID in list of VP IDs given in config")
	return uint64(0), err
}

// Broadcast sends a message to all validating peers.
func (h *Helper) Broadcast(msg *pb.OpenchainMessage) error {
	_ = h.coordinator.Broadcast(msg) // TODO process the errors
	return nil
}

// Unicast sends a message to a specified receiver.
func (h *Helper) Unicast(msgPayload []byte, receiver string) error {
	// TODO Call a function in the comms layer; wait for Jeff's implementation.
	return nil
}

// BeginTxBatch gets invoked when the next round of transaction-batch
// execution begins.
func (h *Helper) BeginTxBatch(id interface{}) error {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return fmt.Errorf("Failed to get the ledger: %v", err)
	}
	if err := ledger.BeginTxBatch(id); err != nil {
		return fmt.Errorf("Failed to begin transaction with the ledger: %v", err)
	}
	return nil
}

// ExecTXs executes all the transactions listed in the txs array
// one-by-one. If all the executions are successful, it returns
// the candidate global state hash, and nil error array.
func (h *Helper) ExecTXs(txs []*pb.Transaction) ([]byte, []error) {
	return chaincode.ExecuteTransactions(context.Background(), chaincode.DefaultChain, txs, h.coordinator.GetSecHelper())
}

// CommitTxBatch gets invoked when the current transaction-batch needs
// to be committed. This function returns successfully iff the
// transactions details and state changes (that may have happened
// during execution of this transaction-batch) have been committed to
// permanent storage.
func (h *Helper) CommitTxBatch(id interface{}, transactions []*pb.Transaction, proof []byte) error {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return fmt.Errorf("Failed to get the ledger: %v", err)
	}
	if err := ledger.CommitTxBatch(id, transactions, proof); err != nil {
		return fmt.Errorf("Failed to commit transaction to the ledger: %v", err)
	}
	return nil
}

// RollbackTxBatch discards all the state changes that may have taken
// place during the execution of current transaction-batch.
func (h *Helper) RollbackTxBatch(id interface{}) error {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return fmt.Errorf("Failed to get the ledger: %v", err)
	}
	if err := ledger.RollbackTxBatch(id); err != nil {
		return fmt.Errorf("Failed to rollback transaction with the ledger: %v", err)
	}
	return nil
}

// GetBlock returns a block from the chain
func (h *Helper) GetBlock(blockNumber uint64) (block *pb.Block, err error) {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return nil, fmt.Errorf("Failed to get the ledger :%v", err)
	}
	return ledger.GetBlockByNumber(blockNumber)
}

// GetCurrentStateHash returns the current/temporary state hash
func (h *Helper) GetCurrentStateHash() (stateHash []byte, err error) {
	ledger, err := ledger.GetLedger()
	if err != nil {
		return nil, fmt.Errorf("Failed to get the ledger :%v", err)
	}
	return ledger.GetTempStateHash()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"strconv"
)

type State struct {
	Samples  int
	TimeBase int
	TrigLev  int
	SoftGain int
}

func NewState(m map[string]interface{}) State {
	var s State

	s.Samples = atoi(m["Samples"])
	s.TrigLev = atoi(m["TrigLev"])
	s.TimeBase = atoi(m["TimeBase"])
	s.SoftGain = atoi(m["SoftGain"])

	return s
}

func (s *State) WriteTo(w tty) {
	w.writeInt(MAGIC)
	w.writeInt(s.Samples)
	w.writeInt(s.TimeBase)
	w.writeInt(s.TrigLev)
	w.writeInt(s.SoftGain)
}

func atoi(a interface{}) int {
	i, err := strconv.Atoi(fmt.Sprint(a))
	if err != nil {
		log.Println(err)
	}
	return i
}
<commit_msg>state.go bugifx<commit_after>package main

import (
	"fmt"
	"log"
	"strconv"
)

type State struct {
	Samples  int
	TimeBase int
	TrigLev  int
	SoftGain int
}

func NewState(m map[string]interface{}) State {
	var s State

	s.Samples = atoi(m["Samples"])
	s.TrigLev = atoi(m["TrigLev"])
	s.TimeBase = atoi(m["TimeBase"])
	s.SoftGain = atoi(m["SoftGain"])

	return s
}

func (s *State) WriteTo(w tty) {
	w.writeInt(s.Samples)
	w.writeInt(s.TimeBase)
	w.writeInt(s.TrigLev)
	w.writeInt(s.SoftGain)
}

func atoi(a interface{}) int {
	i, err := strconv.Atoi(fmt.Sprint(a))
	if err != nil {
		log.Println(err)
	}
	return i
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/hanwen/go-fuse/termite"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"rpc"
)

const _SOCKET = ".termite-socket"

func OpenConn(socket string, channel string) net.Conn {
	conn, err := net.Dial("unix", socket)
	if err != nil {
		log.Fatal("Dial:", err)
	}
	if len(channel) != 8 {
		panic(channel)
	}
	_, err = io.WriteString(conn, channel)
	if err != nil {
		log.Fatal("WriteString", err)
	}
	return conn
}

func main() {
	path := os.Getenv("PATH")

	args := os.Args
	_, base := filepath.Split(args[0])
	binary := ""
	for _, c := range filepath.SplitList(path) {
		_, dirBase := filepath.Split(c)
		if dirBase == "termite" {
			continue
		}

		try := filepath.Join(c, base)
		_, err := os.Stat(try)
		if err == nil {
			binary = try
		}
	}
	if binary == "" {
		log.Fatal("could not find", base)
	}
	args[0] = binary
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal("Getwd", err)
	}

	id := termite.RandomBytes(4)
	req := termite.WorkRequest{
		StdinId: fmt.Sprintf(termite.STDIN_FMT, id),
		Binary:  binary,
		Argv:    args,
		Env:     os.Environ(),
		Dir:     wd,
	}

	socket := os.Getenv("TERMITE_SOCKET")
	if socket == "" {
		socketPath := wd
		for socketPath != "/" {
			cand := filepath.Join(socketPath, _SOCKET)
			fi, _ := os.Lstat(cand)
			if fi != nil && fi.IsSocket() {
				socket = cand
				break
			}
			socketPath = filepath.Clean(filepath.Join(socketPath, ".."))
		}
	}
	conn := OpenConn(socket, termite.RPC_CHANNEL)
	stdinConn := OpenConn(socket, req.StdinId)
	go func() {
		err := io.Copy(stdinConn, os.Stdin)
		stdinConn.Close()
	}()
	client := rpc.NewClient(conn)

	rep := termite.WorkReply{}
	err = client.Call("LocalMaster.Run", &req, &rep)
	if err != nil {
		log.Fatal("LocalMaster.Run: ", err)
	}

	os.Stdout.Write([]byte(rep.Stdout))
	os.Stderr.Write([]byte(rep.Stderr))
	// TODO -something with signals.
	os.Exit(rep.Exit.ExitStatus())
}
<commit_msg>Fix compile error.<commit_after>package main

import (
	"fmt"
	"github.com/hanwen/go-fuse/termite"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"rpc"
)

const _SOCKET = ".termite-socket"

func OpenConn(socket string, channel string) net.Conn {
	conn, err := net.Dial("unix", socket)
	if err != nil {
		log.Fatal("Dial:", err)
	}
	if len(channel) != 8 {
		panic(channel)
	}
	_, err = io.WriteString(conn, channel)
	if err != nil {
		log.Fatal("WriteString", err)
	}
	return conn
}

func main() {
	path := os.Getenv("PATH")

	args := os.Args
	_, base := filepath.Split(args[0])
	binary := ""
	for _, c := range filepath.SplitList(path) {
		_, dirBase := filepath.Split(c)
		if dirBase == "termite" {
			continue
		}

		try := filepath.Join(c, base)
		_, err := os.Stat(try)
		if err == nil {
			binary = try
		}
	}
	if binary == "" {
		log.Fatal("could not find", base)
	}
	args[0] = binary
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal("Getwd", err)
	}

	id := termite.RandomBytes(4)
	req := termite.WorkRequest{
		StdinId: fmt.Sprintf(termite.STDIN_FMT, id),
		Binary:  binary,
		Argv:    args,
		Env:     os.Environ(),
		Dir:     wd,
	}

	socket := os.Getenv("TERMITE_SOCKET")
	if socket == "" {
		socketPath := wd
		for socketPath != "/" {
			cand := filepath.Join(socketPath, _SOCKET)
			fi, _ := os.Lstat(cand)
			if fi != nil && fi.IsSocket() {
				socket = cand
				break
			}
			socketPath = filepath.Clean(filepath.Join(socketPath, ".."))
		}
	}
	conn := OpenConn(socket, termite.RPC_CHANNEL)
	stdinConn := OpenConn(socket, req.StdinId)
	go func() {
		io.Copy(stdinConn, os.Stdin)
		stdinConn.Close()
	}()
	client := rpc.NewClient(conn)

	rep := termite.WorkReply{}
	err = client.Call("LocalMaster.Run", &req, &rep)
	if err != nil {
		log.Fatal("LocalMaster.Run: ", err)
	}

	os.Stdout.Write([]byte(rep.Stdout))
	os.Stderr.Write([]byte(rep.Stderr))
	// TODO -something with signals.
	os.Exit(rep.Exit.ExitStatus())
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fsblkstorage

import (
	"github.com/hyperledger/fabric/common/ledger"
	"github.com/hyperledger/fabric/common/ledger/blkstorage"
	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
	"github.com/hyperledger/fabric/protos/common"
	"github.com/hyperledger/fabric/protos/peer"
)

// fsBlockStore - filesystem based implementation for `BlockStore`
type fsBlockStore struct {
	id      string
	conf    *Conf
	fileMgr *blockfileMgr
}

// NewFsBlockStore constructs a `FsBlockStore`
func newFsBlockStore(id string, conf *Conf, indexConfig *blkstorage.IndexConfig,
	dbHandle *leveldbhelper.DBHandle) *fsBlockStore {
	return &fsBlockStore{id, conf, newBlockfileMgr(id, conf, indexConfig, dbHandle)}
}

// AddBlock adds a new block
func (store *fsBlockStore) AddBlock(block *common.Block) error {
	return store.fileMgr.addBlock(block)
}

// GetBlockchainInfo returns the current info about blockchain
func (store *fsBlockStore) GetBlockchainInfo() (*common.BlockchainInfo, error) {
	return store.fileMgr.getBlockchainInfo(), nil
}

// RetrieveBlocks returns an iterator that can be used for iterating over a range of blocks
func (store *fsBlockStore) RetrieveBlocks(startNum uint64) (ledger.ResultsIterator, error) {
	var itr *blocksItr
	var err error
	if itr, err = store.fileMgr.retrieveBlocks(startNum); err != nil {
		return nil, err
	}
	return itr, nil
}

// RetrieveBlockByHash returns the block for given block-hash
func (store *fsBlockStore) RetrieveBlockByHash(blockHash []byte) (*common.Block, error) {
	return store.fileMgr.retrieveBlockByHash(blockHash)
}

// RetrieveBlockByNumber returns the block at a given blockchain height
func (store *fsBlockStore) RetrieveBlockByNumber(blockNum uint64) (*common.Block, error) {
	return store.fileMgr.retrieveBlockByNumber(blockNum)
}

// RetrieveTxByID returns a transaction for given transaction id
func (store *fsBlockStore) RetrieveTxByID(txID string) (*common.Envelope, error) {
	return store.fileMgr.retrieveTransactionByID(txID)
}

// RetrieveTxByID returns a transaction for given transaction id
func (store *fsBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error) {
	return store.fileMgr.retrieveTransactionByBlockNumTranNum(blockNum, tranNum)
}

func (store *fsBlockStore) RetrieveBlockByTxID(txID string) (*common.Block, error) {
	return store.fileMgr.retrieveBlockByTxID(txID)
}

func (store *fsBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
	return store.fileMgr.retrieveTxValidationCodeByTxID(txID)
}

// Shutdown shuts down the block store
func (store *fsBlockStore) Shutdown() {
	logger.Debugf("closing fs blockStore:%s", store.id)
	store.fileMgr.close()
}
<commit_msg>[FAB-13261] Refactor function RetrieveBlocks<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fsblkstorage

import (
	"github.com/hyperledger/fabric/common/ledger"
	"github.com/hyperledger/fabric/common/ledger/blkstorage"
	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
	"github.com/hyperledger/fabric/protos/common"
	"github.com/hyperledger/fabric/protos/peer"
)

// fsBlockStore - filesystem based implementation for `BlockStore`
type fsBlockStore struct {
	id      string
	conf    *Conf
	fileMgr *blockfileMgr
}

// NewFsBlockStore constructs a `FsBlockStore`
func newFsBlockStore(id string, conf *Conf, indexConfig *blkstorage.IndexConfig,
	dbHandle *leveldbhelper.DBHandle) *fsBlockStore {
	return &fsBlockStore{id, conf, newBlockfileMgr(id, conf, indexConfig, dbHandle)}
}

// AddBlock adds a new block
func (store *fsBlockStore) AddBlock(block *common.Block) error {
	return store.fileMgr.addBlock(block)
}

// GetBlockchainInfo returns the current info about blockchain
func (store *fsBlockStore) GetBlockchainInfo() (*common.BlockchainInfo, error) {
	return store.fileMgr.getBlockchainInfo(), nil
}

// RetrieveBlocks returns an iterator that can be used for iterating over a range of blocks
func (store *fsBlockStore) RetrieveBlocks(startNum uint64) (ledger.ResultsIterator, error) {
	return store.fileMgr.retrieveBlocks(startNum)
}

// RetrieveBlockByHash returns the block for given block-hash
func (store *fsBlockStore) RetrieveBlockByHash(blockHash []byte) (*common.Block, error) {
	return store.fileMgr.retrieveBlockByHash(blockHash)
}

// RetrieveBlockByNumber returns the block at a given blockchain height
func (store *fsBlockStore) RetrieveBlockByNumber(blockNum uint64) (*common.Block, error) {
	return store.fileMgr.retrieveBlockByNumber(blockNum)
}

// RetrieveTxByID returns a transaction for given transaction id
func (store *fsBlockStore) RetrieveTxByID(txID string) (*common.Envelope, error) {
	return store.fileMgr.retrieveTransactionByID(txID)
}

// RetrieveTxByID returns a transaction for given transaction id
func (store *fsBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error) {
	return store.fileMgr.retrieveTransactionByBlockNumTranNum(blockNum, tranNum)
}

func (store *fsBlockStore) RetrieveBlockByTxID(txID string) (*common.Block, error) {
	return store.fileMgr.retrieveBlockByTxID(txID)
}

func (store *fsBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
	return store.fileMgr.retrieveTxValidationCodeByTxID(txID)
}

// Shutdown shuts down the block store
func (store *fsBlockStore) Shutdown() {
	logger.Debugf("closing fs blockStore:%s", store.id)
	store.fileMgr.close()
}
<|endoftext|>


<commit_before>package ecs

import "github.com/denverdino/aliyungo/common"

type DescribeInstanceTypesArgs struct {
	InstanceTypeFamily string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype
type InstanceTypeItemType struct {
	InstanceTypeId     string
	CpuCoreCount       int
	MemorySize         float64
	InstanceTypeFamily string
}

type DescribeInstanceTypesResponse struct {
	common.Response
	InstanceTypes struct {
		InstanceType []InstanceTypeItemType
	}
}

// DescribeInstanceTypes describes all instance types
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/other&describeinstancetypes
func (client *Client) DescribeInstanceTypes() (instanceTypes []InstanceTypeItemType, err error) {
	response := DescribeInstanceTypesResponse{}

	err = client.Invoke("DescribeInstanceTypes", &DescribeInstanceTypesArgs{}, &response)

	if err != nil {
		return []InstanceTypeItemType{}, err
	}
	return response.InstanceTypes.InstanceType, nil

}

// support user args
func (client *Client) DescribeInstanceTypesNew(args *DescribeInstanceTypesArgs) (instanceTypes []InstanceTypeItemType, err error) {
	response := DescribeInstanceTypesResponse{}

	err = client.Invoke("DescribeInstanceTypes", args, &response)

	if err != nil {
		return []InstanceTypeItemType{}, err
	}
	return response.InstanceTypes.InstanceType, nil

}

type DescribeInstanceTypeFamiliesArgs struct {
	RegionId   common.Region
	Generation string
}

type InstanceTypeFamilies struct {
	InstanceTypeFamily []InstanceTypeFamily
}

type InstanceTypeFamily struct {
	InstanceTypeFamilyId string
	Generation           string
}

type DescribeInstanceTypeFamiliesResponse struct {
	common.Response

	InstanceTypeFamilies InstanceTypeFamilies
}

func (client *Client) DescribeInstanceTypeFamilies(args *DescribeInstanceTypeFamiliesArgs) (*DescribeInstanceTypeFamiliesResponse, error) {
	response := &DescribeInstanceTypeFamiliesResponse{}

	err := client.Invoke("DescribeInstanceTypeFamilies", args, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}
<commit_msg>output more instance type attribution<commit_after>package ecs

import "github.com/denverdino/aliyungo/common"

type DescribeInstanceTypesArgs struct {
	InstanceTypeFamily string
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype
type InstanceTypeItemType struct {
	InstanceTypeId       string
	CpuCoreCount         int
	MemorySize           float64
	InstanceTypeFamily   string
	GPUAmount            int
	GPUSpec              string
	InitialCredit        int
	BaselineCredit       int
	EniQuantity          int
	LocalStorageCapacity int
	LocalStorageAmount   int
	LocalStorageCategory string
}

type DescribeInstanceTypesResponse struct {
	common.Response
	InstanceTypes struct {
		InstanceType []InstanceTypeItemType
	}
}

// DescribeInstanceTypes describes all instance types
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/other&describeinstancetypes
func (client *Client) DescribeInstanceTypes() (instanceTypes []InstanceTypeItemType, err error) {
	response := DescribeInstanceTypesResponse{}

	err = client.Invoke("DescribeInstanceTypes", &DescribeInstanceTypesArgs{}, &response)

	if err != nil {
		return []InstanceTypeItemType{}, err
	}
	return response.InstanceTypes.InstanceType, nil

}

// support user args
func (client *Client) DescribeInstanceTypesNew(args *DescribeInstanceTypesArgs) (instanceTypes []InstanceTypeItemType, err error) {
	response := DescribeInstanceTypesResponse{}

	err = client.Invoke("DescribeInstanceTypes", args, &response)

	if err != nil {
		return []InstanceTypeItemType{}, err
	}
	return response.InstanceTypes.InstanceType, nil

}

type DescribeInstanceTypeFamiliesArgs struct {
	RegionId   common.Region
	Generation string
}

type InstanceTypeFamilies struct {
	InstanceTypeFamily []InstanceTypeFamily
}

type InstanceTypeFamily struct {
	InstanceTypeFamilyId string
	Generation           string
}

type DescribeInstanceTypeFamiliesResponse struct {
	common.Response

	InstanceTypeFamilies InstanceTypeFamilies
}

func (client *Client) DescribeInstanceTypeFamilies(args *DescribeInstanceTypeFamiliesArgs) (*DescribeInstanceTypeFamiliesResponse, error) {
	response := &DescribeInstanceTypeFamiliesResponse{}

	err := client.Invoke("DescribeInstanceTypeFamilies", args, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}
<|endoftext|>


<commit_before>package stream

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/ericxtang/m3u8"
	"github.com/golang/glog"
	lpmon "github.com/livepeer/go-livepeer/monitor"
)

const DefaultMediaPlLen = uint(500)
const DefaultSegWaitTime = time.Second * 10
const SegWaitInterval = time.Second

var ErrAddVariant = errors.New("ErrAddVariant")
var ErrAddHLSSegment = errors.New("ErrAddHLSSegment")

//BasicHLSVideoStream is a basic implementation of HLSVideoStream
type BasicHLSVideoStream struct {
	masterPlCache       *m3u8.MasterPlaylist
	variantMediaPlCache map[string]*m3u8.MediaPlaylist //StrmID -> MediaPlaylist
	sqMap               map[string]*HLSSegment
	lockMap             map[string]sync.Locker
	strmID              string
	segWaitTime         time.Duration
	subscriber          func(HLSVideoStream, string, *HLSSegment)

	//Keep track of the non-variant stream separately
	strmMediaPlCache *m3u8.MediaPlaylist
	strmLock         sync.Locker
}

func NewBasicHLSVideoStream(strmID string, segWaitTime time.Duration) *BasicHLSVideoStream {
	mpl := m3u8.NewMasterPlaylist()
	pl, _ := m3u8.NewMediaPlaylist(DefaultMediaPlLen, DefaultMediaPlLen)
	strm := &BasicHLSVideoStream{
		masterPlCache:       mpl,
		variantMediaPlCache: make(map[string]*m3u8.MediaPlaylist),
		sqMap:               make(map[string]*HLSSegment),
		lockMap:             make(map[string]sync.Locker),
		strmID:              strmID,
		segWaitTime:         segWaitTime,
		strmMediaPlCache:    pl,
		strmLock:            &sync.Mutex{}}

	return strm
}

//SetSubscriber sets the callback function that will be called when a new hls segment is inserted
func (s *BasicHLSVideoStream) SetSubscriber(f func(s HLSVideoStream, strmID string, seg *HLSSegment)) {
	s.subscriber = f
}

//GetStreamID returns the streamID
func (s *BasicHLSVideoStream) GetStreamID() string { return s.strmID }

//GetStreamFormat always returns HLS
func (s *BasicHLSVideoStream) GetStreamFormat() VideoFormat { return HLS }

//GetMasterPlaylist returns the master playlist. It will return nil if no variant has been added.
func (s *BasicHLSVideoStream) GetMasterPlaylist() (*m3u8.MasterPlaylist, error) {
	return s.masterPlCache, nil
}

//GetVariantPlaylist returns the media playlist represented by the streamID
func (s *BasicHLSVideoStream) GetVariantPlaylist(strmID string) (*m3u8.MediaPlaylist, error) {
	if strmID == s.GetStreamID() {
		return s.strmMediaPlCache, nil
	}

	pl, ok := s.variantMediaPlCache[strmID]
	if !ok {
		return nil, ErrNotFound
	}

	return pl, nil
}

//GetHLSSegment gets the HLS segment.  It blocks until something is found, or timeout happens.
func (s *BasicHLSVideoStream) GetHLSSegment(strmID string, segName string) (*HLSSegment, error) {
	start := time.Now()
	for {
		if time.Since(start) > s.segWaitTime {
			return nil, ErrNotFound
		}

		seg, ok := s.sqMap[sqMapKey(strmID, segName)]
		if !ok {
			lpmon.Instance().LogBuffer(strmID)
			time.Sleep(SegWaitInterval)
			continue
		}

		return seg, nil
	}
}

//AddVariant adds a new variant playlist (and therefore, a new HLS video stream) to the master playlist.
func (s *BasicHLSVideoStream) AddVariant(strmID string, variant *m3u8.Variant) error {
	if variant == nil {
		glog.Errorf("Cannot add nil variant")
		return ErrAddVariant
	}

	_, ok := s.variantMediaPlCache[strmID]
	if ok {
		glog.Errorf("Variant %v already exists", strmID)
		return ErrAddVariant
	}

	for _, v := range s.masterPlCache.Variants {
		if v.Bandwidth == variant.Bandwidth && v.Resolution == variant.Resolution {
			glog.Errorf("Variant with Bandwidth %v and Resolution %v already exists", v.Bandwidth, v.Resolution)
			return ErrAddVariant
		}
	}

	//Append to master playlist
	s.masterPlCache.Append(variant.URI, variant.Chunklist, variant.VariantParams)

	//Add to mediaPLCache
	s.variantMediaPlCache[strmID] = variant.Chunklist

	//Create the "media playlist specific" lock
	s.lockMap[strmID] = &sync.Mutex{}

	return nil
}

//AddHLSSegment adds the hls segment to the right stream
func (s *BasicHLSVideoStream) AddHLSSegment(strmID string, seg *HLSSegment) error {
	// glog.Infof("Adding segment: %v", seg.Name)
	if strmID == s.GetStreamID() {
		s.strmLock.Lock()
		defer s.strmLock.Unlock()
		glog.Infof("Adding segment %v to stream: %v.  pl len: %v", seg.SeqNo, strmID, len(s.strmMediaPlCache.Segments))
		if err := s.strmMediaPlCache.InsertSegment(seg.SeqNo, &m3u8.MediaSegment{SeqId: seg.SeqNo, Duration: seg.Duration, URI: seg.Name}); err != nil {
			glog.Errorf("Error inserting segment %v: %v", seg.Name, err)
			return ErrAddHLSSegment
		}
		s.sqMap[sqMapKey(strmID, seg.Name)] = seg
		if s.subscriber != nil {
			s.subscriber(s, strmID, seg)
		}
		return nil
	}

	lock, ok := s.lockMap[strmID]
	if !ok {
		return ErrNotFound
	}
	lock.Lock()
	defer lock.Unlock()

	//Add segment to media playlist
	pl, err := s.GetVariantPlaylist(strmID)
	if err != nil {
		return err
	}
	pl.InsertSegment(seg.SeqNo, &m3u8.MediaSegment{SeqId: seg.SeqNo, Duration: seg.Duration, URI: seg.Name})

	//Add to buffer
	s.sqMap[sqMapKey(strmID, seg.Name)] = seg

	//Call subscriber
	if s.subscriber != nil {
		s.subscriber(s, strmID, seg)
	}

	return nil
}

func (s BasicHLSVideoStream) String() string {
	return fmt.Sprintf("StreamID: %v, Type: %v, len: %v", s.GetStreamID(), s.GetStreamFormat(), len(s.sqMap))
}

func sqMapKey(strmID, segName string) string {
	return fmt.Sprintf("%v_%v", strmID, segName)
}
<commit_msg>comment out log<commit_after>package stream

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/ericxtang/m3u8"
	"github.com/golang/glog"
	lpmon "github.com/livepeer/go-livepeer/monitor"
)

const DefaultMediaPlLen = uint(500)
const DefaultSegWaitTime = time.Second * 10
const SegWaitInterval = time.Second

var ErrAddVariant = errors.New("ErrAddVariant")
var ErrAddHLSSegment = errors.New("ErrAddHLSSegment")

//BasicHLSVideoStream is a basic implementation of HLSVideoStream
type BasicHLSVideoStream struct {
	masterPlCache       *m3u8.MasterPlaylist
	variantMediaPlCache map[string]*m3u8.MediaPlaylist //StrmID -> MediaPlaylist
	sqMap               map[string]*HLSSegment
	lockMap             map[string]sync.Locker
	strmID              string
	segWaitTime         time.Duration
	subscriber          func(HLSVideoStream, string, *HLSSegment)

	//Keep track of the non-variant stream separately
	strmMediaPlCache *m3u8.MediaPlaylist
	strmLock         sync.Locker
}

func NewBasicHLSVideoStream(strmID string, segWaitTime time.Duration) *BasicHLSVideoStream {
	mpl := m3u8.NewMasterPlaylist()
	pl, _ := m3u8.NewMediaPlaylist(DefaultMediaPlLen, DefaultMediaPlLen)
	strm := &BasicHLSVideoStream{
		masterPlCache:       mpl,
		variantMediaPlCache: make(map[string]*m3u8.MediaPlaylist),
		sqMap:               make(map[string]*HLSSegment),
		lockMap:             make(map[string]sync.Locker),
		strmID:              strmID,
		segWaitTime:         segWaitTime,
		strmMediaPlCache:    pl,
		strmLock:            &sync.Mutex{}}

	return strm
}

//SetSubscriber sets the callback function that will be called when a new hls segment is inserted
func (s *BasicHLSVideoStream) SetSubscriber(f func(s HLSVideoStream, strmID string, seg *HLSSegment)) {
	s.subscriber = f
}

//GetStreamID returns the streamID
func (s *BasicHLSVideoStream) GetStreamID() string { return s.strmID }

//GetStreamFormat always returns HLS
func (s *BasicHLSVideoStream) GetStreamFormat() VideoFormat { return HLS }

//GetMasterPlaylist returns the master playlist. It will return nil if no variant has been added.
func (s *BasicHLSVideoStream) GetMasterPlaylist() (*m3u8.MasterPlaylist, error) {
	return s.masterPlCache, nil
}

//GetVariantPlaylist returns the media playlist represented by the streamID
func (s *BasicHLSVideoStream) GetVariantPlaylist(strmID string) (*m3u8.MediaPlaylist, error) {
	if strmID == s.GetStreamID() {
		return s.strmMediaPlCache, nil
	}

	pl, ok := s.variantMediaPlCache[strmID]
	if !ok {
		return nil, ErrNotFound
	}

	return pl, nil
}

//GetHLSSegment gets the HLS segment.  It blocks until something is found, or timeout happens.
func (s *BasicHLSVideoStream) GetHLSSegment(strmID string, segName string) (*HLSSegment, error) {
	start := time.Now()
	for {
		if time.Since(start) > s.segWaitTime {
			return nil, ErrNotFound
		}

		seg, ok := s.sqMap[sqMapKey(strmID, segName)]
		if !ok {
			lpmon.Instance().LogBuffer(strmID)
			time.Sleep(SegWaitInterval)
			continue
		}

		return seg, nil
	}
}

//AddVariant adds a new variant playlist (and therefore, a new HLS video stream) to the master playlist.
func (s *BasicHLSVideoStream) AddVariant(strmID string, variant *m3u8.Variant) error {
	if variant == nil {
		glog.Errorf("Cannot add nil variant")
		return ErrAddVariant
	}

	_, ok := s.variantMediaPlCache[strmID]
	if ok {
		glog.Errorf("Variant %v already exists", strmID)
		return ErrAddVariant
	}

	for _, v := range s.masterPlCache.Variants {
		if v.Bandwidth == variant.Bandwidth && v.Resolution == variant.Resolution {
			glog.Errorf("Variant with Bandwidth %v and Resolution %v already exists", v.Bandwidth, v.Resolution)
			return ErrAddVariant
		}
	}

	//Append to master playlist
	s.masterPlCache.Append(variant.URI, variant.Chunklist, variant.VariantParams)

	//Add to mediaPLCache
	s.variantMediaPlCache[strmID] = variant.Chunklist

	//Create the "media playlist specific" lock
	s.lockMap[strmID] = &sync.Mutex{}

	return nil
}

//AddHLSSegment adds the hls segment to the right stream
func (s *BasicHLSVideoStream) AddHLSSegment(strmID string, seg *HLSSegment) error {
	// glog.Infof("Adding segment: %v", seg.Name)
	if strmID == s.GetStreamID() {
		s.strmLock.Lock()
		defer s.strmLock.Unlock()
		// glog.Infof("Adding segment %v to stream: %v.  pl len: %v", seg.SeqNo, strmID, len(s.strmMediaPlCache.Segments))
		if err := s.strmMediaPlCache.InsertSegment(seg.SeqNo, &m3u8.MediaSegment{SeqId: seg.SeqNo, Duration: seg.Duration, URI: seg.Name}); err != nil {
			glog.Errorf("Error inserting segment %v: %v", seg.Name, err)
			return ErrAddHLSSegment
		}
		s.sqMap[sqMapKey(strmID, seg.Name)] = seg
		if s.subscriber != nil {
			s.subscriber(s, strmID, seg)
		}
		return nil
	}

	lock, ok := s.lockMap[strmID]
	if !ok {
		return ErrNotFound
	}
	lock.Lock()
	defer lock.Unlock()

	//Add segment to media playlist
	pl, err := s.GetVariantPlaylist(strmID)
	if err != nil {
		return err
	}
	pl.InsertSegment(seg.SeqNo, &m3u8.MediaSegment{SeqId: seg.SeqNo, Duration: seg.Duration, URI: seg.Name})

	//Add to buffer
	s.sqMap[sqMapKey(strmID, seg.Name)] = seg

	//Call subscriber
	if s.subscriber != nil {
		s.subscriber(s, strmID, seg)
	}

	return nil
}

func (s BasicHLSVideoStream) String() string {
	return fmt.Sprintf("StreamID: %v, Type: %v, len: %v", s.GetStreamID(), s.GetStreamFormat(), len(s.sqMap))
}

func sqMapKey(strmID, segName string) string {
	return fmt.Sprintf("%v_%v", strmID, segName)
}
<|endoftext|>