<commit_before>package main

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/mcuadros/go-version"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"path"
	"mime/multipart"
)

type Config struct {
	Host               string
	Port               int
	RequestCacheSize   int
	Logfile            string
	RepoLocation       string
	TmpDir             string
	RepoRebuildCommand string
	Token              []Token
}

type Token struct {
	Value string
	Owner string
	Repo  []Repo
}

type Repo struct {
	Name string
}

func main() {
	var config Config
	if _, err := toml.DecodeFile("/etc/deb-drop/deb-drop.toml", &config); err != nil {
		fmt.Println("Failed to parse config file", err.Error())
		os.Exit(1)
	}

	logfile, err := os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		fmt.Println("Can not open logfile", config.Logfile, err)
		os.Exit(1)
	}
	lg := log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	// We need to validate config a bit before we run server
	for _, token := range config.Token {
		for _, repo := range token.Repo {
			err = validateRepos(lg, config.RepoLocation, []string{repo.Name})
			if err != nil {
				lg.Println("Found invalid repo. Next time will refuse to run", err)
			}
		}

	}

	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
	if err != nil {
		lg.Println("Error:", err)
	}

	http.HandleFunc("/", makeHandler(lg, &config, mainHandler))
	err = fcgi.Serve(l, nil)

	if err != nil {
		lg.Println("Error:", err)
	}
}

func makeHandler(lg *log.Logger, config *Config, fn func(http.ResponseWriter, *http.Request, *Config, *log.Logger)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fn(w, r, config, lg)
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request, config *Config, lg *log.Logger) {

	repos := strings.Split(r.FormValue("repos"), ",")
	err := validateRepos(lg, config.RepoLocation, repos)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	err = validateToken(lg, config, r.FormValue("token"), repos)
	if err != nil {
		w.WriteHeader(http.StatusForbidden)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	//Check if old packages should be removed
	keepVersions, err := strconv.Atoi(r.FormValue("versions"))
	if err != nil || keepVersions < 1 {
		keepVersions = 5
	}

	var content multipart.File
	var packageName string

	// We can get package name from FORM or from parameter. It depends if there is an upload or copy/get
	if r.FormValue("package") != "" {
		packageName = r.FormValue("package")
	} else {
		// This is upload
		header := new(multipart.FileHeader)
		content, header, err = r.FormFile("package")
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}
		defer content.Close()
		packageName = header.Filename
	}

	if r.Method == "GET" {
		if len(repos) != 1 {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println("You should pass exactly 1 repo")
			fmt.Fprintln(w, "You should pass exactly 1 repo")
			return
		}
		pattern := config.RepoLocation + "/" + repos[0] + "/" + packageName + "*"
		matches := getPackagesByPattern(pattern)
		if len(matches) == 0 {
			w.WriteHeader(http.StatusNotFound)
			lg.Println(pattern + " is not found")
			fmt.Fprintln(w, "%s is not found in %s", packageName, repos[0])
			return
		} else {
			w.WriteHeader(http.StatusOK)
			for i:=0; i<keepVersions; i++ {
				element := len(matches)-1-i
				if element < 0 {
					break
				}
				fmt.Fprintln(w, path.Base(matches[element]))
			}
			return
		}
	} else if r.Method == "POST" {
		// Allow caching of up to <amount> in memory before buffering to disk. In MB
		err = r.ParseMultipartForm(int64(config.RequestCacheSize * 1024))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		// Package name needs to be validated only when we are making changes
		err = validatePackageName(lg, packageName)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		repositories := repos

		if r.FormValue("package") != "" {
			// This is used when package is passed as name, which means it is copy action

			// Open original file
			content, err = os.Open(config.RepoLocation + "/" + repos[0] + "/" + packageName)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println(err)
				fmt.Fprintln(w, err)
				return
			}
			defer content.Close()

			// We need at least 2 repos to copy package between
			if len(repos) < 2 {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println("You should pass at least 2 repo")
				fmt.Fprintln(w, "You should pass at least 2 repo")
				return
			}
			repositories = repos[1:]
		}

		err = addToRepos(lg, config, content, repositories, packageName)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		err = removeOldPackages(lg, config, repos, packageName, keepVersions)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
			return
		}
	}

	err = generateRepos(lg, config, repos)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, err)
		return
	}
}

func validateToken(lg *log.Logger, config *Config, token string, repos []string) error {
	// Going over all tokens in configuration to find requested
	if token == "" {
		lg.Printf("Attempt to access %s without token", repos)
		return fmt.Errorf("%s", "You must specify token")
	}

	var foundToken bool
	for _, configToken := range config.Token {
		if configToken.Value == token {
			foundToken = true
			// Checking all requested repos to be allowed for this token
			for _, requestedRepo := range repos {
				var foundRepo bool
				for _, configRepo := range configToken.Repo {
					if configRepo.Name == requestedRepo {
						foundRepo = true
						break
					}
				}
				if !foundRepo {
					lg.Println("Use of valid token with not listed repo " + requestedRepo)
					return fmt.Errorf("%s", "Token is not allowed to use on one or more of the specified repos")
				}
			}
			return nil
		}
	}

	if !foundToken {
		lg.Printf("Attempt to access %s with invalid token\n", repos)
		return fmt.Errorf("%s", "Token is not allowed to use one or more of the specified repos")
	}

	return nil
}

func validateRepos(lg *log.Logger, repoLocation string, repos []string) error {
	if len(repos) == 0 {
		lg.Println("You should pass at least 1 repo")
		return fmt.Errorf("%s", "You should pass at least 1 repo")
	}

	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		if len(parts) != 3 {
			lg.Println("Repo has invalid format")
			return fmt.Errorf("%s", "Repo has invalid format")
		}

		stat, err := os.Stat(repoLocation + "/" + repo)
		if err != nil {
			lg.Println("Repository does not exist", err)
			return fmt.Errorf("%s", "Repository does not exist")
		}

		if !stat.IsDir() {
			lg.Println("Specified repository location exists but is not a directory")
			return fmt.Errorf("%s", "Specified repository location exists but is not a directory")
		}
	}
	return nil
}

func validatePackageName(lg *log.Logger, name string) error {
	if !strings.HasSuffix(name, ".deb") {
		lg.Println("Somebody tried to upload invalid package name - missing .deb", name)
		return fmt.Errorf("%s", "Package name must end with .deb")
	}
	if len(strings.Split(name, "_")) != 3 {
		lg.Println("Somebody tried to upload invalid package name - does not contain 3 _", name)
		return fmt.Errorf("%s", "the package name does not look like a valid debian package name")
	}
	return nil
}

func writeStreamToTmpFile(lg *log.Logger, content io.Reader, tmpFilePath string) error {
	tmpDir := filepath.Dir(tmpFilePath)
	stat, err := os.Stat(tmpDir)
	if err != nil {
		lg.Printf("%s does not exist. Creating...\n", tmpDir)
		err = os.Mkdir(tmpDir, os.ModePerm)
		if err != nil {
			lg.Println(err)
			return err
		}
	} else if !stat.IsDir() {
		lg.Printf("%s exists, but it is not a directory\n", tmpDir)
		return fmt.Errorf("%s exists, but it is not a directory", tmpDir)
	}

	tmpFile, err := os.Create(tmpFilePath)
	if err != nil {
		lg.Println(err)
		return err
	}
	defer tmpFile.Close()

	_, err = io.Copy(tmpFile, content)
	if err != nil {
		lg.Printf("Can not save data from POST to %s\n", tmpFilePath)
		return err
	}
	return nil

}

func addToRepos(lg *log.Logger, config *Config, content io.Reader, repos []string, packageName string) error {
	tmpFilePath := fmt.Sprintf("%s/%s", config.TmpDir, packageName)
	err := writeStreamToTmpFile(lg, content, tmpFilePath)
	if err != nil {
		return err
	}
	defer os.Remove(tmpFilePath)

	for _, repo := range repos {
		fileInRepo := config.RepoLocation + "/" + repo + "/" + packageName
		err := os.Link(tmpFilePath, fileInRepo)
		if err != nil {
			lg.Printf("Can not link package %s to %s", tmpFilePath, fileInRepo)
			return err
		}
	}
	return nil
}

func getPackagesByPattern(pattern string) []string {
	matches, _ := filepath.Glob(pattern)
	version.Sort(matches)
	return matches
}

func removeOldPackages(lg *log.Logger, config *Config, repos []string, fileName string, keepVersions int) error {
	packageName := strings.Split(fileName, "_")[0]
	for _, repo := range repos {
		matches := getPackagesByPattern(config.RepoLocation + "/" + repo + "/" + packageName + "_*")
		if len(matches) > keepVersions {
			to_remove := len(matches) - keepVersions
			for _, file := range matches[:to_remove] {
				lg.Println("Removing", file)
				err := os.Remove(file)
				if err != nil {
					lg.Println("Could remove package '", file, "' from Repo: '", err, "'")
					return fmt.Errorf("%s", "Cleanup of old packages has failed")
				}
			}
		}

	}
	return nil
}

func generateRepos(lg *log.Logger, config *Config, repos []string) error {
	// Rebuild repositories only once
	names := make(map[string]string)
	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		names[parts[0]] = repo
	}

	for name, repo := range names {
		var cmd *exec.Cmd
		lg.Println("running", config.RepoRebuildCommand, repo)
		parts := strings.Fields(config.RepoRebuildCommand)
		head := parts[0]
		parts = parts[1:]
		parts = append(parts, repo)
		cmd = exec.Command(head, parts...)
		err := cmd.Run()
		if err != nil {
			lg.Println("Could not generate metadata for", name, ":", err)
			return fmt.Errorf("Could not generate metadata for %s : %v", name, err)
		}
	}
	return nil
}
<commit_msg>Remove redundant message<commit_after>package main

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/mcuadros/go-version"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"path"
	"mime/multipart"
)

type Config struct {
	Host               string
	Port               int
	RequestCacheSize   int
	Logfile            string
	RepoLocation       string
	TmpDir             string
	RepoRebuildCommand string
	Token              []Token
}

type Token struct {
	Value string
	Owner string
	Repo  []Repo
}

type Repo struct {
	Name string
}

func main() {
	var config Config
	if _, err := toml.DecodeFile("/etc/deb-drop/deb-drop.toml", &config); err != nil {
		fmt.Println("Failed to parse config file", err.Error())
		os.Exit(1)
	}

	logfile, err := os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		fmt.Println("Can not open logfile", config.Logfile, err)
		os.Exit(1)
	}
	lg := log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	// We need to validate config a bit before we run server
	for _, token := range config.Token {
		for _, repo := range token.Repo {
			err = validateRepos(lg, config.RepoLocation, []string{repo.Name})
			if err != nil {
				lg.Println("Found invalid repo. Next time will refuse to run", err)
			}
		}

	}

	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
	if err != nil {
		lg.Println("Error:", err)
	}

	http.HandleFunc("/", makeHandler(lg, &config, mainHandler))
	err = fcgi.Serve(l, nil)

	if err != nil {
		lg.Println("Error:", err)
	}
}

func makeHandler(lg *log.Logger, config *Config, fn func(http.ResponseWriter, *http.Request, *Config, *log.Logger)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fn(w, r, config, lg)
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request, config *Config, lg *log.Logger) {

	repos := strings.Split(r.FormValue("repos"), ",")
	err := validateRepos(lg, config.RepoLocation, repos)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	err = validateToken(lg, config, r.FormValue("token"), repos)
	if err != nil {
		w.WriteHeader(http.StatusForbidden)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	//Check if old packages should be removed
	keepVersions, err := strconv.Atoi(r.FormValue("versions"))
	if err != nil || keepVersions < 1 {
		keepVersions = 5
	}

	var content multipart.File
	var packageName string

	// We can get package name from FORM or from parameter. It depends if there is an upload or copy/get
	if r.FormValue("package") != "" {
		packageName = r.FormValue("package")
	} else {
		// This is upload
		header := new(multipart.FileHeader)
		content, header, err = r.FormFile("package")
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}
		defer content.Close()
		packageName = header.Filename
	}

	if r.Method == "GET" {
		if len(repos) != 1 {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println("You should pass exactly 1 repo")
			fmt.Fprintln(w, "You should pass exactly 1 repo")
			return
		}
		pattern := config.RepoLocation + "/" + repos[0] + "/" + packageName + "*"
		matches := getPackagesByPattern(pattern)
		if len(matches) == 0 {
			w.WriteHeader(http.StatusNotFound)
			lg.Println(pattern + " is not found")
			return
		} else {
			w.WriteHeader(http.StatusOK)
			for i:=0; i<keepVersions; i++ {
				element := len(matches)-1-i
				if element < 0 {
					break
				}
				fmt.Fprintln(w, path.Base(matches[element]))
			}
			return
		}
	} else if r.Method == "POST" {
		// Allow caching of up to <amount> in memory before buffering to disk. In MB
		err = r.ParseMultipartForm(int64(config.RequestCacheSize * 1024))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		// Package name needs to be validated only when we are making changes
		err = validatePackageName(lg, packageName)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		repositories := repos

		if r.FormValue("package") != "" {
			// This is used when package is passed as name, which means it is copy action

			// Open original file
			content, err = os.Open(config.RepoLocation + "/" + repos[0] + "/" + packageName)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println(err)
				fmt.Fprintln(w, err)
				return
			}
			defer content.Close()

			// We need at least 2 repos to copy package between
			if len(repos) < 2 {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println("You should pass at least 2 repo")
				fmt.Fprintln(w, "You should pass at least 2 repo")
				return
			}
			repositories = repos[1:]
		}

		err = addToRepos(lg, config, content, repositories, packageName)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		err = removeOldPackages(lg, config, repos, packageName, keepVersions)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
			return
		}
	}

	err = generateRepos(lg, config, repos)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, err)
		return
	}
}

