<commit_before>package store

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

var awsSession *session.Session
var s3SessionTable = map[string]*s3.S3{} // region => session
var s3logger hasPrintf
var s3region string // default region

func s3client(region string) *s3.S3 {
	if awsSession == nil {
		var err error
		awsSession, err = session.NewSession()
		if err != nil {
			s3log("s3client: could not create session: %v", err)
			return nil
		}
		s3log("s3client: session created")
	}

	if region == "" {
		region = s3region // fallback to default region
	}
	if region == "" {
		s3log("s3client: could not find region")
		return nil
	}

	s3Session, ok := s3SessionTable[region]
	if !ok {
		s3Session = s3.New(awsSession, aws.NewConfig().WithRegion(region))
		s3SessionTable[region] = s3Session
		s3log("s3client: client created: region=[%s]", region)
	}

	return s3Session
}

func s3init(logger hasPrintf, region string) {
	if s3logger != nil {
		panic("s3 store reinitialization")
	}
	if logger == nil {
		panic("s3 store nil logger")
	}
	s3region = region
	s3logger = logger
	s3log("initialized: default region=[%s]", s3region)
}

func s3log(format string, v ...interface{}) {
	if s3logger == nil {
		log.Printf("s3 store (unitialized): "+format, v...)
		return
	}
	s3logger.Printf("s3 store: "+format, v...)
}

// S3Path checks if path is an aws s3 path.
func S3Path(path string) bool {
	return s3path(path)
}

func s3path(path string) bool {
	s3match := strings.HasPrefix(path, "arn:aws:s3:")
	if s3match {
		s3log("s3path: [%s]", path)
	}
	return s3match
}

//  Input: "arn:aws:s3:region::bucket/folder/file.xxx"
// Output: "region", "bucket", "folder/file.xxx"
func s3parse(path string) (string, string, string) {
	s := strings.Split(path, ":")
	if len(s) < 6 {
		return "", "", ""
	}
	region := s[3]
	file := s[5]
	slash := strings.IndexByte(file, '/')
	if slash < 1 {
		return "", "", ""
	}
	bucket := file[:slash]
	key := file[slash+1:]
	return region, bucket, key
}

func s3fileExists(path string) bool {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		s3log("s3fileExists: missing s3 client: ugh")
		return false // ugh
	}

	params := &s3.HeadObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}
	if _, err := svc.HeadObject(params); err == nil {
		//s3log("s3fileExists: FOUND [%s]", path)
		return true // found
	}

	return false
}

func s3fileput(path string, buf []byte) error {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3fileput: missing s3 client")
	}

	params := &s3.PutObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
		Body:   bytes.NewReader(buf),
	}
	_, err := svc.PutObject(params)

	//s3log("s3fileput: [%s] upload: error: %v", path, err)

	return err
}

func s3fileRemove(path string) error {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3fileRemove: missing s3 client")
	}

	params := &s3.DeleteObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}
	_, err := svc.DeleteObject(params)

	//s3log("s3fileRemove: [%s] delete: error: %v", path, err)

	return err
}

func s3fileRename(p1, p2 string) error {

	region, bucket1, key1 := s3parse(p1)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3fileRename: missing s3 client")
	}

	_, bucket2, key2 := s3parse(p2)

	params := &s3.CopyObjectInput{
		Bucket:     aws.String(bucket2),              // Required
		CopySource: aws.String(bucket1 + "/" + key1), // Required
		Key:        aws.String(key2),                 // Required
	}
	_, copyErr := svc.CopyObject(params)
	if copyErr != nil {
		return copyErr
	}

	if removeErr := s3fileRemove(p1); removeErr != nil {
		// could not remove old file
		s3fileRemove(p2) // remove new file (clean up)
		return removeErr
	}

	return nil
}

func s3fileRead(path string) ([]byte, error) {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return nil, fmt.Errorf("s3fileRead: missing s3 client")
	}

	params := &s3.GetObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}

	resp, err := svc.GetObject(params)
	if err != nil {
		return nil, err
	}

	s3log("s3fileRead: FIXME limit number of lines read from s3 object")

	return ioutil.ReadAll(resp.Body)
}

func s3fileFirstLine(path string) (string, error) {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return "", fmt.Errorf("s3fileFirstLine: missing s3 client")
	}

	params := &s3.GetObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}

	resp, err := svc.GetObject(params)
	if err != nil {
		return "", err
	}

	r := bufio.NewReader(resp.Body)
	line, _, readErr := r.ReadLine()

	return string(line[:]), readErr
}

func s3dirList(path string) (string, []string, error) {

	dirname := filepath.Dir(path)
	var names []string

	region, bucket, prefix := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return dirname, names, fmt.Errorf("s3dirList: missing s3 client")
	}

	params := &s3.ListObjectsV2Input{
		Bucket: aws.String(bucket), // Required
		Prefix: aws.String(prefix),
	}

	for {
		resp, err := svc.ListObjectsV2(params)
		if err != nil {
			return dirname, names, err
		}

		//s3log("s3dirList: FOUND %d keys [%s]", *resp.KeyCount, path)

		for _, obj := range resp.Contents {
			key := *obj.Key
			name := filepath.Base(key)
			//s3log("s3dirList: [%s] found: dir=[%s] file=[%s]", path, dirname, name)
			names = append(names, name)
		}

		if *resp.IsTruncated {
			params.ContinuationToken = resp.NextContinuationToken
			continue
		}

		break
	}

	//s3log("s3dirList: FOUND %d total keys [%s]", len(names), path)

	return dirname, names, nil
}

func s3dirClean(path string) error {

	// retrieve object list
	_, names, listErr := s3dirList(path)
	if listErr != nil {
		return listErr
	}

	if len(names) < 1 {
		return nil
	}

	region, bucket, prefix := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3dirClean: missing s3 client")
	}

	// build object list
	folder := filepath.Dir(prefix)
	list := []*s3.ObjectIdentifier{}
	for _, filename := range names {
		key := folder + "/" + filename
		s3log("s3dirClean: [%s] bucket=[%s] key=[%s]", path, bucket, key)
		obj := &s3.ObjectIdentifier{
			Key: aws.String(key), // Required
		}
		list = append(list, obj)
	}

	// query parameters
	params := &s3.DeleteObjectsInput{
		Bucket: aws.String(bucket), // Required
		Delete: &s3.Delete{ // Required
			Objects: list, // Required
		},
	}

	// send
	_, err := svc.DeleteObjects(params)

	return err
}

func s3fileInfo(path string) (time.Time, int64, error) {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return time.Time{}, 0, fmt.Errorf("s3fileInfo: missing s3 client")
	}

	params := &s3.HeadObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}
	resp, err := svc.HeadObject(params)
	if err != nil {
		return time.Time{}, 0, err
	}

	mod := *resp.LastModified
	size := *resp.ContentLength

	return mod, size, nil
}

func s3fileCompare(p1, p2 string) (bool, error) {
	return false, fmt.Errorf("s3fileCompare: FIXME WRITEME cant currently compare files on S3: [%s,%s]", p1, p2)
}
<commit_msg>Clean-up.<commit_after>package store

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

var awsSession *session.Session
var s3SvcTable = map[string]*s3.S3{} // region => session
var s3logger hasPrintf
var s3region string // default region

func s3session() *session.Session {
	if awsSession == nil {
		var err error
		awsSession, err = session.NewSession()
		if err != nil {
			s3log("s3client: could not create session: %v", err)
			return nil
		}
		s3log("s3session: new session created")
	}
	return awsSession
}

func s3client(region string) *s3.S3 {

	svc, ok := s3SvcTable[region]
	if !ok {
		sess := s3session()
		if sess == nil {
			return nil
		}

		if region == "" {
			region = s3region // fallback to default region
		}
		if region == "" {
			s3log("s3client: could not find region")
			return nil
		}

		svc = s3.New(sess, aws.NewConfig().WithRegion(region))
		s3SvcTable[region] = svc
		s3log("s3client: client created: region=[%s]", region)
	}

	return svc
}

func s3init(logger hasPrintf, region string) {
	if s3logger != nil {
		panic("s3 store reinitialization")
	}
	if logger == nil {
		panic("s3 store nil logger")
	}
	s3region = region
	s3logger = logger
	s3log("initialized: default region=[%s]", s3region)
}

func s3log(format string, v ...interface{}) {
	if s3logger == nil {
		log.Printf("s3 store (unitialized): "+format, v...)
		return
	}
	s3logger.Printf("s3 store: "+format, v...)
}

// S3Path checks if path is an aws s3 path.
func S3Path(path string) bool {
	return s3path(path)
}

func s3path(path string) bool {
	s3match := strings.HasPrefix(path, "arn:aws:s3:")
	if s3match {
		s3log("s3path: [%s]", path)
	}
	return s3match
}

//  Input: "arn:aws:s3:region::bucket/folder/file.xxx"
// Output: "region", "bucket", "folder/file.xxx"
func s3parse(path string) (string, string, string) {
	s := strings.Split(path, ":")
	if len(s) < 6 {
		return "", "", ""
	}
	region := s[3]
	file := s[5]
	slash := strings.IndexByte(file, '/')
	if slash < 1 {
		return "", "", ""
	}
	bucket := file[:slash]
	key := file[slash+1:]
	return region, bucket, key
}

func s3fileExists(path string) bool {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		s3log("s3fileExists: missing s3 client: ugh")
		return false // ugh
	}

	params := &s3.HeadObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}
	if _, err := svc.HeadObject(params); err == nil {
		//s3log("s3fileExists: FOUND [%s]", path)
		return true // found
	}

	return false
}

func s3fileput(path string, buf []byte) error {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3fileput: missing s3 client")
	}

	params := &s3.PutObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
		Body:   bytes.NewReader(buf),
	}
	_, err := svc.PutObject(params)

	//s3log("s3fileput: [%s] upload: error: %v", path, err)

	return err
}

func s3fileRemove(path string) error {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3fileRemove: missing s3 client")
	}

	params := &s3.DeleteObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}
	_, err := svc.DeleteObject(params)

	//s3log("s3fileRemove: [%s] delete: error: %v", path, err)

	return err
}

func s3fileRename(p1, p2 string) error {

	region, bucket1, key1 := s3parse(p1)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3fileRename: missing s3 client")
	}

	_, bucket2, key2 := s3parse(p2)

	params := &s3.CopyObjectInput{
		Bucket:     aws.String(bucket2),              // Required
		CopySource: aws.String(bucket1 + "/" + key1), // Required
		Key:        aws.String(key2),                 // Required
	}
	_, copyErr := svc.CopyObject(params)
	if copyErr != nil {
		return copyErr
	}

	if removeErr := s3fileRemove(p1); removeErr != nil {
		// could not remove old file
		s3fileRemove(p2) // remove new file (clean up)
		return removeErr
	}

	return nil
}

func s3fileRead(path string) ([]byte, error) {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return nil, fmt.Errorf("s3fileRead: missing s3 client")
	}

	params := &s3.GetObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}

	resp, err := svc.GetObject(params)
	if err != nil {
		return nil, err
	}

	s3log("s3fileRead: FIXME limit number of lines read from s3 object")

	return ioutil.ReadAll(resp.Body)
}

func s3fileFirstLine(path string) (string, error) {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return "", fmt.Errorf("s3fileFirstLine: missing s3 client")
	}

	params := &s3.GetObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}

	resp, err := svc.GetObject(params)
	if err != nil {
		return "", err
	}

	r := bufio.NewReader(resp.Body)
	line, _, readErr := r.ReadLine()

	return string(line[:]), readErr
}

func s3dirList(path string) (string, []string, error) {

	dirname := filepath.Dir(path)
	var names []string

	region, bucket, prefix := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return dirname, names, fmt.Errorf("s3dirList: missing s3 client")
	}

	params := &s3.ListObjectsV2Input{
		Bucket: aws.String(bucket), // Required
		Prefix: aws.String(prefix),
	}

	for {
		resp, err := svc.ListObjectsV2(params)
		if err != nil {
			return dirname, names, err
		}

		//s3log("s3dirList: FOUND %d keys [%s]", *resp.KeyCount, path)

		for _, obj := range resp.Contents {
			key := *obj.Key
			name := filepath.Base(key)
			//s3log("s3dirList: [%s] found: dir=[%s] file=[%s]", path, dirname, name)
			names = append(names, name)
		}

		if *resp.IsTruncated {
			params.ContinuationToken = resp.NextContinuationToken
			continue
		}

		break
	}

	//s3log("s3dirList: FOUND %d total keys [%s]", len(names), path)

	return dirname, names, nil
}

func s3dirClean(path string) error {

	// retrieve object list
	_, names, listErr := s3dirList(path)
	if listErr != nil {
		return listErr
	}

	if len(names) < 1 {
		return nil
	}

	region, bucket, prefix := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return fmt.Errorf("s3dirClean: missing s3 client")
	}

	// build object list
	folder := filepath.Dir(prefix)
	list := []*s3.ObjectIdentifier{}
	for _, filename := range names {
		key := folder + "/" + filename
		s3log("s3dirClean: [%s] bucket=[%s] key=[%s]", path, bucket, key)
		obj := &s3.ObjectIdentifier{
			Key: aws.String(key), // Required
		}
		list = append(list, obj)
	}

	// query parameters
	params := &s3.DeleteObjectsInput{
		Bucket: aws.String(bucket), // Required
		Delete: &s3.Delete{ // Required
			Objects: list, // Required
		},
	}

	// send
	_, err := svc.DeleteObjects(params)

	return err
}

