<commit_before>package ytdl

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/rs/zerolog/log"
)

const youtubeBaseURL = "https://www.youtube.com/watch"
const youtubeEmbeddedBaseURL = "https://www.youtube.com/embed/"
const youtubeVideoEURL = "https://youtube.googleapis.com/v/"
const youtubeVideoInfoURL = "https://www.youtube.com/get_video_info"
const youtubeDateFormat = "2006-01-02"

// VideoInfo contains the info a youtube video
type VideoInfo struct {
	ID              string     // The video ID
	Title           string     // The video title
	Description     string     // The video description
	DatePublished   time.Time  // The date the video was published
	Formats         FormatList // Formats the video is available in
	DASHManifestURL string     // URI of the DASH manifest file
	HLSManifestURL  string     // URI of the HLS manifest file
	Keywords        []string   // List of keywords associated with the video
	Uploader        string     // Author of the video
	Song            string
	Artist          string
	Album           string
	Writers         string
	Duration        time.Duration // Duration of the video
	htmlPlayerFile  string
}

// GetVideoInfo fetches info from a url string, url object, or a url string
func GetVideoInfo(cx context.Context, value interface{}) (*VideoInfo, error) {
	return DefaultClient.GetVideoInfo(cx, value)
}

// GetVideoInfo fetches info from a url string, url object, or a url string
func (c *Client) GetVideoInfo(cx context.Context, value interface{}) (*VideoInfo, error) {
	switch t := value.(type) {
	case *url.URL:
		videoID := extractVideoID(t)
		if len(videoID) == 0 {
			return nil, fmt.Errorf("invalid youtube URL, no video id")
		}
		return c.GetVideoInfoFromID(cx, videoID)
	case string:
		if strings.HasPrefix(t, "https://") {
			uri, err := url.ParseRequestURI(t)
			if err != nil {
				return nil, err
			}
			return c.GetVideoInfo(cx, uri)
		}
		return c.GetVideoInfoFromID(cx, t)
	default:
		return nil, fmt.Errorf("Identifier type must be a string, *url.URL, or []byte")
	}
}

// GetVideoInfoFromShortURL fetches video info from a short youtube url
func extractVideoID(u *url.URL) string {
	switch u.Host {
	case "www.youtube.com", "youtube.com", "m.youtube.com":
		if u.Path == "/watch" {
			return u.Query().Get("v")
		}
		if strings.HasPrefix(u.Path, "/embed/") {
			return u.Path[7:]
		}
	case "youtu.be":
		if len(u.Path) > 1 {
			return u.Path[1:]
		}
	}
	return ""
}

// GetVideoInfoFromID fetches video info from a youtube video id
func (c *Client) GetVideoInfoFromID(cx context.Context, id string) (*VideoInfo, error) {
	body, err := c.httpGetAndCheckResponseReadBody(cx, youtubeBaseURL+"?v="+id)

	if err != nil {
		return nil, err
	}
	return c.getVideoInfoFromHTML(cx, id, body)
}

// GetDownloadURL gets the download url for a format
func (c *Client) GetDownloadURL(cx context.Context, info *VideoInfo, format *Format) (*url.URL, error) {
	return c.getDownloadURL(cx, format, info.htmlPlayerFile)
}

// GetThumbnailURL returns a url for the thumbnail image
// with the given quality
func (info *VideoInfo) GetThumbnailURL(quality ThumbnailQuality) *url.URL {
	u, _ := url.Parse(fmt.Sprintf("http://img.youtube.com/vi/%s/%s.jpg",
		info.ID, quality))
	return u
}

// Download is a convenience method to download a format to an io.Writer
func (c *Client) Download(cx context.Context, info *VideoInfo, format *Format, dest io.Writer) error {
	u, err := c.GetDownloadURL(cx, info, format)
	if err != nil {
		return err
	}

	resp, err := c.httpGetAndCheckResponse(cx, u.String())
	if err != nil {
		return err
	}

	defer resp.Body.Close()
	_, err = io.Copy(dest, resp.Body)
	return err
}

var (
	regexpPlayerConfig          = regexp.MustCompile("ytplayer.config = (.*?);ytplayer.load")
	regexpInitialData           = regexp.MustCompile(`\["ytInitialData"\] = (.+);`)
	regexpInitialPlayerResponse = regexp.MustCompile(`\["ytInitialPlayerResponse"\] = (.+);`)
)

func (c *Client) getVideoInfoFromHTML(cx context.Context, id string, html []byte) (*VideoInfo, error) {
	info := &VideoInfo{}

	if matches := regexpInitialData.FindSubmatch(html); len(matches) > 0 {
		data := initialData{}

		if err := json.Unmarshal(matches[1], &data); err != nil {
			return nil, err
		}

		contents := data.Contents.TwoColumnWatchNextResults.Results.Results.Contents

		if len(contents) >= 2 {
			infoRenderer := contents[1].VideoSecondaryInfoRenderer

			info.Description = infoRenderer.Description.String()
			rows := infoRenderer.MetadataRowContainer.MetadataRowContainerRenderer.Rows

			info.Artist = rows.Get("Artist")
			info.Album = rows.Get("Album")
			info.Song = rows.Get("Song")
			info.Writers = rows.Get("Writers")
		}
	}

	info.ID = id

	var jsonConfig playerConfig

	// match json in javascript
	if matches := regexpPlayerConfig.FindSubmatch(html); len(matches) > 1 {
		err := json.Unmarshal(matches[1], &jsonConfig)
		if err != nil {
			return nil, err
		}
		//glog.Errorf("jsonCOnfig %s\n\n", jsonConfig.Args.PlayerResponse)
	} else {
		log.Debug().Msg("Unable to extract json from default url, trying embedded url")

		info, err := c.getVideoInfoFromEmbedded(cx, id)
		if err != nil {
			return nil, err
		}
		query := url.Values{
			"video_id": []string{id},
			"eurl":     []string{youtubeVideoEURL + id},
		}

		if sts, ok := info["sts"].(float64); ok {
			query.Add("sts", strconv.Itoa(int(sts)))
		}
		body, err := c.httpGetAndCheckResponseReadBody(cx, youtubeVideoInfoURL+"?"+query.Encode())
		if err != nil {
			return nil, fmt.Errorf("Unable to read video info: %w", err)
		}

		query, err = url.ParseQuery(string(body))
		if err != nil {
			return nil, fmt.Errorf("Unable to parse video info data: %w", err)
		}

		for k, v := range query {
			switch k {
			case "errorcode":
				jsonConfig.Args.Errorcode = v[0]
			case "reason":
				jsonConfig.Args.Reason = v[0]
			case "status":
				jsonConfig.Args.Status = v[0]
			case "player_response":
				jsonConfig.Args.PlayerResponse = v[0]
			case "url_encoded_fmt_stream_map":
				jsonConfig.Args.URLEncodedFmtStreamMap = v[0]
			case "adaptive_fmts":
				jsonConfig.Args.AdaptiveFmts = v[0]
			case "dashmpd":
				jsonConfig.Args.Dashmpd = v[0]
			default:
				// log.Debug().Msgf("unknown query param: %v", k)
			}
		}
	}

	inf := jsonConfig.Args
	if inf.Status == "fail" {
		return nil, fmt.Errorf("Error %s:%s", inf.Errorcode, inf.Reason)
	}

	var formats FormatList
	c.addFormatsByQueryStrings(&formats, strings.NewReader(inf.URLEncodedFmtStreamMap), false)
	if inf.AdaptiveFmts != "" {
		c.addFormatsByQueryStrings(&formats, strings.NewReader(inf.AdaptiveFmts), true)
	}
	if inf.PlayerResponse != "" {
		response := &playerResponse{}

		if err := json.Unmarshal([]byte(inf.PlayerResponse), &response); err != nil {
			return nil, fmt.Errorf("Couldn't parse player response: %w", err)
		}
		info.DASHManifestURL = response.StreamingData.DashManifestUrl
		info.HLSManifestURL = response.StreamingData.HlsManifestUrl
		if response.PlayabilityStatus.Status != "OK" {
			return nil, fmt.Errorf("Unavailable because: %s", response.PlayabilityStatus.Reason)
		}

		c.addFormatsByInfos(&formats, response.StreamingData.Formats, false)
		c.addFormatsByInfos(&formats, response.StreamingData.AdaptiveFormats, true)
		if seconds := response.VideoDetails.LengthSeconds; seconds != "" {
			val, err := strconv.Atoi(seconds)
			if err == nil {
				info.Duration = time.Duration(val) * time.Second
			}
		}

		if date, err := time.Parse(youtubeDateFormat, response.Microformat.Renderer.PublishDate); err == nil {
			info.DatePublished = date
		} else {
			log.Debug().Msgf("Unable to parse date published %v", err)
		}

		info.Title = response.VideoDetails.Title
		info.Uploader = response.VideoDetails.Author
	} else {
		log.Debug().Msg("Unable to extract player response JSON")
	}

	info.htmlPlayerFile = jsonConfig.Assets.JS

	if len(formats) == 0 {
		log.Debug().Msgf("No formats found")
	}
	if dashManifest := inf.Dashmpd; dashManifest != "" {
		tokens, err := c.getSigTokens(cx, info.htmlPlayerFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to extract signature tokens: %w", err)
		}
		regex := regexp.MustCompile("\\/s\\/([a-fA-F0-9\\.]+)")
		regexSub := regexp.MustCompile("([a-fA-F0-9\\.]+)")
		info.DASHManifestURL = regex.ReplaceAllStringFunc(dashManifest, func(str string) string {
			return "/signature/" + decipherTokens(tokens, regexSub.FindString(str))
		})
		dashFormats, err := c.getDashManifest(cx, info.DASHManifestURL)
		if err != nil {
			return nil, fmt.Errorf("Unable to extract dash manifest: %w", err)
		}
		for _, dashFormat := range dashFormats {
			added := false
			for j, format := range formats {
				if dashFormat.Itag == format.Itag {
					formats[j] = dashFormat
					added = true
					break
				}
			}
			if !added {
				formats = append(formats, dashFormat)
			}
		}
	}
	info.Formats = formats
	return info, nil
}

func (c *Client) addFormatsByInfos(formats *FormatList, infos []formatInfo, adaptive bool) {
	for _, info := range infos {
		if err := formats.addByInfo(info, adaptive); err != nil {
			log.Debug().Err(err)
		}
	}
}

func (c *Client) addFormatsByQueryStrings(formats *FormatList, rd io.Reader, adaptive bool) {
	r := bufio.NewReader(rd)
	for {
		line, err := r.ReadString(',')
		if err == io.EOF {
			break
		}
		if err := formats.addByQueryString(line[:len(line)-1], adaptive); err != nil {
			log.Debug().Err(err)
		}
	}
}

func (c *Client) getVideoInfoFromEmbedded(cx context.Context, id string) (map[string]interface{}, error) {
	var jsonConfig map[string]interface{}

	html, err := c.httpGetAndCheckResponseReadBody(cx, youtubeEmbeddedBaseURL+id)

	if err != nil {
		return nil, fmt.Errorf("Embedded url request returned %w", err)
	}

	//	re = regexp.MustCompile("\"sts\"\\s*:\\s*(\\d+)")
	re := regexp.MustCompile("yt.setConfig\\({'PLAYER_CONFIG': (.*?)}\\);")

	matches := re.FindSubmatch(html)
	if len(matches) < 2 {
		return nil, fmt.Errorf("Error extracting sts from embedded url response")
	}
	dec := json.NewDecoder(bytes.NewBuffer(matches[1]))
	err = dec.Decode(&jsonConfig)
	if err != nil {
		return nil, fmt.Errorf("Unable to extract json from embedded url: %w", err)
	}

	return jsonConfig, nil
}

