<commit_before>//  Copyright (c) 2012 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package db

import (
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"

	"github.com/couchbaselabs/sync_gateway/base"
)

// Attachments shorter than this will be left in the JSON as base64 rather than being a separate
// MIME part.
const kMaxInlineAttachmentSize = 200

// JSON bodies smaller than this won't be GZip-encoded.
const kMinCompressedJSONSize = 300

// Key for retrieving an attachment from Couchbase.
type AttachmentKey string

// Given a CouchDB document body about to be stored in the database, goes through the _attachments
// dict, finds attachments with inline bodies, copies the bodies into the Couchbase db, and replaces
// the bodies with the 'digest' attributes which are the keys to retrieving them.
func (db *Database) storeAttachments(doc *document, body Body, generation int, parentRev string) error {
	var parentAttachments map[string]interface{}
	atts := BodyAttachments(body)
	for name, value := range atts {
		meta := value.(map[string]interface{})
		data, exists := meta["data"]
		if exists {
			// Attachment contains data, so store it in the db:
			attachment, err := decodeAttachment(data)
			if err != nil {
				return err
			}
			key, err := db.setAttachment(attachment)
			if err != nil {
				return err
			}
			delete(meta, "data")
			meta["stub"] = true
			meta["digest"] = string(key)
			meta["revpos"] = generation
			if meta["encoding"] == nil {
				meta["length"] = len(attachment)
				delete(meta, "encoded_length")
			} else {
				meta["encoded_length"] = len(attachment)
			}
		} else {
			// No data given; look it up from the parent revision.
			if parentAttachments == nil {
				parent, err := db.getAvailableRev(doc, parentRev)
				if err != nil {
					base.Warn("storeAttachments: no such parent rev %q to find %v", parentRev, meta)
					return err
				}
				parentAttachments, exists = parent["_attachments"].(map[string]interface{})
				if !exists {
					return base.HTTPErrorf(400, "Unknown attachment %s", name)
				}
			}
			parentAttachment := parentAttachments[name]
			if parentAttachment == nil {
				return base.HTTPErrorf(400, "Unknown attachment %s", name)
			}
			atts[name] = parentAttachment
		}
	}
	return nil
}

// Goes through a revisions '_attachments' map, loads attachments (by their 'digest' properties)
// and adds 'data' properties containing the data. The data is added as raw []byte; the JSON
// marshaler will convert that to base64.
// If minRevpos is > 0, then only attachments that have been changed in a revision of that
// generation or later are loaded.
func (db *Database) loadBodyAttachments(body Body, minRevpos int) error {
	for _, value := range BodyAttachments(body) {
		meta := value.(map[string]interface{})
		revpos, ok := base.ToInt64(meta["revpos"])
		if ok && revpos >= int64(minRevpos) {
			key := AttachmentKey(meta["digest"].(string))
			data, err := db.GetAttachment(key)
			if err != nil {
				return err
			}
			meta["data"] = data
			delete(meta, "stub")
		}
	}
	return nil
}

// Retrieves an attachment, base64-encoded, given its key.
func (db *Database) GetAttachment(key AttachmentKey) ([]byte, error) {
	return db.Bucket.GetRaw(attachmentKeyToString(key))
}

// Stores a base64-encoded attachment and returns the key to get it by.
func (db *Database) setAttachment(attachment []byte) (AttachmentKey, error) {
	key := AttachmentKey(sha1DigestKey(attachment))
	_, err := db.Bucket.AddRaw(attachmentKeyToString(key), 0, attachment)
	if err == nil {
		base.LogTo("Attach", "\tAdded attachment %q", key)
	}
	return key, err
}

//////// MIME MULTIPART:

// Parses a JSON MIME body, unmarshaling it into "into".
func ReadJSONFromMIME(headers http.Header, input io.Reader, into interface{}) error {
	contentType := headers.Get("Content-Type")
	if contentType != "" && !strings.HasPrefix(contentType, "application/json") {
		return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType)
	}

	switch headers.Get("Content-Encoding") {
	case "gzip":
		var err error
		if input, err = gzip.NewReader(input); err != nil {
			return err
		}
	case "":
		break
	default:
		return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Unsupported Content-Encoding; use gzip")
	}

	decoder := json.NewDecoder(input)
	if err := decoder.Decode(into); err != nil {
		base.Warn("Couldn't parse JSON in HTTP request: %v", err)
		return base.HTTPErrorf(http.StatusBadRequest, "Bad JSON")
	}
	return nil
}

type attInfo struct {
	name        string
	contentType string
	data        []byte
}

func writeJSONPart(writer *multipart.Writer, contentType string, body Body, compressed bool) (err error) {
	bytes, err := json.Marshal(body)
	if err != nil {
		return err
	}
	if len(bytes) < kMinCompressedJSONSize {
		compressed = false
	}

	partHeaders := textproto.MIMEHeader{}
	partHeaders.Set("Content-Type", contentType)
	if compressed {
		partHeaders.Set("Content-Encoding", "gzip")
	}
	part, err := writer.CreatePart(partHeaders)
	if err != nil {
		return err
	}

	if compressed {
		gz := gzip.NewWriter(part)
		_, err = gz.Write(bytes)
		gz.Close()
	} else {
		_, err = part.Write(bytes)
	}
	return
}

// Writes a revision to a MIME multipart writer, encoding large attachments as separate parts.
func (db *Database) WriteMultipartDocument(body Body, writer *multipart.Writer, compress bool) {
	// First extract the attachments that should follow:
	following := []attInfo{}
	for name, value := range BodyAttachments(body) {
		meta := value.(map[string]interface{})
		var info attInfo
		info.contentType, _ = meta["content_type"].(string)
		info.data, _ = decodeAttachment(meta["data"])
		if info.data != nil && len(info.data) > kMaxInlineAttachmentSize {
			info.name = name
			following = append(following, info)
			delete(meta, "data")
			meta["follows"] = true
		}
	}

	// Write the main JSON body:
	writeJSONPart(writer, "application/json", body, compress)

	// Write the following attachments
	for _, info := range following {
		partHeaders := textproto.MIMEHeader{}
		if info.contentType != "" {
			partHeaders.Set("Content-Type", info.contentType)
		}
		partHeaders.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", info.name))
		part, _ := writer.CreatePart(partHeaders)
		part.Write(info.data)
	}
}

// Adds a new part to the given multipart writer, containing the given revision.
// The revision will be written as a nested multipart body if it has attachments.
func (db *Database) WriteRevisionAsPart(revBody Body, isError bool, compress bool, writer *multipart.Writer) error {
	partHeaders := textproto.MIMEHeader{}
	docID, _ := revBody["_id"].(string)
	revID, _ := revBody["_rev"].(string)
	if len(docID) > 0 {
		partHeaders.Set("X-Doc-ID", docID)
		partHeaders.Set("X-Rev-ID", revID)
	}

	if hasInlineAttachments(revBody) {
		// Write as multipart, including attachments:
		// OPT: Find a way to do this w/o having to buffer the MIME body in memory!
		var buffer bytes.Buffer
		docWriter := multipart.NewWriter(&buffer)
		contentType := fmt.Sprintf("multipart/related; boundary=%q",
			docWriter.Boundary())
		partHeaders.Set("Content-Type", contentType)
		db.WriteMultipartDocument(revBody, docWriter, compress)
		docWriter.Close()
		content := bytes.TrimRight(buffer.Bytes(), "\r\n")

		part, err := writer.CreatePart(partHeaders)
		if err == nil {
			_, err = part.Write(content)
		}
		return err
	} else {
		// Write as JSON:
		contentType := "application/json"
		if isError {
			contentType += `; error="true"`
		}
		return writeJSONPart(writer, contentType, revBody, compress)
	}
}

func ReadMultipartDocument(reader *multipart.Reader) (Body, error) {
	// First read the main JSON document body:
	mainPart, err := reader.NextPart()
	if err != nil {
		return nil, err
	}
	var body Body
	err = ReadJSONFromMIME(http.Header(mainPart.Header), mainPart, &body)
	mainPart.Close()
	if err != nil {
		return nil, err
	}

	// Collect the attachments with a "follows" property, which will appear as MIME parts:
	followingAttachments := map[string]map[string]interface{}{}
	for name, value := range BodyAttachments(body) {
		if meta := value.(map[string]interface{}); meta["follows"] == true {
			followingAttachments[name] = meta
		}
	}

	// Subroutine to look up a following attachment given its digest. (I used to precompute a
	// map from digest->name, which was faster, but that broke down if there were multiple
	// attachments with the same contents! See #96)
	findFollowingAttachment := func(withDigest string) (string, map[string]interface{}) {
		for name, meta := range followingAttachments {
			if meta["follows"] == true {
				if digest, ok := meta["digest"].(string); ok && digest == withDigest {
					return name, meta
				}
			}
		}
		return "", nil
	}

	// Read the parts one by one:
	for i := 0; i < len(followingAttachments); i++ {
		part, err := reader.NextPart()
		if err != nil {
			if err == io.EOF {
				err = base.HTTPErrorf(http.StatusBadRequest,
					"Too few MIME parts: expected %d attachments, got %d",
					len(followingAttachments), i)
			}
			return nil, err
		}
		data, err := ioutil.ReadAll(part)
		part.Close()
		if err != nil {
			return nil, err
		}

		// Look up the attachment by its digest:
		digest := sha1DigestKey(data)
		name, meta := findFollowingAttachment(digest)
		if meta == nil {
			name, meta = findFollowingAttachment(md5DigestKey(data))
			if meta == nil {
				return nil, base.HTTPErrorf(http.StatusBadRequest,
					"MIME part #%d doesn't match any attachment", i+2)
			}
		}

		length, ok := base.ToInt64(meta["encoded_length"])
		if !ok {
			length, ok = base.ToInt64(meta["length"])
		}
		if ok {
			if length != int64(len(data)) {
				return nil, base.HTTPErrorf(http.StatusBadRequest, "Attachment length mismatch for %q: read %d bytes, should be %g", name, len(data), length)
			}
		}

		// Stuff the data into the attachment metadata and remove the "follows" property:
		delete(meta, "follows")
		meta["data"] = data
		meta["digest"] = digest
	}

	// Make sure there are no unused MIME parts:
	if _, err = reader.NextPart(); err != io.EOF {
		if err == nil {
			err = base.HTTPErrorf(http.StatusBadRequest,
				"Too many MIME parts (expected %d)", len(followingAttachments)+1)
		}
		return nil, err
	}

	return body, nil
}

//////// HELPERS:

func sha1DigestKey(data []byte) string {
	digester := sha1.New()
	digester.Write(data)
	return "sha1-" + base64.StdEncoding.EncodeToString(digester.Sum(nil))
}

func md5DigestKey(data []byte) string {
	digester := md5.New()
	digester.Write(data)
	return "md5-" + base64.StdEncoding.EncodeToString(digester.Sum(nil))
}

func BodyAttachments(body Body) map[string]interface{} {
	atts, _ := body["_attachments"].(map[string]interface{})
	return atts
}

func hasInlineAttachments(body Body) bool {
	for _, value := range BodyAttachments(body) {
		if meta, ok := value.(map[string]interface{}); ok && meta["data"] != nil {
			return true
		}
	}
	return false
}

func attachmentKeyToString(key AttachmentKey) string {
	return "_sync:att:" + string(key)
}

func decodeAttachment(att interface{}) ([]byte, error) {
	switch att := att.(type) {
	case string:
		return base64.StdEncoding.DecodeString(att)
	case []byte:
		return att, nil
	}
	return nil, base.HTTPErrorf(400, "invalid attachment data")
}
<commit_msg>Validate structure of _attachments property of incoming revisions<commit_after>//  Copyright (c) 2012 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package db

