<commit_before>// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package udb

import (
	"bytes"
	"compress/gzip"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"testing"

	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrwallet/walletdb"
	_ "github.com/decred/dcrwallet/walletdb/bdb"
)

var dbUpgradeTests = [...]struct {
	verify   func(*testing.T, walletdb.DB)
	filename string // in testdata directory
}{
	{verifyV2Upgrade, "v1.db.gz"},
	{verifyV3Upgrade, "v2.db.gz"},
	{verifyV4Upgrade, "v3.db.gz"},
}

var pubPass = []byte("public")

func TestUpgrades(t *testing.T) {
	t.Parallel()

	d, err := ioutil.TempDir("", "dcrwallet_udb_TestUpgrades")
	if err != nil {
		t.Fatal(err)
	}

	var wg sync.WaitGroup
	wg.Add(len(dbUpgradeTests))
	for i, test := range dbUpgradeTests {
		test := test
		name := fmt.Sprintf("test%d", i)
		go func() {
			t.Run(name, func(t *testing.T) {
				defer wg.Done()
				testFile, err := os.Open(filepath.Join("testdata", test.filename))
				if err != nil {
					t.Fatal(err)
				}
				defer testFile.Close()
				r, err := gzip.NewReader(testFile)
				if err != nil {
					t.Fatal(err)
				}
				dbPath := filepath.Join(d, name+".db")
				fi, err := os.Create(dbPath)
				if err != nil {
					t.Fatal(err)
				}
				_, err = io.Copy(fi, r)
				fi.Close()
				if err != nil {
					t.Fatal(err)
				}
				db, err := walletdb.Open("bdb", dbPath)
				if err != nil {
					t.Fatal(err)
				}
				defer db.Close()
				err = Upgrade(db, pubPass)
				if err != nil {
					t.Fatalf("Upgrade failed: %v", err)
				}
				test.verify(t, db)
			})
		}()
	}

	wg.Wait()
	os.RemoveAll(d)
}

func verifyV2Upgrade(t *testing.T, db walletdb.DB) {
	amgr, _, _, err := Open(db, &chaincfg.TestNet2Params, pubPass)
	if err != nil {
		t.Fatalf("Open after Upgrade failed: %v", err)
	}

	err = walletdb.View(db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(waddrmgrBucketKey)
		nsMetaBucket := ns.NestedReadBucket(metaBucketName)

		accounts := []struct {
			totalAddrs uint32
			lastUsed   uint32
		}{
			{^uint32(0), ^uint32(0)},
			{20, 18},
			{20, 19},
			{20, 19},
			{30, 25},
			{30, 29},
			{30, 29},
			{200, 185},
			{200, 199},
		}

		switch lastAccount, err := fetchLastAccount(ns); {
		case err != nil:
			t.Errorf("fetchLastAccount: %v", err)
		case lastAccount != uint32(len(accounts)-1):
			t.Errorf("Number of BIP0044 accounts got %v want %v",
				lastAccount+1, uint32(len(accounts)))
		}

		for i, a := range accounts {
			account := uint32(i)

			if nsMetaBucket.Get(accountNumberToAddrPoolKey(false, account)) != nil {
				t.Errorf("Account %v external address pool bucket still exists", account)
			}
			if nsMetaBucket.Get(accountNumberToAddrPoolKey(true, account)) != nil {
				t.Errorf("Account %v external address pool bucket still exists", account)
			}

			props, err := amgr.AccountProperties(ns, account)
			if err != nil {
				t.Errorf("AccountProperties: %v", err)
				continue
			}
			if props.LastUsedExternalIndex != a.lastUsed {
				t.Errorf("Account %v last used ext index got %v want %v",
					account, props.LastUsedExternalIndex, a.lastUsed)
			}
			if props.LastUsedInternalIndex != a.lastUsed {
				t.Errorf("Account %v last used int index got %v want %v",
					account, props.LastUsedInternalIndex, a.lastUsed)
			}
		}

		if ns.NestedReadBucket(usedAddrBucketName) != nil {
			t.Error("Used address bucket still exists")
		}

		return nil
	})
	if err != nil {
		t.Error(err)
	}
}

func verifyV3Upgrade(t *testing.T, db walletdb.DB) {
	_, _, smgr, err := Open(db, &chaincfg.TestNet2Params, pubPass)
	if err != nil {
		t.Fatalf("Open after Upgrade failed: %v", err)
	}

	err = walletdb.View(db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(wstakemgrBucketKey)

		const (
			ticketHashStr     = "4516ef1d548f3284c1a27b3e706c4677392031df7071ad2022050af376837033"
			votingAddrStr     = "Tcu5oEdEp1W93fRT9FGSwMin7LonfRjNYe4"
			ticketPurchaseHex = "01000000024bf0a303a7e6d174833d9eb761815b61f8ba8c6fa8852a6bf51c703daefc0ef60400000000ffffffff4bf0a303a7e6d174833d9eb761815b61f8ba8c6fa8852a6bf51c703daefc0ef60500000000ffffffff056f78d37a00000000000018baa914ec97b165a5f028b50fb12ae717c5f6c1b9057b5f8700000000000000000000206a1e7f686bc0e548bbb92f487db6da070e43a34117288ed59100000000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac00000000000000000000206a1e9d8e8bdc618035be32a14ab752af2e331f9abf3651074a7a000000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac00000000ad480000028ed59100000000009c480000010000006b483045022100c240bdd6a656c20e9035b839fc91faae6c766772f76149adb91a1fdcf20faf9c02203d68038b83263293f864b173c8f3f00e4371b67bf36fb9ec9f5132bdf68d2858012102adc226dec4de09a18c5a522f8f00917fb6d4eb2361a105218ac3f87d802ae3d451074a7a000000009c480000010000006a47304402205af53185f2662a30a22014b0d19760c1bfde8ec8f065b19cacab6a7abcec76a202204a2614cfcb4db3fc1c86eb0b1ca577f9039ec6db29e9c44ddcca2fe6e3c8bd5d012102adc226dec4de09a18c5a522f8f00917fb6d4eb2361a105218ac3f87d802ae3d4"

			// Stored timestamp uses time.Now().  The generated database test
			// artifact uses this time (2017-04-10 11:50:04 -0400 EDT).  If the
			// db is ever regenerated, this expected value be updated as well.
			timeStamp = 1491839404
		)

		// Verify ticket purchase is still present with correct info, and no
		// vote bits.
		ticketPurchaseHash, err := chainhash.NewHashFromStr(ticketHashStr)
		if err != nil {
			return err
		}
		rec, err := fetchSStxRecord(ns, ticketPurchaseHash, 3)
		if err != nil {
			return err
		}
		if rec.voteBitsSet || rec.voteBits != 0 || rec.voteBitsExt != nil {
			t.Errorf("Ticket purchase record still has vote bits")
		}
		votingAddr, err := smgr.SStxAddress(ns, ticketPurchaseHash)
		if err != nil {
			return err
		}
		if votingAddr.String() != votingAddrStr {
			t.Errorf("Unexpected voting address, got %v want %v",
				votingAddr.String(), votingAddrStr)
		}
		if rec.ts.Unix() != timeStamp {
			t.Errorf("Unexpected timestamp, got %v want %v", rec.ts.Unix(), timeStamp)
		}
		var buf bytes.Buffer
		err = rec.tx.MsgTx().Serialize(&buf)
		if err != nil {
			return err
		}
		expectedBytes, err := hex.DecodeString(ticketPurchaseHex)
		if err != nil {
			return err
		}
		if !bytes.Equal(buf.Bytes(), expectedBytes) {
			t.Errorf("Serialized transaction does not match expected")
		}

		// Verify that the agenda preferences bucket was created.
		if tx.ReadBucket(agendaPreferences.rootBucketKey()) == nil {
			t.Errorf("Agenda preferences bucket was not created")
		}

		return nil
	})
	if err != nil {
		t.Error(err)
	}
}

func verifyV4Upgrade(t *testing.T, db walletdb.DB) {
	err := walletdb.View(db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(waddrmgrBucketKey)
		mainBucket := ns.NestedReadBucket(mainBucketName)
		if mainBucket.Get(seedName) != nil {
			t.Errorf("Seed was not deleted")
		}
		return nil
	})
	if err != nil {
		t.Error(err)
	}
}
<commit_msg>Refactor test to avoid data race.<commit_after>// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package udb

import (
	"bytes"
	"compress/gzip"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrwallet/walletdb"
	_ "github.com/decred/dcrwallet/walletdb/bdb"
)

var dbUpgradeTests = [...]struct {
	verify   func(*testing.T, walletdb.DB)
	filename string // in testdata directory
}{
	{verifyV2Upgrade, "v1.db.gz"},
	{verifyV3Upgrade, "v2.db.gz"},
	{verifyV4Upgrade, "v3.db.gz"},
}

var pubPass = []byte("public")

func TestUpgrades(t *testing.T) {
	t.Parallel()

	d, err := ioutil.TempDir("", "dcrwallet_udb_TestUpgrades")
	if err != nil {
		t.Fatal(err)
	}

	t.Run("group", func(t *testing.T) {
		for i, test := range dbUpgradeTests {
			test := test
			name := fmt.Sprintf("test%d", i)
			t.Run(name, func(t *testing.T) {
				t.Parallel()
				testFile, err := os.Open(filepath.Join("testdata", test.filename))
				if err != nil {
					t.Fatal(err)
				}
				defer testFile.Close()
				r, err := gzip.NewReader(testFile)
				if err != nil {
					t.Fatal(err)
				}
				dbPath := filepath.Join(d, name+".db")
				fi, err := os.Create(dbPath)
				if err != nil {
					t.Fatal(err)
				}
				_, err = io.Copy(fi, r)
				fi.Close()
				if err != nil {
					t.Fatal(err)
				}
				db, err := walletdb.Open("bdb", dbPath)
				if err != nil {
					t.Fatal(err)
				}
				defer db.Close()
				err = Upgrade(db, pubPass)
				if err != nil {
					t.Fatalf("Upgrade failed: %v", err)
				}
				test.verify(t, db)
			})
		}
	})

	os.RemoveAll(d)
}

func verifyV2Upgrade(t *testing.T, db walletdb.DB) {
	amgr, _, _, err := Open(db, &chaincfg.TestNet2Params, pubPass)
	if err != nil {
		t.Fatalf("Open after Upgrade failed: %v", err)
	}

	err = walletdb.View(db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(waddrmgrBucketKey)
		nsMetaBucket := ns.NestedReadBucket(metaBucketName)

		accounts := []struct {
			totalAddrs uint32
			lastUsed   uint32
		}{
			{^uint32(0), ^uint32(0)},
			{20, 18},
			{20, 19},
			{20, 19},
			{30, 25},
			{30, 29},
			{30, 29},
			{200, 185},
			{200, 199},
		}

		switch lastAccount, err := fetchLastAccount(ns); {
		case err != nil:
			t.Errorf("fetchLastAccount: %v", err)
		case lastAccount != uint32(len(accounts)-1):
			t.Errorf("Number of BIP0044 accounts got %v want %v",
				lastAccount+1, uint32(len(accounts)))
		}

		for i, a := range accounts {
			account := uint32(i)

			if nsMetaBucket.Get(accountNumberToAddrPoolKey(false, account)) != nil {
				t.Errorf("Account %v external address pool bucket still exists", account)
			}
			if nsMetaBucket.Get(accountNumberToAddrPoolKey(true, account)) != nil {
				t.Errorf("Account %v external address pool bucket still exists", account)
			}

			props, err := amgr.AccountProperties(ns, account)
			if err != nil {
				t.Errorf("AccountProperties: %v", err)
				continue
			}
			if props.LastUsedExternalIndex != a.lastUsed {
				t.Errorf("Account %v last used ext index got %v want %v",
					account, props.LastUsedExternalIndex, a.lastUsed)
			}
			if props.LastUsedInternalIndex != a.lastUsed {
				t.Errorf("Account %v last used int index got %v want %v",
					account, props.LastUsedInternalIndex, a.lastUsed)
			}
		}

		if ns.NestedReadBucket(usedAddrBucketName) != nil {
			t.Error("Used address bucket still exists")
		}

		return nil
	})
	if err != nil {
		t.Error(err)
	}
}