func (c *Client) getDashManifest(cx context.Context, urlString string) (formats []*Format, err error) {

	resp, err := c.httpGetAndCheckResponse(cx, urlString)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	dec := xml.NewDecoder(resp.Body)
	var token xml.Token
	for ; err == nil; token, err = dec.Token() {
		if el, ok := token.(xml.StartElement); ok && el.Name.Local == "Representation" {
			var rep representation
			err = dec.DecodeElement(&rep, &el)
			if err != nil {
				break
			}
			if itag := getItag(rep.Itag); itag != nil {
				format := &Format{
					Itag:     *itag,
					url:      rep.URL,
					FromDASH: true,
				}
				if rep.Height != 0 {
					format.Itag.Resolution = strconv.Itoa(rep.Height) + "p"
				}
				formats = append(formats, format)
			} else {
				log.Debug().Msgf("No metadata found for itag: %v, skipping...", rep.Itag)
			}
		}
	}
	if err != io.EOF {
		return nil, err
	}
	return formats, nil
}
<commit_msg>Update regex to end at ytplayer.web_player_context_config instead of ytplayer.load<commit_after>package ytdl

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/rs/zerolog/log"
)

const youtubeBaseURL = "https://www.youtube.com/watch"
const youtubeEmbeddedBaseURL = "https://www.youtube.com/embed/"
const youtubeVideoEURL = "https://youtube.googleapis.com/v/"
const youtubeVideoInfoURL = "https://www.youtube.com/get_video_info"
const youtubeDateFormat = "2006-01-02"

// VideoInfo contains the info a youtube video
type VideoInfo struct {
	ID              string     // The video ID
	Title           string     // The video title
	Description     string     // The video description
	DatePublished   time.Time  // The date the video was published
	Formats         FormatList // Formats the video is available in
	DASHManifestURL string     // URI of the DASH manifest file
	HLSManifestURL  string     // URI of the HLS manifest file
	Keywords        []string   // List of keywords associated with the video
	Uploader        string     // Author of the video
	Song            string
	Artist          string
	Album           string
	Writers         string
	Duration        time.Duration // Duration of the video
	htmlPlayerFile  string
}

// GetVideoInfo fetches info from a url string, url object, or a url string
func GetVideoInfo(cx context.Context, value interface{}) (*VideoInfo, error) {
	return DefaultClient.GetVideoInfo(cx, value)
}

// GetVideoInfo fetches info from a url string, url object, or a url string
func (c *Client) GetVideoInfo(cx context.Context, value interface{}) (*VideoInfo, error) {
	switch t := value.(type) {
	case *url.URL:
		videoID := extractVideoID(t)
		if len(videoID) == 0 {
			return nil, fmt.Errorf("invalid youtube URL, no video id")
		}
		return c.GetVideoInfoFromID(cx, videoID)
	case string:
		if strings.HasPrefix(t, "https://") {
			uri, err := url.ParseRequestURI(t)
			if err != nil {
				return nil, err
			}
			return c.GetVideoInfo(cx, uri)
		}
		return c.GetVideoInfoFromID(cx, t)
	default:
		return nil, fmt.Errorf("Identifier type must be a string, *url.URL, or []byte")
	}
}

// GetVideoInfoFromShortURL fetches video info from a short youtube url
func extractVideoID(u *url.URL) string {
	switch u.Host {
	case "www.youtube.com", "youtube.com", "m.youtube.com":
		if u.Path == "/watch" {
			return u.Query().Get("v")
		}
		if strings.HasPrefix(u.Path, "/embed/") {
			return u.Path[7:]
		}
	case "youtu.be":
		if len(u.Path) > 1 {
			return u.Path[1:]
		}
	}
	return ""
}

// GetVideoInfoFromID fetches video info from a youtube video id
func (c *Client) GetVideoInfoFromID(cx context.Context, id string) (*VideoInfo, error) {
	body, err := c.httpGetAndCheckResponseReadBody(cx, youtubeBaseURL+"?v="+id)

	if err != nil {
		return nil, err
	}
	return c.getVideoInfoFromHTML(cx, id, body)
}

// GetDownloadURL gets the download url for a format
func (c *Client) GetDownloadURL(cx context.Context, info *VideoInfo, format *Format) (*url.URL, error) {
	return c.getDownloadURL(cx, format, info.htmlPlayerFile)
}

// GetThumbnailURL returns a url for the thumbnail image
// with the given quality
func (info *VideoInfo) GetThumbnailURL(quality ThumbnailQuality) *url.URL {
	u, _ := url.Parse(fmt.Sprintf("http://img.youtube.com/vi/%s/%s.jpg",
		info.ID, quality))
	return u
}

// Download is a convenience method to download a format to an io.Writer
func (c *Client) Download(cx context.Context, info *VideoInfo, format *Format, dest io.Writer) error {
	u, err := c.GetDownloadURL(cx, info, format)
	if err != nil {
		return err
	}

	resp, err := c.httpGetAndCheckResponse(cx, u.String())
	if err != nil {
		return err
	}

	defer resp.Body.Close()
	_, err = io.Copy(dest, resp.Body)
	return err
}

var (
	regexpPlayerConfig          = regexp.MustCompile("ytplayer.config = (.*?);ytplayer.web_player_context_config")
	regexpInitialData           = regexp.MustCompile(`\["ytInitialData"\] = (.+);`)
	regexpInitialPlayerResponse = regexp.MustCompile(`\["ytInitialPlayerResponse"\] = (.+);`)
)

func (c *Client) getVideoInfoFromHTML(cx context.Context, id string, html []byte) (*VideoInfo, error) {
	info := &VideoInfo{}

	if matches := regexpInitialData.FindSubmatch(html); len(matches) > 0 {
		data := initialData{}

		if err := json.Unmarshal(matches[1], &data); err != nil {
			return nil, err
		}

		contents := data.Contents.TwoColumnWatchNextResults.Results.Results.Contents

		if len(contents) >= 2 {
			infoRenderer := contents[1].VideoSecondaryInfoRenderer

			info.Description = infoRenderer.Description.String()
			rows := infoRenderer.MetadataRowContainer.MetadataRowContainerRenderer.Rows

			info.Artist = rows.Get("Artist")
			info.Album = rows.Get("Album")
			info.Song = rows.Get("Song")
			info.Writers = rows.Get("Writers")
		}
	}

	info.ID = id

	var jsonConfig playerConfig

	// match json in javascript
	if matches := regexpPlayerConfig.FindSubmatch(html); len(matches) > 1 {
		err := json.Unmarshal(matches[1], &jsonConfig)
		if err != nil {
			return nil, err
		}
		//glog.Errorf("jsonCOnfig %s\n\n", jsonConfig.Args.PlayerResponse)
	} else {
		log.Debug().Msg("Unable to extract json from default url, trying embedded url")

		info, err := c.getVideoInfoFromEmbedded(cx, id)
		if err != nil {
			return nil, err
		}
		query := url.Values{
			"video_id": []string{id},
			"eurl":     []string{youtubeVideoEURL + id},
		}

		if sts, ok := info["sts"].(float64); ok {
			query.Add("sts", strconv.Itoa(int(sts)))
		}
		body, err := c.httpGetAndCheckResponseReadBody(cx, youtubeVideoInfoURL+"?"+query.Encode())
		if err != nil {
			return nil, fmt.Errorf("Unable to read video info: %w", err)
		}

		query, err = url.ParseQuery(string(body))
		if err != nil {
			return nil, fmt.Errorf("Unable to parse video info data: %w", err)
		}

		for k, v := range query {
			switch k {
			case "errorcode":
				jsonConfig.Args.Errorcode = v[0]
			case "reason":
				jsonConfig.Args.Reason = v[0]
			case "status":
				jsonConfig.Args.Status = v[0]
			case "player_response":
				jsonConfig.Args.PlayerResponse = v[0]
			case "url_encoded_fmt_stream_map":
				jsonConfig.Args.URLEncodedFmtStreamMap = v[0]
			case "adaptive_fmts":
				jsonConfig.Args.AdaptiveFmts = v[0]
			case "dashmpd":
				jsonConfig.Args.Dashmpd = v[0]
			default:
				// log.Debug().Msgf("unknown query param: %v", k)
			}
		}
	}

	inf := jsonConfig.Args
	if inf.Status == "fail" {
		return nil, fmt.Errorf("Error %s:%s", inf.Errorcode, inf.Reason)
	}

	var formats FormatList
	c.addFormatsByQueryStrings(&formats, strings.NewReader(inf.URLEncodedFmtStreamMap), false)
	if inf.AdaptiveFmts != "" {
		c.addFormatsByQueryStrings(&formats, strings.NewReader(inf.AdaptiveFmts), true)
	}
	if inf.PlayerResponse != "" {
		response := &playerResponse{}

		if err := json.Unmarshal([]byte(inf.PlayerResponse), &response); err != nil {
			return nil, fmt.Errorf("Couldn't parse player response: %w", err)
		}
		info.DASHManifestURL = response.StreamingData.DashManifestUrl
		info.HLSManifestURL = response.StreamingData.HlsManifestUrl
		if response.PlayabilityStatus.Status != "OK" {
			return nil, fmt.Errorf("Unavailable because: %s", response.PlayabilityStatus.Reason)
		}

		c.addFormatsByInfos(&formats, response.StreamingData.Formats, false)
		c.addFormatsByInfos(&formats, response.StreamingData.AdaptiveFormats, true)
		if seconds := response.VideoDetails.LengthSeconds; seconds != "" {
			val, err := strconv.Atoi(seconds)
			if err == nil {
				info.Duration = time.Duration(val) * time.Second
			}
		}

		if date, err := time.Parse(youtubeDateFormat, response.Microformat.Renderer.PublishDate); err == nil {
			info.DatePublished = date
		} else {
			log.Debug().Msgf("Unable to parse date published %v", err)
		}

		info.Title = response.VideoDetails.Title
		info.Uploader = response.VideoDetails.Author
	} else {
		log.Debug().Msg("Unable to extract player response JSON")
	}

	info.htmlPlayerFile = jsonConfig.Assets.JS

	if len(formats) == 0 {
		log.Debug().Msgf("No formats found")
	}
	if dashManifest := inf.Dashmpd; dashManifest != "" {
		tokens, err := c.getSigTokens(cx, info.htmlPlayerFile)
		if err != nil {
			return nil, fmt.Errorf("Unable to extract signature tokens: %w", err)
		}
		regex := regexp.MustCompile("\\/s\\/([a-fA-F0-9\\.]+)")
		regexSub := regexp.MustCompile("([a-fA-F0-9\\.]+)")
		info.DASHManifestURL = regex.ReplaceAllStringFunc(dashManifest, func(str string) string {
			return "/signature/" + decipherTokens(tokens, regexSub.FindString(str))
		})
		dashFormats, err := c.getDashManifest(cx, info.DASHManifestURL)
		if err != nil {
			return nil, fmt.Errorf("Unable to extract dash manifest: %w", err)
		}
		for _, dashFormat := range dashFormats {
			added := false
			for j, format := range formats {
				if dashFormat.Itag == format.Itag {
					formats[j] = dashFormat
					added = true
					break
				}
			}
			if !added {
				formats = append(formats, dashFormat)
			}
		}
	}
	info.Formats = formats
	return info, nil
}