func s3fileInfo(path string) (time.Time, int64, error) {

	region, bucket, key := s3parse(path)

	svc := s3client(region)
	if svc == nil {
		return time.Time{}, 0, fmt.Errorf("s3fileInfo: missing s3 client")
	}

	params := &s3.HeadObjectInput{
		Bucket: aws.String(bucket), // Required
		Key:    aws.String(key),    // Required
	}
	resp, err := svc.HeadObject(params)
	if err != nil {
		return time.Time{}, 0, err
	}

	mod := *resp.LastModified
	size := *resp.ContentLength

	return mod, size, nil
}

func s3fileCompare(p1, p2 string) (bool, error) {
	return false, fmt.Errorf("s3fileCompare: FIXME WRITEME cant currently compare files on S3: [%s,%s]", p1, p2)
}
<|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 scanner

// A Go scanner. Takes a []byte as source which can then be
// tokenized through repeated calls to the Scan() function.
//
// Sample use:
//
//	import "token"
//	import "scanner"
//
//	func tokenize(src []byte) {
//		var s scanner.Scanner;
//		s.Init(src, nil /* no error handler */, false /* ignore comments */);
//		for {
//			pos, tok, lit := s.Scan();
//			if tok == Scanner.EOF {
//				return;
//			}
//			println(pos, token.TokenString(tok), string(lit));
//		}
//	}

import (
	"utf8";
	"unicode";
	"strconv";
	"token";
)

// An implementation of an ErrorHandler must be provided to the Scanner.
// If a syntax error is encountered, Error() is called with the exact
// token position (the byte position of the token in the source) and the
// error message.

type ErrorHandler interface {
	Error(pos int, msg string);
}

type Scanner struct {
	// immutable state
	src []byte;  // source
	err ErrorHandler;  // error reporting
	scan_comments bool;  // if set, comments are reported as tokens

	// scanning state
	pos int;  // current reading position
	ch int;  // one char look-ahead
	chpos int;  // position of ch
}

func isLetter(ch int) bool {
	return
		'a' <= ch && ch <= 'z' ||
		'A' <= ch && ch <= 'Z' ||
		ch == '_' ||
		ch >= 0x80 && unicode.IsLetter(ch);
}

func digitVal(ch int) int {
	switch {
	case '0' <= ch && ch <= '9': return ch - '0';
	case 'a' <= ch && ch <= 'f': return ch - 'a' + 10;
	case 'A' <= ch && ch <= 'F': return ch - 'A' + 10;
	}
	return 16;  // larger than any legal digit val
}

// Read the next Unicode char into S.ch.
// S.ch < 0 means end-of-file.
func (S *Scanner) next() {
	if S.pos < len(S.src) {
		// assume ASCII
		r, w := int(S.src[S.pos]), 1;
		if r >= 0x80 {
			// not ASCII
			r, w = utf8.DecodeRune(S.src[S.pos : len(S.src)]);
		}
		S.ch = r;
		S.chpos = S.pos;
		S.pos += w;
	} else {
		S.ch = -1;  // eof
		S.chpos = len(S.src);
	}
}

// Initialize the scanner.
//
// The error handler (err) is called when an illegal token is encountered.
// If scan_comments is set to true, newline characters ('\n') and comments
// are recognized as token.COMMENT, otherwise they are treated as white
// space and ignored.

func (S *Scanner) Init(src []byte, err ErrorHandler, scan_comments bool) {
	S.src = src;
	S.err = err;
	S.scan_comments = scan_comments;
	S.next();
}

func charString(ch int) string {
	s := string(ch);
	switch ch {
	case '\a': s = `\a`;
	case '\b': s = `\b`;
	case '\f': s = `\f`;
	case '\n': s = `\n`;
	case '\r': s = `\r`;
	case '\t': s = `\t`;
	case '\v': s = `\v`;
	case '\\': s = `\\`;
	case '\'': s = `\'`;
	}
	return "'" + s + "' (U+" + strconv.Itob(ch, 16) + ")";
}

func (S *Scanner) error(pos int, msg string) {
	S.err.Error(pos, msg);
}

func (S *Scanner) expect(ch int) {
	if S.ch != ch {
		S.error(S.chpos, "expected " + charString(ch) + ", found " + charString(S.ch));
	}
	S.next();  // always make progress
}

func (S *Scanner) skipWhitespace() {
	for {
		switch S.ch {
		case '\t', '\r', ' ':
			// nothing to do
		case '\n':
			if S.scan_comments {
				return;
			}
		default:
			return;
		}
		S.next();
	}
	panic("UNREACHABLE");
}

func (S *Scanner) scanComment() []byte {
	// first '/' already consumed
	pos := S.chpos - 1;

	if S.ch == '/' {
		//-style comment
		for S.ch >= 0 {
			S.next();
			if S.ch == '\n' {
				// '\n' terminates comment but we do not include
				// it in the comment (otherwise we don't see the
				// start of a newline in skipWhitespace()).
				return S.src[pos : S.chpos];
			}
		}

	} else {
		/*-style comment */
		S.expect('*');
		for S.ch >= 0 {
			ch := S.ch;
			S.next();
			if ch == '*' && S.ch == '/' {
				S.next();
				return S.src[pos : S.chpos];
			}
		}
	}

	S.error(pos, "comment not terminated");
	return S.src[pos : S.chpos];
}

func (S *Scanner) scanIdentifier() (tok int, lit []byte) {
	pos := S.chpos;
	for isLetter(S.ch) || digitVal(S.ch) < 10 {
		S.next();
	}
	lit = S.src[pos : S.chpos];
	return token.Lookup(lit), lit;
}

func (S *Scanner) scanMantissa(base int) {
	for digitVal(S.ch) < base {
		S.next();
	}
}

func (S *Scanner) scanNumber(seen_decimal_point bool) (tok int, lit []byte) {
	pos := S.chpos;
	tok = token.INT;

	if seen_decimal_point {
		tok = token.FLOAT;
		pos--;  // '.' is one byte
		S.scanMantissa(10);
		goto exponent;
	}

	if S.ch == '0' {
		// int or float
		S.next();
		if S.ch == 'x' || S.ch == 'X' {
			// hexadecimal int
			S.next();
			S.scanMantissa(16);
		} else {
			// octal int or float
			S.scanMantissa(8);
			if digitVal(S.ch) < 10 || S.ch == '.' || S.ch == 'e' || S.ch == 'E' {
				// float
				tok = token.FLOAT;
				goto mantissa;
			}
			// octal int
		}
		goto exit;
	}

mantissa:
	// decimal int or float
	S.scanMantissa(10);

	if S.ch == '.' {
		// float
		tok = token.FLOAT;
		S.next();
		S.scanMantissa(10)
	}

exponent:
	if S.ch == 'e' || S.ch == 'E' {
		// float
		tok = token.FLOAT;
		S.next();
		if S.ch == '-' || S.ch == '+' {
			S.next();
		}
		S.scanMantissa(10);
	}

exit:
	return tok, S.src[pos : S.chpos];
}

func (S *Scanner) scanDigits(n int, base int) {
	for digitVal(S.ch) < base {
		S.next();
		n--;
	}
	if n > 0 {
		S.error(S.chpos, "illegal char escape");
	}
}

func (S *Scanner) scanEscape(quote int) {
	ch := S.ch;
	pos := S.chpos;
	S.next();
	switch ch {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
		// nothing to do
	case '0', '1', '2', '3', '4', '5', '6', '7':
		S.scanDigits(3 - 1, 8);  // 1 char read already
	case 'x':
		S.scanDigits(2, 16);
	case 'u':
		S.scanDigits(4, 16);
	case 'U':
		S.scanDigits(8, 16);
	default:
		S.error(pos, "illegal char escape");
	}
}

func (S *Scanner) scanChar() []byte {
	// '\'' already consumed

	pos := S.chpos - 1;
	ch := S.ch;
	S.next();
	if ch == '\\' {
		S.scanEscape('\'');
	}

	S.expect('\'');
	return S.src[pos : S.chpos];
}

func (S *Scanner) scanString() []byte {
	// '"' already consumed

	pos := S.chpos - 1;
	for S.ch != '"' {
		ch := S.ch;
		S.next();
		if ch == '\n' || ch < 0 {
			S.error(pos, "string not terminated");
			break;
		}
		if ch == '\\' {
			S.scanEscape('"');
		}
	}

	S.next();
	return S.src[pos : S.chpos];
}

func (S *Scanner) scanRawString() []byte {
	// '`' already consumed

	pos := S.chpos - 1;
	for S.ch != '`' {
		ch := S.ch;
		S.next();
		if ch == '\n' || ch < 0 {
			S.error(pos, "string not terminated");
			break;
		}
	}

	S.next();
	return S.src[pos : S.chpos];
}

// Helper functions for scanning multi-byte tokens such as >> += >>= .
// Different routines recognize different length tok_i based on matches
// of ch_i. If a token ends in '=', the result is tok1 or tok3
// respectively. Otherwise, the result is tok0 if there was no other
// matching character, or tok2 if the matching character was ch2.

func (S *Scanner) switch2(tok0, tok1 int) int {
	if S.ch == '=' {
		S.next();
		return tok1;
	}
	return tok0;
}

func (S *Scanner) switch3(tok0, tok1, ch2, tok2 int) int {
	if S.ch == '=' {
		S.next();
		return tok1;
	}
	if S.ch == ch2 {
		S.next();
		return tok2;
	}
	return tok0;
}

func (S *Scanner) switch4(tok0, tok1, ch2, tok2, tok3 int) int {
	if S.ch == '=' {
		S.next();
		return tok1;
	}
	if S.ch == ch2 {
		S.next();
		if S.ch == '=' {
			S.next();
			return tok3;
		}
		return tok2;
	}
	return tok0;
}

// Scans the next token. Returns the token byte position in the source,
// its token value, and the corresponding literal text if the token is
// an identifier or basic type literal (token.IsLiteral(tok) == true).

func (S *Scanner) Scan() (pos, tok int, lit []byte) {
scan_again:
	S.skipWhitespace();

	pos, tok = S.chpos, token.ILLEGAL;

	switch ch := S.ch; {
	case isLetter(ch):
		tok, lit = S.scanIdentifier();
	case digitVal(ch) < 10:
		tok, lit = S.scanNumber(false);
	default:
		S.next();  // always make progress
		switch ch {
		case -1  : tok = token.EOF;
		case '\n': tok, lit = token.COMMENT, []byte{'\n'};
		case '"' : tok, lit = token.STRING, S.scanString();
		case '\'': tok, lit = token.CHAR, S.scanChar();
		case '`' : tok, lit = token.STRING, S.scanRawString();
		case ':' : tok = S.switch2(token.COLON, token.DEFINE);
		case '.' :
			if digitVal(S.ch) < 10 {
				tok, lit = S.scanNumber(true);
			} else if S.ch == '.' {
				S.next();
				if S.ch == '.' {
					S.next();
					tok = token.ELLIPSIS;
				}
			} else {
				tok = token.PERIOD;
			}
		case ',': tok = token.COMMA;
		case ';': tok = token.SEMICOLON;
		case '(': tok = token.LPAREN;
		case ')': tok = token.RPAREN;
		case '[': tok = token.LBRACK;
		case ']': tok = token.RBRACK;
		case '{': tok = token.LBRACE;
		case '}': tok = token.RBRACE;
		case '+': tok = S.switch3(token.ADD, token.ADD_ASSIGN, '+', token.INC);
		case '-': tok = S.switch3(token.SUB, token.SUB_ASSIGN, '-', token.DEC);
		case '*': tok = S.switch2(token.MUL, token.MUL_ASSIGN);
		case '/':
			if S.ch == '/' || S.ch == '*' {
				tok, lit = token.COMMENT, S.scanComment();
				if !S.scan_comments {
					goto scan_again;
				}
			} else {
				tok = S.switch2(token.QUO, token.QUO_ASSIGN);
			}
		case '%': tok = S.switch2(token.REM, token.REM_ASSIGN);
		case '^': tok = S.switch2(token.XOR, token.XOR_ASSIGN);
		case '<':
			if S.ch == '-' {
				S.next();
				tok = token.ARROW;
			} else {
				tok = S.switch4(token.LSS, token.LEQ, '<', token.SHL, token.SHL_ASSIGN);
			}
		case '>': tok = S.switch4(token.GTR, token.GEQ, '>', token.SHR, token.SHR_ASSIGN);
		case '=': tok = S.switch2(token.ASSIGN, token.EQL);
		case '!': tok = S.switch2(token.NOT, token.NEQ);
		case '&': tok = S.switch3(token.AND, token.AND_ASSIGN, '&', token.LAND);
		case '|': tok = S.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR);
		default: S.error(pos, "illegal character " + charString(ch));
		}
	}

	return pos, tok, lit;
}
<commit_msg>scanner.go documentation<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.

// A Go scanner. Takes a []byte as source which can then be
// tokenized through repeated calls to the Scan() function.
//
// Sample use:
//
//	import "token"
//	import "scanner"
//
//	func tokenize(src []byte) {
//		var s scanner.Scanner;
//		s.Init(src, nil /* no error handler */, false /* ignore comments */);
//		for {
//			pos, tok, lit := s.Scan();
//			if tok == Scanner.EOF {
//				return;
//			}
//			println(pos, token.TokenString(tok), string(lit));
//		}
//	}
//
package scanner

