<commit_before>package gost

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"github.com/go-log/log"
)

type httpConnector struct {
	User *url.Userinfo
}

// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector {
	return &httpConnector{User: user}
}

func (c *httpConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
	req := &http.Request{
		Method:     http.MethodConnect,
		URL:        &url.URL{Host: addr},
		Host:       addr,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	req.Header.Set("User-Agent", DefaultUserAgent)
	req.Header.Set("Proxy-Connection", "keep-alive")

	if c.User != nil {
		u := c.User.Username()
		p, _ := c.User.Password()
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
	}

	if err := req.Write(conn); err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Log(string(dump))
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Log(string(dump))
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s", resp.Status)
	}

	return conn, nil
}

type httpHandler struct {
	options *HandlerOptions
}

// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler {
	h := &httpHandler{
		options: &HandlerOptions{},
	}
	for _, opt := range opts {
		opt(h.options)
	}
	return h
}

func (h *httpHandler) Handle(conn net.Conn) {
	defer conn.Close()

	req, err := http.ReadRequest(bufio.NewReader(conn))
	if err != nil {
		log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		return
	}

	if Debug {
		log.Logf("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto)
		dump, _ := httputil.DumpRequest(req, false)
		log.Logf(string(dump))
	}

	if req.Method == "PRI" && req.ProtoMajor == 2 {
		log.Logf("[http] %s <- %s : Not an HTTP2 server", conn.RemoteAddr(), req.Host)
		resp := "HTTP/1.1 400 Bad Request\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		return
	}

	u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if Debug && (u != "" || p != "") {
		log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
	}
	if !authenticate(u, p, h.options.Users...) {
		log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
		resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
			"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		return
	}

	req.Header.Del("Proxy-Authorization")
	req.Header.Del("Proxy-Connection")

	if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
		log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
		b := []byte("HTTP/1.1 403 Forbidden\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		conn.Write(b)
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		return
	}

	// forward http request
	lastNode := h.options.Chain.LastNode()
	if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
		h.forwardRequest(conn, req)
		return
	}

	host := req.Host
	if !strings.Contains(req.Host, ":") {
		host += ":80"
	}
	cc, err := h.options.Chain.Dial(host)
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if req.Method == http.MethodConnect {
		b := []byte("HTTP/1.1 200 Connection established\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		conn.Write(b)
	} else {
		req.Header.Del("Proxy-Connection")

		if err = req.Write(cc); err != nil {
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
			return
		}
	}

	log.Logf("[http] %s <-> %s", cc.LocalAddr(), req.Host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", cc.LocalAddr(), req.Host)
}

func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
	if h.options.Chain.IsEmpty() {
		return
	}
	lastNode := h.options.Chain.LastNode()

	cc, err := h.options.Chain.Conn()
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), lastNode.Addr, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if lastNode.User != nil {
		s := lastNode.User.String()
		if _, set := lastNode.User.Password(); !set {
			s += ":"
		}
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
	}

	cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
	if err = req.WriteProxy(cc); err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
		return
	}
	cc.SetWriteDeadline(time.Time{})

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
	return
}

func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
	if proxyAuth == "" {
		return
	}

	if !strings.HasPrefix(proxyAuth, "Basic ") {
		return
	}
	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}

	return cs[:s], cs[s+1:], true
}

func authenticate(username, password string, users ...*url.Userinfo) bool {
	if len(users) == 0 {
		return true
	}

	for _, user := range users {
		u := user.Username()
		p, _ := user.Password()
		if (u == username && p == password) ||
			(u == username && p == "") ||
			(u == "" && p == password) {
			return true
		}
	}
	return false
}
<commit_msg>fix #149<commit_after>package gost

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"github.com/go-log/log"
)

type httpConnector struct {
	User *url.Userinfo
}

// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector {
	return &httpConnector{User: user}
}

func (c *httpConnector) Connect(conn net.Conn, addr string) (net.Conn, error) {
	req := &http.Request{
		Method:     http.MethodConnect,
		URL:        &url.URL{Host: addr},
		Host:       addr,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	req.Header.Set("User-Agent", DefaultUserAgent)
	req.Header.Set("Proxy-Connection", "keep-alive")

	if c.User != nil {
		u := c.User.Username()
		p, _ := c.User.Password()
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
	}

	if err := req.Write(conn); err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Log(string(dump))
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Log(string(dump))
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s", resp.Status)
	}

	return conn, nil
}

type httpHandler struct {
	options *HandlerOptions
}

// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler {
	h := &httpHandler{
		options: &HandlerOptions{},
	}
	for _, opt := range opts {
		opt(h.options)
	}
	return h
}

func (h *httpHandler) Handle(conn net.Conn) {
	defer conn.Close()

	req, err := http.ReadRequest(bufio.NewReader(conn))
	if err != nil {
		log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		return
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
	}

	if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
		resp := "HTTP/1.1 400 Bad Request\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp)
		}
		return
	}

	u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if Debug && (u != "" || p != "") {
		log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
	}
	if !authenticate(u, p, h.options.Users...) {
		log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
		resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
			"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n"
		conn.Write([]byte(resp))
		return
	}

	req.Header.Del("Proxy-Authorization")
	req.Header.Del("Proxy-Connection")

	if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
		log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
		b := []byte("HTTP/1.1 403 Forbidden\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		conn.Write(b)
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		return
	}

	// forward http request
	lastNode := h.options.Chain.LastNode()
	if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
		h.forwardRequest(conn, req)
		return
	}

	host := req.Host
	if !strings.Contains(req.Host, ":") {
		host += ":80"
	}
	cc, err := h.options.Chain.Dial(host)
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if req.Method == http.MethodConnect {
		b := []byte("HTTP/1.1 200 Connection established\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
		}
		conn.Write(b)
	} else {
		req.Header.Del("Proxy-Connection")

		if err = req.Write(cc); err != nil {
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
			return
		}
	}

	log.Logf("[http] %s <-> %s", cc.LocalAddr(), req.Host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", cc.LocalAddr(), req.Host)
}

func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
	if h.options.Chain.IsEmpty() {
		return
	}
	lastNode := h.options.Chain.LastNode()

	cc, err := h.options.Chain.Conn()
	if err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)

		b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), lastNode.Addr, string(b))
		}
		conn.Write(b)
		return
	}
	defer cc.Close()

	if lastNode.User != nil {
		s := lastNode.User.String()
		if _, set := lastNode.User.Password(); !set {
			s += ":"
		}
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
	}

	cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
	if err = req.WriteProxy(cc); err != nil {
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
		return
	}
	cc.SetWriteDeadline(time.Time{})

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), req.Host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
	return
}

func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
	if proxyAuth == "" {
		return
	}

	if !strings.HasPrefix(proxyAuth, "Basic ") {
		return
	}
	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}

	return cs[:s], cs[s+1:], true
}