func (c *Client) addFormatsByInfos(formats *FormatList, infos []formatInfo, adaptive bool) {
	for _, info := range infos {
		if err := formats.addByInfo(info, adaptive); err != nil {
			log.Debug().Err(err)
		}
	}
}

func (c *Client) addFormatsByQueryStrings(formats *FormatList, rd io.Reader, adaptive bool) {
	r := bufio.NewReader(rd)
	for {
		line, err := r.ReadString(',')
		if err == io.EOF {
			break
		}
		if err := formats.addByQueryString(line[:len(line)-1], adaptive); err != nil {
			log.Debug().Err(err)
		}
	}
}

func (c *Client) getVideoInfoFromEmbedded(cx context.Context, id string) (map[string]interface{}, error) {
	var jsonConfig map[string]interface{}

	html, err := c.httpGetAndCheckResponseReadBody(cx, youtubeEmbeddedBaseURL+id)

	if err != nil {
		return nil, fmt.Errorf("Embedded url request returned %w", err)
	}

	//	re = regexp.MustCompile("\"sts\"\\s*:\\s*(\\d+)")
	re := regexp.MustCompile("yt.setConfig\\({'PLAYER_CONFIG': (.*?)}\\);")

	matches := re.FindSubmatch(html)
	if len(matches) < 2 {
		return nil, fmt.Errorf("Error extracting sts from embedded url response")
	}
	dec := json.NewDecoder(bytes.NewBuffer(matches[1]))
	err = dec.Decode(&jsonConfig)
	if err != nil {
		return nil, fmt.Errorf("Unable to extract json from embedded url: %w", err)
	}

	return jsonConfig, nil
}

func (c *Client) getDashManifest(cx context.Context, urlString string) (formats []*Format, err error) {

	resp, err := c.httpGetAndCheckResponse(cx, urlString)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	dec := xml.NewDecoder(resp.Body)
	var token xml.Token
	for ; err == nil; token, err = dec.Token() {
		if el, ok := token.(xml.StartElement); ok && el.Name.Local == "Representation" {
			var rep representation
			err = dec.DecodeElement(&rep, &el)
			if err != nil {
				break
			}
			if itag := getItag(rep.Itag); itag != nil {
				format := &Format{
					Itag:     *itag,
					url:      rep.URL,
					FromDASH: true,
				}
				if rep.Height != 0 {
					format.Itag.Resolution = strconv.Itoa(rep.Height) + "p"
				}
				formats = append(formats, format)
			} else {
				log.Debug().Msgf("No metadata found for itag: %v, skipping...", rep.Itag)
			}
		}
	}
	if err != io.EOF {
		return nil, err
	}
	return formats, nil
}
<|endoftext|>


<commit_before>package golang

import (
	"bytes"
	"fmt"
	"github.com/mdempsky/gocode/suggest"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"margo.sh/mg"
	"margo.sh/mgpf"
	"margo.sh/sublime"
	"os"
	"strings"
	"time"
	"unicode"
)

var (
	gocodeClassTags = map[string]mg.CompletionTag{
		"const":   mg.ConstantTag,
		"func":    mg.FunctionTag,
		"package": mg.PackageTag,
		"import":  mg.PackageTag,
		"type":    mg.TypeTag,
		"var":     mg.VariableTag,
	}
)

type gocodeReq struct {
	g   *Gocode
	mx  *mg.Ctx
	st  *mg.State
	gx  *gocodeCtx
	res chan *mg.State
}

func (gr *gocodeReq) reduce() *mg.State {
	candidates := gr.gx.candidates()
	completions := make([]mg.Completion, 0, len(candidates))
	for _, v := range candidates {
		if c, ok := gr.g.completion(gr.mx, gr.gx, v); ok {
			completions = append(completions, c)
		}
	}

	return gr.st.AddCompletions(completions...)
}

type Gocode struct {
	mg.ReducerType

	// Whether or not to do gocode completion using source code
	// instead of the pre-compiled package files.
	// Using source is often slower but offer more up-to-date completions.
	Source bool

	ProposeBuiltins          bool
	ProposeTests             bool
	Autobuild                bool
	UnimportedPackages       bool
	AllowExplicitCompletions bool
	AllowWordCompletions     bool
	ShowFuncParams           bool
	ShowFuncResultNames      bool

	// Whether or not to log debugging info
	Debug bool

	reqs chan gocodeReq
}

func (g *Gocode) ReducerConfig(mx *mg.Ctx) mg.EditorConfig {
	cfg, ok := mx.Config.(sublime.Config)
	if !ok {
		return nil
	}

	// ST might query the GoSublime plugin first, so we must always disable it
	cfg = cfg.DisableGsComplete()
	// but we don't want to affect editor completions in non-go files
	if !g.ReducerCond(mx) {
		return cfg
	}

	if !g.AllowExplicitCompletions {
		cfg = cfg.InhibitExplicitCompletions()
	}
	if !g.AllowWordCompletions {
		cfg = cfg.InhibitWordCompletions()
	}
	return cfg
}

func (g *Gocode) ReducerCond(mx *mg.Ctx) bool {
	return mx.ActionIs(mg.QueryCompletions{}) && mx.LangIs(mg.Go)
}

func (g *Gocode) ReducerMount(mx *mg.Ctx) {
	g.reqs = make(chan gocodeReq)
	go func() {
		for gr := range g.reqs {
			gr.res <- gr.reduce()
		}
	}()
}

func (g *Gocode) ReducerUnmount(mx *mg.Ctx) {
	close(g.reqs)
}

func (g *Gocode) Reduce(mx *mg.Ctx) *mg.State {
	start := time.Now()

	st, gx := initGocodeReducer(mx, *g)
	if gx == nil {
		return st
	}

	qTimeout := 100 * time.Millisecond
	gr := gocodeReq{
		g:   g,
		mx:  mx,
		st:  st,
		gx:  gx,
		res: make(chan *mg.State, 1),
	}
	select {
	case g.reqs <- gr:
	case <-time.After(qTimeout):
		mx.Log.Println("gocode didn't accept the request after", mgpf.D(time.Since(start)))
		return st
	}

	pTimeout := 150 * time.Millisecond
	if d := qTimeout - time.Since(start); d > 0 {
		pTimeout += d
	}

	select {
	case st := <-gr.res:
		return st
	case <-time.After(pTimeout):
		go func() {
			<-gr.res

			mx.Log.Println("gocode eventually responded after", mgpf.Since(start))

			if g.Debug {
				opts := mgpf.DefaultPrintOpts
				opts.MinDuration = 3 * time.Millisecond
				mx.Profile.Fprint(os.Stderr, &opts)
			}
		}()

		mx.Log.Println("gocode didn't respond after", mgpf.D(pTimeout), "taking", mgpf.Since(start))
		return st
	}
}

func (g Gocode) funcTitle(fx *ast.FuncType, buf *bytes.Buffer, decl string) string {
	// TODO: caching

	buf.Reset()
	fset := token.NewFileSet()

	buf.WriteString("func(")
	if fx.Params != nil {
		switch {
		case g.ShowFuncParams:
			printFields(buf, fset, fx.Params.List, true)
		case fx.Params.NumFields() != 0:
			buf.WriteString("…")
		}
	}
	buf.WriteString(")")

	if fl := fx.Results; fl != nil {
		buf.WriteString(" ")
		hasNames := g.ShowFuncResultNames && len(fl.List) != 0 && len(fl.List[0].Names) != 0
		if hasNames {
			buf.WriteString("(")
		}
		printFields(buf, fset, fl.List, g.ShowFuncResultNames)
		if hasNames {
			buf.WriteString(")")
		}
	}

	return buf.String()
}

func (g Gocode) funcSrc(fx *ast.FuncType, buf *bytes.Buffer, v suggest.Candidate, gx *gocodeCtx) string {
	// TODO: caching
	// TODO: only output the name, if we're in a call, assignment, etc. that takes a func

	outputArgs := true
	for _, c := range gx.src[gx.pos:] {
		if c == '(' {
			outputArgs = false
			break
		}
		r := rune(c)
		if !IsLetter(r) && !unicode.IsSpace(r) {
			break
		}
	}

	buf.Reset()
	buf.WriteString(v.Name)
	if outputArgs {
		buf.WriteString("(")
		pos := 0
		for _, field := range fx.Params.List {
			for _, name := range field.Names {
				pos++
				if pos > 1 {
					buf.WriteString(", ")
				}
				fmt.Fprintf(buf, "${%d:%s}", pos, name)
			}
		}
		buf.WriteString(")")
	}
	buf.WriteString("${0}")
	return buf.String()
}

func printFields(w io.Writer, fset *token.FileSet, list []*ast.Field, printNames bool) {
	for i, field := range list {
		if i > 0 {
			fmt.Fprint(w, ", ")
		}
		if printNames {
			for j, name := range field.Names {
				if j > 0 {
					fmt.Fprint(w, ", ")
				}
				fmt.Fprint(w, name.String())
			}
			if len(field.Names) != 0 {
				fmt.Fprint(w, " ")
			}
		}
		printer.Fprint(w, fset, field.Type)
	}
}

func (g Gocode) completion(mx *mg.Ctx, gx *gocodeCtx, v suggest.Candidate) (c mg.Completion, ok bool) {
	buf := bytes.NewBuffer(nil)
	if v.Class == "PANIC" {
		mx.Log.Printf("gocode panicked in '%s' at pos '%d'\n", gx.fn, gx.pos)
		return c, false
	}
	if !g.ProposeTests && g.matchTests(v) {
		return c, false
	}

	var fx *ast.FuncType
	if strings.HasPrefix(v.Type, "func(") {
		x, _ := parser.ParseExpr(v.Type)
		fx, _ = x.(*ast.FuncType)
	}

	c = mg.Completion{
		Query: g.compQuery(v),
		Tag:   g.compTag(v),
		Src:   g.compSrc(fx, buf, v, gx),
		Title: g.compTitle(fx, buf, v),
	}
	return c, true
}

func (g Gocode) compQuery(v suggest.Candidate) string {
	return v.Name
}

func (g Gocode) compSrc(fx *ast.FuncType, buf *bytes.Buffer, v suggest.Candidate, gx *gocodeCtx) string {
	if fx == nil {
		return v.Name
	}
	return g.funcSrc(fx, buf, v, gx)
}

func (g Gocode) compTag(v suggest.Candidate) mg.CompletionTag {
	if tag, ok := gocodeClassTags[v.Class]; ok {
		return tag
	}
	return mg.UnknownTag
}

func (g Gocode) compTitle(fx *ast.FuncType, buf *bytes.Buffer, v suggest.Candidate) string {
	if fx != nil {
		return g.funcTitle(fx, buf, v.Type)
	}
	if v.Type == "" {
		return v.Class
	}
	return v.Type
}

func (g Gocode) matchTests(c suggest.Candidate) bool {
	if !strings.HasPrefix(c.Type, "func(") {
		return false
	}
	return strings.HasPrefix(c.Name, "Test") ||
		strings.HasPrefix(c.Name, "Benchmark") ||
		strings.HasPrefix(c.Name, "Example")
}

type gocodeCtx struct {
	Gocode
	gsu  *gcSuggest
	mx   *mg.Ctx
	cn   *CursorNode
	fn   string
	src  []byte
	pos  int
	bctx *build.Context
}

