<commit_before>package google

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"runtime/debug"
	"strings"
	"time"

	"github.com/TimothyYe/godns"
)

var (
	// GoogleUrl the API address for Google Domains
	GoogleUrl = "https://domains.google.com/nic/update"
)

// Handler struct
type Handler struct {
	Configuration *godns.Settings
}

// SetConfiguration pass dns settings and store it to handler instance
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
	handler.Configuration = conf
}

// DomainLoop the main logic loop
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("Recovered in %v: %v\n", err, debug.Stack())
			panicChan <- *domain
		}
	}()

	var lastIP string

	for {
		currentIP, err := godns.GetCurrentIP(handler.Configuration)

		if err != nil {
			log.Println("get_currentIP:", err)
			continue
		}
		log.Println("currentIP is:", currentIP)

		//check against locally cached IP, if no change, skip update
		if currentIP == lastIP {
			log.Printf("IP is the same as cached one. Skip update.\n")
		} else {
			lastIP = currentIP

			for _, subDomain := range domain.SubDomains {
				log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
				handler.UpdateIP(domain.DomainName, subDomain, currentIP)

				// Send mail notification if notify is enabled
				if handler.Configuration.Notify.Enabled {
					log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
					if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
						log.Println("Failed to send notification")
					}
				}
			}
		}
		// Sleep with interval
		log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
		time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
	}

}

// UpdateIP update subdomain with current IP
func (handler *Handler) UpdateIP(domain, subDomain, currentIP string) {
	values := url.Values{}
	values.Add("hostname", fmt.Sprintf("%s.%s", subDomain, domain))
	values.Add("username:password", fmt.Sprintf("%s:%s", handler.Configuration.Email, handler.Configuration.Password))
	values.Add("myip", currentIP)

	client := godns.GetHttpClient(handler.Configuration)
	req, _ := http.NewRequest("POST", GoogleUrl, strings.NewReader(values.Encode()))

	if handler.Configuration.UserAgent != "" {
		req.Header.Add("User-Agent", handler.Configuration.UserAgent)
	}

	resp, err := client.Do(req)

	if err != nil {
		log.Println("Request error...")
		log.Println("Err:", err.Error())
	} else {
		body, _ := ioutil.ReadAll(resp.Body)
		if resp.StatusCode == http.StatusOK {
			log.Println("Update IP success:", string(body))
		} else {
			log.Println("Update IP failed:", string(body))
		}
	}
}
<commit_msg>fix basic auth issue<commit_after>package google

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"runtime/debug"
	"strings"
	"time"

	"github.com/TimothyYe/godns"
)

var (
	// GoogleUrl the API address for Google Domains
	GoogleUrl = "https://domains.google.com/nic/update"
)

// Handler struct
type Handler struct {
	Configuration *godns.Settings
}

// SetConfiguration pass dns settings and store it to handler instance
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
	handler.Configuration = conf
}

// DomainLoop the main logic loop
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("Recovered in %v: %v\n", err, debug.Stack())
			panicChan <- *domain
		}
	}()

	var lastIP string

	for {
		currentIP, err := godns.GetCurrentIP(handler.Configuration)

		if err != nil {
			log.Println("get_currentIP:", err)
			continue
		}
		log.Println("currentIP is:", currentIP)

		//check against locally cached IP, if no change, skip update
		if currentIP == lastIP {
			log.Printf("IP is the same as cached one. Skip update.\n")
		} else {
			lastIP = currentIP

			for _, subDomain := range domain.SubDomains {
				log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
				handler.UpdateIP(domain.DomainName, subDomain, currentIP)

				// Send mail notification if notify is enabled
				if handler.Configuration.Notify.Enabled {
					log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
					if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
						log.Println("Failed to send notification")
					}
				}
			}
		}
		// Sleep with interval
		log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
		time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
	}

}