func authenticate(username, password string, users ...*url.Userinfo) bool {
	if len(users) == 0 {
		return true
	}

	for _, user := range users {
		u := user.Username()
		p, _ := user.Password()
		if (u == username && p == password) ||
			(u == username && p == "") ||
			(u == "" && p == password) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package hipdate

import (
	"fmt"
	"github.com/3onyc/hipdate/backends"
	"github.com/3onyc/stoppableListener"
	"log"
	"net"
	"net/http"
)

type HttpServer struct {
	l *stoppableListener.StoppableListener
	s *http.Server
	b backends.Backend
}

func NewHttpServer(b backends.Backend) *HttpServer {
	return &HttpServer{
		s: &http.Server{},
		b: b,
	}
}

func (h *HttpServer) Start() error {
	l, err := net.Listen("tcp", ":8889")
	if err != nil {
		return err
	}

	sl, err := stoppableListener.New(l)
	if err != nil {
		return err
	}

	http.HandleFunc("/status", h.status)

	h.l = sl
	h.s.Serve(h.l)

	return nil
}

func (h *HttpServer) Stop() {
	h.l.Stop()
	log.Println("[http] stopped")
}

func (h *HttpServer) status(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Add("Content-Type", "text/html")
	fmt.Fprintf(rw, h.b.ListHosts().Pprint())
}
<commit_msg>Change HTTP server to serve an API rather than a webpage<commit_after>package hipdate

import (
	"encoding/json"
	"fmt"
	"github.com/3onyc/hipdate/backends"
	"github.com/3onyc/stoppableListener"
	"log"
	"net"
	"net/http"
)

type HttpServer struct {
	l *stoppableListener.StoppableListener
	s *http.Server
	b backends.Backend
}

func NewHttpServer(b backends.Backend) *HttpServer {
	return &HttpServer{
		s: &http.Server{},
		b: b,
	}
}

func (h *HttpServer) Start() error {
	l, err := net.Listen("tcp", ":8889")
	if err != nil {
		return err
	}

	sl, err := stoppableListener.New(l)
	if err != nil {
		return err
	}

	http.HandleFunc("/api/v1/status.json", h.status)

	h.l = sl
	h.s.Serve(h.l)

	return nil
}

func (h *HttpServer) Stop() {
	h.l.Stop()
	log.Println("[http] stopped")
}

func (h *HttpServer) status(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Add("Content-Type", "application/json")

	b, err := json.MarshalIndent(h.b.ListHosts(), "", "    ")
	if err != nil {
		rw.WriteHeader(500)
		fmt.Fprint(rw, err)
		return
	}

	if _, err := rw.Write(b); err != nil {
		log.Println("ERROR http", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"syscall"
	"text/template"
	"time"
)

var postTemplate string = `---
title: {{.Title}}
date: "{{.Date}}"
image_name: {{.ImagePath}}
previous_image: {{.PreviousImage}}
next_image: {{.NextImage}}
next_post_path: {{.NextPostPath}}
previous_post_path: {{.PreviousPostPath}}
---
`

type GalleryItem struct {
	Title            string
	Date             string
	ImagePath        string
	NextImage        string
	PreviousImage    string
	NextPostPath     string
	PreviousPostPath string
}

func check(e error) int {
	result := 0
	if e != nil {
		result = 1
		defer func() {
			panic(e)
		}()
	}
	return result
}

func main() {
	if len(os.Args) < 4 {
		fmt.Printf("Usage: %s <Source Path> <Destination Section> <Title> [BaseUrl]\n", os.Args[0])
		syscall.Exit(1)
	}

	sourcePath := os.Args[1]
	staticRoot := strings.Replace(os.Args[1], "static/", "", 1) + "/"
	section := os.Args[2] + "/"
	title := os.Args[3]
	baseUrl := os.Args[4]
	contentPath := "content/" + section

	src, err := os.Stat(contentPath)
	if err != nil || !src.IsDir() {
		err = os.Mkdir(contentPath, 0755)
		if err != nil {
			fmt.Printf("content directory not found! Are you in a hugo directory?\n")
			os.Exit(1)
		}
	}

	postList, err := ioutil.ReadDir(sourcePath)
	if err != nil {
		fmt.Printf("Source path <%s> not found!\n", sourcePath)
		os.Exit(1)
	}

	for index, file := range postList {
		previousImage, nextImage := getPreviousAndNextPost(index, postList)
		generatePost(index, file, staticRoot, contentPath, title, previousImage, nextImage, section, baseUrl)
	}
}

func getPreviousAndNextPost(index int, postList []os.FileInfo) (previous os.FileInfo, next os.FileInfo) {
	if index+1 < len(postList) {
		next = postList[index+1]
	}
	if index >= 1 {
		previous = postList[index-1]
	}
	return
}

func stripExtension(baseUri string) (fileName string) {
	extension := path.Ext(baseUri)
	fileName = baseUri[0 : len(baseUri)-len(extension)]
	return
}

func buildPathFromFileInfo(imageFile os.FileInfo, sourcePath string, excludeExtension bool, baseUrl string) (imagePath string) {
	if imageFile != nil {
		fileName := imageFile.Name()
		if excludeExtension {
			fileName = stripExtension(imageFile.Name())
		}
		if baseUrl != "" && !excludeExtension {
			imagePath = baseUrl + "/" + sourcePath + fileName
		} else {
			imagePath = sourcePath + fileName
		}
	}
	return
}

func generatePost(index int, file os.FileInfo, sourcePath string, contentPath string, title string, previousImage os.FileInfo, nextImage os.FileInfo, section string, baseUrl string) {
	nextImagePath := buildPathFromFileInfo(nextImage, sourcePath, false, baseUrl)
	previousImagePath := buildPathFromFileInfo(previousImage, sourcePath, false, baseUrl)
	nextPostPath := buildPathFromFileInfo(nextImage, section, true, baseUrl)
	previousPostPath := buildPathFromFileInfo(previousImage, section, true, baseUrl)
	currentImagePath := ""
	if baseUrl != "" {
		currentImagePath = baseUrl + "/" + sourcePath + file.Name()
	} else {
		currentImagePath = sourcePath + file.Name()
	}

	galleryItem := GalleryItem{
		Title:            title,
		ImagePath:        currentImagePath,
		Date:             time.Now().Format("2006-01-02"),
		NextImage:        nextImagePath,
		PreviousImage:    previousImagePath,
		NextPostPath:     nextPostPath,
		PreviousPostPath: previousPostPath,
	}

	var buffer bytes.Buffer
	generateTemplate(galleryItem, &buffer)

	filePath := contentPath + stripExtension(file.Name()) + ".md"
	f, err := os.Create(filePath)
	check(err)
	defer f.Close()
	f.Sync()
	w := bufio.NewWriter(f)
	w.WriteString(buffer.String())
	w.Flush()
}

func generateTemplate(galleryItem GalleryItem, buffer *bytes.Buffer) {
	t := template.New("post template")
	t, _ = t.Parse(postTemplate)
	err := t.Execute(buffer, galleryItem)
	check(err)
}
<commit_msg>Fix crash that would happen if the 'optional' URL arg wasn't provided<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"syscall"
	"text/template"
	"time"
)

var postTemplate string = `---
title: {{.Title}}
date: "{{.Date}}"
image_name: {{.ImagePath}}
previous_image: {{.PreviousImage}}
next_image: {{.NextImage}}
next_post_path: {{.NextPostPath}}
previous_post_path: {{.PreviousPostPath}}
---
`

type GalleryItem struct {
	Title            string
	Date             string
	ImagePath        string
	NextImage        string
	PreviousImage    string
	NextPostPath     string
	PreviousPostPath string
}

func check(e error) int {
	result := 0
	if e != nil {
		result = 1
		defer func() {
			panic(e)
		}()
	}
	return result
}

func main() {
	if len(os.Args) < 4 {
		fmt.Printf("Usage: %s <Source Path> <Destination Section> <Title> [BaseUrl]\n", os.Args[0])
		syscall.Exit(1)
	}

	sourcePath := os.Args[1]
	staticRoot := strings.Replace(os.Args[1], "static/", "", 1) + "/"
	section := os.Args[2] + "/"
	title := os.Args[3]
	var baseUrl = ""
	if len(os.Args) > 4 {
		baseUrl = os.Args[4]
	}
	contentPath := "content/" + section

	src, err := os.Stat(contentPath)
	if err != nil || !src.IsDir() {
		err = os.Mkdir(contentPath, 0755)
		if err != nil {
			fmt.Printf("content directory not found! Are you in a hugo directory?\n")
			os.Exit(1)
		}
	}

	postList, err := ioutil.ReadDir(sourcePath)
	if err != nil {
		fmt.Printf("Source path <%s> not found!\n", sourcePath)
		os.Exit(1)
	}

	for index, file := range postList {
		previousImage, nextImage := getPreviousAndNextPost(index, postList)
		generatePost(index, file, staticRoot, contentPath, title, previousImage, nextImage, section, baseUrl)
	}
}

func getPreviousAndNextPost(index int, postList []os.FileInfo) (previous os.FileInfo, next os.FileInfo) {
	if index+1 < len(postList) {
		next = postList[index+1]
	}
	if index >= 1 {
		previous = postList[index-1]
	}
	return
}

func stripExtension(baseUri string) (fileName string) {
	extension := path.Ext(baseUri)
	fileName = baseUri[0 : len(baseUri)-len(extension)]
	return
}

func buildPathFromFileInfo(imageFile os.FileInfo, sourcePath string, excludeExtension bool, baseUrl string) (imagePath string) {
	if imageFile != nil {
		fileName := imageFile.Name()
		if excludeExtension {
			fileName = stripExtension(imageFile.Name())
		}
		if baseUrl != "" && !excludeExtension {
			imagePath = baseUrl + "/" + sourcePath + fileName
		} else {
			imagePath = sourcePath + fileName
		}
	}
	return
}

func generatePost(index int, file os.FileInfo, sourcePath string, contentPath string, title string, previousImage os.FileInfo, nextImage os.FileInfo, section string, baseUrl string) {
	nextImagePath := buildPathFromFileInfo(nextImage, sourcePath, false, baseUrl)
	previousImagePath := buildPathFromFileInfo(previousImage, sourcePath, false, baseUrl)
	nextPostPath := buildPathFromFileInfo(nextImage, section, true, baseUrl)
	previousPostPath := buildPathFromFileInfo(previousImage, section, true, baseUrl)
	currentImagePath := ""
	if baseUrl != "" {
		currentImagePath = baseUrl + "/" + sourcePath + file.Name()
	} else {
		currentImagePath = sourcePath + file.Name()
	}

	galleryItem := GalleryItem{
		Title:            title,
		ImagePath:        currentImagePath,
		Date:             time.Now().Format("2006-01-02"),
		NextImage:        nextImagePath,
		PreviousImage:    previousImagePath,
		NextPostPath:     nextPostPath,
		PreviousPostPath: previousPostPath,
	}

	var buffer bytes.Buffer
	generateTemplate(galleryItem, &buffer)

	filePath := contentPath + stripExtension(file.Name()) + ".md"
	f, err := os.Create(filePath)
	check(err)
	defer f.Close()
	f.Sync()
	w := bufio.NewWriter(f)
	w.WriteString(buffer.String())
	w.Flush()
}

func generateTemplate(galleryItem GalleryItem, buffer *bytes.Buffer) {
	t := template.New("post template")
	t, _ = t.Parse(postTemplate)
	err := t.Execute(buffer, galleryItem)
	check(err)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/astaxie/bat/httplib"
)

var defaultSetting = httplib.BeegoHttpSettings{
	ShowDebug:        true,
	UserAgent:        "bat/" + version,
	ConnectTimeout:   60 * time.Second,
	ReadWriteTimeout: 60 * time.Second,
	Gzip:             true,
	DumpBody:         true,
}

func getHTTP(method string, url string, args []string) (r *httplib.BeegoHttpRequest) {
	r = httplib.NewBeegoRequest(url, method)
	r.Setting(defaultSetting)
	r.Header("Accept-Encoding", "gzip, deflate")
	if *isjson {
		r.Header("Accept", "application/json")
	} else if form || method == "GET" {
		r.Header("Accept", "*/*")
	} else {
		r.Header("Accept", "application/json")
	}
	for i := range args {
		// Json raws
		strs := strings.Split(args[i], ":=")
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				var j interface{}
				err = json.Unmarshal(content, &j)
				if err != nil {
					log.Fatal("Read from File", strings.TrimLeft(strs[1], "@"), "Unmarshal", err)
				}
				jsonmap[strs[0]] = j
				continue
			}
			jsonmap[strs[0]] = toRealType(strs[1])
			continue
		}
		// Params
		strs = strings.Split(args[i], "=")
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				strs[1] = string(content)
			}
			if form || method == "GET" {
				r.Param(strs[0], strs[1])
			} else {
				jsonmap[strs[0]] = strs[1]
			}
			continue
		}
		// files
		strs = strings.Split(args[i], "@")
		if len(strs) == 2 {
			if !form {
				log.Fatal("file upload only support in forms style: -f=true")
			}
			r.PostFile(strs[0], strs[1])
			continue
		}
		// Headers
		strs = strings.Split(args[i], ":")
		if len(strs) >= 2 {
			if strs[0] == "Host" {
				r.SetHost(strings.Join(strs[1:], ":"))
			}
			r.Header(strs[0], strings.Join(strs[1:], ":"))
			continue
		}
	}
	if !form && len(jsonmap) > 0 {
		r.JsonBody(jsonmap)
	}
	return
}

func formatResponseBody(res *http.Response, httpreq *httplib.BeegoHttpRequest, pretty bool) string {
	body, err := httpreq.Bytes()
	if err != nil {
		log.Fatalln("can't get the url", err)
	}
	fmt.Println("")
	match, err := regexp.MatchString(contentJsonRegex, res.Header.Get("Content-Type"))
	if err != nil {
		log.Fatalln("failed to compile regex", err)
	}
	if pretty && match {
		var output bytes.Buffer
		err := json.Indent(&output, body, "", "  ")
		if err != nil {
			log.Fatal("Response Json Indent: ", err)
		}

		return output.String()
	}

	return string(body)
}
<commit_msg>handle param's values which contains "="<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/astaxie/bat/httplib"
)

var defaultSetting = httplib.BeegoHttpSettings{
	ShowDebug:        true,
	UserAgent:        "bat/" + version,
	ConnectTimeout:   60 * time.Second,
	ReadWriteTimeout: 60 * time.Second,
	Gzip:             true,
	DumpBody:         true,
}

func getHTTP(method string, url string, args []string) (r *httplib.BeegoHttpRequest) {
	r = httplib.NewBeegoRequest(url, method)
	r.Setting(defaultSetting)
	r.Header("Accept-Encoding", "gzip, deflate")
	if *isjson {
		r.Header("Accept", "application/json")
	} else if form || method == "GET" {
		r.Header("Accept", "*/*")
	} else {
		r.Header("Accept", "application/json")
	}
	for i := range args {
		// Json raws
		strs := strings.SplitN(args[i], ":=", 2)
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				var j interface{}
				err = json.Unmarshal(content, &j)
				if err != nil {
					log.Fatal("Read from File", strings.TrimLeft(strs[1], "@"), "Unmarshal", err)
				}
				jsonmap[strs[0]] = j
				continue
			}
			jsonmap[strs[0]] = toRealType(strs[1])
			continue
		}
		// Params
		strs = strings.SplitN(args[i], "=", 2)
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				strs[1] = string(content)
			}
			if form || method == "GET" {
				r.Param(strs[0], strs[1])
			} else {
				jsonmap[strs[0]] = strs[1]
			}
			continue
		}
		// files
		strs = strings.SplitN(args[i], "@", 2)
		if len(strs) == 2 {
			if !form {
				log.Fatal("file upload only support in forms style: -f=true")
			}
			r.PostFile(strs[0], strs[1])
			continue
		}
		// Headers
		strs = strings.Split(args[i], ":")
		if len(strs) >= 2 {
			if strs[0] == "Host" {
				r.SetHost(strings.Join(strs[1:], ":"))
			}
			r.Header(strs[0], strings.Join(strs[1:], ":"))
			continue
		}
	}
	if !form && len(jsonmap) > 0 {
		r.JsonBody(jsonmap)
	}
	return
}