func initGocodeReducer(mx *mg.Ctx, g Gocode) (*mg.State, *gocodeCtx) {
	st := mx.State
	bctx := BuildContext(mx)
	src, _ := st.View.ReadAll()
	if len(src) == 0 {
		return st, nil
	}
	pos := clampSrcPos(src, st.View.Pos)

	cx := NewCompletionCtx(mx, src, pos)
	if cx.Scope.Any(PackageScope, FileScope) {
		return st, nil
	}
	cn := cx.CursorNode
	// don't do completion inside comments
	if cn.Comment != nil {
		return st, nil
	}
	// don't do completion inside strings unless it's an import
	if cn.ImportSpec == nil && cn.BasicLit != nil && cn.BasicLit.Kind == token.STRING {
		return st, nil
	}

	gx := &gocodeCtx{
		mx: mx,
		gsu: newGcSuggest(mx, gsuOpts{
			Source:          g.Source,
			Debug:           g.Debug,
			ProposeBuiltins: g.ProposeBuiltins,
		}),
		cn:   cn,
		fn:   st.View.Filename(),
		pos:  pos,
		src:  src,
		bctx: bctx,
	}
	return st, gx
}

func (gx *gocodeCtx) candidates() []suggest.Candidate {
	if len(gx.src) == 0 {
		return nil
	}
	return gx.gsu.candidates(gx.mx)
}

func clampSrcPos(src []byte, pos int) int {
	if pos < 0 {
		return 0
	}
	if pos > len(src) {
		return len(src) - 1
	}
	return pos
}
<commit_msg>sync margo<commit_after>package golang

import (
	"bytes"
	"fmt"
	"github.com/mdempsky/gocode/suggest"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"margo.sh/mg"
	"margo.sh/mgpf"
	"margo.sh/sublime"
	"os"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)

var (
	gocodeClassTags = map[string]mg.CompletionTag{
		"const":   mg.ConstantTag,
		"func":    mg.FunctionTag,
		"package": mg.PackageTag,
		"import":  mg.PackageTag,
		"type":    mg.TypeTag,
		"var":     mg.VariableTag,
	}
)

type gocodeReq struct {
	g   *Gocode
	mx  *mg.Ctx
	st  *mg.State
	gx  *gocodeCtx
	res chan *mg.State
}

func (gr *gocodeReq) reduce() *mg.State {
	candidates := gr.gx.candidates()
	completions := make([]mg.Completion, 0, len(candidates))

	gr.mx.Profile.Push("gocodeReq.finalize").Pop()
	for _, v := range candidates {
		if c, ok := gr.g.completion(gr.mx, gr.gx, v); ok {
			completions = append(completions, c)
		}
	}

	return gr.st.AddCompletions(completions...)
}

type Gocode struct {
	mg.ReducerType

	// Whether or not to do gocode completion using source code
	// instead of the pre-compiled package files.
	// Using source is often slower but offer more up-to-date completions.
	Source bool

	ProposeBuiltins          bool
	ProposeTests             bool
	Autobuild                bool
	UnimportedPackages       bool
	AllowExplicitCompletions bool
	AllowWordCompletions     bool
	ShowFuncParams           bool
	ShowFuncResultNames      bool

	// Whether or not to log debugging info
	Debug bool

	reqs chan gocodeReq
}

func (g *Gocode) ReducerConfig(mx *mg.Ctx) mg.EditorConfig {
	cfg, ok := mx.Config.(sublime.Config)
	if !ok {
		return nil
	}

	// ST might query the GoSublime plugin first, so we must always disable it
	cfg = cfg.DisableGsComplete()
	// but we don't want to affect editor completions in non-go files
	if !g.ReducerCond(mx) {
		return cfg
	}

	if !g.AllowExplicitCompletions {
		cfg = cfg.InhibitExplicitCompletions()
	}
	if !g.AllowWordCompletions {
		cfg = cfg.InhibitWordCompletions()
	}
	return cfg
}

func (g *Gocode) ReducerCond(mx *mg.Ctx) bool {
	return mx.ActionIs(mg.QueryCompletions{}) && mx.LangIs(mg.Go)
}

func (g *Gocode) ReducerMount(mx *mg.Ctx) {
	g.reqs = make(chan gocodeReq)
	go func() {
		for gr := range g.reqs {
			gr.res <- gr.reduce()
		}
	}()
}

func (g *Gocode) ReducerUnmount(mx *mg.Ctx) {
	close(g.reqs)
}

func (g *Gocode) Reduce(mx *mg.Ctx) *mg.State {
	start := time.Now()

	mx, gx := initGocodeReducer(mx, *g)
	st := mx.State
	if gx == nil {
		return st
	}

	qTimeout := 100 * time.Millisecond
	gr := gocodeReq{
		g:   g,
		mx:  mx,
		st:  st,
		gx:  gx,
		res: make(chan *mg.State, 1),
	}
	select {
	case g.reqs <- gr:
	case <-time.After(qTimeout):
		mx.Log.Println("gocode didn't accept the request after", mgpf.D(time.Since(start)))
		return st
	}

	pTimeout := 150 * time.Millisecond
	if d := qTimeout - time.Since(start); d > 0 {
		pTimeout += d
	}

	select {
	case st := <-gr.res:
		return st
	case <-time.After(pTimeout):
		go func() {
			<-gr.res

			mx.Log.Println("gocode eventually responded after", mgpf.Since(start))

			if g.Debug {
				opts := mgpf.DefaultPrintOpts
				opts.MinDuration = 3 * time.Millisecond
				mx.Profile.Fprint(os.Stderr, &opts)
			}
		}()

		mx.Log.Println("gocode didn't respond after", mgpf.D(pTimeout), "taking", mgpf.Since(start))
		return st
	}
}

func (g Gocode) funcTitle(fx *ast.FuncType, buf *bytes.Buffer, decl string) string {
	// TODO: caching

	buf.Reset()
	fset := token.NewFileSet()

	buf.WriteString("func(")
	if fx.Params != nil {
		switch {
		case g.ShowFuncParams:
			printFields(buf, fset, fx.Params.List, true)
		case fx.Params.NumFields() != 0:
			buf.WriteString("…")
		}
	}
	buf.WriteString(")")

	if fl := fx.Results; fl != nil {
		buf.WriteString(" ")
		hasNames := g.ShowFuncResultNames && len(fl.List) != 0 && len(fl.List[0].Names) != 0
		if hasNames {
			buf.WriteString("(")
		}
		printFields(buf, fset, fl.List, g.ShowFuncResultNames)
		if hasNames {
			buf.WriteString(")")
		}
	}

	return buf.String()
}

func (g Gocode) funcSrc(fx *ast.FuncType, buf *bytes.Buffer, v suggest.Candidate, gx *gocodeCtx) string {
	// TODO: caching
	// TODO: only output the name, if we're in a call, assignment, etc. that takes a func

	outputArgs := true
	for _, c := range gx.src[gx.pos:] {
		if c == '(' {
			outputArgs = false
			break
		}
		r := rune(c)
		if !IsLetter(r) && !unicode.IsSpace(r) {
			break
		}
	}

	buf.Reset()
	buf.WriteString(v.Name)
	if outputArgs {
		buf.WriteString("(")
		pos := 0
		for _, field := range fx.Params.List {
			for _, name := range field.Names {
				pos++
				if pos > 1 {
					buf.WriteString(", ")
				}
				fmt.Fprintf(buf, "${%d:%s}", pos, name)
			}
		}
		buf.WriteString(")")
	}
	buf.WriteString("${0}")
	return buf.String()
}

func printFields(w io.Writer, fset *token.FileSet, list []*ast.Field, printNames bool) {
	for i, field := range list {
		if i > 0 {
			fmt.Fprint(w, ", ")
		}
		if printNames {
			for j, name := range field.Names {
				if j > 0 {
					fmt.Fprint(w, ", ")
				}
				fmt.Fprint(w, name.String())
			}
			if len(field.Names) != 0 {
				fmt.Fprint(w, " ")
			}
		}
		printer.Fprint(w, fset, field.Type)
	}
}

func (g Gocode) completion(mx *mg.Ctx, gx *gocodeCtx, v suggest.Candidate) (c mg.Completion, ok bool) {
	buf := bytes.NewBuffer(nil)
	if v.Class == "PANIC" {
		mx.Log.Printf("gocode panicked in '%s' at pos '%d'\n", gx.fn, gx.pos)
		return c, false
	}
	if !g.ProposeTests && g.matchTests(v) {
		return c, false
	}

	var fx *ast.FuncType
	if strings.HasPrefix(v.Type, "func(") {
		x, _ := parser.ParseExpr(v.Type)
		fx, _ = x.(*ast.FuncType)
	}

	c = mg.Completion{
		Query: g.compQuery(v),
		Tag:   g.compTag(v),
		Src:   g.compSrc(fx, buf, v, gx),
		Title: g.compTitle(fx, buf, v),
	}
	return c, true
}

func (g Gocode) compQuery(v suggest.Candidate) string {
	return v.Name
}

func (g Gocode) compSrc(fx *ast.FuncType, buf *bytes.Buffer, v suggest.Candidate, gx *gocodeCtx) string {
	if fx == nil {
		return v.Name
	}
	return g.funcSrc(fx, buf, v, gx)
}

func (g Gocode) compTag(v suggest.Candidate) mg.CompletionTag {
	if tag, ok := gocodeClassTags[v.Class]; ok {
		return tag
	}
	return mg.UnknownTag
}

func (g Gocode) compTitle(fx *ast.FuncType, buf *bytes.Buffer, v suggest.Candidate) string {
	if fx != nil {
		return g.funcTitle(fx, buf, v.Type)
	}
	if v.Type == "" {
		return v.Class
	}
	return v.Type
}

func (g Gocode) matchTests(c suggest.Candidate) bool {
	if !strings.HasPrefix(c.Type, "func(") {
		return false
	}
	return strings.HasPrefix(c.Name, "Test") ||
		strings.HasPrefix(c.Name, "Benchmark") ||
		strings.HasPrefix(c.Name, "Example")
}

type gocodeCtx struct {
	Gocode
	gsu  *gcSuggest
	mx   *mg.Ctx
	cn   *CursorNode
	fn   string
	src  []byte
	pos  int
	bctx *build.Context
}

func initGocodeReducer(mx *mg.Ctx, g Gocode) (*mg.Ctx, *gocodeCtx) {
	// TODO: simplify and get rid of this func, it's only used once

	st := mx.State
	bctx := BuildContext(mx)
	src, _ := st.View.ReadAll()
	if len(src) == 0 {
		return mx, nil
	}
	pos := clampSrcPos(src, st.View.Pos)

	// move the cursor off the word.
	// xxx.yyy| ~> xxx.|
	// xxx| ~> |xxx
	// this results in fetching all possible results
	// which is desirable because the editor is usually better at filtering the list
	for {
		r, n := utf8.DecodeLastRune(src[:pos])
		if !IsLetter(r) {
			break
		}
		pos -= n
	}
	// seems legit...
	st = st.SetView(st.View.Copy(func(v *mg.View) { v.Pos = pos }))
	mx = mx.SetState(st)

	cx := NewCompletionCtx(mx, src, pos)
	if cx.Scope.Any(PackageScope, FileScope) {
		return mx, nil
	}
	cn := cx.CursorNode
	// don't do completion inside comments
	if cn.Comment != nil {
		return mx, nil
	}
	// don't do completion inside strings unless it's an import
	if cn.ImportSpec == nil && cn.BasicLit != nil && cn.BasicLit.Kind == token.STRING {
		return mx, nil
	}

	gx := &gocodeCtx{
		mx: mx,
		gsu: newGcSuggest(mx, gsuOpts{
			Source:          g.Source,
			Debug:           g.Debug,
			ProposeBuiltins: g.ProposeBuiltins,
		}),
		cn:   cn,
		fn:   st.View.Filename(),
		pos:  pos,
		src:  src,
		bctx: bctx,
	}
	return mx, gx
}