func verifyV3Upgrade(t *testing.T, db walletdb.DB) {
	_, _, smgr, err := Open(db, &chaincfg.TestNet2Params, pubPass)
	if err != nil {
		t.Fatalf("Open after Upgrade failed: %v", err)
	}

	err = walletdb.View(db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(wstakemgrBucketKey)

		const (
			ticketHashStr     = "4516ef1d548f3284c1a27b3e706c4677392031df7071ad2022050af376837033"
			votingAddrStr     = "Tcu5oEdEp1W93fRT9FGSwMin7LonfRjNYe4"
			ticketPurchaseHex = "01000000024bf0a303a7e6d174833d9eb761815b61f8ba8c6fa8852a6bf51c703daefc0ef60400000000ffffffff4bf0a303a7e6d174833d9eb761815b61f8ba8c6fa8852a6bf51c703daefc0ef60500000000ffffffff056f78d37a00000000000018baa914ec97b165a5f028b50fb12ae717c5f6c1b9057b5f8700000000000000000000206a1e7f686bc0e548bbb92f487db6da070e43a34117288ed59100000000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac00000000000000000000206a1e9d8e8bdc618035be32a14ab752af2e331f9abf3651074a7a000000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac00000000ad480000028ed59100000000009c480000010000006b483045022100c240bdd6a656c20e9035b839fc91faae6c766772f76149adb91a1fdcf20faf9c02203d68038b83263293f864b173c8f3f00e4371b67bf36fb9ec9f5132bdf68d2858012102adc226dec4de09a18c5a522f8f00917fb6d4eb2361a105218ac3f87d802ae3d451074a7a000000009c480000010000006a47304402205af53185f2662a30a22014b0d19760c1bfde8ec8f065b19cacab6a7abcec76a202204a2614cfcb4db3fc1c86eb0b1ca577f9039ec6db29e9c44ddcca2fe6e3c8bd5d012102adc226dec4de09a18c5a522f8f00917fb6d4eb2361a105218ac3f87d802ae3d4"

			// Stored timestamp uses time.Now().  The generated database test
			// artifact uses this time (2017-04-10 11:50:04 -0400 EDT).  If the
			// db is ever regenerated, this expected value be updated as well.
			timeStamp = 1491839404
		)

		// Verify ticket purchase is still present with correct info, and no
		// vote bits.
		ticketPurchaseHash, err := chainhash.NewHashFromStr(ticketHashStr)
		if err != nil {
			return err
		}
		rec, err := fetchSStxRecord(ns, ticketPurchaseHash, 3)
		if err != nil {
			return err
		}
		if rec.voteBitsSet || rec.voteBits != 0 || rec.voteBitsExt != nil {
			t.Errorf("Ticket purchase record still has vote bits")
		}
		votingAddr, err := smgr.SStxAddress(ns, ticketPurchaseHash)
		if err != nil {
			return err
		}
		if votingAddr.String() != votingAddrStr {
			t.Errorf("Unexpected voting address, got %v want %v",
				votingAddr.String(), votingAddrStr)
		}
		if rec.ts.Unix() != timeStamp {
			t.Errorf("Unexpected timestamp, got %v want %v", rec.ts.Unix(), timeStamp)
		}
		var buf bytes.Buffer
		err = rec.tx.MsgTx().Serialize(&buf)
		if err != nil {
			return err
		}
		expectedBytes, err := hex.DecodeString(ticketPurchaseHex)
		if err != nil {
			return err
		}
		if !bytes.Equal(buf.Bytes(), expectedBytes) {
			t.Errorf("Serialized transaction does not match expected")
		}

		// Verify that the agenda preferences bucket was created.
		if tx.ReadBucket(agendaPreferences.rootBucketKey()) == nil {
			t.Errorf("Agenda preferences bucket was not created")
		}

		return nil
	})
	if err != nil {
		t.Error(err)
	}
}