func formatResponseBody(res *http.Response, httpreq *httplib.BeegoHttpRequest, pretty bool) string {
	body, err := httpreq.Bytes()
	if err != nil {
		log.Fatalln("can't get the url", err)
	}
	fmt.Println("")
	match, err := regexp.MatchString(contentJsonRegex, res.Header.Get("Content-Type"))
	if err != nil {
		log.Fatalln("failed to compile regex", err)
	}
	if pretty && match {
		var output bytes.Buffer
		err := json.Indent(&output, body, "", "  ")
		if err != nil {
			log.Fatal("Response Json Indent: ", err)
		}

		return output.String()
	}

	return string(body)
}
<|endoftext|>


<commit_before>package main

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

// Service is a combination of a ServiceHandlerFunc and options, used by the rootServiceHandler
type Service struct {
	fn                ServiceHandlerFunc
	omitClientSession bool
}

// ServiceHandlerFunc defines the layout of a service handler func.. d'oh.
type ServiceHandlerFunc func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (outData interface{}, err error)

var services = map[string]Service{
	// list of services that don not require a clientSession.
	// please keep this list sorted
	"/service/sessionInit":    Service{newSessionInitHandlerFunc(), true},
	"/service/sessionCheck":   Service{newSessionCheckHandlerFunc(), true},
	"/service/sessionDestroy": Service{newSessionDestroyHandlerFunc(), false},
}

// initHTTPServer sets up the http.FileServer and other http services.
func initHTTPServer() {
	// add handlers to http.DefaultServeMux
	// try to serve static files on root uri
	http.Handle("/", http.FileServer(http.Dir("./http-files/")))
	http.HandleFunc("/service/", rootServiceHandler)

	// run http server in goroutine
	go func() {
		port := "8000"
		log.Printf("starting http server on port %s\n", port)
		err := http.ListenAndServe(":"+port, nil)
		if err != nil {
			log.Fatal(err)
		}
	}()
}

func rootServiceHandler(w http.ResponseWriter, r *http.Request) {
	var err error

	// lookup service
	s, exists := services[r.RequestURI]
	if !exists {
		log.Printf("invalid request (404) for service on: %s\n", r.RequestURI)
		http.NotFound(w, r)
		return
	}

	//++ check origin
	//++ check referer
	fmt.Println("check origin")
	fmt.Println("check referer")

	// find ClientSession
	var cs *ClientSession
	if !s.omitClientSession {
		cs, err = getClientSession(r.Header.Get("X-Nulpunt-SessionKey"))
		if err != nil {
			http.Error(w, "forbidden without valid sessionKey", http.StatusForbidden)
			return
		}
		defer cs.done()
	}

	// call actual handler
	outData, err := s.fn(w, r, cs)
	if err != nil {
		log.Printf("error from service %s: %s\n", r.RequestURI, err)
		http.Error(w, "server error", http.StatusInternalServerError)
		return
	}

	// we're responding with json
	w.Header().Add("Content-Type", "application/json")

	// encode response data to json for client
	err = json.NewEncoder(w).Encode(outData)
	if err != nil {
		log.Printf("error encoding service outData to client for service %s: %s\n", r.RequestURI, err)
	}
}

func newSessionInitHandlerFunc() ServiceHandlerFunc {
	type outDataType struct {
		SessionKey string `json:"sessionKey"`
	}

	return func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (interface{}, error) {
		// create a new cs
		newCS := newClientSession()

		// return cs key
		out := &outDataType{
			SessionKey: newCS.key,
		}

		return out, nil
	}
}
func newSessionCheckHandlerFunc() ServiceHandlerFunc {
	type inDataType struct {
		SessionKey string `json:"sessionKey"`
	}

	type outDataType struct {
		Result bool `json:"result"`
	}

	return func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (interface{}, error) {
		// decode input data
		inData := &inDataType{}
		err := json.NewDecoder(r.Body).Decode(inData)
		if err != nil {
			return nil, err
		}

		// new outData
		outData := &outDataType{}

		// get ClientSession
		s, err := getClientSession(inData.SessionKey)
		if err != nil {
			log.Printf("Could not find CS for key %s\n", inData.SessionKey)
			return outData, nil
		}

		// already done
		s.done()

		// session is valid
		outData.Result = true

		// return data
		return outData, nil
	}
}

func newSessionDestroyHandlerFunc() ServiceHandlerFunc {
	type outDataType struct{}

	return func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (interface{}, error) {
		cs.ping <- false //++ TODO: race when this is called more then once
		return nil, nil
	}
}
<commit_msg>Added unix listener<commit_after>package main

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

// Service is a combination of a ServiceHandlerFunc and options, used by the rootServiceHandler
type Service struct {
	fn                ServiceHandlerFunc
	omitClientSession bool
}

// ServiceHandlerFunc defines the layout of a service handler func.. d'oh.
type ServiceHandlerFunc func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (outData interface{}, err error)

var services = map[string]Service{
	// list of services that don not require a clientSession.
	// please keep this list sorted
	"/service/sessionInit":    Service{newSessionInitHandlerFunc(), true},
	"/service/sessionCheck":   Service{newSessionCheckHandlerFunc(), true},
	"/service/sessionDestroy": Service{newSessionDestroyHandlerFunc(), false},
}

// initHTTPServer sets up the http.FileServer and other http services.
func initHTTPServer() {
	// add handlers to http.DefaultServeMux
	// try to serve static files on root uri
	http.Handle("/", http.FileServer(http.Dir("./http-files/")))
	http.HandleFunc("/service/", rootServiceHandler)

	// run http server in goroutine
	go func() {
		port := "8000"
		log.Printf("starting http server on port %s\n", port)
		err := http.ListenAndServe(":"+port, nil)
		if err != nil {
			log.Fatal(err)
		}
	}()

	if flags.UnixSocket {
		go func() {
			socket, err := net.ListenUnix("unix", &net.UnixAddr{"./nulpunt.socket", "unix"})
			if err != nil {
				log.Fatal(err)
			}
			err = http.Serve(socket, nil)
			if err != nil {
				log.Fatal(err)
			}
		}()
	}
}

func rootServiceHandler(w http.ResponseWriter, r *http.Request) {
	var err error

	// lookup service
	s, exists := services[r.RequestURI]
	if !exists {
		log.Printf("invalid request (404) for service on: %s\n", r.RequestURI)
		http.NotFound(w, r)
		return
	}

	//++ check origin
	//++ check referer
	fmt.Println("check origin")
	fmt.Println("check referer")

	// find ClientSession
	var cs *ClientSession
	if !s.omitClientSession {
		cs, err = getClientSession(r.Header.Get("X-Nulpunt-SessionKey"))
		if err != nil {
			http.Error(w, "forbidden without valid sessionKey", http.StatusForbidden)
			return
		}
		defer cs.done()
	}

	// call actual handler
	outData, err := s.fn(w, r, cs)
	if err != nil {
		log.Printf("error from service %s: %s\n", r.RequestURI, err)
		http.Error(w, "server error", http.StatusInternalServerError)
		return
	}

	// we're responding with json
	w.Header().Add("Content-Type", "application/json")

	// encode response data to json for client
	err = json.NewEncoder(w).Encode(outData)
	if err != nil {
		log.Printf("error encoding service outData to client for service %s: %s\n", r.RequestURI, err)
	}
}

func newSessionInitHandlerFunc() ServiceHandlerFunc {
	type outDataType struct {
		SessionKey string `json:"sessionKey"`
	}

	return func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (interface{}, error) {
		// create a new cs
		newCS := newClientSession()

		// return cs key
		out := &outDataType{
			SessionKey: newCS.key,
		}

		return out, nil
	}
}
func newSessionCheckHandlerFunc() ServiceHandlerFunc {
	type inDataType struct {
		SessionKey string `json:"sessionKey"`
	}

	type outDataType struct {
		Result bool `json:"result"`
	}

	return func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (interface{}, error) {
		// decode input data
		inData := &inDataType{}
		err := json.NewDecoder(r.Body).Decode(inData)
		if err != nil {
			return nil, err
		}

		// new outData
		outData := &outDataType{}

		// get ClientSession
		s, err := getClientSession(inData.SessionKey)
		if err != nil {
			log.Printf("Could not find CS for key %s\n", inData.SessionKey)
			return outData, nil
		}

		// already done
		s.done()

		// session is valid
		outData.Result = true

		// return data
		return outData, nil
	}
}