func (gx *gocodeCtx) candidates() []suggest.Candidate {
	if len(gx.src) == 0 {
		return nil
	}
	return gx.gsu.candidates(gx.mx)
}

func clampSrcPos(src []byte, pos int) int {
	if pos < 0 {
		return 0
	}
	if pos > len(src) {
		return len(src) - 1
	}
	return pos
}
<|endoftext|>


<commit_before>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package viper

import (
	"bytes"
	"os"
	"sort"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

var yamlExample = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
  jacket: leather
  trousers: denim
age: 35
beard: true
`)

var tomlExample = []byte(`
title = "TOML Example"

[owner]
organization = "MongoDB"
Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)

var jsonExample = []byte(`{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters": {
        "batter": [
                { "type": "Regular" },
                { "type": "Chocolate" },
                { "type": "Blueberry" },
                { "type": "Devil's Food" }
            ]
    }
}`)

func TestBasics(t *testing.T) {
	SetConfigFile("/tmp/config.yaml")
	assert.Equal(t, "/tmp/config.yaml", getConfigFile())
}

func TestDefault(t *testing.T) {
	SetDefault("age", 45)
	assert.Equal(t, 45, Get("age"))
}

func TestMarshalling(t *testing.T) {
	SetConfigType("yaml")
	r := bytes.NewReader(yamlExample)

	MarshallReader(r)
	assert.True(t, InConfig("name"))
	assert.False(t, InConfig("state"))
	assert.Equal(t, "steve", Get("name"))
	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
	assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim"}, Get("clothing"))
	assert.Equal(t, 35, Get("age"))
}

func TestOverrides(t *testing.T) {
	Set("age", 40)
	assert.Equal(t, 40, Get("age"))
}

func TestDefaultPost(t *testing.T) {
	assert.NotEqual(t, "NYC", Get("state"))
	SetDefault("state", "NYC")
	assert.Equal(t, "NYC", Get("state"))
}

func TestAliases(t *testing.T) {
	RegisterAlias("years", "age")
	assert.Equal(t, 40, Get("years"))
	Set("years", 45)
	assert.Equal(t, 45, Get("age"))
}

func TestAliasInConfigFile(t *testing.T) {
	// the config file specifies "beard".  If we make this an alias for
	// "hasbeard", we still want the old config file to work with beard.
	RegisterAlias("beard", "hasbeard")
	assert.Equal(t, true, Get("hasbeard"))
	Set("hasbeard", false)
	assert.Equal(t, false, Get("beard"))
}

func TestYML(t *testing.T) {
	Reset()
	SetConfigType("yml")
	r := bytes.NewReader(yamlExample)

	MarshallReader(r)
	assert.Equal(t, "steve", Get("name"))
}

func TestJSON(t *testing.T) {
	SetConfigType("json")
	r := bytes.NewReader(jsonExample)

	MarshallReader(r)
	assert.Equal(t, "0001", Get("id"))
}

func TestTOML(t *testing.T) {
	SetConfigType("toml")
	r := bytes.NewReader(tomlExample)

	MarshallReader(r)
	assert.Equal(t, "TOML Example", Get("title"))
}

func TestEnv(t *testing.T) {
	SetConfigType("json")
	r := bytes.NewReader(jsonExample)
	MarshallReader(r)
	BindEnv("id")
	BindEnv("f", "FOOD")

	os.Setenv("ID", "13")
	os.Setenv("FOOD", "apple")
	os.Setenv("NAME", "crunk")

	assert.Equal(t, "13", Get("id"))
	assert.Equal(t, "apple", Get("f"))
	assert.Equal(t, "Cake", Get("name"))

	AutomaticEnv()

	assert.Equal(t, "crunk", Get("name"))
}

func TestAllKeys(t *testing.T) {
	ks := sort.StringSlice{"title", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type"}
	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
	all := map[string]interface{}{"hacker": true, "beard": true, "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "ppu": 0.55, "clothing": map[interface{}]interface{}{"jacket": "leather", "trousers": "denim"}, "name": "crunk", "owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "id": "13", "title": "TOML Example", "age": 35, "type": "donut"}

	var allkeys sort.StringSlice
	allkeys = AllKeys()
	allkeys.Sort()
	ks.Sort()

	assert.Equal(t, ks, allkeys)
	assert.Equal(t, all, AllSettings())
}

func TestCaseInSensitive(t *testing.T) {
	assert.Equal(t, true, Get("hacker"))
	Set("Title", "Checking Case")
	assert.Equal(t, "Checking Case", Get("tItle"))
}

func TestAliasesOfAliases(t *testing.T) {
	RegisterAlias("Foo", "Bar")
	RegisterAlias("Bar", "Title")
	assert.Equal(t, "Checking Case", Get("FOO"))
}

func TestRecursiveAliases(t *testing.T) {
	RegisterAlias("Baz", "Roo")
	RegisterAlias("Roo", "baz")
}

func TestMarshal(t *testing.T) {
	SetDefault("port", 1313)
	Set("name", "Steve")

	type config struct {
		Port int
		Name string
	}

	var C config

	err := Marshal(&C)
	if err != nil {
		t.Fatalf("unable to decode into struct, %v", err)
	}

	assert.Equal(t, &C, &config{Name: "Steve", Port: 1313})

	Set("port", 1234)
	err = Marshal(&C)
	if err != nil {
		t.Fatalf("unable to decode into struct, %v", err)
	}
	assert.Equal(t, &C, &config{Name: "Steve", Port: 1234})
}
<commit_msg>adding simple test for BindPFlag<commit_after>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package viper

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"testing"
	"time"

	"github.com/spf13/pflag"
	"github.com/stretchr/testify/assert"
)

var yamlExample = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
  jacket: leather
  trousers: denim
age: 35
beard: true
`)

var tomlExample = []byte(`
title = "TOML Example"

[owner]
organization = "MongoDB"
Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)

var jsonExample = []byte(`{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters": {
        "batter": [
                { "type": "Regular" },
                { "type": "Chocolate" },
                { "type": "Blueberry" },
                { "type": "Devil's Food" }
            ]
    }
}`)

//stubs for PFlag Values
type stringValue string

func newStringValue(val string, p *string) *stringValue {
	*p = val
	return (*stringValue)(p)
}

func (s *stringValue) Set(val string) error {
	*s = stringValue(val)
	return nil
}

func (s *stringValue) Type() string {
	return "string"
}

func (s *stringValue) String() string {
	return fmt.Sprintf("%s", *s)
}

func TestBasics(t *testing.T) {
	SetConfigFile("/tmp/config.yaml")
	assert.Equal(t, "/tmp/config.yaml", getConfigFile())
}

func TestDefault(t *testing.T) {
	SetDefault("age", 45)
	assert.Equal(t, 45, Get("age"))
}

func TestMarshalling(t *testing.T) {
	SetConfigType("yaml")
	r := bytes.NewReader(yamlExample)

	MarshallReader(r)
	assert.True(t, InConfig("name"))
	assert.False(t, InConfig("state"))
	assert.Equal(t, "steve", Get("name"))
	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
	assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim"}, Get("clothing"))
	assert.Equal(t, 35, Get("age"))
}

func TestOverrides(t *testing.T) {
	Set("age", 40)
	assert.Equal(t, 40, Get("age"))
}

func TestDefaultPost(t *testing.T) {
	assert.NotEqual(t, "NYC", Get("state"))
	SetDefault("state", "NYC")
	assert.Equal(t, "NYC", Get("state"))
}

func TestAliases(t *testing.T) {
	RegisterAlias("years", "age")
	assert.Equal(t, 40, Get("years"))
	Set("years", 45)
	assert.Equal(t, 45, Get("age"))
}

func TestAliasInConfigFile(t *testing.T) {
	// the config file specifies "beard".  If we make this an alias for
	// "hasbeard", we still want the old config file to work with beard.
	RegisterAlias("beard", "hasbeard")
	assert.Equal(t, true, Get("hasbeard"))
	Set("hasbeard", false)
	assert.Equal(t, false, Get("beard"))
}

func TestYML(t *testing.T) {
	Reset()
	SetConfigType("yml")
	r := bytes.NewReader(yamlExample)

	MarshallReader(r)
	assert.Equal(t, "steve", Get("name"))
}

func TestJSON(t *testing.T) {
	SetConfigType("json")
	r := bytes.NewReader(jsonExample)

	MarshallReader(r)
	assert.Equal(t, "0001", Get("id"))
}

func TestTOML(t *testing.T) {
	SetConfigType("toml")
	r := bytes.NewReader(tomlExample)

	MarshallReader(r)
	assert.Equal(t, "TOML Example", Get("title"))
}

func TestEnv(t *testing.T) {
	SetConfigType("json")
	r := bytes.NewReader(jsonExample)
	MarshallReader(r)
	BindEnv("id")
	BindEnv("f", "FOOD")

	os.Setenv("ID", "13")
	os.Setenv("FOOD", "apple")
	os.Setenv("NAME", "crunk")

	assert.Equal(t, "13", Get("id"))
	assert.Equal(t, "apple", Get("f"))
	assert.Equal(t, "Cake", Get("name"))

	AutomaticEnv()

	assert.Equal(t, "crunk", Get("name"))
}

func TestAllKeys(t *testing.T) {
	ks := sort.StringSlice{"title", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type"}
	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
	all := map[string]interface{}{"hacker": true, "beard": true, "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "ppu": 0.55, "clothing": map[interface{}]interface{}{"jacket": "leather", "trousers": "denim"}, "name": "crunk", "owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "id": "13", "title": "TOML Example", "age": 35, "type": "donut"}

	var allkeys sort.StringSlice
	allkeys = AllKeys()
	allkeys.Sort()
	ks.Sort()

	assert.Equal(t, ks, allkeys)
	assert.Equal(t, all, AllSettings())
}

func TestCaseInSensitive(t *testing.T) {
	assert.Equal(t, true, Get("hacker"))
	Set("Title", "Checking Case")
	assert.Equal(t, "Checking Case", Get("tItle"))
}

func TestAliasesOfAliases(t *testing.T) {
	RegisterAlias("Foo", "Bar")
	RegisterAlias("Bar", "Title")
	assert.Equal(t, "Checking Case", Get("FOO"))
}

func TestRecursiveAliases(t *testing.T) {
	RegisterAlias("Baz", "Roo")
	RegisterAlias("Roo", "baz")
}

func TestMarshal(t *testing.T) {
	SetDefault("port", 1313)
	Set("name", "Steve")

	type config struct {
		Port int
		Name string
	}

	var C config

	err := Marshal(&C)
	if err != nil {
		t.Fatalf("unable to decode into struct, %v", err)
	}

	assert.Equal(t, &C, &config{Name: "Steve", Port: 1313})

	Set("port", 1234)
	err = Marshal(&C)
	if err != nil {
		t.Fatalf("unable to decode into struct, %v", err)
	}
	assert.Equal(t, &C, &config{Name: "Steve", Port: 1234})
}

func TestBindPFlag(t *testing.T) {
	var testString = "testing"
	var testValue = newStringValue(testString, &testString)

	flag := &pflag.Flag{
		Name:    "testflag",
		Value:   testValue,
		Changed: false,
	}

	BindPFlag("testvalue", flag)

	assert.Equal(t, testString, Get("testvalue"))

	flag.Value.Set("testing_mutate")
	flag.Changed = true //hack for pflag usage

	assert.Equal(t, "testing_mutate", Get("testvalue"))

}
<|endoftext|>


<commit_before>// +build !windows

package ole

func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) {
	return nil, NewError(E_NOTIMPL)
}