func validateToken(lg *log.Logger, config *Config, token string, repos []string) error {
	// Going over all tokens in configuration to find requested
	if token == "" {
		lg.Printf("Attempt to access %s without token", repos)
		return fmt.Errorf("%s", "You must specify token")
	}

	var foundToken bool
	for _, configToken := range config.Token {
		if configToken.Value == token {
			foundToken = true
			// Checking all requested repos to be allowed for this token
			for _, requestedRepo := range repos {
				var foundRepo bool
				for _, configRepo := range configToken.Repo {
					if configRepo.Name == requestedRepo {
						foundRepo = true
						break
					}
				}
				if !foundRepo {
					lg.Println("Use of valid token with not listed repo " + requestedRepo)
					return fmt.Errorf("%s", "Token is not allowed to use on one or more of the specified repos")
				}
			}
			return nil
		}
	}

	if !foundToken {
		lg.Printf("Attempt to access %s with invalid token\n", repos)
		return fmt.Errorf("%s", "Token is not allowed to use one or more of the specified repos")
	}

	return nil
}

func validateRepos(lg *log.Logger, repoLocation string, repos []string) error {
	if len(repos) == 0 {
		lg.Println("You should pass at least 1 repo")
		return fmt.Errorf("%s", "You should pass at least 1 repo")
	}

	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		if len(parts) != 3 {
			lg.Println("Repo has invalid format")
			return fmt.Errorf("%s", "Repo has invalid format")
		}

		stat, err := os.Stat(repoLocation + "/" + repo)
		if err != nil {
			lg.Println("Repository does not exist", err)
			return fmt.Errorf("%s", "Repository does not exist")
		}

		if !stat.IsDir() {
			lg.Println("Specified repository location exists but is not a directory")
			return fmt.Errorf("%s", "Specified repository location exists but is not a directory")
		}
	}
	return nil
}

func validatePackageName(lg *log.Logger, name string) error {
	if !strings.HasSuffix(name, ".deb") {
		lg.Println("Somebody tried to upload invalid package name - missing .deb", name)
		return fmt.Errorf("%s", "Package name must end with .deb")
	}
	if len(strings.Split(name, "_")) != 3 {
		lg.Println("Somebody tried to upload invalid package name - does not contain 3 _", name)
		return fmt.Errorf("%s", "the package name does not look like a valid debian package name")
	}
	return nil
}

func writeStreamToTmpFile(lg *log.Logger, content io.Reader, tmpFilePath string) error {
	tmpDir := filepath.Dir(tmpFilePath)
	stat, err := os.Stat(tmpDir)
	if err != nil {
		lg.Printf("%s does not exist. Creating...\n", tmpDir)
		err = os.Mkdir(tmpDir, os.ModePerm)
		if err != nil {
			lg.Println(err)
			return err
		}
	} else if !stat.IsDir() {
		lg.Printf("%s exists, but it is not a directory\n", tmpDir)
		return fmt.Errorf("%s exists, but it is not a directory", tmpDir)
	}

	tmpFile, err := os.Create(tmpFilePath)
	if err != nil {
		lg.Println(err)
		return err
	}
	defer tmpFile.Close()

	_, err = io.Copy(tmpFile, content)
	if err != nil {
		lg.Printf("Can not save data from POST to %s\n", tmpFilePath)
		return err
	}
	return nil

}

func addToRepos(lg *log.Logger, config *Config, content io.Reader, repos []string, packageName string) error {
	tmpFilePath := fmt.Sprintf("%s/%s", config.TmpDir, packageName)
	err := writeStreamToTmpFile(lg, content, tmpFilePath)
	if err != nil {
		return err
	}
	defer os.Remove(tmpFilePath)

	for _, repo := range repos {
		fileInRepo := config.RepoLocation + "/" + repo + "/" + packageName
		err := os.Link(tmpFilePath, fileInRepo)
		if err != nil {
			lg.Printf("Can not link package %s to %s", tmpFilePath, fileInRepo)
			return err
		}
	}
	return nil
}

func getPackagesByPattern(pattern string) []string {
	matches, _ := filepath.Glob(pattern)
	version.Sort(matches)
	return matches
}

func removeOldPackages(lg *log.Logger, config *Config, repos []string, fileName string, keepVersions int) error {
	packageName := strings.Split(fileName, "_")[0]
	for _, repo := range repos {
		matches := getPackagesByPattern(config.RepoLocation + "/" + repo + "/" + packageName + "_*")
		if len(matches) > keepVersions {
			to_remove := len(matches) - keepVersions
			for _, file := range matches[:to_remove] {
				lg.Println("Removing", file)
				err := os.Remove(file)
				if err != nil {
					lg.Println("Could remove package '", file, "' from Repo: '", err, "'")
					return fmt.Errorf("%s", "Cleanup of old packages has failed")
				}
			}
		}

	}
	return nil
}

func generateRepos(lg *log.Logger, config *Config, repos []string) error {
	// Rebuild repositories only once
	names := make(map[string]string)
	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		names[parts[0]] = repo
	}

	for name, repo := range names {
		var cmd *exec.Cmd
		lg.Println("running", config.RepoRebuildCommand, repo)
		parts := strings.Fields(config.RepoRebuildCommand)
		head := parts[0]
		parts = parts[1:]
		parts = append(parts, repo)
		cmd = exec.Command(head, parts...)
		err := cmd.Run()
		if err != nil {
			lg.Println("Could not generate metadata for", name, ":", err)
			return fmt.Errorf("Could not generate metadata for %s : %v", name, err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package utils

import (
	"flag"
	"os"
)

// AppConfig provides the global configuration of the application
type AppConfig struct {
	HTTPListenAddress string
}

var (
	listenAddr = flag.String("listen", ":8080", "listen address for the reverse proxy")
)

// NewAppConfig sets up all the basic configuration data from flags, env, etc
func NewAppConfig() (*AppConfig, error) {

	addr := os.Getenv("HTTP_ADDRESS")
	if len(addr) == 0 {
		addr = *listenAddr
	}

	return &AppConfig{HTTPListenAddress: addr}, nil
}
<commit_msg>parse the flags before using 'em<commit_after>package utils

import (
	"flag"
	"os"
)

// AppConfig provides the global configuration of the application
type AppConfig struct {
	HTTPListenAddress string
}

var (
	listenAddr = flag.String("listen", ":8080", "listen address for the reverse proxy")
)

// NewAppConfig sets up all the basic configuration data from flags, env, etc
func NewAppConfig() (*AppConfig, error) {

	flag.Parse()

	addr := os.Getenv("HTTP_ADDRESS")
	if len(addr) == 0 {
		addr = *listenAddr
	}

	return &AppConfig{HTTPListenAddress: addr}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"strings"

	"net/http"

	"github.com/stretchr/gomniauth"
	"github.com/stretchr/objx"
)

type authHandler struct {
	next http.Handler
}

func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if cookie, err := r.Cookie("auth"); err == http.ErrNoCookie || cookie.Value == "" {
		// not authenticated
		w.Header()["Location"] = []string{"/login"}
		w.WriteHeader(http.StatusTemporaryRedirect)
	} else if err != nil {
		// some other error
		panic(err.Error())
	} else {
		// success - call the next handler
		h.next.ServeHTTP(w, r)
	}

}

func MustAuth(handler http.Handler) http.Handler {
	return &authHandler{next: handler}
}

// loginHandler handles the third-party login process.
func loginHandler(w http.ResponseWriter, r *http.Request) {
	segs := strings.Split(r.URL.Path, "/")
	action := segs[2]
	provider := segs[3]
	switch action {
	case "login":

		provider, err := gomniauth.Provider(provider)
		if err != nil {
			log.Fatalln("Error when trying to get provider", provider, "-", err)
		}

		loginUrl, err := provider.GetBeginAuthURL(nil, nil)
		if err != nil {
			log.Fatalln("Error when trying to GetBeginAuthURL for", provider, "-", err)
		}

		w.Header()["Location"] = []string{loginUrl}
		w.WriteHeader(http.StatusTemporaryRedirect)

	case "callback":

		provider, err := gomniauth.Provider(provider)
		if err != nil {
			log.Fatalln("Error when trying to get provider", provider, "-", err)
		}

		// get the credentials
		creds, err := provider.CompleteAuth(objx.MustFromURLQuery(r.URL.RawQuery))
		if err != nil {
			log.Fatalln("Error when trying to complete auth for", provider, "-", err)
		}

		// get the user
		user, err := provider.GetUser(creds)
		if err != nil {
			log.Fatalln("Error when trying to get user from", provider, "-", err)
		}

		// save some data
		authCookieValue := objx.New(map[string]interface{}{
			"name":       user.Name(),
			"avatar_url": user.AvatarURL(),
			"email":      user.Email(),
		}).MustBase64()

		http.SetCookie(w, &http.Cookie{
			Name:  "auth",
			Value: authCookieValue,
			Path:  "/"})

		w.Header()["Location"] = []string{"/chat"}
		w.WriteHeader(http.StatusTemporaryRedirect)

	default:
		w.Write([]byte(fmt.Sprintf("Auth action %s not supported", action)))
		w.WriteHeader(http.StatusNotFound)
	}
}
<commit_msg>moved userid generation to point of login<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"log"
	"strings"

	"net/http"

	"github.com/stretchr/gomniauth"
	"github.com/stretchr/objx"
)

type authHandler struct {
	next http.Handler
}

func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if cookie, err := r.Cookie("auth"); err == http.ErrNoCookie || cookie.Value == "" {
		// not authenticated
		w.Header()["Location"] = []string{"/login"}
		w.WriteHeader(http.StatusTemporaryRedirect)
	} else if err != nil {
		// some other error
		panic(err.Error())
	} else {
		// success - call the next handler
		h.next.ServeHTTP(w, r)
	}

}

func MustAuth(handler http.Handler) http.Handler {
	return &authHandler{next: handler}
}