func newSessionDestroyHandlerFunc() ServiceHandlerFunc {
	type outDataType struct{}

	return func(w http.ResponseWriter, r *http.Request, cs *ClientSession) (interface{}, error) {
		cs.ping <- false //++ TODO: race when this is called more then once
		return nil, nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Terraformer Authors.
//
// 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 gcp

import (
	"context"
	"fmt"
	"log"
	"strconv"

	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"

	"cloud.google.com/go/storage"
	"google.golang.org/api/iterator"
)

var GcsAllowEmptyValues = []string{"labels.", "created_before"}

var GcsAdditionalFields = map[string]string{}

type GcsGenerator struct {
	GCPService
}

func (g *GcsGenerator) createResources(bucketIterator *storage.BucketIterator) []terraform_utils.Resource {
	resources := []terraform_utils.Resource{}
	for {
		battrs, err := bucketIterator.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Println("error with bucket:", err)
			continue
		}
		resources = append(resources, terraform_utils.NewResource(
			battrs.Name,
			battrs.Name,
			"google_storage_bucket",
			"google",
			map[string]string{
				"name":          battrs.Name,
				"force_destroy": "false",
			},
			GcsAllowEmptyValues,
			GcsAdditionalFields,
		))
		resources = append(resources, terraform_utils.NewResource(
			battrs.Name,
			battrs.Name,
			"google_storage_bucket_acl",
			"google",
			map[string]string{
				"bucket":        battrs.Name,
				"role_entity.#": strconv.Itoa(len(battrs.ACL)),
			},
			GcsAllowEmptyValues,
			GcsAdditionalFields,
		))
		resources = append(resources, terraform_utils.NewResource(
			battrs.Name,
			battrs.Name,
			"google_storage_bucket_iam_binding",
			"google",
			map[string]string{
				"bucket": battrs.Name,
			},
			GcsAllowEmptyValues,
			GcsAdditionalFields,
		))
		resources = append(resources, terraform_utils.NewResource(
			battrs.Name,
			battrs.Name,
			"google_storage_bucket_iam_member",
			"google",
			map[string]string{
				"bucket": battrs.Name,
			},
			GcsAllowEmptyValues,
			GcsAdditionalFields,
		))
		resources = append(resources, terraform_utils.NewResource(
			battrs.Name,
			battrs.Name,
			"google_storage_bucket_iam_policy",
			"google",
			map[string]string{
				"bucket": battrs.Name,
			},
			GcsAllowEmptyValues,
			GcsAdditionalFields,
		))
	}
	return resources
}

// Generate TerraformResources from GCP API,
// from each bucket  create 1 TerraformResource
// Need bucket name as ID for terraform resource
func (g *GcsGenerator) InitResources() error {
	ctx := context.Background()

	projectID := g.GetArgs()["project"]
	client, err := storage.NewClient(ctx)
	if err != nil {
		log.Print(err)
		return err
	}
	bucketIterator := client.Buckets(ctx, projectID)

	g.Resources = g.createResources(bucketIterator)
	g.PopulateIgnoreKeys()
	return nil
}

// PostGenerateHook for add bucket policy json as heredoc
// support only bucket with policy
func (g *GcsGenerator) PostConvertHook() error {
	for i, resource := range g.Resources {
		if resource.InstanceInfo.Type != "google_storage_bucket_iam_policy" {
			continue
		}
		policy := resource.Item["policy_data"].(string)
		g.Resources[i].Item["policy_data"] = fmt.Sprintf(`<<POLICY
%s
POLICY`, policy)
	}
	return nil
}
<commit_msg>add support google_storage_notification,google_storage_default_object_acl + refactor gcs<commit_after>// Copyright 2018 The Terraformer Authors.
//
// 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 gcp

import (
	"context"
	"fmt"
	"log"
	"strconv"

	"golang.org/x/oauth2/google"

	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"

	"google.golang.org/api/storage/v1"
)

var GcsAllowEmptyValues = []string{"labels.", "created_before"}

var GcsAdditionalFields = map[string]string{}

type GcsGenerator struct {
	GCPService
}

func (g *GcsGenerator) createBucketsResources(ctx context.Context, gcsService *storage.Service) []terraform_utils.Resource {
	resources := []terraform_utils.Resource{}
	bucketList := gcsService.Buckets.List(g.GetArgs()["project"])
	if err := bucketList.Pages(ctx, func(page *storage.Buckets) error {
		for _, bucket := range page.Items {
			resources = append(resources, terraform_utils.NewResource(
				bucket.Name,
				bucket.Name,
				"google_storage_bucket",
				"google",
				map[string]string{
					"name":          bucket.Name,
					"force_destroy": "false",
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
			resources = append(resources, terraform_utils.NewResource(
				bucket.Name,
				bucket.Name,
				"google_storage_bucket_acl",
				"google",
				map[string]string{
					"bucket":        bucket.Name,
					"role_entity.#": strconv.Itoa(len(bucket.Acl)),
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
			resources = append(resources, terraform_utils.NewResource(
				bucket.Name,
				bucket.Name,
				"google_storage_default_object_acl",
				"google",
				map[string]string{
					"bucket":        bucket.Name,
					"role_entity.#": strconv.Itoa(len(bucket.Acl)),
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
			resources = append(resources, terraform_utils.NewResource(
				bucket.Name,
				bucket.Name,
				"google_storage_bucket_iam_binding",
				"google",
				map[string]string{
					"bucket": bucket.Name,
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
			resources = append(resources, terraform_utils.NewResource(
				bucket.Name,
				bucket.Name,
				"google_storage_bucket_iam_member",
				"google",
				map[string]string{
					"bucket": bucket.Name,
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
			resources = append(resources, terraform_utils.NewResource(
				bucket.Name,
				bucket.Name,
				"google_storage_bucket_iam_policy",
				"google",
				map[string]string{
					"bucket": bucket.Name,
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
			resources = append(resources, g.createNotificationResources(ctx, gcsService, bucket)...)
		}
		return nil
	}); err != nil {
		log.Fatal(err)
	}
	return resources
}

func (g *GcsGenerator) createNotificationResources(ctx context.Context, gcsService *storage.Service, bucket *storage.Bucket) []terraform_utils.Resource {
	resources := []terraform_utils.Resource{}
	notificationList, err := gcsService.Notifications.List(bucket.Name).Do()
	if err != nil {
		log.Fatal(err)
		return resources
	}
	for _, notification := range notificationList.Items {
		resources = append(resources, terraform_utils.NewResource(
			bucket.Name+"/notificationConfigs/"+notification.Id,
			bucket.Name+"/"+notification.Id,
			"google_storage_notification",
			"google",
			map[string]string{},
			GcsAllowEmptyValues,
			GcsAdditionalFields,
		))
	}
	return resources
}

/*
func (g *GcsGenerator) createTransferJobsResources(ctx context.Context, storageTransferService *storagetransfer.Service) []terraform_utils.Resource {
	resources := []terraform_utils.Resource{}
	transferJobsList := storageTransferService.TransferJobs.List()
	err := transferJobsList.Pages(ctx, func(page *storagetransfer.ListTransferJobsResponse) error {
		log.Println(page.TransferJobs)
		for _, transferJob := range page.TransferJobs {
			resources = append(resources, terraform_utils.NewResource(
				transferJob.Name,
				transferJob.Name,
				"google_storage_transfer_job",
				"google",
				map[string]string{
					"name": transferJob.Name,
				},
				GcsAllowEmptyValues,
				GcsAdditionalFields,
			))
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
	return resources
}
*/

// Generate TerraformResources from GCP API,
// from each bucket  create 1 TerraformResource
// Need bucket name as ID for terraform resource
func (g *GcsGenerator) InitResources() error {
	ctx := context.Background()
	c, err := google.DefaultClient(ctx, storage.CloudPlatformReadOnlyScope)
	if err != nil {
		log.Fatal(err)
	}

	gcsService, err := storage.New(c)
	if err != nil {
		log.Print(err)
		return err
	}
	g.Resources = g.createBucketsResources(ctx, gcsService)

	// TODO find bug with storageTransferService.TransferJobs.List().Pages
	//storageTransferService, err := storagetransfer.New(c)
	//if err != nil {
	//	log.Print(err)
	//		return err
	//	}
	//g.Resources = append(g.Resources, g.createTransferJobsResources(ctx, storageTransferService)...)
	g.PopulateIgnoreKeys()
	return nil
}

// PostGenerateHook for add bucket policy json as heredoc
// support only bucket with policy
func (g *GcsGenerator) PostConvertHook() error {
	for i, resource := range g.Resources {
		if resource.InstanceInfo.Type != "google_storage_bucket_iam_policy" {
			continue
		}
		policy := resource.Item["policy_data"].(string)
		g.Resources[i].Item["policy_data"] = fmt.Sprintf(`<<POLICY
%s
POLICY`, policy)
	}
	return nil
}
<|endoftext|>


<commit_before>package socks

import (
	"errors"
	"io"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/v2ray/v2ray-core"
	"github.com/v2ray/v2ray-core/common/alloc"
	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/proxy"
	jsonconfig "github.com/v2ray/v2ray-core/proxy/socks/config/json"
	"github.com/v2ray/v2ray-core/proxy/socks/protocol"
)

var (
	UnsupportedSocksCommand = errors.New("Unsupported socks command.")
	UnsupportedAuthMethod   = errors.New("Unsupported auth method.")
)

// SocksServer is a SOCKS 5 proxy server
type SocksServer struct {
	accepting bool
	vPoint    *core.Point
	config    *jsonconfig.SocksConfig
}

func NewSocksServer(vp *core.Point, config *jsonconfig.SocksConfig) *SocksServer {
	return &SocksServer{
		vPoint: vp,
		config: config,
	}
}

func (server *SocksServer) Listen(port uint16) error {
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		log.Error("Socks failed to listen on port %d: %v", port, err)
		return err
	}
	server.accepting = true
	go server.AcceptConnections(listener)
	if server.config.UDPEnabled {
		server.ListenUDP(port)
	}
	return nil
}

func (server *SocksServer) AcceptConnections(listener net.Listener) {
	for server.accepting {
		connection, err := listener.Accept()
		if err != nil {
			log.Error("Socks failed to accept new connection %v", err)
			continue
		}
		go server.HandleConnection(connection)
	}
}

func (server *SocksServer) HandleConnection(connection net.Conn) error {
	defer connection.Close()

	reader := v2net.NewTimeOutReader(120, connection)

	auth, auth4, err := protocol.ReadAuthentication(reader)
	if err != nil && err != protocol.Socks4Downgrade {
		log.Error("Socks failed to read authentication: %v", err)
		return err
	}

	if err != nil && err == protocol.Socks4Downgrade {
		return server.handleSocks4(reader, connection, auth4)
	} else {
		return server.handleSocks5(reader, connection, auth)
	}
}

func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.Writer, auth protocol.Socks5AuthenticationRequest) error {
	expectedAuthMethod := protocol.AuthNotRequired
	if server.config.IsPassword() {
		expectedAuthMethod = protocol.AuthUserPass
	}

	if !auth.HasAuthMethod(expectedAuthMethod) {
		authResponse := protocol.NewAuthenticationResponse(protocol.AuthNoMatchingMethod)
		err := protocol.WriteAuthentication(writer, authResponse)
		if err != nil {
			log.Error("Socks failed to write authentication: %v", err)
			return err
		}
		log.Warning("Socks client doesn't support allowed any auth methods.")
		return UnsupportedAuthMethod
	}

	authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
	err := protocol.WriteAuthentication(writer, authResponse)
	if err != nil {
		log.Error("Socks failed to write authentication: %v", err)
		return err
	}
	if server.config.IsPassword() {
		upRequest, err := protocol.ReadUserPassRequest(reader)
		if err != nil {
			log.Error("Socks failed to read username and password: %v", err)
			return err
		}
		status := byte(0)
		if !server.config.HasAccount(upRequest.Username(), upRequest.Password()) {
			status = byte(0xFF)
		}
		upResponse := protocol.NewSocks5UserPassResponse(status)
		err = protocol.WriteUserPassResponse(writer, upResponse)
		if err != nil {
			log.Error("Socks failed to write user pass response: %v", err)
			return err
		}
		if status != byte(0) {
			log.Warning("Invalid user account: %s", upRequest.AuthDetail())
			return proxy.InvalidAuthentication
		}
	}

	request, err := protocol.ReadRequest(reader)
	if err != nil {
		log.Error("Socks failed to read request: %v", err)
		return err
	}

	if request.Command == protocol.CmdUdpAssociate && server.config.UDPEnabled {
		return server.handleUDP(reader, writer)
	}

	response := protocol.NewSocks5Response()
	if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate {
		response := protocol.NewSocks5Response()
		response.Error = protocol.ErrorCommandNotSupported

		responseBuffer := alloc.NewSmallBuffer().Clear()
		response.Write(responseBuffer)
		_, err = writer.Write(responseBuffer.Value)
		responseBuffer.Release()
		if err != nil {
			log.Error("Socks failed to write response: %v", err)
			return err
		}
		log.Warning("Unsupported socks command %d", request.Command)
		return UnsupportedSocksCommand
	}

	response.Error = protocol.ErrorSuccess

	// Some SOCKS software requires a value other than dest. Let's fake one:
	response.Port = uint16(1717)
	response.AddrType = protocol.AddrTypeIPv4
	response.IPv4[0] = 0
	response.IPv4[1] = 0
	response.IPv4[2] = 0
	response.IPv4[3] = 0

	responseBuffer := alloc.NewSmallBuffer().Clear()
	response.Write(responseBuffer)
	_, err = writer.Write(responseBuffer.Value)
	responseBuffer.Release()
	if err != nil {
		log.Error("Socks failed to write response: %v", err)
		return err
	}

	dest := request.Destination()
	data, err := v2net.ReadFrom(reader, nil)
	if err != nil {
		return err
	}

	packet := v2net.NewPacket(dest, data, true)
	server.transport(reader, writer, packet)
	return nil
}

func (server *SocksServer) handleUDP(reader *v2net.TimeOutReader, writer io.Writer) error {
	response := protocol.NewSocks5Response()
	response.Error = protocol.ErrorSuccess

	udpAddr := server.getUDPAddr()

	response.Port = udpAddr.Port()
	switch {
	case udpAddr.IsIPv4():
		response.AddrType = protocol.AddrTypeIPv4
		copy(response.IPv4[:], udpAddr.IP())
	case udpAddr.IsIPv6():
		response.AddrType = protocol.AddrTypeIPv6
		copy(response.IPv6[:], udpAddr.IP())
	case udpAddr.IsDomain():
		response.AddrType = protocol.AddrTypeDomain
		response.Domain = udpAddr.Domain()
	}

	responseBuffer := alloc.NewSmallBuffer().Clear()
	response.Write(responseBuffer)
	_, err := writer.Write(responseBuffer.Value)
	responseBuffer.Release()

	if err != nil {
		log.Error("Socks failed to write response: %v", err)
		return err
	}

	reader.SetTimeOut(300)      /* 5 minutes */
	v2net.ReadFrom(reader, nil) // Just in case of anything left in the socket
	// The TCP connection closes after this method returns. We need to wait until
	// the client closes it.
	// TODO: get notified from UDP part
	<-time.After(5 * time.Minute)

	return nil
}

func (server *SocksServer) handleSocks4(reader io.Reader, writer io.Writer, auth protocol.Socks4AuthenticationRequest) error {
	result := protocol.Socks4RequestGranted
	if auth.Command == protocol.CmdBind {
		result = protocol.Socks4RequestRejected
	}
	socks4Response := protocol.NewSocks4AuthenticationResponse(result, auth.Port, auth.IP[:])

	responseBuffer := alloc.NewSmallBuffer().Clear()
	socks4Response.Write(responseBuffer)
	writer.Write(responseBuffer.Value)
	responseBuffer.Release()

	if result == protocol.Socks4RequestRejected {
		log.Warning("Unsupported socks 4 command %d", auth.Command)
		return UnsupportedSocksCommand
	}

	dest := v2net.NewTCPDestination(v2net.IPAddress(auth.IP[:], auth.Port))
	data, err := v2net.ReadFrom(reader, nil)
	if err != nil {
		return err
	}

	packet := v2net.NewPacket(dest, data, true)
	server.transport(reader, writer, packet)
	return nil
}

func (server *SocksServer) transport(reader io.Reader, writer io.Writer, firstPacket v2net.Packet) {
	ray := server.vPoint.DispatchToOutbound(firstPacket)
	input := ray.InboundInput()
	output := ray.InboundOutput()

	var inputFinish, outputFinish sync.Mutex
	inputFinish.Lock()
	outputFinish.Lock()

	go dumpInput(reader, input, &inputFinish)
	go dumpOutput(writer, output, &outputFinish)
	outputFinish.Lock()
}

func dumpInput(reader io.Reader, input chan<- *alloc.Buffer, finish *sync.Mutex) {
	v2net.ReaderToChan(input, reader)
	finish.Unlock()
	close(input)
}

func dumpOutput(writer io.Writer, output <-chan *alloc.Buffer, finish *sync.Mutex) {
	v2net.ChanToWriter(writer, output)
	finish.Unlock()
}
<commit_msg>Lazy initialize of socks5 response<commit_after>package socks

import (
	"errors"
	"io"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/v2ray/v2ray-core"
	"github.com/v2ray/v2ray-core/common/alloc"
	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/proxy"
	jsonconfig "github.com/v2ray/v2ray-core/proxy/socks/config/json"
	"github.com/v2ray/v2ray-core/proxy/socks/protocol"
)

var (
	UnsupportedSocksCommand = errors.New("Unsupported socks command.")
	UnsupportedAuthMethod   = errors.New("Unsupported auth method.")
)

// SocksServer is a SOCKS 5 proxy server
type SocksServer struct {
	accepting bool
	vPoint    *core.Point
	config    *jsonconfig.SocksConfig
}

func NewSocksServer(vp *core.Point, config *jsonconfig.SocksConfig) *SocksServer {
	return &SocksServer{
		vPoint: vp,
		config: config,
	}
}

func (server *SocksServer) Listen(port uint16) error {
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		log.Error("Socks failed to listen on port %d: %v", port, err)
		return err
	}
	server.accepting = true
	go server.AcceptConnections(listener)
	if server.config.UDPEnabled {
		server.ListenUDP(port)
	}
	return nil
}

func (server *SocksServer) AcceptConnections(listener net.Listener) {
	for server.accepting {
		connection, err := listener.Accept()
		if err != nil {
			log.Error("Socks failed to accept new connection %v", err)
			continue
		}
		go server.HandleConnection(connection)
	}
}

func (server *SocksServer) HandleConnection(connection net.Conn) error {
	defer connection.Close()

	reader := v2net.NewTimeOutReader(120, connection)

	auth, auth4, err := protocol.ReadAuthentication(reader)
	if err != nil && err != protocol.Socks4Downgrade {
		log.Error("Socks failed to read authentication: %v", err)
		return err
	}

	if err != nil && err == protocol.Socks4Downgrade {
		return server.handleSocks4(reader, connection, auth4)
	} else {
		return server.handleSocks5(reader, connection, auth)
	}
}

func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.Writer, auth protocol.Socks5AuthenticationRequest) error {
	expectedAuthMethod := protocol.AuthNotRequired
	if server.config.IsPassword() {
		expectedAuthMethod = protocol.AuthUserPass
	}

	if !auth.HasAuthMethod(expectedAuthMethod) {
		authResponse := protocol.NewAuthenticationResponse(protocol.AuthNoMatchingMethod)
		err := protocol.WriteAuthentication(writer, authResponse)
		if err != nil {
			log.Error("Socks failed to write authentication: %v", err)
			return err
		}
		log.Warning("Socks client doesn't support allowed any auth methods.")
		return UnsupportedAuthMethod
	}

	authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
	err := protocol.WriteAuthentication(writer, authResponse)
	if err != nil {
		log.Error("Socks failed to write authentication: %v", err)
		return err
	}
	if server.config.IsPassword() {
		upRequest, err := protocol.ReadUserPassRequest(reader)
		if err != nil {
			log.Error("Socks failed to read username and password: %v", err)
			return err
		}
		status := byte(0)
		if !server.config.HasAccount(upRequest.Username(), upRequest.Password()) {
			status = byte(0xFF)
		}
		upResponse := protocol.NewSocks5UserPassResponse(status)
		err = protocol.WriteUserPassResponse(writer, upResponse)
		if err != nil {
			log.Error("Socks failed to write user pass response: %v", err)
			return err
		}
		if status != byte(0) {
			log.Warning("Invalid user account: %s", upRequest.AuthDetail())
			return proxy.InvalidAuthentication
		}
	}

	request, err := protocol.ReadRequest(reader)
	if err != nil {
		log.Error("Socks failed to read request: %v", err)
		return err
	}

	if request.Command == protocol.CmdUdpAssociate && server.config.UDPEnabled {
		return server.handleUDP(reader, writer)
	}

	if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate {
		response := protocol.NewSocks5Response()
		response.Error = protocol.ErrorCommandNotSupported

		responseBuffer := alloc.NewSmallBuffer().Clear()
		response.Write(responseBuffer)
		_, err = writer.Write(responseBuffer.Value)
		responseBuffer.Release()
		if err != nil {
			log.Error("Socks failed to write response: %v", err)
			return err
		}
		log.Warning("Unsupported socks command %d", request.Command)
		return UnsupportedSocksCommand
	}

  response := protocol.NewSocks5Response()
	response.Error = protocol.ErrorSuccess

	// Some SOCKS software requires a value other than dest. Let's fake one:
	response.Port = uint16(1717)
	response.AddrType = protocol.AddrTypeIPv4
	response.IPv4[0] = 0
	response.IPv4[1] = 0
	response.IPv4[2] = 0
	response.IPv4[3] = 0

	responseBuffer := alloc.NewSmallBuffer().Clear()
	response.Write(responseBuffer)
	_, err = writer.Write(responseBuffer.Value)
	responseBuffer.Release()
	if err != nil {
		log.Error("Socks failed to write response: %v", err)
		return err
	}

	dest := request.Destination()
	data, err := v2net.ReadFrom(reader, nil)
	if err != nil {
		return err
	}

	packet := v2net.NewPacket(dest, data, true)
	server.transport(reader, writer, packet)
	return nil
}

func (server *SocksServer) handleUDP(reader *v2net.TimeOutReader, writer io.Writer) error {
	response := protocol.NewSocks5Response()
	response.Error = protocol.ErrorSuccess

	udpAddr := server.getUDPAddr()

	response.Port = udpAddr.Port()
	switch {
	case udpAddr.IsIPv4():
		response.AddrType = protocol.AddrTypeIPv4
		copy(response.IPv4[:], udpAddr.IP())
	case udpAddr.IsIPv6():
		response.AddrType = protocol.AddrTypeIPv6
		copy(response.IPv6[:], udpAddr.IP())
	case udpAddr.IsDomain():
		response.AddrType = protocol.AddrTypeDomain
		response.Domain = udpAddr.Domain()
	}

	responseBuffer := alloc.NewSmallBuffer().Clear()
	response.Write(responseBuffer)
	_, err := writer.Write(responseBuffer.Value)
	responseBuffer.Release()

	if err != nil {
		log.Error("Socks failed to write response: %v", err)
		return err
	}

	reader.SetTimeOut(300)      /* 5 minutes */
	v2net.ReadFrom(reader, nil) // Just in case of anything left in the socket
	// The TCP connection closes after this method returns. We need to wait until
	// the client closes it.
	// TODO: get notified from UDP part
	<-time.After(5 * time.Minute)

	return nil
}

func (server *SocksServer) handleSocks4(reader io.Reader, writer io.Writer, auth protocol.Socks4AuthenticationRequest) error {
	result := protocol.Socks4RequestGranted
	if auth.Command == protocol.CmdBind {
		result = protocol.Socks4RequestRejected
	}
	socks4Response := protocol.NewSocks4AuthenticationResponse(result, auth.Port, auth.IP[:])

	responseBuffer := alloc.NewSmallBuffer().Clear()
	socks4Response.Write(responseBuffer)
	writer.Write(responseBuffer.Value)
	responseBuffer.Release()

	if result == protocol.Socks4RequestRejected {
		log.Warning("Unsupported socks 4 command %d", auth.Command)
		return UnsupportedSocksCommand
	}

	dest := v2net.NewTCPDestination(v2net.IPAddress(auth.IP[:], auth.Port))
	data, err := v2net.ReadFrom(reader, nil)
	if err != nil {
		return err
	}

	packet := v2net.NewPacket(dest, data, true)
	server.transport(reader, writer, packet)
	return nil
}

func (server *SocksServer) transport(reader io.Reader, writer io.Writer, firstPacket v2net.Packet) {
	ray := server.vPoint.DispatchToOutbound(firstPacket)
	input := ray.InboundInput()
	output := ray.InboundOutput()

	var inputFinish, outputFinish sync.Mutex
	inputFinish.Lock()
	outputFinish.Lock()

	go dumpInput(reader, input, &inputFinish)
	go dumpOutput(writer, output, &outputFinish)
	outputFinish.Lock()
}

func dumpInput(reader io.Reader, input chan<- *alloc.Buffer, finish *sync.Mutex) {
	v2net.ReaderToChan(input, reader)
	finish.Unlock()
	close(input)
}

func dumpOutput(writer io.Writer, output <-chan *alloc.Buffer, finish *sync.Mutex) {
	v2net.ChanToWriter(writer, output)
	finish.Unlock()
}
<|endoftext|>


<commit_before>package vcfgo

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
)

type InfoByte struct {
	Info []byte

	header *Header
}

func NewInfoByte(info []byte, h *Header) *InfoByte {
	if len(info) == 1 && info[0] == '.' {
		return &InfoByte{Info: []byte(""), header: h}
	}
	return &InfoByte{Info: info, header: h}
}

/*
func bytesToString(b []byte) string {
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sh := reflect.StringHeader{bh.Data, bh.Len}
	return *(*string)(unsafe.Pointer(&sh))
}
*/

// return the start and end positions of the value.
// for flag the value is the flag.
func getpositions(info []byte, key string) (start, end int) {
	//if ipos := strings.LastIndex(bytesToString(info), ";"+key+"="); ipos != -1 {
	if ipos := bytes.Index(info, []byte(";"+key+"=")); ipos != -1 {
		start = ipos + 2 + len(key)
		for end := start + 1; end < len(info)-1; end++ {
			if info[end] == ';' {
				return start, end - 1
			}
		}
		return start, len(info) - 1
	}

	bkey := []byte(key)
	ipos, pos := 0, 0

	for {
		if pos >= len(info) {
			return -1, -1
		}
		if pos == 0 {
			ipos = bytes.Index(info, bkey)
		} else {
			ipos = bytes.Index(info[pos:], bkey)
		}

		if ipos == -1 {
			return -1, -1
		}
		pos += ipos
		if pos != 0 && info[pos-1] != ';' {
			pos += 1
			continue
		}
		eq := pos + bytes.IndexByte(info[pos:], '=')
		// at end of field and we found an Flag
		var semi int
		if eq == -1 {
			return pos, len(info)
		} else if eq-pos != len(bkey) {
			// found a longer key with same prefix.
			semi = bytes.IndexByte(info[pos:], ';')
			// flag field
			if semi == -1 {
				semi = len(info)
			} else {
				semi += pos
			}
			if semi-pos == len(bkey) {
				return pos, semi - 1
			}
			pos = semi + 1
			continue
		} else {
			semi = bytes.IndexByte(info[pos+1:], byte(';'))
		}
		if semi > -1 && eq > pos+semi {
			// should be a flag.
			return pos, pos + semi
		}

		// not at end of info field
		if semi != -1 {
			semi += pos
		}
		return eq + 1, semi
	}
}

func (i InfoByte) Contains(key string) bool {
	// short-circuit common case.
	if !bytes.Contains(i.Info, []byte(key+"=")) {
		return false
	}
	s, _ := getpositions(i.Info, key)
	return s != -1
}

func (i InfoByte) Keys() []string {
	sp := bytes.Split(i.Info, []byte{';'})
	keys := make([]string, 0, len(sp))
	for _, pair := range sp {
		key := bytes.SplitN(pair, []byte{'='}, 2)[0]
		keys = append(keys, string(key))
	}
	return keys

}

func (i *InfoByte) Delete(key string) {
	s, e := getpositions(i.Info, key)
	if s == -1 {
		return
	}
	// check if it's a flag
	if s != 0 && i.Info[s-1] != ';' {
		s -= (len(key) + 1)
	}
	if s < 0 {
		s = 0
	}
	if e == -1 {
		e = len(i.Info)
	} else {
		e += 2
	}
	if s == 0 && e == len(i.Info) {
		i.Info = i.Info[:0]
	} else if e < len(i.Info) {
		i.Info = append(i.Info[:s], i.Info[e:]...)
	} else {
		i.Info = i.Info[:s-1]
	}
}

func ItoS(k string, v interface{}) string {
	if _, ok := v.(bool); ok {
		return k
	} else {
		switch v.(type) {
		case []interface{}:
			vs := v.([]interface{})
			out := make([]string, len(vs))
			for i, vv := range vs {
				out[i] = ItoS(k, vv)
			}
			return strings.Join(out, ",")
		case float32:
			return fmtFloat32(v.(float32))
		case float64:
			return fmtFloat64(v.(float64))
		case int:
			return fmt.Sprintf("%d", v.(int))
		case uint32:
			return fmt.Sprintf("%d", v.(uint32))
		case []float64:
			vals := v.([]float64)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = fmtFloat64(float64(val))
			}
			return strings.Join(svals, ",")
		case []float32:
			vals := v.([]float32)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = fmtFloat32(float32(val))
			}
			return strings.Join(svals, ",")
		case []int:
			vals := v.([]int)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = strconv.Itoa(int(val))
			}
			return strings.Join(svals, ",")
		case []int32:
			vals := v.([]int32)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = strconv.Itoa(int(int32(val)))
			}
			return strings.Join(svals, ",")
		case []string:
			return strings.Join(v.([]string), ",")

		default:
			return v.(string)
		}
	}
}

func (i InfoByte) SGet(key string) []byte {
	var sub []byte
	if key == "" || len(i.Info) == 1 {
		return sub
	}
	start, end := getpositions(i.Info, key)
	if start == -1 {
		return sub
	}
	if end == -1 {
		end = len(i.Info) - 1
	}
	val := i.Info[start : end+1]
	return val
}

// Get a value from the bytes typed according to the header.
func (i InfoByte) Get(key string) (interface{}, error) {
	v := string(i.SGet(key))
	skey := string(key)
	var ok bool
	var hi *Info
	if i.header == nil || i.header.Infos == nil {
		ok = false
	} else {
		i.header.RLock()
		hi, ok = i.header.Infos[key]
		i.header.RUnlock()
	}
	if !ok {
		err := fmt.Errorf("Info Error: %s not found in header", skey)
		// flag
		if skey == v {
			return true, err
		}
		if v == "" {
			return nil, err
		}
		return v, err
	}

	if v == "" {
		var err error
		var val interface{} = nil
		if hi.Type != "Flag" {
			err = fmt.Errorf("Info Error: %s not found in INFO", skey)
		} else {
			val = false
		}
		return val, err
	}
	if v == key {
		var err error
		if hi.Type != "Flag" {
			err = fmt.Errorf("Info Error: flag field (%s) should be specified as such in the header", skey)
		}
		return true, err
	}

	var err error
	var iv interface{}
	if hi.Number == "1" || (hi.Number == "." && strings.IndexByte(v, ',') == -1) {
		return parseOne(skey, v, hi.Type)
	}

	switch hi.Number {

	case "0":
		if hi.Type != "Flag" {
			err = fmt.Errorf("Info Error: flag field (%s) should have Number=0", skey)
		}
		return true, err

	case "R", "A", "G", "2", "3", ".":
		vals := strings.Split(v, ",")
		var vi interface{}
		switch hi.Type {
		case "Integer":
			vi = make([]int, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]int)[j] = iv.(int)
			}
		case "Float":
			vi = make([]float32, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				if o, ok := iv.(float32); ok {
					vi.([]float32)[j] = o
				} else {
					vi.([]float32)[j] = float32(iv.(float64))
				}

			}
		case "String":
			vi = make([]string, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]string)[j] = iv.(string)
			}
		default:
			vi = make([]interface{}, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]interface{})[j] = iv
			}
		}
		return vi, nil

	default:
		vals := strings.Split(v, ",")
		var vi interface{} = make([]interface{}, len(vals))
		if _, err := strconv.Atoi(hi.Number); err == nil {
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]interface{})[j] = iv
			}
		}
		return vi, err

	}
}

func (i InfoByte) String() string {
	return string(i.Info)
}

func (i InfoByte) Bytes() []byte {
	return i.Info
}

func (i *InfoByte) UpdateHeader(key string, value interface{}) {
	i.header.Lock()
	defer i.header.Unlock()
	if i.header != nil {
		switch value.(type) {
		case bool:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "0", Type: "Flag"}
		case string:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "1", Type: "Character"}
		case int, int32, int64, uint32, uint64:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "1", Type: "Integer"}
		case float32, float64:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "1", Type: "Float"}
		case []interface{}:
			v := value.([]interface{})[0]
			i.UpdateHeader(key, v)
		}
	}
}

