<commit_before>package gobay

import (
    "bytes"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

type EbayCall struct {
    DevID              string
    AppID              string
    CertID             string
    CompatLevel        string
    SiteID             string
    EndPoint           string
    EbayAuthToken      string
    Country            string
    Currency           string
    Language           string
    MessageID          string
    WarningLevel       string
    PayPalEmailAddress string
    Callname           string
    XMLData            string
    Headers            map[string]string
    Items           []Item
    TheClient          *http.Client
    CategoryCallInfo   GetCategoriesStruct
}

func NewEbayCallEx(conf []byte) (*EbayCall, error) {
    var e EbayCall
    m := make(map[string]string)
    c := make(map[interface{}]interface{})
    err := LoadConfiguration(conf, &c)

    if err != nil {
        return nil, err
    }

    e.DevID = c["DevID"].(string)
    e.AppID = c["AppID"].(string)
    e.CertID = c["CertID"].(string)
    e.CompatLevel = c["CompatLevel"].(string)
    e.SiteID = c["SiteID"].(string)
    e.EndPoint = c["EndPoint"].(string)
    e.EbayAuthToken = c["EbayAuthToken"].(string)
    e.Country = c["Country"].(string)
    e.Currency = c["Currency"].(string)
    e.PayPalEmailAddress = c["PayPalEmailAddress"].(string)
    e.Language = c["Language"].(string)
    e.WarningLevel = c["WarningLevel"].(string)

    m["X-EBAY-API-COMPATIBILITY-LEVEL"] = fmt.Sprintf("%s", e.CompatLevel)
    m["X-EBAY-API-DEV-NAME"] = fmt.Sprintf("%s", e.DevID)
    m["X-EBAY-API-APP-NAME"] = fmt.Sprintf("%s", e.AppID)
    m["X-EBAY-API-CERT-NAME"] = fmt.Sprintf("%s", e.CertID)
    //m["X-EBAY-API-CALL-NAME"] = fmt.Sprintf("%s", e.CallName)
    m["X-EBAY-API-SITEID"] = fmt.Sprintf("%s", e.SiteID)
    e.Headers = m

    return &e, nil
}

func (o *EbayCall) NewItem() *Item {
    p := NewItem()
    p.Country = o.Country
    p.Site = SiteIDToCode(o.SiteID)
    p.Currency = o.Currency
    p.PayPalEmailAddress = o.PayPalEmailAddress
    return p
}

func (o *EbayCall) SetHeader(k string, v string) {
    o.Headers[k] = v
}
func (o *EbayCall) GetHeader(k string) string {
    return o.Headers[k]
}

func (o *EbayCall) Execute(r *[]Result) error {
    cl := o.GetCallname()
    if cl == "GeteBayOfficialTime" {
        err := o.GeteBayOfficialTime(r)
        if err != nil {
            return err
        }
        return o.Send(r)
    }

    if cl == "GetAllCategories" {
        o.SetCallname("GetCategories")
        err := o.GetAllCategories(r)
        if err != nil {
            return err
        }
        return o.Send(r)
    }

    return nil
}
func (o *EbayCall) Send(r *[]Result) error {
    o.TheClient = new(http.Client)

    globalDebugFunction(DBG_DEBUG, fmt.Sprintf("About to send [[%s]]\n\n", o.XMLData))

    if o.XMLData == "" {
        err := errors.New("XMLData was empty!")
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    req, err := http.NewRequest("POST", o.EndPoint, bytes.NewBufferString(o.XMLData))
    if err != nil {
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    //req.URL.Host = "148.251.124.116:9090"
    for k, v := range o.Headers {
        req.Header.Set(k, v)
    }

    resp, err := o.TheClient.Do(req)
    //Post(o.EndPoint, "text/xml; charset=utf-8", )
    if err != nil {
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    //fmt.Printf("%+v\n", resp)
    b, err := ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    // We should cache the results of certain calls

    globalDebugFunction(DBG_DEBUG, fmt.Sprintf("[[BODY: %s]]", string(b)))
    if err != nil {
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    if !fileExists(".cache") {
        err = os.Mkdir(".cache", 0777)
        if err != nil {
            globalDebugFunction(DBG_WARN, "Could not create .cache")
        } else {
            filePutContents(fmt.Sprintf(".cache/%s-%s.xml", o.GetCallname(), o.MessageID), string(b))
        }
    }
    res, err := NewResult(b)
    if err != nil {
        res = NewFakeResult(fmt.Sprintf("%s", err))
    }
    *r = append(*r, *res)
    return nil
}
func (o *EbayCall) GeteBayOfficialTime(r *[]Result) error {
    o.MessageID, _ = pseudoUUID()
    body, err := compileGoString("Time", GeteBayOfficialTimeTemplate(), o, nil)
    if err != nil {
        return err
    }
    final_xml, err := compileGoString("FinalTime", WrapCall("GeteBayOfficialTime", "", body, ""), o, nil)
    if err != nil {
        return err
    }
    o.XMLData = final_xml
    return nil
}

func (o *EbayCall) GetAllCategories(r *[]Result) error {
    o.MessageID, _ = pseudoUUID()
    o.CategoryCallInfo.SiteID = o.SiteID
    if o.CategoryCallInfo.LevelLimit == "" {
        o.CategoryCallInfo.LevelLimit = "3"
    }
    if o.CategoryCallInfo.ViewAllNodes != "true" {
        o.CategoryCallInfo.ViewAllNodes = "false"
    }

    body, err := compileGoString("Time", GetAllCategoriesTemplate(), o.CategoryCallInfo, nil)
    if err != nil {
        return err
    }
    final_xml, err := compileGoString("FinalGetAllCategories", WrapCall("GetCategories", "", body, ""), o, nil)
    if err != nil {
        return err
    }
    o.XMLData = final_xml
    return nil
}

// Getters and Setters

type ItemFilter func (o Item) bool
func NewEbayCall () *EbayCall {
    return &EbayCall{}
}

func (o *EbayCall) Clone () *EbayCall {
    var no EbayCall
    no.DevID = o.DevID
    no.AppID = o.AppID
    no.CertID = o.CertID
    no.CompatLevel = o.CompatLevel
    no.SiteID = o.SiteID
    no.EndPoint = o.EndPoint
    no.EbayAuthToken = o.EbayAuthToken
    no.Country = o.Country
    no.Currency = o.Currency
    no.Language = o.Language
    no.MessageID = o.MessageID
    no.WarningLevel = o.WarningLevel
    no.PayPalEmailAddress = o.PayPalEmailAddress
    no.Callname = o.Callname
    no.XMLData = o.XMLData
    no.Headers = o.Headers
    no.Items = o.Items
    no.CategoryCallInfo = o.CategoryCallInfo
    return &no
}

func (o *EbayCall) SetDevID (v string) {
    o.DevID = v
}

func (o *EbayCall) GetDevID () string {
    return o.DevID
}

func (o *EbayCall) SetAppID (v string) {
    o.AppID = v
}

func (o *EbayCall) GetAppID () string {
    return o.AppID
}

func (o *EbayCall) SetCertID (v string) {
    o.CertID = v
}

func (o *EbayCall) GetCertID () string {
    return o.CertID
}

func (o *EbayCall) SetCompatLevel (v string) {
    o.CompatLevel = v
}

func (o *EbayCall) GetCompatLevel () string {
    return o.CompatLevel
}

func (o *EbayCall) SetSiteID (v string) {
    o.SiteID = v
}

func (o *EbayCall) GetSiteID () string {
    return o.SiteID
}

func (o *EbayCall) SetEndPoint (v string) {
    o.EndPoint = v
}

func (o *EbayCall) GetEndPoint () string {
    return o.EndPoint
}

func (o *EbayCall) SetEbayAuthToken (v string) {
    o.EbayAuthToken = v
}

func (o *EbayCall) GetEbayAuthToken () string {
    return o.EbayAuthToken
}

func (o *EbayCall) SetCountry (v string) {
    o.Country = v
}

func (o *EbayCall) GetCountry () string {
    return o.Country
}

func (o *EbayCall) SetCurrency (v string) {
    o.Currency = v
}

func (o *EbayCall) GetCurrency () string {
    return o.Currency
}

func (o *EbayCall) SetLanguage (v string) {
    o.Language = v
}

func (o *EbayCall) GetLanguage () string {
    return o.Language
}

func (o *EbayCall) SetMessageID (v string) {
    o.MessageID = v
}

func (o *EbayCall) GetMessageID () string {
    return o.MessageID
}

func (o *EbayCall) SetWarningLevel (v string) {
    o.WarningLevel = v
}

func (o *EbayCall) GetWarningLevel () string {
    return o.WarningLevel
}

func (o *EbayCall) SetPayPalEmailAddress (v string) {
    o.PayPalEmailAddress = v
}

func (o *EbayCall) GetPayPalEmailAddress () string {
    return o.PayPalEmailAddress
}

func (o *EbayCall) SetXMLData (v string) {
    o.XMLData = v
}
func (o *EbayCall) SetCallname(v string) {
    o.Callname = v
    o.Headers["X-EBAY-API-CALL-NAME"] = v
}
func (o *EbayCall) GetCallname() string {
    return o.Callname
}
func (o *EbayCall) GetXMLData () string {
    return o.XMLData
}

func (o *EbayCall) SetHeaders (v map[string]string) {
    o.Headers = v
}

func (o *EbayCall) GetHeaders () map[string]string {
    return o.Headers
}

func (o *EbayCall) FilterItems(f ItemFilter) []Item {
    tmp := o.Items[:0]
    for _, x := range o.Items {
        if f(x) {
            tmp = append(tmp, x)
        }
    }
    return tmp
}

func (o *EbayCall) AddItem(v Item) {
    o.Items = append(o.Items,v)
}

func (o *EbayCall) RemoveItem(i int) {
    if i > len(o.Items) {
        panic(fmt.Sprintf("i:%d is out of bounds for %s.%s(%d)!\n","EbayCall","Items",len(o.Items)))
    }
    o.Items = o.Items[:i+copy(o.Items[i:], o.Items[i+1:])]
}

func (o *EbayCall) GetItem(i int) Item {
    if i > len(o.Items) {
        panic(fmt.Sprintf("i:%d is out of bounds for %s.%s(%d)!\n","EbayCall","Items",len(o.Items)))
    }
    return o.Items[i]
}

func (o *EbayCall) SetItems (v []Item) {
    o.Items = v
}

func (o *EbayCall) GetItems () []Item {
    return o.Items
}

func (o *EbayCall) SetCategoryCallInfo (v GetCategoriesStruct) {
    o.CategoryCallInfo = v
}

func (o *EbayCall) GetCategoryCallInfo () GetCategoriesStruct {
    return o.CategoryCallInfo
}

// Debug Functions

func (o_EbayCall *EbayCall) Debug() string {
    var txt string
    txt = fmt.Sprintf("%sEbayCall.DevID: %s\n",txt, o_EbayCall.DevID)
    txt = fmt.Sprintf("%sEbayCall.AppID: %s\n",txt, o_EbayCall.AppID)
    txt = fmt.Sprintf("%sEbayCall.CertID: %s\n",txt, o_EbayCall.CertID)
    txt = fmt.Sprintf("%sEbayCall.CompatLevel: %s\n",txt, o_EbayCall.CompatLevel)
    txt = fmt.Sprintf("%sEbayCall.SiteID: %s\n",txt, o_EbayCall.SiteID)
    txt = fmt.Sprintf("%sEbayCall.EndPoint: %s\n",txt, o_EbayCall.EndPoint)
    txt = fmt.Sprintf("%sEbayCall.EbayAuthToken: %s\n",txt, o_EbayCall.EbayAuthToken)
    txt = fmt.Sprintf("%sEbayCall.Country: %s\n",txt, o_EbayCall.Country)
    txt = fmt.Sprintf("%sEbayCall.Currency: %s\n",txt, o_EbayCall.Currency)
    txt = fmt.Sprintf("%sEbayCall.Language: %s\n",txt, o_EbayCall.Language)
    txt = fmt.Sprintf("%sEbayCall.MessageID: %s\n",txt, o_EbayCall.MessageID)
    txt = fmt.Sprintf("%sEbayCall.WarningLevel: %s\n",txt, o_EbayCall.WarningLevel)
    txt = fmt.Sprintf("%sEbayCall.PayPalEmailAddress: %s\n",txt, o_EbayCall.PayPalEmailAddress)
    txt = fmt.Sprintf("%sEbayCall.Callname: %s\n",txt, o_EbayCall.Callname)
    txt = fmt.Sprintf("%sEbayCall.XMLData: %s\n",txt, o_EbayCall.XMLData)
    for k,v := range o_EbayCall.Headers {
        txt = fmt.Sprintf("%sEbayCall.Headers [%s]: %s\n",txt,k,v)
    }
    for i,v := range o_EbayCall.Items {
        txt = fmt.Sprintf("%sEbayCall.Items [%d]: %s\n",txt,i,v.Debug())
    }
    globalDebugFunction(DBG_DEBUG, txt)
    return txt
}
<commit_msg>Adding GetAllCategories specific caching<commit_after>package gobay

import (
    "bytes"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

type EbayCall struct {
    DevID              string
    AppID              string
    CertID             string
    CompatLevel        string
    SiteID             string
    EndPoint           string
    EbayAuthToken      string
    Country            string
    Currency           string
    Language           string
    MessageID          string
    WarningLevel       string
    PayPalEmailAddress string
    Callname           string
    XMLData            string
    Headers            map[string]string
    Items           []Item
    TheClient          *http.Client
    CategoryCallInfo   GetCategoriesStruct
}

func NewEbayCallEx(conf []byte) (*EbayCall, error) {
    var e EbayCall
    m := make(map[string]string)
    c := make(map[interface{}]interface{})
    err := LoadConfiguration(conf, &c)

    if err != nil {
        return nil, err
    }

    e.DevID = c["DevID"].(string)
    e.AppID = c["AppID"].(string)
    e.CertID = c["CertID"].(string)
    e.CompatLevel = c["CompatLevel"].(string)
    e.SiteID = c["SiteID"].(string)
    e.EndPoint = c["EndPoint"].(string)
    e.EbayAuthToken = c["EbayAuthToken"].(string)
    e.Country = c["Country"].(string)
    e.Currency = c["Currency"].(string)
    e.PayPalEmailAddress = c["PayPalEmailAddress"].(string)
    e.Language = c["Language"].(string)
    e.WarningLevel = c["WarningLevel"].(string)

    m["X-EBAY-API-COMPATIBILITY-LEVEL"] = fmt.Sprintf("%s", e.CompatLevel)
    m["X-EBAY-API-DEV-NAME"] = fmt.Sprintf("%s", e.DevID)
    m["X-EBAY-API-APP-NAME"] = fmt.Sprintf("%s", e.AppID)
    m["X-EBAY-API-CERT-NAME"] = fmt.Sprintf("%s", e.CertID)
    //m["X-EBAY-API-CALL-NAME"] = fmt.Sprintf("%s", e.Callname)
    m["X-EBAY-API-SITEID"] = fmt.Sprintf("%s", e.SiteID)
    e.Headers = m

    return &e, nil
}

func (o *EbayCall) NewItem() *Item {
    p := NewItem()
    p.Country = o.Country
    p.Site = SiteIDToCode(o.SiteID)
    p.Currency = o.Currency
    p.PayPalEmailAddress = o.PayPalEmailAddress
    return p
}

func (o *EbayCall) SetHeader(k string, v string) {
    o.Headers[k] = v
}
func (o *EbayCall) GetHeader(k string) string {
    return o.Headers[k]
}

func (o *EbayCall) Execute(r *[]Result) error {
    cl := o.GetCallname()
    if cl == "GeteBayOfficialTime" {
        err := o.GeteBayOfficialTime(r)
        if err != nil {
            return err
        }
        return o.Send(r)
    }

    if cl == "GetAllCategories" {
        o.SetCallname("GetCategories")
        o.Callname = "GetAllCategories"
        err := o.GetAllCategories(r)
        if err != nil {
            return err
        }
        return o.Send(r)
    }

    return nil
}
func (o *EbayCall) Send(r *[]Result) error {
    o.TheClient = new(http.Client)

    globalDebugFunction(DBG_DEBUG, fmt.Sprintf("About to send [[%s]]\n\n", o.XMLData))

    if o.XMLData == "" {
        err := errors.New("XMLData was empty!")
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    req, err := http.NewRequest("POST", o.EndPoint, bytes.NewBufferString(o.XMLData))
    if err != nil {
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    //req.URL.Host = "148.251.124.116:9090"
    for k, v := range o.Headers {
        req.Header.Set(k, v)
    }

    resp, err := o.TheClient.Do(req)
    //Post(o.EndPoint, "text/xml; charset=utf-8", )
    if err != nil {
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    //fmt.Printf("%+v\n", resp)
    b, err := ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    // We should cache the results of certain calls

    globalDebugFunction(DBG_DEBUG, fmt.Sprintf("[[BODY: %s]]", string(b)))
    if err != nil {
        e := NewFakeResult(fmt.Sprintf("%s", err))
        *r = append(*r, *e)
        return err
    }
    if !fileExists(".cache") {
        err = os.Mkdir(".cache", 0777)
        if err != nil {
            globalDebugFunction(DBG_WARN, "Could not create .cache")
        }
    }
    if fileExists(".cache") {
        if o.Callname == "GetAllCategories" {
            // GetAllCategories is a semi-special case, because we really want
            // to cache this result, and rarely if ever update it
            filePutContents(fmt.Sprintf(".cache/%s.xml", o.Callname), string(b))
        } else {
            filePutContents(fmt.Sprintf(".cache/%s-%s.xml", o.GetCallname(), o.MessageID), string(b))
        }
    }
    res, err := NewResult(b)
    if err != nil {
        res = NewFakeResult(fmt.Sprintf("%s", err))
    }
    *r = append(*r, *res)
    return nil
}
func (o *EbayCall) GeteBayOfficialTime(r *[]Result) error {
    o.MessageID, _ = pseudoUUID()
    body, err := compileGoString("Time", GeteBayOfficialTimeTemplate(), o, nil)
    if err != nil {
        return err
    }
    final_xml, err := compileGoString("FinalTime", WrapCall("GeteBayOfficialTime", "", body, ""), o, nil)
    if err != nil {
        return err
    }
    o.XMLData = final_xml
    return nil
}

func (o *EbayCall) GetAllCategories(r *[]Result) error {
    o.MessageID, _ = pseudoUUID()
    o.CategoryCallInfo.SiteID = o.SiteID
    if o.CategoryCallInfo.LevelLimit == "" {
        o.CategoryCallInfo.LevelLimit = "3"
    }
    if o.CategoryCallInfo.ViewAllNodes != "true" {
        o.CategoryCallInfo.ViewAllNodes = "false"
    }

    body, err := compileGoString("Time", GetAllCategoriesTemplate(), o.CategoryCallInfo, nil)
    if err != nil {
        return err
    }
    final_xml, err := compileGoString("FinalGetAllCategories", WrapCall("GetCategories", "", body, ""), o, nil)
    if err != nil {
        return err
    }
    o.XMLData = final_xml
    return nil
}

// Getters and Setters

type ItemFilter func (o Item) bool
func NewEbayCall () *EbayCall {
    return &EbayCall{}
}

func (o *EbayCall) Clone () *EbayCall {
    var no EbayCall
    no.DevID = o.DevID
    no.AppID = o.AppID
    no.CertID = o.CertID
    no.CompatLevel = o.CompatLevel
    no.SiteID = o.SiteID
    no.EndPoint = o.EndPoint
    no.EbayAuthToken = o.EbayAuthToken
    no.Country = o.Country
    no.Currency = o.Currency
    no.Language = o.Language
    no.MessageID = o.MessageID
    no.WarningLevel = o.WarningLevel
    no.PayPalEmailAddress = o.PayPalEmailAddress
    no.Callname = o.Callname
    no.XMLData = o.XMLData
    no.Headers = o.Headers
    no.Items = o.Items
    no.CategoryCallInfo = o.CategoryCallInfo
    return &no
}

func (o *EbayCall) SetDevID (v string) {
    o.DevID = v
}

func (o *EbayCall) GetDevID () string {
    return o.DevID
}

func (o *EbayCall) SetAppID (v string) {
    o.AppID = v
}

func (o *EbayCall) GetAppID () string {
    return o.AppID
}

func (o *EbayCall) SetCertID (v string) {
    o.CertID = v
}

func (o *EbayCall) GetCertID () string {
    return o.CertID
}

func (o *EbayCall) SetCompatLevel (v string) {
    o.CompatLevel = v
}

func (o *EbayCall) GetCompatLevel () string {
    return o.CompatLevel
}

func (o *EbayCall) SetSiteID (v string) {
    o.SiteID = v
}

func (o *EbayCall) GetSiteID () string {
    return o.SiteID
}

func (o *EbayCall) SetEndPoint (v string) {
    o.EndPoint = v
}

func (o *EbayCall) GetEndPoint () string {
    return o.EndPoint
}

func (o *EbayCall) SetEbayAuthToken (v string) {
    o.EbayAuthToken = v
}

func (o *EbayCall) GetEbayAuthToken () string {
    return o.EbayAuthToken
}

func (o *EbayCall) SetCountry (v string) {
    o.Country = v
}

func (o *EbayCall) GetCountry () string {
    return o.Country
}

func (o *EbayCall) SetCurrency (v string) {
    o.Currency = v
}

func (o *EbayCall) GetCurrency () string {
    return o.Currency
}

func (o *EbayCall) SetLanguage (v string) {
    o.Language = v
}

func (o *EbayCall) GetLanguage () string {
    return o.Language
}

func (o *EbayCall) SetMessageID (v string) {
    o.MessageID = v
}

func (o *EbayCall) GetMessageID () string {
    return o.MessageID
}

func (o *EbayCall) SetWarningLevel (v string) {
    o.WarningLevel = v
}

func (o *EbayCall) GetWarningLevel () string {
    return o.WarningLevel
}

func (o *EbayCall) SetPayPalEmailAddress (v string) {
    o.PayPalEmailAddress = v
}

func (o *EbayCall) GetPayPalEmailAddress () string {
    return o.PayPalEmailAddress
}

func (o *EbayCall) SetXMLData (v string) {
    o.XMLData = v
}
func (o *EbayCall) SetCallname(v string) {
    o.Callname = v
    o.Headers["X-EBAY-API-CALL-NAME"] = v
}
func (o *EbayCall) GetCallname() string {
    return o.Callname
}
func (o *EbayCall) GetXMLData () string {
    return o.XMLData
}

func (o *EbayCall) SetHeaders (v map[string]string) {
    o.Headers = v
}

func (o *EbayCall) GetHeaders () map[string]string {
    return o.Headers
}

func (o *EbayCall) FilterItems(f ItemFilter) []Item {
    tmp := o.Items[:0]
    for _, x := range o.Items {
        if f(x) {
            tmp = append(tmp, x)
        }
    }
    return tmp
}

func (o *EbayCall) AddItem(v Item) {
    o.Items = append(o.Items,v)
}

func (o *EbayCall) RemoveItem(i int) {
    if i > len(o.Items) {
        panic(fmt.Sprintf("i:%d is out of bounds for %s.%s(%d)!\n","EbayCall","Items",len(o.Items)))
    }
    o.Items = o.Items[:i+copy(o.Items[i:], o.Items[i+1:])]
}

func (o *EbayCall) GetItem(i int) Item {
    if i > len(o.Items) {
        panic(fmt.Sprintf("i:%d is out of bounds for %s.%s(%d)!\n","EbayCall","Items",len(o.Items)))
    }
    return o.Items[i]
}

func (o *EbayCall) SetItems (v []Item) {
    o.Items = v
}

func (o *EbayCall) GetItems () []Item {
    return o.Items
}

func (o *EbayCall) SetCategoryCallInfo (v GetCategoriesStruct) {
    o.CategoryCallInfo = v
}

func (o *EbayCall) GetCategoryCallInfo () GetCategoriesStruct {
    return o.CategoryCallInfo
}

// Debug Functions

func (o_EbayCall *EbayCall) Debug() string {
    var txt string
    txt = fmt.Sprintf("%sEbayCall.DevID: %s\n",txt, o_EbayCall.DevID)
    txt = fmt.Sprintf("%sEbayCall.AppID: %s\n",txt, o_EbayCall.AppID)
    txt = fmt.Sprintf("%sEbayCall.CertID: %s\n",txt, o_EbayCall.CertID)
    txt = fmt.Sprintf("%sEbayCall.CompatLevel: %s\n",txt, o_EbayCall.CompatLevel)
    txt = fmt.Sprintf("%sEbayCall.SiteID: %s\n",txt, o_EbayCall.SiteID)
    txt = fmt.Sprintf("%sEbayCall.EndPoint: %s\n",txt, o_EbayCall.EndPoint)
    txt = fmt.Sprintf("%sEbayCall.EbayAuthToken: %s\n",txt, o_EbayCall.EbayAuthToken)
    txt = fmt.Sprintf("%sEbayCall.Country: %s\n",txt, o_EbayCall.Country)
    txt = fmt.Sprintf("%sEbayCall.Currency: %s\n",txt, o_EbayCall.Currency)
    txt = fmt.Sprintf("%sEbayCall.Language: %s\n",txt, o_EbayCall.Language)
    txt = fmt.Sprintf("%sEbayCall.MessageID: %s\n",txt, o_EbayCall.MessageID)
    txt = fmt.Sprintf("%sEbayCall.WarningLevel: %s\n",txt, o_EbayCall.WarningLevel)
    txt = fmt.Sprintf("%sEbayCall.PayPalEmailAddress: %s\n",txt, o_EbayCall.PayPalEmailAddress)
    txt = fmt.Sprintf("%sEbayCall.Callname: %s\n",txt, o_EbayCall.Callname)
    txt = fmt.Sprintf("%sEbayCall.XMLData: %s\n",txt, o_EbayCall.XMLData)
    for k,v := range o_EbayCall.Headers {
        txt = fmt.Sprintf("%sEbayCall.Headers [%s]: %s\n",txt,k,v)
    }
    for i,v := range o_EbayCall.Items {
        txt = fmt.Sprintf("%sEbayCall.Items [%d]: %s\n",txt,i,v.Debug())
    }
    globalDebugFunction(DBG_DEBUG, txt)
    return txt
}
<|endoftext|>


<commit_before>package schema

import (
	"regexp"
	"testing"
)

// To be in par with the python library.
func TestDecodeString_URIMustRequireScheme(t *testing.T) {
	if _, err := decodeString(stringURI, "google.com", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_InvalidUUIDVersion(t *testing.T) {
	// This is a uuid3: namespace DNS and python.org.
	if _, err := decodeString(stringUUID, "6fa459ea-ee8a-3ca4-894e-db77e160355e", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_ErrorCheckingConstraints(t *testing.T) {
	data := []struct {
		desc        string
		value       string
		format      string
		constraints Constraints
	}{
		{"InvalidMinLength_UUID", "6fa459ea-ee8a-3ca4-894e-db77e160355e", stringUUID, Constraints{MinLength: 100}},
		{"InvalidMinLength_Email", "foo@bar.com", stringEmail, Constraints{MinLength: 100}},
		{"InvalidMinLength_URI", "http://google.com", stringURI, Constraints{MinLength: 100}},
		{"InvalidMaxLength_UUID", "6fa459ea-ee8a-3ca4-894e-db77e160355e", stringUUID, Constraints{MaxLength: 1}},
		{"InvalidMaxLength_Email", "foo@bar.com", stringEmail, Constraints{MaxLength: 1}},
		{"InvalidMaxLength_URI", "http://google.com", stringURI, Constraints{MaxLength: 1}},
		{"InvalidPattern_UUID", "6fa459ea-ee8a-3ca4-894e-db77e160355e", stringUUID, Constraints{compiledPattern: regexp.MustCompile("^[0-9a-f]{1}-.*")}},
		{"InvalidPattern_Email", "foo@bar.com", stringEmail, Constraints{compiledPattern: regexp.MustCompile("[0-9].*")}},
		{"InvalidPattern_URI", "http://google.com", stringURI, Constraints{compiledPattern: regexp.MustCompile("^//.*")}},
	}
	for _, d := range data {
		t.Run(d.desc, func(t *testing.T) {
			if _, err := decodeString(d.format, d.value, d.constraints); err == nil {
				t.Fatalf("err want:err got:nil")
			}
		})
	}
}

func TestDecodeString_Success(t *testing.T) {
	var data = []struct {
		desc        string
		value       string
		format      string
		constraints Constraints
	}{
		{"URI", "http://google.com", stringURI, Constraints{MinLength: 1, compiledPattern: regexp.MustCompile("^http://.*")}},
		{"Email", "foo@bar.com", stringEmail, Constraints{MinLength: 1, compiledPattern: regexp.MustCompile(".*@.*")}},
		{"UUID", "C56A4180-65AA-42EC-A945-5FD21DEC0538", stringUUID, Constraints{MinLength: 36, MaxLength: 36, compiledPattern: regexp.MustCompile("[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}")}},
	}
	for _, d := range data {
		t.Run(d.desc, func(t *testing.T) {
			v, err := decodeString(d.format, d.value, d.constraints)
			if err != nil {
				t.Errorf("want:nil got:%q", err)
			}
			if v != d.value {
				t.Errorf("want:%s got:%s", d.value, v)
			}
		})
	}
}
<commit_msg>Also pass pattern to constraints in testing<commit_after>package schema

import (
	"regexp"
	"testing"
)

// To be in par with the python library.
func TestDecodeString_URIMustRequireScheme(t *testing.T) {
	if _, err := decodeString(stringURI, "google.com", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_InvalidUUIDVersion(t *testing.T) {
	// This is a uuid3: namespace DNS and python.org.
	if _, err := decodeString(stringUUID, "6fa459ea-ee8a-3ca4-894e-db77e160355e", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_ErrorCheckingConstraints(t *testing.T) {
	data := []struct {
		desc        string
		value       string
		format      string
		constraints Constraints
	}{
		{"InvalidMinLength_UUID", "6fa459ea-ee8a-3ca4-894e-db77e160355e", stringUUID, Constraints{MinLength: 100}},
		{"InvalidMinLength_Email", "foo@bar.com", stringEmail, Constraints{MinLength: 100}},
		{"InvalidMinLength_URI", "http://google.com", stringURI, Constraints{MinLength: 100}},
		{"InvalidMaxLength_UUID", "6fa459ea-ee8a-3ca4-894e-db77e160355e", stringUUID, Constraints{MaxLength: 1}},
		{"InvalidMaxLength_Email", "foo@bar.com", stringEmail, Constraints{MaxLength: 1}},
		{"InvalidMaxLength_URI", "http://google.com", stringURI, Constraints{MaxLength: 1}},
		{"InvalidPattern_UUID", "6fa459ea-ee8a-3ca4-894e-db77e160355e", stringUUID, Constraints{compiledPattern: regexp.MustCompile("^[0-9a-f]{1}-.*"), Pattern: "^[0-9a-f]{1}-.*"}},
		{"InvalidPattern_Email", "foo@bar.com", stringEmail, Constraints{compiledPattern: regexp.MustCompile("[0-9].*"), Pattern: "[0-9].*"}},
		{"InvalidPattern_URI", "http://google.com", stringURI, Constraints{compiledPattern: regexp.MustCompile("^//.*"), Pattern: "^//.*"}},
	}
	for _, d := range data {
		t.Run(d.desc, func(t *testing.T) {
			if _, err := decodeString(d.format, d.value, d.constraints); err == nil {
				t.Fatalf("err want:err got:nil")
			}
		})
	}
}

func TestDecodeString_Success(t *testing.T) {
	var data = []struct {
		desc        string
		value       string
		format      string
		constraints Constraints
	}{
		{"URI", "http://google.com", stringURI, Constraints{MinLength: 1, compiledPattern: regexp.MustCompile("^http://.*"), Pattern: "^http://.*"}},
		{"Email", "foo@bar.com", stringEmail, Constraints{MinLength: 1, compiledPattern: regexp.MustCompile(".*@.*"), Pattern: ".*@.*"}},
		{"UUID", "C56A4180-65AA-42EC-A945-5FD21DEC0538", stringUUID, Constraints{MinLength: 36, MaxLength: 36, compiledPattern: regexp.MustCompile("[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}"), Pattern: "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}"}},
	}
	for _, d := range data {
		t.Run(d.desc, func(t *testing.T) {
			v, err := decodeString(d.format, d.value, d.constraints)
			if err != nil {
				t.Errorf("want:nil got:%q", err)
			}
			if v != d.value {
				t.Errorf("want:%s got:%s", d.value, v)
			}
		})
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"

	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/core"
	"github.com/jbenet/go-ipfs/merkledag"
	u "github.com/jbenet/go-ipfs/util"
)

var pinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Pin (and unpin) objects to local storage",
	},

	Subcommands: map[string]*cmds.Command{
		"add": addPinCmd,
		"rm":  rmPinCmd,
		"ls":  listPinCmd,
	},
}

var addPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Pins objects to local storage",
		ShortDescription: `
Retrieves the object named by <ipfs-path> and stores it locally
on disk.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned"),
	},
	Options: []cmds.Option{
		cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		// set recursive flag
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false
		}

		_, err = pin(n, req.Arguments(), recursive)
		if err != nil {
			return nil, err
		}

		// TODO: create some output to show what got pinned
		return nil, nil
	},
}

var rmPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Unpin an object from local storage",
		ShortDescription: `
Removes the pin from the given object allowing it to be garbage
collected if needed.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned"),
	},
	Options: []cmds.Option{
		cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		// set recursive flag
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false // default
		}

		_, err = unpin(n, req.Arguments(), recursive)
		if err != nil {
			return nil, err
		}

		// TODO: create some output to show what got unpinned
		return nil, nil
	},
}

var listPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List objects pinned to local storage",
		ShortDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.
`,
	},

	Options: []cmds.Option{
		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\""),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		typeStr, found, err := req.Option("type").String()
		if err != nil {
			return nil, err
		}
		if !found {
			typeStr = "all"
		}

		if typeStr != "all" && typeStr != "direct" && typeStr != "indirect" && typeStr != "recursive" {
			return nil, cmds.ClientError("Invalid type '" + typeStr + "', must be \"direct\", \"indirect\", \"recursive\", or \"all\"")
		}

		keys := make([]u.Key, 0)
		if typeStr == "direct" || typeStr == "all" {
			keys = append(keys, n.Pinning.DirectKeys()...)
		}
		if typeStr == "indirect" || typeStr == "all" {
			keys = append(keys, n.Pinning.IndirectKeys()...)
		}
		if typeStr == "recursive" || typeStr == "all" {
			keys = append(keys, n.Pinning.RecursiveKeys()...)
		}

		return &KeyList{Keys: keys}, nil
	},
	Type: &KeyList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: KeyListTextMarshaler,
	},
}

func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {

	dagnodes := make([]*merkledag.Node, 0)
	for _, path := range paths {
		dagnode, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, fmt.Errorf("pin error: %v", err)
		}
		dagnodes = append(dagnodes, dagnode)
	}

	for _, dagnode := range dagnodes {
		err := n.Pinning.Pin(dagnode, recursive)
		if err != nil {
			return nil, fmt.Errorf("pin: %v", err)
		}
	}

	err := n.Pinning.Flush()
	if err != nil {
		return nil, err
	}

	return dagnodes, nil
}

func unpin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {

	dagnodes := make([]*merkledag.Node, 0)
	for _, path := range paths {
		dagnode, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, err
		}
		dagnodes = append(dagnodes, dagnode)
	}

	for _, dagnode := range dagnodes {
		k, _ := dagnode.Key()
		err := n.Pinning.Unpin(k, recursive)
		if err != nil {
			return nil, err
		}
	}

	err := n.Pinning.Flush()
	if err != nil {
		return nil, err
	}
	return dagnodes, nil
}
<commit_msg>core/commands: pin ls: Default type to 'direct'<commit_after>package commands

import (
	"fmt"

	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/core"
	"github.com/jbenet/go-ipfs/merkledag"
	u "github.com/jbenet/go-ipfs/util"
)

var pinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Pin (and unpin) objects to local storage",
	},

	Subcommands: map[string]*cmds.Command{
		"add": addPinCmd,
		"rm":  rmPinCmd,
		"ls":  listPinCmd,
	},
}

var addPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Pins objects to local storage",
		ShortDescription: `
Retrieves the object named by <ipfs-path> and stores it locally
on disk.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned"),
	},
	Options: []cmds.Option{
		cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		// set recursive flag
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false
		}

		_, err = pin(n, req.Arguments(), recursive)
		if err != nil {
			return nil, err
		}

		// TODO: create some output to show what got pinned
		return nil, nil
	},
}

var rmPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Unpin an object from local storage",
		ShortDescription: `
Removes the pin from the given object allowing it to be garbage
collected if needed.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned"),
	},
	Options: []cmds.Option{
		cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		// set recursive flag
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false // default
		}

		_, err = unpin(n, req.Arguments(), recursive)
		if err != nil {
			return nil, err
		}

		// TODO: create some output to show what got unpinned
		return nil, nil
	},
}

var listPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List objects pinned to local storage",
		ShortDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.
`,
		LongDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.

Use --type=<type> to specify the type of pinned keys to list. Valid values are:
    * "direct"
    * "indirect"
    * "recursive"
    * "all"
(Defaults to "direct")
`,
	},

	Options: []cmds.Option{
		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\". Defaults to \"direct\""),
	},
	Run: func(req cmds.Request) (interface{}, error) {
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}

		typeStr, found, err := req.Option("type").String()
		if err != nil {
			return nil, err
		}
		if !found {
			typeStr = "direct"
		}

		if typeStr != "all" && typeStr != "direct" && typeStr != "indirect" && typeStr != "recursive" {
			return nil, cmds.ClientError("Invalid type '" + typeStr + "', must be \"direct\", \"indirect\", \"recursive\", or \"all\"")
		}

		keys := make([]u.Key, 0)
		if typeStr == "direct" || typeStr == "all" {
			keys = append(keys, n.Pinning.DirectKeys()...)
		}
		if typeStr == "indirect" || typeStr == "all" {
			keys = append(keys, n.Pinning.IndirectKeys()...)
		}
		if typeStr == "recursive" || typeStr == "all" {
			keys = append(keys, n.Pinning.RecursiveKeys()...)
		}

		return &KeyList{Keys: keys}, nil
	},
	Type: &KeyList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: KeyListTextMarshaler,
	},
}

func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {

	dagnodes := make([]*merkledag.Node, 0)
	for _, path := range paths {
		dagnode, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, fmt.Errorf("pin error: %v", err)
		}
		dagnodes = append(dagnodes, dagnode)
	}

	for _, dagnode := range dagnodes {
		err := n.Pinning.Pin(dagnode, recursive)
		if err != nil {
			return nil, fmt.Errorf("pin: %v", err)
		}
	}

	err := n.Pinning.Flush()
	if err != nil {
		return nil, err
	}

	return dagnodes, nil
}

func unpin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {

	dagnodes := make([]*merkledag.Node, 0)
	for _, path := range paths {
		dagnode, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, err
		}
		dagnodes = append(dagnodes, dagnode)
	}

	for _, dagnode := range dagnodes {
		k, _ := dagnode.Key()
		err := n.Pinning.Unpin(k, recursive)
		if err != nil {
			return nil, err
		}
	}

	err := n.Pinning.Flush()
	if err != nil {
		return nil, err
	}
	return dagnodes, nil
}
<|endoftext|>


<commit_before>// Package list implements persistent list.
package list

// List implements a persistent list. The empty value is a valid empty list.
type List struct {
	first interface{}
	rest  *List
	count int
}

// Empty is an empty List.
var Empty = &List{}

// Cons returns a new List with an additional value in the front.
func (l *List) Cons(val interface{}) *List {
	return &List{val, l, l.count + 1}
}

// First returns the first value in the list.
func (l *List) First() interface{} {
	return l.first
}

// Rest returns the list after the first value.
func (l *List) Rest() *List {
	return l.rest
}

// Count returns the number of values in the list.
func (l *List) Count() int {
	return l.count
}
<commit_msg>Make list.List an interface.<commit_after>// Package list implements persistent list.
package list

import "github.com/xiaq/persistent/types"

// List is a persistent list.
type List interface {
	types.Equaler
	// Len returns the number of values in the list.
	Len() int
	// Cons returns a new list with an additional value in the front.
	Cons(interface{}) List
	// First returns the first value in the list.
	First() interface{}
	// Rest returns the list after the first value.
	Rest() List
}

// Empty is an empty list.
var Empty List = &list{}

type list struct {
	first interface{}
	rest  *list
	count int
}

func (l *list) Len() int {
	return l.count
}

func (l *list) Cons(val interface{}) List {
	return &list{val, l, l.count + 1}
}

func (l *list) First() interface{} {
	return l.first
}

func (l *list) Rest() List {
	return l.rest
}

func (l *list) Equal(other interface{}) bool {
	l2, ok := other.(List)
	if !ok {
		return false
	}
	return Equal(l, l2)
}

// Equal returns whether two List values are equal to each other. The values are
// compared using the types.Equaler interface if the value in l1 implements
// types.Equaler, or with == otherwise.
func Equal(l1, l2 List) bool {
	if l1.Len() != l2.Len() {
		return false
	}
	for i := 0; i < l1.Len(); i++ {
		v1 := l1.First()
		v2 := l2.First()
		if v1eq, ok := v1.(types.Equaler); ok {
			if !v1eq.Equal(v2) {
				return false
			}
		} else {
			if v1 != v2 {
				return false
			}
		}
		l1 = l1.Rest()
		l2 = l2.Rest()
	}
	return true
}
<|endoftext|>


<commit_before>package keyshareserver

import (
	"database/sql"
	"errors"
	"sync"
	"time"

	"github.com/privacybydesign/irmago/internal/keysharecore"

	_ "github.com/jackc/pgx/stdlib"
)

var (
	ErrUserAlreadyExists = errors.New("Cannot create user, username already taken")
	ErrUserNotFound      = errors.New("Could not find specified user")
	ErrInvalidData       = errors.New("Invalid user datastructure passed")
	ErrInvalidRecord     = errors.New("Invalid record in database")
)

type KeyshareDB interface {
	NewUser(user KeyshareUserData) error
	User(username string) (KeyshareUser, error)
	UpdateUser(user KeyshareUser) error

	// Reserve returns (allow, tries, wait, error)
	ReservePincheck(user KeyshareUser) (bool, int, int64, error)
	ClearPincheck(user KeyshareUser) error

	SetSeen(user KeyshareUser) error
}

type KeyshareUser interface {
	Data() *KeyshareUserData
}

type KeyshareUserData struct {
	Username string
	Coredata keysharecore.EncryptedKeysharePacket
}

type keyshareMemoryDB struct {
	lock  sync.Mutex
	users map[string]keysharecore.EncryptedKeysharePacket
}

type keyshareMemoryUser struct {
	KeyshareUserData
}

func (m *keyshareMemoryUser) Data() *KeyshareUserData {
	return &m.KeyshareUserData
}

func NewMemoryDatabase() KeyshareDB {
	return &keyshareMemoryDB{users: map[string]keysharecore.EncryptedKeysharePacket{}}
}

func (db *keyshareMemoryDB) User(username string) (KeyshareUser, error) {
	// Ensure access to database is single-threaded
	db.lock.Lock()
	defer db.lock.Unlock()

	// Check and fetch user data
	data, ok := db.users[username]
	if !ok {
		return nil, ErrUserNotFound
	}
	return &keyshareMemoryUser{KeyshareUserData{Username: username, Coredata: data}}, nil
}

func (db *keyshareMemoryDB) NewUser(user KeyshareUserData) error {
	// Ensure access to database is single-threaded
	db.lock.Lock()
	defer db.lock.Unlock()

	// Check and insert user
	_, exists := db.users[user.Username]
	if exists {
		return ErrUserAlreadyExists
	}
	db.users[user.Username] = user.Coredata
	return nil
}

func (db *keyshareMemoryDB) UpdateUser(user KeyshareUser) error {
	userdata, ok := user.(*keyshareMemoryUser)
	if !ok {
		return ErrInvalidData
	}

	// Ensure access to database is single-threaded
	db.lock.Lock()
	defer db.lock.Unlock()

	// Check and update user.
	_, exists := db.users[userdata.Username]
	if !exists {
		return ErrUserNotFound
	}
	db.users[userdata.Username] = userdata.Coredata
	return nil
}

func (db *keyshareMemoryDB) ReservePincheck(user KeyshareUser) (bool, int, int64, error) {
	// Since this is a testing DB, implementing anything more than always allow creates hastle
	return false, 1, 0, nil
}

func (db *keyshareMemoryDB) ClearPincheck(user KeyshareUser) error {
	// Since this is a testing DB, implementing anything more than always allow creates hastle
	return nil
}

func (db *keyshareMemoryDB) SetSeen(user KeyshareUser) error {
	return nil
}

type keysharePostgresDatabase struct {
	db *sql.DB
}

type keysharePostgresUser struct {
	KeyshareUserData
	id int
}

func (m *keysharePostgresUser) Data() *KeyshareUserData {
	return &m.KeyshareUserData
}

const MAX_PIN_TRIES = 3
const BACKOFF_START = 30

func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
	db, err := sql.Open("pgx", connstring)
	if err != nil {
		return nil, err
	}
	return &keysharePostgresDatabase{
		db: db,
	}, nil
}

func (db *keysharePostgresDatabase) NewUser(user KeyshareUserData) error {
	res, err := db.db.Exec("INSERT INTO irma.users (username, coredata, pinCounter, pinBlockDate) VALUES ($1, $2, 0, 0);", user.Username, user.Coredata[:])
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserAlreadyExists
	}
	return nil
}

func (db *keysharePostgresDatabase) User(username string) (KeyshareUser, error) {
	rows, err := db.db.Query("SELECT id, username, coredata FROM irma.users WHERE username = $1", username)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	if !rows.Next() {
		return nil, ErrUserNotFound
	}
	var result keysharePostgresUser
	var ep []byte
	err = rows.Scan(&result.id, &result.Username, &ep)
	if err != nil {
		return nil, err
	}
	if len(ep) != len(result.Coredata[:]) {
		return nil, ErrInvalidRecord
	}
	copy(result.Coredata[:], ep)
	return &result, nil
}

func (db *keysharePostgresDatabase) UpdateUser(user KeyshareUser) error {
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return ErrInvalidData
	}
	res, err := db.db.Exec("UPDATE irma.users SET username=$1, coredata=$2 WHERE id=$3", userdata.Username, userdata.Coredata, userdata.id)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}

func (db *keysharePostgresDatabase) ReservePincheck(user KeyshareUser) (bool, int, int64, error) {
	// Extract data
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return false, 0, 0, ErrInvalidData
	}

	// Check that account is not blocked already, and if not,
	//  update pinCounter and pinBlockDate
	uprows, err := db.db.Query(`
		UPDATE irma.users
		SET pinCounter = pinCounter+1,
			pinBlockDate = $1+$2*2^GREATEST(0, pinCounter-$3)
		WHERE id=$4 AND pinBlockDate<=$5
		RETURNING pinCounter, pinBlockDate`,
		time.Now().Unix()-1-BACKOFF_START, // Grace time of 2 seconds on pinBlockDate set
		BACKOFF_START,
		MAX_PIN_TRIES-2,
		userdata.id,
		time.Now().Unix())
	if err != nil {
		return false, 0, 0, err
	}
	defer uprows.Close()

	// Check whether we have results
	if !uprows.Next() {
		// if no, then account either does not exist (which would be weird here) or is blocked
		// so request wait timeout
		pinrows, err := db.db.Query("SELECT pinBlockDate FROM irma.users WHERE id=$1", userdata.id)
		if err != nil {
			return false, 0, 0, err
		}
		defer pinrows.Close()
		if !pinrows.Next() {
			return false, 0, 0, ErrUserNotFound
		}
		var wait int64
		err = pinrows.Scan(&wait)
		if err != nil {
			return false, 0, 0, err
		}
		return false, 0, wait - time.Now().Unix(), nil
	}

	// Pin check is allowed (implied since there is a result, so pinBlockDate <= now)
	//  calculate tries remaining and wait time
	var tries int
	var wait int64
	err = uprows.Scan(&tries, &wait)
	if err != nil {
		return false, 0, 0, err
	}
	tries = MAX_PIN_TRIES - tries
	if tries < 0 {
		tries = 0
	}
	return true, tries, wait - time.Now().Unix(), nil
}

func (db *keysharePostgresDatabase) ClearPincheck(user KeyshareUser) error {
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return ErrInvalidData
	}
	res, err := db.db.Exec("UPDATE irma.users SET pinCounter=0, pinBlockDate=0 WHERE id=$1", userdata.id)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}

func (db *keysharePostgresDatabase) SetSeen(user KeyshareUser) error {
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return ErrInvalidData
	}
	res, err := db.db.Exec("UPDATE irma.users SET lastSeen = $1 WHERE id = $2", time.Now().Unix(), userdata.id)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}
<commit_msg>Fixed bug in keyshare server memory database causing rejection of pin attempts.<commit_after>package keyshareserver

import (
	"database/sql"
	"errors"
	"sync"
	"time"

	"github.com/privacybydesign/irmago/internal/keysharecore"

	_ "github.com/jackc/pgx/stdlib"
)

var (
	ErrUserAlreadyExists = errors.New("Cannot create user, username already taken")
	ErrUserNotFound      = errors.New("Could not find specified user")
	ErrInvalidData       = errors.New("Invalid user datastructure passed")
	ErrInvalidRecord     = errors.New("Invalid record in database")
)

type KeyshareDB interface {
	NewUser(user KeyshareUserData) error
	User(username string) (KeyshareUser, error)
	UpdateUser(user KeyshareUser) error

	// Reserve returns (allow, tries, wait, error)
	ReservePincheck(user KeyshareUser) (bool, int, int64, error)
	ClearPincheck(user KeyshareUser) error

	SetSeen(user KeyshareUser) error
}

type KeyshareUser interface {
	Data() *KeyshareUserData
}

type KeyshareUserData struct {
	Username string
	Coredata keysharecore.EncryptedKeysharePacket
}

type keyshareMemoryDB struct {
	lock  sync.Mutex
	users map[string]keysharecore.EncryptedKeysharePacket
}

type keyshareMemoryUser struct {
	KeyshareUserData
}

func (m *keyshareMemoryUser) Data() *KeyshareUserData {
	return &m.KeyshareUserData
}

func NewMemoryDatabase() KeyshareDB {
	return &keyshareMemoryDB{users: map[string]keysharecore.EncryptedKeysharePacket{}}
}

func (db *keyshareMemoryDB) User(username string) (KeyshareUser, error) {
	// Ensure access to database is single-threaded
	db.lock.Lock()
	defer db.lock.Unlock()

	// Check and fetch user data
	data, ok := db.users[username]
	if !ok {
		return nil, ErrUserNotFound
	}
	return &keyshareMemoryUser{KeyshareUserData{Username: username, Coredata: data}}, nil
}

func (db *keyshareMemoryDB) NewUser(user KeyshareUserData) error {
	// Ensure access to database is single-threaded
	db.lock.Lock()
	defer db.lock.Unlock()

	// Check and insert user
	_, exists := db.users[user.Username]
	if exists {
		return ErrUserAlreadyExists
	}
	db.users[user.Username] = user.Coredata
	return nil
}

func (db *keyshareMemoryDB) UpdateUser(user KeyshareUser) error {
	userdata, ok := user.(*keyshareMemoryUser)
	if !ok {
		return ErrInvalidData
	}

	// Ensure access to database is single-threaded
	db.lock.Lock()
	defer db.lock.Unlock()

	// Check and update user.
	_, exists := db.users[userdata.Username]
	if !exists {
		return ErrUserNotFound
	}
	db.users[userdata.Username] = userdata.Coredata
	return nil
}

func (db *keyshareMemoryDB) ReservePincheck(user KeyshareUser) (bool, int, int64, error) {
	// Since this is a testing DB, implementing anything more than always allow creates hastle
	return true, 1, 0, nil
}

func (db *keyshareMemoryDB) ClearPincheck(user KeyshareUser) error {
	// Since this is a testing DB, implementing anything more than always allow creates hastle
	return nil
}

func (db *keyshareMemoryDB) SetSeen(user KeyshareUser) error {
	return nil
}

type keysharePostgresDatabase struct {
	db *sql.DB
}

type keysharePostgresUser struct {
	KeyshareUserData
	id int
}

func (m *keysharePostgresUser) Data() *KeyshareUserData {
	return &m.KeyshareUserData
}

const MAX_PIN_TRIES = 3
const BACKOFF_START = 30

func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
	db, err := sql.Open("pgx", connstring)
	if err != nil {
		return nil, err
	}
	return &keysharePostgresDatabase{
		db: db,
	}, nil
}

func (db *keysharePostgresDatabase) NewUser(user KeyshareUserData) error {
	res, err := db.db.Exec("INSERT INTO irma.users (username, coredata, pinCounter, pinBlockDate) VALUES ($1, $2, 0, 0);", user.Username, user.Coredata[:])
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserAlreadyExists
	}
	return nil
}

func (db *keysharePostgresDatabase) User(username string) (KeyshareUser, error) {
	rows, err := db.db.Query("SELECT id, username, coredata FROM irma.users WHERE username = $1", username)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	if !rows.Next() {
		return nil, ErrUserNotFound
	}
	var result keysharePostgresUser
	var ep []byte
	err = rows.Scan(&result.id, &result.Username, &ep)
	if err != nil {
		return nil, err
	}
	if len(ep) != len(result.Coredata[:]) {
		return nil, ErrInvalidRecord
	}
	copy(result.Coredata[:], ep)
	return &result, nil
}

func (db *keysharePostgresDatabase) UpdateUser(user KeyshareUser) error {
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return ErrInvalidData
	}
	res, err := db.db.Exec("UPDATE irma.users SET username=$1, coredata=$2 WHERE id=$3", userdata.Username, userdata.Coredata, userdata.id)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}

func (db *keysharePostgresDatabase) ReservePincheck(user KeyshareUser) (bool, int, int64, error) {
	// Extract data
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return false, 0, 0, ErrInvalidData
	}

	// Check that account is not blocked already, and if not,
	//  update pinCounter and pinBlockDate
	uprows, err := db.db.Query(`
		UPDATE irma.users
		SET pinCounter = pinCounter+1,
			pinBlockDate = $1+$2*2^GREATEST(0, pinCounter-$3)
		WHERE id=$4 AND pinBlockDate<=$5
		RETURNING pinCounter, pinBlockDate`,
		time.Now().Unix()-1-BACKOFF_START, // Grace time of 2 seconds on pinBlockDate set
		BACKOFF_START,
		MAX_PIN_TRIES-2,
		userdata.id,
		time.Now().Unix())
	if err != nil {
		return false, 0, 0, err
	}
	defer uprows.Close()

	// Check whether we have results
	if !uprows.Next() {
		// if no, then account either does not exist (which would be weird here) or is blocked
		// so request wait timeout
		pinrows, err := db.db.Query("SELECT pinBlockDate FROM irma.users WHERE id=$1", userdata.id)
		if err != nil {
			return false, 0, 0, err
		}
		defer pinrows.Close()
		if !pinrows.Next() {
			return false, 0, 0, ErrUserNotFound
		}
		var wait int64
		err = pinrows.Scan(&wait)
		if err != nil {
			return false, 0, 0, err
		}
		return false, 0, wait - time.Now().Unix(), nil
	}

	// Pin check is allowed (implied since there is a result, so pinBlockDate <= now)
	//  calculate tries remaining and wait time
	var tries int
	var wait int64
	err = uprows.Scan(&tries, &wait)
	if err != nil {
		return false, 0, 0, err
	}
	tries = MAX_PIN_TRIES - tries
	if tries < 0 {
		tries = 0
	}
	return true, tries, wait - time.Now().Unix(), nil
}

func (db *keysharePostgresDatabase) ClearPincheck(user KeyshareUser) error {
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return ErrInvalidData
	}
	res, err := db.db.Exec("UPDATE irma.users SET pinCounter=0, pinBlockDate=0 WHERE id=$1", userdata.id)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}

func (db *keysharePostgresDatabase) SetSeen(user KeyshareUser) error {
	userdata, ok := user.(*keysharePostgresUser)
	if !ok {
		return ErrInvalidData
	}
	res, err := db.db.Exec("UPDATE irma.users SET lastSeen = $1 WHERE id = $2", time.Now().Unix(), userdata.id)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package reddit

import (
	"encoding/json"
	"fmt"
)

// Headline represents an individual post from the perspective
// of a subreddit.
type Headline struct {
	Author       string  `json:"author"`
	Title        string  `json:"title"`
	URL          string  `json:"url"`
	Domain       string  `json:"domain"`
	Subreddit    string  `json:"subreddit"`
	SubredditId  string  `json:"subreddit_id"`
	FullId       string  `json:"name"`
	Id           string  `json:"id"`
	Permalink    string  `json:"permalink"`
	Selftext     string  `json:"selftext"`
	ThumbnailURL string  `json:"thumbnail"`
	DateCreated  float32 `json:"created_utc"`
	NumComments  int     `json:"num_comments"`
	Score        int     `json:"score"`
	// Ups and downs are fake to trick spammers
	Ups        int     `json:"ups"`
	Downs      int     `json:"downs"`
	IsNSFW     bool    `json:"over_18"`
	IsSelf     bool    `json:"is_self"`
	WasClicked bool    `json:"clicked"`
	IsSaved    bool    `json:"saved"`
	BannedBy   *string `json:"banned_by"`
}

// FullPermalink returns the full URL of a headline.
func (h Headline) FullPermalink() string {
	return "http://reddit.com" + h.Permalink
}

// String returns the string representation of a headline.
func (h Headline) String() string {
	var comments string
	switch h.NumComments {
	case 0:
		comments = "0 comments"
	case 1:
		comments = "1 comment"
	default:
		comments = fmt.Sprintf("%d comments", h.NumComments)
	}
	return fmt.Sprintf("%d - %s (%s)", h.Score, h.Title, comments)
}

// DefaultHeadlines returns a slice of headlines on the default reddit frontpage.
func DefaultHeadlines() ([]Headline, error) {
	url := "http://www.reddit.com/.json"
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make([]Headline, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SubredditHeadlines returns a slice of headlines on the given subreddit.
func SubredditHeadlines(subreddit string) ([]Headline, error) {
	url := fmt.Sprintf("http://www.reddit.com/r/%s.json", subreddit)
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make([]Headline, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}
<commit_msg>Added sorting function and options to headlines<commit_after>// Copyright 2012 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package reddit

import (
	"encoding/json"
	"fmt"
)

// Headline represents an individual post from the perspective
// of a subreddit.
type Headline struct {
	Author       string  `json:"author"`
	Title        string  `json:"title"`
	URL          string  `json:"url"`
	Domain       string  `json:"domain"`
	Subreddit    string  `json:"subreddit"`
	SubredditId  string  `json:"subreddit_id"`
	FullId       string  `json:"name"`
	Id           string  `json:"id"`
	Permalink    string  `json:"permalink"`
	Selftext     string  `json:"selftext"`
	ThumbnailURL string  `json:"thumbnail"`
	DateCreated  float32 `json:"created_utc"`
	NumComments  int     `json:"num_comments"`
	Score        int     `json:"score"`
	// Ups and downs are fake to trick spammers
	Ups        int     `json:"ups"`
	Downs      int     `json:"downs"`
	IsNSFW     bool    `json:"over_18"`
	IsSelf     bool    `json:"is_self"`
	WasClicked bool    `json:"clicked"`
	IsSaved    bool    `json:"saved"`
	BannedBy   *string `json:"banned_by"`
}

// Sort headlines by popularity
type PopularitySort string

const (
	DefaultPopularity      PopularitySort = ""
	HotHeadlines                          = "hot"
	NewHeadlines                          = "new"
	RisingHeadlines                       = "rising"
	TopHeadlines                          = "top"
	ControversialHeadlines                = "controversial"
)

// Sort headlines by age
type AgeSort string

const (
	DefaultAge AgeSort = ""
	ThisHour           = "hour"
	ThisMonth          = "month"
	ThisYear           = "year"
	AllTime            = "all"
)

type Headlines []*Headline

// FullPermalink returns the full URL of a headline.
func (h *Headline) FullPermalink() string {
	return "http://reddit.com" + h.Permalink
}

// String returns the string representation of a headline.
func (h *Headline) String() string {
	plural := ""
	if h.NumComments != 1 {
		plural = "s"
	}
	comments := fmt.Sprintf("%d comment%s", h.NumComments, plural)
	/*var comments string
	switch h.NumComments {
	case 0:
		comments = "0 comments"
	case 1:
		comments = "1 comment"
	default:
		comments = fmt.Sprintf("%d comments", h.NumComments)
	}*/
	return fmt.Sprintf("%d - %s (%s)", h.Score, h.Title, comments)
}

// DefaultHeadlines returns a slice of headlines on the default reddit frontpage.
func DefaultHeadlines() (Headlines, error) {
	url := "http://www.reddit.com/.json"
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SubredditHeadlines returns a slice of headlines on the given subreddit.
func SubredditHeadlines(subreddit string) (Headlines, error) {
	url := fmt.Sprintf("http://www.reddit.com/r/%s.json", subreddit)
	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil
}

// SortedHeadlines will return headlines from a subreddit (or homepage if "") by popularity and age
func SortedHeadlines(subreddit string, popularity PopularitySort, age AgeSort) (Headlines, error) {
	if age != DefaultAge {
		switch popularity {
		case NewHeadlines, RisingHeadlines, HotHeadlines:
			return nil, fmt.Errorf("Cannot sort %s by %s", popularity, age)
		}
	}

	url := "http://reddit.com/"

	if subreddit != "" {
		url = fmt.Sprintf("http://%s.reddit.com/", subreddit)
	}

	if popularity != DefaultPopularity {
		if popularity == NewHeadlines || popularity == RisingHeadlines {
			url = fmt.Sprintf("%s.json?sort=%s", url, popularity)
		} else {
			url = fmt.Sprintf("%s%s.json?sort=%s", url, popularity, popularity)
		}
	} else {
		url = fmt.Sprintf("%s.json", url)
	}

	if age != DefaultAge {
		if popularity != DefaultPopularity {
			url = fmt.Sprintf("%s&t=%s", url, age)
		} else {
			url = fmt.Sprintf("%s?t=%s", url, age)
		}
	}

	body, err := getResponse(url, nil, nil)
	if err != nil {
		return nil, err
	}

	type Response struct {
		Data struct {
			Children []struct {
				Data *Headline
			}
		}
	}

	r := new(Response)
	err = json.NewDecoder(body).Decode(r)
	if err != nil {
		return nil, err
	}

	headlines := make(Headlines, len(r.Data.Children))
	for i, child := range r.Data.Children {
		headlines[i] = child.Data
	}

	return headlines, nil

}
<|endoftext|>


<commit_before>package repo

import (
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Masterminds/glide/cfg"
	"github.com/Masterminds/glide/dependency"
	"github.com/Masterminds/glide/importer"
	"github.com/Masterminds/glide/msg"
	gpath "github.com/Masterminds/glide/path"
	"github.com/Masterminds/glide/util"
	"github.com/codegangsta/cli"
)

// Installer provides facilities for installing the repos in a config file.
type Installer struct {

	// Force the install when certain normally stopping conditions occur.
	Force bool

	// Home is the location of cache
	Home string

	// Vendor contains the path to put the vendor packages
	Vendor string

	// Use a cache
	UseCache bool
	// Use Gopath to cache
	UseCacheGopath bool
	// Use Gopath as a source to read from
	UseGopath bool

	// UpdateVendored instructs the environment to update in a way that is friendly
	// to packages that have been "vendored in" (e.g. are copies of source, not repos)
	UpdateVendored bool

	// DeleteUnused deletes packages that are unused, but found in the vendor dir.
	DeleteUnused bool

	// RootPackage is the top level package importing other packages. If an
	// imported pacakgage references this pacakage it does not need to be
	// downloaded and searched out again.
	RootPackage string

	// Ignore contains a list of package names to skip
	Ignore []string
}

// VendorPath returns the path to the location to put vendor packages
func (i *Installer) VendorPath() string {
	if i.Vendor != "" {
		return i.Vendor
	}

	vp, err := gpath.Vendor()
	if err != nil {
		return filepath.FromSlash("./vendor")
	}

	return vp
}

// Install installs the dependencies from a Lockfile.
func (i *Installer) Install(lock *cfg.Lockfile, conf *cfg.Config) (*cfg.Config, error) {

	cwd, err := gpath.Vendor()
	if err != nil {
		return conf, err
	}

	// Create a config setup based on the Lockfile data to process with
	// existing commands.
	newConf := &cfg.Config{}
	newConf.Name = conf.Name

	newConf.Imports = make(cfg.Dependencies, len(lock.Imports))
	for k, v := range lock.Imports {
		newConf.Imports[k] = &cfg.Dependency{
			Name:        v.Name,
			Reference:   v.Version,
			Repository:  v.Repository,
			VcsType:     v.VcsType,
			Subpackages: v.Subpackages,
			Arch:        v.Arch,
			Os:          v.Os,
		}
	}

	newConf.DevImports = make(cfg.Dependencies, len(lock.DevImports))
	for k, v := range lock.DevImports {
		newConf.DevImports[k] = &cfg.Dependency{
			Name:        v.Name,
			Reference:   v.Version,
			Repository:  v.Repository,
			VcsType:     v.VcsType,
			Subpackages: v.Subpackages,
			Arch:        v.Arch,
			Os:          v.Os,
		}
	}

	newConf.DeDupe()

	if len(newConf.Imports) == 0 {
		msg.Info("No dependencies found. Nothing installed.\n")
		return newConf, nil
	}

	ConcurrentUpdate(newConf.Imports, cwd, i)
	ConcurrentUpdate(newConf.DevImports, cwd, i)
	return newConf, nil
}

// Checkout reads the config file and checks out all dependencies mentioned there.
//
// This is used when initializing an empty vendor directory, or when updating a
// vendor directory based on changed config.
func (i *Installer) Checkout(conf *cfg.Config, useDev bool) error {

	dest := i.VendorPath()

	if err := ConcurrentUpdate(conf.Imports, dest, i); err != nil {
		return err
	}

	if useDev {
		return ConcurrentUpdate(conf.DevImports, dest, i)
	}

	return nil
}

// Update updates all dependencies.
//
// It begins with the dependencies in the config file, but also resolves
// transitive dependencies. The returned lockfile has all of the dependencies
// listed, but the version reconciliation has not been done.
//
// In other words, all versions in the Lockfile will be empty.
func (i *Installer) Update(conf *cfg.Config) error {
	base := "."
	vpath := i.VendorPath()

	m := &MissingPackageHandler{
		destination: vpath,

		cache:       i.UseCache,
		cacheGopath: i.UseCacheGopath,
		useGopath:   i.UseGopath,
		home:        i.Home,
	}

	v := &VersionHandler{
		Destination: vpath,
		Deps:        make(map[string]*cfg.Dependency),
		Use:         make(map[string]*cfg.Dependency),
		Imported:    make(map[string]bool),
	}

	// Update imports
	res, err := dependency.NewResolver(base)
	res.Ignore = i.Ignore
	if err != nil {
		msg.Die("Failed to create a resolver: %s", err)
	}
	res.Handler = m
	res.VersionHandler = v
	msg.Info("Resolving imports")
	packages, err := allPackages(conf.Imports, res)
	if err != nil {
		msg.Die("Failed to retrieve a list of dependencies: %s", err)
	}

	msg.Warn("devImports not resolved.")

	deps := depsFromPackages(packages)
	err = ConcurrentUpdate(deps, vpath, i)

	// Placed the pinned versions onto the Dependency instances
	for _, d := range deps {
		d2, found := v.Deps[d.Name]
		if found {
			d.Pin = d2.Pin
		}
	}

	return err
}

func (i *Installer) List(conf *cfg.Config) []*cfg.Dependency {
	base := "."

	// Update imports
	res, err := dependency.NewResolver(base)
	if err != nil {
		msg.Die("Failed to create a resolver: %s", err)
	}

	msg.Info("Resolving imports")
	packages, err := allPackages(conf.Imports, res)
	if err != nil {
		msg.Die("Failed to retrieve a list of dependencies: %s", err)
	}
	deps := depsFromPackages(packages)

	msg.Warn("devImports not resolved.")

	return deps
}

// ConcurrentUpdate takes a list of dependencies and updates in parallel.
func ConcurrentUpdate(deps []*cfg.Dependency, cwd string, i *Installer) error {
	done := make(chan struct{}, concurrentWorkers)
	in := make(chan *cfg.Dependency, concurrentWorkers)
	var wg sync.WaitGroup
	var lock sync.Mutex
	var returnErr error

	for ii := 0; ii < concurrentWorkers; ii++ {
		go func(ch <-chan *cfg.Dependency) {
			for {
				select {
				case dep := <-ch:
					if err := VcsUpdate(dep, cwd, i); err != nil {
						msg.Warn("Update failed for %s: %s\n", dep.Name, err)

						// Capture the error while making sure the concurrent
						// operations don't step on each other.
						lock.Lock()
						if returnErr == nil {
							returnErr = err
						} else {
							returnErr = cli.NewMultiError(returnErr, err)
						}
						lock.Unlock()
					}
					wg.Done()
				case <-done:
					return
				}
			}
		}(in)
	}

	for _, dep := range deps {
		wg.Add(1)
		in <- dep
	}

	wg.Wait()

	// Close goroutines setting the version
	for ii := 0; ii < concurrentWorkers; ii++ {
		done <- struct{}{}
	}

	return returnErr
}

// allPackages gets a list of all packages required to satisfy the given deps.
func allPackages(deps []*cfg.Dependency, res *dependency.Resolver) ([]string, error) {
	if len(deps) == 0 {
		return []string{}, nil
	}

	vdir, err := gpath.Vendor()
	if err != nil {
		return []string{}, err
	}
	vdir += string(os.PathSeparator)
	ll, err := res.ResolveAll(deps)
	if err != nil {
		return []string{}, err
	}

	for i := 0; i < len(ll); i++ {
		ll[i] = strings.TrimPrefix(ll[i], vdir)
	}
	return ll, nil
}

/* unused
func reposFromPackages(pkgs []string) []string {
	// Make sure we don't have to resize this.
	seen := make(map[string]bool, len(pkgs))

	// Order is important.
	repos := []string{}

	for _, p := range pkgs {
		rr, _ := util.NormalizeName(p)
		if !seen[rr] {
			seen[rr] = true
			repos = append(repos, rr)
		}
	}
	return repos
}
*/

func depsFromPackages(pkgs []string) []*cfg.Dependency {
	// Make sure we don't have to resize this.
	seen := make(map[string]*cfg.Dependency, len(pkgs))

	// Order is important.
	deps := []*cfg.Dependency{}

	for _, p := range pkgs {
		rr, sp := util.NormalizeName(p)
		if _, ok := seen[rr]; !ok {
			subpkg := []string{}
			if sp != "" {
				subpkg = append(subpkg, sp)
			}

			dd := &cfg.Dependency{
				Name:        rr,
				Subpackages: subpkg,
			}

			deps = append(deps, dd)
			seen[rr] = dd
		} else if sp != "" {
			seen[rr].Subpackages = append(seen[rr].Subpackages, sp)
		}
	}
	return deps
}

// MissingPackageHandler is a dependency.MissingPackageHandler.
//
// When a package is not found, this attempts to resolve and fetch.
//
// When a package is found on the GOPATH, this notifies the user.
type MissingPackageHandler struct {
	destination                   string
	home                          string
	cache, cacheGopath, useGopath bool
	RootPackage                   string
	Ignore                        []string
}

func (m *MissingPackageHandler) NotFound(pkg string) (bool, error) {
	root := util.GetRootFromPackage(pkg)

	// Skip any references to the root package.
	if root == m.RootPackage {
		return false, nil
	}
	for _, v := range m.Ignore {
		if v == root || v == pkg {
			return false, nil
		}
	}

	dest := filepath.Join(m.destination, root)

	// This package may have been placed on the list to look for when it wasn't
	// downloaded but it has since been downloaded before coming to this entry.
	if _, err := os.Stat(dest); err == nil {
		return true, nil
	}

	msg.Info("Fetching %s into %s", pkg, m.destination)

	d := &cfg.Dependency{Name: root}
	if err := VcsGet(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath); err != nil {
		return false, err
	}
	return true, nil
}

func (m *MissingPackageHandler) OnGopath(pkg string) (bool, error) {
	// If useGopath is false, we fall back to the strategy of fetching from
	// remote.
	if !m.useGopath {
		return m.NotFound(pkg)
	}

	root := util.GetRootFromPackage(pkg)

	// Skip any references to the root package.
	if root == m.RootPackage {
		return false, nil
	}
	for _, v := range m.Ignore {
		if v == root || v == pkg {
			return false, nil
		}
	}

	msg.Info("Copying package %s from the GOPATH.", pkg)
	dest := filepath.Join(m.destination, pkg)
	// Find package on Gopath
	for _, gp := range gpath.Gopaths() {
		src := filepath.Join(gp, pkg)
		// FIXME: Should probably check if src is a dir or symlink.
		if _, err := os.Stat(src); err == nil {
			if err := os.MkdirAll(dest, os.ModeDir|0755); err != nil {
				return false, err
			}
			if err := gpath.CopyDir(src, dest); err != nil {
				return false, err
			}
			return true, nil
		}
	}

	msg.Error("Could not locate %s on the GOPATH, though it was found before.", pkg)
	return false, nil
}

// VersionHandler handles setting the proper version in the VCS.
type VersionHandler struct {

	// Deps provides a map of packages and their dependency instances.
	Deps map[string]*cfg.Dependency

	// If Try to use the version here if we have one. This is a cache and will
	// change over the course of setting versions.
	Use map[string]*cfg.Dependency

	// Cache if importing scan has already occured here.
	Imported map[string]bool

	// Where the packages exist to set the version on.
	Destination string

	RootPackage string
	Ignore      []string
}

// SetVersion sets the version for a package. If that package version is already
// set it handles the case by:
// - keeping the already set version
// - proviting messaging about the version conflict
func (d *VersionHandler) SetVersion(pkg string) (e error) {
	root := util.GetRootFromPackage(pkg)

	// Skip any references to the root package.
	if root == d.RootPackage {
		return nil
	}
	for _, v := range d.Ignore {
		if v == root || v == pkg {
			return nil
		}
	}

	v, found := d.Deps[root]

	// We have not tried to import, yet.
	// Should we look in places other than the root of the project?
	if d.Imported[root] == false {
		d.Imported[root] = true
		f, deps, err := importer.Import(root)
		if f && err != nil {

			// Store the imported version information. This will overwrite
			// previous entries. The latest imported is the version to use when
			// something is not pinned already. Once a version is set and pinned
			// it will not be changed later. So, the first to set the version
			// wins.
			for _, dep := range deps {
				if dep.Reference != "" {
					d.Use[dep.Name] = dep
				}
			}
		} else if err != nil {
			msg.Error("Unable to import from %s. Err: %s", root, err)
			e = err
		}
	}

	// If we are already pinned provide some useful messaging.
	if found {
		msg.Debug("Package %s is already pinned to %q", pkg, v.Pin)

		// Catch requested version conflicts here.
		if d.Use[root].Reference != "" && d.Use[root].Reference != d.Deps[root].Pin &&
			d.Use[root].Reference != d.Deps[root].Reference {
			msg.Warn("Conflict: %s version is %s, but also asked for %s\n", root, d.Deps[root].Pin, d.Use[root].Reference)
		}

		return
	}

	// The first time we've encountered this so try to set the version.
	dep, found := d.Use[root]
	if !found {
		msg.Debug("Unable to set version on %s, version to set unknown", root)
		return
	}
	err := VcsVersion(dep, d.Destination)
	if err != nil {
		msg.Warn("Unable to set verion on %s to %s. Err: ", root, dep.Reference, err)
		e = err
	}
	d.Deps[root] = dep
	return
}
<commit_msg>Fixing issue with importing config files while resolving dependencies<commit_after>package repo

import (
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Masterminds/glide/cfg"
	"github.com/Masterminds/glide/dependency"
	"github.com/Masterminds/glide/importer"
	"github.com/Masterminds/glide/msg"
	gpath "github.com/Masterminds/glide/path"
	"github.com/Masterminds/glide/util"
	"github.com/codegangsta/cli"
)

// Installer provides facilities for installing the repos in a config file.
type Installer struct {

	// Force the install when certain normally stopping conditions occur.
	Force bool

	// Home is the location of cache
	Home string

	// Vendor contains the path to put the vendor packages
	Vendor string

	// Use a cache
	UseCache bool
	// Use Gopath to cache
	UseCacheGopath bool
	// Use Gopath as a source to read from
	UseGopath bool

	// UpdateVendored instructs the environment to update in a way that is friendly
	// to packages that have been "vendored in" (e.g. are copies of source, not repos)
	UpdateVendored bool

	// DeleteUnused deletes packages that are unused, but found in the vendor dir.
	DeleteUnused bool

	// RootPackage is the top level package importing other packages. If an
	// imported pacakgage references this pacakage it does not need to be
	// downloaded and searched out again.
	RootPackage string

	// Ignore contains a list of package names to skip
	Ignore []string
}

// VendorPath returns the path to the location to put vendor packages
func (i *Installer) VendorPath() string {
	if i.Vendor != "" {
		return i.Vendor
	}

	vp, err := gpath.Vendor()
	if err != nil {
		return filepath.FromSlash("./vendor")
	}

	return vp
}

// Install installs the dependencies from a Lockfile.
func (i *Installer) Install(lock *cfg.Lockfile, conf *cfg.Config) (*cfg.Config, error) {

	cwd, err := gpath.Vendor()
	if err != nil {
		return conf, err
	}

	// Create a config setup based on the Lockfile data to process with
	// existing commands.
	newConf := &cfg.Config{}
	newConf.Name = conf.Name

	newConf.Imports = make(cfg.Dependencies, len(lock.Imports))
	for k, v := range lock.Imports {
		newConf.Imports[k] = &cfg.Dependency{
			Name:        v.Name,
			Reference:   v.Version,
			Repository:  v.Repository,
			VcsType:     v.VcsType,
			Subpackages: v.Subpackages,
			Arch:        v.Arch,
			Os:          v.Os,
		}
	}

	newConf.DevImports = make(cfg.Dependencies, len(lock.DevImports))
	for k, v := range lock.DevImports {
		newConf.DevImports[k] = &cfg.Dependency{
			Name:        v.Name,
			Reference:   v.Version,
			Repository:  v.Repository,
			VcsType:     v.VcsType,
			Subpackages: v.Subpackages,
			Arch:        v.Arch,
			Os:          v.Os,
		}
	}

	newConf.DeDupe()

	if len(newConf.Imports) == 0 {
		msg.Info("No dependencies found. Nothing installed.\n")
		return newConf, nil
	}

	ConcurrentUpdate(newConf.Imports, cwd, i)
	ConcurrentUpdate(newConf.DevImports, cwd, i)
	return newConf, nil
}

// Checkout reads the config file and checks out all dependencies mentioned there.
//
// This is used when initializing an empty vendor directory, or when updating a
// vendor directory based on changed config.
func (i *Installer) Checkout(conf *cfg.Config, useDev bool) error {

	dest := i.VendorPath()

	if err := ConcurrentUpdate(conf.Imports, dest, i); err != nil {
		return err
	}

	if useDev {
		return ConcurrentUpdate(conf.DevImports, dest, i)
	}

	return nil
}

// Update updates all dependencies.
//
// It begins with the dependencies in the config file, but also resolves
// transitive dependencies. The returned lockfile has all of the dependencies
// listed, but the version reconciliation has not been done.
//
// In other words, all versions in the Lockfile will be empty.
func (i *Installer) Update(conf *cfg.Config) error {
	base := "."
	vpath := i.VendorPath()

	m := &MissingPackageHandler{
		destination: vpath,

		cache:       i.UseCache,
		cacheGopath: i.UseCacheGopath,
		useGopath:   i.UseGopath,
		home:        i.Home,
	}

	v := &VersionHandler{
		Destination: vpath,
		Deps:        make(map[string]*cfg.Dependency),
		Use:         make(map[string]*cfg.Dependency),
		Imported:    make(map[string]bool),
	}

	// Update imports
	res, err := dependency.NewResolver(base)
	res.Ignore = i.Ignore
	if err != nil {
		msg.Die("Failed to create a resolver: %s", err)
	}
	res.Handler = m
	res.VersionHandler = v
	msg.Info("Resolving imports")
	packages, err := allPackages(conf.Imports, res)
	if err != nil {
		msg.Die("Failed to retrieve a list of dependencies: %s", err)
	}

	msg.Warn("devImports not resolved.")

	deps := depsFromPackages(packages)
	err = ConcurrentUpdate(deps, vpath, i)

	// Placed the pinned versions onto the Dependency instances
	for _, d := range deps {
		d2, found := v.Deps[d.Name]
		if found {
			d.Pin = d2.Pin
		}
	}

	return err
}

func (i *Installer) List(conf *cfg.Config) []*cfg.Dependency {
	base := "."

	// Update imports
	res, err := dependency.NewResolver(base)
	if err != nil {
		msg.Die("Failed to create a resolver: %s", err)
	}

	msg.Info("Resolving imports")
	packages, err := allPackages(conf.Imports, res)
	if err != nil {
		msg.Die("Failed to retrieve a list of dependencies: %s", err)
	}
	deps := depsFromPackages(packages)

	msg.Warn("devImports not resolved.")

	return deps
}

// ConcurrentUpdate takes a list of dependencies and updates in parallel.
func ConcurrentUpdate(deps []*cfg.Dependency, cwd string, i *Installer) error {
	done := make(chan struct{}, concurrentWorkers)
	in := make(chan *cfg.Dependency, concurrentWorkers)
	var wg sync.WaitGroup
	var lock sync.Mutex
	var returnErr error

	for ii := 0; ii < concurrentWorkers; ii++ {
		go func(ch <-chan *cfg.Dependency) {
			for {
				select {
				case dep := <-ch:
					if err := VcsUpdate(dep, cwd, i); err != nil {
						msg.Warn("Update failed for %s: %s\n", dep.Name, err)

						// Capture the error while making sure the concurrent
						// operations don't step on each other.
						lock.Lock()
						if returnErr == nil {
							returnErr = err
						} else {
							returnErr = cli.NewMultiError(returnErr, err)
						}
						lock.Unlock()
					}
					wg.Done()
				case <-done:
					return
				}
			}
		}(in)
	}

	for _, dep := range deps {
		wg.Add(1)
		in <- dep
	}

	wg.Wait()

	// Close goroutines setting the version
	for ii := 0; ii < concurrentWorkers; ii++ {
		done <- struct{}{}
	}

	return returnErr
}

// allPackages gets a list of all packages required to satisfy the given deps.
func allPackages(deps []*cfg.Dependency, res *dependency.Resolver) ([]string, error) {
	if len(deps) == 0 {
		return []string{}, nil
	}

	vdir, err := gpath.Vendor()
	if err != nil {
		return []string{}, err
	}
	vdir += string(os.PathSeparator)
	ll, err := res.ResolveAll(deps)
	if err != nil {
		return []string{}, err
	}

	for i := 0; i < len(ll); i++ {
		ll[i] = strings.TrimPrefix(ll[i], vdir)
	}
	return ll, nil
}

/* unused
func reposFromPackages(pkgs []string) []string {
	// Make sure we don't have to resize this.
	seen := make(map[string]bool, len(pkgs))

	// Order is important.
	repos := []string{}

	for _, p := range pkgs {
		rr, _ := util.NormalizeName(p)
		if !seen[rr] {
			seen[rr] = true
			repos = append(repos, rr)
		}
	}
	return repos
}
*/

func depsFromPackages(pkgs []string) []*cfg.Dependency {
	// Make sure we don't have to resize this.
	seen := make(map[string]*cfg.Dependency, len(pkgs))

	// Order is important.
	deps := []*cfg.Dependency{}

	for _, p := range pkgs {
		rr, sp := util.NormalizeName(p)
		if _, ok := seen[rr]; !ok {
			subpkg := []string{}
			if sp != "" {
				subpkg = append(subpkg, sp)
			}

			dd := &cfg.Dependency{
				Name:        rr,
				Subpackages: subpkg,
			}

			deps = append(deps, dd)
			seen[rr] = dd
		} else if sp != "" {
			seen[rr].Subpackages = append(seen[rr].Subpackages, sp)
		}
	}
	return deps
}

// MissingPackageHandler is a dependency.MissingPackageHandler.
//
// When a package is not found, this attempts to resolve and fetch.
//
// When a package is found on the GOPATH, this notifies the user.
type MissingPackageHandler struct {
	destination                   string
	home                          string
	cache, cacheGopath, useGopath bool
	RootPackage                   string
	Ignore                        []string
}

func (m *MissingPackageHandler) NotFound(pkg string) (bool, error) {
	root := util.GetRootFromPackage(pkg)

	// Skip any references to the root package.
	if root == m.RootPackage {
		return false, nil
	}
	for _, v := range m.Ignore {
		if v == root || v == pkg {
			return false, nil
		}
	}

	dest := filepath.Join(m.destination, root)

	// This package may have been placed on the list to look for when it wasn't
	// downloaded but it has since been downloaded before coming to this entry.
	if _, err := os.Stat(dest); err == nil {
		return true, nil
	}

	msg.Info("Fetching %s into %s", pkg, m.destination)

	d := &cfg.Dependency{Name: root}
	if err := VcsGet(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath); err != nil {
		return false, err
	}
	return true, nil
}

func (m *MissingPackageHandler) OnGopath(pkg string) (bool, error) {
	// If useGopath is false, we fall back to the strategy of fetching from
	// remote.
	if !m.useGopath {
		return m.NotFound(pkg)
	}

	root := util.GetRootFromPackage(pkg)

	// Skip any references to the root package.
	if root == m.RootPackage {
		return false, nil
	}
	for _, v := range m.Ignore {
		if v == root || v == pkg {
			return false, nil
		}
	}

	msg.Info("Copying package %s from the GOPATH.", pkg)
	dest := filepath.Join(m.destination, pkg)
	// Find package on Gopath
	for _, gp := range gpath.Gopaths() {
		src := filepath.Join(gp, pkg)
		// FIXME: Should probably check if src is a dir or symlink.
		if _, err := os.Stat(src); err == nil {
			if err := os.MkdirAll(dest, os.ModeDir|0755); err != nil {
				return false, err
			}
			if err := gpath.CopyDir(src, dest); err != nil {
				return false, err
			}
			return true, nil
		}
	}

	msg.Error("Could not locate %s on the GOPATH, though it was found before.", pkg)
	return false, nil
}

// VersionHandler handles setting the proper version in the VCS.
type VersionHandler struct {

	// Deps provides a map of packages and their dependency instances.
	Deps map[string]*cfg.Dependency

	// If Try to use the version here if we have one. This is a cache and will
	// change over the course of setting versions.
	Use map[string]*cfg.Dependency

	// Cache if importing scan has already occured here.
	Imported map[string]bool

	// Where the packages exist to set the version on.
	Destination string

	RootPackage string
	Ignore      []string
}

// SetVersion sets the version for a package. If that package version is already
// set it handles the case by:
// - keeping the already set version
// - proviting messaging about the version conflict
func (d *VersionHandler) SetVersion(pkg string) (e error) {
	root := util.GetRootFromPackage(pkg)

	// Skip any references to the root package.
	if root == d.RootPackage {
		return nil
	}
	for _, v := range d.Ignore {
		if v == root || v == pkg {
			return nil
		}
	}

	v, found := d.Deps[root]

	// We have not tried to import, yet.
	// Should we look in places other than the root of the project?
	if d.Imported[root] == false {
		d.Imported[root] = true
		p := filepath.Join(d.Destination, root)
		f, deps, err := importer.Import(p)
		if f && err == nil {

			// Store the imported version information. This will overwrite
			// previous entries. The latest imported is the version to use when
			// something is not pinned already. Once a version is set and pinned
			// it will not be changed later. So, the first to set the version
			// wins.
			for _, dep := range deps {
				if dep.Reference != "" {
					d.Use[dep.Name] = dep
				}
			}
		} else if err != nil {
			msg.Error("Unable to import from %s. Err: %s", root, err)
			e = err
		}
	}

	// If we are already pinned provide some useful messaging.
	if found {
		msg.Debug("Package %s is already pinned to %q", pkg, v.Pin)

		// Catch requested version conflicts here.
		if d.Use[root].Reference != "" && d.Use[root].Reference != d.Deps[root].Pin &&
			d.Use[root].Reference != d.Deps[root].Reference {
			msg.Warn("Conflict: %s version is %s, but also asked for %s\n", root, d.Deps[root].Pin, d.Use[root].Reference)
		}

		return
	}

	// The first time we've encountered this so try to set the version.
	dep, found := d.Use[root]
	if !found {
		msg.Debug("Unable to set version on %s, version to set unknown", root)
		return
	}
	err := VcsVersion(dep, d.Destination)
	if err != nil {
		msg.Warn("Unable to set verion on %s to %s. Err: ", root, dep.Reference, err)
		e = err
	}
	d.Deps[root] = dep
	return
}
<|endoftext|>


<commit_before>package grunway

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

	"github.com/amattn/deeperror"
)

// convenience struct holding all the stuff you usually want to know about an endpoint
type Endpoint struct {
	VersionStr string
	EntityName string
	PrimaryKey int64
	Action     string
	Extras     []string

	// internal only
	version        VersionUint
	versionConvErr error
}

// return a typed number, not a string
// cache value so we only do this once.
func (e *Endpoint) Version() VersionUint {
	if e.versionConvErr == nil && e.version == 0 {
		var v64 uint64
		v64, e.versionConvErr = strconv.ParseUint(e.VersionStr, 10, VERSION_BIT_DEPTH)
		e.version = VersionUint(v64)
	}
	return e.version
}

func parsePath(urlPtr *url.URL, prefix string) (endpoint Endpoint, clientErr, serverErr *deeperror.DeepError) {
	urlPath := strings.Trim(urlPtr.Path, "/")
	prefix = strings.TrimLeft(prefix, "/")

	if strings.HasPrefix(urlPath, prefix) == false {
		// is this an error?
		return Endpoint{}, deeperror.NewHTTPError(3475081071, "Invalid Prefix", nil, http.StatusNotFound), nil
	}
	urlPath = urlPath[len(prefix):]
	urlPath = strings.Trim(urlPath, "/")

	pathComponents := strings.Split(urlPath, "/")
	pathComponentsLen := len(pathComponents)

	// basic validation: should have at least a version and an entity
	if pathComponentsLen < 2 {
		return Endpoint{}, deeperror.NewHTTPError(3475081072, "Cannot parse endpoint path, insufficent number of path components", nil, http.StatusNotFound), nil
	}

	// parse version
	if pathComponentsLen >= 1 {
		s := strings.TrimLeft(pathComponents[0], "vV")
		s = strings.TrimLeft(s, "0")
		endpoint.VersionStr = s
	}

	// parse entity
	if pathComponentsLen >= 2 {
		endpoint.EntityName = pathComponents[1]
	}

	// parse pk and extra
	if pathComponentsLen >= 3 {
		// parse pk
		pkeyOrActionString := pathComponents[2]
		pkey, err := strconv.ParseInt(pkeyOrActionString, 10, 64)
		if err == nil {
			endpoint.PrimaryKey = pkey
			if pathComponentsLen >= 4 {
				endpoint.Action = pathComponents[3]
			}
		} else {
			//it's probably an action
			endpoint.Action = pkeyOrActionString
		}

		// parse extras
		endpoint.Extras = pathComponents[2:]
	}

	return
}
<commit_msg>export an endpoint parser for testing<commit_after>package grunway

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

	"github.com/amattn/deeperror"
)

// convenience struct holding all the stuff you usually want to know about an endpoint
type Endpoint struct {
	VersionStr string
	EntityName string
	PrimaryKey int64
	Action     string
	Extras     []string

	// internal only
	version        VersionUint
	versionConvErr error
}

// return a typed number, not a string
// cache value so we only do this once.
func (e *Endpoint) Version() VersionUint {
	if e.versionConvErr == nil && e.version == 0 {
		var v64 uint64
		v64, e.versionConvErr = strconv.ParseUint(e.VersionStr, 10, VERSION_BIT_DEPTH)
		e.version = VersionUint(v64)
	}
	return e.version
}

// exported for other packages to be able to unit test.
func ParsePathForTesting(urlPtr *url.URL, prefix string) (endpoint Endpoint, err error) {
	endpoint, clientErr, serverErr := parsePath(urlPtr, prefix)

	if serverErr != nil {
		err = serverErr
	}
	if clientErr != nil {
		err = clientErr
	}

	return endpoint, err
}

func parsePath(urlPtr *url.URL, prefix string) (endpoint Endpoint, clientErr, serverErr *deeperror.DeepError) {
	urlPath := strings.Trim(urlPtr.Path, "/")
	prefix = strings.TrimLeft(prefix, "/")

	if strings.HasPrefix(urlPath, prefix) == false {
		// is this an error?
		return Endpoint{}, deeperror.NewHTTPError(3475081071, "Invalid Prefix", nil, http.StatusNotFound), nil
	}
	urlPath = urlPath[len(prefix):]
	urlPath = strings.Trim(urlPath, "/")

	pathComponents := strings.Split(urlPath, "/")
	pathComponentsLen := len(pathComponents)

	// basic validation: should have at least a version and an entity
	if pathComponentsLen < 2 {
		return Endpoint{}, deeperror.NewHTTPError(3475081072, "Cannot parse endpoint path, insufficent number of path components", nil, http.StatusNotFound), nil
	}

	// parse version
	if pathComponentsLen >= 1 {
		s := strings.TrimLeft(pathComponents[0], "vV")
		s = strings.TrimLeft(s, "0")
		endpoint.VersionStr = s
	}

	// parse entity
	if pathComponentsLen >= 2 {
		endpoint.EntityName = pathComponents[1]
	}

	// parse pk and extra
	if pathComponentsLen >= 3 {
		// parse pk
		pkeyOrActionString := pathComponents[2]
		pkey, err := strconv.ParseInt(pkeyOrActionString, 10, 64)
		if err == nil {
			endpoint.PrimaryKey = pkey
			if pathComponentsLen >= 4 {
				endpoint.Action = pathComponents[3]
			}
		} else {
			//it's probably an action
			endpoint.Action = pkeyOrActionString
		}

		// parse extras
		endpoint.Extras = pathComponents[2:]
	}

	return
}
<|endoftext|>