// loginHandler handles the third-party login process.
func loginHandler(w http.ResponseWriter, r *http.Request) {
	segs := strings.Split(r.URL.Path, "/")
	action := segs[2]
	provider := segs[3]
	switch action {
	case "login":

		provider, err := gomniauth.Provider(provider)
		if err != nil {
			log.Fatalln("Error when trying to get provider", provider, "-", err)
		}

		loginUrl, err := provider.GetBeginAuthURL(nil, nil)
		if err != nil {
			log.Fatalln("Error when trying to GetBeginAuthURL for", provider, "-", err)
		}

		w.Header()["Location"] = []string{loginUrl}
		w.WriteHeader(http.StatusTemporaryRedirect)

	case "callback":

		provider, err := gomniauth.Provider(provider)
		if err != nil {
			log.Fatalln("Error when trying to get provider", provider, "-", err)
		}

		// get the credentials
		creds, err := provider.CompleteAuth(objx.MustFromURLQuery(r.URL.RawQuery))
		if err != nil {
			log.Fatalln("Error when trying to complete auth for", provider, "-", err)
		}

		// get the user
		user, err := provider.GetUser(creds)
		if err != nil {
			log.Fatalln("Error when trying to get user from", provider, "-", err)
		}

		// hash the users email address to act as their
		// unique ID
		m := md5.New()
		io.WriteString(m, strings.ToLower(user.Name()))
		userId := fmt.Sprintf("%x", m.Sum(nil))

		// save some data
		authCookieValue := objx.New(map[string]interface{}{
			"userid":     userId,
			"name":       user.Name(),
			"avatar_url": user.AvatarURL(),
			"email":      user.Email(),
		}).MustBase64()

		http.SetCookie(w, &http.Cookie{
			Name:  "auth",
			Value: authCookieValue,
			Path:  "/"})

		w.Header()["Location"] = []string{"/chat"}
		w.WriteHeader(http.StatusTemporaryRedirect)

	default:
		w.Write([]byte(fmt.Sprintf("Auth action %s not supported", action)))
		w.WriteHeader(http.StatusNotFound)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"net"
	"strings"

	"github.com/miekg/dns"
	"github.com/pkg/errors"
)

/* .Target is kind of a mess.
For simple rtypes it is the record's value. (i.e. for an A record
	it is the IP address).
For complex rtypes (like an MX record has a preference and a value)
	it might be a space-delimited string with all the parameters, or it
	might just be the hostname.

This was a bad design decision that I regret. Eventually we will eliminate this
field and replace it with setters/getters.  The setters/getters are below
so that it is easy to do things the right way in preparation.
*/

// GetTargetField returns the target. There may be other fields (for example
// an MX record also has a .MxPreference field.
func (rc *RecordConfig) GetTargetField() string {
	return rc.Target
}

// // GetTargetSingle returns the target for types that have a single value target
// // and panics for all others.
// func (rc *RecordConfig) GetTargetSingle() string {
// 	if rc.Type == "MX" || rc.Type == "SRV" || rc.Type == "CAA" || rc.Type == "TLSA" || rc.Type == "TXT" {
// 		panic("TargetSingle called on a type with a multi-parameter rtype.")
// 	}
// 	return rc.Target
// }

// GetTargetIP returns the net.IP stored in Target.
func (rc *RecordConfig) GetTargetIP() net.IP {
	if rc.Type != "A" && rc.Type != "AAAA" {
		panic(errors.Errorf("GetTargetIP called on an inappropriate rtype (%s)", rc.Type))
	}
	return net.ParseIP(rc.Target)
}

// GetTargetCombined returns a string with the various fields combined.
// For example, an MX record might output `10 mx10.example.tld`.
func (rc *RecordConfig) GetTargetCombined() string {
	// If this is a pseudo record, just return the target.
	if _, ok := dns.StringToType[rc.Type]; !ok {
		return rc.Target
	}

	// We cheat by converting to a dns.RR and use the String() function.
	// This combines all the data for us, and even does proper quoting.
	// Sadly String() always includes a header, which we must strip out.
	// TODO(tlim): Request the dns project add a function that returns
	// the string without the header.
	rr := rc.ToRR()
	header := rr.Header().String()
	full := rr.String()
	if !strings.HasPrefix(full, header) {
		panic("assertion failed. dns.Hdr.String() behavior has changed in an incompatible way")
	}
	return full[len(header):]
}

// GetTargetSortable returns a string that is sortable.
func (rc *RecordConfig) GetTargetSortable() string {
	return rc.GetTargetDebug()
}

// GetTargetDebug returns a string with the various fields spelled out.
func (rc *RecordConfig) GetTargetDebug() string {
	content := fmt.Sprintf("%s %s %s %d", rc.Type, rc.NameFQDN, rc.Target, rc.TTL)
	switch rc.Type { // #rtype_variations
	case "A", "AAAA", "CNAME", "NS", "PTR", "TXT":
		// Nothing special.
	case "NAPTR":
		content += fmt.Sprintf(" naptrorder=%d naptrpreference=%d naptrflags=%s naptrservice=%s naptrregexp=%s", rc.NaptrOrder, rc.NaptrPreference, rc.NaptrFlags, rc.NaptrService, rc.NaptrRegexp)
	case "MX":
		content += fmt.Sprintf(" pref=%d", rc.MxPreference)
	case "SOA":
		content = fmt.Sprintf("%s %s %s %d", rc.Type, rc.Name, rc.Target, rc.TTL)
	case "SRV":
		content += fmt.Sprintf(" srvpriority=%d srvweight=%d srvport=%d", rc.SrvPriority, rc.SrvWeight, rc.SrvPort)
	case "SSHFP":
		content += fmt.Sprintf(" sshfpalgorithm=%d sshfpfingerprint=%d", rc.SshfpAlgorithm, rc.SshfpFingerprint)
	case "TLSA":
		content += fmt.Sprintf(" tlsausage=%d tlsaselector=%d tlsamatchingtype=%d", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType)
	case "CAA":
		content += fmt.Sprintf(" caatag=%s caaflag=%d", rc.CaaTag, rc.CaaFlag)
	case "R53_ALIAS":
		content += fmt.Sprintf(" type=%s zone_id=%s", rc.R53Alias["type"], rc.R53Alias["zone_id"])
	default:
		panic(errors.Errorf("rc.String rtype %v unimplemented", rc.Type))
		// We panic so that we quickly find any switch statements
		// that have not been updated for a new RR type.
	}
	for k, v := range rc.Metadata {
		content += fmt.Sprintf(" %s=%s", k, v)
	}
	return content
}

// SetTarget sets the target, assuming that the rtype is appropriate.
func (rc *RecordConfig) SetTarget(target string) error {
	rc.Target = target
	return nil
}

// SetTargetIP sets the target to an IP, verifying this is an appropriate rtype.
func (rc *RecordConfig) SetTargetIP(ip net.IP) error {
	// TODO(tlim): Verify the rtype is appropriate for an IP.
	rc.SetTarget(ip.String())
	return nil
}

// // SetTargetFQDN sets the target to a string, verifying this is an appropriate rtype.
// func (rc *RecordConfig) SetTargetFQDN(target string) error {
// 	// TODO(tlim): Verify the rtype is appropriate for an hostname.
// 	rc.Target = target
// 	return nil
// }
<commit_msg>BUG: R53_ALIAS false positive during duplicate checking (#505)<commit_after>package models

import (
	"fmt"
	"net"
	"strings"

	"github.com/miekg/dns"
	"github.com/pkg/errors"
)

/* .Target is kind of a mess.
For simple rtypes it is the record's value. (i.e. for an A record
	it is the IP address).
For complex rtypes (like an MX record has a preference and a value)
	it might be a space-delimited string with all the parameters, or it
	might just be the hostname.

This was a bad design decision that I regret. Eventually we will eliminate this
field and replace it with setters/getters.  The setters/getters are below
so that it is easy to do things the right way in preparation.
*/

// GetTargetField returns the target. There may be other fields (for example
// an MX record also has a .MxPreference field.
func (rc *RecordConfig) GetTargetField() string {
	return rc.Target
}

// // GetTargetSingle returns the target for types that have a single value target
// // and panics for all others.
// func (rc *RecordConfig) GetTargetSingle() string {
// 	if rc.Type == "MX" || rc.Type == "SRV" || rc.Type == "CAA" || rc.Type == "TLSA" || rc.Type == "TXT" {
// 		panic("TargetSingle called on a type with a multi-parameter rtype.")
// 	}
// 	return rc.Target
// }

// GetTargetIP returns the net.IP stored in Target.
func (rc *RecordConfig) GetTargetIP() net.IP {
	if rc.Type != "A" && rc.Type != "AAAA" {
		panic(errors.Errorf("GetTargetIP called on an inappropriate rtype (%s)", rc.Type))
	}
	return net.ParseIP(rc.Target)
}

// GetTargetCombined returns a string with the various fields combined.
// For example, an MX record might output `10 mx10.example.tld`.
func (rc *RecordConfig) GetTargetCombined() string {
	// Pseudo records:
	if _, ok := dns.StringToType[rc.Type]; !ok {
		switch rc.Type { // #rtype_variations
		case "R53_ALIAS":
			// Differentiate between multiple R53_ALIASs on the same label.
			return fmt.Sprintf("%s type=%s zone_id=%s", rc.Target, rc.R53Alias["type"], rc.R53Alias["zone_id"])
		default:
			// Just return the target.
			return rc.Target
		}
	}

	// We cheat by converting to a dns.RR and use the String() function.
	// This combines all the data for us, and even does proper quoting.
	// Sadly String() always includes a header, which we must strip out.
	// TODO(tlim): Request the dns project add a function that returns
	// the string without the header.
	rr := rc.ToRR()
	header := rr.Header().String()
	full := rr.String()
	if !strings.HasPrefix(full, header) {
		panic("assertion failed. dns.Hdr.String() behavior has changed in an incompatible way")
	}
	return full[len(header):]
}

// GetTargetSortable returns a string that is sortable.
func (rc *RecordConfig) GetTargetSortable() string {
	return rc.GetTargetDebug()
}

// GetTargetDebug returns a string with the various fields spelled out.
func (rc *RecordConfig) GetTargetDebug() string {
	content := fmt.Sprintf("%s %s %s %d", rc.Type, rc.NameFQDN, rc.Target, rc.TTL)
	switch rc.Type { // #rtype_variations
	case "A", "AAAA", "CNAME", "NS", "PTR", "TXT":
		// Nothing special.
	case "NAPTR":
		content += fmt.Sprintf(" naptrorder=%d naptrpreference=%d naptrflags=%s naptrservice=%s naptrregexp=%s", rc.NaptrOrder, rc.NaptrPreference, rc.NaptrFlags, rc.NaptrService, rc.NaptrRegexp)
	case "MX":
		content += fmt.Sprintf(" pref=%d", rc.MxPreference)
	case "SOA":
		content = fmt.Sprintf("%s %s %s %d", rc.Type, rc.Name, rc.Target, rc.TTL)
	case "SRV":
		content += fmt.Sprintf(" srvpriority=%d srvweight=%d srvport=%d", rc.SrvPriority, rc.SrvWeight, rc.SrvPort)
	case "SSHFP":
		content += fmt.Sprintf(" sshfpalgorithm=%d sshfpfingerprint=%d", rc.SshfpAlgorithm, rc.SshfpFingerprint)
	case "TLSA":
		content += fmt.Sprintf(" tlsausage=%d tlsaselector=%d tlsamatchingtype=%d", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType)
	case "CAA":
		content += fmt.Sprintf(" caatag=%s caaflag=%d", rc.CaaTag, rc.CaaFlag)
	case "R53_ALIAS":
		content += fmt.Sprintf(" type=%s zone_id=%s", rc.R53Alias["type"], rc.R53Alias["zone_id"])
	default:
		panic(errors.Errorf("rc.String rtype %v unimplemented", rc.Type))
		// We panic so that we quickly find any switch statements
		// that have not been updated for a new RR type.
	}
	for k, v := range rc.Metadata {
		content += fmt.Sprintf(" %s=%s", k, v)
	}
	return content
}

// SetTarget sets the target, assuming that the rtype is appropriate.
func (rc *RecordConfig) SetTarget(target string) error {
	rc.Target = target
	return nil
}

// SetTargetIP sets the target to an IP, verifying this is an appropriate rtype.
func (rc *RecordConfig) SetTargetIP(ip net.IP) error {
	// TODO(tlim): Verify the rtype is appropriate for an IP.
	rc.SetTarget(ip.String())
	return nil
}

// // SetTargetFQDN sets the target to a string, verifying this is an appropriate rtype.
// func (rc *RecordConfig) SetTargetFQDN(target string) error {
// 	// TODO(tlim): Verify the rtype is appropriate for an hostname.
// 	rc.Target = target
// 	return nil
// }
<|endoftext|>


<commit_before>// Copyright 2017 Mathew Robinson <mrobinson@praelatus.io>. All rights reserved.
// Use of this source code is governed by the AGPLv3 license that can be found in
// the LICENSE file.

package models

import (
	"log"
	"time"

	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// StatusType represents the statuses type based on the basic three types:
// TODO, In Progress, or Done
type StatusType string

// Available status types
const (
	StatusTodo       StatusType = "TODO"
	StatusInProgress            = "IN_PROGRESS"
	StatusDone                  = "Done"

	// StatusNull is a special type used for transitions that allow from any or
	// for create transitions
	StatusNull = "NULL"
)

// Status is a ticket and workflow status
type Status struct {
	Name string     `bson:"name" json:"name"`
	Type StatusType `bson:"type" json:"type"`
}

// Ticket represents a ticket
type Ticket struct {
	CreatedDate time.Time `json:"createdDate"`
	UpdatedDate time.Time `json:"updatedDate"`
	Key         string    `bson:"_id" json:"key"`
	Summary     string    `json:"summary" required:"true"`
	Description string    `json:"description" required:"true"`
	Status      Status    `json:"status"`
	Reporter    string    `json:"reporter" required:"true"`
	Assignee    string    `json:"assignee"`
	Type        string    `json:"type" required:"true"`
	Labels      []string  `json:"labels"`

	Fields   []Field   `json:"fields"`
	Comments []Comment `json:"comments,omitempty"`

	Workflow bson.ObjectId `json:"workflow"`
	Project  string        `json:"project" required:"true"`
}

func (t Ticket) String() string {
	return jsonString(t)
}

// Transition searches through the available transitions for the ticket
// returning a boolean indicating success or failure and the transition
func (t Ticket) Transition(db *mgo.Database, name string) (Transition, bool) {
	var workflow Workflow

	err := db.C("workflows").FindId(t.Workflow).One(&workflow)
	if err != nil {
		log.Println(err.Error())
		return Transition{}, false
	}

	for _, transition := range workflow.Transitions {
		if transition.Name == name && t.Status == transition.FromStatus {
			return transition, true
		}
	}

	return Transition{}, false
}

// Comment is a comment on an issue / ticket.
type Comment struct {
	UpdatedDate time.Time `json:"updatedDate"`
	CreatedDate time.Time `json:"createdDate"`
	Body        string    `json:"body" required:"true"`
	Author      string    `json:"author" required:"true"`
}

func (c *Comment) String() string {
	return jsonString(c)
}
<commit_msg>Add watchers to ticket<commit_after>// Copyright 2017 Mathew Robinson <mrobinson@praelatus.io>. All rights reserved.
// Use of this source code is governed by the AGPLv3 license that can be found in
// the LICENSE file.

package models

import (
	"log"
	"time"

	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// StatusType represents the statuses type based on the basic three types:
// TODO, In Progress, or Done
type StatusType string

// Available status types
const (
	StatusTodo       StatusType = "TODO"
	StatusInProgress            = "IN_PROGRESS"
	StatusDone                  = "Done"

	// StatusNull is a special type used for transitions that allow from any or
	// for create transitions
	StatusNull = "NULL"
)

// Status is a ticket and workflow status
type Status struct {
	Name string     `bson:"name" json:"name"`
	Type StatusType `bson:"type" json:"type"`
}

// Ticket represents a ticket
type Ticket struct {
	CreatedDate time.Time `json:"createdDate"`
	UpdatedDate time.Time `json:"updatedDate"`
	Key         string    `bson:"_id" json:"key"`
	Summary     string    `json:"summary" required:"true"`
	Description string    `json:"description" required:"true"`
	Status      Status    `json:"status"`
	Reporter    string    `json:"reporter" required:"true"`
	Assignee    string    `json:"assignee"`
	Type        string    `json:"type" required:"true"`
	Labels      []string  `json:"labels"`
	Watchers    []string  `json:"watchers"`

	Fields   []Field   `json:"fields"`
	Comments []Comment `json:"comments,omitempty"`

	Workflow bson.ObjectId `json:"workflow"`
	Project  string        `json:"project" required:"true"`
}

func (t Ticket) String() string {
	return jsonString(t)
}

// Transition searches through the available transitions for the ticket
// returning a boolean indicating success or failure and the transition
func (t Ticket) Transition(db *mgo.Database, name string) (Transition, bool) {
	var workflow Workflow

	err := db.C("workflows").FindId(t.Workflow).One(&workflow)
	if err != nil {
		log.Println(err.Error())
		return Transition{}, false
	}

	for _, transition := range workflow.Transitions {
		if transition.Name == name && t.Status == transition.FromStatus {
			return transition, true
		}
	}

	return Transition{}, false
}

// Comment is a comment on an issue / ticket.
type Comment struct {
	UpdatedDate time.Time `json:"updatedDate"`
	CreatedDate time.Time `json:"createdDate"`
	Body        string    `json:"body" required:"true"`
	Author      string    `json:"author" required:"true"`
}

func (c *Comment) String() string {
	return jsonString(c)
}
<|endoftext|>


<commit_before>package photosync

import (
	"os"
	"io"
	"fmt"
	"log"
	"net/url"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"encoding/xml"
	"strconv"
	"sync"
	"github.com/garyburd/go-oauth/oauth"
	"mime/multipart"
	"bytes"
)

const flickrTimeLayout = "2006-01-02 15:04:05"

type Photo struct {
	Id string
	Owner string
	Secret string
	Title string
	Ispublic int `json:"string"`
	Isfriend int `json:"string"`
	Isfamily int `json:"string"`
}

type PhotoInfo struct {
	Rotation int
	Originalformat string
	Media string
}

type PhotoSize struct {
	Label string
	Source string
}

type FlickrApiResponse struct {
	Stat string
	Data struct {
		Page int
		Pages int
		Perpage int
		Total string
		Photos []Photo `json:"photo"`
	} `json:"photos"`
	User FlickrUser `json:"user"`
	PhotoDetails PhotoInfo `json:"photo"`
	SizeData struct {
		Sizes []PhotoSize `json:"size"`
	} `json:"sizes"`
}

type FlickrUploadResponse struct {
	XMLName xml.Name `xml:"rsp"`
	Status string `xml:"stat,attr"`
	PhotoId string `xml:"photoid"`
}

type FlickrUser struct {
	Id string
	Username struct {
		Content string `json:"_content"`
	} `json:"username"`
}

type FlickrAPI struct {
	config PhotosyncConfig
	FlickrUserId string `json:"flickr_user_id"`
	apiBase string
	form url.Values
	oauthClient oauth.Client
}

// ***** Public Functions *****

func NewFlickrAPI(config *PhotosyncConfig) *FlickrAPI {
	return &FlickrAPI{
		config: *config, // config the value is set in photosync.go
		apiBase: "https://api.flickr.com/services",
		form: url.Values{ // default querystring values
			"format": {"json"},
			"nojsoncallback": {"1"},
		},
		oauthClient: oauth.Client {
			TemporaryCredentialRequestURI: "https://api.flickr.com/services/oauth/request_token",
			ResourceOwnerAuthorizationURI: "https://api.flickr.com/services/oauth/authorize",
			TokenRequestURI:               "https://api.flickr.com/services/oauth/access_token",
			Credentials: config.Consumer, // setup the consumer key and secret from the confis
		},
	}
}

func (this *FlickrAPI) GetPhotos(user *FlickrUser) (*PhotosMap, error) {
	this.form.Set("user_id", user.Id)
	defer this.form.Del("user_id") // remove from form values when done

	this.form.Set("media", "photos")
	defer this.form.Del("media") // remove from form values when done

	return this.Search(&this.form)
}

func (this *FlickrAPI) GetVideos(user *FlickrUser) (*PhotosMap, error) {
	this.form.Set("user_id", user.Id)
	defer this.form.Del("user_id") // remove from form values when done

	this.form.Set("media", "videos")
	defer this.form.Del("media") // remove from form values when done

	return this.Search(&this.form)
}

func (this *FlickrAPI) Search(form *url.Values) (*PhotosMap, error) {
	form.Set("method", "flickr.photos.search")

	// needed for getAllPages
	form.Set("per_page", "500") // max page size
	defer form.Del("per_page") // remove from form values when done

	photos := make(PhotosMap)

	err := this.getAllPages(func(data *FlickrApiResponse) {
		// extract into photos map
		for _, img := range data.Data.Photos {
			photos[img.Title] = img
		}
	})

	return &photos, err
}

func (this *FlickrAPI) GetLogin() (*FlickrUser, error) {
	this.form.Set("method", "flickr.test.login")

	data, err := this.apiGet()
	if err != nil {
		return nil, err
	}

	return &data.User, nil
}

func (this *FlickrAPI) GetExtention(info *PhotoInfo) (string, error) {
	switch info.Media {
	case "photo":
		return "jpg", nil
	case "video":
		return "mp4", nil
	default:
		return "", Error{"Unable to find file extention."}
	}
}

func (this *FlickrAPI) GetInfo(p *Photo) (*PhotoInfo, error) {
	this.form.Set("method", "flickr.photos.getInfo")

	this.form.Set("photo_id", p.Id)
	defer this.form.Del("photo_id") // remove from form values when done

	data, err := this.apiGet()
	if err != nil {
		return nil, err
	}

	return &data.PhotoDetails, nil
}

func (this *FlickrAPI) GetSizes(p *Photo) (*[]PhotoSize, error) {
	this.form.Set("method", "flickr.photos.getSizes")

	this.form.Set("photo_id", p.Id)
	defer this.form.Del("photo_id") // remove from form values when done

	data, err := this.apiGet()
	if err != nil {
		return nil, err
	}

	return &data.SizeData.Sizes, nil
}

func (this *FlickrAPI) SetTitle(p *Photo, title string) error {
	this.form.Set("method", "flickr.photos.setMeta")

	this.form.Set("photo_id", string(p.Id))
	defer this.form.Del("photo_id") // remove from form values when done

	this.form.Set("title", title)
	defer this.form.Del("title")

	_, err := this.apiGet()

	return err
}

func (this *FlickrAPI) SetDate(photoId string, date string) error {
	this.form.Set("method", "flickr.photos.setDates")

	this.form.Set("photo_id", photoId)
	defer this.form.Del("photo_id") // remove from form values when done

	this.form.Set("date_taken", date)
	defer this.form.Del("date_taken")

	_, err := this.apiGet()

	return err
}

func (this *FlickrAPI) Upload(path string, file os.FileInfo) (*FlickrUploadResponse, error) {
	// Prepare a form that you will submit to that URL.
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	// Add your image file
	f, err := os.Open(path)
	if err != nil { return nil, err }

	fw, err := w.CreateFormFile("photo", file.Name())
	if err != nil { return nil, err }

	if _, err = io.Copy(fw, f); err != nil { return nil, err }

	// close this to get the terminating boundary
	w.Close()

	// create the request
	req, err := http.NewRequest("POST", this.apiBase+"/upload/", &b)
	if err != nil { return nil, err }

	// set the content type for the mutlipart
	req.Header.Set("Content-Type", w.FormDataContentType())

	// add the oauth sig as well
	req.Header.Set("Authorization", this.oauthClient.AuthorizationHeader(&this.config.Access, "POST", req.URL, url.Values{}))

	// do the actual post
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil { return nil, err }

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil { return nil, err }

	// Check the response
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bad status: %s", resp.Status)
	}

	xr := FlickrUploadResponse{}
	if err := xml.Unmarshal(body, &xr); err != nil { return nil, err }

	if xr.Status != "ok" {
		return nil, Error{"failed status on upload"}
	}

	return &xr, nil
}

func (this *FlickrAPI) Download(info *PhotoInfo, p *Photo) error {
	sizes, _ := this.GetSizes(p)
	ext, _ := this.GetExtention(info)

	for _, v := range *sizes {
		if (info.Media == "video" && v.Label == "Video Original") || (info.Media == "photo" && v.Label == "Original") {
			out, err := os.Create(p.Title+"."+ext)
			if err != nil { return err }

			r, err := http.Get(v.Source)
			if err != nil { return err }

			defer r.Body.Close()

			n, err := io.Copy(out, r.Body)

			fmt.Println("written ",n)
		}
	}

	return nil
}

// ***** Private Functions *****

func (this *FlickrAPI) apiGet() (*FlickrApiResponse, error) {
	resp := FlickrApiResponse{}
	r, err := this.oauthClient.Get(http.DefaultClient, &this.config.Access, this.apiBase+"/rest", this.form)
	if err != nil { return nil, err }

	defer r.Body.Close()

	if r.StatusCode != 200 {
		return nil, &Error{r.Status}
	}

	contents, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(contents, &resp)
	if err != nil {
		return nil, err
	}
	if resp.Stat != "ok" {
		return nil, &Error{ string(contents) }
	}

	return &resp, nil
}

func (this *FlickrAPI) getAllPages(fn func(*FlickrApiResponse)) error {
	var wg sync.WaitGroup

	data, err := this.apiGet()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print("\rloading: ",int((float32(1)/float32(data.Data.Pages))*100),"%")
	wg.Add(data.Data.Pages)
	//go func() {
	func() {
		defer wg.Done()
		fn(data)
	}()

	// get the rest of the pages
	for page := 2; page <= data.Data.Pages; page++ {
		// comment out the parallel requesting as the flickr api seems occasionally return a dup page response
		//go func(page int) {
		func(page int) {
			defer wg.Done()

			this.form.Set("page", strconv.Itoa(page))
			defer this.form.Del("page")

			data, err := this.apiGet()
			if err != nil {
				log.Fatal(err)
			}

			fmt.Print("\rloading: ",int((float32(page)/float32(data.Data.Pages))*100),"%")

			fn(data)
		}(page)
	}

	wg.Wait()
	fmt.Println("")

	return nil
}
<commit_msg>add the rest of the http methods like post, put, and delete<commit_after>package photosync

import (
	"os"
	"io"
	"fmt"
	"log"
	"net/url"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"encoding/xml"
	"strconv"
	"sync"
	"github.com/garyburd/go-oauth/oauth"
	"mime/multipart"
	"bytes"
)

const flickrTimeLayout = "2006-01-02 15:04:05"

type Photo struct {
	Id string
	Owner string
	Secret string
	Title string
	Ispublic int `json:"string"`
	Isfriend int `json:"string"`
	Isfamily int `json:"string"`
}

type PhotoInfo struct {
	Rotation int
	Originalformat string
	Media string
}

type PhotoSize struct {
	Label string
	Source string
}

type FlickrApiResponse struct {
	Stat string
	Data struct {
		Page int
		Pages int
		Perpage int
		Total string
		Photos []Photo `json:"photo"`
	} `json:"photos"`
	User FlickrUser `json:"user"`
	PhotoDetails PhotoInfo `json:"photo"`
	SizeData struct {
		Sizes []PhotoSize `json:"size"`
	} `json:"sizes"`
}

type FlickrUploadResponse struct {
	XMLName xml.Name `xml:"rsp"`
	Status string `xml:"stat,attr"`
	PhotoId string `xml:"photoid"`
}

type FlickrUser struct {
	Id string
	Username struct {
		Content string `json:"_content"`
	} `json:"username"`
}

type FlickrAPI struct {
	config PhotosyncConfig
	FlickrUserId string `json:"flickr_user_id"`
	apiBase string
	form url.Values
	oauthClient oauth.Client
}

// ***** Public Functions *****

func NewFlickrAPI(config *PhotosyncConfig) *FlickrAPI {
	return &FlickrAPI{
		config: *config, // config the value is set in photosync.go
		apiBase: "https://api.flickr.com/services",
		form: url.Values{ // default querystring values
			"format": {"json"},
			"nojsoncallback": {"1"},
		},
		oauthClient: oauth.Client {
			TemporaryCredentialRequestURI: "https://api.flickr.com/services/oauth/request_token",
			ResourceOwnerAuthorizationURI: "https://api.flickr.com/services/oauth/authorize",
			TokenRequestURI:               "https://api.flickr.com/services/oauth/access_token",
			Credentials: config.Consumer, // setup the consumer key and secret from the confis
		},
	}
}

func (this *FlickrAPI) GetPhotos(user *FlickrUser) (*PhotosMap, error) {
	this.form.Set("user_id", user.Id)
	defer this.form.Del("user_id") // remove from form values when done

	this.form.Set("media", "photos")
	defer this.form.Del("media") // remove from form values when done

	return this.Search(&this.form)
}

func (this *FlickrAPI) GetVideos(user *FlickrUser) (*PhotosMap, error) {
	this.form.Set("user_id", user.Id)
	defer this.form.Del("user_id") // remove from form values when done

	this.form.Set("media", "videos")
	defer this.form.Del("media") // remove from form values when done

	return this.Search(&this.form)
}

func (this *FlickrAPI) Search(form *url.Values) (*PhotosMap, error) {
	form.Set("method", "flickr.photos.search")

	// needed for getAllPages
	form.Set("per_page", "500") // max page size
	defer form.Del("per_page") // remove from form values when done

	photos := make(PhotosMap)

	err := this.getAllPages(func(data *FlickrApiResponse) {
		// extract into photos map
		for _, img := range data.Data.Photos {
			photos[img.Title] = img
		}
	})

	return &photos, err
}

func (this *FlickrAPI) GetLogin() (*FlickrUser, error) {
	this.form.Set("method", "flickr.test.login")

	data, err := this.get()
	if err != nil {
		return nil, err
	}

	return &data.User, nil
}

func (this *FlickrAPI) GetExtention(info *PhotoInfo) (string, error) {
	switch info.Media {
	case "photo":
		return "jpg", nil
	case "video":
		return "mp4", nil
	default:
		return "", Error{"Unable to find file extention."}
	}
}

func (this *FlickrAPI) GetInfo(p *Photo) (*PhotoInfo, error) {
	this.form.Set("method", "flickr.photos.getInfo")

	this.form.Set("photo_id", p.Id)
	defer this.form.Del("photo_id") // remove from form values when done

	data, err := this.get()
	if err != nil {
		return nil, err
	}

	return &data.PhotoDetails, nil
}

func (this *FlickrAPI) GetSizes(p *Photo) (*[]PhotoSize, error) {
	this.form.Set("method", "flickr.photos.getSizes")

	this.form.Set("photo_id", p.Id)
	defer this.form.Del("photo_id") // remove from form values when done

	data, err := this.get()
	if err != nil {
		return nil, err
	}

	return &data.SizeData.Sizes, nil
}

func (this *FlickrAPI) SetTitle(p *Photo, title string) error {
	this.form.Set("method", "flickr.photos.setMeta")

	this.form.Set("photo_id", string(p.Id))
	defer this.form.Del("photo_id") // remove from form values when done

	this.form.Set("title", title)
	defer this.form.Del("title")

	_, err := this.post()

	return err
}

func (this *FlickrAPI) SetDate(photoId string, date string) error {
	this.form.Set("method", "flickr.photos.setDates")

	this.form.Set("photo_id", photoId)
	defer this.form.Del("photo_id") // remove from form values when done

	this.form.Set("date_taken", date)
	defer this.form.Del("date_taken")

	_, err := this.get()

	return err
}

func (this *FlickrAPI) Upload(path string, file os.FileInfo) (*FlickrUploadResponse, error) {
	// Prepare a form that you will submit to that URL.
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	// Add your image file
	f, err := os.Open(path)
	if err != nil { return nil, err }

	fw, err := w.CreateFormFile("photo", file.Name())
	if err != nil { return nil, err }

	if _, err = io.Copy(fw, f); err != nil { return nil, err }

	// close this to get the terminating boundary
	w.Close()

	// create the request
	req, err := http.NewRequest("POST", this.apiBase+"/upload/", &b)
	if err != nil { return nil, err }

	// set the content type for the mutlipart
	req.Header.Set("Content-Type", w.FormDataContentType())

	// add the oauth sig as well
	req.Header.Set("Authorization", this.oauthClient.AuthorizationHeader(&this.config.Access, "POST", req.URL, url.Values{}))

	// do the actual post
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil { return nil, err }

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil { return nil, err }

	// Check the response
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bad status: %s", resp.Status)
	}

	xr := FlickrUploadResponse{}
	if err := xml.Unmarshal(body, &xr); err != nil { return nil, err }

	if xr.Status != "ok" {
		return nil, Error{"failed status on upload"}
	}

	return &xr, nil
}

func (this *FlickrAPI) Download(info *PhotoInfo, p *Photo) error {
	sizes, _ := this.GetSizes(p)
	ext, _ := this.GetExtention(info)

	for _, v := range *sizes {
		if (info.Media == "video" && v.Label == "Video Original") || (info.Media == "photo" && v.Label == "Original") {
			out, err := os.Create(p.Title+"."+ext)
			if err != nil { return err }

			r, err := http.Get(v.Source)
			if err != nil { return err }

			defer r.Body.Close()

			n, err := io.Copy(out, r.Body)

			fmt.Println("written ",n)
		}
	}

	return nil
}

// ***** Private Functions *****

func (this *FlickrAPI) get() (*FlickrApiResponse, error) {
	return this.do("GET")
}

func (this *FlickrAPI) post() (*FlickrApiResponse, error) {
	return this.do("POST")
}

func (this *FlickrAPI) put() (*FlickrApiResponse, error) {
	return this.do("PUT")
}

func (this *FlickrAPI) del() (*FlickrApiResponse, error) {
	return this.do("DELETE")
}

func (this *FlickrAPI) do(method string) (*FlickrApiResponse, error) {
	resp := FlickrApiResponse{}
	methodFunc := this.oauthClient.Get
	switch method { // override the default method of get
		case "POST":
			methodFunc = this.oauthClient.Post
		case "PUT":
			methodFunc = this.oauthClient.Put
		case "DELETE":
			methodFunc = this.oauthClient.Delete
	}
	r, err := methodFunc(http.DefaultClient, &this.config.Access, this.apiBase+"/rest", this.form)
	if err != nil { return nil, err }

	defer r.Body.Close()

	if r.StatusCode != 200 {
		return nil, &Error{r.Status}
	}

	contents, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(contents, &resp)
	if err != nil {
		return nil, err
	}
	if resp.Stat != "ok" {
		return nil, &Error{ string(contents) }
	}

	return &resp, nil
}

func (this *FlickrAPI) getAllPages(fn func(*FlickrApiResponse)) error {
	var wg sync.WaitGroup

	data, err := this.get()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print("\rloading: ",int((float32(1)/float32(data.Data.Pages))*100),"%")
	wg.Add(data.Data.Pages)
	//go func() {
	func() {
		defer wg.Done()
		fn(data)
	}()

	// get the rest of the pages
	for page := 2; page <= data.Data.Pages; page++ {
		// comment out the parallel requesting as the flickr api seems occasionally return a dup page response
		//go func(page int) {
		func(page int) {
			defer wg.Done()

			this.form.Set("page", strconv.Itoa(page))
			defer this.form.Del("page")

			data, err := this.get()
			if err != nil {
				log.Fatal(err)
			}

			fmt.Print("\rloading: ",int((float32(page)/float32(data.Data.Pages))*100),"%")

			fn(data)
		}(page)
	}

	wg.Wait()
	fmt.Println("")

	return nil
}
<|endoftext|>


<commit_before>package w32

// nt!_ALPC_MESSAGE_ATTRIBUTES
//  +0x000 AllocatedAttributes : Uint4B
//  +0x004 ValidAttributes  : Uint4B
type ALPC_MESSAGE_ATTRIBUTES struct {
	AllocatedAttributes uint32
	ValidAttributes     uint32
}

// nt!_CLIENT_ID
//  +0x000 UniqueProcess    : Ptr64 Void
//  +0x008 UniqueThread     : Ptr64 Void
type CLIENT_ID struct {
	UniqueProcess uintptr
	UniqueThread  uintptr
}

// nt!_UNICODE_STRING
//  +0x000 Length           : Uint2B
//  +0x002 MaximumLength    : Uint2B
//  +0x008 Buffer           : Ptr64 Uint2B
type UNICODE_STRING struct {
	Length        uint16
	MaximumLength uint16
	Buffer        unsafe.Pointer
}

// nt!_OBJECT_ATTRIBUTES
//  +0x000 Length           : Uint4B
//  +0x008 RootDirectory    : Ptr64 Void
//  +0x010 ObjectName       : Ptr64 _UNICODE_STRING
//  +0x018 Attributes       : Uint4B
//  +0x020 SecurityDescriptor : Ptr64 Void
//  +0x028 SecurityQualityOfService : Ptr64 Void
type OBJECT_ATTRIBUTES struct {
	Length                   uint32
	padding1                 [4]byte
	RootDirectory            HANDLE
	ObjectName               *UNICODE_STRING
	Attributes               uint32
	padding2                 [4]byte
	SecurityDescriptor       *SECURITY_DESCRIPTOR
	SecurityQualityOfService *SECURITY_QUALITY_OF_SERVICE
}

// cf: http://j00ru.vexillium.org/?p=502
// nt!_PORT_MESSAGE
//    +0x000 u1               : <unnamed-tag>
//    +0x004 u2               : <unnamed-tag>
//    +0x008 ClientId         : _CLIENT_ID
//    +0x008 DoNotUseThisField : Float
//    +0x018 MessageId        : Uint4B
//    +0x020 ClientViewSize   : Uint8B
//    +0x020 CallbackId       : Uint4B
type PORT_MESSAGE struct {
	DataLength     uint16 // These are the two unnamed unions
	TotalLength    uint16 // without Length and ZeroInit
	Type           uint16
	DataInfoOffset uint16
	ClientId       CLIENT_ID
	MessageId      uint32
	padding        [4]byte
	ClientViewSize uint64
}

func (pm PORT_MESSAGE) CallbackId() uint32 {
	return uint32(pm.ClientViewSize >> 32)
}

func (pm PORT_MESSAGE) DoNotUseThisField() float {
	panic("WE TOLD YOU NOT TO USE THIS FIELD")
}

type SECURITY_IMPERSONATION_LEVEL int

const (
	SecurityAnonymous SECURITY_IMPERSONATION_LEVEL = iota
	SecurityIdentification
	SecurityImpersonation
	SecurityDelegation
)

// http://www.nirsoft.net/kernel_struct/vista/SECURITY_QUALITY_OF_SERVICE.html
// Added internal padding to make it 0xC bytes, as per the dt output below
type SECURITY_QUALITY_OF_SERVICE struct {
	Length              uint32
	ImpersonationLevel  SECURITY_IMPERSONATION_LEVEL
	ContextTrackingMode byte
	EffectiveOnly       byte
	padding             [2]byte
}

// nt!_ALPC_PORT_ATTRIBUTES
//  +0x000 Flags            : Uint4B
//  +0x004 SecurityQos      : _SECURITY_QUALITY_OF_SERVICE
//  +0x010 MaxMessageLength : Uint8B
//  +0x018 MemoryBandwidth  : Uint8B
//  +0x020 MaxPoolUsage     : Uint8B
//  +0x028 MaxSectionSize   : Uint8B
//  +0x030 MaxViewSize      : Uint8B
//  +0x038 MaxTotalSectionSize : Uint8B
//  +0x040 DupObjectTypes   : Uint4B
//  +0x044 Reserved         : Uint4B
type ALPC_PORT_ATTRIBUTES struct {
	Flags               uint32
	SecurityQos         SECURITY_QUALITY_OF_SERVICE
	MaxMessageLength    uint64
	MemoryBandwidth     uint64
	MaxPoolUsage        uint64
	MaxSectionSize      uint64
	MaxViewSize         uint64
	MaxTotalSectionSize uint64
	DupObjectTypes      uint32
	Reserved            uint32
}

//   typedef struct _TRANSFERRED_MESSAGE
// {
//     PORT_MESSAGE Header;
//     ULONG   Command;
//     WCHAR   MessageText[48];
// }
type TRANSFERRED_MESSAGE struct {
	Header      PORT_MESSAGE
	Command     uint32
	MessageText [48]uint16
}
<commit_msg>bugfix<commit_after>package w32

import (
	"unsafe"
)

// nt!_ALPC_MESSAGE_ATTRIBUTES
//  +0x000 AllocatedAttributes : Uint4B
//  +0x004 ValidAttributes  : Uint4B
type ALPC_MESSAGE_ATTRIBUTES struct {
	AllocatedAttributes uint32
	ValidAttributes     uint32
}

// nt!_CLIENT_ID
//  +0x000 UniqueProcess    : Ptr64 Void
//  +0x008 UniqueThread     : Ptr64 Void
type CLIENT_ID struct {
	UniqueProcess uintptr
	UniqueThread  uintptr
}

// nt!_UNICODE_STRING
//  +0x000 Length           : Uint2B
//  +0x002 MaximumLength    : Uint2B
//  +0x008 Buffer           : Ptr64 Uint2B
type UNICODE_STRING struct {
	Length        uint16
	MaximumLength uint16
	Buffer        unsafe.Pointer
}

// nt!_OBJECT_ATTRIBUTES
//  +0x000 Length           : Uint4B
//  +0x008 RootDirectory    : Ptr64 Void
//  +0x010 ObjectName       : Ptr64 _UNICODE_STRING
//  +0x018 Attributes       : Uint4B
//  +0x020 SecurityDescriptor : Ptr64 Void
//  +0x028 SecurityQualityOfService : Ptr64 Void
type OBJECT_ATTRIBUTES struct {
	Length                   uint32
	padding1                 [4]byte
	RootDirectory            HANDLE
	ObjectName               *UNICODE_STRING
	Attributes               uint32
	padding2                 [4]byte
	SecurityDescriptor       *SECURITY_DESCRIPTOR
	SecurityQualityOfService *SECURITY_QUALITY_OF_SERVICE
}

// cf: http://j00ru.vexillium.org/?p=502
// nt!_PORT_MESSAGE
//    +0x000 u1               : <unnamed-tag>
//    +0x004 u2               : <unnamed-tag>
//    +0x008 ClientId         : _CLIENT_ID
//    +0x008 DoNotUseThisField : Float
//    +0x018 MessageId        : Uint4B
//    +0x020 ClientViewSize   : Uint8B
//    +0x020 CallbackId       : Uint4B
type PORT_MESSAGE struct {
	DataLength     uint16 // These are the two unnamed unions
	TotalLength    uint16 // without Length and ZeroInit
	Type           uint16
	DataInfoOffset uint16
	ClientId       CLIENT_ID
	MessageId      uint32
	padding        [4]byte
	ClientViewSize uint64
}

func (pm PORT_MESSAGE) CallbackId() uint32 {
	return uint32(pm.ClientViewSize >> 32)
}

func (pm PORT_MESSAGE) DoNotUseThisField() float {
	panic("WE TOLD YOU NOT TO USE THIS FIELD")
}

type SECURITY_IMPERSONATION_LEVEL int

const (
	SecurityAnonymous SECURITY_IMPERSONATION_LEVEL = iota
	SecurityIdentification
	SecurityImpersonation
	SecurityDelegation
)

// http://www.nirsoft.net/kernel_struct/vista/SECURITY_QUALITY_OF_SERVICE.html
// Added internal padding to make it 0xC bytes, as per the dt output below
type SECURITY_QUALITY_OF_SERVICE struct {
	Length              uint32
	ImpersonationLevel  SECURITY_IMPERSONATION_LEVEL
	ContextTrackingMode byte
	EffectiveOnly       byte
	padding             [2]byte
}

// nt!_ALPC_PORT_ATTRIBUTES
//  +0x000 Flags            : Uint4B
//  +0x004 SecurityQos      : _SECURITY_QUALITY_OF_SERVICE
//  +0x010 MaxMessageLength : Uint8B
//  +0x018 MemoryBandwidth  : Uint8B
//  +0x020 MaxPoolUsage     : Uint8B
//  +0x028 MaxSectionSize   : Uint8B
//  +0x030 MaxViewSize      : Uint8B
//  +0x038 MaxTotalSectionSize : Uint8B
//  +0x040 DupObjectTypes   : Uint4B
//  +0x044 Reserved         : Uint4B
type ALPC_PORT_ATTRIBUTES struct {
	Flags               uint32
	SecurityQos         SECURITY_QUALITY_OF_SERVICE
	MaxMessageLength    uint64
	MemoryBandwidth     uint64
	MaxPoolUsage        uint64
	MaxSectionSize      uint64
	MaxViewSize         uint64
	MaxTotalSectionSize uint64
	DupObjectTypes      uint32
	Reserved            uint32
}

//   typedef struct _TRANSFERRED_MESSAGE
// {
//     PORT_MESSAGE Header;
//     ULONG   Command;
//     WCHAR   MessageText[48];
// }
type TRANSFERRED_MESSAGE struct {
	Header      PORT_MESSAGE
	Command     uint32
	MessageText [48]uint16
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/cloudfoundry-incubator/candiedyaml"
)

type ContainerConfig struct {
	ContainerName  string
	Daemon         bool
	Mounts         map[string]string
	PublishedPorts map[int]int
	EnvVars        map[string]string
	ImageTag       string
	Command        []string
	DropletDir     string
}

func NewStageContainerConfig(directories *Directories) (containerConfig *ContainerConfig) {
	containerConfig = &ContainerConfig{
		ContainerName: "cloudfocker-staging",
		Mounts:        directories.Mounts(),
		ImageTag:      "cloudfocker-base:latest",
		Command:       []string{"/focker/fock", "stage", "internal"},
	}
	return
}

func NewRuntimeContainerConfig(cloudfoundryDropletDir string) (containerConfig *ContainerConfig) {
	containerConfig = &ContainerConfig{
		ContainerName: "cloudfocker-runtime",
		Daemon:        true,
		Mounts: map[string]string{
			cloudfoundryDropletDir + "/app": "/app",
		},
		PublishedPorts: map[int]int{8080: 8080},
		EnvVars: map[string]string{
			"HOME":          "/app",
			"TMPDIR":        "/app/tmp",
			"PORT":          "8080",
			"VCAP_SERVICES": vcapServices(cloudfoundryDropletDir),
			"DATABASE_URL":  databaseURL(cloudfoundryDropletDir),
		},
		ImageTag: "cloudfocker-base:latest",
		Command: append([]string{"/bin/bash", "/app/cloudfocker-start-1c4352a23e52040ddb1857d7675fe3cc.sh", "/app"},
			parseStartCommand(cloudfoundryDropletDir)...),
		DropletDir: cloudfoundryDropletDir,
	}
	return
}

func vcapServices(cloudfoundryDropletDir string) (services string) {
	servicesBytes, err := ioutil.ReadFile(cloudfoundryDropletDir + "/app/vcap_services.json")
	if err != nil {
		return
	}
	services = string(servicesBytes)
	return
}

type database struct {
	Credentials struct {
		URI string
	}
}

func databaseURL(cloudfoundryDropletDir string) (databaseURL string) {
	servicesBytes, err := ioutil.ReadFile(cloudfoundryDropletDir + "/app/vcap_services.json")
	if err != nil {
		return
	}

	var services map[string][]database

	json.Unmarshal(servicesBytes, &services)

	for _, serviceDatabase := range services {
		if len(serviceDatabase) > 0 && serviceDatabase[0].Credentials.URI != "" {
			databaseURL = serviceDatabase[0].Credentials.URI
		}
	}

	return
}

type StagingInfoYml struct {
	DetectedBuildpack string `yaml:"detected_buildpack"`
	StartCommand      string `yaml:"start_command"`
}

type ProcfileYml struct {
	Web string `yaml:"web"`
}

func parseStartCommand(cloudfoundryDropletDir string) (startCommand []string) {
	stagingInfoFile, err := os.Open(cloudfoundryDropletDir + "/staging_info.yml")
	if err == nil {
		stagingInfo := new(StagingInfoYml)
		decoder := candiedyaml.NewDecoder(stagingInfoFile)
		err = decoder.Decode(stagingInfo)
		if err != nil {
			log.Fatalf("Failed to decode document: %s", err)
		}
		startCommand = strings.Split(stagingInfo.StartCommand, " ")
		if startCommand[0] != "" {
			return
		}
		procfileFile, err := os.Open(cloudfoundryDropletDir + "/app/Procfile")
		if err == nil {
			procfileInfo := new(ProcfileYml)
			decoder := candiedyaml.NewDecoder(procfileFile)
			err = decoder.Decode(procfileInfo)
			if err != nil {
				log.Fatalf("Failed to decode document: %s", err)
			}
			startCommand = strings.Split(procfileInfo.Web, " ")
			return
		}
	}
	log.Fatal("Unable to find staging_info.yml")
	return
}
<commit_msg>renamed cloudfoundryDropletDir to dropletDir<commit_after>package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/cloudfoundry-incubator/candiedyaml"
)

type ContainerConfig struct {
	ContainerName  string
	Daemon         bool
	Mounts         map[string]string
	PublishedPorts map[int]int
	EnvVars        map[string]string
	ImageTag       string
	Command        []string
	DropletDir     string
}

func NewStageContainerConfig(directories *Directories) (containerConfig *ContainerConfig) {
	containerConfig = &ContainerConfig{
		ContainerName: "cloudfocker-staging",
		Mounts:        directories.Mounts(),
		ImageTag:      "cloudfocker-base:latest",
		Command:       []string{"/focker/fock", "stage", "internal"},
	}
	return
}

func NewRuntimeContainerConfig(dropletDir string) (containerConfig *ContainerConfig) {
	containerConfig = &ContainerConfig{
		ContainerName: "cloudfocker-runtime",
		Daemon:        true,
		Mounts: map[string]string{
			dropletDir + "/app": "/app",
		},
		PublishedPorts: map[int]int{8080: 8080},
		EnvVars: map[string]string{
			"HOME":          "/app",
			"TMPDIR":        "/app/tmp",
			"PORT":          "8080",
			"VCAP_SERVICES": vcapServices(dropletDir),
			"DATABASE_URL":  databaseURL(dropletDir),
		},
		ImageTag: "cloudfocker-base:latest",
		Command: append([]string{"/bin/bash", "/app/cloudfocker-start-1c4352a23e52040ddb1857d7675fe3cc.sh", "/app"},
			parseStartCommand(dropletDir)...),
		DropletDir: dropletDir,
	}
	return
}

func vcapServices(dropletDir string) (services string) {
	servicesBytes, err := ioutil.ReadFile(dropletDir + "/app/vcap_services.json")
	if err != nil {
		return
	}
	services = string(servicesBytes)
	return
}

type database struct {
	Credentials struct {
		URI string
	}
}

func databaseURL(dropletDir string) (databaseURL string) {
	servicesBytes, err := ioutil.ReadFile(dropletDir + "/app/vcap_services.json")
	if err != nil {
		return
	}

	var services map[string][]database

	json.Unmarshal(servicesBytes, &services)

	for _, serviceDatabase := range services {
		if len(serviceDatabase) > 0 && serviceDatabase[0].Credentials.URI != "" {
			databaseURL = serviceDatabase[0].Credentials.URI
		}
	}

	return
}

type StagingInfoYml struct {
	DetectedBuildpack string `yaml:"detected_buildpack"`
	StartCommand      string `yaml:"start_command"`
}

type ProcfileYml struct {
	Web string `yaml:"web"`
}

func parseStartCommand(dropletDir string) (startCommand []string) {
	stagingInfoFile, err := os.Open(dropletDir + "/staging_info.yml")
	if err == nil {
		stagingInfo := new(StagingInfoYml)
		decoder := candiedyaml.NewDecoder(stagingInfoFile)
		err = decoder.Decode(stagingInfo)
		if err != nil {
			log.Fatalf("Failed to decode document: %s", err)
		}
		startCommand = strings.Split(stagingInfo.StartCommand, " ")
		if startCommand[0] != "" {
			return
		}
		procfileFile, err := os.Open(dropletDir + "/app/Procfile")
		if err == nil {
			procfileInfo := new(ProcfileYml)
			decoder := candiedyaml.NewDecoder(procfileFile)
			err = decoder.Decode(procfileInfo)
			if err != nil {
				log.Fatalf("Failed to decode document: %s", err)
			}
			startCommand = strings.Split(procfileInfo.Web, " ")
			return
		}
	}
	log.Fatal("Unable to find staging_info.yml")
	return
}
<|endoftext|>


<commit_before>// Copyright 2014 Matt Ho
//
// 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 wemo

import (
	"regexp"
	"time"
)

var belkinRE *regexp.Regexp = regexp.MustCompile(`http://([^/]+)/setup.xml`)

type Wemo struct {
	ipAddr string
	Debug  bool
}

func (self *Wemo) DiscoverAll(timeout time.Duration) ([]*Device, error) {
	urns := []string{
		"urn:Belkin:device:controllee:1",
		"urn:Belkin:device:light:1",
		"urn:Belkin:device:sensor:1",
	}

	var all []*Device
	for _, urn := range urns {
		devices, _ := self.Discover(urn, timeout)
		for _, device := range devices {
			all = append(all, device)
		}
	}

	return all, nil
}

func (self *Wemo) Discover(urn string, timeout time.Duration) ([]*Device, error) {
	locations, err := self.scan(urn, timeout)
	if err != nil {
		return nil, err
	}

	var devices []*Device
	for _, uri := range locations {
		if matches := belkinRE.FindStringSubmatch(uri.String()); len(matches) == 2 {
			host := matches[1]
			devices = append(devices, &Device{Host: host})
		}
	}

	return devices, nil
}
<commit_msg>added urn for netcam so it finds NetCam camera<commit_after>// Copyright 2014 Matt Ho
//
// 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 wemo

import (
	"regexp"
	"time"
)

var belkinRE *regexp.Regexp = regexp.MustCompile(`http://([^/]+)/setup.xml`)

type Wemo struct {
	ipAddr string
	Debug  bool
}

func (self *Wemo) DiscoverAll(timeout time.Duration) ([]*Device, error) {
	urns := []string{
		"urn:Belkin:device:controllee:1",
		"urn:Belkin:device:light:1",
		"urn:Belkin:device:sensor:1",
		"urn:Belkin:device:netcam:1",
	}

	var all []*Device
	for _, urn := range urns {
		devices, _ := self.Discover(urn, timeout)
		for _, device := range devices {
			all = append(all, device)
		}
	}

	return all, nil
}

func (self *Wemo) Discover(urn string, timeout time.Duration) ([]*Device, error) {
	locations, err := self.scan(urn, timeout)
	if err != nil {
		return nil, err
	}

	var devices []*Device
	for _, uri := range locations {
		if matches := belkinRE.FindStringSubmatch(uri.String()); len(matches) == 2 {
			host := matches[1]
			devices = append(devices, &Device{Host: host})
		}
	}
	return devices, nil
}
<|endoftext|>


<commit_before>package node

import (
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/docker/docker/api/client"
	"github.com/docker/docker/api/client/inspect"
	"github.com/docker/docker/cli"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/docker/engine-api/types/swarm"
	"github.com/docker/go-units"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
)

type inspectOptions struct {
	nodeIds []string
	format  string
	pretty  bool
}

func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
	var opts inspectOptions

	cmd := &cobra.Command{
		Use:   "inspect [OPTIONS] self|NODE [NODE...]",
		Short: "Display detailed information on one or more nodes",
		Args:  cli.RequiresMinArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			opts.nodeIds = args
			return runInspect(dockerCli, opts)
		},
	}

	flags := cmd.Flags()
	flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template")
	flags.BoolVar(&opts.pretty, "pretty", false, "Print the information in a human friendly format.")
	return cmd
}