func (i *InfoByte) Set(key string, value interface{}) error {
	if len(i.Info) == 0 {
		if v, ok := value.(bool); ok {
			if v {
				i.Info = []byte(key)
			}
		} else {
			i.Info = []byte(fmt.Sprintf("%s=%s", key, ItoS(key, value)))
		}
		return nil
	}
	s, e := getpositions(i.Info, key)
	if s == -1 || s == len(i.Info) {
		if b, ok := value.(bool); ok {
			if b {
				i.Info = append(i.Info, ';')
				i.Info = append(i.Info, key...)
			}
			return nil
		}
		slug := fmt.Sprintf(";%s=%s", key, ItoS(key, value))
		i.Info = append(i.Info, slug...)
		//i.UpdateHeader(key, value)
		return nil
	}
	if b, ok := value.(bool); ok {
		if !b {
			i.Delete(key)
		}
		return nil
	}
	slug := []byte(ItoS(key, value))
	if e == -1 {
		i.Info = append(i.Info[:s], slug...)
	} else {
		i.Info = append(i.Info[:s], append(slug, i.Info[e+1:]...)...)
	}
	return nil
}

func (i *InfoByte) Add(key string, value interface{}) {
	i.Set(key, value)
}
<commit_msg>String() nil -> "."<commit_after>package vcfgo

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
)