import (
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"

	"github.com/couchbaselabs/sync_gateway/base"
)

// Attachments shorter than this will be left in the JSON as base64 rather than being a separate
// MIME part.
const kMaxInlineAttachmentSize = 200

// JSON bodies smaller than this won't be GZip-encoded.
const kMinCompressedJSONSize = 300

// Key for retrieving an attachment from Couchbase.
type AttachmentKey string

// Given a CouchDB document body about to be stored in the database, goes through the _attachments
// dict, finds attachments with inline bodies, copies the bodies into the Couchbase db, and replaces
// the bodies with the 'digest' attributes which are the keys to retrieving them.
func (db *Database) storeAttachments(doc *document, body Body, generation int, parentRev string) error {
	var parentAttachments map[string]interface{}
	atts := BodyAttachments(body)
	if atts == nil && body["_attachments"] != nil {
		return base.HTTPErrorf(400, "Invalid _attachments")
	}
	for name, value := range atts {
		meta, ok := value.(map[string]interface{})
		if !ok {
			return base.HTTPErrorf(400, "Invalid _attachments")
		}
		data, exists := meta["data"]
		if exists {
			// Attachment contains data, so store it in the db:
			attachment, err := decodeAttachment(data)
			if err != nil {
				return err
			}
			key, err := db.setAttachment(attachment)
			if err != nil {
				return err
			}
			delete(meta, "data")
			meta["stub"] = true
			meta["digest"] = string(key)
			meta["revpos"] = generation
			if meta["encoding"] == nil {
				meta["length"] = len(attachment)
				delete(meta, "encoded_length")
			} else {
				meta["encoded_length"] = len(attachment)
			}
		} else {
			// No data given; look it up from the parent revision.
			if parentAttachments == nil {
				parent, err := db.getAvailableRev(doc, parentRev)
				if err != nil {
					base.Warn("storeAttachments: no such parent rev %q to find %v", parentRev, meta)
					return err
				}
				parentAttachments, exists = parent["_attachments"].(map[string]interface{})
				if !exists {
					return base.HTTPErrorf(400, "Unknown attachment %s", name)
				}
			}
			parentAttachment := parentAttachments[name]
			if parentAttachment == nil {
				return base.HTTPErrorf(400, "Unknown attachment %s", name)
			}
			atts[name] = parentAttachment
		}
	}
	return nil
}

// Goes through a revisions '_attachments' map, loads attachments (by their 'digest' properties)
// and adds 'data' properties containing the data. The data is added as raw []byte; the JSON
// marshaler will convert that to base64.
// If minRevpos is > 0, then only attachments that have been changed in a revision of that
// generation or later are loaded.
func (db *Database) loadBodyAttachments(body Body, minRevpos int) error {
	for _, value := range BodyAttachments(body) {
		meta := value.(map[string]interface{})
		revpos, ok := base.ToInt64(meta["revpos"])
		if ok && revpos >= int64(minRevpos) {
			key := AttachmentKey(meta["digest"].(string))
			data, err := db.GetAttachment(key)
			if err != nil {
				return err
			}
			meta["data"] = data
			delete(meta, "stub")
		}
	}
	return nil
}

// Retrieves an attachment, base64-encoded, given its key.
func (db *Database) GetAttachment(key AttachmentKey) ([]byte, error) {
	return db.Bucket.GetRaw(attachmentKeyToString(key))
}

// Stores a base64-encoded attachment and returns the key to get it by.
func (db *Database) setAttachment(attachment []byte) (AttachmentKey, error) {
	key := AttachmentKey(sha1DigestKey(attachment))
	_, err := db.Bucket.AddRaw(attachmentKeyToString(key), 0, attachment)
	if err == nil {
		base.LogTo("Attach", "\tAdded attachment %q", key)
	}
	return key, err
}

//////// MIME MULTIPART:

// Parses a JSON MIME body, unmarshaling it into "into".
func ReadJSONFromMIME(headers http.Header, input io.Reader, into interface{}) error {
	contentType := headers.Get("Content-Type")
	if contentType != "" && !strings.HasPrefix(contentType, "application/json") {
		return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType)
	}

	switch headers.Get("Content-Encoding") {
	case "gzip":
		var err error
		if input, err = gzip.NewReader(input); err != nil {
			return err
		}
	case "":
		break
	default:
		return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Unsupported Content-Encoding; use gzip")
	}

	decoder := json.NewDecoder(input)
	if err := decoder.Decode(into); err != nil {
		base.Warn("Couldn't parse JSON in HTTP request: %v", err)
		return base.HTTPErrorf(http.StatusBadRequest, "Bad JSON")
	}
	return nil
}

type attInfo struct {
	name        string
	contentType string
	data        []byte
}

func writeJSONPart(writer *multipart.Writer, contentType string, body Body, compressed bool) (err error) {
	bytes, err := json.Marshal(body)
	if err != nil {
		return err
	}
	if len(bytes) < kMinCompressedJSONSize {
		compressed = false
	}

	partHeaders := textproto.MIMEHeader{}
	partHeaders.Set("Content-Type", contentType)
	if compressed {
		partHeaders.Set("Content-Encoding", "gzip")
	}
	part, err := writer.CreatePart(partHeaders)
	if err != nil {
		return err
	}

	if compressed {
		gz := gzip.NewWriter(part)
		_, err = gz.Write(bytes)
		gz.Close()
	} else {
		_, err = part.Write(bytes)
	}
	return
}

// Writes a revision to a MIME multipart writer, encoding large attachments as separate parts.
func (db *Database) WriteMultipartDocument(body Body, writer *multipart.Writer, compress bool) {
	// First extract the attachments that should follow:
	following := []attInfo{}
	for name, value := range BodyAttachments(body) {
		meta := value.(map[string]interface{})
		var info attInfo
		info.contentType, _ = meta["content_type"].(string)
		info.data, _ = decodeAttachment(meta["data"])
		if info.data != nil && len(info.data) > kMaxInlineAttachmentSize {
			info.name = name
			following = append(following, info)
			delete(meta, "data")
			meta["follows"] = true
		}
	}

	// Write the main JSON body:
	writeJSONPart(writer, "application/json", body, compress)

	// Write the following attachments
	for _, info := range following {
		partHeaders := textproto.MIMEHeader{}
		if info.contentType != "" {
			partHeaders.Set("Content-Type", info.contentType)
		}
		partHeaders.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", info.name))
		part, _ := writer.CreatePart(partHeaders)
		part.Write(info.data)
	}
}

// Adds a new part to the given multipart writer, containing the given revision.
// The revision will be written as a nested multipart body if it has attachments.
func (db *Database) WriteRevisionAsPart(revBody Body, isError bool, compress bool, writer *multipart.Writer) error {
	partHeaders := textproto.MIMEHeader{}
	docID, _ := revBody["_id"].(string)
	revID, _ := revBody["_rev"].(string)
	if len(docID) > 0 {
		partHeaders.Set("X-Doc-ID", docID)
		partHeaders.Set("X-Rev-ID", revID)
	}

	if hasInlineAttachments(revBody) {
		// Write as multipart, including attachments:
		// OPT: Find a way to do this w/o having to buffer the MIME body in memory!
		var buffer bytes.Buffer
		docWriter := multipart.NewWriter(&buffer)
		contentType := fmt.Sprintf("multipart/related; boundary=%q",
			docWriter.Boundary())
		partHeaders.Set("Content-Type", contentType)
		db.WriteMultipartDocument(revBody, docWriter, compress)
		docWriter.Close()
		content := bytes.TrimRight(buffer.Bytes(), "\r\n")

		part, err := writer.CreatePart(partHeaders)
		if err == nil {
			_, err = part.Write(content)
		}
		return err
	} else {
		// Write as JSON:
		contentType := "application/json"
		if isError {
			contentType += `; error="true"`
		}
		return writeJSONPart(writer, contentType, revBody, compress)
	}
}

func ReadMultipartDocument(reader *multipart.Reader) (Body, error) {
	// First read the main JSON document body:
	mainPart, err := reader.NextPart()
	if err != nil {
		return nil, err
	}
	var body Body
	err = ReadJSONFromMIME(http.Header(mainPart.Header), mainPart, &body)
	mainPart.Close()
	if err != nil {
		return nil, err
	}

	// Collect the attachments with a "follows" property, which will appear as MIME parts:
	followingAttachments := map[string]map[string]interface{}{}
	for name, value := range BodyAttachments(body) {
		if meta := value.(map[string]interface{}); meta["follows"] == true {
			followingAttachments[name] = meta
		}
	}

	// Subroutine to look up a following attachment given its digest. (I used to precompute a
	// map from digest->name, which was faster, but that broke down if there were multiple
	// attachments with the same contents! See #96)
	findFollowingAttachment := func(withDigest string) (string, map[string]interface{}) {
		for name, meta := range followingAttachments {
			if meta["follows"] == true {
				if digest, ok := meta["digest"].(string); ok && digest == withDigest {
					return name, meta
				}
			}
		}
		return "", nil
	}

	// Read the parts one by one:
	for i := 0; i < len(followingAttachments); i++ {
		part, err := reader.NextPart()
		if err != nil {
			if err == io.EOF {
				err = base.HTTPErrorf(http.StatusBadRequest,
					"Too few MIME parts: expected %d attachments, got %d",
					len(followingAttachments), i)
			}
			return nil, err
		}
		data, err := ioutil.ReadAll(part)
		part.Close()
		if err != nil {
			return nil, err
		}

		// Look up the attachment by its digest:
		digest := sha1DigestKey(data)
		name, meta := findFollowingAttachment(digest)
		if meta == nil {
			name, meta = findFollowingAttachment(md5DigestKey(data))
			if meta == nil {
				return nil, base.HTTPErrorf(http.StatusBadRequest,
					"MIME part #%d doesn't match any attachment", i+2)
			}
		}

		length, ok := base.ToInt64(meta["encoded_length"])
		if !ok {
			length, ok = base.ToInt64(meta["length"])
		}
		if ok {
			if length != int64(len(data)) {
				return nil, base.HTTPErrorf(http.StatusBadRequest, "Attachment length mismatch for %q: read %d bytes, should be %g", name, len(data), length)
			}
		}

		// Stuff the data into the attachment metadata and remove the "follows" property:
		delete(meta, "follows")
		meta["data"] = data
		meta["digest"] = digest
	}

	// Make sure there are no unused MIME parts:
	if _, err = reader.NextPart(); err != io.EOF {
		if err == nil {
			err = base.HTTPErrorf(http.StatusBadRequest,
				"Too many MIME parts (expected %d)", len(followingAttachments)+1)
		}
		return nil, err
	}

	return body, nil
}

//////// HELPERS:

func sha1DigestKey(data []byte) string {
	digester := sha1.New()
	digester.Write(data)
	return "sha1-" + base64.StdEncoding.EncodeToString(digester.Sum(nil))
}

func md5DigestKey(data []byte) string {
	digester := md5.New()
	digester.Write(data)
	return "md5-" + base64.StdEncoding.EncodeToString(digester.Sum(nil))
}

func BodyAttachments(body Body) map[string]interface{} {
	atts, _ := body["_attachments"].(map[string]interface{})
	return atts
}

func hasInlineAttachments(body Body) bool {
	for _, value := range BodyAttachments(body) {
		if meta, ok := value.(map[string]interface{}); ok && meta["data"] != nil {
			return true
		}
	}
	return false
}

func attachmentKeyToString(key AttachmentKey) string {
	return "_sync:att:" + string(key)
}