import (
	"utf8";
	"unicode";
	"strconv";
	"token";
)

// An implementation of an ErrorHandler must be provided to the Scanner.
// If a syntax error is encountered, Error() is called with the exact
// token position (the byte position of the token in the source) and the
// error message.
//
type ErrorHandler interface {
	Error(pos int, msg string);
}

// A Scanner holds the scanner's internal state while processing
// a given text.  It can be allocated as part of another data
// structure but must be initialized via Init() before use.
// See also the package comment for a sample use.
//
type Scanner struct {
	// immutable state
	src []byte;  // source
	err ErrorHandler;  // error reporting
	scan_comments bool;  // if set, comments are reported as tokens

	// scanning state
	pos int;  // current reading position
	ch int;  // one char look-ahead
	chpos int;  // position of ch
}

func isLetter(ch int) bool {
	return
		'a' <= ch && ch <= 'z' ||
		'A' <= ch && ch <= 'Z' ||
		ch == '_' ||
		ch >= 0x80 && unicode.IsLetter(ch);
}

func digitVal(ch int) int {
	switch {
	case '0' <= ch && ch <= '9': return ch - '0';
	case 'a' <= ch && ch <= 'f': return ch - 'a' + 10;
	case 'A' <= ch && ch <= 'F': return ch - 'A' + 10;
	}
	return 16;  // larger than any legal digit val
}

// Read the next Unicode char into S.ch.
// S.ch < 0 means end-of-file.
func (S *Scanner) next() {
	if S.pos < len(S.src) {
		// assume ASCII
		r, w := int(S.src[S.pos]), 1;
		if r >= 0x80 {
			// not ASCII
			r, w = utf8.DecodeRune(S.src[S.pos : len(S.src)]);
		}
		S.ch = r;
		S.chpos = S.pos;
		S.pos += w;
	} else {
		S.ch = -1;  // eof
		S.chpos = len(S.src);
	}
}

// Init() prepares the scanner S to tokenize the text src. Calls to Scan()
// will use the error handler err if they encounter a syntax error. The boolean
// scan_comments specifies whether newline characters and comments should be
// recognized and returned by Scan as token.COMMENT. If scan_comments is false,
// they are treated as white space and ignored.
//
func (S *Scanner) Init(src []byte, err ErrorHandler, scan_comments bool) {
	S.src = src;
	S.err = err;
	S.scan_comments = scan_comments;
	S.next();
}

func charString(ch int) string {
	s := string(ch);
	switch ch {
	case '\a': s = `\a`;
	case '\b': s = `\b`;
	case '\f': s = `\f`;
	case '\n': s = `\n`;
	case '\r': s = `\r`;
	case '\t': s = `\t`;
	case '\v': s = `\v`;
	case '\\': s = `\\`;
	case '\'': s = `\'`;
	}
	return "'" + s + "' (U+" + strconv.Itob(ch, 16) + ")";
}

func (S *Scanner) error(pos int, msg string) {
	S.err.Error(pos, msg);
}

func (S *Scanner) expect(ch int) {
	if S.ch != ch {
		S.error(S.chpos, "expected " + charString(ch) + ", found " + charString(S.ch));
	}
	S.next();  // always make progress
}

func (S *Scanner) skipWhitespace() {
	for {
		switch S.ch {
		case '\t', '\r', ' ':
			// nothing to do
		case '\n':
			if S.scan_comments {
				return;
			}
		default:
			return;
		}
		S.next();
	}
	panic("UNREACHABLE");
}

func (S *Scanner) scanComment() []byte {
	// first '/' already consumed
	pos := S.chpos - 1;

	if S.ch == '/' {
		//-style comment
		for S.ch >= 0 {
			S.next();
			if S.ch == '\n' {
				// '\n' terminates comment but we do not include
				// it in the comment (otherwise we don't see the
				// start of a newline in skipWhitespace()).
				return S.src[pos : S.chpos];
			}
		}

	} else {
		/*-style comment */
		S.expect('*');
		for S.ch >= 0 {
			ch := S.ch;
			S.next();
			if ch == '*' && S.ch == '/' {
				S.next();
				return S.src[pos : S.chpos];
			}
		}
	}

	S.error(pos, "comment not terminated");
	return S.src[pos : S.chpos];
}

func (S *Scanner) scanIdentifier() (tok int, lit []byte) {
	pos := S.chpos;
	for isLetter(S.ch) || digitVal(S.ch) < 10 {
		S.next();
	}
	lit = S.src[pos : S.chpos];
	return token.Lookup(lit), lit;
}

func (S *Scanner) scanMantissa(base int) {
	for digitVal(S.ch) < base {
		S.next();
	}
}

func (S *Scanner) scanNumber(seen_decimal_point bool) (tok int, lit []byte) {
	pos := S.chpos;
	tok = token.INT;

	if seen_decimal_point {
		tok = token.FLOAT;
		pos--;  // '.' is one byte
		S.scanMantissa(10);
		goto exponent;
	}

	if S.ch == '0' {
		// int or float
		S.next();
		if S.ch == 'x' || S.ch == 'X' {
			// hexadecimal int
			S.next();
			S.scanMantissa(16);
		} else {
			// octal int or float
			S.scanMantissa(8);
			if digitVal(S.ch) < 10 || S.ch == '.' || S.ch == 'e' || S.ch == 'E' {
				// float
				tok = token.FLOAT;
				goto mantissa;
			}
			// octal int
		}
		goto exit;
	}

mantissa:
	// decimal int or float
	S.scanMantissa(10);

	if S.ch == '.' {
		// float
		tok = token.FLOAT;
		S.next();
		S.scanMantissa(10)
	}

exponent:
	if S.ch == 'e' || S.ch == 'E' {
		// float
		tok = token.FLOAT;
		S.next();
		if S.ch == '-' || S.ch == '+' {
			S.next();
		}
		S.scanMantissa(10);
	}

exit:
	return tok, S.src[pos : S.chpos];
}

func (S *Scanner) scanDigits(n int, base int) {
	for digitVal(S.ch) < base {
		S.next();
		n--;
	}
	if n > 0 {
		S.error(S.chpos, "illegal char escape");
	}
}

func (S *Scanner) scanEscape(quote int) {
	ch := S.ch;
	pos := S.chpos;
	S.next();
	switch ch {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
		// nothing to do
	case '0', '1', '2', '3', '4', '5', '6', '7':
		S.scanDigits(3 - 1, 8);  // 1 char read already
	case 'x':
		S.scanDigits(2, 16);
	case 'u':
		S.scanDigits(4, 16);
	case 'U':
		S.scanDigits(8, 16);
	default:
		S.error(pos, "illegal char escape");
	}
}

func (S *Scanner) scanChar() []byte {
	// '\'' already consumed

	pos := S.chpos - 1;
	ch := S.ch;
	S.next();
	if ch == '\\' {
		S.scanEscape('\'');
	}

	S.expect('\'');
	return S.src[pos : S.chpos];
}

func (S *Scanner) scanString() []byte {
	// '"' already consumed

	pos := S.chpos - 1;
	for S.ch != '"' {
		ch := S.ch;
		S.next();
		if ch == '\n' || ch < 0 {
			S.error(pos, "string not terminated");
			break;
		}
		if ch == '\\' {
			S.scanEscape('"');
		}
	}

	S.next();
	return S.src[pos : S.chpos];
}

func (S *Scanner) scanRawString() []byte {
	// '`' already consumed

	pos := S.chpos - 1;
	for S.ch != '`' {
		ch := S.ch;
		S.next();
		if ch == '\n' || ch < 0 {
			S.error(pos, "string not terminated");
			break;
		}
	}

	S.next();
	return S.src[pos : S.chpos];
}

// Helper functions for scanning multi-byte tokens such as >> += >>= .
// Different routines recognize different length tok_i based on matches
// of ch_i. If a token ends in '=', the result is tok1 or tok3
// respectively. Otherwise, the result is tok0 if there was no other
// matching character, or tok2 if the matching character was ch2.

func (S *Scanner) switch2(tok0, tok1 int) int {
	if S.ch == '=' {
		S.next();
		return tok1;
	}
	return tok0;
}

func (S *Scanner) switch3(tok0, tok1, ch2, tok2 int) int {
	if S.ch == '=' {
		S.next();
		return tok1;
	}
	if S.ch == ch2 {
		S.next();
		return tok2;
	}
	return tok0;
}

func (S *Scanner) switch4(tok0, tok1, ch2, tok2, tok3 int) int {
	if S.ch == '=' {
		S.next();
		return tok1;
	}
	if S.ch == ch2 {
		S.next();
		if S.ch == '=' {
			S.next();
			return tok3;
		}
		return tok2;
	}
	return tok0;
}

// Scan() scans the next token and returns the token byte position in the
// source, its token value, and the corresponding literal text if the token
// is an identifier, basic type literal (token.IsLiteral(tok) == true), or
// comment.
//
func (S *Scanner) Scan() (pos, tok int, lit []byte) {
scan_again:
	S.skipWhitespace();

	pos, tok = S.chpos, token.ILLEGAL;

	switch ch := S.ch; {
	case isLetter(ch):
		tok, lit = S.scanIdentifier();
	case digitVal(ch) < 10:
		tok, lit = S.scanNumber(false);
	default:
		S.next();  // always make progress
		switch ch {
		case -1  : tok = token.EOF;
		case '\n': tok, lit = token.COMMENT, []byte{'\n'};
		case '"' : tok, lit = token.STRING, S.scanString();
		case '\'': tok, lit = token.CHAR, S.scanChar();
		case '`' : tok, lit = token.STRING, S.scanRawString();
		case ':' : tok = S.switch2(token.COLON, token.DEFINE);
		case '.' :
			if digitVal(S.ch) < 10 {
				tok, lit = S.scanNumber(true);
			} else if S.ch == '.' {
				S.next();
				if S.ch == '.' {
					S.next();
					tok = token.ELLIPSIS;
				}
			} else {
				tok = token.PERIOD;
			}
		case ',': tok = token.COMMA;
		case ';': tok = token.SEMICOLON;
		case '(': tok = token.LPAREN;
		case ')': tok = token.RPAREN;
		case '[': tok = token.LBRACK;
		case ']': tok = token.RBRACK;
		case '{': tok = token.LBRACE;
		case '}': tok = token.RBRACE;
		case '+': tok = S.switch3(token.ADD, token.ADD_ASSIGN, '+', token.INC);
		case '-': tok = S.switch3(token.SUB, token.SUB_ASSIGN, '-', token.DEC);
		case '*': tok = S.switch2(token.MUL, token.MUL_ASSIGN);
		case '/':
			if S.ch == '/' || S.ch == '*' {
				tok, lit = token.COMMENT, S.scanComment();
				if !S.scan_comments {
					goto scan_again;
				}
			} else {
				tok = S.switch2(token.QUO, token.QUO_ASSIGN);
			}
		case '%': tok = S.switch2(token.REM, token.REM_ASSIGN);
		case '^': tok = S.switch2(token.XOR, token.XOR_ASSIGN);
		case '<':
			if S.ch == '-' {
				S.next();
				tok = token.ARROW;
			} else {
				tok = S.switch4(token.LSS, token.LEQ, '<', token.SHL, token.SHL_ASSIGN);
			}
		case '>': tok = S.switch4(token.GTR, token.GEQ, '>', token.SHR, token.SHR_ASSIGN);
		case '=': tok = S.switch2(token.ASSIGN, token.EQL);
		case '!': tok = S.switch2(token.NOT, token.NEQ);
		case '&': tok = S.switch3(token.AND, token.AND_ASSIGN, '&', token.LAND);
		case '|': tok = S.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR);
		default: S.error(pos, "illegal character " + charString(ch));
		}
	}

	return pos, tok, lit;
}
<|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 sync

func cas(val *int32, old, new int32) bool
func semacquire(*int32)
func semrelease(*int32)

type Mutex struct {
	key int32;
	sema int32;
}

func xadd(val *int32, delta int32) (new int32) {
	for {
		v := *val;
		if cas(val, v, v+delta) {
			return v+delta;
		}
	}
	panic("unreached")
}

func (m *Mutex) Lock() {
	if xadd(&m.key, 1) == 1 {
		// changed from 0 to 1; we hold lock
		return;
	}
	semacquire(&m.sema);
}

func (m *Mutex) Unlock() {
	if xadd(&m.key, -1) == 0 {
		// changed from 1 to 0; no contention
		return;
	}
	semrelease(&m.sema);
}

// Stub implementation of r/w locks.
// This satisfies the semantics but
// is not terribly efficient.
// TODO(rsc): Real r/w locks.

type RWMutex struct {
	Mutex;
}

func (m *RWMutex) RLock() {
	m.Lock();
}

func (m *RWMutex) RUnlock() {
	m.Unlock();
}

<commit_msg>sync: add documentation<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.

// The sync package provides basic synchronization primitives
// such as mutual exclusion locks.  These are intended for use
// by low-level library routines.  Higher-level synchronization
// is better done via channels and communication.
package sync

func cas(val *int32, old, new int32) bool
func semacquire(*int32)
func semrelease(*int32)

// A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures;
// the zero value for a Mutex is an unlocked mutex.
type Mutex struct {
	key int32;
	sema int32;
}