type InfoByte struct {
	Info []byte

	header *Header
}

func NewInfoByte(info []byte, h *Header) *InfoByte {
	if len(info) == 1 && info[0] == '.' {
		return &InfoByte{Info: []byte(""), header: h}
	}
	return &InfoByte{Info: info, header: h}
}

/*
func bytesToString(b []byte) string {
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sh := reflect.StringHeader{bh.Data, bh.Len}
	return *(*string)(unsafe.Pointer(&sh))
}
*/

// return the start and end positions of the value.
// for flag the value is the flag.
func getpositions(info []byte, key string) (start, end int) {
	//if ipos := strings.LastIndex(bytesToString(info), ";"+key+"="); ipos != -1 {
	if ipos := bytes.Index(info, []byte(";"+key+"=")); ipos != -1 {
		start = ipos + 2 + len(key)
		for end := start + 1; end < len(info)-1; end++ {
			if info[end] == ';' {
				return start, end - 1
			}
		}
		return start, len(info) - 1
	}

	bkey := []byte(key)
	ipos, pos := 0, 0

	for {
		if pos >= len(info) {
			return -1, -1
		}
		if pos == 0 {
			ipos = bytes.Index(info, bkey)
		} else {
			ipos = bytes.Index(info[pos:], bkey)
		}

		if ipos == -1 {
			return -1, -1
		}
		pos += ipos
		if pos != 0 && info[pos-1] != ';' {
			pos += 1
			continue
		}
		eq := pos + bytes.IndexByte(info[pos:], '=')
		// at end of field and we found an Flag
		var semi int
		if eq == -1 {
			return pos, len(info)
		} else if eq-pos != len(bkey) {
			// found a longer key with same prefix.
			semi = bytes.IndexByte(info[pos:], ';')
			// flag field
			if semi == -1 {
				semi = len(info)
			} else {
				semi += pos
			}
			if semi-pos == len(bkey) {
				return pos, semi - 1
			}
			pos = semi + 1
			continue
		} else {
			semi = bytes.IndexByte(info[pos+1:], byte(';'))
		}
		if semi > -1 && eq > pos+semi {
			// should be a flag.
			return pos, pos + semi
		}

		// not at end of info field
		if semi != -1 {
			semi += pos
		}
		return eq + 1, semi
	}
}

func (i InfoByte) Contains(key string) bool {
	// short-circuit common case.
	if !bytes.Contains(i.Info, []byte(key+"=")) {
		return false
	}
	s, _ := getpositions(i.Info, key)
	return s != -1
}

func (i InfoByte) Keys() []string {
	sp := bytes.Split(i.Info, []byte{';'})
	keys := make([]string, 0, len(sp))
	for _, pair := range sp {
		key := bytes.SplitN(pair, []byte{'='}, 2)[0]
		keys = append(keys, string(key))
	}
	return keys

}

func (i *InfoByte) Delete(key string) {
	s, e := getpositions(i.Info, key)
	if s == -1 {
		return
	}
	// check if it's a flag
	if s != 0 && i.Info[s-1] != ';' {
		s -= (len(key) + 1)
	}
	if s < 0 {
		s = 0
	}
	if e == -1 {
		e = len(i.Info)
	} else {
		e += 2
	}
	if s == 0 && e == len(i.Info) {
		i.Info = i.Info[:0]
	} else if e < len(i.Info) {
		i.Info = append(i.Info[:s], i.Info[e:]...)
	} else {
		i.Info = i.Info[:s-1]
	}
}