func decodeAttachment(att interface{}) ([]byte, error) {
	switch att := att.(type) {
	case string:
		return base64.StdEncoding.DecodeString(att)
	case []byte:
		return att, nil
	}
	return nil, base.HTTPErrorf(400, "invalid attachment data")
}
<|endoftext|>


<commit_before>package pir

import (
	"fmt"
	"testing"

	"github.com/willf/bitset"
)

func generateData(size int) []byte {
	data := make([]byte, size)
	for i := 0; i < size; i++ {
		data[i] = byte(i)
	}
	return data
}

func HelperTestShardRead(t *testing.T, shard Shard, bucketSize int) {
	reqs := make([]bitset.BitSet, 3)
	reqs[0].SetTo(1, true)
	reqs[1].SetTo(0, true)
	reqs[2].SetTo(0, true)
	reqs[2].SetTo(1, true)
	reqs[2].SetTo(2, true)

	response, err := shard.Read(reqs)

	if err != nil {
		t.Fatalf("error calling shard.Read: %v\n", err)
	}

	if response == nil {
		t.Fatalf("no response received")
	}

	// Check request 0
	res := response[0:bucketSize]
	for i := 0; i < bucketSize; i++ {
		if res[i] != byte(bucketSize+i) {
			t.Fatalf("response0 is incorrect. byte %d was %d, not '%d'", i, res[i], bucketSize+i)
		}
	}
	// Check request 1
	res = response[bucketSize : 2*bucketSize]
	for i := 0; i < bucketSize; i++ {
		if res[i] != byte(i) {
			t.Fatalf("response1 is incorrect. byte %d was %d, not '%d'", i, res[i], i)
		}
	}
	// Check request 2
	res = response[2*bucketSize : 3*bucketSize]
	for i := 0; i < bucketSize; i++ {
		expected := i ^ (bucketSize + i) ^ (2*bucketSize + i)
		if res[i] != byte(expected) {
			t.Fatalf("response is incorrect. byte %d was %d, not '%d'", i, res[i], expected)
		}
	}

}

func TestShardCPURead(t *testing.T) {
	fmt.Printf("TestShardCPURead: ...\n")
	numMessages := 32
	messageSize := 2
	depth := 2 // 16 buckets
	shard, err := NewShardCPU("testshard", depth*messageSize, generateData(numMessages*messageSize), 0)
	if err != nil {
		t.Fatalf("cannot create new ShardCPU v0: error=%v\n", err)
	}
	HelperTestShardRead(t, shard, depth*messageSize)
	shard, err = NewShardCPU("testshard", depth*messageSize, generateData(numMessages*messageSize), 1)
	if err != nil {
		t.Fatalf("cannot create new ShardCPU v1: error=%v\n", err)
	}
	HelperTestShardRead(t, shard, depth*messageSize)
	fmt.Printf("... done \n")
}

/**
func BenchmarkPir(b *testing.B) {
	cellLength := 1024
	cellCount := 2048
	batchSize := 8
	if os.Getenv("PIR_CELL_LENGTH") != "" {
		cellLength, _ = strconv.Atoi(os.Getenv("PIR_CELL_LENGTH"))
	}
	if os.Getenv("PIR_CELL_COUNT") != "" {
		cellCount, _ = strconv.Atoi(os.Getenv("PIR_CELL_COUNT"))
	}
	if os.Getenv("PIR_BATCH_SIZE") != "" {
		batchSize, _ = strconv.Atoi(os.Getenv("PIR_BATCH_SIZE"))
	}

	sockName := getSocket()
	status := make(chan int)
	go CreateMockServer(status, sockName)
	<-status

	pirServer, err := Connect(sockName)
	if err != nil {
		b.Error(err)
		return
	}

	pirServer.Configure(cellLength, cellCount, batchSize)
	db, err := pirServer.GetDB()
	if err != nil {
		b.Error(err)
		return
	}
	for x := range db.DB {
		db.DB[x] = byte(x)
	}

	pirServer.SetDB(db)

	responseChan := make(chan []byte)
	masks := make([]byte, cellCount*batchSize/8)
	for i := 0; i < len(masks); i++ {
		masks[i] = byte(rand.Int())
	}

	b.ResetTimer()

	signalChan := make(chan int)
	go func() {
		for j := 0; j < b.N; j++ {
			response := <-responseChan
			b.SetBytes(int64(len(response)))
		}
		signalChan <- 1
	}()

	for i := 0; i < b.N; i++ {
		err := pirServer.Read(masks, responseChan)

		if err != nil {
			b.Error(err)
		}
	}

	<-signalChan

	pirServer.Disconnect()

	status <- 1
	<-status
}
**/
<commit_msg>cleanup shard tests<commit_after>package pir

import (
	"fmt"
	"testing"

	"github.com/willf/bitset"
)

func generateData(size int) []byte {
	data := make([]byte, size)
	for i := 0; i < size; i++ {
		data[i] = byte(i)
	}
	return data
}

func HelperTestShardRead(t *testing.T, shard Shard) {
	fmt.Printf("TestShardCPURead: %s ...\n", shard.GetName())

	reqs := make([]bitset.BitSet, 3)
	reqs[0].SetTo(1, true)
	reqs[1].SetTo(0, true)
	reqs[2].SetTo(0, true)
	reqs[2].SetTo(1, true)
	reqs[2].SetTo(2, true)

	if shard.GetNumBuckets() < 3 {
		t.Fatalf("test misconfigured. shard has %d buckets, needs %d\n", shard.GetNumBuckets(), 3)
	}

	response, err := shard.Read(reqs)

	if err != nil {
		t.Fatalf("error calling shard.Read: %v\n", err)
	}

	if response == nil {
		t.Fatalf("no response received")
	}

	bucketSize := shard.GetBucketSize()
	data := shard.GetData()
	// Check request 0
	res := response[0:bucketSize]
	for i := 0; i < bucketSize; i++ {
		if res[i] != data[bucketSize+i] {
			t.Fatalf("response0 is incorrect. byte %d was %d, not '%d'\n", i, res[i], bucketSize+i)
		}
	}
	// Check request 1
	res = response[bucketSize : 2*bucketSize]
	for i := 0; i < bucketSize; i++ {
		if res[i] != data[i] {
			t.Fatalf("response1 is incorrect. byte %d was %d, not '%d'\n", i, res[i], i)
		}
	}
	// Check request 2
	res = response[2*bucketSize : 3*bucketSize]
	for i := 0; i < bucketSize; i++ {
		expected := data[i] ^ data[bucketSize+i] ^ data[2*bucketSize+i]
		if res[i] != expected {
			t.Fatalf("response is incorrect. byte %d was %d, not '%d'\n", i, res[i], expected)
		}
	}

	err = shard.Free()
	if err != nil {
		t.Fatalf("error freeing shard: %v\n", err)
	}

	fmt.Printf("... done \n")

}

func TestShardCPURead(t *testing.T) {
	numMessages := 32
	messageSize := 2
	depth := 2 // 16 buckets
	shard, err := NewShardCPU("shardcpuv0", depth*messageSize, generateData(numMessages*messageSize), 0)
	if err != nil {
		t.Fatalf("cannot create new ShardCPU v0: error=%v\n", err)
	}
	HelperTestShardRead(t, shard)
	shard, err = NewShardCPU("shardcpuv1", depth*messageSize, generateData(numMessages*messageSize), 1)
	if err != nil {
		t.Fatalf("cannot create new ShardCPU v1: error=%v\n", err)
	}
	HelperTestShardRead(t, shard)
}

/**
func BenchmarkPir(b *testing.B) {
	cellLength := 1024
	cellCount := 2048
	batchSize := 8
	if os.Getenv("PIR_CELL_LENGTH") != "" {
		cellLength, _ = strconv.Atoi(os.Getenv("PIR_CELL_LENGTH"))
	}
	if os.Getenv("PIR_CELL_COUNT") != "" {
		cellCount, _ = strconv.Atoi(os.Getenv("PIR_CELL_COUNT"))
	}
	if os.Getenv("PIR_BATCH_SIZE") != "" {
		batchSize, _ = strconv.Atoi(os.Getenv("PIR_BATCH_SIZE"))
	}

	sockName := getSocket()
	status := make(chan int)
	go CreateMockServer(status, sockName)
	<-status

	pirServer, err := Connect(sockName)
	if err != nil {
		b.Error(err)
		return
	}

	pirServer.Configure(cellLength, cellCount, batchSize)
	db, err := pirServer.GetDB()
	if err != nil {
		b.Error(err)
		return
	}
	for x := range db.DB {
		db.DB[x] = byte(x)
	}

	pirServer.SetDB(db)

	responseChan := make(chan []byte)
	masks := make([]byte, cellCount*batchSize/8)
	for i := 0; i < len(masks); i++ {
		masks[i] = byte(rand.Int())
	}

	b.ResetTimer()

	signalChan := make(chan int)
	go func() {
		for j := 0; j < b.N; j++ {
			response := <-responseChan
			b.SetBytes(int64(len(response)))
		}
		signalChan <- 1
	}()

	for i := 0; i < b.N; i++ {
		err := pirServer.Read(masks, responseChan)

		if err != nil {
			b.Error(err)
		}
	}

	<-signalChan

	pirServer.Disconnect()

	status <- 1
	<-status
}
**/
<|endoftext|>


<commit_before>package handler

import (
	"net/http"
	"net/url"
	"io/ioutil"
	"errors"
	"log"
	"strconv"
	"encoding/json"
	"strings"
	"github.com/gorilla/context"
	"github.com/spf13/viper"
	"github.com/untoldone/bloomapi/api"
	"github.com/satori/go.uuid"
)

func YourChartCreateHandler (w http.ResponseWriter, req *http.Request) {
	yourchartUrl := viper.GetString("yourchartUrl")
	yourchartLabUrl := viper.GetString("yourchartLabUrl")
	username := req.FormValue("username")
	password := req.FormValue("password")
	orgId := req.FormValue("orgId")

	apiKey, ok := context.Get(req, "api_key").(string)

	enabled, err := YourchartEnabled(apiKey)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	if (!ok || !enabled) {
		api.Render(w, req, 404, map[string]string{
			"name": "Source Not Found",
			"message": "Please contact support@bloomapi.com if this is in error",
		})
		return
	}

	jobId := uuid.NewV4().String()

	// Yourchart
	postResp, err := http.PostForm(yourchartUrl, url.Values{
			"username": {username},
			"password": {password},
			"orgId": {orgId},
		})
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}
	defer postResp.Body.Close()
	body, err := ioutil.ReadAll(postResp.Body)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var decoded map[string]interface{}
	decoder := json.NewDecoder(strings.NewReader(string(body)))
	err = decoder.Decode(&decoded)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var upstreamJobId string
	rawUpstreamJobId, ok := decoded["statusId"]
	if !ok {
		log.Println(errors.New("Unable to get new job's statusId"))
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	// Yourchart Lab!!
	postLabResp, err := http.PostForm(yourchartLabUrl, url.Values{
			"username": {username},
			"password": {password},
			"orgId": {orgId},
		})
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}
	defer postLabResp.Body.Close()
	labBody, err := ioutil.ReadAll(postLabResp.Body)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var labDecoded map[string]interface{}
	labDecoder := json.NewDecoder(strings.NewReader(string(labBody)))
	err = labDecoder.Decode(&labDecoded)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var upstreamLabJobId string
	rawUpstreamLabJobId, ok := labDecoded["statusId"]
	if !ok {
		log.Println(errors.New("Unable to get new job's statusId"))
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	switch rawUpstreamLabJobId := rawUpstreamLabJobId.(type) {
	case string:
		upstreamLabJobId = rawUpstreamLabJobId
	case float64:
		upstreamLabJobId = strconv.FormatFloat(rawUpstreamLabJobId, 'f', 0, 64)
	}

	switch rawUpstreamJobId := rawUpstreamJobId.(type) {
	case string:
		upstreamJobId = rawUpstreamJobId
	case float64:
		upstreamJobId = strconv.FormatFloat(rawUpstreamJobId, 'f', 0, 64)
	}

	err = YourchartAuthorize(apiKey, jobId, upstreamJobId, upstreamLabJobId)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	api.AddFeature(req, "handler:yourchart:create")

	api.Render(w, req, http.StatusOK, map[string]string{
	    "statusId": jobId,
		})
}<commit_msg>outdated yourchart handler<commit_after>package handler