func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
	client := dockerCli.Client()
	ctx := context.Background()
	getRef := func(ref string) (interface{}, []byte, error) {
		nodeRef, err := Reference(client, ctx, ref)
		if err != nil {
			return nil, nil, err
		}
		node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
		return node, nil, err
	}

	if !opts.pretty {
		return inspect.Inspect(dockerCli.Out(), opts.nodeIds, opts.format, getRef)
	}
	return printHumanFriendly(dockerCli.Out(), opts.nodeIds, getRef)
}

func printHumanFriendly(out io.Writer, refs []string, getRef inspect.GetRefFunc) error {
	for idx, ref := range refs {
		obj, _, err := getRef(ref)
		if err != nil {
			return err
		}
		printNode(out, obj.(swarm.Node))

		// TODO: better way to do this?
		// print extra space between objects, but not after the last one
		if idx+1 != len(refs) {
			fmt.Fprintf(out, "\n\n")
		}
	}
	return nil
}

// TODO: use a template
func printNode(out io.Writer, node swarm.Node) {
	fmt.Fprintf(out, "ID:\t\t\t%s\n", node.ID)
	ioutils.FprintfIfNotEmpty(out, "Name:\t\t\t%s\n", node.Spec.Name)
	if node.Spec.Labels != nil {
		fmt.Fprintln(out, "Labels:")
		for k, v := range node.Spec.Labels {
			fmt.Fprintf(out, " - %s = %s\n", k, v)
		}
	}

	ioutils.FprintfIfNotEmpty(out, "Hostname:\t\t%s\n", node.Description.Hostname)
	fmt.Fprintln(out, "Status:")
	fmt.Fprintf(out, " State:\t\t\t%s\n", client.PrettyPrint(node.Status.State))
	ioutils.FprintfIfNotEmpty(out, " Message:\t\t%s\n", client.PrettyPrint(node.Status.Message))
	fmt.Fprintf(out, " Availability:\t\t%s\n", client.PrettyPrint(node.Spec.Availability))

	if node.ManagerStatus != nil {
		fmt.Fprintln(out, "Manager Status:")
		fmt.Fprintf(out, " Address:\t\t%s\n", node.ManagerStatus.Addr)
		fmt.Fprintf(out, " Raft Status:\t\t%s\n", client.PrettyPrint(node.ManagerStatus.Reachability))
		leader := "No"
		if node.ManagerStatus.Leader {
			leader = "Yes"
		}
		fmt.Fprintf(out, " Leader:\t\t%s\n", leader)
	}

	fmt.Fprintln(out, "Platform:")
	fmt.Fprintf(out, " Operating System:\t%s\n", node.Description.Platform.OS)
	fmt.Fprintf(out, " Architecture:\t\t%s\n", node.Description.Platform.Architecture)

	fmt.Fprintln(out, "Resources:")
	fmt.Fprintf(out, " CPUs:\t\t\t%d\n", node.Description.Resources.NanoCPUs/1e9)
	fmt.Fprintf(out, " Memory:\t\t%s\n", units.BytesSize(float64(node.Description.Resources.MemoryBytes)))

	var pluginTypes []string
	pluginNamesByType := map[string][]string{}
	for _, p := range node.Description.Engine.Plugins {
		// append to pluginTypes only if not done previously
		if _, ok := pluginNamesByType[p.Type]; !ok {
			pluginTypes = append(pluginTypes, p.Type)
		}
		pluginNamesByType[p.Type] = append(pluginNamesByType[p.Type], p.Name)
	}

	if len(pluginTypes) > 0 {
		fmt.Fprintln(out, "Plugins:")
		sort.Strings(pluginTypes) // ensure stable output
		for _, pluginType := range pluginTypes {
			fmt.Fprintf(out, "  %s:\t\t%s\n", pluginType, strings.Join(pluginNamesByType[pluginType], ", "))
		}
	}
	fmt.Fprintf(out, "Engine Version:\t\t%s\n", node.Description.Engine.EngineVersion)

	if len(node.Description.Engine.Labels) != 0 {
		fmt.Fprintln(out, "Engine Labels:")
		for k, v := range node.Description.Engine.Labels {
			fmt.Fprintf(out, " - %s = %s", k, v)
		}
	}

}
<commit_msg>Adding Joined at to node inspect<commit_after>package node