func ItoS(k string, v interface{}) string {
	if _, ok := v.(bool); ok {
		return k
	} else if v == nil {
		return "."
	} else {
		switch v.(type) {
		case []interface{}:
			vs := v.([]interface{})
			out := make([]string, len(vs))
			for i, vv := range vs {
				out[i] = ItoS(k, vv)
			}
			return strings.Join(out, ",")
		case float32:
			return fmtFloat32(v.(float32))
		case float64:
			return fmtFloat64(v.(float64))
		case int:
			return fmt.Sprintf("%d", v.(int))
		case uint32:
			return fmt.Sprintf("%d", v.(uint32))
		case []float64:
			vals := v.([]float64)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = fmtFloat64(float64(val))
			}
			return strings.Join(svals, ",")
		case []float32:
			vals := v.([]float32)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = fmtFloat32(float32(val))
			}
			return strings.Join(svals, ",")
		case []int:
			vals := v.([]int)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = strconv.Itoa(int(val))
			}
			return strings.Join(svals, ",")
		case []int32:
			vals := v.([]int32)
			svals := make([]string, len(vals))
			for i, val := range vals {
				svals[i] = strconv.Itoa(int(int32(val)))
			}
			return strings.Join(svals, ",")
		case []string:
			return strings.Join(v.([]string), ",")

		default:
			return v.(string)
		}
	}
}

func (i InfoByte) SGet(key string) []byte {
	var sub []byte
	if key == "" || len(i.Info) == 1 {
		return sub
	}
	start, end := getpositions(i.Info, key)
	if start == -1 {
		return sub
	}
	if end == -1 {
		end = len(i.Info) - 1
	}
	val := i.Info[start : end+1]
	return val
}

// Get a value from the bytes typed according to the header.
func (i InfoByte) Get(key string) (interface{}, error) {
	v := string(i.SGet(key))
	skey := string(key)
	var ok bool
	var hi *Info
	if i.header == nil || i.header.Infos == nil {
		ok = false
	} else {
		i.header.RLock()
		hi, ok = i.header.Infos[key]
		i.header.RUnlock()
	}
	if !ok {
		err := fmt.Errorf("Info Error: %s not found in header", skey)
		// flag
		if skey == v {
			return true, err
		}
		if v == "" {
			return nil, err
		}
		return v, err
	}

	if v == "" {
		var err error
		var val interface{} = nil
		if hi.Type != "Flag" {
			err = fmt.Errorf("Info Error: %s not found in INFO", skey)
		} else {
			val = false
		}
		return val, err
	}
	if v == key {
		var err error
		if hi.Type != "Flag" {
			err = fmt.Errorf("Info Error: flag field (%s) should be specified as such in the header", skey)
		}
		return true, err
	}

	var err error
	var iv interface{}
	if hi.Number == "1" || (hi.Number == "." && strings.IndexByte(v, ',') == -1) {
		return parseOne(skey, v, hi.Type)
	}

	switch hi.Number {

	case "0":
		if hi.Type != "Flag" {
			err = fmt.Errorf("Info Error: flag field (%s) should have Number=0", skey)
		}
		return true, err

	case "R", "A", "G", "2", "3", ".":
		vals := strings.Split(v, ",")
		var vi interface{}
		switch hi.Type {
		case "Integer":
			vi = make([]int, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]int)[j] = iv.(int)
			}
		case "Float":
			vi = make([]float32, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				if o, ok := iv.(float32); ok {
					vi.([]float32)[j] = o
				} else {
					vi.([]float32)[j] = float32(iv.(float64))
				}

			}
		case "String":
			vi = make([]string, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]string)[j] = iv.(string)
			}
		default:
			vi = make([]interface{}, len(vals))
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]interface{})[j] = iv
			}
		}
		return vi, nil

	default:
		vals := strings.Split(v, ",")
		var vi interface{} = make([]interface{}, len(vals))
		if _, err := strconv.Atoi(hi.Number); err == nil {
			for j, val := range vals {
				iv, err = parseOne(skey, val, hi.Type)
				vi.([]interface{})[j] = iv
			}
		}
		return vi, err

	}
}

func (i InfoByte) String() string {
	return string(i.Info)
}

func (i InfoByte) Bytes() []byte {
	return i.Info
}

func (i *InfoByte) UpdateHeader(key string, value interface{}) {
	i.header.Lock()
	defer i.header.Unlock()
	if i.header != nil {
		switch value.(type) {
		case bool:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "0", Type: "Flag"}
		case string:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "1", Type: "Character"}
		case int, int32, int64, uint32, uint64:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "1", Type: "Integer"}
		case float32, float64:
			i.header.Infos[key] = &Info{Id: key, Description: key, Number: "1", Type: "Float"}
		case []interface{}:
			v := value.([]interface{})[0]
			i.UpdateHeader(key, v)
		}
	}
}

func (i *InfoByte) Set(key string, value interface{}) error {
	if len(i.Info) == 0 {
		if v, ok := value.(bool); ok {
			if v {
				i.Info = []byte(key)
			}
		} else {
			i.Info = []byte(fmt.Sprintf("%s=%s", key, ItoS(key, value)))
		}
		return nil
	}
	s, e := getpositions(i.Info, key)
	if s == -1 || s == len(i.Info) {
		if b, ok := value.(bool); ok {
			if b {
				i.Info = append(i.Info, ';')
				i.Info = append(i.Info, key...)
			}
			return nil
		}
		slug := fmt.Sprintf(";%s=%s", key, ItoS(key, value))
		i.Info = append(i.Info, slug...)
		//i.UpdateHeader(key, value)
		return nil
	}
	if b, ok := value.(bool); ok {
		if !b {
			i.Delete(key)
		}
		return nil
	}
	slug := []byte(ItoS(key, value))
	if e == -1 {
		i.Info = append(i.Info[:s], slug...)
	} else {
		i.Info = append(i.Info[:s], append(slug, i.Info[e+1:]...)...)
	}
	return nil
}

func (i *InfoByte) Add(key string, value interface{}) {
	i.Set(key, value)
}
<|endoftext|>


<commit_before>package apns

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"math/rand"
	"strconv"
	"time"
)

// Push commands always start with command value 2.
const pushCommandValue = 2

// Your total notification payload cannot exceed 2 KB.
const MaxPayloadSizeBytes = 2048

// Every push notification gets a pseudo-unique identifier;
// this establishes the upper boundary for it. Apple will return
// this identifier if there is an issue sending your notification.
const IdentifierUbound = 9999

// Constants related to the payload fields and their lengths.
const (
	deviceTokenItemid            = 1
	payloadItemid                = 2
	notificationIdentifierItemid = 3
	expirationDateItemid         = 4
	priorityItemid               = 5
	deviceTokenItemLength            = 64
	notificationIdentifierLength = 4
	expirationDateLength         = 4
	priorityLength               = 1
)

// Payload contains the notification data for your request.
//
// Alert is an interface here because it supports either a string
// or a dictionary, represented within by an AlertDictionary struct.
type Payload struct {
	Alert            interface{} `json:"alert,omitempty"`
	Badge            int         `json:"badge,omitempty"`
	Sound            string      `json:"sound,omitempty"`
	ContentAvailable int         `json:"content-available,omitempty"`
	Category         string      `json:"category,omitempty"`
}

// NewPayload creates and returns a Payload structure.
func NewPayload() *Payload {
	return new(Payload)
}

// AlertDictionary is a more complex notification payload.
//
// From the APN docs: "Use the ... alert dictionary in general only if you absolutely need to."
// The AlertDictionary is suitable for specific localization needs.
type AlertDictionary struct {
	Body         string   `json:"body,omitempty"`
	ActionLocKey string   `json:"action-loc-key,omitempty"`
	LocKey       string   `json:"loc-key,omitempty"`
	LocArgs      []string `json:"loc-args,omitempty"`
	LaunchImage  string   `json:"launch-image,omitempty"`
}

// NewAlertDictionary creates and returns an AlertDictionary structure.
func NewAlertDictionary() *AlertDictionary {
	return new(AlertDictionary)
}

// PushNotification is the wrapper for the Payload.
// The length fields are computed in ToBytes() and aren't represented here.
type PushNotification struct {
	Identifier  uint32 `json:"identifier"`
	Expiry      uint32 `json:"expiry"`
	DeviceToken string `json:"device_token"`
	Payload     map[string]interface{} `json:"payload"`
	Priority    uint8 `json:"priority"`
}

// NewPushNotification creates and returns a PushNotification structure.
// It also initializes the pseudo-random identifier.
func NewPushNotification() (pn *PushNotification) {
	pn = new(PushNotification)
	pn.Payload = make(map[string]interface{})
	pn.Identifier = rand.New(rand.NewSource(time.Now().UnixNano())).Uint32()
	pn.Priority = 10
	return
}

// AddPayload sets the "aps" Payload section of the request. It also
// has a hack described within to deal with specific zero values.
func (pn *PushNotification) AddPayload(p *Payload) {
	// This deserves some explanation.
	//
	// Setting an exported field of type int to 0
	// triggers the omitempty behavior if you've set it.
	// Since the badge is optional, we should omit it if
	// it's not set. However, we want to include it if the
	// value is 0, so there's a hack in push_notification.go
	// that exploits the fact that Apple treats -1 for a
	// badge value as though it were 0 (i.e. it clears the
	// badge but doesn't stop the notification from going
	// through successfully.)
	//
	// Still a hack though :)
	if p.Badge == 0 {
		p.Badge = -1
	}
	pn.Set("aps", p)
}

// Get returns the value of a Payload key, if it exists.
func (pn *PushNotification) Get(key string) interface{} {
	return pn.Payload[key]
}

// Set defines the value of a Payload key.
func (pn *PushNotification) Set(key string, value interface{}) {
	pn.Payload[key] = value
}

// PayloadJSON returns the current Payload in JSON format.
func (pn *PushNotification) PayloadJSON() ([]byte, error) {
	return json.Marshal(pn.Payload)
}

// PayloadString returns the current Payload in string format.
func (pn *PushNotification) PayloadString() (string, error) {
	j, err := pn.PayloadJSON()
	return string(j), err
}