import (
	"net/http"
	"net/url"
	"io/ioutil"
	"errors"
	"log"
	"strconv"
	"encoding/json"
	"strings"
	"github.com/gorilla/context"
	"github.com/spf13/viper"
	"github.com/untoldone/bloomapi/api"
	"github.com/satori/go.uuid"
)

func YourChartCreateHandler (w http.ResponseWriter, req *http.Request) {
	yourchartUrl := viper.GetString("yourchartUrl")
	//yourchartLabUrl := viper.GetString("yourchartLabUrl")
	username := req.FormValue("username")
	password := req.FormValue("password")
	orgId := req.FormValue("orgId")

	apiKey, ok := context.Get(req, "api_key").(string)

	enabled, err := YourchartEnabled(apiKey)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	if (!ok || !enabled) {
		api.Render(w, req, 404, map[string]string{
			"name": "Source Not Found",
			"message": "Please contact support@bloomapi.com if this is in error",
		})
		return
	}

	jobId := uuid.NewV4().String()

	// Yourchart
	postResp, err := http.PostForm(yourchartUrl, url.Values{
			"username": {username},
			"password": {password},
			"orgId": {orgId},
		})
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}
	defer postResp.Body.Close()
	body, err := ioutil.ReadAll(postResp.Body)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var decoded map[string]interface{}
	decoder := json.NewDecoder(strings.NewReader(string(body)))
	err = decoder.Decode(&decoded)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var upstreamJobId string
	rawUpstreamJobId, ok := decoded["statusId"]
	if !ok {
		log.Println(errors.New("Unable to get new job's statusId"))
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	// Yourchart Lab!!
	/*postLabResp, err := http.PostForm(yourchartLabUrl, url.Values{
			"username": {username},
			"password": {password},
			"orgId": {orgId},
		})
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}
	defer postLabResp.Body.Close()
	labBody, err := ioutil.ReadAll(postLabResp.Body)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var labDecoded map[string]interface{}
	labDecoder := json.NewDecoder(strings.NewReader(string(labBody)))
	err = labDecoder.Decode(&labDecoded)
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	var upstreamLabJobId string
	rawUpstreamLabJobId, ok := labDecoded["statusId"]
	if !ok {
		log.Println(errors.New("Unable to get new job's statusId"))
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	switch rawUpstreamLabJobId := rawUpstreamLabJobId.(type) {
	case string:
		upstreamLabJobId = rawUpstreamLabJobId
	case float64:
		upstreamLabJobId = strconv.FormatFloat(rawUpstreamLabJobId, 'f', 0, 64)
	}*/

	switch rawUpstreamJobId := rawUpstreamJobId.(type) {
	case string:
		upstreamJobId = rawUpstreamJobId
	case float64:
		upstreamJobId = strconv.FormatFloat(rawUpstreamJobId, 'f', 0, 64)
	}

	err = YourchartAuthorize(apiKey, jobId, upstreamJobId, "")
	if err != nil {
		log.Println(err)
		api.Render(w, req, http.StatusInternalServerError, "Internal Server Error")
		return
	}

	api.AddFeature(req, "handler:yourchart:create")

	api.Render(w, req, http.StatusOK, map[string]string{
	    "statusId": jobId,
		})
}<|endoftext|>


<commit_before>package api

import (
	log "github.com/Sirupsen/logrus"
	"github.com/hellofresh/janus/pkg/cors"
	"github.com/hellofresh/janus/pkg/middleware"
	"github.com/hellofresh/janus/pkg/oauth"
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/hellofresh/janus/pkg/router"
	"github.com/hellofresh/janus/pkg/store"
	"github.com/ulule/limiter"
)

// Loader is responsible for loading all apis form a datastore and configure them in a register
type Loader struct {
	register *proxy.Register
	store    store.Store
	accessor *middleware.DatabaseAccessor
	manager  *oauth.Manager
	debug    bool
}

// NewLoader creates a new instance of the api manager
func NewLoader(register *proxy.Register, store store.Store, accessor *middleware.DatabaseAccessor, manager *oauth.Manager, debug bool) *Loader {
	return &Loader{register, store, accessor, manager, debug}
}

// Load loads all api specs from a datasource
func (m *Loader) Load() {
	specs := m.getAPISpecs()
	m.RegisterApis(specs)
}

// RegisterApis load application middleware
func (m *Loader) RegisterApis(apiSpecs []*Spec) {
	log.Debug("Loading API configurations")

	for _, referenceSpec := range apiSpecs {
		m.RegisterApi(referenceSpec)
	}
}

func (m *Loader) RegisterApi(referenceSpec *Spec) {
	var skip bool

	//Validates the proxy
	skip = proxy.Validate(referenceSpec.Proxy)
	if false == referenceSpec.Active {
		log.Debug("API is not active, skiping...")
		skip = false
	}

	if skip {
		var handlers []router.Constructor
		if referenceSpec.RateLimit.Enabled {
			rate, err := limiter.NewRateFromFormatted(referenceSpec.RateLimit.Limit)
			if err != nil {
				panic(err)
			}

			limiterStore, err := m.store.ToLimiterStore(referenceSpec.Slug)
			if err != nil {
				panic(err)
			}

			handlers = append(handlers, limiter.NewHTTPMiddleware(limiter.NewLimiter(limiterStore, rate)).Handler)
		} else {
			log.Debug("Rate limit is not enabled")
		}

		if referenceSpec.CorsMeta.Enabled {
			handlers = append(handlers, cors.NewMiddleware(referenceSpec.CorsMeta, m.debug).Handler)
		} else {
			log.Debug("CORS is not enabled")
		}

		if referenceSpec.UseOauth2 {
			handlers = append(handlers, oauth.NewKeyExistsMiddleware(m.manager, referenceSpec.OAuthServerID).Handler)
		} else {
			log.Debug("OAuth2 is not enabled")
		}

		m.register.Add(proxy.NewRoute(referenceSpec.Proxy, handlers...))
		log.Debug("API registered")
	} else {
		log.Error("Listen path is empty, skipping...")
	}
}

//getAPISpecs Load application specs from datasource
func (m *Loader) getAPISpecs() []*Spec {
	log.Debug("Using App Configuration from Mongo DB")
	repo, err := NewMongoAppRepository(m.accessor.Session.DB(""))
	if err != nil {
		log.Panic(err)
	}

	definitions, err := repo.FindAll()
	if err != nil {
		log.Panic(err)
	}

	var specs []*Spec
	for _, definition := range definitions {
		spec := new(Spec)
		spec.Definition = definition
		specs = append(specs, spec)
	}

	return specs
}
<commit_msg>Added the rate limit middleware<commit_after>package api

import (
	log "github.com/Sirupsen/logrus"
	"github.com/hellofresh/janus/pkg/cors"
	"github.com/hellofresh/janus/pkg/middleware"
	"github.com/hellofresh/janus/pkg/oauth"
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/hellofresh/janus/pkg/router"
	"github.com/hellofresh/janus/pkg/store"
	"github.com/ulule/limiter"
)

// Loader is responsible for loading all apis form a datastore and configure them in a register
type Loader struct {
	register *proxy.Register
	store    store.Store
	accessor *middleware.DatabaseAccessor
	manager  *oauth.Manager
	debug    bool
}

// NewLoader creates a new instance of the api manager
func NewLoader(register *proxy.Register, store store.Store, accessor *middleware.DatabaseAccessor, manager *oauth.Manager, debug bool) *Loader {
	return &Loader{register, store, accessor, manager, debug}
}

// Load loads all api specs from a datasource
func (m *Loader) Load() {
	specs := m.getAPISpecs()
	m.RegisterApis(specs)
}

// RegisterApis load application middleware
func (m *Loader) RegisterApis(apiSpecs []*Spec) {
	log.Debug("Loading API configurations")

	for _, referenceSpec := range apiSpecs {
		m.RegisterApi(referenceSpec)
	}
}

func (m *Loader) RegisterApi(referenceSpec *Spec) {
	var skip bool

	//Validates the proxy
	skip = proxy.Validate(referenceSpec.Proxy)
	if false == referenceSpec.Active {
		log.Debug("API is not active, skiping...")
		skip = false
	}

	if skip {
		var handlers []router.Constructor
		if referenceSpec.RateLimit.Enabled {
			rate, err := limiter.NewRateFromFormatted(referenceSpec.RateLimit.Limit)
			if err != nil {
				panic(err)
			}

			limiterStore, err := m.store.ToLimiterStore(referenceSpec.ID.String())
			if err != nil {
				panic(err)
			}

			handlers = append(handlers, limiter.NewHTTPMiddleware(limiter.NewLimiter(limiterStore, rate)).Handler)
			handlers = append(handlers, middleware.NewRateLimitLogger().Handler)
		} else {
			log.Debug("Rate limit is not enabled")
		}

		if referenceSpec.CorsMeta.Enabled {
			handlers = append(handlers, cors.NewMiddleware(referenceSpec.CorsMeta, m.debug).Handler)
		} else {
			log.Debug("CORS is not enabled")
		}

		if referenceSpec.UseOauth2 {
			handlers = append(handlers, oauth.NewKeyExistsMiddleware(m.manager, referenceSpec.OAuthServerID).Handler)
		} else {
			log.Debug("OAuth2 is not enabled")
		}

		m.register.Add(proxy.NewRoute(referenceSpec.Proxy, handlers...))
		log.Debug("API registered")
	} else {
		log.Error("Listen path is empty, skipping...")
	}
}

//getAPISpecs Load application specs from datasource
func (m *Loader) getAPISpecs() []*Spec {
	log.Debug("Using App Configuration from Mongo DB")
	repo, err := NewMongoAppRepository(m.accessor.Session.DB(""))
	if err != nil {
		log.Panic(err)
	}

	definitions, err := repo.FindAll()
	if err != nil {
		log.Panic(err)
	}

	var specs []*Spec
	for _, definition := range definitions {
		spec := new(Spec)
		spec.Definition = definition
		specs = append(specs, spec)
	}

	return specs
}
<|endoftext|>


<commit_before>package security

import (
	"context"
	"fmt"
	"strings"
)