import (
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/docker/docker/api/client"
	"github.com/docker/docker/api/client/inspect"
	"github.com/docker/docker/cli"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/docker/engine-api/types/swarm"
	"github.com/docker/go-units"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
)

type inspectOptions struct {
	nodeIds []string
	format  string
	pretty  bool
}

func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
	var opts inspectOptions

	cmd := &cobra.Command{
		Use:   "inspect [OPTIONS] self|NODE [NODE...]",
		Short: "Display detailed information on one or more nodes",
		Args:  cli.RequiresMinArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			opts.nodeIds = args
			return runInspect(dockerCli, opts)
		},
	}

	flags := cmd.Flags()
	flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template")
	flags.BoolVar(&opts.pretty, "pretty", false, "Print the information in a human friendly format.")
	return cmd
}

func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
	client := dockerCli.Client()
	ctx := context.Background()
	getRef := func(ref string) (interface{}, []byte, error) {
		nodeRef, err := Reference(client, ctx, ref)
		if err != nil {
			return nil, nil, err
		}
		node, _, err := client.NodeInspectWithRaw(ctx, nodeRef)
		return node, nil, err
	}

	if !opts.pretty {
		return inspect.Inspect(dockerCli.Out(), opts.nodeIds, opts.format, getRef)
	}
	return printHumanFriendly(dockerCli.Out(), opts.nodeIds, getRef)
}