func verifyV4Upgrade(t *testing.T, db walletdb.DB) {
	err := walletdb.View(db, func(tx walletdb.ReadTx) error {
		ns := tx.ReadBucket(waddrmgrBucketKey)
		mainBucket := ns.NestedReadBucket(mainBucketName)
		if mainBucket.Get(seedName) != nil {
			t.Errorf("Seed was not deleted")
		}
		return nil
	})
	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package kvfs

import (
	"io"
	"net/url"
	"os"
	"path/filepath"

	. "github.com/polydawn/go-errcat"
	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/lib/guid"
	"go.polydawn.net/rio/warehouse"
	"go.polydawn.net/rio/warehouse/util"
)

var (
	_ warehouse.BlobstoreController      = Controller{}
	_ warehouse.BlobstoreWriteController = &WriteController{}
)

type Controller struct {
	addr     api.WarehouseAddr // user's string retained for messages
	basePath fs.AbsolutePath
	ctntAddr bool
}

/*
	Initialize a new warehouse controller that operates on a local filesystem.

	May return errors of category:

	  - `rio.ErrUsage` -- for unsupported addressses
	  - `rio.ErrWarehouseUnavailable` -- if the warehouse doesn't exist
*/
func NewController(addr api.WarehouseAddr) (warehouse.BlobstoreController, error) {
	// Stamp out a warehouse handle.
	//  More values will be accumulated in shortly.
	whCtrl := Controller{
		addr: addr,
	}

	// Verify that the addr is sensible up front, and extract features.
	//  - We parse things mostly like URLs.
	//  - We extract whether or not it's content-addressible mode here;
	//  - and extract the filesystem path, and normalize it to its absolute form.
	u, err := url.Parse(string(addr))
	if err != nil {
		return whCtrl, Errorf(rio.ErrUsage, "failed to parse URI: %s", err)
	}
	switch u.Scheme {
	case "file":
	case "ca+file":
		whCtrl.ctntAddr = true
	default:
		return whCtrl, Errorf(rio.ErrUsage, "unsupported scheme in warehouse addr: %q (valid options are 'file' or 'ca+file')", u.Scheme)
	}
	absPth, err := filepath.Abs(filepath.Join(u.Host, u.Path))
	if err != nil {
		panic(err)
	}
	whCtrl.basePath = fs.MustAbsolutePath(absPth)

	// Check that the warehouse exists.
	//  If it does, we're good: return happily.
	checkPath := whCtrl.basePath
	if !whCtrl.ctntAddr {
		// In non-CA mode, the check for warehouse existence is a little strange;
		//  for reading, we could declare 404 if the path doesn't exist... but we don't
		//  know whether this is going to be used for reading or writing yet!
		//  So we have to look at the path segment above it to see if a write might be valid.
		checkPath = checkPath.Dir()
	}
	stat, err := os.Stat(checkPath.String())
	switch {
	case os.IsNotExist(err):
		return whCtrl, Errorf(rio.ErrWarehouseUnavailable, "warehouse does not exist (%s)", err)
	case err != nil: // normally we'd style this as the default cause, but, we must check it before the IsDir option
		return whCtrl, Errorf(rio.ErrWarehouseUnavailable, "warehouse unavailable (%s)", err)
	case !stat.IsDir():
		return whCtrl, Errorf(rio.ErrWarehouseUnavailable, "warehouse does not exist (%s is not a dir)", checkPath)
	default: // only thing left is err == nil
		return whCtrl, nil
	}
}

func (whCtrl Controller) OpenReader(wareID api.WareID) (io.ReadCloser, error) {
	finalPath := whCtrl.basePath
	if whCtrl.ctntAddr {
		chunkA, chunkB, _ := util.ChunkifyHash(wareID)
		finalPath = finalPath.
			Join(fs.MustRelPath(chunkA)).
			Join(fs.MustRelPath(chunkB)).
			Join(fs.MustRelPath(wareID.Hash))
	}
	file, err := os.OpenFile(finalPath.String(), os.O_RDONLY, 0)
	switch {
	case err == nil:
		return file, nil
	case os.IsNotExist(err):
		return file, Errorf(rio.ErrWareNotFound, "ware %s not found in warehouse %s", wareID, whCtrl.addr)
	default:
		return file, Errorf(rio.ErrWarehouseUnavailable, "ware %s could not be retrieved from warehouse %s: %s", wareID, whCtrl.addr, err)
	}
}

func (whCtrl Controller) OpenWriter() (warehouse.BlobstoreWriteController, error) {
	wc := &WriteController{whCtrl: whCtrl}
	// Pick a random upload path.
	if whCtrl.ctntAddr {
		tmpName := fs.MustRelPath(".tmp.upload." + guid.New())
		wc.stagePath = whCtrl.basePath.Join(tmpName)
	} else {
		// In non-CA mode, "base" path isn't really "base"; it's the final destination.
		tmpName := fs.MustRelPath(".tmp.upload." + whCtrl.basePath.Last() + "." + guid.New())
		wc.stagePath = whCtrl.basePath.Dir().Join(tmpName)
	}
	// Open file the file for write.
	file, err := os.OpenFile(wc.stagePath.String(), os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
	if err != nil {
		return wc, Errorf(rio.ErrWarehouseUnwritable, "failed to reserve temp space in warehouse: %s", err)
	}
	wc.stream = file
	// Return the controller -- which has methods to either commit+close, or cancel+close.
	return wc, nil
}

type WriteController struct {
	stream    io.WriteCloser  // Write to this.
	whCtrl    Controller      // Needed for the final move-into-place.
	stagePath fs.AbsolutePath // Needed for the final move-into-place.
}

func (wc *WriteController) Write(bs []byte) (int, error) {
	return wc.stream.Write(bs)
}

/*
	Cancel the current write.  Close the stream, and remove any temporary files.
*/
func (wc *WriteController) Close() error {
	wc.stream.Close()
	return os.Remove(wc.stagePath.String())
}

/*
	Commit the current data as the given hash.
	Caller must be an adult and specify the hash truthfully.
	Closes the writer and invalidates any future use.
*/
func (wc *WriteController) Commit(wareID api.WareID) error {
	// Close the file.
	if err := wc.stream.Close(); err != nil {
		return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
	}
	// Compute final path.
	// Make parent dirs if necessary in content-addr mode.
	finalPath := wc.whCtrl.basePath
	if wc.whCtrl.ctntAddr {
		chunkA, chunkB, _ := util.ChunkifyHash(wareID)
		finalPath = finalPath.Join(fs.MustRelPath(chunkA))
		if err := os.Mkdir(finalPath.String(), 0755); err != nil && !os.IsExist(err) {
			return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
		}
		finalPath = finalPath.Join(fs.MustRelPath(chunkB))
		if err := os.Mkdir(finalPath.String(), 0755); err != nil && !os.IsExist(err) {
			return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
		}
		finalPath = finalPath.Join(fs.MustRelPath(wareID.Hash))
	}
	// Move into place.
	if err := os.Rename(wc.stagePath.String(), finalPath.String()); err != nil {
		return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
	}
	return nil
}
<commit_msg>FIX YET ANOTHER TYPED-NIL PROBLEM.<commit_after>package kvfs

import (
	"io"
	"net/url"
	"os"
	"path/filepath"

	. "github.com/polydawn/go-errcat"
	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/lib/guid"
	"go.polydawn.net/rio/warehouse"
	"go.polydawn.net/rio/warehouse/util"
)

var (
	_ warehouse.BlobstoreController      = Controller{}
	_ warehouse.BlobstoreWriteController = &WriteController{}
)

type Controller struct {
	addr     api.WarehouseAddr // user's string retained for messages
	basePath fs.AbsolutePath
	ctntAddr bool
}

/*
	Initialize a new warehouse controller that operates on a local filesystem.

	May return errors of category:

	  - `rio.ErrUsage` -- for unsupported addressses
	  - `rio.ErrWarehouseUnavailable` -- if the warehouse doesn't exist
*/
func NewController(addr api.WarehouseAddr) (warehouse.BlobstoreController, error) {
	// Stamp out a warehouse handle.
	//  More values will be accumulated in shortly.
	whCtrl := Controller{
		addr: addr,
	}

	// Verify that the addr is sensible up front, and extract features.
	//  - We parse things mostly like URLs.
	//  - We extract whether or not it's content-addressible mode here;
	//  - and extract the filesystem path, and normalize it to its absolute form.
	u, err := url.Parse(string(addr))
	if err != nil {
		return whCtrl, Errorf(rio.ErrUsage, "failed to parse URI: %s", err)
	}
	switch u.Scheme {
	case "file":
	case "ca+file":
		whCtrl.ctntAddr = true
	default:
		return whCtrl, Errorf(rio.ErrUsage, "unsupported scheme in warehouse addr: %q (valid options are 'file' or 'ca+file')", u.Scheme)
	}
	absPth, err := filepath.Abs(filepath.Join(u.Host, u.Path))
	if err != nil {
		panic(err)
	}
	whCtrl.basePath = fs.MustAbsolutePath(absPth)

	// Check that the warehouse exists.
	//  If it does, we're good: return happily.
	checkPath := whCtrl.basePath
	if !whCtrl.ctntAddr {
		// In non-CA mode, the check for warehouse existence is a little strange;
		//  for reading, we could declare 404 if the path doesn't exist... but we don't
		//  know whether this is going to be used for reading or writing yet!
		//  So we have to look at the path segment above it to see if a write might be valid.
		checkPath = checkPath.Dir()
	}
	stat, err := os.Stat(checkPath.String())
	switch {
	case os.IsNotExist(err):
		return whCtrl, Errorf(rio.ErrWarehouseUnavailable, "warehouse does not exist (%s)", err)
	case err != nil: // normally we'd style this as the default cause, but, we must check it before the IsDir option
		return whCtrl, Errorf(rio.ErrWarehouseUnavailable, "warehouse unavailable (%s)", err)
	case !stat.IsDir():
		return whCtrl, Errorf(rio.ErrWarehouseUnavailable, "warehouse does not exist (%s is not a dir)", checkPath)
	default: // only thing left is err == nil
		return whCtrl, nil
	}
}

func (whCtrl Controller) OpenReader(wareID api.WareID) (io.ReadCloser, error) {
	finalPath := whCtrl.basePath
	if whCtrl.ctntAddr {
		chunkA, chunkB, _ := util.ChunkifyHash(wareID)
		finalPath = finalPath.
			Join(fs.MustRelPath(chunkA)).
			Join(fs.MustRelPath(chunkB)).
			Join(fs.MustRelPath(wareID.Hash))
	}
	file, err := os.OpenFile(finalPath.String(), os.O_RDONLY, 0)
	switch {
	case err == nil:
		return file, nil
	case os.IsNotExist(err):
		return nil, Errorf(rio.ErrWareNotFound, "ware %s not found in warehouse %s", wareID, whCtrl.addr)
	default:
		return nil, Errorf(rio.ErrWarehouseUnavailable, "ware %s could not be retrieved from warehouse %s: %s", wareID, whCtrl.addr, err)
	}
}

func (whCtrl Controller) OpenWriter() (warehouse.BlobstoreWriteController, error) {
	wc := &WriteController{whCtrl: whCtrl}
	// Pick a random upload path.
	if whCtrl.ctntAddr {
		tmpName := fs.MustRelPath(".tmp.upload." + guid.New())
		wc.stagePath = whCtrl.basePath.Join(tmpName)
	} else {
		// In non-CA mode, "base" path isn't really "base"; it's the final destination.
		tmpName := fs.MustRelPath(".tmp.upload." + whCtrl.basePath.Last() + "." + guid.New())
		wc.stagePath = whCtrl.basePath.Dir().Join(tmpName)
	}
	// Open file the file for write.
	file, err := os.OpenFile(wc.stagePath.String(), os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
	if err != nil {
		return wc, Errorf(rio.ErrWarehouseUnwritable, "failed to reserve temp space in warehouse: %s", err)
	}
	wc.stream = file
	// Return the controller -- which has methods to either commit+close, or cancel+close.
	return wc, nil
}

type WriteController struct {
	stream    io.WriteCloser  // Write to this.
	whCtrl    Controller      // Needed for the final move-into-place.
	stagePath fs.AbsolutePath // Needed for the final move-into-place.
}

func (wc *WriteController) Write(bs []byte) (int, error) {
	return wc.stream.Write(bs)
}

/*
	Cancel the current write.  Close the stream, and remove any temporary files.
*/
func (wc *WriteController) Close() error {
	wc.stream.Close()
	return os.Remove(wc.stagePath.String())
}

/*
	Commit the current data as the given hash.
	Caller must be an adult and specify the hash truthfully.
	Closes the writer and invalidates any future use.
*/
func (wc *WriteController) Commit(wareID api.WareID) error {
	// Close the file.
	if err := wc.stream.Close(); err != nil {
		return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
	}
	// Compute final path.
	// Make parent dirs if necessary in content-addr mode.
	finalPath := wc.whCtrl.basePath
	if wc.whCtrl.ctntAddr {
		chunkA, chunkB, _ := util.ChunkifyHash(wareID)
		finalPath = finalPath.Join(fs.MustRelPath(chunkA))
		if err := os.Mkdir(finalPath.String(), 0755); err != nil && !os.IsExist(err) {
			return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
		}
		finalPath = finalPath.Join(fs.MustRelPath(chunkB))
		if err := os.Mkdir(finalPath.String(), 0755); err != nil && !os.IsExist(err) {
			return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
		}
		finalPath = finalPath.Join(fs.MustRelPath(wareID.Hash))
	}
	// Move into place.
	if err := os.Rename(wc.stagePath.String(), finalPath.String()); err != nil {
		return Errorf(rio.ErrWarehouseUnwritable, "failed to commit to file: %s", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package engine

import (
	"sync"
	"testing"

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

func Test_Scene_Locate(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations:      []Location{},
		locationsMutex: &sync.RWMutex{},
	}

	location := Location{Dot{1, 1}, Dot{1, 2}}
	err := scene.Locate(location)
	require.Nil(t, err)
	require.Equal(t, []Location{location}, scene.locations)
}

func Benchmark_Scene_Locate(b *testing.B) {
	// TODO: Implement benchmark.
}

func Benchmark_Scene_Relocate(b *testing.B) {
	// TODO: Implement benchmark.
}

func Test_Scene_LocateRandomRect(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations:      []Location{},
		locationsMutex: &sync.RWMutex{},
	}

	location, err := scene.LocateRandomRect(1, 5)
	require.Nil(t, err)
	require.Equal(t, []Location{location}, scene.locations)
}
<commit_msg>Create tests for scene LocateAvailableDots method<commit_after>package engine

import (
	"sync"
	"testing"

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

func Test_Scene_Locate(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations:      []Location{},
		locationsMutex: &sync.RWMutex{},
	}

	location := Location{Dot{1, 1}, Dot{1, 2}}
	err := scene.Locate(location)
	require.Nil(t, err)
	require.Equal(t, []Location{location}, scene.locations)
}

func Benchmark_Scene_Locate(b *testing.B) {
	// TODO: Implement benchmark.
}

func Benchmark_Scene_Relocate(b *testing.B) {
	// TODO: Implement benchmark.
}

func Test_Scene_LocateRandomRect(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations:      []Location{},
		locationsMutex: &sync.RWMutex{},
	}

	location, err := scene.LocateRandomRect(1, 5)
	require.Nil(t, err)
	require.Equal(t, []Location{location}, scene.locations)
}

func Test_Scene_LocateAvailableDots_EmptyScene(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations:      []Location{},
		locationsMutex: &sync.RWMutex{},
	}

	location := Location{Dot{1, 1}, Dot{1, 2}}
	locationActual := scene.LocateAvailableDots(location)
	require.Equal(t, []Location{location}, scene.locations)
	require.Equal(t, []Location{locationActual}, scene.locations)
	require.Equal(t, location, locationActual)
}

func Test_Scene_LocateAvailableDots_LocationNotAvailable(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations: []Location{
			{Dot{1, 1}, Dot{1, 2}},
		},
		locationsMutex: &sync.RWMutex{},
	}

	location := Location{Dot{1, 1}, Dot{1, 2}}
	locationActual := scene.LocateAvailableDots(location)
	require.Equal(t, Location{}, locationActual)
}

func Test_Scene_LocateAvailableDots_LocationsIntersects(t *testing.T) {
	scene := &Scene{
		area: Area{
			width:  100,
			height: 100,
		},
		locations: []Location{
			{Dot{1, 1}, Dot{1, 2}, Dot{1, 3}, Dot{1, 4}},
		},
		locationsMutex: &sync.RWMutex{},
	}

	location := Location{Dot{1, 1}, Dot{1, 0}}
	locationActual := scene.LocateAvailableDots(location)
	require.Equal(t, Location{Dot{1, 0}}, locationActual)
	require.Equal(t, []Location{
		{Dot{1, 1}, Dot{1, 2}, Dot{1, 3}, Dot{1, 4}},
		{Dot{1, 0}},
	}, scene.locations)
}
<|endoftext|>


<commit_before><commit_msg>"veyron/tools/principal": Remove default expiry for fork<commit_after><|endoftext|>


<commit_before>// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
// Use of this source code is governed by the license that can be
// found in the LICENSE file.

package yara

/*
#ifdef _WIN32
#define fdopen _fdopen
#endif
#include <stdio.h>

#include <yara.h>

// This signature should be generated by cgo from the exported
// function below
void compilerCallback(int, char*, int, char*, void*);
*/
import "C"
import (
	"errors"
	"os"
	"runtime"
	"unsafe"
)

//export compilerCallback
func compilerCallback(errorLevel C.int, filename *C.char, linenumber C.int, message *C.char, ptr unsafe.Pointer) {
	if ptr == nil {
		return
	}
	compiler := (*Compiler)(ptr)
	msg := CompilerMessage{
		Filename: C.GoString(filename),
		Line:     int(linenumber),
		Text:     C.GoString(message),
	}
	switch errorLevel {
	case C.YARA_ERROR_LEVEL_ERROR:
		compiler.Errors = append(compiler.Errors, msg)
	case C.YARA_ERROR_LEVEL_WARNING:
		compiler.Warnings = append(compiler.Warnings, msg)
	}
}

// A Compiler encapsulates the YARA compiler that transforms rules
// into YARA's internal, binary form which in turn is used for
// scanning files or memory blocks.
type Compiler struct {
	*compiler
	Errors   []CompilerMessage
	Warnings []CompilerMessage
}

type compiler struct {
	cptr *C.YR_COMPILER
}

// A CompilerMessage contains an error or warning message produced
// while compiling sets of rules using AddString or AddFile.
type CompilerMessage struct {
	Filename string
	Line     int
	Text     string
}

// NewCompiler creates a YARA compiler.
func NewCompiler() (*Compiler, error) {
	var yrCompiler *C.YR_COMPILER
	if err := newError(C.yr_compiler_create(&yrCompiler)); err != nil {
		return nil, err
	}
	c := &Compiler{compiler: &compiler{cptr: yrCompiler}}
	C.yr_compiler_set_callback(yrCompiler, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), unsafe.Pointer(c))
	runtime.SetFinalizer(c.compiler, (*compiler).finalize)
	return c, nil
}

func (c *compiler) finalize() {
	C.yr_compiler_destroy(c.cptr)
	runtime.SetFinalizer(c, nil)
}

// Destroy destroys the YARA data structure representing a compiler.
// Since a Finalizer for the underlying YR_COMPILER structure is
// automatically set up on creation, it should not be necessary to
// explicitly call this method.
func (c *Compiler) Destroy() {
	if c.compiler != nil {
		c.compiler.finalize()
		c.compiler = nil
	}
}

// AddFile compiles rules from an os.File. Rules are added to the
// specified namespace.
func (c *Compiler) AddFile(file os.File, namespace string) (err error) {
	fh, err := C.fdopen(C.int(file.Fd()), C.CString("r"))
	if err != nil {
		return err
	}
	defer C.free(unsafe.Pointer(fh))
	var ns *C.char
	if namespace != "" {
		ns = C.CString(namespace)
		defer C.free(unsafe.Pointer(ns))
	}
	filename := C.CString(file.Name())
	defer C.free(unsafe.Pointer(filename))
	numErrors := int(C.yr_compiler_add_file(c.cptr, fh, ns, filename))
	if numErrors > 0 {
		var buf [1024]C.char
		msg := C.GoString(C.yr_compiler_get_error_message(
			c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024))
		err = errors.New(msg)
	}
	return
}

// AddString compiles rules from a string. Rules are added to the
// specified namespace.
func (c *Compiler) AddString(rules string, namespace string) (err error) {
	var ns *C.char
	if namespace != "" {
		ns = C.CString(namespace)
		defer C.free(unsafe.Pointer(ns))
	}
	crules := C.CString(rules)
	defer C.free(unsafe.Pointer(crules))
	numErrors := int(C.yr_compiler_add_string(c.cptr, crules, ns))
	if numErrors > 0 {
		var buf [1024]C.char
		msg := C.GoString(C.yr_compiler_get_error_message(
			c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024))
		err = errors.New(msg)
	}
	return
}

// DefineVariable defines a named variable for use by the compiler.
// Boolean, int64, float64, and string types are supported.
func (c *Compiler) DefineVariable(name string, value interface{}) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	switch value.(type) {
	case bool:
		var v int
		if value.(bool) {
			v = 1
		}
		err = newError(C.yr_compiler_define_boolean_variable(
			c.cptr, cname, C.int(v)))
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		value := toint64(value)
		err = newError(C.yr_compiler_define_integer_variable(
			c.cptr, cname, C.int64_t(value)))
	case float64:
		err = newError(C.yr_compiler_define_float_variable(
			c.cptr, cname, C.double(value.(float64))))
	case string:
		cvalue := C.CString(value.(string))
		defer C.free(unsafe.Pointer(cvalue))
		err = newError(C.yr_compiler_define_string_variable(
			c.cptr, cname, cvalue))
	default:
		err = errors.New("wrong value type passed to DefineVariable; bool, int64, float64, string are accepted")
	}
	return
}