type (
	// BasicScheme represents the BasicAuth security scheme.
	// It consists of a simple username and password.
	BasicScheme struct {
		// Name is the scheme name defined in the design.
		Name string
	}

	// APIKeyScheme represents the API key security scheme.
	// It consists of a key which is used in authentication.
	APIKeyScheme struct {
		// Name is the scheme name defined in the design.
		Name string
	}

	// JWTScheme represents an API key based scheme with support
	// for scopes.
	JWTScheme struct {
		// Name is the scheme name defined in the design.
		Name string
		// Scopes holds a list of scopes for the scheme.
		Scopes []string
		// RequiredScopes holds a list of scopes which are required
		// by the scheme. It is a subset of Scopes field.
		RequiredScopes []string
	}

	// OAuth2Scheme represents the oauth2 security scheme.
	OAuth2Scheme struct {
		// Name is the scheme name defined in the design.
		Name string
		// Scopes holds a list of scopes for the scheme.
		Scopes []string
		// RequiredScopes holds a list of scopes which are required
		// by the scheme. It is a subset of Scopes field.
		RequiredScopes []string
		// Flows determine the oauth2 flows.
		Flows []*OAuthFlow
	}

	// OAuthFlow represents the OAuth2 flow defined by the scheme.
	OAuthFlow struct {
		// Type is the type of grant.
		Type string
		// AuthorizationURL to be used for implicit or authorizationCode flows.
		AuthorizationURL string
		// TokenURL to be used for password, clientCredentials or authorizationCode flows.
		TokenURL string
		// RefreshURL to be used for obtaining refresh token.
		RefreshURL string
	}

	// AuthBasicFunc is the function type that implements the basic auth
	// scheme of using username and password.
	AuthBasicFunc func(ctx context.Context, user, pass string, s *BasicScheme) (context.Context, error)

	// AuthAPIKeyFunc is the function type that implements the API key
	// scheme of using an API key.
	AuthAPIKeyFunc func(ctx context.Context, key string, s *APIKeyScheme) (context.Context, error)

	// AuthOAuth2Func is the function type that implements the OAuth2
	// scheme of using an OAuth2 token.
	AuthOAuth2Func func(ctx context.Context, token string, s *OAuth2Scheme) (context.Context, error)

	// AuthJWTFunc is the function type that implements the JWT
	// scheme of using a JWT token.
	AuthJWTFunc func(ctx context.Context, token string, s *JWTScheme) (context.Context, error)
)

// Validate returns a non-nil error if scopes does not contain all of
// OAuth2 scheme's required scopes.
func (s *OAuth2Scheme) Validate(scopes []string) error {
	return validateScopes(s.RequiredScopes, scopes)
}

// Validate returns a non-nil error if scopes does not contain all of
// JWT scheme's required scopes.
func (s *JWTScheme) Validate(scopes []string) error {
	return validateScopes(s.RequiredScopes, scopes)
}

func validateScopes(expected, actual []string) error {
	var missing []string
	for _, r := range expected {
		found := false
		for _, s := range actual {
			if s == r {
				found = true
				break
			}
		}
		if !found {
			missing = append(missing, r)
		}
	}
	if len(missing) == 0 {
		return nil
	}
	return fmt.Errorf("missing sopes: %s", strings.Join(missing, ", "))
}
<commit_msg>Scopes typo (#1908)<commit_after>package security

import (
	"context"
	"fmt"
	"strings"
)

type (
	// BasicScheme represents the BasicAuth security scheme.
	// It consists of a simple username and password.
	BasicScheme struct {
		// Name is the scheme name defined in the design.
		Name string
	}

	// APIKeyScheme represents the API key security scheme.
	// It consists of a key which is used in authentication.
	APIKeyScheme struct {
		// Name is the scheme name defined in the design.
		Name string
	}

	// JWTScheme represents an API key based scheme with support
	// for scopes.
	JWTScheme struct {
		// Name is the scheme name defined in the design.
		Name string
		// Scopes holds a list of scopes for the scheme.
		Scopes []string
		// RequiredScopes holds a list of scopes which are required
		// by the scheme. It is a subset of Scopes field.
		RequiredScopes []string
	}

	// OAuth2Scheme represents the oauth2 security scheme.
	OAuth2Scheme struct {
		// Name is the scheme name defined in the design.
		Name string
		// Scopes holds a list of scopes for the scheme.
		Scopes []string
		// RequiredScopes holds a list of scopes which are required
		// by the scheme. It is a subset of Scopes field.
		RequiredScopes []string
		// Flows determine the oauth2 flows.
		Flows []*OAuthFlow
	}

	// OAuthFlow represents the OAuth2 flow defined by the scheme.
	OAuthFlow struct {
		// Type is the type of grant.
		Type string
		// AuthorizationURL to be used for implicit or authorizationCode flows.
		AuthorizationURL string
		// TokenURL to be used for password, clientCredentials or authorizationCode flows.
		TokenURL string
		// RefreshURL to be used for obtaining refresh token.
		RefreshURL string
	}

	// AuthBasicFunc is the function type that implements the basic auth
	// scheme of using username and password.
	AuthBasicFunc func(ctx context.Context, user, pass string, s *BasicScheme) (context.Context, error)

	// AuthAPIKeyFunc is the function type that implements the API key
	// scheme of using an API key.
	AuthAPIKeyFunc func(ctx context.Context, key string, s *APIKeyScheme) (context.Context, error)

	// AuthOAuth2Func is the function type that implements the OAuth2
	// scheme of using an OAuth2 token.
	AuthOAuth2Func func(ctx context.Context, token string, s *OAuth2Scheme) (context.Context, error)

	// AuthJWTFunc is the function type that implements the JWT
	// scheme of using a JWT token.
	AuthJWTFunc func(ctx context.Context, token string, s *JWTScheme) (context.Context, error)
)

// Validate returns a non-nil error if scopes does not contain all of
// OAuth2 scheme's required scopes.
func (s *OAuth2Scheme) Validate(scopes []string) error {
	return validateScopes(s.RequiredScopes, scopes)
}

// Validate returns a non-nil error if scopes does not contain all of
// JWT scheme's required scopes.
func (s *JWTScheme) Validate(scopes []string) error {
	return validateScopes(s.RequiredScopes, scopes)
}

func validateScopes(expected, actual []string) error {
	var missing []string
	for _, r := range expected {
		found := false
		for _, s := range actual {
			if s == r {
				found = true
				break
			}
		}
		if !found {
			missing = append(missing, r)
		}
	}
	if len(missing) == 0 {
		return nil
	}
	return fmt.Errorf("missing scopes: %s", strings.Join(missing, ", "))
}
<|endoftext|>


<commit_before>package cli

// the cli package contains urfave/cli related structs that help make up
// the command line for buildah commands. it resides here so other projects
// that vendor in this code can use them too.

import (
	"fmt"
	"os"
	"strings"

	"github.com/containers/buildah"
	"github.com/containers/buildah/util"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

var (
	usernsFlags = []cli.Flag{
		cli.StringFlag{
			Name:  "userns",
			Usage: "'container', `path` of user namespace to join, or 'host'",
		},
		cli.StringSliceFlag{
			Name:  "userns-uid-map",
			Usage: "`containerID:hostID:length` UID mapping to use in user namespace",
		},
		cli.StringSliceFlag{
			Name:  "userns-gid-map",
			Usage: "`containerID:hostID:length` GID mapping to use in user namespace",
		},
		cli.StringFlag{
			Name:  "userns-uid-map-user",
			Usage: "`name` of entries from /etc/subuid to use to set user namespace UID mapping",
		},
		cli.StringFlag{
			Name:  "userns-gid-map-group",
			Usage: "`name` of entries from /etc/subgid to use to set user namespace GID mapping",
		},
	}

	NamespaceFlags = []cli.Flag{
		cli.StringFlag{
			Name:  string(specs.IPCNamespace),
			Usage: "'container', `path` of IPC namespace to join, or 'host'",
		},
		cli.StringFlag{
			Name:  string(specs.NetworkNamespace) + ", net",
			Usage: "'container', `path` of network namespace to join, or 'host'",
		},
		cli.StringFlag{
			Name:  "cni-config-dir",
			Usage: "`directory` of CNI configuration files",
			Value: util.DefaultCNIConfigDir,
		},
		cli.StringFlag{
			Name:  "cni-plugin-path",
			Usage: "`path` of CNI network plugins",
			Value: util.DefaultCNIPluginPath,
		},
		cli.StringFlag{
			Name:  string(specs.PIDNamespace),
			Usage: "'container', `path` of PID namespace to join, or 'host'",
		},
		cli.StringFlag{
			Name:  string(specs.UTSNamespace),
			Usage: "'container', `path` of UTS namespace to join, or 'host'",
		},
	}

	LayerFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "layers",
			Usage: fmt.Sprintf("cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override. (default %t)", UseLayers()),
		},
	}

	BudFlags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "annotation",
			Usage: "Set metadata for an image (default [])",
		},
		cli.StringFlag{
			Name:  "authfile",
			Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
		},
		cli.StringSliceFlag{
			Name:  "build-arg",
			Usage: "`argument=value` to supply to the builder",
		},
		cli.StringFlag{
			Name:  "cache-from",
			Usage: "Images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.",
		},
		cli.StringFlag{
			Name:  "cert-dir",
			Value: "",
			Usage: "use certificates at the specified path to access the registry",
		},
		cli.BoolFlag{
			Name:  "compress",
			Usage: "This is legacy option, which has no effect on the image",
		},
		cli.StringFlag{
			Name:  "creds",
			Value: "",
			Usage: "use `[username[:password]]` for accessing the registry",
		},
		cli.BoolFlag{
			Name:  "disable-content-trust",
			Usage: "This is a Docker specific option and is a NOOP",
		},
		cli.StringSliceFlag{
			Name:  "file, f",
			Usage: "`pathname or URL` of a Dockerfile",
		},
		cli.BoolFlag{
			Name:  "force-rm",
			Usage: "Always remove intermediate containers after a build, even if the build is unsuccessful.",
		},
		cli.StringFlag{
			Name:  "format",
			Usage: "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.",
			Value: DefaultFormat(),
		},
		cli.StringFlag{
			Name:  "iidfile",
			Usage: "`file` to write the image ID to",
		},
		cli.StringFlag{
			Name:  "isolation",
			Usage: "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.",
			Value: DefaultIsolation(),
		},
		cli.StringSliceFlag{
			Name:  "label",
			Usage: "Set metadata for an image (default [])",
		},
		cli.BoolFlag{
			Name:  "no-cache",
			Usage: "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.",
		},
		cli.StringFlag{
			Name:  "logfile",
			Usage: "log to `file` instead of stdout/stderr",
		},
		cli.IntFlag{
			Name:  "loglevel",
			Usage: "adjust logging level (range from -2 to 3)",
		},
		cli.BoolTFlag{
			Name:  "pull",
			Usage: "pull the image if not present",
		},
		cli.BoolFlag{
			Name:  "pull-always",
			Usage: "pull the image, even if a version is present",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "refrain from announcing build instructions and image read/write progress",
		},
		cli.BoolTFlag{
			Name:  "rm",
			Usage: "Remove intermediate containers after a successful build (default true)",
		},
		cli.StringFlag{
			Name:  "runtime",
			Usage: "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.",
			Value: util.Runtime(),
		},
		cli.StringSliceFlag{
			Name:  "runtime-flag",
			Usage: "add global flags for the container runtime",
		},
		cli.StringFlag{
			Name:  "signature-policy",
			Usage: "`pathname` of signature policy file (not usually used)",
		},
		cli.BoolFlag{
			Name:  "squash",
			Usage: "Squash newly built layers into a single new layer. The build process does not currently support caching so this is a NOOP.",
		},
		cli.StringSliceFlag{
			Name:  "tag, t",
			Usage: "tagged `name` to apply to the built image",
		},
		cli.BoolTFlag{
			Name:  "tls-verify",
			Usage: "require HTTPS and verify certificates when accessing the registry",
		},
	}

	FromAndBudFlags = append(append([]cli.Flag{
		cli.StringSliceFlag{
			Name:  "add-host",
			Usage: "add a custom host-to-IP mapping (`host:ip`) (default [])",
		},
		cli.StringSliceFlag{
			Name:  "cap-add",
			Usage: "add the specified capability when running (default [])",
		},
		cli.StringSliceFlag{
			Name:  "cap-drop",
			Usage: "drop the specified capability when running (default [])",
		},
		cli.StringFlag{
			Name:  "cgroup-parent",
			Usage: "optional parent cgroup for the container",
		},
		cli.Uint64Flag{
			Name:  "cpu-period",
			Usage: "limit the CPU CFS (Completely Fair Scheduler) period",
		},
		cli.Int64Flag{
			Name:  "cpu-quota",
			Usage: "limit the CPU CFS (Completely Fair Scheduler) quota",
		},
		cli.Uint64Flag{
			Name:  "cpu-shares, c",
			Usage: "CPU shares (relative weight)",
		},
		cli.StringFlag{
			Name:  "cpuset-cpus",
			Usage: "CPUs in which to allow execution (0-3, 0,1)",
		},
		cli.StringFlag{
			Name:  "cpuset-mems",
			Usage: "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.",
		},
		cli.StringFlag{
			Name:  "memory, m",
			Usage: "memory limit (format: <number>[<unit>], where unit = b, k, m or g)",
		},
		cli.StringFlag{
			Name:  "memory-swap",
			Usage: "swap limit equal to memory plus swap: '-1' to enable unlimited swap",
		},
		cli.StringSliceFlag{
			Name:  "security-opt",
			Usage: "security options (default [])",
		},
		cli.StringFlag{
			Name:  "shm-size",
			Usage: "size of '/dev/shm'. The format is `<number><unit>`.",
			Value: "65536k",
		},
		cli.StringSliceFlag{
			Name:  "ulimit",
			Usage: "ulimit options (default [])",
		},
		cli.StringSliceFlag{
			Name:  "volume, v",
			Usage: "bind mount a volume into the container (default [])",
		},
	}, usernsFlags...), NamespaceFlags...)
)