func (enum *IEnumVARIANT) Reset() error {
	return NewError(E_NOTIMPL)
}

func (enum *IEnumVARIANT) Skip(celt uint) error {
	return NewError(E_NOTIMPL)
}

func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) {
	return nil, 0, NewError(E_NOTIMPL)
}
<commit_msg>Need to pass Variant.<commit_after>// +build !windows

package ole

func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) {
	return nil, NewError(E_NOTIMPL)
}

func (enum *IEnumVARIANT) Reset() error {
	return NewError(E_NOTIMPL)
}

func (enum *IEnumVARIANT) Skip(celt uint) error {
	return NewError(E_NOTIMPL)
}

func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) {
	return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL)
}
<|endoftext|>


<commit_before>package revok

import (
	"cred-alert/db"
	"cred-alert/gitclient"
	"cred-alert/kolsch"
	"cred-alert/metrics"
	"cred-alert/scanners"
	"cred-alert/scanners/diffscanner"
	"cred-alert/sniff"
	"strings"

	"code.cloudfoundry.org/lager"
	git "github.com/libgit2/git2go"
)

//go:generate counterfeiter . Scanner

type Scanner interface {
	Scan(lager.Logger, string, string, string, string) error
}

type scanner struct {
	gitClient            gitclient.Client
	repositoryRepository db.RepositoryRepository
	scanRepository       db.ScanRepository
	sniffer              sniff.Sniffer
}

func NewScanner(
	gitClient gitclient.Client,
	repositoryRepository db.RepositoryRepository,
	scanRepository db.ScanRepository,
	sniffer sniff.Sniffer,
	emitter metrics.Emitter,
) Scanner {
	return &scanner{
		gitClient:            gitClient,
		repositoryRepository: repositoryRepository,
		scanRepository:       scanRepository,
		sniffer:              sniffer,
	}
}

func (s *scanner) Scan(
	logger lager.Logger,
	owner string,
	repository string,
	startSHA string,
	stopSHA string,
) error {
	dbRepository, err := s.repositoryRepository.Find(owner, repository)
	if err != nil {
		logger.Error("failed-to-find-db-repo", err)
		return err
	}

	repo, err := git.OpenRepository(dbRepository.Path)
	if err != nil {
		logger.Error("failed-to-open-repo", err)
		return err
	}

	startOid, err := git.NewOid(startSHA)
	if err != nil {
		logger.Error("failed-to-create-start-oid", err)
		return err
	}

	var stopOid *git.Oid
	if stopSHA != "" {
		var err error
		stopOid, err = git.NewOid(stopSHA)
		if err != nil {
			logger.Error("failed-to-create-stop-oid", err)
			return err
		}
	}

	quietLogger := kolsch.NewLogger()
	scan := s.scanRepository.Start(quietLogger, "repo-scan", &dbRepository, nil)

	scannedOids := map[git.Oid]struct{}{}
	err = s.scanAncestors(
		quietLogger,
		logger,
		repo,
		dbRepository,
		scan,
		scannedOids,
		startOid,
		stopOid,
	)
	if err != nil {
		logger.Error("failed-to-scan", err)
	}

	err = scan.Finish()
	if err != nil {
		logger.Error("failed-to-finish-scan", err)
		return err
	}

	return nil
}

func (s *scanner) scanAncestors(
	quietLogger lager.Logger,
	logger lager.Logger,
	repo *git.Repository,
	dbRepository db.Repository,
	scan db.ActiveScan,
	scannedOids map[git.Oid]struct{},
	child *git.Oid,
	stopPoint *git.Oid,
) error {
	parents, err := s.gitClient.GetParents(repo, child)
	if err != nil {
		return err
	}

	if len(parents) == 0 {
		return s.scan(quietLogger, logger, dbRepository, scan, scannedOids, child)
	}

	for _, parent := range parents {
		if _, found := scannedOids[*parent]; found {
			continue
		}

		err = s.scan(quietLogger, logger, dbRepository, scan, scannedOids, child, parent)
		if err != nil {
			return err
		}

		if stopPoint != nil && parent.Equal(stopPoint) {
			continue
		}

		return s.scanAncestors(quietLogger, logger, repo, dbRepository, scan, scannedOids, parent, stopPoint)
	}

	return nil
}

func (s *scanner) scan(
	quietLogger lager.Logger,
	logger lager.Logger,
	dbRepository db.Repository,
	scan db.ActiveScan,
	scannedOids map[git.Oid]struct{},
	child *git.Oid,
	parents ...*git.Oid,
) error {
	var parent *git.Oid
	if len(parents) == 1 {
		parent = parents[0]
	}

	diff, err := s.gitClient.Diff(dbRepository.Path, parent, child)
	if err != nil {
		return err
	}

	s.sniffer.Sniff(
		quietLogger,
		diffscanner.NewDiffScanner(strings.NewReader(diff)),
		func(logger lager.Logger, violation scanners.Violation) error {
			line := violation.Line
			scan.RecordCredential(db.NewCredential(
				dbRepository.Owner,
				dbRepository.Name,
				child.String(),
				line.Path,
				line.LineNumber,
				violation.Start,
				violation.End,
			))
			return nil
		},
	)

	scannedOids[*child] = struct{}{}

	return nil
}
<commit_msg>Clean up ancestry scanner<commit_after>package revok

import (
	"cred-alert/db"
	"cred-alert/gitclient"
	"cred-alert/kolsch"
	"cred-alert/metrics"
	"cred-alert/scanners"
	"cred-alert/scanners/diffscanner"
	"cred-alert/sniff"
	"strings"

	"code.cloudfoundry.org/lager"
	git "github.com/libgit2/git2go"
)

//go:generate counterfeiter . Scanner

type Scanner interface {
	Scan(lager.Logger, string, string, string, string) error
}

type scanner struct {
	gitClient            gitclient.Client
	repositoryRepository db.RepositoryRepository
	scanRepository       db.ScanRepository
	sniffer              sniff.Sniffer
}

func NewScanner(
	gitClient gitclient.Client,
	repositoryRepository db.RepositoryRepository,
	scanRepository db.ScanRepository,
	sniffer sniff.Sniffer,
	emitter metrics.Emitter,
) Scanner {
	return &scanner{
		gitClient:            gitClient,
		repositoryRepository: repositoryRepository,
		scanRepository:       scanRepository,
		sniffer:              sniffer,
	}
}

func (s *scanner) Scan(
	logger lager.Logger,
	owner string,
	repository string,
	startSHA string,
	stopSHA string,
) error {
	dbRepository, err := s.repositoryRepository.Find(owner, repository)
	if err != nil {
		logger.Error("failed-to-find-db-repo", err)
		return err
	}

	repo, err := git.OpenRepository(dbRepository.Path)
	if err != nil {
		logger.Error("failed-to-open-repo", err)
		return err
	}

	startOid, err := git.NewOid(startSHA)
	if err != nil {
		logger.Error("failed-to-create-start-oid", err)
		return err
	}

	var stopOid *git.Oid
	if stopSHA != "" {
		var err error
		stopOid, err = git.NewOid(stopSHA)
		if err != nil {
			logger.Error("failed-to-create-stop-oid", err)
			return err
		}
	}

	quietLogger := kolsch.NewLogger()
	scan := s.scanRepository.Start(quietLogger, "repo-scan", &dbRepository, nil)
	scannedOids := map[git.Oid]struct{}{}
	scanFunc := func(child, parent *git.Oid) error {
		diff, err := s.gitClient.Diff(dbRepository.Path, parent, child)
		if err != nil {
			return err
		}

		s.sniffer.Sniff(
			quietLogger,
			diffscanner.NewDiffScanner(strings.NewReader(diff)),
			func(logger lager.Logger, violation scanners.Violation) error {
				line := violation.Line
				scan.RecordCredential(db.NewCredential(
					dbRepository.Owner,
					dbRepository.Name,
					child.String(),
					line.Path,
					line.LineNumber,
					violation.Start,
					violation.End,
				))
				return nil
			},
		)

		scannedOids[*child] = struct{}{}

		return nil
	}

	err = s.scanAncestors(repo, scanFunc, scannedOids, startOid, stopOid)
	if err != nil {
		logger.Error("failed-to-scan", err)
	}

	err = scan.Finish()
	if err != nil {
		logger.Error("failed-to-finish-scan", err)
		return err
	}

	return nil
}

func (s *scanner) scanAncestors(
	repo *git.Repository,
	scanFunc func(*git.Oid, *git.Oid) error,
	scannedOids map[git.Oid]struct{},
	child *git.Oid,
	stopPoint *git.Oid,
) error {
	parents, err := s.gitClient.GetParents(repo, child)
	if err != nil {
		return err
	}

	if len(parents) == 0 {
		return scanFunc(child, nil)
	}

	for _, parent := range parents {
		if _, found := scannedOids[*parent]; found {
			continue
		}

		err = scanFunc(child, parent)
		if err != nil {
			return err
		}

		if stopPoint != nil && parent.Equal(stopPoint) {
			continue
		}

		return s.scanAncestors(repo, scanFunc, scannedOids, parent, stopPoint)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Santiago Arias | Remy Jourde
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

// Package sessions provides the JSON handlers to handle connections to gonawin app.
package sessions

import (
	"errors"
	golog "log"
	"net/http"
	"net/url"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"appengine/user"

	oauth "github.com/garyburd/go-oauth/oauth"

	"github.com/santiaago/gonawin/helpers"
	authhlp "github.com/santiaago/gonawin/helpers/auth"
	"github.com/santiaago/gonawin/helpers/log"
	"github.com/santiaago/gonawin/helpers/memcache"
	templateshlp "github.com/santiaago/gonawin/helpers/templates"

	gwconfig "github.com/santiaago/gonawin/config"
	mdl "github.com/santiaago/gonawin/models"
)

var (
	config                 *gwconfig.GwConfig
	twitterConfig          oauth.Client
	twitterCallbackURL     string
	googleVerifyTokenURL   string
	facebookVerifyTokenURL string
)

func init() {
	// read config file.
	var err error
	if config, err = gwconfig.ReadConfig(""); err != nil {
		golog.Printf("Error: unable to read config file; %v", err)
	}
	// Set up a configuration for twitter.
	twitterConfig = oauth.Client{
		Credentials:                   oauth.Credentials{Token: config.Twitter.Token, Secret: config.Twitter.Secret},
		TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token",
		ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authorize",
		TokenRequestURI:               "https://api.twitter.com/oauth/access_token",
	}
	twitterCallbackURL = "/j/auth/twitter/callback"
	googleVerifyTokenURL = "https://www.google.com/accounts/AuthSubTokenInfo?bearer_token"
	facebookVerifyTokenURL = "https://graph.facebook.com/me?access_token"
}

// Authenticate handler, use it to authenticate a user.
// It returns the JSON data of the requested user.
func Authenticate(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)

	userInfo := authhlp.UserInfo{Id: r.FormValue("id"), Email: r.FormValue("email"), Name: r.FormValue("name")}

	var verifyURL string
	if r.FormValue("provider") == "google" {
		verifyURL = googleVerifyTokenURL
	} else if r.FormValue("provider") == "facebook" {
		verifyURL = facebookVerifyTokenURL
	}

	if !authhlp.CheckUserValidity(r, verifyURL, r.FormValue("access_token")) {
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsAccessTokenNotValid)}
	}

	var user *mdl.User
	var err error
	if user, err = mdl.SigninUser(w, r, "Email", userInfo.Email, userInfo.Name, userInfo.Name); err != nil {
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsUnableToSignin)}
	}

	imageURL := helpers.UserImageURL(user.Username, user.Id)

	userData := struct {
		User     *mdl.User
		ImageURL string
	}{
		user,
		imageURL,
	}

	return templateshlp.RenderJson(w, c, userData)
}