func printHumanFriendly(out io.Writer, refs []string, getRef inspect.GetRefFunc) error {
	for idx, ref := range refs {
		obj, _, err := getRef(ref)
		if err != nil {
			return err
		}
		printNode(out, obj.(swarm.Node))

		// TODO: better way to do this?
		// print extra space between objects, but not after the last one
		if idx+1 != len(refs) {
			fmt.Fprintf(out, "\n\n")
		}
	}
	return nil
}

// TODO: use a template
func printNode(out io.Writer, node swarm.Node) {
	fmt.Fprintf(out, "ID:\t\t\t%s\n", node.ID)
	ioutils.FprintfIfNotEmpty(out, "Name:\t\t\t%s\n", node.Spec.Name)
	if node.Spec.Labels != nil {
		fmt.Fprintln(out, "Labels:")
		for k, v := range node.Spec.Labels {
			fmt.Fprintf(out, " - %s = %s\n", k, v)
		}
	}

	ioutils.FprintfIfNotEmpty(out, "Hostname:\t\t%s\n", node.Description.Hostname)
	fmt.Fprintf(out, "Joined at:\t\t%s\n", client.PrettyPrint(node.CreatedAt))
	fmt.Fprintln(out, "Status:")
	fmt.Fprintf(out, " State:\t\t\t%s\n", client.PrettyPrint(node.Status.State))
	ioutils.FprintfIfNotEmpty(out, " Message:\t\t%s\n", client.PrettyPrint(node.Status.Message))
	fmt.Fprintf(out, " Availability:\t\t%s\n", client.PrettyPrint(node.Spec.Availability))

	if node.ManagerStatus != nil {
		fmt.Fprintln(out, "Manager Status:")
		fmt.Fprintf(out, " Address:\t\t%s\n", node.ManagerStatus.Addr)
		fmt.Fprintf(out, " Raft Status:\t\t%s\n", client.PrettyPrint(node.ManagerStatus.Reachability))
		leader := "No"
		if node.ManagerStatus.Leader {
			leader = "Yes"
		}
		fmt.Fprintf(out, " Leader:\t\t%s\n", leader)
	}

	fmt.Fprintln(out, "Platform:")
	fmt.Fprintf(out, " Operating System:\t%s\n", node.Description.Platform.OS)
	fmt.Fprintf(out, " Architecture:\t\t%s\n", node.Description.Platform.Architecture)

	fmt.Fprintln(out, "Resources:")
	fmt.Fprintf(out, " CPUs:\t\t\t%d\n", node.Description.Resources.NanoCPUs/1e9)
	fmt.Fprintf(out, " Memory:\t\t%s\n", units.BytesSize(float64(node.Description.Resources.MemoryBytes)))

	var pluginTypes []string
	pluginNamesByType := map[string][]string{}
	for _, p := range node.Description.Engine.Plugins {
		// append to pluginTypes only if not done previously
		if _, ok := pluginNamesByType[p.Type]; !ok {
			pluginTypes = append(pluginTypes, p.Type)
		}
		pluginNamesByType[p.Type] = append(pluginNamesByType[p.Type], p.Name)
	}

	if len(pluginTypes) > 0 {
		fmt.Fprintln(out, "Plugins:")
		sort.Strings(pluginTypes) // ensure stable output
		for _, pluginType := range pluginTypes {
			fmt.Fprintf(out, "  %s:\t\t%s\n", pluginType, strings.Join(pluginNamesByType[pluginType], ", "))
		}
	}
	fmt.Fprintf(out, "Engine Version:\t\t%s\n", node.Description.Engine.EngineVersion)

	if len(node.Description.Engine.Labels) != 0 {
		fmt.Fprintln(out, "Engine Labels:")
		for k, v := range node.Description.Engine.Labels {
			fmt.Fprintf(out, " - %s = %s", k, v)
		}
	}

}
<|endoftext|>