// UseLayers returns true if BUILDAH_LAYERS is set to "1" or "true"
// otherwise it returns false
func UseLayers() bool {
	layers := os.Getenv("BUILDAH_LAYERS")
	if strings.ToLower(layers) == "true" || layers == "1" {
		return true
	}
	return false
}

// DefaultFormat returns the default image format
func DefaultFormat() string {
	format := os.Getenv("BUILDAH_FORMAT")
	if format != "" {
		return format
	}
	return buildah.OCI
}

// DefaultIsolation returns the default image format
func DefaultIsolation() string {
	isolation := os.Getenv("BUILDAH_ISOLATION")
	if isolation != "" {
		return isolation
	}
	return buildah.OCI
}

func VerifyFlagsArgsOrder(args []string) error {
	for _, arg := range args {
		if strings.HasPrefix(arg, "-") {
			return errors.Errorf("No options (%s) can be specified after the image or container name", arg)
		}
	}
	return nil
}
<commit_msg>from: fix isolation option<commit_after>package cli

// the cli package contains urfave/cli related structs that help make up
// the command line for buildah commands. it resides here so other projects
// that vendor in this code can use them too.

import (
	"fmt"
	"os"
	"strings"

	"github.com/containers/buildah"
	"github.com/containers/buildah/util"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

var (
	usernsFlags = []cli.Flag{
		cli.StringFlag{
			Name:  "userns",
			Usage: "'container', `path` of user namespace to join, or 'host'",
		},
		cli.StringSliceFlag{
			Name:  "userns-uid-map",
			Usage: "`containerID:hostID:length` UID mapping to use in user namespace",
		},
		cli.StringSliceFlag{
			Name:  "userns-gid-map",
			Usage: "`containerID:hostID:length` GID mapping to use in user namespace",
		},
		cli.StringFlag{
			Name:  "userns-uid-map-user",
			Usage: "`name` of entries from /etc/subuid to use to set user namespace UID mapping",
		},
		cli.StringFlag{
			Name:  "userns-gid-map-group",
			Usage: "`name` of entries from /etc/subgid to use to set user namespace GID mapping",
		},
	}

	NamespaceFlags = []cli.Flag{
		cli.StringFlag{
			Name:  string(specs.IPCNamespace),
			Usage: "'container', `path` of IPC namespace to join, or 'host'",
		},
		cli.StringFlag{
			Name:  string(specs.NetworkNamespace) + ", net",
			Usage: "'container', `path` of network namespace to join, or 'host'",
		},
		cli.StringFlag{
			Name:  "cni-config-dir",
			Usage: "`directory` of CNI configuration files",
			Value: util.DefaultCNIConfigDir,
		},
		cli.StringFlag{
			Name:  "cni-plugin-path",
			Usage: "`path` of CNI network plugins",
			Value: util.DefaultCNIPluginPath,
		},
		cli.StringFlag{
			Name:  string(specs.PIDNamespace),
			Usage: "'container', `path` of PID namespace to join, or 'host'",
		},
		cli.StringFlag{
			Name:  string(specs.UTSNamespace),
			Usage: "'container', `path` of UTS namespace to join, or 'host'",
		},
	}

	LayerFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "layers",
			Usage: fmt.Sprintf("cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override. (default %t)", UseLayers()),
		},
	}

	BudFlags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "annotation",
			Usage: "Set metadata for an image (default [])",
		},
		cli.StringFlag{
			Name:  "authfile",
			Usage: "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
		},
		cli.StringSliceFlag{
			Name:  "build-arg",
			Usage: "`argument=value` to supply to the builder",
		},
		cli.StringFlag{
			Name:  "cache-from",
			Usage: "Images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.",
		},
		cli.StringFlag{
			Name:  "cert-dir",
			Value: "",
			Usage: "use certificates at the specified path to access the registry",
		},
		cli.BoolFlag{
			Name:  "compress",
			Usage: "This is legacy option, which has no effect on the image",
		},
		cli.StringFlag{
			Name:  "creds",
			Value: "",
			Usage: "use `[username[:password]]` for accessing the registry",
		},
		cli.BoolFlag{
			Name:  "disable-content-trust",
			Usage: "This is a Docker specific option and is a NOOP",
		},
		cli.StringSliceFlag{
			Name:  "file, f",
			Usage: "`pathname or URL` of a Dockerfile",
		},
		cli.BoolFlag{
			Name:  "force-rm",
			Usage: "Always remove intermediate containers after a build, even if the build is unsuccessful.",
		},
		cli.StringFlag{
			Name:  "format",
			Usage: "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.",
			Value: DefaultFormat(),
		},
		cli.StringFlag{
			Name:  "iidfile",
			Usage: "`file` to write the image ID to",
		},
		cli.StringSliceFlag{
			Name:  "label",
			Usage: "Set metadata for an image (default [])",
		},
		cli.BoolFlag{
			Name:  "no-cache",
			Usage: "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.",
		},
		cli.StringFlag{
			Name:  "logfile",
			Usage: "log to `file` instead of stdout/stderr",
		},
		cli.IntFlag{
			Name:  "loglevel",
			Usage: "adjust logging level (range from -2 to 3)",
		},
		cli.BoolTFlag{
			Name:  "pull",
			Usage: "pull the image if not present",
		},
		cli.BoolFlag{
			Name:  "pull-always",
			Usage: "pull the image, even if a version is present",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "refrain from announcing build instructions and image read/write progress",
		},
		cli.BoolTFlag{
			Name:  "rm",
			Usage: "Remove intermediate containers after a successful build (default true)",
		},
		cli.StringFlag{
			Name:  "runtime",
			Usage: "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.",
			Value: util.Runtime(),
		},
		cli.StringSliceFlag{
			Name:  "runtime-flag",
			Usage: "add global flags for the container runtime",
		},
		cli.StringFlag{
			Name:  "signature-policy",
			Usage: "`pathname` of signature policy file (not usually used)",
		},
		cli.BoolFlag{
			Name:  "squash",
			Usage: "Squash newly built layers into a single new layer. The build process does not currently support caching so this is a NOOP.",
		},
		cli.StringSliceFlag{
			Name:  "tag, t",
			Usage: "tagged `name` to apply to the built image",
		},
		cli.BoolTFlag{
			Name:  "tls-verify",
			Usage: "require HTTPS and verify certificates when accessing the registry",
		},
	}

	FromAndBudFlags = append(append([]cli.Flag{
		cli.StringSliceFlag{
			Name:  "add-host",
			Usage: "add a custom host-to-IP mapping (`host:ip`) (default [])",
		},
		cli.StringSliceFlag{
			Name:  "cap-add",
			Usage: "add the specified capability when running (default [])",
		},
		cli.StringSliceFlag{
			Name:  "cap-drop",
			Usage: "drop the specified capability when running (default [])",
		},
		cli.StringFlag{
			Name:  "cgroup-parent",
			Usage: "optional parent cgroup for the container",
		},
		cli.Uint64Flag{
			Name:  "cpu-period",
			Usage: "limit the CPU CFS (Completely Fair Scheduler) period",
		},
		cli.Int64Flag{
			Name:  "cpu-quota",
			Usage: "limit the CPU CFS (Completely Fair Scheduler) quota",
		},
		cli.Uint64Flag{
			Name:  "cpu-shares, c",
			Usage: "CPU shares (relative weight)",
		},
		cli.StringFlag{
			Name:  "cpuset-cpus",
			Usage: "CPUs in which to allow execution (0-3, 0,1)",
		},
		cli.StringFlag{
			Name:  "cpuset-mems",
			Usage: "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.",
		},
		cli.StringFlag{
			Name:  "isolation",
			Usage: "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.",
			Value: DefaultIsolation(),
		},
		cli.StringFlag{
			Name:  "memory, m",
			Usage: "memory limit (format: <number>[<unit>], where unit = b, k, m or g)",
		},
		cli.StringFlag{
			Name:  "memory-swap",
			Usage: "swap limit equal to memory plus swap: '-1' to enable unlimited swap",
		},
		cli.StringSliceFlag{
			Name:  "security-opt",
			Usage: "security options (default [])",
		},
		cli.StringFlag{
			Name:  "shm-size",
			Usage: "size of '/dev/shm'. The format is `<number><unit>`.",
			Value: "65536k",
		},
		cli.StringSliceFlag{
			Name:  "ulimit",
			Usage: "ulimit options (default [])",
		},
		cli.StringSliceFlag{
			Name:  "volume, v",
			Usage: "bind mount a volume into the container (default [])",
		},
	}, usernsFlags...), NamespaceFlags...)
)

// UseLayers returns true if BUILDAH_LAYERS is set to "1" or "true"
// otherwise it returns false
func UseLayers() bool {
	layers := os.Getenv("BUILDAH_LAYERS")
	if strings.ToLower(layers) == "true" || layers == "1" {
		return true
	}
	return false
}

// DefaultFormat returns the default image format
func DefaultFormat() string {
	format := os.Getenv("BUILDAH_FORMAT")
	if format != "" {
		return format
	}
	return buildah.OCI
}

// DefaultIsolation returns the default image format
func DefaultIsolation() string {
	isolation := os.Getenv("BUILDAH_ISOLATION")
	if isolation != "" {
		return isolation
	}
	return buildah.OCI
}