// GetRules returns the compiled ruleset.
func (c *Compiler) GetRules() (*Rules, error) {
	var yrRules *C.YR_RULES
	if err := newError(C.yr_compiler_get_rules(c.cptr, &yrRules)); err != nil {
		return nil, err
	}
	r := &Rules{rules: &rules{cptr: yrRules}}
	runtime.SetFinalizer(r.rules, (*rules).finalize)
	return r, nil
}

// Compile compiles rules and an (optional) set of variables into a
// Rules object in a single step.
func Compile(rules string, variables map[string]interface{}) (r *Rules, err error) {
	var c *Compiler
	if c, err = NewCompiler(); err != nil {
		return
	}
	for k, v := range variables {
		if err = c.DefineVariable(k, v); err != nil {
			return
		}
	}
	if err = c.AddString(rules, ""); err != nil {
		return
	}
	r, err = c.GetRules()
	return
}
<commit_msg>compiler.AddFile: Use fdopen on dup file descriptor and properly close it<commit_after>// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
// Use of this source code is governed by the license that can be
// found in the LICENSE file.

package yara

/*
#ifdef _WIN32
#define fdopen _fdopen
#endif
#include <stdio.h>
#include <unistd.h>

#include <yara.h>

// This signature should be generated by cgo from the exported
// function below
void compilerCallback(int, char*, int, char*, void*);
*/
import "C"
import (
	"errors"
	"os"
	"runtime"
	"unsafe"
)

//export compilerCallback
func compilerCallback(errorLevel C.int, filename *C.char, linenumber C.int, message *C.char, ptr unsafe.Pointer) {
	if ptr == nil {
		return
	}
	compiler := (*Compiler)(ptr)
	msg := CompilerMessage{
		Filename: C.GoString(filename),
		Line:     int(linenumber),
		Text:     C.GoString(message),
	}
	switch errorLevel {
	case C.YARA_ERROR_LEVEL_ERROR:
		compiler.Errors = append(compiler.Errors, msg)
	case C.YARA_ERROR_LEVEL_WARNING:
		compiler.Warnings = append(compiler.Warnings, msg)
	}
}

// A Compiler encapsulates the YARA compiler that transforms rules
// into YARA's internal, binary form which in turn is used for
// scanning files or memory blocks.
type Compiler struct {
	*compiler
	Errors   []CompilerMessage
	Warnings []CompilerMessage
}

type compiler struct {
	cptr *C.YR_COMPILER
}

// A CompilerMessage contains an error or warning message produced
// while compiling sets of rules using AddString or AddFile.
type CompilerMessage struct {
	Filename string
	Line     int
	Text     string
}

// NewCompiler creates a YARA compiler.
func NewCompiler() (*Compiler, error) {
	var yrCompiler *C.YR_COMPILER
	if err := newError(C.yr_compiler_create(&yrCompiler)); err != nil {
		return nil, err
	}
	c := &Compiler{compiler: &compiler{cptr: yrCompiler}}
	C.yr_compiler_set_callback(yrCompiler, C.YR_COMPILER_CALLBACK_FUNC(C.compilerCallback), unsafe.Pointer(c))
	runtime.SetFinalizer(c.compiler, (*compiler).finalize)
	return c, nil
}

func (c *compiler) finalize() {
	C.yr_compiler_destroy(c.cptr)
	runtime.SetFinalizer(c, nil)
}

// Destroy destroys the YARA data structure representing a compiler.
// Since a Finalizer for the underlying YR_COMPILER structure is
// automatically set up on creation, it should not be necessary to
// explicitly call this method.
func (c *Compiler) Destroy() {
	if c.compiler != nil {
		c.compiler.finalize()
		c.compiler = nil
	}
}

// AddFile compiles rules from an os.File. Rules are added to the
// specified namespace.
func (c *Compiler) AddFile(file os.File, namespace string) (err error) {
	fd := C.dup(C.int(file.Fd()))
	fh, err := C.fdopen(fd, C.CString("r"))
	if err != nil {
		return err
	}
	defer C.fclose(fh)
	var ns *C.char
	if namespace != "" {
		ns = C.CString(namespace)
		defer C.free(unsafe.Pointer(ns))
	}
	filename := C.CString(file.Name())
	defer C.free(unsafe.Pointer(filename))
	numErrors := int(C.yr_compiler_add_file(c.cptr, fh, ns, filename))
	if numErrors > 0 {
		var buf [1024]C.char
		msg := C.GoString(C.yr_compiler_get_error_message(
			c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024))
		err = errors.New(msg)
	}
	return
}

// AddString compiles rules from a string. Rules are added to the
// specified namespace.
func (c *Compiler) AddString(rules string, namespace string) (err error) {
	var ns *C.char
	if namespace != "" {
		ns = C.CString(namespace)
		defer C.free(unsafe.Pointer(ns))
	}
	crules := C.CString(rules)
	defer C.free(unsafe.Pointer(crules))
	numErrors := int(C.yr_compiler_add_string(c.cptr, crules, ns))
	if numErrors > 0 {
		var buf [1024]C.char
		msg := C.GoString(C.yr_compiler_get_error_message(
			c.cptr, (*C.char)(unsafe.Pointer(&buf[0])), 1024))
		err = errors.New(msg)
	}
	return
}

// DefineVariable defines a named variable for use by the compiler.
// Boolean, int64, float64, and string types are supported.
func (c *Compiler) DefineVariable(name string, value interface{}) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	switch value.(type) {
	case bool:
		var v int
		if value.(bool) {
			v = 1
		}
		err = newError(C.yr_compiler_define_boolean_variable(
			c.cptr, cname, C.int(v)))
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		value := toint64(value)
		err = newError(C.yr_compiler_define_integer_variable(
			c.cptr, cname, C.int64_t(value)))
	case float64:
		err = newError(C.yr_compiler_define_float_variable(
			c.cptr, cname, C.double(value.(float64))))
	case string:
		cvalue := C.CString(value.(string))
		defer C.free(unsafe.Pointer(cvalue))
		err = newError(C.yr_compiler_define_string_variable(
			c.cptr, cname, cvalue))
	default:
		err = errors.New("wrong value type passed to DefineVariable; bool, int64, float64, string are accepted")
	}
	return
}

// GetRules returns the compiled ruleset.
func (c *Compiler) GetRules() (*Rules, error) {
	var yrRules *C.YR_RULES
	if err := newError(C.yr_compiler_get_rules(c.cptr, &yrRules)); err != nil {
		return nil, err
	}
	r := &Rules{rules: &rules{cptr: yrRules}}
	runtime.SetFinalizer(r.rules, (*rules).finalize)
	return r, nil
}