func xadd(val *int32, delta int32) (new int32) {
	for {
		v := *val;
		if cas(val, v, v+delta) {
			return v+delta;
		}
	}
	panic("unreached")
}

// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
	if xadd(&m.key, 1) == 1 {
		// changed from 0 to 1; we hold lock
		return;
	}
	semacquire(&m.sema);
}

// Unlock unlocks m.
// It is a run-time error if m is not locked on entry to Unlock.
//
// A locked Mutex is not associated with a particular goroutine.
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
	if xadd(&m.key, -1) == 0 {
		// changed from 1 to 0; no contention
		return;
	}
	semrelease(&m.sema);
}

// Stub implementation of r/w locks.
// This satisfies the semantics but
// is not terribly efficient.

// The next comment goes in the BUGS section of the document,
// in its own paragraph, without the (rsc) tag.

// BUG(rsc): RWMutex does not (yet) allow multiple readers;
// instead it behaves as if RLock and RUnlock were Lock and Unlock.

// An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers
// or a single writer.
// RWMutexes can be created as part of other
// structures; the zero value for a RWMutex is
// an unlocked mutex.
type RWMutex struct {
	m Mutex;
}

// RLock locks rw for reading.
// If the lock is already locked for writing or there is a writer already waiting
// to acquire the lock, RLock blocks until the writer has released the lock.
func (rw *RWMutex) RLock() {
	rw.m.Lock();
}

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
	rw.m.Unlock();
}

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
// To ensure that the lock eventually becomes available,
// a blocked Lock call excludes new readers from acquiring
// the lock.
func (rw *RWMutex) Lock() {
	rw.m.Lock();
}

// Unlock unlocks rw for writing.
// It is a run-time error if rw is not locked for writing
// on entry to Unlock.
//
// Like for Mutexes,
// a locked RWMutex is not associated with a particular goroutine.
// It is allowed for one goroutine to RLock (Lock) an RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
	rw.m.Unlock();
}

<|endoftext|>


<commit_before>// Copyright 2009 The Ninep 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 srv

import (
	"sync/atomic"

	"github.com/lionkov/ninep"
)

func (srv *Srv) version(req *Req) {
	tc := req.Tc
	conn := req.Conn

	if tc.Msize < ninep.IOHDRSZ {
		req.RespondError(&ninep.Error{"msize too small", ninep.EINVAL})
		return
	}

	if tc.Msize < conn.Msize {
		conn.Msize = tc.Msize
	}

	conn.Dotu = tc.Version == "9P2000.u" && srv.Dotu
	ver := "9P2000"
	if conn.Dotu {
		ver = "9P2000.u"
	}

	/* make sure that the responses of all current requests will be ignored */
	conn.Lock()
	for tag, r := range conn.Reqs {
		if tag == ninep.NOTAG {
			continue
		}

		for rr := r; rr != nil; rr = rr.next {
			rr.Lock()
			rr.status |= reqFlush
			rr.Unlock()
		}
	}
	conn.Unlock()

	atomic.AddUint32(&srv.Versioned, 1)
	req.RespondRversion(conn.Msize, ver)
}

func (srv *Srv) auth(req *Req) {
	tc := req.Tc
	conn := req.Conn
	if tc.Afid == ninep.NOFID {
		req.RespondError(Eunknownfid)
		return
	}

	req.Afid = conn.FidNew(tc.Afid)
	if req.Afid == nil {
		req.RespondError(Einuse)
		return
	}

	var user ninep.User = nil
	if tc.Unamenum != ninep.NOUID || conn.Dotu {
		user = srv.Upool.Uid2User(int(tc.Unamenum))
	} else if tc.Uname != "" {
		user = srv.Upool.Uname2User(tc.Uname)
	}

	if user == nil {
		req.RespondError(Enouser)
		return
	}

	req.Afid.User = user
	req.Afid.Type = ninep.QTAUTH
	if aop, ok := (srv.ops).(AuthOps); ok {
		aqid, err := aop.AuthInit(req.Afid, tc.Aname)
		if err != nil {
			req.RespondError(err)
		} else {
			aqid.Type |= ninep.QTAUTH // just in case
			req.RespondRauth(aqid)
		}
	} else {
		req.RespondError(Enoauth)
	}

}

func (srv *Srv) authPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rauth {
		req.Afid.IncRef()
	}
}

func (srv *Srv) attach(req *Req) {
	tc := req.Tc
	conn := req.Conn
	if tc.Fid == ninep.NOFID {
		req.RespondError(Eunknownfid)
		return
	}

	req.Fid = conn.FidNew(tc.Fid)
	if req.Fid == nil {
		req.RespondError(Einuse)
		return
	}

	if tc.Afid != ninep.NOFID {
		req.Afid = conn.FidGet(tc.Afid)
		if req.Afid == nil {
			req.RespondError(Eunknownfid)
		}
	}

	var user ninep.User = nil
	if tc.Unamenum != ninep.NOUID || conn.Dotu {
		user = srv.Upool.Uid2User(int(tc.Unamenum))
	} else if tc.Uname != "" {
		user = srv.Upool.Uname2User(tc.Uname)
	}

	if user == nil {
		req.RespondError(Enouser)
		return
	}

	req.Fid.User = user
	if aop, ok := (srv.ops).(AuthOps); ok {
		err := aop.AuthCheck(req.Fid, req.Afid, tc.Aname)
		if err != nil {
			req.RespondError(err)
			return
		}
	}

	(srv.ops).(ReqOps).Attach(req)
}

func (srv *Srv) attachPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rattach {
		req.Fid.Type = req.Rc.Qid.Type
		req.Fid.IncRef()
	}
}

func (srv *Srv) flush(req *Req) {
	conn := req.Conn
	tag := req.Tc.Oldtag
	ninep.PackRflush(req.Rc)
	conn.Lock()
	r := conn.Reqs[tag]
	if r != nil {
		req.flushreq = r.flushreq
		r.flushreq = req
	}
	conn.Unlock()

	if r == nil {
		// there are no requests with that tag
		req.Respond()
		return
	}

	r.Lock()
	status := r.status
	if (status & (reqWork | reqSaved)) == 0 {
		/* the request is not worked on yet */
		r.status |= reqFlush
	}
	r.Unlock()

	if (status & (reqWork | reqSaved)) == 0 {
		r.Respond()
	} else {
		if op, ok := (srv.ops).(FlushOp); ok {
			op.Flush(r)
		}
	}
}

func (srv *Srv) walk(req *Req) {
	conn := req.Conn
	tc := req.Tc
	fid := req.Fid

	/* we can't walk regular files, only clone them */
	if len(tc.Wname) > 0 && (fid.Type&ninep.QTDIR) == 0 {
		req.RespondError(Enotdir)
		return
	}

	/* we can't walk open files */
	if fid.opened {
		req.RespondError(Ebaduse)
		return
	}

	if tc.Fid != tc.Newfid {
		req.Newfid = conn.FidNew(tc.Newfid)
		if req.Newfid == nil {
			req.RespondError(Einuse)
			return
		}

		req.Newfid.User = fid.User
		req.Newfid.Type = fid.Type
	} else {
		req.Newfid = req.Fid
		req.Newfid.IncRef()
	}

	(req.Conn.Srv.ops).(ReqOps).Walk(req)
}

func (srv *Srv) walkPost(req *Req) {
	rc := req.Rc
	if rc == nil || rc.Type != ninep.Rwalk || req.Newfid == nil {
		return
	}

	n := len(rc.Wqid)
	if n > 0 {
		req.Newfid.Type = rc.Wqid[n-1].Type
	} else {
		req.Newfid.Type = req.Fid.Type
	}

	// Don't retain the fid if only a partial walk succeeded
	if n != len(req.Tc.Wname) {
		return
	}

	if req.Newfid.fid != req.Fid.fid {
		req.Newfid.IncRef()
	}
}

func (srv *Srv) open(req *Req) {
	fid := req.Fid
	tc := req.Tc
	if fid.opened {
		req.RespondError(Eopen)
		return
	}

	if (fid.Type&ninep.QTDIR) != 0 && tc.Mode != ninep.OREAD {
		req.RespondError(Eperm)
		return
	}

	fid.Omode = tc.Mode
	(req.Conn.Srv.ops).(ReqOps).Open(req)
}

func (srv *Srv) openPost(req *Req) {
	if req.Fid != nil {
		req.Fid.opened = req.Rc != nil && req.Rc.Type == ninep.Ropen
	}
}

func (srv *Srv) create(req *Req) {
	fid := req.Fid
	tc := req.Tc
	if fid.opened {
		req.RespondError(Eopen)
		return
	}

	if (fid.Type & ninep.QTDIR) == 0 {
		req.RespondError(Enotdir)
		return
	}

	/* can't open directories for other than reading */
	if (tc.Perm&ninep.DMDIR) != 0 && tc.Mode != ninep.OREAD {
		req.RespondError(Eperm)
		return
	}

	/* can't create special files if not 9P2000.u */
	if (tc.Perm&(ninep.DMNAMEDPIPE|ninep.DMSYMLINK|ninep.DMLINK|ninep.DMDEVICE|ninep.DMSOCKET)) != 0 && !req.Conn.Dotu {
		req.RespondError(Eperm)
		return
	}

	fid.Omode = tc.Mode
	(req.Conn.Srv.ops).(ReqOps).Create(req)
}

func (srv *Srv) createPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rcreate && req.Fid != nil {
		req.Fid.Type = req.Rc.Qid.Type
		req.Fid.opened = true
	}
}

func (srv *Srv) read(req *Req) {
	tc := req.Tc
	fid := req.Fid
	if tc.Count+ninep.IOHDRSZ > req.Conn.Msize {
		req.RespondError(Etoolarge)
		return
	}

	if (fid.Type & ninep.QTAUTH) != 0 {
		var n int

		rc := req.Rc
		err := ninep.InitRread(rc, tc.Count)
		if err != nil {
			req.RespondError(err)
			return
		}

		if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
			n, err = op.AuthRead(fid, tc.Offset, rc.Data)
			if err != nil {
				req.RespondError(err)
				return
			}

			ninep.SetRreadCount(rc, uint32(n))
			req.Respond()
		} else {
			req.RespondError(Enotimpl)
		}

		return
	}

	if (fid.Type & ninep.QTDIR) != 0 {
		fid.Lock()
		if tc.Offset == 0 {
			fid.Diroffset = 0
		} else if tc.Offset != fid.Diroffset {
			// This used to be an error, at this
			// level. But maybe the provider can handle
			// offsets that change. In one version of 9p
			// we were able to support arbitrary
			// offsets. At the least, we're going to let
			// the provider decide if this is an error.
			fid.Diroffset = tc.Offset
		}
		fid.Unlock()
	}

	(req.Conn.Srv.ops).(ReqOps).Read(req)
}

func (srv *Srv) readPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rread && (req.Fid.Type&ninep.QTDIR) != 0 {
		req.Fid.Lock()
		req.Fid.Diroffset += uint64(req.Rc.Count)
		req.Fid.Unlock()
	}
}

func (srv *Srv) write(req *Req) {
	fid := req.Fid
	tc := req.Tc
	if (fid.Type & ninep.QTAUTH) != 0 {
		tc := req.Tc
		if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
			n, err := op.AuthWrite(req.Fid, tc.Offset, tc.Data)
			if err != nil {
				req.RespondError(err)
			} else {
				req.RespondRwrite(uint32(n))
			}
		} else {
			req.RespondError(Enotimpl)
		}

		return
	}

	if !fid.opened || (fid.Type&ninep.QTDIR) != 0 || (fid.Omode&3) == ninep.OREAD {
		req.RespondError(Ebaduse)
		return
	}

	if tc.Count+ninep.IOHDRSZ > req.Conn.Msize {
		req.RespondError(Etoolarge)
		return
	}

	(req.Conn.Srv.ops).(ReqOps).Write(req)
}

func (srv *Srv) clunk(req *Req) {
	fid := req.Fid
	if (fid.Type & ninep.QTAUTH) != 0 {
		if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
			op.AuthDestroy(fid)
			req.RespondRclunk()
		} else {
			req.RespondError(Enotimpl)
		}

		return
	}

	(req.Conn.Srv.ops).(ReqOps).Clunk(req)
}

func (srv *Srv) clunkPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rclunk && req.Fid != nil {
		req.Fid.DecRef()
	}
}

func (srv *Srv) remove(req *Req) { (req.Conn.Srv.ops).(ReqOps).Remove(req) }

func (srv *Srv) removePost(req *Req) {
	if req.Rc != nil && req.Fid != nil {
		req.Fid.DecRef()
	}
}

func (srv *Srv) stat(req *Req) { (req.Conn.Srv.ops).(ReqOps).Stat(req) }

func (srv *Srv) wstat(req *Req) {
	/*
		fid := req.Fid
		d := &req.Tc.Dir
		if d.Type != uint16(0xFFFF) || d.Dev != uint32(0xFFFFFFFF) || d.Version != uint32(0xFFFFFFFF) ||
			d.Path != uint64(0xFFFFFFFFFFFFFFFF) {
			req.RespondError(Eperm)
			return
		}

		if (d.Mode != 0xFFFFFFFF) && (((fid.Type&ninep.QTDIR) != 0 && (d.Mode&ninep.DMDIR) == 0) ||
			((d.Type&ninep.QTDIR) == 0 && (d.Mode&ninep.DMDIR) != 0)) {
			req.RespondError(Edirchange)
			return
		}
	*/

	(req.Conn.Srv.ops).(ReqOps).Wstat(req)
}
<commit_msg>Log failures due to fidinuse.<commit_after>// Copyright 2009 The Ninep 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 srv