// TwitterAuth handler, use it to authenticate via twitter.
func TwitterAuth(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)
	desc := "Twitter Auth handler:"

	credentials, err := twitterConfig.RequestTemporaryCredentials(urlfetch.Client(c), "http://"+r.Host+twitterCallbackURL, nil)
	if err != nil {
		c.Errorf("JsonTwitterAuth, error = %v", err)
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetTempCredentials)}
	}

	if err = memcache.Set(c, "secret", credentials.Secret); err != nil {
		// store secret in datastore
		secretId, _, err := datastore.AllocateIDs(c, "Secret", nil, 1)
		if err != nil {
			log.Errorf(c, "%s Cannot allocate ID for secret. %v", desc, err)
		}

		key := datastore.NewKey(c, "Secret", "", secretId, nil)

		_, err = datastore.Put(c, key, credentials.Secret)
		if err != nil {
			log.Errorf(c, "%s Cannot put secret in Datastore. %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotSetSecretValue)}
		}
	}

	// return OAuth token
	oAuthToken := struct {
		OAuthToken string
	}{
		credentials.Token,
	}

	return templateshlp.RenderJson(w, c, oAuthToken)
}

// TwitterAuthCallback handler, use it to make a callback for Twitter Authentication.
func TwitterAuthCallback(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	http.Redirect(w, r, "http://"+r.Host+"/#/auth/twitter/callback?oauth_token="+r.FormValue("oauth_token")+"&oauth_verifier="+r.FormValue("oauth_verifier"), http.StatusFound)
	return nil
}