func VerifyFlagsArgsOrder(args []string) error {
	for _, arg := range args {
		if strings.HasPrefix(arg, "-") {
			return errors.Errorf("No options (%s) can be specified after the image or container name", arg)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package env

import "io/ioutil"

func WriteScriptTo(fileName string, contents string) error {
	return ioutil.WriteFile(fileName, []byte(contents), 0744)
}

func ExecuteScript(scriptPath string) (string, error) {
	return ExecuteCommand("bash", scriptPath)
}
<commit_msg>Change fileName to filename in pkg/env<commit_after>package env

import "io/ioutil"

func WriteScriptTo(filename string, contents string) error {
	return ioutil.WriteFile(filename, []byte(contents), 0744)
}

func ExecuteScript(scriptPath string) (string, error) {
	return ExecuteCommand("bash", scriptPath)
}
<|endoftext|>


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

// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package os

import (
	"runtime"
	"sync/atomic"
	"syscall"
)

// File represents an open file descriptor.
type File struct {
	*file
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	fd      int
	name    string
	dirinfo *dirInfo // nil unless directory being read
	nepipe  int32    // number of consecutive EPIPE in Write
}

// Fd returns the integer Unix file descriptor referencing the open file.
func (f *File) Fd() uintptr {
	if f == nil {
		return ^(uintptr(0))
	}
	return uintptr(f.fd)
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
	fdi := int(fd)
	if fdi < 0 {
		return nil
	}
	f := &File{&file{fd: fdi, name: name}}
	runtime.SetFinalizer(f.file, (*file).close)
	return f
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
	buf  []byte // buffer for directory I/O
	nbuf int    // length of buf; return value from Getdirentries
	bufp int    // location of next record in buf.
}

func epipecheck(file *File, e error) {
	if e == syscall.EPIPE {
		if atomic.AddInt32(&file.nepipe, 1) >= 10 {
			sigpipe()
		}
	} else {
		atomic.StoreInt32(&file.nepipe, 0)
	}
}

// DevNull is the name of the operating system's ``null device.''
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"

// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec_unix.go.
	if !supportsCloseOnExec {
		syscall.CloseOnExec(r)
	}

	return NewFile(uintptr(r), name), nil
}

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
	if f == nil {
		return ErrInvalid
	}
	return f.file.close()
}

func (file *file) close() error {
	if file == nil || file.fd < 0 {
		return syscall.EINVAL
	}
	var err error
	if e := syscall.Close(file.fd); e != nil {
		err = &PathError{"close", file.name, e}
	}
	file.fd = -1 // so it can't be closed again

	// no need for a finalizer anymore
	runtime.SetFinalizer(file, nil)
	return err
}

// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (f *File) Stat() (fi FileInfo, err error) {
	if f == nil {
		return nil, ErrInvalid
	}
	var stat syscall.Stat_t
	err = syscall.Fstat(f.fd, &stat)
	if err != nil {
		return nil, &PathError{"stat", f.name, err}
	}
	return fileInfoFromStat(&stat, f.name), nil
}

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(name string) (fi FileInfo, err error) {
	var stat syscall.Stat_t
	err = syscall.Stat(name, &stat)
	if err != nil {
		return nil, &PathError{"stat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
func Lstat(name string) (fi FileInfo, err error) {
	var stat syscall.Stat_t
	err = syscall.Lstat(name, &stat)
	if err != nil {
		return nil, &PathError{"lstat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

func (f *File) readdir(n int) (fi []FileInfo, err error) {
	dirname := f.name
	if dirname == "" {
		dirname = "."
	}
	names, err := f.Readdirnames(n)
	fi = make([]FileInfo, 0, len(names))
	for _, filename := range names {
		fip, lerr := lstat(dirname + "/" + filename)
		if IsNotExist(lerr) {
			// File disappeared between readdir + stat.
			// Just treat it as if it didn't exist.
			continue
		}
		if lerr != nil {
			return fi, lerr
		}
		fi = append(fi, fip)
	}
	return fi, err
}

// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	return syscall.Read(f.fd, b)
}

// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to 0.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	return syscall.Pread(f.fd, b, off)
}

// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	for {
		m, err := syscall.Write(f.fd, b)
		n += m

		// If the syscall wrote some data but not all (short write)
		// or it returned EINTR, then assume it stopped early for
		// reasons that are uninteresting to the caller, and try again.
		if 0 < m && m < len(b) || err == syscall.EINTR {
			b = b[m:]
			continue
		}

		return n, err
	}
}

// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	return syscall.Pwrite(f.fd, b, off)
}

// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	return syscall.Seek(f.fd, offset, whence)
}

// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
// If there is an error, it will be of type *PathError.
func Truncate(name string, size int64) error {
	if e := syscall.Truncate(name, size); e != nil {
		return &PathError{"truncate", name, e}
	}
	return nil
}

// Remove removes the named file or directory.
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if e == nil {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if e1 == nil {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, e}
}

// basename removes trailing slashes and the leading directory name from path name
func basename(name string) string {
	i := len(name) - 1
	// Remove trailing slashes
	for ; i > 0 && name[i] == '/'; i-- {
		name = name[:i]
	}
	// Remove leading directory name
	for i--; i >= 0; i-- {
		if name[i] == '/' {
			name = name[i+1:]
			break
		}
	}

	return name
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	dir := Getenv("TMPDIR")
	if dir == "" {
		dir = "/tmp"
	}
	return dir
}
<commit_msg>os: cap reads and writes to 2GB on Darwin and FreeBSD<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package os

import (
	"runtime"
	"sync/atomic"
	"syscall"
)

// File represents an open file descriptor.
type File struct {
	*file
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	fd      int
	name    string
	dirinfo *dirInfo // nil unless directory being read
	nepipe  int32    // number of consecutive EPIPE in Write
}

// Fd returns the integer Unix file descriptor referencing the open file.
func (f *File) Fd() uintptr {
	if f == nil {
		return ^(uintptr(0))
	}
	return uintptr(f.fd)
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
	fdi := int(fd)
	if fdi < 0 {
		return nil
	}
	f := &File{&file{fd: fdi, name: name}}
	runtime.SetFinalizer(f.file, (*file).close)
	return f
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
	buf  []byte // buffer for directory I/O
	nbuf int    // length of buf; return value from Getdirentries
	bufp int    // location of next record in buf.
}

func epipecheck(file *File, e error) {
	if e == syscall.EPIPE {
		if atomic.AddInt32(&file.nepipe, 1) >= 10 {
			sigpipe()
		}
	} else {
		atomic.StoreInt32(&file.nepipe, 0)
	}
}

// DevNull is the name of the operating system's ``null device.''
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"

// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec_unix.go.
	if !supportsCloseOnExec {
		syscall.CloseOnExec(r)
	}

	return NewFile(uintptr(r), name), nil
}

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
	if f == nil {
		return ErrInvalid
	}
	return f.file.close()
}

func (file *file) close() error {
	if file == nil || file.fd < 0 {
		return syscall.EINVAL
	}
	var err error
	if e := syscall.Close(file.fd); e != nil {
		err = &PathError{"close", file.name, e}
	}
	file.fd = -1 // so it can't be closed again

	// no need for a finalizer anymore
	runtime.SetFinalizer(file, nil)
	return err
}

// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
func (f *File) Stat() (fi FileInfo, err error) {
	if f == nil {
		return nil, ErrInvalid
	}
	var stat syscall.Stat_t
	err = syscall.Fstat(f.fd, &stat)
	if err != nil {
		return nil, &PathError{"stat", f.name, err}
	}
	return fileInfoFromStat(&stat, f.name), nil
}

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(name string) (fi FileInfo, err error) {
	var stat syscall.Stat_t
	err = syscall.Stat(name, &stat)
	if err != nil {
		return nil, &PathError{"stat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
// describes the symbolic link.  Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
func Lstat(name string) (fi FileInfo, err error) {
	var stat syscall.Stat_t
	err = syscall.Lstat(name, &stat)
	if err != nil {
		return nil, &PathError{"lstat", name, err}
	}
	return fileInfoFromStat(&stat, name), nil
}

func (f *File) readdir(n int) (fi []FileInfo, err error) {
	dirname := f.name
	if dirname == "" {
		dirname = "."
	}
	names, err := f.Readdirnames(n)
	fi = make([]FileInfo, 0, len(names))
	for _, filename := range names {
		fip, lerr := lstat(dirname + "/" + filename)
		if IsNotExist(lerr) {
			// File disappeared between readdir + stat.
			// Just treat it as if it didn't exist.
			continue
		}
		if lerr != nil {
			return fi, lerr
		}
		fi = append(fi, fip)
	}
	return fi, err
}

// Darwin and FreeBSD can't read or write 2GB+ at a time,
// even on 64-bit systems. See golang.org/issue/7812.
const (
	needsMaxRW = runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
	maxRW      = 2<<30 - 1
)

// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return syscall.Read(f.fd, b)
}

// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to nil.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return syscall.Pread(f.fd, b, off)
}

// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	for {
		bcap := b
		if needsMaxRW && len(bcap) > maxRW {
			bcap = bcap[:maxRW]
		}
		m, err := syscall.Write(f.fd, bcap)
		n += m

		// If the syscall wrote some data but not all (short write)
		// or it returned EINTR, then assume it stopped early for
		// reasons that are uninteresting to the caller, and try again.
		if 0 < m && m < len(bcap) || err == syscall.EINTR {
			b = b[m:]
			continue
		}

		if needsMaxRW && len(bcap) != len(b) && err == nil {
			b = b[m:]
			continue
		}

		return n, err
	}
}

// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	if needsMaxRW && len(b) > maxRW {
		b = b[:maxRW]
	}
	return syscall.Pwrite(f.fd, b, off)
}

// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	return syscall.Seek(f.fd, offset, whence)
}

// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
// If there is an error, it will be of type *PathError.
func Truncate(name string, size int64) error {
	if e := syscall.Truncate(name, size); e != nil {
		return &PathError{"truncate", name, e}
	}
	return nil
}

// Remove removes the named file or directory.
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
	// System call interface forces us to know
	// whether name is a file or directory.
	// Try both: it is cheaper on average than
	// doing a Stat plus the right one.
	e := syscall.Unlink(name)
	if e == nil {
		return nil
	}
	e1 := syscall.Rmdir(name)
	if e1 == nil {
		return nil
	}

	// Both failed: figure out which error to return.
	// OS X and Linux differ on whether unlink(dir)
	// returns EISDIR, so can't use that.  However,
	// both agree that rmdir(file) returns ENOTDIR,
	// so we can use that to decide which error is real.
	// Rmdir might also return ENOTDIR if given a bad
	// file path, like /etc/passwd/foo, but in that case,
	// both errors will be ENOTDIR, so it's okay to
	// use the error from unlink.
	if e1 != syscall.ENOTDIR {
		e = e1
	}
	return &PathError{"remove", name, e}
}

// basename removes trailing slashes and the leading directory name from path name
func basename(name string) string {
	i := len(name) - 1
	// Remove trailing slashes
	for ; i > 0 && name[i] == '/'; i-- {
		name = name[:i]
	}
	// Remove leading directory name
	for i--; i >= 0; i-- {
		if name[i] == '/' {
			name = name[i+1:]
			break
		}
	}

	return name
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	dir := Getenv("TMPDIR")
	if dir == "" {
		dir = "/tmp"
	}
	return dir
}
<|endoftext|>


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

package strconv

// FormatUint returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
func FormatUint(i uint64, base int) string {
	_, s := formatBits(nil, i, base, false, false)
	return s
}

// FormatInt returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
func FormatInt(i int64, base int) string {
	_, s := formatBits(nil, uint64(i), base, i < 0, false)
	return s
}

// Itoa is shorthand for FormatInt(i, 10).
func Itoa(i int) string {
	return FormatInt(int64(i), 10)
}

// AppendInt appends the string form of the integer i,
// as generated by FormatInt, to dst and returns the extended buffer.
func AppendInt(dst []byte, i int64, base int) []byte {
	dst, _ = formatBits(dst, uint64(i), base, i < 0, true)
	return dst
}

// AppendUint appends the string form of the unsigned integer i,
// as generated by FormatUint, to dst and returns the extended buffer.
func AppendUint(dst []byte, i uint64, base int) []byte {
	dst, _ = formatBits(dst, i, base, false, true)
	return dst
}