import (
	"log"
	"sync/atomic"

	"github.com/lionkov/ninep"
)

func (srv *Srv) version(req *Req) {
	tc := req.Tc
	conn := req.Conn

	if tc.Msize < ninep.IOHDRSZ {
		req.RespondError(&ninep.Error{"msize too small", ninep.EINVAL})
		return
	}

	if tc.Msize < conn.Msize {
		conn.Msize = tc.Msize
	}

	conn.Dotu = tc.Version == "9P2000.u" && srv.Dotu
	ver := "9P2000"
	if conn.Dotu {
		ver = "9P2000.u"
	}

	/* make sure that the responses of all current requests will be ignored */
	conn.Lock()
	for tag, r := range conn.Reqs {
		if tag == ninep.NOTAG {
			continue
		}

		for rr := r; rr != nil; rr = rr.next {
			rr.Lock()
			rr.status |= reqFlush
			rr.Unlock()
		}
	}
	conn.Unlock()

	atomic.AddUint32(&srv.Versioned, 1)
	req.RespondRversion(conn.Msize, ver)
}

func (srv *Srv) auth(req *Req) {
	tc := req.Tc
	conn := req.Conn
	if tc.Afid == ninep.NOFID {
		req.RespondError(Eunknownfid)
		return
	}

	req.Afid = conn.FidNew(tc.Afid)
	if req.Afid == nil {
		log.Printf("in auth(): Fid %v in use?", tc.Afid)
		req.RespondError(Einuse)
		return
	}

	var user ninep.User = nil
	if tc.Unamenum != ninep.NOUID || conn.Dotu {
		user = srv.Upool.Uid2User(int(tc.Unamenum))
	} else if tc.Uname != "" {
		user = srv.Upool.Uname2User(tc.Uname)
	}

	if user == nil {
		req.RespondError(Enouser)
		return
	}

	req.Afid.User = user
	req.Afid.Type = ninep.QTAUTH
	if aop, ok := (srv.ops).(AuthOps); ok {
		aqid, err := aop.AuthInit(req.Afid, tc.Aname)
		if err != nil {
			req.RespondError(err)
		} else {
			aqid.Type |= ninep.QTAUTH // just in case
			req.RespondRauth(aqid)
		}
	} else {
		req.RespondError(Enoauth)
	}

}

func (srv *Srv) authPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rauth {
		req.Afid.IncRef()
	}
}

func (srv *Srv) attach(req *Req) {
	tc := req.Tc
	conn := req.Conn
	if tc.Fid == ninep.NOFID {
		req.RespondError(Eunknownfid)
		return
	}

	req.Fid = conn.FidNew(tc.Fid)
	if req.Fid == nil {
		log.Printf("attach: Fid %v in use? ", tc.Fid)
		req.RespondError(Einuse)
		return
	}

	if tc.Afid != ninep.NOFID {
		req.Afid = conn.FidGet(tc.Afid)
		if req.Afid == nil {
			req.RespondError(Eunknownfid)
		}
	}

	var user ninep.User = nil
	if tc.Unamenum != ninep.NOUID || conn.Dotu {
		user = srv.Upool.Uid2User(int(tc.Unamenum))
	} else if tc.Uname != "" {
		user = srv.Upool.Uname2User(tc.Uname)
	}

	if user == nil {
		req.RespondError(Enouser)
		return
	}

	req.Fid.User = user
	if aop, ok := (srv.ops).(AuthOps); ok {
		err := aop.AuthCheck(req.Fid, req.Afid, tc.Aname)
		if err != nil {
			req.RespondError(err)
			return
		}
	}

	(srv.ops).(ReqOps).Attach(req)
}

func (srv *Srv) attachPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rattach {
		req.Fid.Type = req.Rc.Qid.Type
		req.Fid.IncRef()
	}
}

func (srv *Srv) flush(req *Req) {
	conn := req.Conn
	tag := req.Tc.Oldtag
	ninep.PackRflush(req.Rc)
	conn.Lock()
	r := conn.Reqs[tag]
	if r != nil {
		req.flushreq = r.flushreq
		r.flushreq = req
	}
	conn.Unlock()

	if r == nil {
		// there are no requests with that tag
		req.Respond()
		return
	}

	r.Lock()
	status := r.status
	if (status & (reqWork | reqSaved)) == 0 {
		/* the request is not worked on yet */
		r.status |= reqFlush
	}
	r.Unlock()

	if (status & (reqWork | reqSaved)) == 0 {
		r.Respond()
	} else {
		if op, ok := (srv.ops).(FlushOp); ok {
			op.Flush(r)
		}
	}
}

func (srv *Srv) walk(req *Req) {
	conn := req.Conn
	tc := req.Tc
	fid := req.Fid

	/* we can't walk regular files, only clone them */
	if len(tc.Wname) > 0 && (fid.Type&ninep.QTDIR) == 0 {
		req.RespondError(Enotdir)
		return
	}

	/* we can't walk open files */
	if fid.opened {
		req.RespondError(Ebaduse)
		return
	}

	if tc.Fid != tc.Newfid {
		req.Newfid = conn.FidNew(tc.Newfid)
		if req.Newfid == nil {
			log.Printf("walk: fid %v in use? ", tc.Newfid)
			req.RespondError(Einuse)
			return
		}

		req.Newfid.User = fid.User
		req.Newfid.Type = fid.Type
	} else {
		req.Newfid = req.Fid
		req.Newfid.IncRef()
	}

	(req.Conn.Srv.ops).(ReqOps).Walk(req)
}

func (srv *Srv) walkPost(req *Req) {
	rc := req.Rc
	if rc == nil || rc.Type != ninep.Rwalk || req.Newfid == nil {
		return
	}

	n := len(rc.Wqid)
	if n > 0 {
		req.Newfid.Type = rc.Wqid[n-1].Type
	} else {
		req.Newfid.Type = req.Fid.Type
	}

	// Don't retain the fid if only a partial walk succeeded
	if n != len(req.Tc.Wname) {
		return
	}

	if req.Newfid.fid != req.Fid.fid {
		req.Newfid.IncRef()
	}
}

func (srv *Srv) open(req *Req) {
	fid := req.Fid
	tc := req.Tc
	if fid.opened {
		req.RespondError(Eopen)
		return
	}

	if (fid.Type&ninep.QTDIR) != 0 && tc.Mode != ninep.OREAD {
		req.RespondError(Eperm)
		return
	}

	fid.Omode = tc.Mode
	(req.Conn.Srv.ops).(ReqOps).Open(req)
}

func (srv *Srv) openPost(req *Req) {
	if req.Fid != nil {
		req.Fid.opened = req.Rc != nil && req.Rc.Type == ninep.Ropen
	}
}

func (srv *Srv) create(req *Req) {
	fid := req.Fid
	tc := req.Tc
	if fid.opened {
		req.RespondError(Eopen)
		return
	}

	if (fid.Type & ninep.QTDIR) == 0 {
		req.RespondError(Enotdir)
		return
	}

	/* can't open directories for other than reading */
	if (tc.Perm&ninep.DMDIR) != 0 && tc.Mode != ninep.OREAD {
		req.RespondError(Eperm)
		return
	}

	/* can't create special files if not 9P2000.u */
	if (tc.Perm&(ninep.DMNAMEDPIPE|ninep.DMSYMLINK|ninep.DMLINK|ninep.DMDEVICE|ninep.DMSOCKET)) != 0 && !req.Conn.Dotu {
		req.RespondError(Eperm)
		return
	}

	fid.Omode = tc.Mode
	(req.Conn.Srv.ops).(ReqOps).Create(req)
}

func (srv *Srv) createPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rcreate && req.Fid != nil {
		req.Fid.Type = req.Rc.Qid.Type
		req.Fid.opened = true
	}
}

func (srv *Srv) read(req *Req) {
	tc := req.Tc
	fid := req.Fid
	if tc.Count+ninep.IOHDRSZ > req.Conn.Msize {
		req.RespondError(Etoolarge)
		return
	}

	if (fid.Type & ninep.QTAUTH) != 0 {
		var n int

		rc := req.Rc
		err := ninep.InitRread(rc, tc.Count)
		if err != nil {
			req.RespondError(err)
			return
		}

		if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
			n, err = op.AuthRead(fid, tc.Offset, rc.Data)
			if err != nil {
				req.RespondError(err)
				return
			}

			ninep.SetRreadCount(rc, uint32(n))
			req.Respond()
		} else {
			req.RespondError(Enotimpl)
		}

		return
	}

	if (fid.Type & ninep.QTDIR) != 0 {
		fid.Lock()
		if tc.Offset == 0 {
			fid.Diroffset = 0
		} else if tc.Offset != fid.Diroffset {
			// This used to be an error, at this
			// level. But maybe the provider can handle
			// offsets that change. In one version of 9p
			// we were able to support arbitrary
			// offsets. At the least, we're going to let
			// the provider decide if this is an error.
			fid.Diroffset = tc.Offset
		}
		fid.Unlock()
	}

	(req.Conn.Srv.ops).(ReqOps).Read(req)
}

func (srv *Srv) readPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rread && (req.Fid.Type&ninep.QTDIR) != 0 {
		req.Fid.Lock()
		req.Fid.Diroffset += uint64(req.Rc.Count)
		req.Fid.Unlock()
	}
}

func (srv *Srv) write(req *Req) {
	fid := req.Fid
	tc := req.Tc
	if (fid.Type & ninep.QTAUTH) != 0 {
		tc := req.Tc
		if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
			n, err := op.AuthWrite(req.Fid, tc.Offset, tc.Data)
			if err != nil {
				req.RespondError(err)
			} else {
				req.RespondRwrite(uint32(n))
			}
		} else {
			req.RespondError(Enotimpl)
		}

		return
	}

	if !fid.opened || (fid.Type&ninep.QTDIR) != 0 || (fid.Omode&3) == ninep.OREAD {
		req.RespondError(Ebaduse)
		return
	}

	if tc.Count+ninep.IOHDRSZ > req.Conn.Msize {
		req.RespondError(Etoolarge)
		return
	}

	(req.Conn.Srv.ops).(ReqOps).Write(req)
}

func (srv *Srv) clunk(req *Req) {
	fid := req.Fid
	if (fid.Type & ninep.QTAUTH) != 0 {
		if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
			op.AuthDestroy(fid)
			req.RespondRclunk()
		} else {
			req.RespondError(Enotimpl)
		}

		return
	}

	(req.Conn.Srv.ops).(ReqOps).Clunk(req)
}

func (srv *Srv) clunkPost(req *Req) {
	if req.Rc != nil && req.Rc.Type == ninep.Rclunk && req.Fid != nil {
		req.Fid.DecRef()
	}
}

func (srv *Srv) remove(req *Req) { (req.Conn.Srv.ops).(ReqOps).Remove(req) }

func (srv *Srv) removePost(req *Req) {
	if req.Rc != nil && req.Fid != nil {
		req.Fid.DecRef()
	}
}

func (srv *Srv) stat(req *Req) { (req.Conn.Srv.ops).(ReqOps).Stat(req) }

func (srv *Srv) wstat(req *Req) {
	/*
		fid := req.Fid
		d := &req.Tc.Dir
		if d.Type != uint16(0xFFFF) || d.Dev != uint32(0xFFFFFFFF) || d.Version != uint32(0xFFFFFFFF) ||
			d.Path != uint64(0xFFFFFFFFFFFFFFFF) {
			req.RespondError(Eperm)
			return
		}

		if (d.Mode != 0xFFFFFFFF) && (((fid.Type&ninep.QTDIR) != 0 && (d.Mode&ninep.DMDIR) == 0) ||
			((d.Type&ninep.QTDIR) == 0 && (d.Mode&ninep.DMDIR) != 0)) {
			req.RespondError(Edirchange)
			return
		}
	*/

	(req.Conn.Srv.ops).(ReqOps).Wstat(req)
}
<|endoftext|>


<commit_before>package ssdb

import (
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	sock     *net.TCPConn
	recv_buf bytes.Buffer
}

func Connect(ip string, port int) (*Client, error) {
	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		return nil, err
	}
	sock, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		return nil, err
	}
	var c Client
	c.sock = sock
	return &c, nil
}

func (c *Client) Do(args ...interface{}) ([]string, error) {
	err := c.send(args)
	if err != nil {
		return nil, err
	}
	resp, err := c.recv()
	return resp, err
}

func (c *Client) Set(key string, val string) (interface{}, error) {
	resp, err := c.Do("set", key, val)
	if err != nil {
		return nil, err
	}
	if len(resp) == 2 && resp[0] == "ok" {
		return true, nil
	}
	return nil, fmt.Errorf("bad response")
}

// TODO: Will somebody write addition semantic methods?
func (c *Client) Get(key string) (interface{}, error) {
	resp, err := c.Do("get", key)
	if err != nil {
		return nil, err
	}
	if len(resp) == 2 && resp[0] == "ok" {
		return resp[1], nil
	}
	if resp[0] == "not_found" {
		return nil, nil
	}
	return nil, fmt.Errorf("bad response")
}