// Compile compiles rules and an (optional) set of variables into a
// Rules object in a single step.
func Compile(rules string, variables map[string]interface{}) (r *Rules, err error) {
	var c *Compiler
	if c, err = NewCompiler(); err != nil {
		return
	}
	for k, v := range variables {
		if err = c.DefineVariable(k, v); err != nil {
			return
		}
	}
	if err = c.AddString(rules, ""); err != nil {
		return
	}
	r, err = c.GetRules()
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package instance

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

// AddressType represents the possible ways of specifying a machine location by
// either a hostname resolvable by dns lookup, or ipv4 or ipv6 address.
type AddressType string

const (
	HostName    AddressType = "hostname"
	Ipv4Address AddressType = "ipv4"
	Ipv6Address AddressType = "ipv6"
)

// NetworkScope denotes the context a location may apply to. If a name or
// address can be reached from the wider internet, it is considered public. A
// private network address is either specific to the cloud or cloud subnet a
// machine belongs to, or to the machine itself for containers.
type NetworkScope string

const (
	NetworkUnknown      NetworkScope = ""
	NetworkPublic       NetworkScope = "public"
	NetworkCloudLocal   NetworkScope = "local-cloud"
	NetworkMachineLocal NetworkScope = "local-machine"
)

// Address represents the location of a machine, including metadata about what
// kind of location the address describes.
type Address struct {
	Value       string
	Type        AddressType
	NetworkName string
	NetworkScope
}

// HostPort associates an address with a port.
type HostPort struct {
	Address
	Port int
}

// AddressesWithPort returns the given addresses all
// associated with the given port.
func AddressesWithPort(addrs []Address, port int) []HostPort {
	hps := make([]HostPort, len(addrs))
	for i, addr := range addrs {
		hps[i] = HostPort{
			Address: addr,
			Port:    port,
		}
	}
	return hps
}

// NetAddr returns the host-port as an address
// suitable for calling net.Dial.
func (hp HostPort) NetAddr() string {
	return net.JoinHostPort(hp.Value, strconv.FormatInt(int64(hp.Port), 10))
}

// String returns a string representation of the address,
// in the form: scope:address(network name);
// for example:
//
//	public:c2-54-226-162-124.compute-1.amazonaws.com(ec2network)
//
// If the scope is NetworkUnknown, the initial scope: prefix will
// be omitted. If the NetworkName is blank, the (network name) suffix
// will be omitted.
func (a Address) String() string {
	var buf bytes.Buffer
	if a.NetworkScope != NetworkUnknown {
		buf.WriteString(string(a.NetworkScope))
		buf.WriteByte(':')
	}
	buf.WriteString(a.Value)
	if a.NetworkName != "" {
		buf.WriteByte('(')
		buf.WriteString(a.NetworkName)
		buf.WriteByte(')')
	}
	return buf.String()
}

// NewAddresses is a convenience function to create addresses from a string slice
func NewAddresses(inAddresses []string) (outAddresses []Address) {
	for _, address := range inAddresses {
		outAddresses = append(outAddresses, NewAddress(address))
	}
	return outAddresses
}

func DeriveAddressType(value string) AddressType {
	ip := net.ParseIP(value)
	if ip != nil {
		if ip.To4() != nil {
			return Ipv4Address
		}
		if ip.To16() != nil {
			return Ipv6Address
		}
		panic("Unknown form of IP address")
	}
	// TODO(gz): Check value is a valid hostname
	return HostName
}

func NewAddress(value string) Address {
	addresstype := DeriveAddressType(value)
	return Address{value, addresstype, "", NetworkUnknown}
}

// netLookupIP is a var for testing.
var netLookupIP = net.LookupIP

// HostAddresses looks up the IP addresses of the specified
// host, and translates them into instance.Address values.
//
// The argument passed in is always added ast the final
// address in the resulting slice.
func HostAddresses(host string) (addrs []Address, err error) {
	hostAddr := NewAddress(host)
	if hostAddr.Type != HostName {
		// IPs shouldn't be fed into LookupIP.
		return []Address{hostAddr}, nil
	}
	ipaddrs, err := netLookupIP(host)
	if err != nil {
		return nil, err
	}
	addrs = make([]Address, len(ipaddrs)+1)
	for i, ipaddr := range ipaddrs {
		switch len(ipaddr) {
		case net.IPv4len:
			addrs[i].Type = Ipv4Address
			addrs[i].Value = ipaddr.String()
		case net.IPv6len:
			if ipaddr.To4() != nil {
				// ipaddr is an IPv4 address represented in 16 bytes.
				addrs[i].Type = Ipv4Address
			} else {
				addrs[i].Type = Ipv6Address
			}
			addrs[i].Value = ipaddr.String()
		}
	}
	addrs[len(addrs)-1] = hostAddr
	return addrs, err
}

// SelectPublicAddress picks one address from a slice that would
// be appropriate to display as a publicly accessible endpoint.
// If there are no suitable addresses, the empty string is returned.
func SelectPublicAddress(addresses []Address) string {
	mostpublic := ""
	for _, addr := range addresses {
		if addr.Type != Ipv6Address {
			switch addr.NetworkScope {
			case NetworkPublic:
				return addr.Value
			case NetworkCloudLocal, NetworkUnknown:
				mostpublic = addr.Value
			}
		}
	}
	return mostpublic
}

// SelectInternalAddress picks one address from a slice that can be
// used as an endpoint for juju internal communication.
// If there are no suitable addresses, the empty string is returned.
func SelectInternalAddress(addresses []Address, machineLocal bool) string {
	usableAddress := ""
	for _, addr := range addresses {
		if addr.Type != Ipv6Address {
			switch addr.NetworkScope {
			case NetworkCloudLocal:
				return addr.Value
			case NetworkMachineLocal:
				if machineLocal {
					return addr.Value
				}
			case NetworkPublic, NetworkUnknown:
				if usableAddress == "" {
					usableAddress = addr.Value
				}
			}
		}
	}
	return usableAddress
}
<commit_msg>instance: use simpler strconv function<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package instance

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

// AddressType represents the possible ways of specifying a machine location by
// either a hostname resolvable by dns lookup, or ipv4 or ipv6 address.
type AddressType string

const (
	HostName    AddressType = "hostname"
	Ipv4Address AddressType = "ipv4"
	Ipv6Address AddressType = "ipv6"
)

// NetworkScope denotes the context a location may apply to. If a name or
// address can be reached from the wider internet, it is considered public. A
// private network address is either specific to the cloud or cloud subnet a
// machine belongs to, or to the machine itself for containers.
type NetworkScope string

const (
	NetworkUnknown      NetworkScope = ""
	NetworkPublic       NetworkScope = "public"
	NetworkCloudLocal   NetworkScope = "local-cloud"
	NetworkMachineLocal NetworkScope = "local-machine"
)

// Address represents the location of a machine, including metadata about what
// kind of location the address describes.
type Address struct {
	Value       string
	Type        AddressType
	NetworkName string
	NetworkScope
}

// HostPort associates an address with a port.
type HostPort struct {
	Address
	Port int
}

// AddressesWithPort returns the given addresses all
// associated with the given port.
func AddressesWithPort(addrs []Address, port int) []HostPort {
	hps := make([]HostPort, len(addrs))
	for i, addr := range addrs {
		hps[i] = HostPort{
			Address: addr,
			Port:    port,
		}
	}
	return hps
}

// NetAddr returns the host-port as an address
// suitable for calling net.Dial.
func (hp HostPort) NetAddr() string {
	return net.JoinHostPort(hp.Value, strconv.Itoa(hp.Port))
}

// String returns a string representation of the address,
// in the form: scope:address(network name);
// for example:
//
//	public:c2-54-226-162-124.compute-1.amazonaws.com(ec2network)
//
// If the scope is NetworkUnknown, the initial scope: prefix will
// be omitted. If the NetworkName is blank, the (network name) suffix
// will be omitted.
func (a Address) String() string {
	var buf bytes.Buffer
	if a.NetworkScope != NetworkUnknown {
		buf.WriteString(string(a.NetworkScope))
		buf.WriteByte(':')
	}
	buf.WriteString(a.Value)
	if a.NetworkName != "" {
		buf.WriteByte('(')
		buf.WriteString(a.NetworkName)
		buf.WriteByte(')')
	}
	return buf.String()
}

// NewAddresses is a convenience function to create addresses from a string slice
func NewAddresses(inAddresses []string) (outAddresses []Address) {
	for _, address := range inAddresses {
		outAddresses = append(outAddresses, NewAddress(address))
	}
	return outAddresses
}

func DeriveAddressType(value string) AddressType {
	ip := net.ParseIP(value)
	if ip != nil {
		if ip.To4() != nil {
			return Ipv4Address
		}
		if ip.To16() != nil {
			return Ipv6Address
		}
		panic("Unknown form of IP address")
	}
	// TODO(gz): Check value is a valid hostname
	return HostName
}

func NewAddress(value string) Address {
	addresstype := DeriveAddressType(value)
	return Address{value, addresstype, "", NetworkUnknown}
}

// netLookupIP is a var for testing.
var netLookupIP = net.LookupIP

// HostAddresses looks up the IP addresses of the specified
// host, and translates them into instance.Address values.
//
// The argument passed in is always added ast the final
// address in the resulting slice.
func HostAddresses(host string) (addrs []Address, err error) {
	hostAddr := NewAddress(host)
	if hostAddr.Type != HostName {
		// IPs shouldn't be fed into LookupIP.
		return []Address{hostAddr}, nil
	}
	ipaddrs, err := netLookupIP(host)
	if err != nil {
		return nil, err
	}
	addrs = make([]Address, len(ipaddrs)+1)
	for i, ipaddr := range ipaddrs {
		switch len(ipaddr) {
		case net.IPv4len:
			addrs[i].Type = Ipv4Address
			addrs[i].Value = ipaddr.String()
		case net.IPv6len:
			if ipaddr.To4() != nil {
				// ipaddr is an IPv4 address represented in 16 bytes.
				addrs[i].Type = Ipv4Address
			} else {
				addrs[i].Type = Ipv6Address
			}
			addrs[i].Value = ipaddr.String()
		}
	}
	addrs[len(addrs)-1] = hostAddr
	return addrs, err
}

// SelectPublicAddress picks one address from a slice that would
// be appropriate to display as a publicly accessible endpoint.
// If there are no suitable addresses, the empty string is returned.
func SelectPublicAddress(addresses []Address) string {
	mostpublic := ""
	for _, addr := range addresses {
		if addr.Type != Ipv6Address {
			switch addr.NetworkScope {
			case NetworkPublic:
				return addr.Value
			case NetworkCloudLocal, NetworkUnknown:
				mostpublic = addr.Value
			}
		}
	}
	return mostpublic
}

// SelectInternalAddress picks one address from a slice that can be
// used as an endpoint for juju internal communication.
// If there are no suitable addresses, the empty string is returned.
func SelectInternalAddress(addresses []Address, machineLocal bool) string {
	usableAddress := ""
	for _, addr := range addresses {
		if addr.Type != Ipv6Address {
			switch addr.NetworkScope {
			case NetworkCloudLocal:
				return addr.Value
			case NetworkMachineLocal:
				if machineLocal {
					return addr.Value
				}
			case NetworkPublic, NetworkUnknown:
				if usableAddress == "" {
					usableAddress = addr.Value
				}
			}
		}
	}
	return usableAddress
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package net

import (
	"fmt"
	_net "net"
	"os/exec"
)

// IsIPV6 checks if the input contains a valid IPV6 address
func IsIPV6(ip _net.IP) bool {
	return ip != nil && ip.To4() == nil
}

// IsPortAvailable checks if a TCP port is available or not
func IsPortAvailable(p int) bool {
	conn, err := _net.Dial("tcp", fmt.Sprintf(":%v", p))
	if err != nil {
		return true
	}
	defer conn.Close()
	return false
}

// IsIPv6Enabled checks if IPV6 is enabled or not and we have
// at least one configured in the pod
func IsIPv6Enabled() bool {
	cmd := exec.Command("test", "-f", "/proc/net/if_inet6")
	if cmd.Run() != nil {
		return false
	}

	addrs, err := _net.InterfaceAddrs()
	if err != nil {
		return false
	}

	for _, addr := range addrs {
		ip, _, _ := _net.ParseCIDR(addr.String())
		if IsIPV6(ip) {
			return true
		}
	}

	return false
}
<commit_msg>use listen to ensure the port is free (#6990) (#7467)<commit_after>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package net

import (
	"fmt"
	_net "net"
	"os/exec"
)

// IsIPV6 checks if the input contains a valid IPV6 address
func IsIPV6(ip _net.IP) bool {
	return ip != nil && ip.To4() == nil
}

// IsPortAvailable checks if a TCP port is available or not
func IsPortAvailable(p int) bool {
	ln, err := _net.Listen("tcp", fmt.Sprintf(":%v", p))
	if err != nil {
		return false
	}
	defer ln.Close()
	return true
}

// IsIPv6Enabled checks if IPV6 is enabled or not and we have
// at least one configured in the pod
func IsIPv6Enabled() bool {
	cmd := exec.Command("test", "-f", "/proc/net/if_inet6")
	if cmd.Run() != nil {
		return false
	}

	addrs, err := _net.InterfaceAddrs()
	if err != nil {
		return false
	}

	for _, addr := range addrs {
		ip, _, _ := _net.ParseCIDR(addr.String())
		if IsIPV6(ip) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>/*
 *  Charon: A game authentication server
 *  Copyright (C) 2014-2016  Alex Mayfield <alexmax2742@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package charon

import (
	"crypto/sha256"
	"errors"
	"strings"
	"sync"
	"time"

	"github.com/AlexMax/charon/srp"
	"github.com/go-ini/ini"
	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3" // Database driver
)

// Database is an instance of our database connection and all necessary state
// used to manage said instance.
type Database struct {
	db    *sqlx.DB
	mutex sync.Mutex
}

// Schema for sqlite3.
const schema = `
CREATE TABLE IF NOT EXISTS Users(
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	username VARCHAR(255),
	email VARCHAR(255),
	verifier BLOB,
	salt BLOB,
	access TEXT,
	active TINYINT(1),
	createdAt DATETIME NOT NULL,
	updatedAt DATETIME NOT NULL
);

CREATE TABLE IF NOT EXISTS Profiles(
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	clan VARCHAR(255),
	clantag VARCHAR(255),
	contactinfo VARCHAR(255),
	country VARCHAR(255),
	gravatar TEXT,
	location VARCHAR(255),
	message VARCHAR(255),
	username VARCHAR(255),
	visible TINYINT(1) DEFAULT 1,
	visible_lastseen TINYINT(1) DEFAULT 1,
	createdAt DATETIME NOT NULL,
	updatedAt DATETIME NOT NULL,
	UserId INTEGER
);`

// NewDatabase creates a new Database instance.
func NewDatabase(config *ini.File) (database *Database, err error) {
	// Create a database connection.
	filename := config.Section("database").Key("filename").MustString(":memory:")
	db, err := sqlx.Connect("sqlite3", filename)
	if err != nil {
		return
	}

	// Create the database schema.
	_ = db.MustExec("PRAGMA foreign_keys = ON;")
	_ = db.MustExec(schema)

	database = new(Database)
	database.db = db
	return
}

// User is a representation of the `User` table in the database.
type User struct {
	ID        uint
	Username  string
	Email     string
	Verifier  []byte
	Salt      []byte
	Access    string
	Active    bool
	CreatedAt time.Time `db:"createdAt"`
	UpdatedAt time.Time `db:"updatedAt"`
}

// User access constants.
const (
	UserAccessUnverified string = "UNVERIFIED"
	UserAccessUser       string = "USER"
	UserAccessOp         string = "OP"
	UserAccessMaster     string = "MASTER"
	UserAccessOwner      string = "OWNER"
)

// AddUser adds a new user.
func (database *Database) AddUser(username string, email string, password string) (err error) {
	srp, err := srp.NewSRP("rfc5054.2048", sha256.New, nil)
	if err != nil {
		return err
	}

	user := new(User)
	user.Username = username
	user.Email = email
	user.Access = UserAccessUnverified
	user.Active = false
	user.CreatedAt = time.Now()
	user.UpdatedAt = time.Now()

	user.Salt, user.Verifier, err = srp.ComputeVerifier([]byte(username), []byte(password))
	if err != nil {
		return err
	}

	database.mutex.Lock()
	_, err = database.db.NamedExec("INSERT INTO Users (Username, Email, Verifier, Salt, Access, Active, createdAt, updatedAt) VALUES (:username, :email, :verifier, :salt, :access, :active, :createdAt, :updatedAt)", user)
	database.mutex.Unlock()
	return
}

// FindUser tries to find a specific user by name or email address.
func (database *Database) FindUser(username string) (user *User, err error) {
	user = &User{}
	database.mutex.Lock()
	err = database.db.Get(user, "SELECT * FROM users WHERE username LIKE $1 OR email LIKE $1", strings.ToLower(username))
	database.mutex.Unlock()
	return
}

// LoginUser tries to log a user in with the passed username and password.
func (database *Database) LoginUser(username string, password string) (user *User, err error) {
	user, err = database.FindUser(username)
	if err != nil {
		return
	}

	srpo, err := srp.NewSRP("rfc5054.2048", sha256.New, nil)
	if err != nil {
		return
	}

	cs := srpo.NewClientSession([]byte(user.Username), []byte(password))
	ss := srpo.NewServerSession([]byte(user.Username), user.Salt, user.Verifier)

	_, err = cs.ComputeKey(user.Salt, ss.GetB())
	if err != nil {
		return
	}

	_, err = ss.ComputeKey(cs.GetA())
	if err != nil {
		return
	}

	cauth := cs.ComputeAuthenticator()
	if !ss.VerifyClientAuthenticator(cauth) {
		err = errors.New("Client Authenticator is not valid")
		return
	}

	return
}

// Profile is representation of the `profile` table in the database.
type Profile struct {
	ID                 uint
	User_id            uint
	Clan               string
	Contactinfo        string
	Country            string
	Gravatar           string
	Location           string
	Message            string
	Username           string
	Visible            bool
	Visible_lastplayed bool
}
<commit_msg>Fix race condition caused by sqlx.Connect().<commit_after>/*
 *  Charon: A game authentication server
 *  Copyright (C) 2014-2016  Alex Mayfield <alexmax2742@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package charon

import (
	"crypto/sha256"
	"errors"
	"strings"
	"sync"
	"time"

	"github.com/AlexMax/charon/srp"
	"github.com/go-ini/ini"
	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3" // Database driver
)

// Database is an instance of our database connection and all necessary state
// used to manage said instance.
type Database struct {
	db    *sqlx.DB
	mutex sync.Mutex
}

// Schema for sqlite3.
const schema = `
CREATE TABLE IF NOT EXISTS Users(
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	username VARCHAR(255),
	email VARCHAR(255),
	verifier BLOB,
	salt BLOB,
	access TEXT,
	active TINYINT(1),
	createdAt DATETIME NOT NULL,
	updatedAt DATETIME NOT NULL
);

CREATE TABLE IF NOT EXISTS Profiles(
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	clan VARCHAR(255),
	clantag VARCHAR(255),
	contactinfo VARCHAR(255),
	country VARCHAR(255),
	gravatar TEXT,
	location VARCHAR(255),
	message VARCHAR(255),
	username VARCHAR(255),
	visible TINYINT(1) DEFAULT 1,
	visible_lastseen TINYINT(1) DEFAULT 1,
	createdAt DATETIME NOT NULL,
	updatedAt DATETIME NOT NULL,
	UserId INTEGER
);`

var connectMutex sync.Mutex

// NewDatabase creates a new Database instance.
func NewDatabase(config *ini.File) (database *Database, err error) {
	// Create a database connection.
	filename := config.Section("database").Key("filename").MustString(":memory:")
	connectMutex.Lock()
	db, err := sqlx.Connect("sqlite3", filename)
	connectMutex.Unlock()
	if err != nil {
		return
	}

	// Create the database schema.
	_ = db.MustExec("PRAGMA foreign_keys = ON;")
	_ = db.MustExec(schema)

	database = new(Database)
	database.db = db
	return
}

// User is a representation of the `User` table in the database.
type User struct {
	ID        uint
	Username  string
	Email     string
	Verifier  []byte
	Salt      []byte
	Access    string
	Active    bool
	CreatedAt time.Time `db:"createdAt"`
	UpdatedAt time.Time `db:"updatedAt"`
}

// User access constants.
const (
	UserAccessUnverified string = "UNVERIFIED"
	UserAccessUser       string = "USER"
	UserAccessOp         string = "OP"
	UserAccessMaster     string = "MASTER"
	UserAccessOwner      string = "OWNER"
)

// AddUser adds a new user.
func (database *Database) AddUser(username string, email string, password string) (err error) {
	srp, err := srp.NewSRP("rfc5054.2048", sha256.New, nil)
	if err != nil {
		return err
	}

	user := new(User)
	user.Username = username
	user.Email = email
	user.Access = UserAccessUnverified
	user.Active = false
	user.CreatedAt = time.Now()
	user.UpdatedAt = time.Now()

	user.Salt, user.Verifier, err = srp.ComputeVerifier([]byte(username), []byte(password))
	if err != nil {
		return err
	}

	database.mutex.Lock()
	_, err = database.db.NamedExec("INSERT INTO Users (Username, Email, Verifier, Salt, Access, Active, createdAt, updatedAt) VALUES (:username, :email, :verifier, :salt, :access, :active, :createdAt, :updatedAt)", user)
	database.mutex.Unlock()
	return
}

// FindUser tries to find a specific user by name or email address.
func (database *Database) FindUser(username string) (user *User, err error) {
	user = &User{}
	database.mutex.Lock()
	err = database.db.Get(user, "SELECT * FROM users WHERE username LIKE $1 OR email LIKE $1", strings.ToLower(username))
	database.mutex.Unlock()
	return
}

// LoginUser tries to log a user in with the passed username and password.
func (database *Database) LoginUser(username string, password string) (user *User, err error) {
	user, err = database.FindUser(username)
	if err != nil {
		return
	}

	srpo, err := srp.NewSRP("rfc5054.2048", sha256.New, nil)
	if err != nil {
		return
	}

	cs := srpo.NewClientSession([]byte(user.Username), []byte(password))
	ss := srpo.NewServerSession([]byte(user.Username), user.Salt, user.Verifier)

	_, err = cs.ComputeKey(user.Salt, ss.GetB())
	if err != nil {
		return
	}

	_, err = ss.ComputeKey(cs.GetA())
	if err != nil {
		return
	}

	cauth := cs.ComputeAuthenticator()
	if !ss.VerifyClientAuthenticator(cauth) {
		err = errors.New("Client Authenticator is not valid")
		return
	}

	return
}

// Profile is representation of the `profile` table in the database.
type Profile struct {
	ID                 uint
	User_id            uint
	Clan               string
	Contactinfo        string
	Country            string
	Gravatar           string
	Location           string
	Message            string
	Username           string
	Visible            bool
	Visible_lastplayed bool
}
<|endoftext|>


<commit_before>package network

import (
	"io"
	"os"

	"github.com/moby/buildkit/solver/pb"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

type Opt struct {
	Root          string
	Mode          string
	CNIConfigPath string
	CNIBinaryDir  string
}

// Providers returns the network provider set
func Providers(opt Opt) (map[pb.NetMode]Provider, error) {
	var defaultProvider Provider
	switch opt.Mode {
	case "cni":
		cniProvider, err := NewCNIProvider(opt)
		if err != nil {
			return nil, err
		}
		defaultProvider = cniProvider
	case "host":
		defaultProvider = NewHostProvider()
	case "auto", "":
		if _, err := os.Stat(opt.CNIConfigPath); err == nil {
			cniProvider, err := NewCNIProvider(opt)
			if err != nil {
				return nil, err
			}
			defaultProvider = cniProvider
		} else {
			defaultProvider = NewHostProvider()
		}
	default:
		return nil, errors.Errorf("invalid network mode: %q", opt.Mode)
	}

	return map[pb.NetMode]Provider{
		pb.NetMode_UNSET: defaultProvider,
		pb.NetMode_HOST:  NewHostProvider(),
		pb.NetMode_NONE:  NewNoneProvider(),
	}, nil
}

// Provider interface for Network
type Provider interface {
	New() (Namespace, error)
}

// Namespace of network for workers
type Namespace interface {
	io.Closer
	// Set the namespace on the spec
	Set(*specs.Spec)
}

// NetworkOpts hold network options
type NetworkOpts struct {
	Type          string
	CNIConfigPath string
	CNIPluginPath string
}
<commit_msg>util: add warning if network fallback is used<commit_after>package network

import (
	"io"
	"os"

	"github.com/moby/buildkit/solver/pb"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type Opt struct {
	Root          string
	Mode          string
	CNIConfigPath string
	CNIBinaryDir  string
}

// Providers returns the network provider set
func Providers(opt Opt) (map[pb.NetMode]Provider, error) {
	var defaultProvider Provider
	switch opt.Mode {
	case "cni":
		cniProvider, err := NewCNIProvider(opt)
		if err != nil {
			return nil, err
		}
		defaultProvider = cniProvider
	case "host":
		defaultProvider = NewHostProvider()
	case "auto", "":
		if _, err := os.Stat(opt.CNIConfigPath); err == nil {
			cniProvider, err := NewCNIProvider(opt)
			if err != nil {
				return nil, err
			}
			defaultProvider = cniProvider
		} else {
			logrus.Warnf("using host network as the default")
			defaultProvider = NewHostProvider()
		}
	default:
		return nil, errors.Errorf("invalid network mode: %q", opt.Mode)
	}

	return map[pb.NetMode]Provider{
		pb.NetMode_UNSET: defaultProvider,
		pb.NetMode_HOST:  NewHostProvider(),
		pb.NetMode_NONE:  NewNoneProvider(),
	}, nil
}

// Provider interface for Network
type Provider interface {
	New() (Namespace, error)
}

// Namespace of network for workers
type Namespace interface {
	io.Closer
	// Set the namespace on the spec
	Set(*specs.Spec)
}

// NetworkOpts hold network options
type NetworkOpts struct {
	Type          string
	CNIConfigPath string
	CNIPluginPath string
}
<|endoftext|>


<commit_before>package irc

import (
	"testing"
)

func TestIsDirect(t *testing.T) {
	direct_msg := "kyle: you are pretty terrible at coding"
	if !IsDirect(direct_msg, "kyle") {
		t.Error("Expected", true, "got", false)
	}

	not_direct_msg := "this isn't a direct message"
	if IsDirect(not_direct_msg, "kyle") {
		t.Error("Expected", false, "got", true)
	}
}

func TestIsChannel(t *testing.T) {
	if !IsChannel("#test") {
		t.Error("Expected", true, "got", false)
	}

	if IsChannel("test") {
		t.Error("Expected", false, "got", true)
	}
}
<commit_msg>TestStripNickOnDirect<commit_after>package irc

import (
	"testing"
)

func TestIsDirect(t *testing.T) {
	direct_msg := "kyle: you are pretty terrible at coding"
	if !IsDirect(direct_msg, "kyle") {
		t.Error("Expected", true, "got", false)
	}

	not_direct_msg := "this isn't a direct message"
	if IsDirect(not_direct_msg, "kyle") {
		t.Error("Expected", false, "got", true)
	}
}

func TestIsChannel(t *testing.T) {
	if !IsChannel("#test") {
		t.Error("Expected", true, "got", false)
	}

	if IsChannel("test") {
		t.Error("Expected", false, "got", true)
	}
}

//func TestStripNickOnDirect(msg string, nick string) {
func TestStripNickOnDirect(t *testing.T) {
	new_msg := StripNickOnDirect("kyle: this is a message", "kyle")
	if new_msg != "this is a message" {
		t.Error("Expected", "this is a message", "got", new_msg)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license

package utils

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/base32"
)

var (
	// standard b32 alphabet, but in lowercase for silly aesthetic reasons
	b32encoder = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
)

const (
	SecretTokenLength = 26
)

// generate a secret token that cannot be brute-forced via online attacks
func GenerateSecretToken() string {
	// 128 bits of entropy are enough to resist any online attack:
	var buf [16]byte
	rand.Read(buf[:])
	// 26 ASCII characters, should be fine for most purposes
	return b32encoder.EncodeToString(buf[:])
}

// securely check if a supplied token matches a stored token
func SecretTokensMatch(storedToken string, suppliedToken string) bool {
	// XXX fix a potential gotcha: if the stored token is uninitialized,
	// then nothing should match it, not even supplying an empty token.
	if len(storedToken) == 0 {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(storedToken), []byte(suppliedToken)) == 1
}
<commit_msg>change the b32 alphabet for absolutely no reason<commit_after>// Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license

package utils

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/base32"
)

var (
	// slingamn's own private b32 alphabet, removing 1, l, o, and 0
	b32encoder = base32.NewEncoding("abcdefghijkmnpqrstuvwxyz23456789").WithPadding(base32.NoPadding)
)

const (
	SecretTokenLength = 26
)

// generate a secret token that cannot be brute-forced via online attacks
func GenerateSecretToken() string {
	// 128 bits of entropy are enough to resist any online attack:
	var buf [16]byte
	rand.Read(buf[:])
	// 26 ASCII characters, should be fine for most purposes
	return b32encoder.EncodeToString(buf[:])
}

// securely check if a supplied token matches a stored token
func SecretTokensMatch(storedToken string, suppliedToken string) bool {
	// XXX fix a potential gotcha: if the stored token is uninitialized,
	// then nothing should match it, not even supplying an empty token.
	if len(storedToken) == 0 {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(storedToken), []byte(suppliedToken)) == 1
}
<|endoftext|>


<commit_before>package astits

import "github.com/asticode/go-astilog"

// P-STD buffer scales
const (
	PSTDBufferScale128Bytes  = 0
	PSTDBufferScale1024Bytes = 1
)

// PTS DTS indicator
const (
	PTSDTSIndicatorBothPresent = 3
	PTSDTSIndicatorIsForbidden = 1
	PTSDTSIndicatorNoPTSOrDTS  = 0
	PTSDTSIndicatorOnlyPTS     = 2
)

// Stream IDs
const (
	StreamIDPaddingStream  = 190
	StreamIDPrivateStream2 = 191
)

// Trick mode controls
const (
	TrickModeControlFastForward = 0
	TrickModeControlFastReverse = 3
	TrickModeControlFreezeFrame = 2
	TrickModeControlSlowMotion  = 1
	TrickModeControlSlowReverse = 4
)

// PESData represents a PES data
// https://en.wikipedia.org/wiki/Packetized_elementary_stream
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
// http://happy.emu.id.au/lab/tut/dttb/dtbtut4b.htm
type PESData struct {
	Data   []byte
	Header *PESHeader
}

// PESHeader represents a packet PES header
type PESHeader struct {
	OptionalHeader *PESOptionalHeader
	PacketLength   uint16 // Specifies the number of bytes remaining in the packet after this field. Can be zero. If the PES packet length is set to zero, the PES packet can be of any length. A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream.
	StreamID       uint8  // Examples: Audio streams (0xC0-0xDF), Video streams (0xE0-0xEF)
}

// PESOptionalHeader represents a PES optional header
type PESOptionalHeader struct {
	AdditionalCopyInfo              uint8
	CRC                             uint16
	DataAlignmentIndicator          bool // True indicates that the PES packet header is immediately followed by the video start code or audio syncword
	DSMTrickMode                    *DSMTrickMode
	DTS                             *ClockReference
	ESCR                            *ClockReference
	ESRate                          uint32
	Extension2Data                  []byte
	Extension2Length                uint8
	HasAdditionalCopyInfo           bool
	HasCRC                          bool
	HasDSMTrickMode                 bool
	HasESCR                         bool
	HasESRate                       bool
	HasExtension                    bool
	HasExtension2                   bool
	HasOptionalFields               bool
	HasPackHeaderField              bool
	HasPrivateData                  bool
	HasProgramPacketSequenceCounter bool
	HasPSTDBuffer                   bool
	HeaderLength                    uint8
	IsCopyrighted                   bool
	IsOriginal                      bool
	MarkerBits                      uint8
	MPEG1OrMPEG2ID                  uint8
	OriginalStuffingLength          uint8
	PacketSequenceCounter           uint8
	PackField                       uint8
	Priority                        bool
	PrivateData                     []byte
	PSTDBufferScale                 uint8
	PSTDBufferSize                  uint16
	PTS                             *ClockReference
	PTSDTSIndicator                 uint8
	ScramblingControl               uint8
}

// DSMTrickMode represents a DSM trick mode
// https://books.google.fr/books?id=vwUrAwAAQBAJ&pg=PT501&lpg=PT501&dq=dsm+trick+mode+control&source=bl&ots=fI-9IHXMRL&sig=PWnhxrsoMWNQcl1rMCPmJGNO9Ds&hl=fr&sa=X&ved=0ahUKEwjogafD8bjXAhVQ3KQKHeHKD5oQ6AEINDAB#v=onepage&q=dsm%20trick%20mode%20control&f=false
type DSMTrickMode struct {
	FieldID             uint8
	FrequencyTruncation uint8
	IntraSliceRefresh   uint8
	RepeatControl       uint8
	TrickModeControl    uint8
}

// parsePESData parses a PES data
func parsePESData(i []byte) (d *PESData) {
	// Init
	d = &PESData{}

	// Parse header
	var offset, dataStart, dataEnd = 3, 0, 0
	d.Header, dataStart, dataEnd = parsePESHeader(i, &offset)

	// Parse data
	d.Data = i[dataStart:dataEnd]
	return
}

// hasPESOptionalHeader checks whether the data has a PES optional header
func hasPESOptionalHeader(streamID uint8) bool {
	return streamID != StreamIDPaddingStream && streamID != StreamIDPrivateStream2
}

// parsePESData parses a PES header
func parsePESHeader(i []byte, offset *int) (h *PESHeader, dataStart, dataEnd int) {
	// Init
	h = &PESHeader{}

	// Stream ID
	h.StreamID = uint8(i[*offset])
	*offset += 1

	// Length
	h.PacketLength = uint16(i[*offset])<<8 | uint16(i[*offset+1])
	*offset += 2

	// Data end
	if h.PacketLength > 0 {
		dataEnd = *offset + int(h.PacketLength)
	} else {
		dataEnd = len(i)
	}

	// Check for incomplete data
	// TODO Throw away the data?
	if dataEnd > len(i) {
		astilog.Debug("PES dataEnd > len(i), needs fixing")
		dataEnd = len(i)
	}

	// Optional header
	if hasPESOptionalHeader(h.StreamID) {
		h.OptionalHeader, dataStart = parsePESOptionalHeader(i, offset)
	} else {
		dataStart = *offset
	}
	return
}

// parsePESOptionalHeader parses a PES optional header
func parsePESOptionalHeader(i []byte, offset *int) (h *PESOptionalHeader, dataStart int) {
	// Init
	h = &PESOptionalHeader{}

	// Marker bits
	h.MarkerBits = uint8(i[*offset]) >> 6

	// Scrambling control
	h.ScramblingControl = uint8(i[*offset]) >> 4 & 0x3

	// Priority
	h.Priority = uint8(i[*offset])&0x8 > 0

	// Data alignment indicator
	h.DataAlignmentIndicator = uint8(i[*offset])&0x4 > 0

	// Copyrighted
	h.IsCopyrighted = uint(i[*offset])&0x2 > 0

	// Original or copy
	h.IsOriginal = uint8(i[*offset])&0x1 > 0
	*offset += 1

	// PTS DST indicator
	h.PTSDTSIndicator = uint8(i[*offset]) >> 6 & 0x3

	// Flags
	h.HasESCR = uint8(i[*offset])&0x20 > 0
	h.HasESRate = uint8(i[*offset])&0x10 > 0
	h.HasDSMTrickMode = uint8(i[*offset])&0x8 > 0
	h.HasAdditionalCopyInfo = uint8(i[*offset])&0x4 > 0
	h.HasCRC = uint8(i[*offset])&0x2 > 0
	h.HasExtension = uint8(i[*offset])&0x1 > 0
	*offset += 1

	// Header length
	h.HeaderLength = uint8(i[*offset])
	*offset += 1

	// Data start
	dataStart = *offset + int(h.HeaderLength)

	// PTS/DTS
	if h.PTSDTSIndicator == PTSDTSIndicatorOnlyPTS {
		h.PTS = parsePTSOrDTS(i[*offset:])
		*offset += 5
	} else if h.PTSDTSIndicator == PTSDTSIndicatorBothPresent {
		h.PTS = parsePTSOrDTS(i[*offset:])
		*offset += 5
		h.DTS = parsePTSOrDTS(i[*offset:])
		*offset += 5
	}

	// ESCR
	if h.HasESCR {
		h.ESCR = parseESCR(i[*offset:])
		*offset += 6
	}

	// ES rate
	if h.HasESRate {
		h.ESRate = uint32(i[*offset])&0x7f<<15 | uint32(i[*offset+1])<<7 | uint32(i[*offset+2])>>1
		*offset += 3
	}

	// Trick mode
	if h.HasDSMTrickMode {
		h.DSMTrickMode = parseDSMTrickMode(i[*offset])
		*offset += 1
	}

	// Additional copy info
	if h.HasAdditionalCopyInfo {
		h.AdditionalCopyInfo = i[*offset] & 0x7f
		*offset += 1
	}

	// CRC
	if h.HasCRC {
		h.CRC = uint16(i[*offset])>>8 | uint16(i[*offset+1])
		*offset += 2
	}

	// Extension
	if h.HasExtension {
		// Flags
		h.HasPrivateData = i[*offset]&0x80 > 0
		h.HasPackHeaderField = i[*offset]&0x40 > 0
		h.HasProgramPacketSequenceCounter = i[*offset]&0x20 > 0
		h.HasPSTDBuffer = i[*offset]&0x10 > 0
		h.HasExtension2 = i[*offset]&0x1 > 0
		*offset += 1

		// Private data
		if h.HasPrivateData {
			h.PrivateData = i[*offset : *offset+16]
			*offset += 16
		}

		// Pack field length
		if h.HasPackHeaderField {
			h.PackField = uint8(i[*offset])
			*offset += 1
		}

		// Program packet sequence counter
		if h.HasProgramPacketSequenceCounter {
			h.PacketSequenceCounter = uint8(i[*offset]) & 0x7f
			h.MPEG1OrMPEG2ID = uint8(i[*offset+1]) >> 6 & 0x1
			h.OriginalStuffingLength = uint8(i[*offset+1]) & 0x3f
			*offset += 2
		}

		// P-STD buffer
		if h.HasPSTDBuffer {
			h.PSTDBufferScale = i[*offset] >> 5 & 0x1
			h.PSTDBufferSize = uint16(i[*offset])&0x1f<<8 | uint16(i[*offset+1])
			*offset += 2
		}

		// Extension 2
		if h.HasExtension2 {
			// Length
			h.Extension2Length = uint8(i[*offset]) & 0x7f
			*offset += 2

			// Data
			h.Extension2Data = i[*offset : *offset+int(h.Extension2Length)]
			*offset += int(h.Extension2Length)
		}
	}
	return
}

// parseDSMTrickMode parses a DSM trick mode
func parseDSMTrickMode(i byte) (m *DSMTrickMode) {
	m = &DSMTrickMode{}
	m.TrickModeControl = i >> 5
	if m.TrickModeControl == TrickModeControlFastForward || m.TrickModeControl == TrickModeControlFastReverse {
		m.FieldID = i >> 3 & 0x3
		m.IntraSliceRefresh = i >> 2 & 0x1
		m.FrequencyTruncation = i & 0x3
	} else if m.TrickModeControl == TrickModeControlFreezeFrame {
		m.FieldID = i >> 3 & 0x3
	} else if m.TrickModeControl == TrickModeControlSlowMotion || m.TrickModeControl == TrickModeControlSlowReverse {
		m.RepeatControl = i & 0x1f
	}
	return
}

// parsePTSOrDTS parses a PTS or a DTS
func parsePTSOrDTS(i []byte) *ClockReference {
	return newClockReference(int(uint64(i[0])>>1&0x7<<30|uint64(i[1])<<22|uint64(i[2])>>1&0x7f<<15|uint64(i[3])<<7|uint64(i[4])>>1&0x7f), 0)
}

// parseESCR parses an ESCR
func parseESCR(i []byte) *ClockReference {
	var escr = uint64(i[0])>>3&0x7<<39 | uint64(i[0])&0x3<<37 | uint64(i[1])<<29 | uint64(i[2])>>3<<24 | uint64(i[2])&0x3<<22 | uint64(i[3])<<14 | uint64(i[4])>>3<<9 | uint64(i[4])&0x3<<7 | uint64(i[5])>>1
	return newClockReference(int(escr>>9), int(escr&0x1ff))
}
<commit_msg>Added private stream 1<commit_after>package astits

import "github.com/asticode/go-astilog"

// P-STD buffer scales
const (
	PSTDBufferScale128Bytes  = 0
	PSTDBufferScale1024Bytes = 1
)

// PTS DTS indicator
const (
	PTSDTSIndicatorBothPresent = 3
	PTSDTSIndicatorIsForbidden = 1
	PTSDTSIndicatorNoPTSOrDTS  = 0
	PTSDTSIndicatorOnlyPTS     = 2
)

// Stream IDs
const (
	StreamIDPrivateStream1 = 189
	StreamIDPaddingStream  = 190
	StreamIDPrivateStream2 = 191
)

// Trick mode controls
const (
	TrickModeControlFastForward = 0
	TrickModeControlFastReverse = 3
	TrickModeControlFreezeFrame = 2
	TrickModeControlSlowMotion  = 1
	TrickModeControlSlowReverse = 4
)

// PESData represents a PES data
// https://en.wikipedia.org/wiki/Packetized_elementary_stream
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
// http://happy.emu.id.au/lab/tut/dttb/dtbtut4b.htm
type PESData struct {
	Data   []byte
	Header *PESHeader
}

// PESHeader represents a packet PES header
type PESHeader struct {
	OptionalHeader *PESOptionalHeader
	PacketLength   uint16 // Specifies the number of bytes remaining in the packet after this field. Can be zero. If the PES packet length is set to zero, the PES packet can be of any length. A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream.
	StreamID       uint8  // Examples: Audio streams (0xC0-0xDF), Video streams (0xE0-0xEF)
}

// PESOptionalHeader represents a PES optional header
type PESOptionalHeader struct {
	AdditionalCopyInfo              uint8
	CRC                             uint16
	DataAlignmentIndicator          bool // True indicates that the PES packet header is immediately followed by the video start code or audio syncword
	DSMTrickMode                    *DSMTrickMode
	DTS                             *ClockReference
	ESCR                            *ClockReference
	ESRate                          uint32
	Extension2Data                  []byte
	Extension2Length                uint8
	HasAdditionalCopyInfo           bool
	HasCRC                          bool
	HasDSMTrickMode                 bool
	HasESCR                         bool
	HasESRate                       bool
	HasExtension                    bool
	HasExtension2                   bool
	HasOptionalFields               bool
	HasPackHeaderField              bool
	HasPrivateData                  bool
	HasProgramPacketSequenceCounter bool
	HasPSTDBuffer                   bool
	HeaderLength                    uint8
	IsCopyrighted                   bool
	IsOriginal                      bool
	MarkerBits                      uint8
	MPEG1OrMPEG2ID                  uint8
	OriginalStuffingLength          uint8
	PacketSequenceCounter           uint8
	PackField                       uint8
	Priority                        bool
	PrivateData                     []byte
	PSTDBufferScale                 uint8
	PSTDBufferSize                  uint16
	PTS                             *ClockReference
	PTSDTSIndicator                 uint8
	ScramblingControl               uint8
}

// DSMTrickMode represents a DSM trick mode
// https://books.google.fr/books?id=vwUrAwAAQBAJ&pg=PT501&lpg=PT501&dq=dsm+trick+mode+control&source=bl&ots=fI-9IHXMRL&sig=PWnhxrsoMWNQcl1rMCPmJGNO9Ds&hl=fr&sa=X&ved=0ahUKEwjogafD8bjXAhVQ3KQKHeHKD5oQ6AEINDAB#v=onepage&q=dsm%20trick%20mode%20control&f=false
type DSMTrickMode struct {
	FieldID             uint8
	FrequencyTruncation uint8
	IntraSliceRefresh   uint8
	RepeatControl       uint8
	TrickModeControl    uint8
}

// parsePESData parses a PES data
func parsePESData(i []byte) (d *PESData) {
	// Init
	d = &PESData{}

	// Parse header
	var offset, dataStart, dataEnd = 3, 0, 0
	d.Header, dataStart, dataEnd = parsePESHeader(i, &offset)

	// Parse data
	d.Data = i[dataStart:dataEnd]
	return
}

// hasPESOptionalHeader checks whether the data has a PES optional header
func hasPESOptionalHeader(streamID uint8) bool {
	return streamID != StreamIDPaddingStream && streamID != StreamIDPrivateStream2
}

// parsePESData parses a PES header
func parsePESHeader(i []byte, offset *int) (h *PESHeader, dataStart, dataEnd int) {
	// Init
	h = &PESHeader{}

	// Stream ID
	h.StreamID = uint8(i[*offset])
	*offset += 1

	// Length
	h.PacketLength = uint16(i[*offset])<<8 | uint16(i[*offset+1])
	*offset += 2

	// Data end
	if h.PacketLength > 0 {
		dataEnd = *offset + int(h.PacketLength)
	} else {
		dataEnd = len(i)
	}

	// Check for incomplete data
	// TODO Throw away the data?
	if dataEnd > len(i) {
		astilog.Debug("PES dataEnd > len(i), needs fixing")
		dataEnd = len(i)
	}

	// Optional header
	if hasPESOptionalHeader(h.StreamID) {
		h.OptionalHeader, dataStart = parsePESOptionalHeader(i, offset)
	} else {
		dataStart = *offset
	}
	return
}

// parsePESOptionalHeader parses a PES optional header
func parsePESOptionalHeader(i []byte, offset *int) (h *PESOptionalHeader, dataStart int) {
	// Init
	h = &PESOptionalHeader{}

	// Marker bits
	h.MarkerBits = uint8(i[*offset]) >> 6

	// Scrambling control
	h.ScramblingControl = uint8(i[*offset]) >> 4 & 0x3

	// Priority
	h.Priority = uint8(i[*offset])&0x8 > 0

	// Data alignment indicator
	h.DataAlignmentIndicator = uint8(i[*offset])&0x4 > 0

	// Copyrighted
	h.IsCopyrighted = uint(i[*offset])&0x2 > 0

	// Original or copy
	h.IsOriginal = uint8(i[*offset])&0x1 > 0
	*offset += 1

	// PTS DST indicator
	h.PTSDTSIndicator = uint8(i[*offset]) >> 6 & 0x3

	// Flags
	h.HasESCR = uint8(i[*offset])&0x20 > 0
	h.HasESRate = uint8(i[*offset])&0x10 > 0
	h.HasDSMTrickMode = uint8(i[*offset])&0x8 > 0
	h.HasAdditionalCopyInfo = uint8(i[*offset])&0x4 > 0
	h.HasCRC = uint8(i[*offset])&0x2 > 0
	h.HasExtension = uint8(i[*offset])&0x1 > 0
	*offset += 1

	// Header length
	h.HeaderLength = uint8(i[*offset])
	*offset += 1

	// Data start
	dataStart = *offset + int(h.HeaderLength)

	// PTS/DTS
	if h.PTSDTSIndicator == PTSDTSIndicatorOnlyPTS {
		h.PTS = parsePTSOrDTS(i[*offset:])
		*offset += 5
	} else if h.PTSDTSIndicator == PTSDTSIndicatorBothPresent {
		h.PTS = parsePTSOrDTS(i[*offset:])
		*offset += 5
		h.DTS = parsePTSOrDTS(i[*offset:])
		*offset += 5
	}

	// ESCR
	if h.HasESCR {
		h.ESCR = parseESCR(i[*offset:])
		*offset += 6
	}

	// ES rate
	if h.HasESRate {
		h.ESRate = uint32(i[*offset])&0x7f<<15 | uint32(i[*offset+1])<<7 | uint32(i[*offset+2])>>1
		*offset += 3
	}

	// Trick mode
	if h.HasDSMTrickMode {
		h.DSMTrickMode = parseDSMTrickMode(i[*offset])
		*offset += 1
	}

	// Additional copy info
	if h.HasAdditionalCopyInfo {
		h.AdditionalCopyInfo = i[*offset] & 0x7f
		*offset += 1
	}

	// CRC
	if h.HasCRC {
		h.CRC = uint16(i[*offset])>>8 | uint16(i[*offset+1])
		*offset += 2
	}

	// Extension
	if h.HasExtension {
		// Flags
		h.HasPrivateData = i[*offset]&0x80 > 0
		h.HasPackHeaderField = i[*offset]&0x40 > 0
		h.HasProgramPacketSequenceCounter = i[*offset]&0x20 > 0
		h.HasPSTDBuffer = i[*offset]&0x10 > 0
		h.HasExtension2 = i[*offset]&0x1 > 0
		*offset += 1

		// Private data
		if h.HasPrivateData {
			h.PrivateData = i[*offset : *offset+16]
			*offset += 16
		}

		// Pack field length
		if h.HasPackHeaderField {
			h.PackField = uint8(i[*offset])
			*offset += 1
		}

		// Program packet sequence counter
		if h.HasProgramPacketSequenceCounter {
			h.PacketSequenceCounter = uint8(i[*offset]) & 0x7f
			h.MPEG1OrMPEG2ID = uint8(i[*offset+1]) >> 6 & 0x1
			h.OriginalStuffingLength = uint8(i[*offset+1]) & 0x3f
			*offset += 2
		}

		// P-STD buffer
		if h.HasPSTDBuffer {
			h.PSTDBufferScale = i[*offset] >> 5 & 0x1
			h.PSTDBufferSize = uint16(i[*offset])&0x1f<<8 | uint16(i[*offset+1])
			*offset += 2
		}

		// Extension 2
		if h.HasExtension2 {
			// Length
			h.Extension2Length = uint8(i[*offset]) & 0x7f
			*offset += 2

			// Data
			h.Extension2Data = i[*offset : *offset+int(h.Extension2Length)]
			*offset += int(h.Extension2Length)
		}
	}
	return
}

// parseDSMTrickMode parses a DSM trick mode
func parseDSMTrickMode(i byte) (m *DSMTrickMode) {
	m = &DSMTrickMode{}
	m.TrickModeControl = i >> 5
	if m.TrickModeControl == TrickModeControlFastForward || m.TrickModeControl == TrickModeControlFastReverse {
		m.FieldID = i >> 3 & 0x3
		m.IntraSliceRefresh = i >> 2 & 0x1
		m.FrequencyTruncation = i & 0x3
	} else if m.TrickModeControl == TrickModeControlFreezeFrame {
		m.FieldID = i >> 3 & 0x3
	} else if m.TrickModeControl == TrickModeControlSlowMotion || m.TrickModeControl == TrickModeControlSlowReverse {
		m.RepeatControl = i & 0x1f
	}
	return
}

// parsePTSOrDTS parses a PTS or a DTS
func parsePTSOrDTS(i []byte) *ClockReference {
	return newClockReference(int(uint64(i[0])>>1&0x7<<30|uint64(i[1])<<22|uint64(i[2])>>1&0x7f<<15|uint64(i[3])<<7|uint64(i[4])>>1&0x7f), 0)
}

// parseESCR parses an ESCR
func parseESCR(i []byte) *ClockReference {
	var escr = uint64(i[0])>>3&0x7<<39 | uint64(i[0])&0x3<<37 | uint64(i[1])<<29 | uint64(i[2])>>3<<24 | uint64(i[2])&0x3<<22 | uint64(i[3])<<14 | uint64(i[4])>>3<<9 | uint64(i[4])&0x3<<7 | uint64(i[5])>>1
	return newClockReference(int(escr>>9), int(escr&0x1ff))
}
<|endoftext|>