const (
	digits   = "0123456789abcdefghijklmnopqrstuvwxyz"
	digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
	digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
)

var shifts = [len(digits) + 1]uint{
	1 << 1: 1,
	1 << 2: 2,
	1 << 3: 3,
	1 << 4: 4,
	1 << 5: 5,
}

// formatBits computes the string representation of u in the given base.
// If neg is set, u is treated as negative int64 value. If append_ is
// set, the string is appended to dst and the resulting byte slice is
// returned as the first result value; otherwise the string is returned
// as the second result value.
//
func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) {
	if base < 2 || base > len(digits) {
		panic("strconv: illegal AppendInt/FormatInt base")
	}
	// 2 <= base && base <= len(digits)

	var a [64 + 1]byte // +1 for sign of 64bit value in base 2
	i := len(a)

	if neg {
		u = -u
	}

	// convert bits
	if base == 10 {
		// common case: use constants for / and % because
		// the compiler can optimize it into a multiply+shift,
		// and unroll loop
		for u >= 100 {
			i -= 2
			q := u / 100
			j := uintptr(u - q*100)
			a[i+1] = digits01[j]
			a[i+0] = digits10[j]
			u = q
		}
		if u >= 10 {
			i--
			q := u / 10
			a[i] = digits[uintptr(u-q*10)]
			u = q
		}

	} else if s := shifts[base]; s > 0 {
		// base is power of 2: use shifts and masks instead of / and %
		b := uint64(base)
		m := uintptr(b) - 1 // == 1<<s - 1
		for u >= b {
			i--
			a[i] = digits[uintptr(u)&m]
			u >>= s
		}

	} else {
		// general case
		b := uint64(base)
		for u >= b {
			i--
			a[i] = digits[uintptr(u%b)]
			u /= b
		}
	}

	// u < base
	i--
	a[i] = digits[uintptr(u)]

	// add sign, if any
	if neg {
		i--
		a[i] = '-'
	}

	if append_ {
		d = append(dst, a[i:]...)
		return
	}
	s = string(a[i:])
	return
}
<commit_msg>atoi  注释<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strconv

// FormatUint returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
func FormatUint(i uint64, base int) string {
	_, s := formatBits(nil, i, base, false, false)
	return s
}

// FormatInt returns the string representation of i in the given base,
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
// for digit values >= 10.
func FormatInt(i int64, base int) string {
	_, s := formatBits(nil, uint64(i), base, i < 0, false)
	return s
}

// Itoa is shorthand for FormatInt(i, 10).
func Itoa(i int) string {
	return FormatInt(int64(i), 10)
}

// AppendInt appends the string form of the integer i,
// as generated by FormatInt, to dst and returns the extended buffer.
func AppendInt(dst []byte, i int64, base int) []byte {
	dst, _ = formatBits(dst, uint64(i), base, i < 0, true)
	return dst
}

// AppendUint appends the string form of the unsigned integer i,
// as generated by FormatUint, to dst and returns the extended buffer.
func AppendUint(dst []byte, i uint64, base int) []byte {
	dst, _ = formatBits(dst, i, base, false, true)
	return dst
}

const (
	digits   = "0123456789abcdefghijklmnopqrstuvwxyz"
	digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
	digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
)

// 记录了某个位置是 2 的几次幂，这样，可以方便查出 base 属于 2 的几次幂。
// 比如：shifts[32] == 5
// 以下数组分别是在 2、4、8、16、32 处设置了相应值，其他位置都为 0
var shifts = [len(digits) + 1]uint{
	1 << 1: 1,
	1 << 2: 2,
	1 << 3: 3,
	1 << 4: 4,
	1 << 5: 5,
}

// formatBits computes the string representation of u in the given base.
// If neg is set, u is treated as negative int64 value. If append_ is
// set, the string is appended to dst and the resulting byte slice is
// returned as the first result value; otherwise the string is returned
// as the second result value.
//
func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) {
	if base < 2 || base > len(digits) {
		panic("strconv: illegal AppendInt/FormatInt base")
	}
	// 2 <= base && base <= len(digits)

	var a [64 + 1]byte // +1 for sign of 64bit value in base 2
	i := len(a)

	if neg {
		u = -u
	}

	// convert bits
	if base == 10 {
		// common case: use constants for / and % because
		// the compiler can optimize it into a multiply+shift,
		// and unroll loop
		for u >= 100 {
			i -= 2
			q := u / 100
			j := uintptr(u - q*100)
			a[i+1] = digits01[j]
			a[i+0] = digits10[j]
			u = q
		}
		if u >= 10 {
			i--
			q := u / 10
			a[i] = digits[uintptr(u-q*10)]
			u = q
		}

	} else if s := shifts[base]; s > 0 {
		// base is power of 2: use shifts and masks instead of / and %
		b := uint64(base)
		m := uintptr(b) - 1 // == 1<<s - 1
		for u >= b {
			i--
			a[i] = digits[uintptr(u)&m]
			u >>= s
		}

	} else {
		// general case
		b := uint64(base)
		for u >= b {
			i--
			a[i] = digits[uintptr(u%b)]
			u /= b
		}
	}

	// u < base
	i--
	a[i] = digits[uintptr(u)]

	// add sign, if any
	if neg {
		i--
		a[i] = '-'
	}

	if append_ {
		d = append(dst, a[i:]...)
		return
	}
	s = string(a[i:])
	return
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"net/http"

	"github.com/rs/zerolog"

	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/service"
)

//// CreateMovieService creates a Movie
//type CreateMovieService interface {
//	Create(ctx context.Context, r *service.CreateMovieRequest, u user.User) (service.MovieResponse, error)
//}
//
//// UpdateMovieService is a service for updating a Movie
//type UpdateMovieService interface {
//	Update(ctx context.Context, r *service.UpdateMovieRequest, u user.User) (service.MovieResponse, error)
//}
//
//// DeleteMovieService is a service for deleting a Movie
//type DeleteMovieService interface {
//	Delete(ctx context.Context, extlID string) (service.DeleteMovieResponse, error)
//}
//
//// FindMovieService interface reads a Movie form the database
//type FindMovieService interface {
//	FindMovieByID(ctx context.Context, extlID string) (service.MovieResponse, error)
//	FindAllMovies(ctx context.Context) ([]service.MovieResponse, error)
//}

// CreateOrgService creates an Org
type CreateOrgService interface {
	Create(ctx context.Context, r *service.CreateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
}

// UpdateOrgService updates an Org
type UpdateOrgService interface {
	Update(ctx context.Context, r *service.UpdateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
}

// FindOrgService retrieves Org information from the datastore
type FindOrgService interface {
	FindAll(ctx context.Context) ([]service.OrgResponse, error)
	FindByExternalID(ctx context.Context, extlID string) (service.OrgResponse, error)
}

// CreateAppService creates an App
type CreateAppService interface {
	Create(ctx context.Context, r *service.CreateAppRequest, adt audit.Audit) (service.AppResponse, error)
}

// FindAppService retrieves an App
type FindAppService interface {
	// FindAppByAPIKey finds an app given its External ID and determines
	// if the given API key is a valid key for it
	FindAppByAPIKey(ctx context.Context, realm, appExtlID, apiKey string) (app.App, error)
}

// FindUserService retrieves a User
type FindUserService interface {
	FindUserByOauth2Token(ctx context.Context, params service.FindUserParams) (user.User, error)
}

// AuthorizeService determines whether an app and a user can perform
// an action against a resource
type AuthorizeService interface {
	Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error
}

// LoggerService reads and updates the logger state
type LoggerService interface {
	Read() service.LoggerResponse
	Update(r *service.LoggerRequest) (service.LoggerResponse, error)
}

// PingService pings the database and responds whether it is up or down
type PingService interface {
	Ping(ctx context.Context, logger zerolog.Logger) service.PingResponse
}

// SeedService initializes the database with dependent data
type SeedService interface {
	Seed(ctx context.Context, r *service.SeedRequest) (service.SeedResponse, error)
}

// Services are used by the application service handlers
type Services struct {
	SeedService      SeedService
	PingService      PingService
	LoggerService    LoggerService
	CreateOrgService CreateOrgService
	UpdateOrgService UpdateOrgService
	FindOrgService   FindOrgService
	CreateAppService CreateAppService
	FindAppService   FindAppService
	FindUserService  FindUserService
	AuthorizeService AuthorizeService
}
<commit_msg>+ RegisterUserService; seed 2 genesis name change<commit_after>package server

import (
	"context"
	"net/http"

	"github.com/rs/zerolog"

	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/service"
)

//// CreateMovieService creates a Movie
//type CreateMovieService interface {
//	Create(ctx context.Context, r *service.CreateMovieRequest, u user.User) (service.MovieResponse, error)
//}
//
//// UpdateMovieService is a service for updating a Movie
//type UpdateMovieService interface {
//	Update(ctx context.Context, r *service.UpdateMovieRequest, u user.User) (service.MovieResponse, error)
//}
//
//// DeleteMovieService is a service for deleting a Movie
//type DeleteMovieService interface {
//	Delete(ctx context.Context, extlID string) (service.DeleteMovieResponse, error)
//}
//
//// FindMovieService interface reads a Movie form the database
//type FindMovieService interface {
//	FindMovieByID(ctx context.Context, extlID string) (service.MovieResponse, error)
//	FindAllMovies(ctx context.Context) ([]service.MovieResponse, error)
//}

// CreateOrgService creates an Org
type CreateOrgService interface {
	Create(ctx context.Context, r *service.CreateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
}

// UpdateOrgService updates an Org
type UpdateOrgService interface {
	Update(ctx context.Context, r *service.UpdateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
}

// FindOrgService retrieves Org information from the datastore
type FindOrgService interface {
	FindAll(ctx context.Context) ([]service.OrgResponse, error)
	FindByExternalID(ctx context.Context, extlID string) (service.OrgResponse, error)
}

// CreateAppService creates an App
type CreateAppService interface {
	Create(ctx context.Context, r *service.CreateAppRequest, adt audit.Audit) (service.AppResponse, error)
}

// FindAppService retrieves an App
type FindAppService interface {
	// FindAppByAPIKey finds an app given its External ID and determines
	// if the given API key is a valid key for it
	FindAppByAPIKey(ctx context.Context, realm, appExtlID, apiKey string) (app.App, error)
}

// RegisterUserService registers a new user
type RegisterUserService interface {
	SelfRegister(ctx context.Context, adt audit.Audit) error
}

// FindUserService retrieves a User
type FindUserService interface {
	FindUserByOauth2Token(ctx context.Context, params service.FindUserParams) (user.User, error)
}

// AuthorizeService determines whether an app and a user can perform
// an action against a resource
type AuthorizeService interface {
	Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error
}

// LoggerService reads and updates the logger state
type LoggerService interface {
	Read() service.LoggerResponse
	Update(r *service.LoggerRequest) (service.LoggerResponse, error)
}

// PingService pings the database and responds whether it is up or down
type PingService interface {
	Ping(ctx context.Context, logger zerolog.Logger) service.PingResponse
}

// GenesisService initializes the database with dependent data
type GenesisService interface {
	Seed(ctx context.Context, r *service.GenesisRequest) (service.GenesisResponse, error)
}

// Services are used by the application service handlers
type Services struct {
	GenesisService      GenesisService
	PingService         PingService
	LoggerService       LoggerService
	CreateOrgService    CreateOrgService
	UpdateOrgService    UpdateOrgService
	FindOrgService      FindOrgService
	CreateAppService    CreateAppService
	FindAppService      FindAppService
	RegisterUserService RegisterUserService
	FindUserService     FindUserService
	AuthorizeService    AuthorizeService
}
<|endoftext|>