// ToBytes returns a byte array of the complete PushNotification
// struct. This array is what should be transmitted to the APN Service.
func (pn *PushNotification) ToBytes() ([]byte, error) {
	token, err := hex.DecodeString(pn.DeviceToken)
	if err != nil {
		return nil, err
	}
	Payload, err := pn.PayloadJSON()
	if err != nil {
		return nil, err
	}
	if len(Payload) > MaxPayloadSizeBytes {
		return nil, errors.New("Payload is larger than the " + strconv.Itoa(MaxPayloadSizeBytes) + " byte limit")
	}

	frameBuffer := new(bytes.Buffer)

	binary.Write(frameBuffer, binary.BigEndian, uint8(deviceTokenItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(deviceTokenItemLength))
	binary.Write(frameBuffer, binary.BigEndian, token)
	binary.Write(frameBuffer, binary.BigEndian, uint8(payloadItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(len(Payload)))
	binary.Write(frameBuffer, binary.BigEndian, Payload)
	binary.Write(frameBuffer, binary.BigEndian, uint8(notificationIdentifierItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(notificationIdentifierLength))
	binary.Write(frameBuffer, binary.BigEndian, uint32(pn.Identifier))

	binary.Write(frameBuffer, binary.BigEndian, uint8(expirationDateItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(expirationDateLength))

	binary.Write(frameBuffer, binary.BigEndian, pn.Expiry)

	binary.Write(frameBuffer, binary.BigEndian, uint8(priorityItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(priorityLength))
	binary.Write(frameBuffer, binary.BigEndian, uint8(pn.Priority))

	buffer := bytes.NewBuffer([]byte{})
	binary.Write(buffer, binary.BigEndian, pushCommandValue)
	binary.Write(buffer, binary.BigEndian, frameBuffer.Len())
	binary.Write(buffer, binary.BigEndian, frameBuffer.Bytes())

	return buffer.Bytes(), nil
}
<commit_msg>Modifying identifier size<commit_after>package apns

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"math/rand"
	"strconv"
	"time"
)

// Push commands always start with command value 2.
const pushCommandValue = 2

// Your total notification payload cannot exceed 2 KB.
const MaxPayloadSizeBytes = 2048

// Every push notification gets a pseudo-unique identifier;
// this establishes the upper boundary for it. Apple will return
// this identifier if there is an issue sending your notification.
const IdentifierUbound = 9999

// Constants related to the payload fields and their lengths.
const (
	deviceTokenItemid            = 1
	payloadItemid                = 2
	notificationIdentifierItemid = 3
	expirationDateItemid         = 4
	priorityItemid               = 5
	deviceTokenItemLength            = 64
	notificationIdentifierLength = 4
	expirationDateLength         = 4
	priorityLength               = 1
)

// Payload contains the notification data for your request.
//
// Alert is an interface here because it supports either a string
// or a dictionary, represented within by an AlertDictionary struct.
type Payload struct {
	Alert            interface{} `json:"alert,omitempty"`
	Badge            int         `json:"badge,omitempty"`
	Sound            string      `json:"sound,omitempty"`
	ContentAvailable int         `json:"content-available,omitempty"`
	Category         string      `json:"category,omitempty"`
}

// NewPayload creates and returns a Payload structure.
func NewPayload() *Payload {
	return new(Payload)
}

// AlertDictionary is a more complex notification payload.
//
// From the APN docs: "Use the ... alert dictionary in general only if you absolutely need to."
// The AlertDictionary is suitable for specific localization needs.
type AlertDictionary struct {
	Body         string   `json:"body,omitempty"`
	ActionLocKey string   `json:"action-loc-key,omitempty"`
	LocKey       string   `json:"loc-key,omitempty"`
	LocArgs      []string `json:"loc-args,omitempty"`
	LaunchImage  string   `json:"launch-image,omitempty"`
}

// NewAlertDictionary creates and returns an AlertDictionary structure.
func NewAlertDictionary() *AlertDictionary {
	return new(AlertDictionary)
}

// PushNotification is the wrapper for the Payload.
// The length fields are computed in ToBytes() and aren't represented here.
type PushNotification struct {
	Identifier  uint32 `json:"identifier"`
	Expiry      uint32 `json:"expiry"`
	DeviceToken string `json:"device_token"`
	Payload     map[string]interface{} `json:"payload"`
	Priority    uint8 `json:"priority"`
}

// NewPushNotification creates and returns a PushNotification structure.
// It also initializes the pseudo-random identifier.
func NewPushNotification() (pn *PushNotification) {
	pn = new(PushNotification)
	pn.Payload = make(map[string]interface{})
	pn.Identifier = uint32(rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(9999))
	pn.Priority = 10
	return
}

// AddPayload sets the "aps" Payload section of the request. It also
// has a hack described within to deal with specific zero values.
func (pn *PushNotification) AddPayload(p *Payload) {
	// This deserves some explanation.
	//
	// Setting an exported field of type int to 0
	// triggers the omitempty behavior if you've set it.
	// Since the badge is optional, we should omit it if
	// it's not set. However, we want to include it if the
	// value is 0, so there's a hack in push_notification.go
	// that exploits the fact that Apple treats -1 for a
	// badge value as though it were 0 (i.e. it clears the
	// badge but doesn't stop the notification from going
	// through successfully.)
	//
	// Still a hack though :)
	if p.Badge == 0 {
		p.Badge = -1
	}
	pn.Set("aps", p)
}

// Get returns the value of a Payload key, if it exists.
func (pn *PushNotification) Get(key string) interface{} {
	return pn.Payload[key]
}

// Set defines the value of a Payload key.
func (pn *PushNotification) Set(key string, value interface{}) {
	pn.Payload[key] = value
}

// PayloadJSON returns the current Payload in JSON format.
func (pn *PushNotification) PayloadJSON() ([]byte, error) {
	return json.Marshal(pn.Payload)
}

// PayloadString returns the current Payload in string format.
func (pn *PushNotification) PayloadString() (string, error) {
	j, err := pn.PayloadJSON()
	return string(j), err
}

// ToBytes returns a byte array of the complete PushNotification
// struct. This array is what should be transmitted to the APN Service.
func (pn *PushNotification) ToBytes() ([]byte, error) {
	token, err := hex.DecodeString(pn.DeviceToken)
	if err != nil {
		return nil, err
	}
	Payload, err := pn.PayloadJSON()
	if err != nil {
		return nil, err
	}
	if len(Payload) > MaxPayloadSizeBytes {
		return nil, errors.New("Payload is larger than the " + strconv.Itoa(MaxPayloadSizeBytes) + " byte limit")
	}

	frameBuffer := new(bytes.Buffer)

	binary.Write(frameBuffer, binary.BigEndian, uint8(deviceTokenItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(len(token)))
	binary.Write(frameBuffer, binary.BigEndian, token)
	binary.Write(frameBuffer, binary.BigEndian, uint8(payloadItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(len(Payload)))
	binary.Write(frameBuffer, binary.BigEndian, Payload)
	binary.Write(frameBuffer, binary.BigEndian, uint8(notificationIdentifierItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(notificationIdentifierLength))
	binary.Write(frameBuffer, binary.BigEndian, uint32(pn.Identifier))

	binary.Write(frameBuffer, binary.BigEndian, uint8(expirationDateItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(expirationDateLength))

	binary.Write(frameBuffer, binary.BigEndian, pn.Expiry)

	binary.Write(frameBuffer, binary.BigEndian, uint8(priorityItemid))
	binary.Write(frameBuffer, binary.BigEndian, uint16(priorityLength))
	binary.Write(frameBuffer, binary.BigEndian, uint8(pn.Priority))

	buffer := bytes.NewBuffer([]byte{})
	binary.Write(buffer, binary.BigEndian, pushCommandValue)
	binary.Write(buffer, binary.BigEndian, frameBuffer.Len())
	binary.Write(buffer, binary.BigEndian, frameBuffer.Bytes())

	return buffer.Bytes(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"time"

	"net/http"
	_ "net/http/pprof"

	"github.com/Sirupsen/logrus"
	"github.com/deshboard/boilerplate-service/app"
	"github.com/evalphobia/logrus_fluent"
	"github.com/kelseyhightower/envconfig"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/sagikazarmark/serverz"
	"golang.org/x/net/trace"
	"gopkg.in/airbrake/gobrake.v2"
	logrus_airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
)

// Global context variables
var (
	config   = &app.Configuration{}
	logger   = logrus.New().WithField("service", app.ServiceName) // Use logrus.FieldLogger type
	tracer   = opentracing.GlobalTracer()
	shutdown = serverz.NewShutdown(logger)
)

func init() {
	// Register shutdown handler in logrus
	logrus.RegisterExitHandler(shutdown.Handle)

	// Load configuration from environment
	err := envconfig.Process("app", config)
	if err != nil {
		logger.Fatal(err)
	}

	defaultAddr := ""

	// Listen on loopback interface in development mode
	if config.Environment == "development" {
		defaultAddr = "127.0.0.1"
	}

	// Load flags into configuration
	flag.StringVar(&config.ServiceAddr, "service", defaultAddr+":80", "Service address.")
	flag.StringVar(&config.HealthAddr, "health", defaultAddr+":10000", "Health service address.")
	flag.StringVar(&config.DebugAddr, "debug", defaultAddr+":10001", "Debug service address.")
	flag.DurationVar(&config.ShutdownTimeout, "shutdown", 2*time.Second, "Shutdown timeout.")

	// This is probably OK as the service runs in Docker
	trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
		return true, true
	}

	// Initialize Airbrake
	if config.AirbrakeEnabled {
		airbrakeHook := logrus_airbrake.NewHook(config.AirbrakeProjectID, config.AirbrakeAPIKey, config.Environment)
		airbrake := airbrakeHook.Airbrake

		airbrake.SetHost(config.AirbrakeEndpoint)

		airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
			notice.Context["version"] = app.Version
			notice.Context["commit"] = app.CommitHash

			return notice
		})

		logger.Logger.Hooks.Add(airbrakeHook)
		shutdown.Register(airbrake.Close)
	}

	// Initialize Fluentd
	if config.FluentdEnabled {
		fluentdHook, err := logrus_fluent.New(config.FluentdHost, config.FluentdPort)
		if err != nil {
			logger.Panic(err)
		}

		fluentdHook.SetTag(app.ServiceName)
		fluentdHook.AddFilter("error", logrus_fluent.FilterError)

		logger.Logger.Hooks.Add(fluentdHook)
		shutdown.Register(fluentdHook.Fluent.Close)
	}
}
<commit_msg>Add expvar to debug mode<commit_after>package main

import (
	"flag"
	"time"

	_ "expvar"
	"net/http"
	_ "net/http/pprof"

	"github.com/Sirupsen/logrus"
	"github.com/deshboard/boilerplate-service/app"
	"github.com/evalphobia/logrus_fluent"
	"github.com/kelseyhightower/envconfig"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/sagikazarmark/serverz"
	"golang.org/x/net/trace"
	"gopkg.in/airbrake/gobrake.v2"
	logrus_airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
)

// Global context variables
var (
	config   = &app.Configuration{}
	logger   = logrus.New().WithField("service", app.ServiceName) // Use logrus.FieldLogger type
	tracer   = opentracing.GlobalTracer()
	shutdown = serverz.NewShutdown(logger)
)

func init() {
	// Register shutdown handler in logrus
	logrus.RegisterExitHandler(shutdown.Handle)

	// Load configuration from environment
	err := envconfig.Process("app", config)
	if err != nil {
		logger.Fatal(err)
	}

	defaultAddr := ""

	// Listen on loopback interface in development mode
	if config.Environment == "development" {
		defaultAddr = "127.0.0.1"
	}

	// Load flags into configuration
	flag.StringVar(&config.ServiceAddr, "service", defaultAddr+":80", "Service address.")
	flag.StringVar(&config.HealthAddr, "health", defaultAddr+":10000", "Health service address.")
	flag.StringVar(&config.DebugAddr, "debug", defaultAddr+":10001", "Debug service address.")
	flag.DurationVar(&config.ShutdownTimeout, "shutdown", 2*time.Second, "Shutdown timeout.")

	// This is probably OK as the service runs in Docker
	trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
		return true, true
	}

	// Initialize Airbrake
	if config.AirbrakeEnabled {
		airbrakeHook := logrus_airbrake.NewHook(config.AirbrakeProjectID, config.AirbrakeAPIKey, config.Environment)
		airbrake := airbrakeHook.Airbrake

		airbrake.SetHost(config.AirbrakeEndpoint)

		airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
			notice.Context["version"] = app.Version
			notice.Context["commit"] = app.CommitHash

			return notice
		})

		logger.Logger.Hooks.Add(airbrakeHook)
		shutdown.Register(airbrake.Close)
	}

	// Initialize Fluentd
	if config.FluentdEnabled {
		fluentdHook, err := logrus_fluent.New(config.FluentdHost, config.FluentdPort)
		if err != nil {
			logger.Panic(err)
		}

		fluentdHook.SetTag(app.ServiceName)
		fluentdHook.AddFilter("error", logrus_fluent.FilterError)

		logger.Logger.Hooks.Add(fluentdHook)
		shutdown.Register(fluentdHook.Fluent.Close)
	}
}
<|endoftext|>