func (c *Client) Del(key string) (interface{}, error) {
	resp, err := c.Do("del", key)
	if err != nil {
		return nil, err
	}
	if len(resp) == 1 && resp[0] == "ok" {
		return true, nil
	}
	return nil, fmt.Errorf("bad response")
}

func (c *Client) send(args []interface{}) error {
	var buf bytes.Buffer
	for _, arg := range args {
		var s string
		switch arg := arg.(type) {
		case string:
			s = arg
		case []byte:
			s = string(arg)
		case []string:
			for _, s := range arg {
				buf.WriteString(fmt.Sprintf("%d", len(s)))
				buf.WriteByte('\n')
				buf.WriteString(s)
				buf.WriteByte('\n')
			}
			continue
		case int:
			s = fmt.Sprintf("%d", arg)
		case int64:
			s = fmt.Sprintf("%d", arg)
		case float64:
			s = fmt.Sprintf("%f", arg)
		case bool:
			if arg {
				s = "1"
			} else {
				s = "0"
			}
		case nil:
			s = ""
		default:
			return fmt.Errorf("bad arguments")
		}
		buf.WriteString(fmt.Sprintf("%d", len(s)))
		buf.WriteByte('\n')
		buf.WriteString(s)
		buf.WriteByte('\n')
	}
	buf.WriteByte('\n')
	_, err := c.sock.Write(buf.Bytes())
	return err
}

func (c *Client) recv() ([]string, error) {
	var tmp [8192]byte
	for {
		n, err := c.sock.Read(tmp[0:])
		if err != nil {
			return nil, err
		}
		c.recv_buf.Write(tmp[0:n])
		resp := c.parse()
		if resp == nil || len(resp) > 0 {
			return resp, nil
		}
	}
}

func (c *Client) parse() []string {
	resp := []string{}
	buf := c.recv_buf.Bytes()
	var idx, offset int
	idx = 0
	offset = 0

	for {
		idx = bytes.IndexByte(buf[offset:], '\n')
		if idx == -1 {
			break
		}
		p := buf[offset : offset+idx]
		offset += idx + 1
		//fmt.Printf("> [%s]\n", p);
		if len(p) == 0 || (len(p) == 1 && p[0] == '\r') {
			if len(resp) == 0 {
				continue
			} else {
				c.recv_buf.Next(offset)
				return resp
			}
		}

		size, err := strconv.Atoi(string(p))
		if err != nil || size < 0 {
			return nil
		}
		if offset+size >= c.recv_buf.Len() {
			break
		}

		v := buf[offset : offset+size]
		resp = append(resp, string(v))
		offset += size + 1
	}

	return []string{}
}

// Close The Client Connection
func (c *Client) Close() error {
	return c.sock.Close()
}
<commit_msg>change len(response) from 1 to 2 in Del response check<commit_after>package ssdb

import (
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	sock     *net.TCPConn
	recv_buf bytes.Buffer
}

func Connect(ip string, port int) (*Client, error) {
	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		return nil, err
	}
	sock, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		return nil, err
	}
	var c Client
	c.sock = sock
	return &c, nil
}

func (c *Client) Do(args ...interface{}) ([]string, error) {
	err := c.send(args)
	if err != nil {
		return nil, err
	}
	resp, err := c.recv()
	return resp, err
}

func (c *Client) Set(key string, val string) (interface{}, error) {
	resp, err := c.Do("set", key, val)
	if err != nil {
		return nil, err
	}
	if len(resp) == 2 && resp[0] == "ok" {
		return true, nil
	}
	return nil, fmt.Errorf("bad response")
}

// TODO: Will somebody write addition semantic methods?
func (c *Client) Get(key string) (interface{}, error) {
	resp, err := c.Do("get", key)
	if err != nil {
		return nil, err
	}
	if len(resp) == 2 && resp[0] == "ok" {
		return resp[1], nil
	}
	if resp[0] == "not_found" {
		return nil, nil
	}
	return nil, fmt.Errorf("bad response")
}

func (c *Client) Del(key string) (interface{}, error) {
	resp, err := c.Do("del", key)
	if err != nil {
		return nil, err
	}

        //response looks like this: [ok 1]
	if len(resp) == 2 && resp[0] == "ok" {
		return true, nil
	}
	return nil, fmt.Errorf("bad response:resp:%v:", resp)
}

func (c *Client) send(args []interface{}) error {
	var buf bytes.Buffer
	for _, arg := range args {
		var s string
		switch arg := arg.(type) {
		case string:
			s = arg
		case []byte:
			s = string(arg)
		case []string:
			for _, s := range arg {
				buf.WriteString(fmt.Sprintf("%d", len(s)))
				buf.WriteByte('\n')
				buf.WriteString(s)
				buf.WriteByte('\n')
			}
			continue
		case int:
			s = fmt.Sprintf("%d", arg)
		case int64:
			s = fmt.Sprintf("%d", arg)
		case float64:
			s = fmt.Sprintf("%f", arg)
		case bool:
			if arg {
				s = "1"
			} else {
				s = "0"
			}
		case nil:
			s = ""
		default:
			return fmt.Errorf("bad arguments")
		}
		buf.WriteString(fmt.Sprintf("%d", len(s)))
		buf.WriteByte('\n')
		buf.WriteString(s)
		buf.WriteByte('\n')
	}
	buf.WriteByte('\n')
	_, err := c.sock.Write(buf.Bytes())
	return err
}

func (c *Client) recv() ([]string, error) {
	var tmp [8192]byte
	for {
		n, err := c.sock.Read(tmp[0:])
		if err != nil {
			return nil, err
		}
		c.recv_buf.Write(tmp[0:n])
		resp := c.parse()
		if resp == nil || len(resp) > 0 {
			return resp, nil
		}
	}
}

func (c *Client) parse() []string {
	resp := []string{}
	buf := c.recv_buf.Bytes()
	var idx, offset int
	idx = 0
	offset = 0

	for {
		idx = bytes.IndexByte(buf[offset:], '\n')
		if idx == -1 {
			break
		}
		p := buf[offset : offset+idx]
		offset += idx + 1
		//fmt.Printf("> [%s]\n", p);
		if len(p) == 0 || (len(p) == 1 && p[0] == '\r') {
			if len(resp) == 0 {
				continue
			} else {
				c.recv_buf.Next(offset)
				return resp
			}
		}

		size, err := strconv.Atoi(string(p))
		if err != nil || size < 0 {
			return nil
		}
		if offset+size >= c.recv_buf.Len() {
			break
		}

		v := buf[offset : offset+size]
		resp = append(resp, string(v))
		offset += size + 1
	}

	return []string{}
}

// Close The Client Connection
func (c *Client) Close() error {
	return c.sock.Close()
}
<|endoftext|>


<commit_before>package aws_role

import (
	"context"
	"crypto"
	"net/http"

	"github.com/Cloud-Foundations/golib/pkg/log"
)

type Params struct {
	// Required parameters.
	KeymasterServer string
	Logger          log.DebugLogger
	// Optional parameters.
	Context    context.Context
	HttpClient *http.Client
	KeyType    string // "RSA"
	Signer     crypto.Signer
	derPubKey  []byte
	pemPubKey  []byte
}

// GetRoleCertificate requests an AWS role identify certificate from the
// Keymaster server specified in params. It returns the certificate DER.
func GetRoleCertificate(params Params) ([]byte, error) {
	return params.getRoleCertificate()
}
<commit_msg>Fix comment.<commit_after>package aws_role

import (
	"context"
	"crypto"
	"net/http"

	"github.com/Cloud-Foundations/golib/pkg/log"
)

type Params struct {
	// Required parameters.
	KeymasterServer string
	Logger          log.DebugLogger
	// Optional parameters.
	Context    context.Context
	HttpClient *http.Client
	KeyType    string // "RSA"
	Signer     crypto.Signer
	derPubKey  []byte
	pemPubKey  []byte
}

// GetRoleCertificate requests an AWS role identify certificate from the
// Keymaster server specified in params. It returns the certificate PEM.
func GetRoleCertificate(params Params) ([]byte, error) {
	return params.getRoleCertificate()
}
<|endoftext|>


<commit_before>package issue

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/emicklei/go-restful"
	"github.com/facebookgo/stackerr"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/validator.v2"

	"github.com/bearded-web/bearded/models/comment"
	"github.com/bearded-web/bearded/models/issue"
	"github.com/bearded-web/bearded/pkg/filters"
	"github.com/bearded-web/bearded/pkg/fltr"
	"github.com/bearded-web/bearded/pkg/manager"
	"github.com/bearded-web/bearded/pkg/pagination"
	"github.com/bearded-web/bearded/services"
)

const ParamId = "issueId"

type IssueService struct {
	*services.BaseService
}

func New(base *services.BaseService) *IssueService {
	return &IssueService{
		BaseService: base,
	}
}

func addDefaults(r *restful.RouteBuilder) {
	r.Notes("Authorization required")
	r.Do(services.ReturnsE(
		http.StatusUnauthorized,
		http.StatusForbidden,
		http.StatusInternalServerError,
	))
}

func (s *IssueService) Register(container *restful.Container) {
	ws := &restful.WebService{}
	ws.Path("/api/v1/issues")
	ws.Doc("Manage Issues")
	ws.Consumes(restful.MIME_JSON)
	ws.Produces(restful.MIME_JSON)
	ws.Filter(filters.AuthRequiredFilter(s.BaseManager()))

	r := ws.GET("").To(s.list)
	addDefaults(r)
	r.Doc("list")
	r.Operation("list")
	s.SetParams(r, fltr.GetParams(ws, manager.IssueFltr{}))
	r.Writes(issue.TargetIssueList{})
	r.Do(services.Returns(http.StatusOK))
	ws.Route(r)

	r = ws.POST("").To(s.create)
	addDefaults(r)
	r.Doc("create")
	r.Operation("create")
	r.Writes(issue.TargetIssue{})
	r.Reads(TargetIssueEntity{})
	r.Do(services.Returns(http.StatusCreated))
	r.Do(services.ReturnsE(
		http.StatusBadRequest,
		http.StatusConflict,
	))
	ws.Route(r)

	r = ws.GET(fmt.Sprintf("{%s}", ParamId)).To(s.TakeIssue(s.get))
	addDefaults(r)
	r.Doc("get")
	r.Operation("get")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Writes(issue.TargetIssue{})
	r.Do(services.Returns(
		http.StatusOK,
		http.StatusNotFound))
	r.Do(services.ReturnsE(http.StatusBadRequest))
	ws.Route(r)

	r = ws.PUT(fmt.Sprintf("{%s}", ParamId)).To(s.TakeIssue(s.update))
	// docs
	r.Doc("update")
	r.Operation("update")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Writes(issue.TargetIssue{})
	r.Reads(TargetIssueEntity{})
	r.Do(services.Returns(
		http.StatusOK,
		http.StatusNotFound))
	r.Do(services.ReturnsE(http.StatusBadRequest))
	ws.Route(r)

	r = ws.DELETE(fmt.Sprintf("{%s}", ParamId)).To(s.TakeIssue(s.delete))
	// docs
	r.Doc("delete")
	r.Operation("delete")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Do(services.Returns(
		http.StatusNoContent,
		http.StatusNotFound))
	r.Do(services.ReturnsE(http.StatusBadRequest))
	ws.Route(r)

	r = ws.GET(fmt.Sprintf("{%s}/comments", ParamId)).To(s.TakeIssue(s.comments))
	r.Doc("comments")
	r.Operation("comments")
	r.Param(ws.PathParameter(ParamId, ""))
	//	s.SetParams(r, fltr.GetParams(ws, manager.CommentFltr{}))
	r.Writes(comment.CommentList{})
	r.Do(services.Returns(
		http.StatusOK,
		http.StatusNotFound))
	ws.Route(r)

	r = ws.POST(fmt.Sprintf("{%s}/comments", ParamId)).To(s.TakeIssue(s.commentsAdd))
	r.Doc("commentsAdd")
	r.Operation("commentsAdd")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Reads(CommentEntity{})
	r.Writes(comment.Comment{})
	r.Do(services.Returns(
		http.StatusCreated,
		http.StatusNotFound))
	ws.Route(r)

	container.Add(ws)
}

// ====== service operations