// Twitter Authentication Callback
func TwitterUser(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Twitter User handler:"
	if r.Method == "GET" {

		var user *mdl.User

		log.Infof(c, "%s oauth_verifier = %s", desc, r.FormValue("oauth_verifier"))
		log.Infof(c, "%s oauth_token = %s", desc, r.FormValue("oauth_token"))

		// get the request token
		requestToken := r.FormValue("oauth_token")
		// update credentials with request token and 'secret cookie value'
		var cred oauth.Credentials
		cred.Token = requestToken
		if secret, err := memcache.Get(c, "secret"); secret != nil {
			cred.Secret = string(secret.([]byte))
		} else {
			log.Errorf(c, "%s cannot get secret value from memcache: %v", desc, err)
			// try to get secret from datastore
			q := datastore.NewQuery("Secret")
			var secrets []string
			if keys, err := q.GetAll(c, &secrets); err == nil && len(secrets) > 0 {
				secret = secrets[0]

				// delete secret from datastore
				if err = datastore.Delete(c, keys[0]); err != nil {
					log.Errorf(c, "%s Error when trying to delete 'secret' key in Datastore: %v", desc, err)
				}

			} else if err != nil || len(secrets) == 0 {
				log.Errorf(c, "%s cannot get secret value from Datastore: %v", desc, err)
				return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetSecretValue)}
			}
		}

		if err := memcache.Delete(c, "secret"); err != nil {
			log.Errorf(c, "%s Error when trying to delete memcached 'secret' key: %v", desc, err)
		}

		token, values, err := twitterConfig.RequestToken(urlfetch.Client(c), &cred, r.FormValue("oauth_verifier"))
		if err != nil {
			log.Errorf(c, "%s Error when trying to delete memcached 'secret' key: %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetRequestToken)}
		}

		// get user info
		urlValues := url.Values{}
		urlValues.Set("user_id", values.Get("user_id"))
		resp, err := twitterConfig.Get(urlfetch.Client(c), token, "https://api.twitter.com/1.1/users/show.json", urlValues)
		if err != nil {
			log.Errorf(c, "%s Cannot get user info from twitter. %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetUserInfo)}
		}

		userInfo, err := authhlp.FetchTwitterUserInfo(resp)
		if err != nil {
			log.Errorf(c, "%s Cannot get user info by fetching twitter response. %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetUserInfo)}
		}

		if user, err = mdl.SigninUser(w, r, "Username", "", userInfo.Screen_name, userInfo.Name); err != nil {
			log.Errorf(c, "%s Unable to signin user %s. %v", desc, userInfo.Name, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsUnableToSignin)}
		}

		// imageURL
		imageURL := helpers.UserImageURL(user.Username, user.Id)
		// return user
		userData := struct {
			User     *mdl.User
			ImageURL string
		}{
			user,
			imageURL,
		}

		return templateshlp.RenderJson(w, c, userData)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// JSON handler to get Google accounts login URL.
func GoogleAccountsLoginURL(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Google Accounts Login URL Handler:"
	if r.Method == "GET" {
		var url string
		var err error
		url, err = user.LoginURL(c, "/j/auth/google/callback/")
		if err != nil {
			log.Errorf(c, "%s error when getting Google accounts login URL", desc)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetGoogleLoginUrl)}
		}

		// return user
		loginData := struct {
			Url string
		}{
			url,
		}

		return templateshlp.RenderJson(w, c, loginData)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// Google Authentication Callback
func GoogleAuthCallback(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Google Accounts Auth Callback Handler:"
	if r.Method == "GET" {
		u := user.Current(c)
		if u == nil {
			log.Errorf(c, "%s user cannot be nil", desc)
			return &helpers.InternalServerError{Err: errors.New("user cannot be nil")}
		}

		http.Redirect(w, r, "http://"+r.Host+"/#/auth/google/callback?auth_token="+u.ID, http.StatusFound)
		return nil
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// JSON handler to get Google accounts user.
func GoogleUser(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Google Accounts User Handler:"
	if r.Method == "GET" {
		u := user.Current(c)
		if u == nil {
			log.Errorf(c, "%s user cannot be nil", desc)
			return &helpers.InternalServerError{Err: errors.New("user cannot be nil")}
		}

		if u.ID != r.FormValue("auth_token") {
			log.Errorf(c, "%s Auth token doesn't match user ID", desc)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsAccessTokenNotValid)}
		}

		userInfo := authhlp.GetUserGoogleInfo(u)

		var user *mdl.User
		var err error
		if user, err = mdl.SigninUser(w, r, "Email", userInfo.Email, userInfo.Name, userInfo.Name); err != nil {
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsUnableToSignin)}
		}

		// imageURL
		imageURL := helpers.UserImageURL(user.Username, user.Id)
		// return user
		userData := struct {
			User     *mdl.User
			ImageURL string
		}{
			user,
			imageURL,
		}

		return templateshlp.RenderJson(w, c, userData)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// JSON handler to delete cookie created by Google account
func GoogleDeleteCookie(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	if r.Method == "GET" {
		cookieName := "ACSID"
		if appengine.IsDevAppServer() {
			cookieName = "dev_appserver_login"
		}
		cookie := http.Cookie{Name: cookieName, Path: "/", MaxAge: -1}
		http.SetCookie(w, &cookie)

		return templateshlp.RenderJson(w, c, "Google user has been logged out")
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

func AuthServiceIds(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)

	if r.Method == "GET" {
		data := struct {
			GooglePlusClientId string
			FacebookAppId      string
		}{
			config.GooglePlus.ClientId,
			config.Facebook.AppId,
		}
		return templateshlp.RenderJson(w, c, data)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}
<commit_msg>session TwitterUser - exit first<commit_after>/*
 * Copyright (c) 2014 Santiago Arias | Remy Jourde
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

// Package sessions provides the JSON handlers to handle connections to gonawin app.
package sessions

import (
	"errors"
	golog "log"
	"net/http"
	"net/url"

	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"appengine/user"

	oauth "github.com/garyburd/go-oauth/oauth"

	"github.com/santiaago/gonawin/helpers"
	authhlp "github.com/santiaago/gonawin/helpers/auth"
	"github.com/santiaago/gonawin/helpers/log"
	"github.com/santiaago/gonawin/helpers/memcache"
	templateshlp "github.com/santiaago/gonawin/helpers/templates"

	gwconfig "github.com/santiaago/gonawin/config"
	mdl "github.com/santiaago/gonawin/models"
)

var (
	config                 *gwconfig.GwConfig
	twitterConfig          oauth.Client
	twitterCallbackURL     string
	googleVerifyTokenURL   string
	facebookVerifyTokenURL string
)

func init() {
	// read config file.
	var err error
	if config, err = gwconfig.ReadConfig(""); err != nil {
		golog.Printf("Error: unable to read config file; %v", err)
	}
	// Set up a configuration for twitter.
	twitterConfig = oauth.Client{
		Credentials:                   oauth.Credentials{Token: config.Twitter.Token, Secret: config.Twitter.Secret},
		TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token",
		ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authorize",
		TokenRequestURI:               "https://api.twitter.com/oauth/access_token",
	}
	twitterCallbackURL = "/j/auth/twitter/callback"
	googleVerifyTokenURL = "https://www.google.com/accounts/AuthSubTokenInfo?bearer_token"
	facebookVerifyTokenURL = "https://graph.facebook.com/me?access_token"
}

// Authenticate handler, use it to authenticate a user.
// It returns the JSON data of the requested user.
func Authenticate(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)

	userInfo := authhlp.UserInfo{Id: r.FormValue("id"), Email: r.FormValue("email"), Name: r.FormValue("name")}

	var verifyURL string
	if r.FormValue("provider") == "google" {
		verifyURL = googleVerifyTokenURL
	} else if r.FormValue("provider") == "facebook" {
		verifyURL = facebookVerifyTokenURL
	}

	if !authhlp.CheckUserValidity(r, verifyURL, r.FormValue("access_token")) {
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsAccessTokenNotValid)}
	}

	var user *mdl.User
	var err error
	if user, err = mdl.SigninUser(w, r, "Email", userInfo.Email, userInfo.Name, userInfo.Name); err != nil {
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsUnableToSignin)}
	}

	imageURL := helpers.UserImageURL(user.Username, user.Id)

	userData := struct {
		User     *mdl.User
		ImageURL string
	}{
		user,
		imageURL,
	}

	return templateshlp.RenderJson(w, c, userData)
}

// TwitterAuth handler, use it to authenticate via twitter.
func TwitterAuth(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)
	desc := "Twitter Auth handler:"

	credentials, err := twitterConfig.RequestTemporaryCredentials(urlfetch.Client(c), "http://"+r.Host+twitterCallbackURL, nil)
	if err != nil {
		c.Errorf("JsonTwitterAuth, error = %v", err)
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetTempCredentials)}
	}

	if err = memcache.Set(c, "secret", credentials.Secret); err != nil {
		// store secret in datastore
		secretId, _, err := datastore.AllocateIDs(c, "Secret", nil, 1)
		if err != nil {
			log.Errorf(c, "%s Cannot allocate ID for secret. %v", desc, err)
		}

		key := datastore.NewKey(c, "Secret", "", secretId, nil)

		_, err = datastore.Put(c, key, credentials.Secret)
		if err != nil {
			log.Errorf(c, "%s Cannot put secret in Datastore. %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotSetSecretValue)}
		}
	}

	// return OAuth token
	oAuthToken := struct {
		OAuthToken string
	}{
		credentials.Token,
	}

	return templateshlp.RenderJson(w, c, oAuthToken)
}

// TwitterAuthCallback handler, use it to make a callback for Twitter Authentication.
func TwitterAuthCallback(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	http.Redirect(w, r, "http://"+r.Host+"/#/auth/twitter/callback?oauth_token="+r.FormValue("oauth_token")+"&oauth_verifier="+r.FormValue("oauth_verifier"), http.StatusFound)
	return nil
}

// TwitterUser handler, use it to get the Twitter user data.
func TwitterUser(w http.ResponseWriter, r *http.Request) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)
	desc := "Twitter User handler:"

	var user *mdl.User

	log.Infof(c, "%s oauth_verifier = %s", desc, r.FormValue("oauth_verifier"))
	log.Infof(c, "%s oauth_token = %s", desc, r.FormValue("oauth_token"))

	// get the request token
	requestToken := r.FormValue("oauth_token")

	// update credentials with request token and 'secret cookie value'
	var cred oauth.Credentials
	cred.Token = requestToken
	if secret, err := memcache.Get(c, "secret"); secret != nil {
		cred.Secret = string(secret.([]byte))
	} else {
		log.Errorf(c, "%s cannot get secret value from memcache: %v", desc, err)
		// try to get secret from datastore
		q := datastore.NewQuery("Secret")
		var secrets []string
		if keys, err := q.GetAll(c, &secrets); err == nil && len(secrets) > 0 {
			secret = secrets[0]

			// delete secret from datastore
			if err = datastore.Delete(c, keys[0]); err != nil {
				log.Errorf(c, "%s Error when trying to delete 'secret' key in Datastore: %v", desc, err)
			}

		} else if err != nil || len(secrets) == 0 {
			log.Errorf(c, "%s cannot get secret value from Datastore: %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetSecretValue)}
		}
	}

	if err := memcache.Delete(c, "secret"); err != nil {
		log.Errorf(c, "%s Error when trying to delete memcached 'secret' key: %v", desc, err)
	}

	token, values, err := twitterConfig.RequestToken(urlfetch.Client(c), &cred, r.FormValue("oauth_verifier"))
	if err != nil {
		log.Errorf(c, "%s Error when trying to delete memcached 'secret' key: %v", desc, err)
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetRequestToken)}
	}

	// get user info
	urlValues := url.Values{}
	urlValues.Set("user_id", values.Get("user_id"))
	resp, err := twitterConfig.Get(urlfetch.Client(c), token, "https://api.twitter.com/1.1/users/show.json", urlValues)
	if err != nil {
		log.Errorf(c, "%s Cannot get user info from twitter. %v", desc, err)
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetUserInfo)}
	}

	userInfo, err := authhlp.FetchTwitterUserInfo(resp)
	if err != nil {
		log.Errorf(c, "%s Cannot get user info by fetching twitter response. %v", desc, err)
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetUserInfo)}
	}

	if user, err = mdl.SigninUser(w, r, "Username", "", userInfo.Screen_name, userInfo.Name); err != nil {
		log.Errorf(c, "%s Unable to signin user %s. %v", desc, userInfo.Name, err)
		return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsUnableToSignin)}
	}

	// imageURL
	imageURL := helpers.UserImageURL(user.Username, user.Id)
	// return user
	userData := struct {
		User     *mdl.User
		ImageURL string
	}{
		user,
		imageURL,
	}

	return templateshlp.RenderJson(w, c, userData)
}

// JSON handler to get Google accounts login URL.
func GoogleAccountsLoginURL(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Google Accounts Login URL Handler:"
	if r.Method == "GET" {
		var url string
		var err error
		url, err = user.LoginURL(c, "/j/auth/google/callback/")
		if err != nil {
			log.Errorf(c, "%s error when getting Google accounts login URL", desc)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsCannotGetGoogleLoginUrl)}
		}

		// return user
		loginData := struct {
			Url string
		}{
			url,
		}

		return templateshlp.RenderJson(w, c, loginData)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// Google Authentication Callback
func GoogleAuthCallback(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Google Accounts Auth Callback Handler:"
	if r.Method == "GET" {
		u := user.Current(c)
		if u == nil {
			log.Errorf(c, "%s user cannot be nil", desc)
			return &helpers.InternalServerError{Err: errors.New("user cannot be nil")}
		}

		http.Redirect(w, r, "http://"+r.Host+"/#/auth/google/callback?auth_token="+u.ID, http.StatusFound)
		return nil
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// JSON handler to get Google accounts user.
func GoogleUser(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	desc := "Google Accounts User Handler:"
	if r.Method == "GET" {
		u := user.Current(c)
		if u == nil {
			log.Errorf(c, "%s user cannot be nil", desc)
			return &helpers.InternalServerError{Err: errors.New("user cannot be nil")}
		}

		if u.ID != r.FormValue("auth_token") {
			log.Errorf(c, "%s Auth token doesn't match user ID", desc)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsAccessTokenNotValid)}
		}

		userInfo := authhlp.GetUserGoogleInfo(u)

		var user *mdl.User
		var err error
		if user, err = mdl.SigninUser(w, r, "Email", userInfo.Email, userInfo.Name, userInfo.Name); err != nil {
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeSessionsUnableToSignin)}
		}

		// imageURL
		imageURL := helpers.UserImageURL(user.Username, user.Id)
		// return user
		userData := struct {
			User     *mdl.User
			ImageURL string
		}{
			user,
			imageURL,
		}

		return templateshlp.RenderJson(w, c, userData)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// JSON handler to delete cookie created by Google account
func GoogleDeleteCookie(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	if r.Method == "GET" {
		cookieName := "ACSID"
		if appengine.IsDevAppServer() {
			cookieName = "dev_appserver_login"
		}
		cookie := http.Cookie{Name: cookieName, Path: "/", MaxAge: -1}
		http.SetCookie(w, &cookie)

		return templateshlp.RenderJson(w, c, "Google user has been logged out")
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

func AuthServiceIds(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)

	if r.Method == "GET" {
		data := struct {
			GooglePlusClientId string
			FacebookAppId      string
		}{
			config.GooglePlus.ClientId,
			config.Facebook.AppId,
		}
		return templateshlp.RenderJson(w, c, data)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

func GetGoVCLDir() string {
	for _, path := range strings.Split(os.Getenv("GOPATH"), ";") {
		pp := path + "/src/github.com/ying32/govcl"
		if checkFileExists(pp) {
			return pp
		}
	}
	for _, path := range strings.Split(os.Getenv("GOROOT"), ";") {
		pp := path + "/src/github.com/ying32/govcl"
		if checkFileExists(pp) {
			return pp
		}
	}
	return ""
}

func GetLibProjectFile(file string) string {
	path := GetGoVCLDir() + "/UILibSources" + file
	if checkFileExists(path) {
		if runtime.GOOS == "windows" {
			return strings.Replace(path, "/", "\\", -1)
		} else {
			return strings.Replace(path, "\\", "/", -1)
		}
	}
	return ""
}

func GetObjFileDir(path string) string {
	return os.TempDir() + string(os.PathSeparator) + path
}

func checkFileExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func fixDirName(dir string) string {
	if strings.HasSuffix(dir, "/") || strings.HasSuffix(dir, "\\") {
		dir = dir[:len(dir)-1]
	}
	return dir
}

func ExtractFilePath(path string) string {
	filename := filepath.Base(path)
	return path[:len(path)-len(filename)]
}

func execCmd(objFileDir, cmdStr string) error {
	extName := ""
	if runtime.GOOS == "windows" {
		extName = ".bat"
	}
	bashFileName := objFileDir + string(os.PathSeparator) + "compile" + extName
	if err := ioutil.WriteFile(bashFileName, []byte(cmdStr), 0755); err != nil {
		return err
	}
	if err := os.Chmod(bashFileName, 0755); err != nil {
		return err
	}
	cmd := exec.Command(bashFileName)
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func readLazarusEnvFile(szPath string) (lazarusDir string, fpcExe string) {
	envFile := szPath + "\\lazarus\\environmentoptions.xml"
	if runtime.GOOS != "windows" {
		envFile = strings.Replace(envFile, "\\", "/", -1)
	}
	if checkFileExists(envFile) {
		bs, err := ioutil.ReadFile(envFile)
		if err == nil {
			reg := regexp.MustCompile(`\<LazarusDirectory Value\=\"(.+?)"\>`)
			matchs := reg.FindSubmatch(bs)
			if len(matchs) >= 2 {
				lazarusDir = string(matchs[1])
			}
			reg = regexp.MustCompile(`\<CompilerFilename Value\=\"(.+?)\"\>`)
			matchs = reg.FindSubmatch(bs)
			if len(matchs) >= 2 {
				fpcExe = string(matchs[1])
			}
		}
	}
	return
}
<commit_msg>update Tools\libBuild\path.go<commit_after>package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

func GetGoVCLDir() string {
	getPath := func(key string) string {
		for _, path := range strings.Split(os.Getenv(key), ";") {
			pp := path + "/src/github.com/ying32/govcl"
			if checkFileExists(pp) {
				return pp
			}
		}
		return ""
	}
	ret := getPath("GOPATH")
	if ret == "" {
		return getPath("GOROOT")
	}
	return ret
}

func GetLibProjectFile(file string) string {
	path := GetGoVCLDir() + "/UILibSources" + file
	if checkFileExists(path) {
		if runtime.GOOS == "windows" {
			return strings.Replace(path, "/", "\\", -1)
		} else {
			return strings.Replace(path, "\\", "/", -1)
		}
	}
	return ""
}

func GetObjFileDir(path string) string {
	return os.TempDir() + string(os.PathSeparator) + path
}

func checkFileExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func fixDirName(dir string) string {
	if strings.HasSuffix(dir, "/") || strings.HasSuffix(dir, "\\") {
		dir = dir[:len(dir)-1]
	}
	return dir
}

func ExtractFilePath(path string) string {
	filename := filepath.Base(path)
	return path[:len(path)-len(filename)]
}

func execCmd(objFileDir, cmdStr string) error {
	extName := ""
	if runtime.GOOS == "windows" {
		extName = ".bat"
	} else {
		extName = ".sh"
	}
	bashFileName := objFileDir + string(os.PathSeparator) + "compile" + extName
	if err := ioutil.WriteFile(bashFileName, []byte(cmdStr), 0755); err != nil {
		return err
	}
	if err := os.Chmod(bashFileName, 0755); err != nil {
		return err
	}
	cmd := exec.Command(bashFileName)
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func readLazarusEnvFile(szPath string) (lazarusDir string, fpcExe string) {
	envFile := szPath + "\\lazarus\\environmentoptions.xml"
	if runtime.GOOS != "windows" {
		envFile = strings.Replace(envFile, "\\", "/", -1)
	}
	if checkFileExists(envFile) {
		bs, err := ioutil.ReadFile(envFile)
		if err == nil {
			reg := regexp.MustCompile(`\<LazarusDirectory Value\=\"(.+?)"\>`)
			matchs := reg.FindSubmatch(bs)
			if len(matchs) >= 2 {
				lazarusDir = string(matchs[1])
			}
			reg = regexp.MustCompile(`\<CompilerFilename Value\=\"(.+?)\"\>`)
			matchs = reg.FindSubmatch(bs)
			if len(matchs) >= 2 {
				fpcExe = string(matchs[1])
			}
		}
	}
	return
}
<|endoftext|>