// UpdateIP update subdomain with current IP
func (handler *Handler) UpdateIP(domain, subDomain, currentIP string) {
	values := url.Values{}
	values.Add("hostname", fmt.Sprintf("%s.%s", subDomain, domain))
	values.Add("myip", currentIP)

	client := godns.GetHttpClient(handler.Configuration)
	req, _ := http.NewRequest("POST", GoogleUrl, strings.NewReader(values.Encode()))
	req.SetBasicAuth(handler.Configuration.Email, handler.Configuration.Password)

	if handler.Configuration.UserAgent != "" {
		req.Header.Add("User-Agent", handler.Configuration.UserAgent)
	}

	resp, err := client.Do(req)

	if err != nil {
		log.Println("Request error...")
		log.Println("Err:", err.Error())
	} else {
		body, _ := ioutil.ReadAll(resp.Body)
		if resp.StatusCode == http.StatusOK {
			log.Println("Update IP success:", string(body))
		} else {
			log.Println("Update IP failed:", string(body))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"testing"

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

func TestNotPersistVersion(t *testing.T) {
	path = "./test.yaml"
	version := "version-not-on-disk"
	revisionDate := "1970-1-1"
	buildDate := "1970-1-1"
	Load(version, revisionDate, buildDate)
	assert.Equal(t, settings.Version, version, "Should be set to version")
}
<commit_msg>Fixed settings_test.go<commit_after>package main

import (
	"testing"

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

func TestNotPersistVersion(t *testing.T) {
	path = "./test.yaml"
	version := "version-not-on-disk"
	revisionDate := "1970-1-1"
	buildDate := "1970-1-1"
	LoadSettings(version, revisionDate, buildDate)
	assert.Equal(t, settings.Version, version, "Should be set to version")
}
<|endoftext|>


<commit_before>package client

import (
	"net/http"
	"runtime"
	"strings"
	"time"

	"github.com/getlantern/analytics"
	"github.com/getlantern/flashlight/client"
	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/util"
	"github.com/getlantern/golog"
)

const (
	cloudConfigPollInterval = time.Second * 60
)

// clientConfig holds global configuration settings for all clients.
var (
	log           = golog.LoggerFor("lantern-android.client")
	clientConfig  *config
	trackingCodes = map[string]string{
		"FireTweet": "UA-21408036-4",
	}
)

// MobileClient is an extension of flashlight client with a few custom declarations for mobile
type MobileClient struct {
	client.Client
	closed  chan bool
	fronter *http.Client
	appName string
}

// init attempts to setup client configuration.
func init() {
	clientConfig = defaultConfig()
}

// NewClient creates a proxy client.
func NewClient(addr, appName string) *MobileClient {

	client := client.Client{
		Addr:         addr,
		ReadTimeout:  0, // don't timeout
		WriteTimeout: 0,
	}

	err := globals.SetTrustedCAs(clientConfig.getTrustedCerts())
	if err != nil {
		log.Errorf("Unable to configure trusted CAs: %s", err)
	}

	hqfd := client.Configure(clientConfig.Client)

	return &MobileClient{
		Client:  client,
		closed:  make(chan bool),
		fronter: hqfd.NewDirectDomainFronter(),
		appName: appName,
	}
}

func (client *MobileClient) ServeHTTP() {

	go func() {
		onListening := func() {
			log.Debugf("Now listening for connections...")
			go client.recordAnalytics()
		}

		defer func() {
			close(client.closed)
		}()

		if err := client.ListenAndServe(onListening); err != nil {
			// Error is not exported: https://golang.org/src/net/net.go#L284
			if !strings.Contains(err.Error(), "use of closed network connection") {
				panic(err.Error())
			}
		}
	}()
	go client.pollConfiguration()
}

func (client *MobileClient) recordAnalytics() {

	sessionPayload := &analytics.Payload{
		HitType:  analytics.EventType,
		Hostname: "localhost",
		Event: &analytics.Event{
			Category: "Session",
			Action:   "Start",
			Label:    runtime.GOOS,
		},
		UserAgent: "FireTweet",
	}

	if client.appName != "" {
		if appTrackingId, ok := trackingCodes[client.appName]; ok {
			sessionPayload.TrackingId = appTrackingId
		}
	}

	// Report analytics, proxying through the local client. Note this
	// is a little unorthodox by Lantern standards because it doesn't
	// pin the certificate of the cloud.yaml root CA, instead relying
	// on the go defaults.
	httpClient, err := util.HTTPClient("", client.Client.Addr)
	if err != nil {
		log.Errorf("Could not create HTTP client %v", err)
	} else {
		analytics.SessionEvent(httpClient, sessionPayload)
	}
}

// updateConfig attempts to pull a configuration file from the network using
// the client proxy itself.
func (client *MobileClient) updateConfig() error {
	var buf []byte
	var err error
	if buf, err = pullConfigFile(client.fronter); err != nil {
		log.Errorf("Could not update config: '%v'", err)
		return err
	}
	return clientConfig.updateFrom(buf)
}

// pollConfiguration periodically checks for updates in the cloud configuration
// file.
func (client *MobileClient) pollConfiguration() {

	// initially poll the config immediately
	pollTimer := time.NewTimer(1)
	defer pollTimer.Stop()

	for {
		select {
		case <-client.closed:
			log.Debug("Closing poll configuration channel")
			return
		case <-pollTimer.C:
			// Attempt to update configuration.
			var err error
			if err = client.updateConfig(); err == nil {
				// Configuration changed, lets reload.
				err := globals.SetTrustedCAs(clientConfig.getTrustedCerts())
				if err != nil {
					log.Debugf("Unable to configure trusted CAs: %s", err)
				}
				hqfc := client.Configure(clientConfig.Client)
				client.fronter = hqfc.NewDirectDomainFronter()
			}
			// Sleeping 'till next pull.
			// update timer to poll every 60 seconds
			pollTimer.Reset(cloudConfigPollInterval)
		}
	}
}

// Stop is currently not implemented but should make the listener stop
// accepting new connections and then kill all active connections.
func (client *MobileClient) Stop() error {
	if err := client.Client.Stop(); err != nil {
		log.Errorf("Unable to stop proxy client: %q", err)
		return err
	}
	return nil
}
<commit_msg>analytics changes<commit_after>package client

import (
	"net/http"
	"runtime"
	"strings"
	"time"

	"github.com/getlantern/analytics"
	"github.com/getlantern/flashlight/client"
	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/util"
	"github.com/getlantern/golog"
)

const (
	cloudConfigPollInterval = time.Second * 60
)

// clientConfig holds global configuration settings for all clients.
var (
	log           = golog.LoggerFor("lantern-android.client")
	clientConfig  *config
	trackingCodes = map[string]string{
		"FireTweet": "UA-21408036-4",
	}
)

// MobileClient is an extension of flashlight client with a few custom declarations for mobile
type MobileClient struct {
	client.Client
	closed  chan bool
	fronter *http.Client
	appName string
}

// init attempts to setup client configuration.
func init() {
	clientConfig = defaultConfig()
}

// NewClient creates a proxy client.
func NewClient(addr, appName string) *MobileClient {

	client := client.Client{
		Addr:         addr,
		ReadTimeout:  0, // don't timeout
		WriteTimeout: 0,
	}

	err := globals.SetTrustedCAs(clientConfig.getTrustedCerts())
	if err != nil {
		log.Errorf("Unable to configure trusted CAs: %s", err)
	}

	hqfd := client.Configure(clientConfig.Client)

	return &MobileClient{
		Client:  client,
		closed:  make(chan bool),
		fronter: hqfd.NewDirectDomainFronter(),
		appName: appName,
	}
}

func (client *MobileClient) ServeHTTP() {

	go func() {
		onListening := func() {
			log.Debugf("Now listening for connections...")
			go client.recordAnalytics()
		}

		defer func() {
			close(client.closed)
		}()

		if err := client.ListenAndServe(onListening); err != nil {
			// Error is not exported: https://golang.org/src/net/net.go#L284
			if !strings.Contains(err.Error(), "use of closed network connection") {
				panic(err.Error())
			}
		}
	}()
	go client.pollConfiguration()
}

func (client *MobileClient) recordAnalytics() {

	sessionPayload := &analytics.Payload{
		HitType:    analytics.EventType,
		Hostname:   "localhost",
		TrackingId: trackingCodes["FireTweet"],
		Event: &analytics.Event{
			Category: "Session",
			Action:   "Start",
			Label:    runtime.GOOS,
		},
		UserAgent: "FireTweet",
	}

	// Report analytics, proxying through the local client. Note this
	// is a little unorthodox by Lantern standards because it doesn't
	// pin the certificate of the cloud.yaml root CA, instead relying
	// on the go defaults.
	httpClient, err := util.HTTPClient("", client.Client.Addr)
	if err != nil {
		log.Errorf("Could not create HTTP client %v", err)
	} else {
		analytics.SessionEvent(httpClient, sessionPayload)
	}
}

// updateConfig attempts to pull a configuration file from the network using
// the client proxy itself.
func (client *MobileClient) updateConfig() error {
	var buf []byte
	var err error
	if buf, err = pullConfigFile(client.fronter); err != nil {
		log.Errorf("Could not update config: '%v'", err)
		return err
	}
	return clientConfig.updateFrom(buf)
}

// pollConfiguration periodically checks for updates in the cloud configuration
// file.
func (client *MobileClient) pollConfiguration() {

	// initially poll the config immediately
	pollTimer := time.NewTimer(time.Second)
	defer pollTimer.Stop()

	for {
		select {
		case <-client.closed:
			log.Debug("Closing poll configuration channel")
			return
		case <-pollTimer.C:
			// Attempt to update configuration.
			var err error
			if err = client.updateConfig(); err == nil {
				// Configuration changed, lets reload.
				err := globals.SetTrustedCAs(clientConfig.getTrustedCerts())
				if err != nil {
					log.Debugf("Unable to configure trusted CAs: %s", err)
				}
				hqfc := client.Configure(clientConfig.Client)
				client.fronter = hqfc.NewDirectDomainFronter()
			}
			// Sleeping 'till next pull.
			// update timer to poll every 60 seconds
			pollTimer.Reset(cloudConfigPollInterval)
		}
	}
}

// Stop is currently not implemented but should make the listener stop
// accepting new connections and then kill all active connections.
func (client *MobileClient) Stop() error {
	if err := client.Client.Stop(); err != nil {
		log.Errorf("Unable to stop proxy client: %q", err)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright

package models

import (
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"time"
)

// types for the bookmarks database
type (

  	// Bookmarks stores a list of bookmarks.
  	// The Key which is generated when the object is passed to the collection.
  	// The Created and Viewed fields are maintained by the collection.
  	// The user of the package should initialise the List field.
	Bookmarks struct {
		//  Id		bson.ObjectId 	`json:"id"	bson:"_id"`
		Key     string     `json:"k"	bson:"k"`
		Created time.Time  `json:"c"	bson:"c"`
		Viewed  time.Time  `json:"v"	bson:"v"`
		List    []Bookmark `json:"l"	bson:"l"`
	}

  	// Bookmark stores a single bookmark.
   	// The user of the package should initialise the URL and Description fields.
	Bookmark struct {
		//  Id	bson.ObjectId 	`json:"id"	bson:"_id"`
		URL         string `json:"u"	bson:"u"`
		Description string `json:"d"	bson:"d"`
	}

  	// bookmarkRepo stores the collection
	bookmarkRepo struct {
		Collection *mgo.Collection
	}

  	// AddRequest stores a request for adding a Bookmarks object
  	// to the bookmarks collection
  	AddRequest struct {
    	list Bookmarks
      	reply chan string // Return the key
    }

  	// ListRetrieve stores a request for retrieving a Bookmarks object
  	// from the bookmarks collection by key
  	ListRetrieve struct {
      key string
      reply chan Bookmarks	// Returns the Bookmarks object found
  	}
)

// Database Collection info
const  listCollection = "lists"

// Retrieve finds a Bookmarks object in the database
// with the value of the Key field given by key
func retrieve(lists bookmarkRepo, key string) Bookmarks {

  	result := Bookmarks{}
  	err := lists.Collection.Find(bson.M{"key": key}).One(&result)
	if err != nil {
		panic(err)
	}

	return result

}

// save inserts a Bookmarks object into the database
func save(lists bookmarkRepo, bookmarks Bookmarks) {

  	// Store the current time
    created := time.Now()
  	bookmarks.Created = created
  	bookmarks.Viewed = created

  	// Enter the bookmarks document into the database
	err := lists.Collection.Insert(bookmarks)
	if err != nil {
		panic(err)
	}

}

// BookmarksCollection maintains the bookmarks collection
// and serves requests. It provides channels for retrieval,
// insertion, and removal. It communicates to WordList via
// the code channel.
func BookmarksCollection(getList chan ListRetrieve,
                         addList chan AddRequest,
                         removeList chan string) {
  // Run database
  newColRequest := make(chan ColRequest)
  dbQuit := make(chan bool)
  go Database(newColRequest, dbQuit)
  defer close(newColRequest)
  defer close(dbQuit)

  // Run the code generator - opens the words collection
  newCode := make(chan string) // For getting a new unique code
  freeCode := make(chan string) // For freeing a code after deletion
  go UniqueCodeTracker(newCode, freeCode, newColRequest)
  defer close(newCode)
  defer close(freeCode)

  // Get the bookmarks collection from the database
  reply := make(chan *mgo.Collection)
  newColRequest <- ColRequest{listCollection, reply}
  collection := <- reply
  lists := bookmarkRepo{collection}

  // Serve requests
  // 1. Get list by key
  // 2. Add list
  // 3. Remove list (propagate entries to parents)
  for {
    select {
    case req, ok := <- getList: // Retrieve list by key
      if ok {
        req.reply <- retrieve(lists, req.key) // reply by req.key
      } else { // Caller is dead
      	return // end silently
      }
    case req, ok := <- addList:
      if ok {
      	req.list.Key = <- newCode   // Generate key
        save(lists, req.list)		// Enter list into database
        req.reply <- req.list.Key   // Return key
      } else { // Caller is dead
      	return // end silently
      }
    case _, ok := <- removeList: // Remove list by key
      if ok {
        // To Delete list by key:
        // 1. Retrieve list
        // 2. Modify list dependencies
        // 3. Remove original from database
        // 4. Free key
        _ = 1
      } else { // Caller is dead
      	return // end silently
      }
    }
  }

}

<commit_msg>Error handling for retrieve If the list is not found, return an empty list<commit_after>// Copyright

package models

import (
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"time"
)

// types for the bookmarks database
type (

  	// Bookmarks stores a list of bookmarks.
  	// The Key which is generated when the object is passed to the collection.
  	// The Created and Viewed fields are maintained by the collection.
  	// The user of the package should initialise the List field.
	Bookmarks struct {
		//  Id		bson.ObjectId 	`json:"id"	bson:"_id"`
		Key     string     `json:"k"	bson:"k"`
		Created time.Time  `json:"c"	bson:"c"`
		Viewed  time.Time  `json:"v"	bson:"v"`
		List    []Bookmark `json:"l"	bson:"l"`
	}

  	// Bookmark stores a single bookmark.
   	// The user of the package should initialise the URL and Description fields.
	Bookmark struct {
		//  Id	bson.ObjectId 	`json:"id"	bson:"_id"`
		URL         string `json:"u"	bson:"u"`
		Description string `json:"d"	bson:"d"`
	}

  	// bookmarkRepo stores the collection
	bookmarkRepo struct {
		Collection *mgo.Collection
	}

  	// AddRequest stores a request for adding a Bookmarks object
  	// to the bookmarks collection
  	AddRequest struct {
    	list Bookmarks
      	reply chan string // Return the key
    }

  	// ListRetrieve stores a request for retrieving a Bookmarks object
  	// from the bookmarks collection by key
  	ListRetrieve struct {
      key string
      reply chan Bookmarks	// Returns the Bookmarks object found
  	}
)

// Database Collection info
const  listCollection = "lists"

// Retrieve finds a Bookmarks object in the database
// with the value of the Key field given by key
func retrieve(lists bookmarkRepo, key string) Bookmarks {

  	result := Bookmarks{}
  	err := lists.Collection.Find(bson.M{"k": key}).One(&result)
    if err != mgo.ErrNotFound {
      if err != nil {
        panic(err)
      }
    }

	return result

}

// save inserts a Bookmarks object into the database
func save(lists bookmarkRepo, bookmarks Bookmarks) {

  	// Store the current time
    created := time.Now()
  	bookmarks.Created = created
  	bookmarks.Viewed = created

  	// Enter the bookmarks document into the database
	err := lists.Collection.Insert(bookmarks)
	if err != nil {
		panic(err)
	}

}

// BookmarksCollection maintains the bookmarks collection
// and serves requests. It provides channels for retrieval,
// insertion, and removal. It communicates to WordList via
// the code channel.
func BookmarksCollection(getList chan ListRetrieve,
                         addList chan AddRequest,
                         removeList chan string) {
  // Run database
  newColRequest := make(chan ColRequest)
  dbQuit := make(chan bool)
  go Database(newColRequest, dbQuit)
  defer close(newColRequest)
  defer close(dbQuit)

  // Run the code generator - opens the words collection
  newCode := make(chan string) // For getting a new unique code
  freeCode := make(chan string) // For freeing a code after deletion
  go UniqueCodeTracker(newCode, freeCode, newColRequest)
  defer close(newCode)
  defer close(freeCode)

  // Get the bookmarks collection from the database
  reply := make(chan *mgo.Collection)
  newColRequest <- ColRequest{listCollection, reply}
  collection := <- reply
  lists := bookmarkRepo{collection}

  // Serve requests
  // 1. Get list by key
  // 2. Add list
  // 3. Remove list (propagate entries to parents)
  for {
    select {
    case req, ok := <- getList: // Retrieve list by key
      if ok {
        req.reply <- retrieve(lists, req.key) // reply by req.key
      } else { // Caller is dead
      	return // end silently
      }
    case req, ok := <- addList:
      if ok {
      	req.list.Key = <- newCode   // Generate key
        save(lists, req.list)		// Enter list into database
        req.reply <- req.list.Key   // Return key
      } else { // Caller is dead
      	return // end silently
      }
    case _, ok := <- removeList: // Remove list by key
      if ok {
        // To Delete list by key:
        // 1. Retrieve list
        // 2. Modify list dependencies
        // 3. Remove original from database
        // 4. Free key
        _ = 1
      } else { // Caller is dead
      	return // end silently
      }
    }
  }

}

<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"github.com/russross/blackfriday"
	"io"
	"io/ioutil"
	"launchpad.net/goyaml"
	"path/filepath"
	"strings"
)

// A Page represents the key-value pairs in a page or posts front-end YAML as
// well as the markup in the body.
type Page map[string]interface{}

// ParsePage will parse a file with front-end YAML and markup content, and
// return a key-value Page structure.
func ParsePage(fn string) (Page, error) {
	c, err := ioutil.ReadFile(fn)
	if err != nil {
		return nil, err
	}
	return parsePage(fn, c)
}

// Helper function that creates a new Page from a byte array, parsing the
// front-end YAML and the markup, and pre-calculating all page-level variables.
func parsePage(fn string, c []byte) (Page, error) {

	page, err := parseMatter(c) //map[string] interface{} { }
	if err != nil {
		return nil, err
	}

	ext := filepath.Ext(fn)
	ext_output := ext
	markdown := isMarkdown(fn)

	// if markdown, change the output extension to html
	if markdown {
		ext_output = ".html"
	}

	page["ext"] = ext
	page["output_ext"] = ext_output
	page["id"] = removeExt(fn)
	page["url"] = replaceExt(fn, ext_output)

	// if markdown, convert to html
	raw := parseContent(c)
	if markdown {
		page["content"] = string(blackfriday.MarkdownCommon(raw))
	} else {
		page["content"] = string(raw)
	}

	if page["layout"] == nil {
		page["layout"] = "default"
	}

	// according to spec, Jekyll allows user to enter either category or
	// categories. Convert single category to string array to be consistent ...
	if category := page.GetString("category"); category != "" {
		page["categories"] = []string{category}
		delete(page, "category")
	}

	return page, nil
}

// Helper function to parse the front-end yaml matter.
func parseMatter(content []byte) (Page, error) {
	page := map[string]interface{}{}
	err := goyaml.Unmarshal(content, &page)
	return page, err
}

// Helper function that separates the front-end yaml from the markup, and
// and returns only the markup (content) as a byte array.
func parseContent(content []byte) []byte {
	//now we need to parse out the markdown section create
	//buffered reader
	b := bytes.NewBuffer(content)
	m := new(bytes.Buffer)
	streams := 0

	//read each line of the file and read the markdown section
	//which is the second document stream in the yaml file
parse:
	for {
		line, err := b.ReadString('\n')
		switch {
		case err == io.EOF && streams >= 2:
			m.WriteString(line)
			break parse
		case err == io.EOF:
			break parse
		case err != nil:
			return nil
		case streams >= 2:
			m.WriteString(line)
		case strings.HasPrefix(line, "---"):
			streams++
		}
	}

	return m.Bytes()
}

// Sets a parameter value.
func (p Page) Set(key string, val interface{}) {
	p[key] = val
}

// Gets a parameter value.
func (p Page) Get(key string) interface{} {
	return p[key]
}

// Gets a parameter value as a string. If none exists return an empty string.
func (p Page) GetString(key string) (str string) {
	if v, ok := p[key]; ok {
		str = v.(string)
	}
	return
}

// Gets a parameter value as a string array.
func (p Page) GetStrings(key string) (strs []string) {
	if v, ok := p[key]; ok {
		switch v.(type) {
		case []interface{}:
			for _, s := range v.([]interface{}) {
				strs = append(strs, s.(string))
			}
		case string:
			for _, s := range strings.Split(v.(string), ",") {
				if x := strings.TrimSpace(s); len(x) > 0 {
					strs = append(strs, x)
				}
			}
		}
	}
	return
}

// Gets a parameter value as a byte array.
func (p Page) GetBytes(key string) (b []byte) {
	if v, ok := p[key]; ok {
		b = v.([]byte)
	}
	return
}

// Gets the layout file to use, without the extension.
// Layout files must be placed in the _layouts directory.
func (p Page) GetLayout() string {
	return p.GetString("layout")
}

// Gets the title of the Page.
func (p Page) GetTitle() string {
	return p.GetString("title")
}

// Gets the URL / relative path of the Page.
// e.g. /2008/12/14/my-post.html
func (p Page) GetUrl() string {
	return p.GetString("url")
}

// Gets the Extension of the File (.html, .md, etc)
func (p Page) GetExt() string {
	return p.GetString("ext")
}

// Gets the un-rendered content of the Page.
func (p Page) GetContent() (c string) {
	if v, ok := p["content"]; ok {
		c = v.(string)
	}
	return
}

// Gets the list of tags to which this Post belongs.
func (p Page) GetTags() []string {
	return p.GetStrings("tags")
}

// Gets the list of categories to which this post belongs.
func (p Page) GetCategories() []string {
	return p.GetStrings("categories")
}
<commit_msg>page.GetString: ensure value can be converted to string, avoids panic.<commit_after>package main

import (
	"bytes"
	"github.com/russross/blackfriday"
	"io"
	"io/ioutil"
	"launchpad.net/goyaml"
	"path/filepath"
	"strings"
)

// A Page represents the key-value pairs in a page or posts front-end YAML as
// well as the markup in the body.
type Page map[string]interface{}

// ParsePage will parse a file with front-end YAML and markup content, and
// return a key-value Page structure.
func ParsePage(fn string) (Page, error) {
	c, err := ioutil.ReadFile(fn)
	if err != nil {
		return nil, err
	}
	return parsePage(fn, c)
}

// Helper function that creates a new Page from a byte array, parsing the
// front-end YAML and the markup, and pre-calculating all page-level variables.
func parsePage(fn string, c []byte) (Page, error) {

	page, err := parseMatter(c) //map[string] interface{} { }
	if err != nil {
		return nil, err
	}

	ext := filepath.Ext(fn)
	ext_output := ext
	markdown := isMarkdown(fn)

	// if markdown, change the output extension to html
	if markdown {
		ext_output = ".html"
	}

	page["ext"] = ext
	page["output_ext"] = ext_output
	page["id"] = removeExt(fn)
	page["url"] = replaceExt(fn, ext_output)

	// if markdown, convert to html
	raw := parseContent(c)
	if markdown {
		page["content"] = string(blackfriday.MarkdownCommon(raw))
	} else {
		page["content"] = string(raw)
	}

	if page["layout"] == nil {
		page["layout"] = "default"
	}

	// according to spec, Jekyll allows user to enter either category or
	// categories. Convert single category to string array to be consistent ...
	if category := page.GetString("category"); category != "" {
		page["categories"] = []string{category}
		delete(page, "category")
	}

	return page, nil
}

// Helper function to parse the front-end yaml matter.
func parseMatter(content []byte) (Page, error) {
	page := map[string]interface{}{}
	err := goyaml.Unmarshal(content, &page)
	return page, err
}

// Helper function that separates the front-end yaml from the markup, and
// and returns only the markup (content) as a byte array.
func parseContent(content []byte) []byte {
	//now we need to parse out the markdown section create
	//buffered reader
	b := bytes.NewBuffer(content)
	m := new(bytes.Buffer)
	streams := 0

	//read each line of the file and read the markdown section
	//which is the second document stream in the yaml file
parse:
	for {
		line, err := b.ReadString('\n')
		switch {
		case err == io.EOF && streams >= 2:
			m.WriteString(line)
			break parse
		case err == io.EOF:
			break parse
		case err != nil:
			return nil
		case streams >= 2:
			m.WriteString(line)
		case strings.HasPrefix(line, "---"):
			streams++
		}
	}

	return m.Bytes()
}

// Sets a parameter value.
func (p Page) Set(key string, val interface{}) {
	p[key] = val
}

// Gets a parameter value.
func (p Page) Get(key string) interface{} {
	return p[key]
}

// Gets a parameter value as a string. If none exists return an empty string.
func (p Page) GetString(key string) (str string) {
	if v, ok := p[key]; ok {
		switch v.(type) {
		case string:
			str = v.(string)
		}
	}
	return
}

// Gets a parameter value as a string array.
func (p Page) GetStrings(key string) (strs []string) {
	if v, ok := p[key]; ok {
		switch v.(type) {
		case []interface{}:
			for _, s := range v.([]interface{}) {
				strs = append(strs, s.(string))
			}
		case string:
			for _, s := range strings.Split(v.(string), ",") {
				if x := strings.TrimSpace(s); len(x) > 0 {
					strs = append(strs, x)
				}
			}
		}
	}
	return
}

// Gets a parameter value as a byte array.
func (p Page) GetBytes(key string) (b []byte) {
	if v, ok := p[key]; ok {
		b = v.([]byte)
	}
	return
}

// Gets the layout file to use, without the extension.
// Layout files must be placed in the _layouts directory.
func (p Page) GetLayout() string {
	return p.GetString("layout")
}

// Gets the title of the Page.
func (p Page) GetTitle() string {
	return p.GetString("title")
}

// Gets the URL / relative path of the Page.
// e.g. /2008/12/14/my-post.html
func (p Page) GetUrl() string {
	return p.GetString("url")
}

// Gets the Extension of the File (.html, .md, etc)
func (p Page) GetExt() string {
	return p.GetString("ext")
}

// Gets the un-rendered content of the Page.
func (p Page) GetContent() (c string) {
	if v, ok := p["content"]; ok {
		c = v.(string)
	}
	return
}

// Gets the list of tags to which this Post belongs.
func (p Page) GetTags() []string {
	return p.GetStrings("tags")
}

// Gets the list of categories to which this post belongs.
func (p Page) GetCategories() []string {
	return p.GetStrings("categories")
}
<|endoftext|>


<commit_before>package slackapi

import (
	"net/url"
	"strconv"
	"strings"
)

// ConversationsArchive archives a conversation.
func (s *SlackAPI) ConversationsArchive(channel string) Response {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out Response
	if err := s.basePOST("/api/conversations.archive", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

type ResponseConversationsClose struct {
	Response
	NoOp          bool `json:"no_op,omitempty"`
	AlreadyClosed bool `json:"already_closed,omitempty"`
}

// ConversationsClose closes a direct message or multi-person direct message.
// Ref: https://api.slack.com/methods/conversations.close
func (s *SlackAPI) ConversationsClose(channel string) ResponseConversationsClose {
	in := struct {
		Token   string `json:"token"`
		Channel string `json:"channel"`
	}{
		Token:   s.token,
		Channel: channel,
	}
	var out ResponseConversationsClose
	if err := s.basePOST("/api/conversations.close", in, &out); err != nil {
		return ResponseConversationsClose{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsCreate creates a channel.
func (s *SlackAPI) ConversationsCreate(name string) ResponseChannelsInfo {
	in := struct {
		Name     string `json:"name"`
		Validate bool   `json:"validate"`
	}{
		Name:     name,
		Validate: true,
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/channels.create", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsHistoryInput struct {
	// Conversation ID to fetch history for.
	Channel string `json:"channel"`
	// Paginate through collections of data by setting the cursor parameter to
	// a next_cursor attribute returned by a previous request's response_metadata.
	// Default value fetches the first "page" of the collection. See pagination
	// for more detail.
	Cursor string `json:"cursor"`
	// Include messages with latest or oldest timestamp in results only when
	// either timestamp is specified.
	Inclusive bool `json:"inclusive"`
	// End of time range of messages to include in results.
	Latest string `json:"latest"`
	// The maximum number of items to return. Fewer than the requested number
	// of items may be returned, even if the end of the users list hasn't been
	// reached.
	Limit int `json:"limit"`
	// Start of time range of messages to include in results.
	Oldest string `json:"oldest"`
}

// ConversationsHistory fetches a conversation's history of messages and events.
func (s *SlackAPI) ConversationsHistory(input ConversationsHistoryInput) History {
	var out History
	if err := s.basePOST("/api/conversations.history", input, &out); err != nil {
		return History{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsInfo retrieve information about a conversation.
func (s *SlackAPI) ConversationsInfo(channel string) ResponseChannelsInfo {
	in := url.Values{
		"channel":             []string{channel},
		"include_locale":      []string{"true"},
		"include_num_members": []string{"true"},
	}
	var out ResponseChannelsInfo
	if err := s.baseGET("/api/conversations.info", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsGenericInfo retrieve information about a generic conversation.
func (s *SlackAPI) ConversationsGenericInfo(channels string) ResponseChannelsGenericInfo {
	in := url.Values{
		"channels": []string{channels},
	}
	var out ResponseChannelsGenericInfo
	if err := s.baseGET("/api/conversations.genericInfo", in, &out); err != nil {
		return ResponseChannelsGenericInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsInvite invites users to a channel.
func (s *SlackAPI) ConversationsInvite(channel string, users ...string) ResponseChannelsInfo {
	in := struct {
		Channel string `json:"channel"`
		Users   string `json:"users"`
	}{
		Channel: channel,
		Users:   strings.Join(users, ","),
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.invite", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsJoin joins an existing conversation.
func (s *SlackAPI) ConversationsJoin(channel string) ResponseChannelsInfo {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.join", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsKick removes a user from a conversation.
func (s *SlackAPI) ConversationsKick(channel string, user string) Response {
	in := struct {
		Channel string `json:"channel"`
		User    string `json:"user"`
	}{
		Channel: channel,
		User:    user,
	}
	var out Response
	if err := s.basePOST("/api/conversations.kick", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

// ConversationsLeave leaves a conversation.
func (s *SlackAPI) ConversationsLeave(channel string) Response {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out Response
	if err := s.basePOST("/api/conversations.leave", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

type ConversationsMarkInput struct {
	// Authentication token bearing required scopes. Tokens should be passed
	// as an HTTP Authorization header or alternatively, as a POST parameter.
	Token string `json:"token"`
	// Conversation ID to fetch thread from.
	Channel string `json:"channel"`
	// Unique identifier of a thread's parent message.
	Timestamp string `json:"ts"`
}

// ConversationsMark sets the read cursor in a channel.
func (s *SlackAPI) ConversationsMark(input ConversationsMarkInput) Response {
	var out Response
	if err := s.basePOST("/api/conversations.mark", input, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

type ConversationsListInput struct {
	// Paginate through collections of data by setting the cursor parameter to
	// a next_cursor attribute returned by a previous request's response_metadata.
	// Default value fetches the first "page" of the collection. See pagination
	// for more detail.
	Cursor string `json:"cursor"`
	// Set to true to exclude archived channels from the list
	ExcludeArchived bool `json:"exclude_archived"`
	// The maximum number of items to return. Fewer than the requested number
	// of items may be returned, even if the end of the list hasn't been reached.
	// Must be an integer no larger than 1000.
	Limit int `json:"limit"`
	// Mix and match channel types by providing a comma-separated list of any
	// combination of public_channel, private_channel, mpim, im.
	Types []string `json:"types"`
}

// ConversationsList lists all channels in a Slack team.
func (s *SlackAPI) ConversationsList(input ConversationsListInput) ResponseChannelsList {
	in := url.Values{}

	if input.Cursor != "" {
		in.Add("cursor", input.Cursor)
	}

	if input.ExcludeArchived {
		in.Add("exclude_archived", "true")
	}

	if input.Limit > 0 {
		in.Add("limit", strconv.Itoa(input.Limit))
	}

	if len(input.Types) > 0 {
		in.Add("types", strings.Join(input.Types, ","))
	}

	var out ResponseChannelsList
	if err := s.baseGET("/api/conversations.list", in, &out); err != nil {
		return ResponseChannelsList{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsMembersInput struct {
	Token   string `json:"token"`
	Channel string `json:"channel"`
	Cursor  string `json:"cursor"`
	Limit   int    `json:"limit"`
}

type ResponseConversationsMembers struct {
	Response
	Members          []string         `json:"members"`
	ResponseMetadata ResponseMetadata `json:"response_metadata"`
}

// ConversationsMembers retrieve members of a conversation.
func (s *SlackAPI) ConversationsMembers(input ConversationsMembersInput) ResponseConversationsMembers {
	if input.Token == "" {
		input.Token = s.token
	}
	var out ResponseConversationsMembers
	if err := s.basePOST("/api/conversations.members", input, &out); err != nil {
		return ResponseConversationsMembers{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsRename renames a conversation.
func (s *SlackAPI) ConversationsRename(channel string, name string) ResponseChannelsInfo {
	in := struct {
		Channel string `json:"channel"`
		Name    string `json:"name"`
	}{
		Channel: channel,
		Name:    name,
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.rename", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsRepliesInput struct {
	// Conversation ID to fetch thread from.
	Channel string `json:"channel"`
	// Unique identifier of a thread's parent message.
	Timestamp string `json:"ts"`
	// Paginate through collections of data by setting the cursor parameter to
	// a next_cursor attribute returned by a previous request's response_metadata.
	// Default value fetches the first "page" of the collection. See pagination
	// for more detail.
	Cursor string `json:"cursor"`
	// Include messages with latest or oldest timestamp in results only when
	// either timestamp is specified.
	Inclusive bool `json:"inclusive"`
	// End of time range of messages to include in results.
	Latest string `json:"latest"`
	// The maximum number of items to return. Fewer than the requested number
	// of items may be returned, even if the end of the users list hasn't been
	// reached.
	Limit int `json:"limit"`
	// Start of time range of messages to include in results.
	Oldest string `json:"oldest"`
}

// ConversationsReplies lists all channels in a Slack team.
func (s *SlackAPI) ConversationsReplies(input ConversationsRepliesInput) History {
	in := url.Values{}

	in.Add("channel", input.Channel)
	in.Add("ts", input.Timestamp)

	if input.Cursor != "" {
		in.Add("cursor", input.Cursor)
	}

	if input.Inclusive {
		in.Add("inclusive", "true")
	}
	in.Add("latest", input.Latest)

	if input.Limit > 0 {
		in.Add("limit", strconv.Itoa(input.Limit))
	}

	in.Add("oldest", input.Oldest)

	var out History
	if err := s.baseGET("/api/conversations.replies", in, &out); err != nil {
		return History{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsSetPurpose sets the purpose for a conversation.
func (s *SlackAPI) ConversationsSetPurpose(channel string, purpose string) ChannelPurposeNow {
	in := struct {
		Channel string `json:"channel"`
		Purpose string `json:"purpose"`
	}{
		Channel: channel,
		Purpose: purpose,
	}
	var out ChannelPurposeNow
	if err := s.basePOST("/api/conversations.setPurpose", in, &out); err != nil {
		return ChannelPurposeNow{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsSetTopic sets the topic for a conversation.
func (s *SlackAPI) ConversationsSetTopic(channel string, topic string) ChannelTopicNow {
	in := struct {
		Channel string `json:"channel"`
		Topic   string `json:"topic"`
	}{
		Channel: channel,
		Topic:   topic,
	}
	var out ChannelTopicNow
	if err := s.basePOST("/api/conversations.setTopic", in, &out); err != nil {
		return ChannelTopicNow{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsUnarchive reverses conversation archival.
func (s *SlackAPI) ConversationsUnarchive(channel string) Response {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out Response
	if err := s.basePOST("/api/conversations.unarchive", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}
<commit_msg>Fix conversations.create method to accept all available input<commit_after>package slackapi

import (
	"net/url"
	"strconv"
	"strings"
)

// ConversationsArchive archives a conversation.
func (s *SlackAPI) ConversationsArchive(channel string) Response {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out Response
	if err := s.basePOST("/api/conversations.archive", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

type ResponseConversationsClose struct {
	Response
	NoOp          bool `json:"no_op,omitempty"`
	AlreadyClosed bool `json:"already_closed,omitempty"`
}

// ConversationsClose closes a direct message or multi-person direct message.
// Ref: https://api.slack.com/methods/conversations.close
func (s *SlackAPI) ConversationsClose(channel string) ResponseConversationsClose {
	in := struct {
		Token   string `json:"token"`
		Channel string `json:"channel"`
	}{
		Token:   s.token,
		Channel: channel,
	}
	var out ResponseConversationsClose
	if err := s.basePOST("/api/conversations.close", in, &out); err != nil {
		return ResponseConversationsClose{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsCreateInput struct {
	// Authentication token bearing required scopes. Tokens should be passed
	// as an HTTP Authorization header or alternatively, as a POST parameter.
	Token string `json:"token"`
	// Name of the public or private channel to create.
	Name string `json:"name"`
	// Create a private channel instead of a public one.
	IsPrivate bool `json:"is_private"`
	// Encoded team id to create the channel in, required if org token is used.
	TeamID string `json:"team_id"`
}

// ConversationsCreate creates a channel.
func (s *SlackAPI) ConversationsCreate(input ConversationsCreateInput) ResponseChannelsInfo {
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.create", input, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsHistoryInput struct {
	// Conversation ID to fetch history for.
	Channel string `json:"channel"`
	// Paginate through collections of data by setting the cursor parameter to
	// a next_cursor attribute returned by a previous request's response_metadata.
	// Default value fetches the first "page" of the collection. See pagination
	// for more detail.
	Cursor string `json:"cursor"`
	// Include messages with latest or oldest timestamp in results only when
	// either timestamp is specified.
	Inclusive bool `json:"inclusive"`
	// End of time range of messages to include in results.
	Latest string `json:"latest"`
	// The maximum number of items to return. Fewer than the requested number
	// of items may be returned, even if the end of the users list hasn't been
	// reached.
	Limit int `json:"limit"`
	// Start of time range of messages to include in results.
	Oldest string `json:"oldest"`
}

// ConversationsHistory fetches a conversation's history of messages and events.
func (s *SlackAPI) ConversationsHistory(input ConversationsHistoryInput) History {
	var out History
	if err := s.basePOST("/api/conversations.history", input, &out); err != nil {
		return History{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsInfo retrieve information about a conversation.
func (s *SlackAPI) ConversationsInfo(channel string) ResponseChannelsInfo {
	in := url.Values{
		"channel":             []string{channel},
		"include_locale":      []string{"true"},
		"include_num_members": []string{"true"},
	}
	var out ResponseChannelsInfo
	if err := s.baseGET("/api/conversations.info", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsGenericInfo retrieve information about a generic conversation.
func (s *SlackAPI) ConversationsGenericInfo(channels string) ResponseChannelsGenericInfo {
	in := url.Values{
		"channels": []string{channels},
	}
	var out ResponseChannelsGenericInfo
	if err := s.baseGET("/api/conversations.genericInfo", in, &out); err != nil {
		return ResponseChannelsGenericInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsInvite invites users to a channel.
func (s *SlackAPI) ConversationsInvite(channel string, users ...string) ResponseChannelsInfo {
	in := struct {
		Channel string `json:"channel"`
		Users   string `json:"users"`
	}{
		Channel: channel,
		Users:   strings.Join(users, ","),
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.invite", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsJoin joins an existing conversation.
func (s *SlackAPI) ConversationsJoin(channel string) ResponseChannelsInfo {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.join", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsKick removes a user from a conversation.
func (s *SlackAPI) ConversationsKick(channel string, user string) Response {
	in := struct {
		Channel string `json:"channel"`
		User    string `json:"user"`
	}{
		Channel: channel,
		User:    user,
	}
	var out Response
	if err := s.basePOST("/api/conversations.kick", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

// ConversationsLeave leaves a conversation.
func (s *SlackAPI) ConversationsLeave(channel string) Response {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out Response
	if err := s.basePOST("/api/conversations.leave", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

type ConversationsMarkInput struct {
	// Authentication token bearing required scopes. Tokens should be passed
	// as an HTTP Authorization header or alternatively, as a POST parameter.
	Token string `json:"token"`
	// Conversation ID to fetch thread from.
	Channel string `json:"channel"`
	// Unique identifier of a thread's parent message.
	Timestamp string `json:"ts"`
}

// ConversationsMark sets the read cursor in a channel.
func (s *SlackAPI) ConversationsMark(input ConversationsMarkInput) Response {
	var out Response
	if err := s.basePOST("/api/conversations.mark", input, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}

type ConversationsListInput struct {
	// Paginate through collections of data by setting the cursor parameter to
	// a next_cursor attribute returned by a previous request's response_metadata.
	// Default value fetches the first "page" of the collection. See pagination
	// for more detail.
	Cursor string `json:"cursor"`
	// Set to true to exclude archived channels from the list
	ExcludeArchived bool `json:"exclude_archived"`
	// The maximum number of items to return. Fewer than the requested number
	// of items may be returned, even if the end of the list hasn't been reached.
	// Must be an integer no larger than 1000.
	Limit int `json:"limit"`
	// Mix and match channel types by providing a comma-separated list of any
	// combination of public_channel, private_channel, mpim, im.
	Types []string `json:"types"`
}

// ConversationsList lists all channels in a Slack team.
func (s *SlackAPI) ConversationsList(input ConversationsListInput) ResponseChannelsList {
	in := url.Values{}

	if input.Cursor != "" {
		in.Add("cursor", input.Cursor)
	}

	if input.ExcludeArchived {
		in.Add("exclude_archived", "true")
	}

	if input.Limit > 0 {
		in.Add("limit", strconv.Itoa(input.Limit))
	}

	if len(input.Types) > 0 {
		in.Add("types", strings.Join(input.Types, ","))
	}

	var out ResponseChannelsList
	if err := s.baseGET("/api/conversations.list", in, &out); err != nil {
		return ResponseChannelsList{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsMembersInput struct {
	Token   string `json:"token"`
	Channel string `json:"channel"`
	Cursor  string `json:"cursor"`
	Limit   int    `json:"limit"`
}

type ResponseConversationsMembers struct {
	Response
	Members          []string         `json:"members"`
	ResponseMetadata ResponseMetadata `json:"response_metadata"`
}

// ConversationsMembers retrieve members of a conversation.
func (s *SlackAPI) ConversationsMembers(input ConversationsMembersInput) ResponseConversationsMembers {
	if input.Token == "" {
		input.Token = s.token
	}
	var out ResponseConversationsMembers
	if err := s.basePOST("/api/conversations.members", input, &out); err != nil {
		return ResponseConversationsMembers{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsRename renames a conversation.
func (s *SlackAPI) ConversationsRename(channel string, name string) ResponseChannelsInfo {
	in := struct {
		Channel string `json:"channel"`
		Name    string `json:"name"`
	}{
		Channel: channel,
		Name:    name,
	}
	var out ResponseChannelsInfo
	if err := s.basePOST("/api/conversations.rename", in, &out); err != nil {
		return ResponseChannelsInfo{Response: Response{Error: err.Error()}}
	}
	return out
}

type ConversationsRepliesInput struct {
	// Conversation ID to fetch thread from.
	Channel string `json:"channel"`
	// Unique identifier of a thread's parent message.
	Timestamp string `json:"ts"`
	// Paginate through collections of data by setting the cursor parameter to
	// a next_cursor attribute returned by a previous request's response_metadata.
	// Default value fetches the first "page" of the collection. See pagination
	// for more detail.
	Cursor string `json:"cursor"`
	// Include messages with latest or oldest timestamp in results only when
	// either timestamp is specified.
	Inclusive bool `json:"inclusive"`
	// End of time range of messages to include in results.
	Latest string `json:"latest"`
	// The maximum number of items to return. Fewer than the requested number
	// of items may be returned, even if the end of the users list hasn't been
	// reached.
	Limit int `json:"limit"`
	// Start of time range of messages to include in results.
	Oldest string `json:"oldest"`
}

// ConversationsReplies lists all channels in a Slack team.
func (s *SlackAPI) ConversationsReplies(input ConversationsRepliesInput) History {
	in := url.Values{}

	in.Add("channel", input.Channel)
	in.Add("ts", input.Timestamp)

	if input.Cursor != "" {
		in.Add("cursor", input.Cursor)
	}

	if input.Inclusive {
		in.Add("inclusive", "true")
	}
	in.Add("latest", input.Latest)

	if input.Limit > 0 {
		in.Add("limit", strconv.Itoa(input.Limit))
	}

	in.Add("oldest", input.Oldest)

	var out History
	if err := s.baseGET("/api/conversations.replies", in, &out); err != nil {
		return History{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsSetPurpose sets the purpose for a conversation.
func (s *SlackAPI) ConversationsSetPurpose(channel string, purpose string) ChannelPurposeNow {
	in := struct {
		Channel string `json:"channel"`
		Purpose string `json:"purpose"`
	}{
		Channel: channel,
		Purpose: purpose,
	}
	var out ChannelPurposeNow
	if err := s.basePOST("/api/conversations.setPurpose", in, &out); err != nil {
		return ChannelPurposeNow{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsSetTopic sets the topic for a conversation.
func (s *SlackAPI) ConversationsSetTopic(channel string, topic string) ChannelTopicNow {
	in := struct {
		Channel string `json:"channel"`
		Topic   string `json:"topic"`
	}{
		Channel: channel,
		Topic:   topic,
	}
	var out ChannelTopicNow
	if err := s.basePOST("/api/conversations.setTopic", in, &out); err != nil {
		return ChannelTopicNow{Response: Response{Error: err.Error()}}
	}
	return out
}

// ConversationsUnarchive reverses conversation archival.
func (s *SlackAPI) ConversationsUnarchive(channel string) Response {
	in := struct {
		Channel string `json:"channel"`
	}{
		Channel: channel,
	}
	var out Response
	if err := s.basePOST("/api/conversations.unarchive", in, &out); err != nil {
		return Response{Error: err.Error()}
	}
	return out
}
<|endoftext|>


<commit_before>package lfs

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/rubyist/tracerx"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"time"
)

func DoHTTP(c *Configuration, req *http.Request) (*http.Response, error) {
	traceHttpRequest(c, req)
	res, err := c.HttpClient().Do(req)
	if res == nil {
		res = &http.Response{StatusCode: 0, Header: make(http.Header), Request: req}
	}
	traceHttpResponse(c, res)
	return res, err
}

func (c *Configuration) HttpClient() *http.Client {
	if c.httpClient == nil {
		tr := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 30 * time.Second,
		}
		sslVerify, _ := c.GitConfig("http.sslverify")
		if sslVerify == "false" || len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 {
			tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		}
		c.httpClient = &http.Client{
			Transport:     tr,
			CheckRedirect: checkRedirect,
		}
	}
	return c.httpClient
}

func checkRedirect(req *http.Request, via []*http.Request) error {
	if len(via) >= 3 {
		return errors.New("stopped after 3 redirects")
	}

	oldest := via[0]
	for key, _ := range oldest.Header {
		if key == "Authorization" {
			if req.URL.Scheme != oldest.URL.Scheme || req.URL.Host != oldest.URL.Host {
				continue
			}
		}
		req.Header.Set(key, oldest.Header.Get(key))
	}

	tracerx.Printf("api: redirect %s %s to %s", oldest.Method, oldest.URL, req.URL)

	return nil
}

var tracedTypes = []string{"json", "text", "xml", "html"}

func traceHttpRequest(c *Configuration, req *http.Request) {
	tracerx.Printf("HTTP: %s %s", req.Method, req.URL.String())

	if c.isTracingHttp == false {
		return
	}

	if req.Body != nil {
		req.Body = newCountedRequest(req)
	}

	fmt.Fprintf(os.Stderr, "> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto)
	for key, _ := range req.Header {
		fmt.Fprintf(os.Stderr, "> %s: %s\n", key, req.Header.Get(key))
	}
}

func traceHttpResponse(c *Configuration, res *http.Response) {
	if res == nil {
		return
	}

	tracerx.Printf("HTTP: %d", res.StatusCode)

	if c.isTracingHttp == false {
		return
	}

	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "< %s %s\n", res.Proto, res.Status)
	for key, _ := range res.Header {
		fmt.Fprintf(os.Stderr, "< %s: %s\n", key, res.Header.Get(key))
	}

	traceBody := false
	ctype := strings.ToLower(strings.SplitN(res.Header.Get("Content-Type"), ";", 2)[0])
	for _, tracedType := range tracedTypes {
		if strings.Contains(ctype, tracedType) {
			traceBody = true
		}
	}

	res.Body = newCountedResponse(res)
	if traceBody {
		res.Body = newTracedBody(res.Body)
	}

	fmt.Fprintf(os.Stderr, "\n")
}

const (
	countingUpload = iota
	countingDownload
)

type countingBody struct {
	Direction int
	Size      int64
	io.ReadCloser
}

func (r *countingBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	r.Size += int64(n)
	return n, err
}

func (r *countingBody) Close() error {
	if r.Direction == countingUpload {
		fmt.Fprintf(os.Stderr, "* uploaded %d bytes\n", r.Size)
	} else {
		fmt.Fprintf(os.Stderr, "* downloaded %d bytes\n", r.Size)
	}
	return r.ReadCloser.Close()
}

func newCountedResponse(res *http.Response) *countingBody {
	return &countingBody{countingDownload, 0, res.Body}
}

func newCountedRequest(req *http.Request) *countingBody {
	return &countingBody{countingUpload, 0, req.Body}
}

type tracedBody struct {
	io.ReadCloser
}

func (r *tracedBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	fmt.Fprintf(os.Stderr, "%s\n", string(p[0:n]))
	return n, err
}

func (r *tracedBody) Close() error {
	return r.ReadCloser.Close()
}

func newTracedBody(body io.ReadCloser) *tracedBody {
	return &tracedBody{body}
}
<commit_msg>set the connect and tls timeouts to just 5s<commit_after>package lfs

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/rubyist/tracerx"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"time"
)

func DoHTTP(c *Configuration, req *http.Request) (*http.Response, error) {
	traceHttpRequest(c, req)
	res, err := c.HttpClient().Do(req)
	if res == nil {
		res = &http.Response{StatusCode: 0, Header: make(http.Header), Request: req}
	}
	traceHttpResponse(c, res)
	return res, err
}

func (c *Configuration) HttpClient() *http.Client {
	if c.httpClient == nil {
		tr := &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial: (&net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout: 5 * time.Second,
		}
		sslVerify, _ := c.GitConfig("http.sslverify")
		if sslVerify == "false" || len(os.Getenv("GIT_SSL_NO_VERIFY")) > 0 {
			tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
		}
		c.httpClient = &http.Client{
			Transport:     tr,
			CheckRedirect: checkRedirect,
		}
	}
	return c.httpClient
}

func checkRedirect(req *http.Request, via []*http.Request) error {
	if len(via) >= 3 {
		return errors.New("stopped after 3 redirects")
	}

	oldest := via[0]
	for key, _ := range oldest.Header {
		if key == "Authorization" {
			if req.URL.Scheme != oldest.URL.Scheme || req.URL.Host != oldest.URL.Host {
				continue
			}
		}
		req.Header.Set(key, oldest.Header.Get(key))
	}

	tracerx.Printf("api: redirect %s %s to %s", oldest.Method, oldest.URL, req.URL)

	return nil
}

var tracedTypes = []string{"json", "text", "xml", "html"}

func traceHttpRequest(c *Configuration, req *http.Request) {
	tracerx.Printf("HTTP: %s %s", req.Method, req.URL.String())

	if c.isTracingHttp == false {
		return
	}

	if req.Body != nil {
		req.Body = newCountedRequest(req)
	}

	fmt.Fprintf(os.Stderr, "> %s %s %s\n", req.Method, req.URL.RequestURI(), req.Proto)
	for key, _ := range req.Header {
		fmt.Fprintf(os.Stderr, "> %s: %s\n", key, req.Header.Get(key))
	}
}

func traceHttpResponse(c *Configuration, res *http.Response) {
	if res == nil {
		return
	}

	tracerx.Printf("HTTP: %d", res.StatusCode)

	if c.isTracingHttp == false {
		return
	}

	fmt.Fprintf(os.Stderr, "\n")
	fmt.Fprintf(os.Stderr, "< %s %s\n", res.Proto, res.Status)
	for key, _ := range res.Header {
		fmt.Fprintf(os.Stderr, "< %s: %s\n", key, res.Header.Get(key))
	}

	traceBody := false
	ctype := strings.ToLower(strings.SplitN(res.Header.Get("Content-Type"), ";", 2)[0])
	for _, tracedType := range tracedTypes {
		if strings.Contains(ctype, tracedType) {
			traceBody = true
		}
	}

	res.Body = newCountedResponse(res)
	if traceBody {
		res.Body = newTracedBody(res.Body)
	}

	fmt.Fprintf(os.Stderr, "\n")
}

const (
	countingUpload = iota
	countingDownload
)

type countingBody struct {
	Direction int
	Size      int64
	io.ReadCloser
}

func (r *countingBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	r.Size += int64(n)
	return n, err
}

func (r *countingBody) Close() error {
	if r.Direction == countingUpload {
		fmt.Fprintf(os.Stderr, "* uploaded %d bytes\n", r.Size)
	} else {
		fmt.Fprintf(os.Stderr, "* downloaded %d bytes\n", r.Size)
	}
	return r.ReadCloser.Close()
}

func newCountedResponse(res *http.Response) *countingBody {
	return &countingBody{countingDownload, 0, res.Body}
}

func newCountedRequest(req *http.Request) *countingBody {
	return &countingBody{countingUpload, 0, req.Body}
}

type tracedBody struct {
	io.ReadCloser
}

func (r *tracedBody) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	fmt.Fprintf(os.Stderr, "%s\n", string(p[0:n]))
	return n, err
}

func (r *tracedBody) Close() error {
	return r.ReadCloser.Close()
}

func newTracedBody(body io.ReadCloser) *tracedBody {
	return &tracedBody{body}
}
<|endoftext|>


<commit_before><commit_msg>Fix dates<commit_after><|endoftext|>


<commit_before>package bosh

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"
)

var (
	client     = http.DefaultClient
	transport  = http.DefaultTransport
	bodyReader = ioutil.ReadAll
)

type Config struct {
	URL                 string
	Host                string
	DirectorCACert      string
	Username            string
	Password            string
	TaskPollingInterval time.Duration
	AllowInsecureSSL    bool
	Transport           http.RoundTripper
	UAA                 bool
}

type Client struct {
	config Config
}

type Task struct {
	Id     int
	State  string
	Result string
}

func NewClient(config Config) Client {
	if config.TaskPollingInterval == time.Duration(0) {
		config.TaskPollingInterval = 5 * time.Second
	}

	if config.Transport == nil {
		config.Transport = http.DefaultTransport
		if config.AllowInsecureSSL {
			config.Transport = &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			}
		}
	} else {
		config.UAA = true
	}

	client = &http.Client{
		Transport: config.Transport,
	}

	transport = config.Transport
	return Client{
		config: config,
	}
}

func (c Client) GetConfig() Config {
	return c.config
}

func (c Client) rewriteURL(uri string) (string, error) {
	parsedURL, err := url.Parse(uri)
	if err != nil {
		return "", err
	}

	parsedURL.Scheme = ""
	parsedURL.Host = ""

	return c.config.URL + parsedURL.String(), nil
}

func (c Client) checkTask(location string) (Task, error) {
	location, err := c.rewriteURL(location)
	if err != nil {
		return Task{}, err
	}

	var task Task
	request, err := http.NewRequest("GET", location, nil)
	if err != nil {
		return task, err
	}
	response, err := c.makeRequest(request)

	if err != nil {
		return task, err
	}

	err = json.NewDecoder(response.Body).Decode(&task)
	if err != nil {
		return task, err
	}

	return task, nil
}

func (c Client) checkTaskStatus(location string) (int, error) {
	for {
		task, err := c.checkTask(location)
		if err != nil {
			return 0, err
		}

		switch task.State {
		case "done":
			return task.Id, nil
		case "error":
			taskOutputs, err := c.GetTaskOutput(task.Id)
			if err != nil {
				return task.Id, fmt.Errorf("failed to get full bosh task event log, bosh task failed with an error status %q", task.Result)
			}
			return task.Id, taskOutputs[len(taskOutputs)-1].Error
		case "errored":
			taskOutputs, err := c.GetTaskOutput(task.Id)
			if err != nil {
				return task.Id, fmt.Errorf("failed to get full bosh task event log, bosh task failed with an errored status %q", task.Result)
			}
			return task.Id, taskOutputs[len(taskOutputs)-1].Error
		case "cancelled":
			return task.Id, errors.New("bosh task was cancelled")
		default:
			time.Sleep(c.config.TaskPollingInterval)
		}
	}
}

func (c Client) makeRequest(request *http.Request) (*http.Response, error) {
	if c.config.UAA {
		urlParts, err := url.Parse(c.config.URL)
		if err != nil {
			return &http.Response{}, err
		}

		boshHost, _, err := net.SplitHostPort(urlParts.Host)
		if err != nil {
			return &http.Response{}, err
		}

		ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{
			Transport: c.config.Transport,
		})
		conf := &clientcredentials.Config{
			ClientID:     c.config.Username,
			ClientSecret: c.config.Password,
			TokenURL:     fmt.Sprintf("https://%s:8443/oauth/token", boshHost),
		}

		httpClient := conf.Client(ctx)
		return httpClient.Do(request)
	} else {
		request.SetBasicAuth(c.config.Username, c.config.Password)
		return transport.RoundTrip(request)
	}
}
<commit_msg>Specify insecure skip verify on transport when specified.<commit_after>package bosh

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"
)

var (
	client     = http.DefaultClient
	transport  = http.DefaultTransport
	bodyReader = ioutil.ReadAll
)

type Config struct {
	URL                 string
	Host                string
	DirectorCACert      string
	Username            string
	Password            string
	TaskPollingInterval time.Duration
	AllowInsecureSSL    bool
	Transport           http.RoundTripper
	UAA                 bool
}

type Client struct {
	config Config
}

type Task struct {
	Id     int
	State  string
	Result string
}

func NewClient(config Config) Client {
	if config.TaskPollingInterval == time.Duration(0) {
		config.TaskPollingInterval = 5 * time.Second
	}

	config.Transport = http.DefaultTransport
	if config.AllowInsecureSSL {
		config.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	client = &http.Client{
		Transport: config.Transport,
	}

	transport = config.Transport
	return Client{
		config: config,
	}
}

func (c Client) GetConfig() Config {
	return c.config
}

func (c Client) rewriteURL(uri string) (string, error) {
	parsedURL, err := url.Parse(uri)
	if err != nil {
		return "", err
	}

	parsedURL.Scheme = ""
	parsedURL.Host = ""

	return c.config.URL + parsedURL.String(), nil
}

func (c Client) checkTask(location string) (Task, error) {
	location, err := c.rewriteURL(location)
	if err != nil {
		return Task{}, err
	}

	var task Task
	request, err := http.NewRequest("GET", location, nil)
	if err != nil {
		return task, err
	}
	response, err := c.makeRequest(request)

	if err != nil {
		return task, err
	}

	err = json.NewDecoder(response.Body).Decode(&task)
	if err != nil {
		return task, err
	}

	return task, nil
}

func (c Client) checkTaskStatus(location string) (int, error) {
	for {
		task, err := c.checkTask(location)
		if err != nil {
			return 0, err
		}

		switch task.State {
		case "done":
			return task.Id, nil
		case "error":
			taskOutputs, err := c.GetTaskOutput(task.Id)
			if err != nil {
				return task.Id, fmt.Errorf("failed to get full bosh task event log, bosh task failed with an error status %q", task.Result)
			}
			return task.Id, taskOutputs[len(taskOutputs)-1].Error
		case "errored":
			taskOutputs, err := c.GetTaskOutput(task.Id)
			if err != nil {
				return task.Id, fmt.Errorf("failed to get full bosh task event log, bosh task failed with an errored status %q", task.Result)
			}
			return task.Id, taskOutputs[len(taskOutputs)-1].Error
		case "cancelled":
			return task.Id, errors.New("bosh task was cancelled")
		default:
			time.Sleep(c.config.TaskPollingInterval)
		}
	}
}

func (c Client) makeRequest(request *http.Request) (*http.Response, error) {
	if c.config.UAA {
		urlParts, err := url.Parse(c.config.URL)
		if err != nil {
			return &http.Response{}, err
		}

		boshHost, _, err := net.SplitHostPort(urlParts.Host)
		if err != nil {
			return &http.Response{}, err
		}

		ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{
			Transport: c.config.Transport,
		})
		conf := &clientcredentials.Config{
			ClientID:     c.config.Username,
			ClientSecret: c.config.Password,
			TokenURL:     fmt.Sprintf("https://%s:8443/oauth/token", boshHost),
		}

		httpClient := conf.Client(ctx)
		return httpClient.Do(request)
	} else {
		request.SetBasicAuth(c.config.Username, c.config.Password)
		return transport.RoundTrip(request)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package lang

import (
	"encoding/json"
	"fmt"
	"strings"

	"regexp"

	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/parser"
	"github.com/sourcegraph/go-langserver/pkg/lsp"
	"github.com/sourcegraph/jsonrpc2"
)

type insertTextFormat int

const (
	text    insertTextFormat = 1
	snippet insertTextFormat = 2
	concept                  = "Concept"
	step                     = "Step"
)

type completionItem struct {
	lsp.CompletionItem
	InsertTextFormat insertTextFormat `json:"insertTextFormat,omitempty"`
}

type completionList struct {
	IsIncomplete bool             `json:"isIncomplete"`
	Items        []completionItem `json:"items"`
}

func completion(req *jsonrpc2.Request) (interface{}, error) {
	var params lsp.TextDocumentPositionParams
	if err := json.Unmarshal(*req.Params, &params); err != nil {
		return nil, err
	}
	line := getLine(params.TextDocument.URI, params.Position.Line)
	pLine := line[:params.Position.Character]
	if !isStepCompletion(pLine, params.Position.Character) {
		return nil, nil
	}
	list := completionList{IsIncomplete: false, Items: []completionItem{}}
	startPos, endPos := getEditPosition(line, params.Position)
	prefix := getPrefix(pLine)
	var givenArgs []gauge.StepArg
	var err error
	givenArgs, err = getStepArgs(strings.TrimSpace(pLine))
	if err != nil {
		return nil, err
	}
	for _, c := range provider.Concepts() {
		fText := prefix + getFilterText(c.StepValue.StepValue, c.StepValue.Parameters, givenArgs)
		cText := prefix + addPlaceHolders(c.StepValue.StepValue, c.StepValue.Parameters)
		list.Items = append(list.Items, newCompletionItem(c.StepValue.ParameterizedStepValue, cText, concept, fText, startPos, endPos))
	}
	for _, s := range provider.Steps() {
		fText := prefix + getFilterText(s.StepValue, s.Args, givenArgs)
		cText := prefix + addPlaceHolders(s.StepValue, s.Args)
		list.Items = append(list.Items, newCompletionItem(s.ParameterizedStepValue, cText, step, fText, startPos, endPos))
	}
	return list, nil
}

func getStepArgs(line string) ([]gauge.StepArg, error) {
	var givenArgs []gauge.StepArg
	if line != "" && strings.TrimSpace(line) != "*" {
		specParser := new(parser.SpecParser)
		tokens, errs := specParser.GenerateTokens(line, "")
		if len(errs) > 0 {
			return nil, fmt.Errorf("Unable to parse text entered")
		}
		var err error
		givenArgs, err = parser.ExtractStepArgsFromToken(tokens[0])
		if err != nil {
			return nil, fmt.Errorf("Unable to parse text entered")
		}
	}
	return givenArgs, nil

}

func isStepCompletion(line string, character int) bool {
	if character == 0 {
		return false
	}
	if !strings.HasPrefix(strings.TrimSpace(line), "*") {
		return false
	}
	return !inParameterContext(line, character)
}

func inParameterContext(line string, charPos int) bool {
	pl := line
	if len(line) > charPos {
		pl = line[:charPos]
	}
	lineAfterCharPos := strings.SplitAfter(pl, "*")
	if len(lineAfterCharPos) == 1 {
		return false
	}
	l := strings.TrimPrefix(strings.SplitAfter(pl, "*")[1], " ")
	var stack string
	for _, value := range l {
		if string(value) == "<" {
			stack = stack + string(value)
		}
		if string(value) == ">" && len(stack) != 0 && stack[len(stack)-1:] == "<" {
			stack = stack[:len(stack)-1]
		}
		if string(value) == "\"" {
			if len(stack) != 0 && stack[len(stack)-1:] == "\"" {
				stack = stack[:len(stack)-1]
			} else {
				stack = stack + string(value)
			}
		}
	}
	return len(stack) != 0
}

func getFilterText(text string, params []string, givenArgs []gauge.StepArg) string {
	if len(params) > 0 {
		for i, p := range params {
			if len(givenArgs) > i {
				if givenArgs[i].ArgType == gauge.Static {
					text = strings.Replace(text, "{}", fmt.Sprintf("\"%s\"", givenArgs[i].ArgValue()), 1)
				} else {
					text = strings.Replace(text, "{}", fmt.Sprintf("<%s>", givenArgs[i].ArgValue()), 1)
				}
			} else {
				text = strings.Replace(text, "{}", fmt.Sprintf("<%s>", p), 1)
			}
		}
	}
	return text
}

func getEditPosition(line string, cursorPos lsp.Position) (lsp.Position, lsp.Position) {
	start := 1
	loc := regexp.MustCompile(`^\s*\*(\s*)`).FindIndex([]byte(line))
	if loc != nil {
		start = loc[1]
	}
	if start > cursorPos.Character {
		start = cursorPos.Character
	}
	end := len(line)
	if end < 2 {
		end = 1
	}
	if end < cursorPos.Character {
		end = cursorPos.Character
	}
	return lsp.Position{Line: cursorPos.Line, Character: start}, lsp.Position{Line: cursorPos.Line, Character: end}
}

func getPrefix(line string) string {
	if strings.HasPrefix(strings.TrimPrefix(line, " "), "* ") {
		return ""
	}
	return " "
}

func resolveCompletion(req *jsonrpc2.Request) (interface{}, error) {
	var params completionItem
	if err := json.Unmarshal(*req.Params, &params); err != nil {
		return nil, err
	}
	return params, nil
}

func newCompletionItem(stepText, text, kind, fText string, startPos, endPos lsp.Position) completionItem {
	return completionItem{
		CompletionItem: lsp.CompletionItem{
			Label:      stepText,
			Detail:     kind,
			Kind:       lsp.CIKFunction,
			TextEdit:   lsp.TextEdit{Range: lsp.Range{Start: startPos, End: endPos}, NewText: text},
			FilterText: fText,
		},
		InsertTextFormat: snippet,
	}
}

func addPlaceHolders(text string, args []string) string {
	text = strings.Replace(text, "{}", "\"{}\"", -1)
	for i, v := range args {
		value := i + 1
		if value == len(args) {
			value = 0
		}
		text = strings.Replace(text, "{}", fmt.Sprintf("${%d:%s}", value, v), 1)
	}
	return text
}
<commit_msg>Fixing error when cache not upto date for lsp completion items<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package lang

import (
	"encoding/json"
	"fmt"
	"strings"

	"regexp"

	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/parser"
	"github.com/sourcegraph/go-langserver/pkg/lsp"
	"github.com/sourcegraph/jsonrpc2"
)

type insertTextFormat int

const (
	text    insertTextFormat = 1
	snippet insertTextFormat = 2
	concept                  = "Concept"
	step                     = "Step"
)

type completionItem struct {
	lsp.CompletionItem
	InsertTextFormat insertTextFormat `json:"insertTextFormat,omitempty"`
}

type completionList struct {
	IsIncomplete bool             `json:"isIncomplete"`
	Items        []completionItem `json:"items"`
}

func completion(req *jsonrpc2.Request) (interface{}, error) {
	var params lsp.TextDocumentPositionParams
	if err := json.Unmarshal(*req.Params, &params); err != nil {
		return nil, err
	}
	line := getLine(params.TextDocument.URI, params.Position.Line)
	pLine := line
	if len(line) > params.Position.Character {
		pLine = line[:params.Position.Character]
	}
	if !isStepCompletion(pLine, params.Position.Character) {
		return nil, nil
	}
	list := completionList{IsIncomplete: false, Items: []completionItem{}}
	startPos, endPos := getEditPosition(line, params.Position)
	prefix := getPrefix(pLine)
	var givenArgs []gauge.StepArg
	var err error
	givenArgs, err = getStepArgs(strings.TrimSpace(pLine))
	if err != nil {
		return nil, err
	}
	for _, c := range provider.Concepts() {
		fText := prefix + getFilterText(c.StepValue.StepValue, c.StepValue.Parameters, givenArgs)
		cText := prefix + addPlaceHolders(c.StepValue.StepValue, c.StepValue.Parameters)
		list.Items = append(list.Items, newCompletionItem(c.StepValue.ParameterizedStepValue, cText, concept, fText, startPos, endPos))
	}
	for _, s := range provider.Steps() {
		fText := prefix + getFilterText(s.StepValue, s.Args, givenArgs)
		cText := prefix + addPlaceHolders(s.StepValue, s.Args)
		list.Items = append(list.Items, newCompletionItem(s.ParameterizedStepValue, cText, step, fText, startPos, endPos))
	}
	return list, nil
}

func getStepArgs(line string) ([]gauge.StepArg, error) {
	var givenArgs []gauge.StepArg
	if line != "" && strings.TrimSpace(line) != "*" {
		specParser := new(parser.SpecParser)
		tokens, errs := specParser.GenerateTokens(line, "")
		if len(errs) > 0 {
			return nil, fmt.Errorf("Unable to parse text entered")
		}
		var err error
		givenArgs, err = parser.ExtractStepArgsFromToken(tokens[0])
		if err != nil {
			return nil, fmt.Errorf("Unable to parse text entered")
		}
	}
	return givenArgs, nil

}

func isStepCompletion(line string, character int) bool {
	if character == 0 {
		return false
	}
	if !strings.HasPrefix(strings.TrimSpace(line), "*") {
		return false
	}
	return !inParameterContext(line, character)
}

func inParameterContext(line string, charPos int) bool {
	pl := line
	if len(line) > charPos {
		pl = line[:charPos]
	}
	lineAfterCharPos := strings.SplitAfter(pl, "*")
	if len(lineAfterCharPos) == 1 {
		return false
	}
	l := strings.TrimPrefix(strings.SplitAfter(pl, "*")[1], " ")
	var stack string
	for _, value := range l {
		if string(value) == "<" {
			stack = stack + string(value)
		}
		if string(value) == ">" && len(stack) != 0 && stack[len(stack)-1:] == "<" {
			stack = stack[:len(stack)-1]
		}
		if string(value) == "\"" {
			if len(stack) != 0 && stack[len(stack)-1:] == "\"" {
				stack = stack[:len(stack)-1]
			} else {
				stack = stack + string(value)
			}
		}
	}
	return len(stack) != 0
}

func getFilterText(text string, params []string, givenArgs []gauge.StepArg) string {
	if len(params) > 0 {
		for i, p := range params {
			if len(givenArgs) > i {
				if givenArgs[i].ArgType == gauge.Static {
					text = strings.Replace(text, "{}", fmt.Sprintf("\"%s\"", givenArgs[i].ArgValue()), 1)
				} else {
					text = strings.Replace(text, "{}", fmt.Sprintf("<%s>", givenArgs[i].ArgValue()), 1)
				}
			} else {
				text = strings.Replace(text, "{}", fmt.Sprintf("<%s>", p), 1)
			}
		}
	}
	return text
}

func getEditPosition(line string, cursorPos lsp.Position) (lsp.Position, lsp.Position) {
	start := 1
	loc := regexp.MustCompile(`^\s*\*(\s*)`).FindIndex([]byte(line))
	if loc != nil {
		start = loc[1]
	}
	if start > cursorPos.Character {
		start = cursorPos.Character
	}
	end := len(line)
	if end < 2 {
		end = 1
	}
	if end < cursorPos.Character {
		end = cursorPos.Character
	}
	return lsp.Position{Line: cursorPos.Line, Character: start}, lsp.Position{Line: cursorPos.Line, Character: end}
}

func getPrefix(line string) string {
	if strings.HasPrefix(strings.TrimPrefix(line, " "), "* ") {
		return ""
	}
	return " "
}

func resolveCompletion(req *jsonrpc2.Request) (interface{}, error) {
	var params completionItem
	if err := json.Unmarshal(*req.Params, &params); err != nil {
		return nil, err
	}
	return params, nil
}

func newCompletionItem(stepText, text, kind, fText string, startPos, endPos lsp.Position) completionItem {
	return completionItem{
		CompletionItem: lsp.CompletionItem{
			Label:      stepText,
			Detail:     kind,
			Kind:       lsp.CIKFunction,
			TextEdit:   lsp.TextEdit{Range: lsp.Range{Start: startPos, End: endPos}, NewText: text},
			FilterText: fText,
		},
		InsertTextFormat: snippet,
	}
}

func addPlaceHolders(text string, args []string) string {
	text = strings.Replace(text, "{}", "\"{}\"", -1)
	for i, v := range args {
		value := i + 1
		if value == len(args) {
			value = 0
		}
		text = strings.Replace(text, "{}", fmt.Sprintf("${%d:%s}", value, v), 1)
	}
	return text
}
<|endoftext|>


<commit_before>package appsettings

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
)

type dataTree map[string]string

type dataStruct struct {
	Tree map[string]dataTree
}

type appsettings struct {
	filename string
	data     dataStruct
}

func NewAppSettings(dbFilename string) (*appsettings, error) {
	var data dataStruct
	if _, err := os.Stat(dbFilename); os.IsNotExist(err) {
		data = dataStruct{
			Tree: make(map[string]dataTree),
		}

		d1, _ := json.Marshal(data)
		err := ioutil.WriteFile(dbFilename, d1, 0644)
		if err != nil {
			return nil, err
		}
	} else {
		d1, err := ioutil.ReadFile(dbFilename)
		if err != nil {
			return nil, err
		}
		json.Unmarshal(d1, &data)

		if err != nil {
			return nil, err
		}
	}

	return &appsettings{filename: dbFilename, data: data}, nil
}

func (a *dataTree) GetString(key string) (string, error) {
	y := *a
	if _, ok := y[key]; !ok {
		return "", fmt.Errorf("Undefined key %s", key)
	}

	return y[key], nil
}

func (a *dataTree) SetString(key string, val string) {
	y := *a
	y[key] = val
}

func (a *dataTree) GetInt(key string) (int, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.Atoi(str)
	if err != nil {
		return 0, err
	}
	return i, nil
}

func (a *dataTree) SetInt(key string, val int) {
	y := *a
	y[key] = strconv.Itoa(val)
}

func (a *appsettings) GetTree(key string) dataTree {
	if _, ok := a.data.Tree[key]; !ok {
		a.data.Tree[key] = make(dataTree)
	}

	return a.data.Tree[key]
}

func (a *appsettings) Persist() error {
	d1, _ := json.Marshal(a.data)
	err := ioutil.WriteFile(a.filename, d1, 0644)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>dataTree -> DataTree<commit_after>package appsettings

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
)

type DataTree map[string]string

type dataStruct struct {
	Tree map[string]DataTree
}

type appsettings struct {
	filename string
	data     dataStruct
}

func NewAppSettings(dbFilename string) (*appsettings, error) {
	var data dataStruct
	if _, err := os.Stat(dbFilename); os.IsNotExist(err) {
		data = dataStruct{
			Tree: make(map[string]DataTree),
		}

		d1, _ := json.Marshal(data)
		err := ioutil.WriteFile(dbFilename, d1, 0644)
		if err != nil {
			return nil, err
		}
	} else {
		d1, err := ioutil.ReadFile(dbFilename)
		if err != nil {
			return nil, err
		}
		json.Unmarshal(d1, &data)

		if err != nil {
			return nil, err
		}
	}

	return &appsettings{filename: dbFilename, data: data}, nil
}

func (a *DataTree) GetString(key string) (string, error) {
	y := *a
	if _, ok := y[key]; !ok {
		return "", fmt.Errorf("Undefined key %s", key)
	}

	return y[key], nil
}

func (a *DataTree) SetString(key string, val string) {
	y := *a
	y[key] = val
}

func (a *DataTree) GetInt(key string) (int, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.Atoi(str)
	if err != nil {
		return 0, err
	}
	return i, nil
}

func (a *DataTree) SetInt(key string, val int) {
	y := *a
	y[key] = strconv.Itoa(val)
}

func (a *appsettings) GetTree(key string) DataTree {
	if _, ok := a.data.Tree[key]; !ok {
		a.data.Tree[key] = make(DataTree)
	}

	return a.data.Tree[key]
}

func (a *appsettings) Persist() error {
	d1, _ := json.Marshal(a.data)
	err := ioutil.WriteFile(a.filename, d1, 0644)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>