<commit_before>package openstack

import (
	"fmt"
	"log"
	"time"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceFWPolicyV1() *schema.Resource {
	return &schema.Resource{
		Create: resourceFWPolicyV1Create,
		Read:   resourceFWPolicyV1Read,
		Update: resourceFWPolicyV1Update,
		Delete: resourceFWPolicyV1Delete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"audited": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"shared": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"tenant_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},
			"rules": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error {

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	v := d.Get("rules").([]interface{})

	log.Printf("[DEBUG] Rules found : %#v", v)
	log.Printf("[DEBUG] Rules count : %d", len(v))

	rules := make([]string, len(v))
	for i, v := range v {
		rules[i] = v.(string)
	}

	audited := d.Get("audited").(bool)
	shared := d.Get("shared").(bool)

	opts := policies.CreateOpts{
		Name:        d.Get("name").(string),
		Description: d.Get("description").(string),
		Audited:     &audited,
		Shared:      &shared,
		TenantID:    d.Get("tenant_id").(string),
		Rules:       rules,
	}

	log.Printf("[DEBUG] Create firewall policy: %#v", opts)

	policy, err := policies.Create(networkingClient, opts).Extract()
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Firewall policy created: %#v", policy)

	d.SetId(policy.ID)

	return resourceFWPolicyV1Read(d, meta)
}

func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] Retrieve information about firewall policy: %s", d.Id())

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	policy, err := policies.Get(networkingClient, d.Id()).Extract()
	if err != nil {
		return CheckDeleted(d, err, "FW policy")
	}

	log.Printf("[DEBUG] Read OpenStack Firewall Policy %s: %#v", d.Id(), policy)

	d.Set("name", policy.Name)
	d.Set("description", policy.Description)
	d.Set("shared", policy.Shared)
	d.Set("audited", policy.Audited)
	d.Set("tenant_id", policy.TenantID)
	d.Set("rules", policy.Rules)
	return nil
}

func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error {

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	opts := policies.UpdateOpts{}

	if d.HasChange("name") {
		opts.Name = d.Get("name").(string)
	}

	if d.HasChange("description") {
		opts.Description = d.Get("description").(string)
	}

	if d.HasChange("rules") {
		v := d.Get("rules").([]interface{})

		log.Printf("[DEBUG] Rules found : %#v", v)
		log.Printf("[DEBUG] Rules count : %d", len(v))

		rules := make([]string, len(v))
		for i, v := range v {
			rules[i] = v.(string)
		}
		opts.Rules = rules
	}

	log.Printf("[DEBUG] Updating firewall policy with id %s: %#v", d.Id(), opts)

	err = policies.Update(networkingClient, d.Id(), opts).Err
	if err != nil {
		return err
	}

	return resourceFWPolicyV1Read(d, meta)
}

func resourceFWPolicyV1Delete(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] Destroy firewall policy: %s", d.Id())

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	for i := 0; i < 15; i++ {

		err = policies.Delete(networkingClient, d.Id()).Err
		if err == nil {
			break
		}

		if _, ok := err.(gophercloud.ErrDefault404); !ok {
			return err
		}

		// This error usually means that the policy is attached
		// to a firewall. At this point, the firewall is probably
		// being delete. So, we retry a few times.

		time.Sleep(time.Second * 2)
	}

	return err
}
<commit_msg>provider/openstack: gophercloud migration: fwaas policy response error handling<commit_after>package openstack