func (s *IssueService) create(req *restful.Request, resp *restful.Response) {
	// TODO (m0sth8): Check permissions
	raw := &TargetIssueEntity{}

	if err := req.ReadEntity(raw); err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(
			http.StatusBadRequest,
			services.WrongEntityErr,
		)
		return
	}
	// check target field, it must be present
	if !s.IsId(raw.Target) {
		resp.WriteServiceError(
			http.StatusBadRequest,
			services.NewBadReq("Target is wrong"),
		)
		return
	}
	// validate other fields
	if err := validator.WithTag("creating").Validate(raw); err != nil {
		resp.WriteServiceError(
			http.StatusBadRequest,
			services.NewBadReq("Validation error: %s", err.Error()),
		)
		return
	}

	mgr := s.Manager()
	defer mgr.Close()

	// load target and project
	t, err := mgr.Targets.GetById(mgr.ToId(raw.Target))
	if err != nil {
		if mgr.IsNotFound(err) {
			resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Target not found"))
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	p, err := mgr.Projects.GetById(t.Project)
	if err != nil {
		if mgr.IsNotFound(err) {
			// This situation is really strange
			logrus.Errorf("Target %s is existed, but his project %s isn't",
				raw.Target, mgr.FromId(t.Project))
			resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Project not found"))
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	//	current user should have a permission to create issue there
	u := filters.GetUser(req)
	if !mgr.Permission.HasProjectAccess(p, u) {
		resp.WriteServiceError(http.StatusForbidden, services.AuthForbidErr)
		return
	}

	newObj := &issue.TargetIssue{
		Project: p.Id,
		Target:  t.Id,
	}
	updateTargetIssue(raw, newObj)
	newObj.AddUserReportActivity(u.Id)

	obj, err := mgr.Issues.Create(newObj)
	if err != nil {
		if mgr.IsDup(err) {
			resp.WriteServiceError(
				http.StatusConflict,
				services.DuplicateErr)
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	resp.WriteHeader(http.StatusCreated)
	resp.WriteEntity(obj)
}

func (s *IssueService) list(req *restful.Request, resp *restful.Response) {
	query, err := fltr.FromRequest(req, manager.IssueFltr{})
	if err != nil {
		resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq(err.Error()))
		return
	}

	mgr := s.Manager()
	defer mgr.Close()

	results, count, err := mgr.Issues.FilterByQuery(query)
	if err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	result := &issue.TargetIssueList{
		Meta:    pagination.Meta{Count: count},
		Results: results,
	}
	resp.WriteEntity(result)
}

func (s *IssueService) get(_ *restful.Request, resp *restful.Response, issueObj *issue.TargetIssue) {
	resp.WriteEntity(issueObj)
}

func (s *IssueService) update(req *restful.Request, resp *restful.Response, issueObj *issue.TargetIssue) {
	// TODO (m0sth8): Check permissions

	raw := &TargetIssueEntity{}

	if err := req.ReadEntity(raw); err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusBadRequest, services.WrongEntityErr)
		return
	}
	mgr := s.Manager()
	defer mgr.Close()

	// update issue object from entity
	updateTargetIssue(raw, issueObj)

	if err := mgr.Issues.Update(issueObj); err != nil {
		if mgr.IsNotFound(err) {
			resp.WriteErrorString(http.StatusNotFound, "Not found")
			return
		}
		if mgr.IsDup(err) {
			resp.WriteServiceError(
				http.StatusConflict,
				services.DuplicateErr)
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	resp.WriteHeader(http.StatusOK)
	resp.WriteEntity(issueObj)
}

func (s *IssueService) delete(_ *restful.Request, resp *restful.Response, obj *issue.TargetIssue) {
	mgr := s.Manager()
	defer mgr.Close()

	mgr.Issues.Remove(obj)
	resp.WriteHeader(http.StatusNoContent)
}

func (s *IssueService) TakeIssue(fn func(*restful.Request,
	*restful.Response, *issue.TargetIssue)) restful.RouteFunction {
	return func(req *restful.Request, resp *restful.Response) {
		id := req.PathParameter(ParamId)
		if !s.IsId(id) {
			resp.WriteServiceError(http.StatusBadRequest, services.IdHexErr)
			return
		}

		mgr := s.Manager()
		defer mgr.Close()

		obj, err := mgr.Issues.GetById(mgr.ToId(id))
		if err != nil {
			if mgr.IsNotFound(err) {
				resp.WriteErrorString(http.StatusNotFound, "Not found")
				return
			}
			logrus.Error(stackerr.Wrap(err))
			resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
			return
		}

		if !s.hasProjectPermission(req, resp, obj.Project) {
			resp.WriteServiceError(http.StatusForbidden, services.AuthForbidErr)
			return
		}

		mgr.Close()
		fn(req, resp, obj)
	}
}

func (s *IssueService) hasProjectPermission(req *restful.Request, resp *restful.Response,
	projectId bson.ObjectId) bool {

	mgr := s.Manager()
	defer mgr.Close()

	p, err := mgr.Projects.GetById(projectId)
	if err != nil {
		if mgr.IsNotFound(err) {
			resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Project not found"))
			return false
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return false
	}

	//	current user should have a permission to create issue there
	u := filters.GetUser(req)
	if !mgr.Permission.HasProjectAccess(p, u) {
		logrus.Warnf("User %s try to access to project %s", u, p)
		return false
	}
	return true
}

func (s *IssueService) comments(_ *restful.Request, resp *restful.Response, obj *issue.TargetIssue) {
	mgr := s.Manager()
	defer mgr.Close()

	results, count, err := mgr.Comments.FilterBy(&manager.CommentFltr{Type: comment.Issue, Link: obj.Id})

	if err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	result := &comment.CommentList{
		Meta:    pagination.Meta{Count: count},
		Results: results,
	}
	resp.WriteEntity(result)
}

func (s *IssueService) commentsAdd(req *restful.Request, resp *restful.Response, t *issue.TargetIssue) {
	ent := &CommentEntity{}
	if err := req.ReadEntity(ent); err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusBadRequest, services.WrongEntityErr)
		return
	}

	if len(ent.Text) == 0 {
		resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Text is required"))
		return
	}

	u := filters.GetUser(req)
	raw := &comment.Comment{
		Owner: u.Id,
		Type:  comment.Issue,
		Link:  t.Id,
		Text:  ent.Text,
	}

	mgr := s.Manager()
	defer mgr.Close()

	obj, err := mgr.Comments.Create(raw)
	if err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	resp.WriteHeader(http.StatusCreated)
	resp.WriteEntity(obj)
}
<commit_msg>reformat service<commit_after>package issue

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/emicklei/go-restful"
	"github.com/facebookgo/stackerr"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/validator.v2"

	"github.com/bearded-web/bearded/models/comment"
	"github.com/bearded-web/bearded/models/issue"
	"github.com/bearded-web/bearded/pkg/filters"
	"github.com/bearded-web/bearded/pkg/fltr"
	"github.com/bearded-web/bearded/pkg/manager"
	"github.com/bearded-web/bearded/pkg/pagination"
	"github.com/bearded-web/bearded/services"
)

const ParamId = "issueId"

type IssueService struct {
	*services.BaseService
}

func New(base *services.BaseService) *IssueService {
	return &IssueService{
		BaseService: base,
	}
}

func addDefaults(r *restful.RouteBuilder) {
	r.Notes("Authorization required")
	r.Do(services.ReturnsE(
		http.StatusUnauthorized,
		http.StatusForbidden,
		http.StatusInternalServerError,
	))
}

func (s *IssueService) Register(container *restful.Container) {
	ws := &restful.WebService{}
	ws.Path("/api/v1/issues")
	ws.Doc("Manage Issues")
	ws.Consumes(restful.MIME_JSON)
	ws.Produces(restful.MIME_JSON)
	ws.Filter(filters.AuthRequiredFilter(s.BaseManager()))

	r := ws.GET("").To(s.list)
	addDefaults(r)
	r.Doc("list")
	r.Operation("list")
	s.SetParams(r, fltr.GetParams(ws, manager.IssueFltr{}))
	r.Writes(issue.TargetIssueList{})
	r.Do(services.Returns(http.StatusOK))
	ws.Route(r)

	r = ws.POST("").To(s.create)
	addDefaults(r)
	r.Doc("create")
	r.Operation("create")
	r.Writes(issue.TargetIssue{})
	r.Reads(TargetIssueEntity{})
	r.Do(services.Returns(http.StatusCreated))
	r.Do(services.ReturnsE(
		http.StatusBadRequest,
		http.StatusConflict,
	))
	ws.Route(r)

	r = ws.GET(fmt.Sprintf("{%s}", ParamId)).To(s.TakeIssue(s.get))
	addDefaults(r)
	r.Doc("get")
	r.Operation("get")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Writes(issue.TargetIssue{})
	r.Do(services.Returns(
		http.StatusOK,
		http.StatusNotFound))
	r.Do(services.ReturnsE(http.StatusBadRequest))
	ws.Route(r)

	r = ws.PUT(fmt.Sprintf("{%s}", ParamId)).To(s.TakeIssue(s.update))
	// docs
	r.Doc("update")
	r.Operation("update")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Writes(issue.TargetIssue{})
	r.Reads(TargetIssueEntity{})
	r.Do(services.Returns(
		http.StatusOK,
		http.StatusNotFound))
	r.Do(services.ReturnsE(http.StatusBadRequest))
	ws.Route(r)

	r = ws.DELETE(fmt.Sprintf("{%s}", ParamId)).To(s.TakeIssue(s.delete))
	// docs
	r.Doc("delete")
	r.Operation("delete")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Do(services.Returns(
		http.StatusNoContent,
		http.StatusNotFound))
	r.Do(services.ReturnsE(http.StatusBadRequest))
	ws.Route(r)

	r = ws.GET(fmt.Sprintf("{%s}/comments", ParamId)).To(s.TakeIssue(s.comments))
	r.Doc("comments")
	r.Operation("comments")
	r.Param(ws.PathParameter(ParamId, ""))
	//	s.SetParams(r, fltr.GetParams(ws, manager.CommentFltr{}))
	r.Writes(comment.CommentList{})
	r.Do(services.Returns(
		http.StatusOK,
		http.StatusNotFound))
	ws.Route(r)

	r = ws.POST(fmt.Sprintf("{%s}/comments", ParamId)).To(s.TakeIssue(s.commentsAdd))
	r.Doc("commentsAdd")
	r.Operation("commentsAdd")
	r.Param(ws.PathParameter(ParamId, ""))
	r.Reads(CommentEntity{})
	r.Writes(comment.Comment{})
	r.Do(services.Returns(
		http.StatusCreated,
		http.StatusNotFound))
	ws.Route(r)

	container.Add(ws)
}

// ====== service operations

func (s *IssueService) create(req *restful.Request, resp *restful.Response) {
	// TODO (m0sth8): Check permissions
	raw := &TargetIssueEntity{}

	if err := req.ReadEntity(raw); err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(
			http.StatusBadRequest,
			services.WrongEntityErr,
		)
		return
	}
	// check target field, it must be present
	if !s.IsId(raw.Target) {
		resp.WriteServiceError(
			http.StatusBadRequest,
			services.NewBadReq("Target is wrong"),
		)
		return
	}
	// validate other fields
	if err := validator.WithTag("creating").Validate(raw); err != nil {
		resp.WriteServiceError(
			http.StatusBadRequest,
			services.NewBadReq("Validation error: %s", err.Error()),
		)
		return
	}

	mgr := s.Manager()
	defer mgr.Close()

	// load target and project
	t, err := mgr.Targets.GetById(mgr.ToId(raw.Target))
	if err != nil {
		if mgr.IsNotFound(err) {
			resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Target not found"))
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	p, err := mgr.Projects.GetById(t.Project)
	if err != nil {
		if mgr.IsNotFound(err) {
			// This situation is really strange
			logrus.Errorf("Target %s is existed, but his project %s isn't",
				raw.Target, mgr.FromId(t.Project))
			resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Project not found"))
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	//	current user should have a permission to create issue there
	u := filters.GetUser(req)
	if !mgr.Permission.HasProjectAccess(p, u) {
		resp.WriteServiceError(http.StatusForbidden, services.AuthForbidErr)
		return
	}

	newObj := &issue.TargetIssue{
		Project: p.Id,
		Target:  t.Id,
	}
	updateTargetIssue(raw, newObj)
	newObj.AddUserReportActivity(u.Id)

	obj, err := mgr.Issues.Create(newObj)
	if err != nil {
		if mgr.IsDup(err) {
			resp.WriteServiceError(
				http.StatusConflict,
				services.DuplicateErr)
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	resp.WriteHeader(http.StatusCreated)
	resp.WriteEntity(obj)
}

func (s *IssueService) list(req *restful.Request, resp *restful.Response) {
	query, err := fltr.FromRequest(req, manager.IssueFltr{})
	if err != nil {
		resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq(err.Error()))
		return
	}

	mgr := s.Manager()
	defer mgr.Close()

	results, count, err := mgr.Issues.FilterByQuery(query)
	if err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	result := &issue.TargetIssueList{
		Meta:    pagination.Meta{Count: count},
		Results: results,
	}
	resp.WriteEntity(result)
}

func (s *IssueService) get(_ *restful.Request, resp *restful.Response, issueObj *issue.TargetIssue) {
	resp.WriteEntity(issueObj)
}

func (s *IssueService) update(req *restful.Request, resp *restful.Response, issueObj *issue.TargetIssue) {
	// TODO (m0sth8): Check permissions

	raw := &TargetIssueEntity{}

	if err := req.ReadEntity(raw); err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusBadRequest, services.WrongEntityErr)
		return
	}
	mgr := s.Manager()
	defer mgr.Close()

	// update issue object from entity
	updateTargetIssue(raw, issueObj)

	if err := mgr.Issues.Update(issueObj); err != nil {
		if mgr.IsNotFound(err) {
			resp.WriteErrorString(http.StatusNotFound, "Not found")
			return
		}
		if mgr.IsDup(err) {
			resp.WriteServiceError(
				http.StatusConflict,
				services.DuplicateErr)
			return
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	resp.WriteHeader(http.StatusOK)
	resp.WriteEntity(issueObj)
}

func (s *IssueService) delete(_ *restful.Request, resp *restful.Response, obj *issue.TargetIssue) {
	mgr := s.Manager()
	defer mgr.Close()

	mgr.Issues.Remove(obj)
	resp.WriteHeader(http.StatusNoContent)
}

func (s *IssueService) comments(_ *restful.Request, resp *restful.Response, obj *issue.TargetIssue) {
	mgr := s.Manager()
	defer mgr.Close()

	results, count, err := mgr.Comments.FilterBy(&manager.CommentFltr{Type: comment.Issue, Link: obj.Id})

	if err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	result := &comment.CommentList{
		Meta:    pagination.Meta{Count: count},
		Results: results,
	}
	resp.WriteEntity(result)
}

func (s *IssueService) commentsAdd(req *restful.Request, resp *restful.Response, t *issue.TargetIssue) {
	ent := &CommentEntity{}
	if err := req.ReadEntity(ent); err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusBadRequest, services.WrongEntityErr)
		return
	}

	if len(ent.Text) == 0 {
		resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Text is required"))
		return
	}

	u := filters.GetUser(req)
	raw := &comment.Comment{
		Owner: u.Id,
		Type:  comment.Issue,
		Link:  t.Id,
		Text:  ent.Text,
	}

	mgr := s.Manager()
	defer mgr.Close()

	obj, err := mgr.Comments.Create(raw)
	if err != nil {
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return
	}

	resp.WriteHeader(http.StatusCreated)
	resp.WriteEntity(obj)
}

// Helpers

func (s *IssueService) TakeIssue(fn func(*restful.Request,
	*restful.Response, *issue.TargetIssue)) restful.RouteFunction {
	return func(req *restful.Request, resp *restful.Response) {
		id := req.PathParameter(ParamId)
		if !s.IsId(id) {
			resp.WriteServiceError(http.StatusBadRequest, services.IdHexErr)
			return
		}

		mgr := s.Manager()
		defer mgr.Close()

		obj, err := mgr.Issues.GetById(mgr.ToId(id))
		if err != nil {
			if mgr.IsNotFound(err) {
				resp.WriteErrorString(http.StatusNotFound, "Not found")
				return
			}
			logrus.Error(stackerr.Wrap(err))
			resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
			return
		}

		if !s.hasProjectPermission(req, resp, obj.Project) {
			resp.WriteServiceError(http.StatusForbidden, services.AuthForbidErr)
			return
		}

		mgr.Close()
		fn(req, resp, obj)
	}
}

func (s *IssueService) hasProjectPermission(req *restful.Request, resp *restful.Response,
	projectId bson.ObjectId) bool {

	mgr := s.Manager()
	defer mgr.Close()

	p, err := mgr.Projects.GetById(projectId)
	if err != nil {
		if mgr.IsNotFound(err) {
			resp.WriteServiceError(http.StatusBadRequest, services.NewBadReq("Project not found"))
			return false
		}
		logrus.Error(stackerr.Wrap(err))
		resp.WriteServiceError(http.StatusInternalServerError, services.DbErr)
		return false
	}

	//	current user should have a permission to create issue there
	u := filters.GetUser(req)
	if !mgr.Permission.HasProjectAccess(p, u) {
		logrus.Warnf("User %s try to access to project %s", u, p)
		return false
	}
	return true
}
<|endoftext|>


<commit_before>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cmdflag unifies the configuration of stores using command line flags across
// several tools.
//
// FIXME: Need a more coherent way of doing this: it's now a huge mess.
package cmdflag

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/mitchellh/goamz/aws"

	"tchaik.com/store"
	"tchaik.com/store/cafs"
)

var localStore, remoteStore string
var mediaFileSystemCache, artworkFileSystemCache string
var trimPathPrefix, addPathPrefix string

func init() {
	flag.StringVar(&localStore, "local-store", "/", "local media store, full local path /path/to/root")
	flag.StringVar(&remoteStore, "remote-store", "", "remote media store, tchstore server address <hostname>:<port>, s3://<bucket>/path/to/root for S3, or gs://<bucket>/path/to/root for Google Cloud Storage")

	flag.StringVar(&artworkFileSystemCache, "artwork-cache", "", "path to local artwork cache (content addressable)")
	flag.StringVar(&mediaFileSystemCache, "media-cache", "", "path to local media cache")

	flag.StringVar(&trimPathPrefix, "trim-path-prefix", "", "remove prefix from every path")
	flag.StringVar(&addPathPrefix, "add-path-prefix", "", "add prefix to every path")
}

type stores struct {
	media, artwork store.FileSystem
}

func buildRemoteStore(s *stores) (err error) {
	if remoteStore == "" {
		return nil
	}

	var c store.Client
	switch {
	case strings.HasPrefix(remoteStore, "s3://"):
		path := strings.TrimPrefix(remoteStore, "s3://")
		bucketPathSplit := strings.Split(path, "/")

		if len(bucketPathSplit) == 0 {
			return fmt.Errorf("invalid S3 path: %#v", remoteStore)
		}
		bucket := bucketPathSplit[0]
		var auth aws.Auth
		auth, err = aws.GetAuth("", "") // Extract credentials from the current instance.
		if err != nil {
			return fmt.Errorf("error getting AWS credentials: %v", err)
		}
		c = store.NewS3Client(bucket, auth, aws.APSoutheast2)

	case strings.HasPrefix(remoteStore, "gs://"):
		path := strings.TrimPrefix(remoteStore, "gs://")
		bucketPathSplit := strings.Split(path, "/")

		if len(bucketPathSplit) == 0 {
			return fmt.Errorf("invalid Google Cloud Storage path: %#v", remoteStore)
		}
		bucket := bucketPathSplit[0]
		if len(bucket) == 0 {
			return fmt.Errorf("invalid Google Cloud Storage path: %#v", remoteStore)
		}
		c = store.NewCloudStorageClient(bucket)

	default:
		c = store.NewClient(remoteStore, "")
		s.artwork = store.NewRemoteFileSystem(store.NewClient(remoteStore, "artwork"))
	}

	s.media = store.NewRemoteChunkedFileSystem(c, 32*1024)
	if s.artwork == nil {
		s.artwork = store.Trace(store.ArtworkFileSystem(s.media), "artwork")
	}
	return nil
}

func buildLocalStore(s *stores) {
	if localStore != "" {
		fs := store.NewFileSystem(http.Dir(localStore), fmt.Sprintf("localstore (%v)", localStore))
		if s.media != nil {
			s.media = store.MultiFileSystem(fs, s.media)
		} else {
			s.media = fs
		}

		afs := store.Trace(store.ArtworkFileSystem(fs), "local artworkstore")
		if s.artwork != nil {
			s.artwork = store.MultiFileSystem(afs, s.artwork)
		} else {
			s.artwork = afs
		}
	}
}

func buildMediaCache(s *stores) {
	if mediaFileSystemCache != "" {
		var errCh <-chan error
		localCache := store.Dir(mediaFileSystemCache)
		s.media, errCh = store.NewCachedFileSystem(s.media, localCache)
		go func() {
			for err := range errCh {
				// TODO: pull this out!
				log.Printf("mediaFileSystem cache: %v", err)
			}
		}()
	}
}

func buildArtworkCache(s *stores) error {
	if artworkFileSystemCache != "" {
		cfs, err := cafs.New(store.Dir(artworkFileSystemCache))
		if err != nil {
			return fmt.Errorf("error creating artwork cafs: %v", err)
		}

		var errCh <-chan error
		s.artwork, errCh = store.NewCachedFileSystem(
			s.artwork,
			cfs,
		)
		go func() {
			for err := range errCh {
				// TODO: pull this out!
				log.Printf("artwork cache: %v", err)
			}
		}()
	}
	return nil
}

// Stores returns a media and artwork filesystem as defined by the command line flags.
func Stores() (media, artwork store.FileSystem, err error) {
	s := &stores{}
	err = buildRemoteStore(s)
	if err != nil {
		return nil, nil, err
	}

	buildLocalStore(s)
	buildMediaCache(s)

	err = buildArtworkCache(s)
	if err != nil {
		return nil, nil, err
	}

	if trimPathPrefix != "" || addPathPrefix != "" {
		s.media = store.PathRewrite(s.media, trimPathPrefix, addPathPrefix)
		s.artwork = store.PathRewrite(s.artwork, trimPathPrefix, addPathPrefix)
	}
	return s.media, s.artwork, nil
}
<commit_msg>Wrap cmdflag clients with new TraceClient.<commit_after>// Copyright 2015, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cmdflag unifies the configuration of stores using command line flags across
// several tools.
//
// FIXME: Need a more coherent way of doing this: it's now a huge mess.
package cmdflag

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/mitchellh/goamz/aws"

	"tchaik.com/store"
	"tchaik.com/store/cafs"
)

var localStore, remoteStore string
var mediaFileSystemCache, artworkFileSystemCache string
var trimPathPrefix, addPathPrefix string

func init() {
	flag.StringVar(&localStore, "local-store", "/", "local media store, full local path /path/to/root")
	flag.StringVar(&remoteStore, "remote-store", "", "remote media store, tchstore server address <hostname>:<port>, s3://<bucket>/path/to/root for S3, or gs://<bucket>/path/to/root for Google Cloud Storage")

	flag.StringVar(&artworkFileSystemCache, "artwork-cache", "", "path to local artwork cache (content addressable)")
	flag.StringVar(&mediaFileSystemCache, "media-cache", "", "path to local media cache")

	flag.StringVar(&trimPathPrefix, "trim-path-prefix", "", "remove prefix from every path")
	flag.StringVar(&addPathPrefix, "add-path-prefix", "", "add prefix to every path")
}

type stores struct {
	media, artwork store.FileSystem
}

func buildRemoteStore(s *stores) (err error) {
	if remoteStore == "" {
		return nil
	}

	var c store.Client
	switch {
	case strings.HasPrefix(remoteStore, "s3://"):
		path := strings.TrimPrefix(remoteStore, "s3://")
		bucketPathSplit := strings.Split(path, "/")

		if len(bucketPathSplit) == 0 {
			return fmt.Errorf("invalid S3 path: %#v", remoteStore)
		}
		bucket := bucketPathSplit[0]
		var auth aws.Auth
		auth, err = aws.GetAuth("", "") // Extract credentials from the current instance.
		if err != nil {
			return fmt.Errorf("error getting AWS credentials: %v", err)
		}
		c = store.TraceClient(store.NewS3Client(bucket, auth, aws.APSoutheast2), "S3")

	case strings.HasPrefix(remoteStore, "gs://"):
		path := strings.TrimPrefix(remoteStore, "gs://")
		bucketPathSplit := strings.Split(path, "/")

		if len(bucketPathSplit) == 0 {
			return fmt.Errorf("invalid Google Cloud Storage path: %#v", remoteStore)
		}
		bucket := bucketPathSplit[0]
		if len(bucket) == 0 {
			return fmt.Errorf("invalid Google Cloud Storage path: %#v", remoteStore)
		}
		c = store.TraceClient(store.NewCloudStorageClient(bucket), "CloudStorage")

	default:
		c = store.TraceClient(store.NewClient(remoteStore, ""), "tchstore")
		s.artwork = store.NewRemoteFileSystem(store.NewClient(remoteStore, "artwork"))
	}

	s.media = store.NewRemoteChunkedFileSystem(c, 32*1024)
	if s.artwork == nil {
		s.artwork = store.Trace(store.ArtworkFileSystem(s.media), "artwork")
	}
	return nil
}

func buildLocalStore(s *stores) {
	if localStore != "" {
		fs := store.NewFileSystem(http.Dir(localStore), fmt.Sprintf("localstore (%v)", localStore))
		if s.media != nil {
			s.media = store.MultiFileSystem(fs, s.media)
		} else {
			s.media = fs
		}

		afs := store.Trace(store.ArtworkFileSystem(fs), "local artworkstore")
		if s.artwork != nil {
			s.artwork = store.MultiFileSystem(afs, s.artwork)
		} else {
			s.artwork = afs
		}
	}
}

func buildMediaCache(s *stores) {
	if mediaFileSystemCache != "" {
		var errCh <-chan error
		localCache := store.Dir(mediaFileSystemCache)
		s.media, errCh = store.NewCachedFileSystem(s.media, localCache)
		go func() {
			for err := range errCh {
				// TODO: pull this out!
				log.Printf("mediaFileSystem cache: %v", err)
			}
		}()
	}
}

func buildArtworkCache(s *stores) error {
	if artworkFileSystemCache != "" {
		cfs, err := cafs.New(store.Dir(artworkFileSystemCache))
		if err != nil {
			return fmt.Errorf("error creating artwork cafs: %v", err)
		}

		var errCh <-chan error
		s.artwork, errCh = store.NewCachedFileSystem(
			s.artwork,
			cfs,
		)
		go func() {
			for err := range errCh {
				// TODO: pull this out!
				log.Printf("artwork cache: %v", err)
			}
		}()
	}
	return nil
}

// Stores returns a media and artwork filesystem as defined by the command line flags.
func Stores() (media, artwork store.FileSystem, err error) {
	s := &stores{}
	err = buildRemoteStore(s)
	if err != nil {
		return nil, nil, err
	}

	buildLocalStore(s)
	buildMediaCache(s)

	err = buildArtworkCache(s)
	if err != nil {
		return nil, nil, err
	}

	if trimPathPrefix != "" || addPathPrefix != "" {
		s.media = store.PathRewrite(s.media, trimPathPrefix, addPathPrefix)
		s.artwork = store.PathRewrite(s.artwork, trimPathPrefix, addPathPrefix)
	}
	return s.media, s.artwork, nil
}
<|endoftext|>