import (
	"fmt"
	"log"
	"time"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceFWPolicyV1() *schema.Resource {
	return &schema.Resource{
		Create: resourceFWPolicyV1Create,
		Read:   resourceFWPolicyV1Read,
		Update: resourceFWPolicyV1Update,
		Delete: resourceFWPolicyV1Delete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"audited": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"shared": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"tenant_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},
			"rules": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error {

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	v := d.Get("rules").([]interface{})

	log.Printf("[DEBUG] Rules found : %#v", v)
	log.Printf("[DEBUG] Rules count : %d", len(v))

	rules := make([]string, len(v))
	for i, v := range v {
		rules[i] = v.(string)
	}

	audited := d.Get("audited").(bool)
	shared := d.Get("shared").(bool)

	opts := policies.CreateOpts{
		Name:        d.Get("name").(string),
		Description: d.Get("description").(string),
		Audited:     &audited,
		Shared:      &shared,
		TenantID:    d.Get("tenant_id").(string),
		Rules:       rules,
	}

	log.Printf("[DEBUG] Create firewall policy: %#v", opts)

	policy, err := policies.Create(networkingClient, opts).Extract()
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Firewall policy created: %#v", policy)

	d.SetId(policy.ID)

	return resourceFWPolicyV1Read(d, meta)
}

func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] Retrieve information about firewall policy: %s", d.Id())

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	policy, err := policies.Get(networkingClient, d.Id()).Extract()
	if err != nil {
		return CheckDeleted(d, err, "FW policy")
	}

	log.Printf("[DEBUG] Read OpenStack Firewall Policy %s: %#v", d.Id(), policy)

	d.Set("name", policy.Name)
	d.Set("description", policy.Description)
	d.Set("shared", policy.Shared)
	d.Set("audited", policy.Audited)
	d.Set("tenant_id", policy.TenantID)
	d.Set("rules", policy.Rules)
	return nil
}

func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error {

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	opts := policies.UpdateOpts{}

	if d.HasChange("name") {
		opts.Name = d.Get("name").(string)
	}

	if d.HasChange("description") {
		opts.Description = d.Get("description").(string)
	}

	if d.HasChange("rules") {
		v := d.Get("rules").([]interface{})

		log.Printf("[DEBUG] Rules found : %#v", v)
		log.Printf("[DEBUG] Rules count : %d", len(v))

		rules := make([]string, len(v))
		for i, v := range v {
			rules[i] = v.(string)
		}
		opts.Rules = rules
	}

	log.Printf("[DEBUG] Updating firewall policy with id %s: %#v", d.Id(), opts)

	err = policies.Update(networkingClient, d.Id(), opts).Err
	if err != nil {
		return err
	}

	return resourceFWPolicyV1Read(d, meta)
}

func resourceFWPolicyV1Delete(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] Destroy firewall policy: %s", d.Id())

	config := meta.(*Config)
	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
	}

	for i := 0; i < 15; i++ {

		err = policies.Delete(networkingClient, d.Id()).Err
		if err == nil {
			break
		}

		if _, ok := err.(gophercloud.ErrDefault404); ok {
			return nil
		}

		if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
			if errCode.Actual == 409 {
				// This error usually means that the policy is attached
				// to a firewall. At this point, the firewall is probably
				// being delete. So, we retry a few times.
				time.Sleep(time.Second * 2)
				continue
			}
		}

		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package subscriptions

import (
	"net/http"

	"github.com/gempir/gempbot/pkg/api"
	"github.com/gempir/gempbot/pkg/auth"
	"github.com/gempir/gempbot/pkg/chat"
	"github.com/gempir/gempbot/pkg/config"
	"github.com/gempir/gempbot/pkg/emotechief"
	"github.com/gempir/gempbot/pkg/eventsub"
	"github.com/gempir/gempbot/pkg/helix"
	"github.com/gempir/gempbot/pkg/store"
	"github.com/gempir/gempbot/pkg/user"
)

func Handler(w http.ResponseWriter, r *http.Request) {
	cfg := config.FromEnv()
	db := store.NewDatabase(cfg)
	helixClient := helix.NewClient(cfg, db)
	auth := auth.NewAuth(cfg, db, helixClient)
	userAdmin := user.NewUserAdmin(cfg, db, helixClient, nil)
	chatClient := chat.NewClient(cfg)
	go chatClient.Connect()
	emoteChief := emotechief.NewEmoteChief(cfg, db, helixClient, chatClient)
	eventSubManager := eventsub.NewEventSubManager(cfg, helixClient, db, emoteChief, chatClient)

	authResp, _, apiErr := auth.AttemptAuth(r, w)
	if apiErr != nil {
		return
	}
	userID := authResp.Data.UserID

	if r.URL.Query().Get("managing") != "" {
		userID, apiErr = userAdmin.CheckEditor(r, userAdmin.GetUserConfig(userID))
		if apiErr != nil {
			http.Error(w, apiErr.Error(), apiErr.Status())
			return
		}
	}

	if r.Method == http.MethodPut {
		eventSubManager.SubscribeChannelPoints(userID)
		eventSubManager.SubscribePredictions(userID)
	} else if r.Method == http.MethodDelete {
		eventSubManager.RemoveAllEventSubSubscriptions(userID)
	}

	api.WriteJson(w, "ok", http.StatusOK)
}
<commit_msg>don't sub like this<commit_after>package subscriptions

import (
	"net/http"

	"github.com/gempir/gempbot/pkg/api"
	"github.com/gempir/gempbot/pkg/auth"
	"github.com/gempir/gempbot/pkg/chat"
	"github.com/gempir/gempbot/pkg/config"
	"github.com/gempir/gempbot/pkg/emotechief"
	"github.com/gempir/gempbot/pkg/eventsub"
	"github.com/gempir/gempbot/pkg/helix"
	"github.com/gempir/gempbot/pkg/store"
	"github.com/gempir/gempbot/pkg/user"
)

func Handler(w http.ResponseWriter, r *http.Request) {
	cfg := config.FromEnv()
	db := store.NewDatabase(cfg)
	helixClient := helix.NewClient(cfg, db)
	auth := auth.NewAuth(cfg, db, helixClient)
	userAdmin := user.NewUserAdmin(cfg, db, helixClient, nil)
	chatClient := chat.NewClient(cfg)
	go chatClient.Connect()
	emoteChief := emotechief.NewEmoteChief(cfg, db, helixClient, chatClient)
	eventSubManager := eventsub.NewEventSubManager(cfg, helixClient, db, emoteChief, chatClient)

	authResp, _, apiErr := auth.AttemptAuth(r, w)
	if apiErr != nil {
		return
	}
	userID := authResp.Data.UserID

	if r.URL.Query().Get("managing") != "" {
		userID, apiErr = userAdmin.CheckEditor(r, userAdmin.GetUserConfig(userID))
		if apiErr != nil {
			http.Error(w, apiErr.Error(), apiErr.Status())
			return
		}
	}

	if r.Method == http.MethodPut {
		eventSubManager.SubscribePredictions(userID)
	} else if r.Method == http.MethodDelete {
		eventSubManager.RemoveAllEventSubSubscriptions(userID)
	}

	api.WriteJson(w, "ok", http.StatusOK)
}
<|endoftext|>