<commit_before>/*
 * Copyright (c) 2013 Dario Castañé.
 * This file is part of Zas.
 *
 * Zas 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.
 *
 * Zas 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 Zas.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import (
	"errors"
	"fmt"
	thtml "html/template"
	"path"
	"strings"
	"github.com/melvinmt/gt"
)

/*
 * Context data store used in templates.
 */
type ZasData struct {
	// Template used as body from current file.
	Body thtml.HTML
	// Current path (usable in URLs).
	Path string
	// Title from first level header (H1).
	FirstTitle string
	// Site configuration, as found in ZAS_CONF_FILE.
	Site ZasSiteData
	// In-page configuration, from first HTML comment (expected as YAML map).
	Page map[interface{}]interface{}
	// Current directory configuration, from ZAS_DIR_CONF_FILE.
	Directory ConfigSection
	// Config loaded from ZAS_CONF_FILE.
	config ConfigSection
	// i18n helper
	i18n *gt.Build
}

/*
 * Site configuration.
 *
 * They are required fields in order to complete social/semantic meta tags.
 */
type ZasSiteData struct {
	BaseURL string
	Image   string
}

/*
 * Current title, from page's config and first level header (H1), in this order.
 */
func (zd *ZasData) Title() (title string) {
	title, ok := zd.Page["title"].(string)
	if !ok {
		title = zd.FirstTitle
	}
	return
}

/*
 * Builds URL from current configuration.
 */
func (zd *ZasData) URL() string {
	return fmt.Sprintf("%s%s", zd.Site.BaseURL, zd.Path)
}

/*
 * Helper template method to get any value from ZasData.config using pathes.
 */
func (zd *ZasData) Extra(keypath string) (value string, err error) {
	keypath = path.Clean(keypath)
	if path.IsAbs(keypath) {
		keypath = keypath[1:]
	}
	steps := strings.Split(keypath, "/")
	last := len(steps) - 1
	key, steps := steps[last], steps[:last]
	section := zd.config
	for _, step := range steps {
		section = section.GetSection(step)
		if section == nil {
			err = errors.New("not found")
			return
		}
	}
	value = section.GetString(key)
	return
}

func (zd *ZasData) Language() (language string) {
	return zd.Resolve("language")
}

func (zd *ZasData) Resolve(id string) string {
	var (
		value interface{}
		ok bool
	)
	value, ok = zd.Page[id]
	if !ok {
		if zd.Directory != nil {
			value, ok = zd.Directory[id]
		}
		if !ok {
			value, _ = zd.Extra(fmt.Sprintf("/site/%s", id))
		}
	}
	return value.(string)
}

func (zd *ZasData) E(s string, a ...interface{}) (t string) {
	var err error
	zd.i18n.SetTarget(zd.Language())
	if len(a) == 0 {
		t, err = zd.i18n.Translate(s)
	} else {
		t, err = zd.i18n.Translate(s, a)
	}
	if err != nil {
		panic(err)
	}
	return
}

func (zd *ZasData) IsHome() bool {
	return zd.Path == "/index.html" || zd.Path == fmt.Sprintf("/%s/index.html", zd.Language())
}

func NewZasData(filepath string, gen *Generator) (data ZasData) {
	// Any path must finish in ".html".
	if strings.HasSuffix(filepath, ".md") {
		filepath = strings.Replace(filepath, ".md", ".html", -1)
	}
	data.Path = fmt.Sprintf("/%s", filepath)
	data.config = gen.Config
	data.i18n = gen.I18n
	data.Site.BaseURL = gen.Config.GetSection("site").GetString("baseurl")
	data.Site.Image = gen.Config.GetSection("site").GetString("image")
	return
}
<commit_msg>i18n support finished.<commit_after>/*
 * Copyright (c) 2013 Dario Castañé.
 * This file is part of Zas.
 *
 * Zas 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.
 *
 * Zas 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 Zas.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import (
	"errors"
	"fmt"
	thtml "html/template"
	"path"
	"strings"
	"github.com/melvinmt/gt"
)

/*
 * Context data store used in templates.
 */
type ZasData struct {
	// Template used as body from current file.
	Body thtml.HTML
	// Current path (usable in URLs).
	Path string
	// Title from first level header (H1).
	FirstTitle string
	// Site configuration, as found in ZAS_CONF_FILE.
	Site ZasSiteData
	// In-page configuration, from first HTML comment (expected as YAML map).
	Page map[interface{}]interface{}
	// Current directory configuration, from ZAS_DIR_CONF_FILE.
	Directory ConfigSection
	// Config loaded from ZAS_CONF_FILE.
	config ConfigSection
	// i18n helper
	i18n *gt.Build
}

/*
 * Site configuration.
 *
 * They are required fields in order to complete social/semantic meta tags.
 */
type ZasSiteData struct {
	BaseURL string
	Image   string
}

/*
 * Current title, from page's config and first level header (H1), in this order.
 */
func (zd *ZasData) Title() (title string) {
	title, ok := zd.Page["title"].(string)
	if !ok {
		title = zd.FirstTitle
	}
	return
}

/*
 * Builds URL from current configuration.
 */
func (zd *ZasData) URL() string {
	return fmt.Sprintf("%s%s", zd.Site.BaseURL, zd.Path)
}

/*
 * Helper template method to get any value from ZasData.config using pathes.
 */
func (zd *ZasData) Extra(keypath string) (value string, err error) {
	keypath = path.Clean(keypath)
	if path.IsAbs(keypath) {
		keypath = keypath[1:]
	}
	steps := strings.Split(keypath, "/")
	last := len(steps) - 1
	key, steps := steps[last], steps[:last]
	section := zd.config
	for _, step := range steps {
		section = section.GetSection(step)
		if section == nil {
			err = errors.New("not found")
			return
		}
	}
	value = section.GetString(key)
	return
}

func (zd *ZasData) Language() (language string) {
	return zd.Resolve("language")
}

func (zd *ZasData) Resolve(id string) string {
	var (
		value interface{}
		ok bool
	)
	value, ok = zd.Page[id]
	if !ok {
		if zd.Directory != nil {
			value, ok = zd.Directory[id]
		}
		if !ok {
			value, _ = zd.Extra(fmt.Sprintf("/site/%s", id))
		}
	}
	return value.(string)
}

func (zd *ZasData) E(s string, a ...interface{}) (t string) {
	var err error
	target := zd.Language()
	if (zd.config.GetSection("site").GetString("language") == target) {
		return s
	}
	zd.i18n.SetTarget(target)
	if len(a) == 0 {
		fmt.Println(s)
		t, err = zd.i18n.Translate(s)
	} else {
		t, err = zd.i18n.Translate(s, a)
	}
	if err != nil {
		t = "**" + s + "**"
		err = nil
	}
	return
}

func (zd *ZasData) IsHome() bool {
	return zd.Path == "/index.html" || zd.Path == fmt.Sprintf("/%s/index.html", zd.Language())
}

func NewZasData(filepath string, gen *Generator) (data ZasData) {
	// Any path must finish in ".html".
	if strings.HasSuffix(filepath, ".md") {
		filepath = strings.Replace(filepath, ".md", ".html", -1)
	}
	data.Path = fmt.Sprintf("/%s", filepath)
	data.config = gen.Config
	data.i18n = gen.I18n
	data.Site.BaseURL = gen.Config.GetSection("site").GetString("baseurl")
	data.Site.Image = gen.Config.GetSection("site").GetString("image")
	return
}
<|endoftext|>


<commit_before>// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// 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 internal

import (
	"context"
	"crypto/ed25519"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/matrix-org/dendrite/keyserver/api"
	"github.com/matrix-org/gomatrixserverlib"
)

var (
	ctx = context.Background()
)

type mockKeyChangeProducer struct {
	events []api.DeviceMessage
}

func (p *mockKeyChangeProducer) ProduceKeyChanges(keys []api.DeviceMessage) error {
	p.events = append(p.events, keys...)
	return nil
}

type mockDeviceListUpdaterDatabase struct {
	staleUsers   map[string]bool
	prevIDsExist func(string, []int) bool
	storedKeys   []api.DeviceMessage
	mu           sync.Mutex // protect staleUsers
}

// StaleDeviceLists returns a list of user IDs ending with the domains provided who have stale device lists.
// If no domains are given, all user IDs with stale device lists are returned.
func (d *mockDeviceListUpdaterDatabase) StaleDeviceLists(ctx context.Context, domains []gomatrixserverlib.ServerName) ([]string, error) {
	d.mu.Lock()
	defer d.mu.Unlock()
	var result []string
	for userID, isStale := range d.staleUsers {
		if !isStale {
			continue
		}
		_, remoteServer, err := gomatrixserverlib.SplitID('@', userID)
		if err != nil {
			return nil, err
		}
		if len(domains) == 0 {
			result = append(result, userID)
			continue
		}
		for _, d := range domains {
			if remoteServer == d {
				result = append(result, userID)
				break
			}
		}
	}
	return result, nil
}

// MarkDeviceListStale sets the stale bit for this user to isStale.
func (d *mockDeviceListUpdaterDatabase) MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.staleUsers[userID] = isStale
	return nil
}

// StoreRemoteDeviceKeys persists the given keys. Keys with the same user ID and device ID will be replaced. An empty KeyJSON removes the key
// for this (user, device). Does not modify the stream ID for keys.
func (d *mockDeviceListUpdaterDatabase) StoreRemoteDeviceKeys(ctx context.Context, keys []api.DeviceMessage, clear []string) error {
	d.storedKeys = append(d.storedKeys, keys...)
	return nil
}

// PrevIDsExists returns true if all prev IDs exist for this user.
func (d *mockDeviceListUpdaterDatabase) PrevIDsExists(ctx context.Context, userID string, prevIDs []int) (bool, error) {
	return d.prevIDsExist(userID, prevIDs), nil
}

func (d *mockDeviceListUpdaterDatabase) DeviceKeysJSON(ctx context.Context, keys []api.DeviceMessage) error {
	return nil
}

type mockDeviceListUpdaterAPI struct {
}

func (d *mockDeviceListUpdaterAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {

}

type roundTripper struct {
	fn func(*http.Request) (*http.Response, error)
}

func (t *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	return t.fn(req)
}

func newFedClient(tripper func(*http.Request) (*http.Response, error)) *gomatrixserverlib.FederationClient {
	_, pkey, _ := ed25519.GenerateKey(nil)
	fedClient := gomatrixserverlib.NewFederationClient(
		gomatrixserverlib.ServerName("example.test"), gomatrixserverlib.KeyID("ed25519:test"), pkey,
	)
	fedClient.Client = *gomatrixserverlib.NewClient(
		gomatrixserverlib.WithTransport(&roundTripper{tripper}),
	)
	return fedClient
}

// Test that the device keys get persisted and emitted if we have the previous IDs.
func TestUpdateHavePrevID(t *testing.T) {
	db := &mockDeviceListUpdaterDatabase{
		staleUsers: make(map[string]bool),
		prevIDsExist: func(string, []int) bool {
			return true
		},
	}
	ap := &mockDeviceListUpdaterAPI{}
	producer := &mockKeyChangeProducer{}
	updater := NewDeviceListUpdater(db, ap, producer, nil, 1)
	event := gomatrixserverlib.DeviceListUpdateEvent{
		DeviceDisplayName: "Foo Bar",
		Deleted:           false,
		DeviceID:          "FOO",
		Keys:              []byte(`{"key":"value"}`),
		PrevID:            []int{0},
		StreamID:          1,
		UserID:            "@alice:localhost",
	}
	err := updater.Update(ctx, event)
	if err != nil {
		t.Fatalf("Update returned an error: %s", err)
	}
	want := api.DeviceMessage{
		Type:     api.TypeDeviceKeyUpdate,
		StreamID: event.StreamID,
		DeviceKeys: &api.DeviceKeys{
			DeviceID:    event.DeviceID,
			DisplayName: event.DeviceDisplayName,
			KeyJSON:     event.Keys,
			UserID:      event.UserID,
		},
	}
	if !reflect.DeepEqual(producer.events, []api.DeviceMessage{want}) {
		t.Errorf("Update didn't produce correct event, got %v want %v", producer.events, want)
	}
	if !reflect.DeepEqual(db.storedKeys, []api.DeviceMessage{want}) {
		t.Errorf("DB didn't store correct event, got %v want %v", db.storedKeys, want)
	}
	if db.staleUsers[event.UserID] {
		t.Errorf("%s incorrectly marked as stale", event.UserID)
	}
}

// Test that device keys are fetched from the remote server if we are missing prev IDs
// and that the user's devices are marked as stale until it succeeds.
func TestUpdateNoPrevID(t *testing.T) {
	db := &mockDeviceListUpdaterDatabase{
		staleUsers: make(map[string]bool),
		prevIDsExist: func(string, []int) bool {
			return false
		},
	}
	ap := &mockDeviceListUpdaterAPI{}
	producer := &mockKeyChangeProducer{}
	remoteUserID := "@alice:example.somewhere"
	var wg sync.WaitGroup
	wg.Add(1)
	keyJSON := `{"user_id":"` + remoteUserID + `","device_id":"JLAFKJWSCS","algorithms":["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],"keys":{"curve25519:JLAFKJWSCS":"3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI","ed25519:JLAFKJWSCS":"lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"},"signatures":{"` + remoteUserID + `":{"ed25519:JLAFKJWSCS":"dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"}}}`
	fedClient := newFedClient(func(req *http.Request) (*http.Response, error) {
		defer wg.Done()
		if req.URL.Path != "/_matrix/federation/v1/user/devices/"+url.PathEscape(remoteUserID) {
			return nil, fmt.Errorf("test: invalid path: %s", req.URL.Path)
		}
		return &http.Response{
			StatusCode: 200,
			Body: ioutil.NopCloser(strings.NewReader(`
			{
				"user_id": "` + remoteUserID + `",
				"stream_id": 5,
				"devices": [
				  {
					"device_id": "JLAFKJWSCS",
					"keys": ` + keyJSON + `,
					"device_display_name": "Mobile Phone"
				  }
				]
			  }
			`)),
		}, nil
	})
	updater := NewDeviceListUpdater(db, ap, producer, fedClient, 2)
	if err := updater.Start(); err != nil {
		t.Fatalf("failed to start updater: %s", err)
	}
	event := gomatrixserverlib.DeviceListUpdateEvent{
		DeviceDisplayName: "Mobile Phone",
		Deleted:           false,
		DeviceID:          "another_device_id",
		Keys:              []byte(`{"key":"value"}`),
		PrevID:            []int{3},
		StreamID:          4,
		UserID:            remoteUserID,
	}
	err := updater.Update(ctx, event)
	if err != nil {
		t.Fatalf("Update returned an error: %s", err)
	}
	t.Log("waiting for /users/devices to be called...")
	wg.Wait()
	// wait a bit for db to be updated...
	time.Sleep(100 * time.Millisecond)
	want := api.DeviceMessage{
		Type:     api.TypeDeviceKeyUpdate,
		StreamID: 5,
		DeviceKeys: &api.DeviceKeys{
			DeviceID:    "JLAFKJWSCS",
			DisplayName: "Mobile Phone",
			UserID:      remoteUserID,
			KeyJSON:     []byte(keyJSON),
		},
	}
	// Now we should have a fresh list and the keys and emitted something
	if db.staleUsers[event.UserID] {
		t.Errorf("%s still marked as stale", event.UserID)
	}
	if !reflect.DeepEqual(producer.events, []api.DeviceMessage{want}) {
		t.Logf("len got %d len want %d", len(producer.events[0].KeyJSON), len(want.KeyJSON))
		t.Errorf("Update didn't produce correct event, got %v want %v", producer.events, want)
	}
	if !reflect.DeepEqual(db.storedKeys, []api.DeviceMessage{want}) {
		t.Errorf("DB didn't store correct event, got %v want %v", db.storedKeys, want)
	}

}

// Test that if we make N calls to ManualUpdate for the same user, we only do it once, assuming the
// update is still ongoing.
func TestDebounce(t *testing.T) {
	db := &mockDeviceListUpdaterDatabase{
		staleUsers: make(map[string]bool),
		prevIDsExist: func(string, []int) bool {
			return true
		},
	}
	ap := &mockDeviceListUpdaterAPI{}
	producer := &mockKeyChangeProducer{}
	fedCh := make(chan *http.Response, 1)
	srv := gomatrixserverlib.ServerName("example.com")
	userID := "@alice:example.com"
	keyJSON := `{"user_id":"` + userID + `","device_id":"JLAFKJWSCS","algorithms":["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],"keys":{"curve25519:JLAFKJWSCS":"3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI","ed25519:JLAFKJWSCS":"lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"},"signatures":{"` + userID + `":{"ed25519:JLAFKJWSCS":"dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"}}}`
	incomingFedReq := make(chan struct{})
	fedClient := newFedClient(func(req *http.Request) (*http.Response, error) {
		if req.URL.Path != "/_matrix/federation/v1/user/devices/"+url.PathEscape(userID) {
			return nil, fmt.Errorf("test: invalid path: %s", req.URL.Path)
		}
		close(incomingFedReq)
		return <-fedCh, nil
	})
	updater := NewDeviceListUpdater(db, ap, producer, fedClient, 1)
	if err := updater.Start(); err != nil {
		t.Fatalf("failed to start updater: %s", err)
	}

	// hit this 5 times
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func() {
			defer wg.Done()
			if err := updater.ManualUpdate(context.Background(), srv, userID); err != nil {
				t.Errorf("ManualUpdate: %s", err)
			}
		}()
	}

	// wait until the updater hits federation
	select {
	case <-incomingFedReq:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for updater to hit federation")
	}

	// user should be marked as stale
	if !db.staleUsers[userID] {
		t.Errorf("user %s not marked as stale", userID)
	}
	// now send the response over federation
	fedCh <- &http.Response{
		StatusCode: 200,
		Body: ioutil.NopCloser(strings.NewReader(`
		{
			"user_id": "` + userID + `",
			"stream_id": 5,
			"devices": [
			  {
				"device_id": "JLAFKJWSCS",
				"keys": ` + keyJSON + `,
				"device_display_name": "Mobile Phone"
			  }
			]
		  }
		`)),
	}
	close(fedCh)
	// wait until all 5 ManualUpdates return. If we hit federation again we won't send a response
	// and should panic with read on a closed channel
	wg.Wait()

	// user is no longer stale now
	if db.staleUsers[userID] {
		t.Errorf("user %s is marked as stale", userID)
	}
}
<commit_msg>Fix data race in unit tests<commit_after>// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// 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 internal

import (
	"context"
	"crypto/ed25519"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/matrix-org/dendrite/keyserver/api"
	"github.com/matrix-org/gomatrixserverlib"
)

var (
	ctx = context.Background()
)

type mockKeyChangeProducer struct {
	events []api.DeviceMessage
}

func (p *mockKeyChangeProducer) ProduceKeyChanges(keys []api.DeviceMessage) error {
	p.events = append(p.events, keys...)
	return nil
}

type mockDeviceListUpdaterDatabase struct {
	staleUsers   map[string]bool
	prevIDsExist func(string, []int) bool
	storedKeys   []api.DeviceMessage
	mu           sync.Mutex // protect staleUsers
}

// StaleDeviceLists returns a list of user IDs ending with the domains provided who have stale device lists.
// If no domains are given, all user IDs with stale device lists are returned.
func (d *mockDeviceListUpdaterDatabase) StaleDeviceLists(ctx context.Context, domains []gomatrixserverlib.ServerName) ([]string, error) {
	d.mu.Lock()
	defer d.mu.Unlock()
	var result []string
	for userID, isStale := range d.staleUsers {
		if !isStale {
			continue
		}
		_, remoteServer, err := gomatrixserverlib.SplitID('@', userID)
		if err != nil {
			return nil, err
		}
		if len(domains) == 0 {
			result = append(result, userID)
			continue
		}
		for _, d := range domains {
			if remoteServer == d {
				result = append(result, userID)
				break
			}
		}
	}
	return result, nil
}

// MarkDeviceListStale sets the stale bit for this user to isStale.
func (d *mockDeviceListUpdaterDatabase) MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.staleUsers[userID] = isStale
	return nil
}

func (d *mockDeviceListUpdaterDatabase) isStale(userID string) bool {
	d.mu.Lock()
	defer d.mu.Unlock()
	return d.staleUsers[userID]
}

// StoreRemoteDeviceKeys persists the given keys. Keys with the same user ID and device ID will be replaced. An empty KeyJSON removes the key
// for this (user, device). Does not modify the stream ID for keys.
func (d *mockDeviceListUpdaterDatabase) StoreRemoteDeviceKeys(ctx context.Context, keys []api.DeviceMessage, clear []string) error {
	d.storedKeys = append(d.storedKeys, keys...)
	return nil
}

// PrevIDsExists returns true if all prev IDs exist for this user.
func (d *mockDeviceListUpdaterDatabase) PrevIDsExists(ctx context.Context, userID string, prevIDs []int) (bool, error) {
	return d.prevIDsExist(userID, prevIDs), nil
}

func (d *mockDeviceListUpdaterDatabase) DeviceKeysJSON(ctx context.Context, keys []api.DeviceMessage) error {
	return nil
}

type mockDeviceListUpdaterAPI struct {
}

func (d *mockDeviceListUpdaterAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {

}

type roundTripper struct {
	fn func(*http.Request) (*http.Response, error)
}

func (t *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	return t.fn(req)
}

func newFedClient(tripper func(*http.Request) (*http.Response, error)) *gomatrixserverlib.FederationClient {
	_, pkey, _ := ed25519.GenerateKey(nil)
	fedClient := gomatrixserverlib.NewFederationClient(
		gomatrixserverlib.ServerName("example.test"), gomatrixserverlib.KeyID("ed25519:test"), pkey,
	)
	fedClient.Client = *gomatrixserverlib.NewClient(
		gomatrixserverlib.WithTransport(&roundTripper{tripper}),
	)
	return fedClient
}

// Test that the device keys get persisted and emitted if we have the previous IDs.
func TestUpdateHavePrevID(t *testing.T) {
	db := &mockDeviceListUpdaterDatabase{
		staleUsers: make(map[string]bool),
		prevIDsExist: func(string, []int) bool {
			return true
		},
	}
	ap := &mockDeviceListUpdaterAPI{}
	producer := &mockKeyChangeProducer{}
	updater := NewDeviceListUpdater(db, ap, producer, nil, 1)
	event := gomatrixserverlib.DeviceListUpdateEvent{
		DeviceDisplayName: "Foo Bar",
		Deleted:           false,
		DeviceID:          "FOO",
		Keys:              []byte(`{"key":"value"}`),
		PrevID:            []int{0},
		StreamID:          1,
		UserID:            "@alice:localhost",
	}
	err := updater.Update(ctx, event)
	if err != nil {
		t.Fatalf("Update returned an error: %s", err)
	}
	want := api.DeviceMessage{
		Type:     api.TypeDeviceKeyUpdate,
		StreamID: event.StreamID,
		DeviceKeys: &api.DeviceKeys{
			DeviceID:    event.DeviceID,
			DisplayName: event.DeviceDisplayName,
			KeyJSON:     event.Keys,
			UserID:      event.UserID,
		},
	}
	if !reflect.DeepEqual(producer.events, []api.DeviceMessage{want}) {
		t.Errorf("Update didn't produce correct event, got %v want %v", producer.events, want)
	}
	if !reflect.DeepEqual(db.storedKeys, []api.DeviceMessage{want}) {
		t.Errorf("DB didn't store correct event, got %v want %v", db.storedKeys, want)
	}
	if db.isStale(event.UserID) {
		t.Errorf("%s incorrectly marked as stale", event.UserID)
	}
}

// Test that device keys are fetched from the remote server if we are missing prev IDs
// and that the user's devices are marked as stale until it succeeds.
func TestUpdateNoPrevID(t *testing.T) {
	db := &mockDeviceListUpdaterDatabase{
		staleUsers: make(map[string]bool),
		prevIDsExist: func(string, []int) bool {
			return false
		},
	}
	ap := &mockDeviceListUpdaterAPI{}
	producer := &mockKeyChangeProducer{}
	remoteUserID := "@alice:example.somewhere"
	var wg sync.WaitGroup
	wg.Add(1)
	keyJSON := `{"user_id":"` + remoteUserID + `","device_id":"JLAFKJWSCS","algorithms":["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],"keys":{"curve25519:JLAFKJWSCS":"3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI","ed25519:JLAFKJWSCS":"lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"},"signatures":{"` + remoteUserID + `":{"ed25519:JLAFKJWSCS":"dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"}}}`
	fedClient := newFedClient(func(req *http.Request) (*http.Response, error) {
		defer wg.Done()
		if req.URL.Path != "/_matrix/federation/v1/user/devices/"+url.PathEscape(remoteUserID) {
			return nil, fmt.Errorf("test: invalid path: %s", req.URL.Path)
		}
		return &http.Response{
			StatusCode: 200,
			Body: ioutil.NopCloser(strings.NewReader(`
			{
				"user_id": "` + remoteUserID + `",
				"stream_id": 5,
				"devices": [
				  {
					"device_id": "JLAFKJWSCS",
					"keys": ` + keyJSON + `,
					"device_display_name": "Mobile Phone"
				  }
				]
			  }
			`)),
		}, nil
	})
	updater := NewDeviceListUpdater(db, ap, producer, fedClient, 2)
	if err := updater.Start(); err != nil {
		t.Fatalf("failed to start updater: %s", err)
	}
	event := gomatrixserverlib.DeviceListUpdateEvent{
		DeviceDisplayName: "Mobile Phone",
		Deleted:           false,
		DeviceID:          "another_device_id",
		Keys:              []byte(`{"key":"value"}`),
		PrevID:            []int{3},
		StreamID:          4,
		UserID:            remoteUserID,
	}
	err := updater.Update(ctx, event)
	if err != nil {
		t.Fatalf("Update returned an error: %s", err)
	}
	t.Log("waiting for /users/devices to be called...")
	wg.Wait()
	// wait a bit for db to be updated...
	time.Sleep(100 * time.Millisecond)
	want := api.DeviceMessage{
		Type:     api.TypeDeviceKeyUpdate,
		StreamID: 5,
		DeviceKeys: &api.DeviceKeys{
			DeviceID:    "JLAFKJWSCS",
			DisplayName: "Mobile Phone",
			UserID:      remoteUserID,
			KeyJSON:     []byte(keyJSON),
		},
	}
	// Now we should have a fresh list and the keys and emitted something
	if db.isStale(event.UserID) {
		t.Errorf("%s still marked as stale", event.UserID)
	}
	if !reflect.DeepEqual(producer.events, []api.DeviceMessage{want}) {
		t.Logf("len got %d len want %d", len(producer.events[0].KeyJSON), len(want.KeyJSON))
		t.Errorf("Update didn't produce correct event, got %v want %v", producer.events, want)
	}
	if !reflect.DeepEqual(db.storedKeys, []api.DeviceMessage{want}) {
		t.Errorf("DB didn't store correct event, got %v want %v", db.storedKeys, want)
	}

}

// Test that if we make N calls to ManualUpdate for the same user, we only do it once, assuming the
// update is still ongoing.
func TestDebounce(t *testing.T) {
	db := &mockDeviceListUpdaterDatabase{
		staleUsers: make(map[string]bool),
		prevIDsExist: func(string, []int) bool {
			return true
		},
	}
	ap := &mockDeviceListUpdaterAPI{}
	producer := &mockKeyChangeProducer{}
	fedCh := make(chan *http.Response, 1)
	srv := gomatrixserverlib.ServerName("example.com")
	userID := "@alice:example.com"
	keyJSON := `{"user_id":"` + userID + `","device_id":"JLAFKJWSCS","algorithms":["m.olm.v1.curve25519-aes-sha2","m.megolm.v1.aes-sha2"],"keys":{"curve25519:JLAFKJWSCS":"3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI","ed25519:JLAFKJWSCS":"lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"},"signatures":{"` + userID + `":{"ed25519:JLAFKJWSCS":"dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"}}}`
	incomingFedReq := make(chan struct{})
	fedClient := newFedClient(func(req *http.Request) (*http.Response, error) {
		if req.URL.Path != "/_matrix/federation/v1/user/devices/"+url.PathEscape(userID) {
			return nil, fmt.Errorf("test: invalid path: %s", req.URL.Path)
		}
		close(incomingFedReq)
		return <-fedCh, nil
	})
	updater := NewDeviceListUpdater(db, ap, producer, fedClient, 1)
	if err := updater.Start(); err != nil {
		t.Fatalf("failed to start updater: %s", err)
	}

	// hit this 5 times
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func() {
			defer wg.Done()
			if err := updater.ManualUpdate(context.Background(), srv, userID); err != nil {
				t.Errorf("ManualUpdate: %s", err)
			}
		}()
	}

	// wait until the updater hits federation
	select {
	case <-incomingFedReq:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for updater to hit federation")
	}

	// user should be marked as stale
	if !db.isStale(userID) {
		t.Errorf("user %s not marked as stale", userID)
	}
	// now send the response over federation
	fedCh <- &http.Response{
		StatusCode: 200,
		Body: ioutil.NopCloser(strings.NewReader(`
		{
			"user_id": "` + userID + `",
			"stream_id": 5,
			"devices": [
			  {
				"device_id": "JLAFKJWSCS",
				"keys": ` + keyJSON + `,
				"device_display_name": "Mobile Phone"
			  }
			]
		  }
		`)),
	}
	close(fedCh)
	// wait until all 5 ManualUpdates return. If we hit federation again we won't send a response
	// and should panic with read on a closed channel
	wg.Wait()

	// user is no longer stale now
	if db.isStale(userID) {
		t.Errorf("user %s is marked as stale", userID)
	}
}
<|endoftext|>


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

package newlzma

import (
	"errors"
	"fmt"

	"github.com/uli-go/xz/basics/u32"
	"github.com/uli-go/xz/hash"
)

/* For compression we need to find byte sequences that match the byte
 * sequence at the dictionary head. A hash table is a simple method to
 * provide this capability.
 */

// MaxDictCap defines the maximum capacity for a dictionary. The LZMA
// specification calls the dictionary capacity dictionary size.
const MaxDictCap = 1<<32 - 1

// slotEntries gives the number of entries in one slot of the hash table. If
// slotEntries is larger than 128 the representation of fields a and b in
// slot must be reworked.
const slotEntries = 24

// The minTableExponent give the minimum and maximum for the table exponent.
// The minimum is somehow arbitrary but the maximum is limited by the
// memory requirements of the hash table.
const (
	minTableExponent = 9
	maxTableExponent = 20
)

// newRoller contains the function used to create an instance of the
// hash.Roller.
var newRoller = func(n int) hash.Roller { return hash.NewCyclicPoly(n) }

// slot defines the data structure for a slot in the hash table. The number of
// entries is given by slotEntries constant.
type slot struct {
	entries [slotEntries]uint32
	// start index; bit 7 set if non-empty
	a uint8
	// next entry to overwrite
	b uint8
}

const slotFilled uint8 = 0x80

// start returns the start index of the slot
func (s *slot) start() int {
	return int(s.a &^ slotFilled)
}

// end returns the end index of the slot
func (s *slot) end() int {
	return int(s.b)
}

// empty returns true if nothing is stored in the slot
func (s *slot) empty() bool {
	return s.a&slotFilled == 0
}

// PutEntry puts an entry into a slot.
func (s *slot) PutEntry(u uint32) {
	a, b := s.start(), s.end()
	s.entries[b] = u
	bp1 := (b + 1) % slotEntries
	if a == b && !s.empty() {
		a, b = bp1, bp1
	} else {
		b = bp1
	}
	s.a = slotFilled | uint8(a)
	s.b = uint8(b)
}

// Reset puts the slot back into a pristine condition.
func (s *slot) Reset() {
	s.a, s.b = 0, 0
}

// hashTable stores the hash table including the rolling hash method.
type hashTable struct {
	// actual hash table
	t []slot
	// capacity of the dictionary
	dictCap int
	// exponent used to compute the hash table size
	exp int
	// mask for computing the index for the hash table
	mask uint64
	// capacity of the dictionary
	// hashOffset
	hoff int64
	// length of the hashed word
	wordLen int
	// hash roller for computing the hash values for the Write
	// method
	wr hash.Roller
	// hash roller for computing arbitrary hashes
	hr hash.Roller
}

// hashTableExponent derives the hash table exponent from the history length.
func hashTableExponent(n uint32) int {
	e := 30 - u32.NLZ(n)
	switch {
	case e < minTableExponent:
		e = minTableExponent
	case e > maxTableExponent:
		e = maxTableExponent
	}
	return e
}

// newHashTable creates a new hash table for n-byte sequences.
func newHashTable(dictCap int, n int) (t *hashTable, err error) {
	if dictCap < 1 {
		return nil, errors.New(
			"newHashTable: dictCap must be larger than 1")
	}
	if dictCap > MaxDictCap {
		return nil, errors.New(
			"newHashTable: dictCap exceeds supported maximum")
	}
	exp := hashTableExponent(uint32(dictCap))
	if !(1 <= n && n <= 4) {
		return nil, errors.New("newHashTable: argument n out of range")
	}
	slotLen := 1 << uint(exp)
	if slotLen <= 0 {
		panic("newHashTable: exponent is too large")
	}
	t = &hashTable{
		t:       make([]slot, slotLen),
		dictCap: dictCap,
		exp:     exp,
		mask:    (uint64(1) << uint(exp)) - 1,
		hoff:    -int64(n),
		wordLen: n,
		wr:      newRoller(n),
		hr:      newRoller(n),
	}
	return t, nil
}

// Reset puts hashTable back into a pristine condition.
func (t *hashTable) Reset() {
	for i := range t.t {
		t.t[i].Reset()
	}
	t.hoff = -int64(t.wordLen)
	t.wr = newRoller(t.wordLen)
	t.hr = newRoller(t.wordLen)
}

// putEntry puts an entry into the hash table using the given hash.
func (t *hashTable) putEntry(h uint64, u uint32) {
	t.t[h&t.mask].PutEntry(u)
}

// WriteByte converts a single byte into a hash and puts them into the hash
// table.
func (t *hashTable) WriteByte(b byte) error {
	h := t.wr.RollByte(b)
	t.hoff++
	if t.hoff >= 0 {
		t.putEntry(h, uint32(t.hoff))
	}
	return nil
}

// Write converts the bytes provided into hash tables and stores the
// abbreviated offsets into the hash table. The function will never return an
// error.
func (t *hashTable) Write(p []byte) (n int, err error) {
	for _, b := range p {
		t.WriteByte(b)
	}
	return len(p), nil
}

// dist computes the distance for uint32 value. Note that on a 32-bit
// platform might return negative value.
func (t *hashTable) dist(u uint32) int {
	d := (t.hoff & (1<<32 - 1)) - int64(u)
	if d < 0 {
		d += 1 << 32
	}
	return int(d) + t.wordLen
}

// getMatches puts the distances found for a specific hash into the
// distances array.
func (t *hashTable) getMatches(h uint64) (distances []int) {
	// get the slot for the hash
	s := &t.t[h&t.mask]
	if s.empty() {
		return nil
	}
	distances = make([]int, 0, slotEntries)
	appendDistances := func(p []uint32) {
		for _, u := range p {
			d := t.dist(u)
			if !(0 < d && d <= t.dictCap) {
				continue
			}
			distances = append(distances, d)
		}
	}
	a, b := s.start(), s.end()
	if a >= b {
		appendDistances(s.entries[a:])
		appendDistances(s.entries[:b])
	} else {
		appendDistances(s.entries[a:b])
	}
	return distances
}

// hash computes the rolling hash for p, which must have the same length as
// SliceLen() returns.
func (t *hashTable) hash(p []byte) uint64 {
	var h uint64
	for _, b := range p {
		h = t.hr.RollByte(b)
	}
	return h
}

// Matches returns the distances of potential matches. Those matches
// must be checked. The byte slice p must have word length of the hash
// table.
func (t *hashTable) Matches(p []byte) (distances []int, err error) {
	if len(p) != t.wordLen {
		return nil, fmt.Errorf(
			"Matches: byte slice must have length %d", t.wordLen)
	}
	h := t.hash(p)
	return t.getMatches(h), nil
}
<commit_msg>newlzma: minor fixes to documentation comments in hashtable.go<commit_after>// Copyright 2015 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package newlzma

import (
	"errors"
	"fmt"

	"github.com/uli-go/xz/basics/u32"
	"github.com/uli-go/xz/hash"
)

/* For compression we need to find byte sequences that match the byte
 * sequence at the dictionary head. A hash table is a simple method to
 * provide this capability.
 */

// MaxDictCap defines the maximum capacity for a dictionary. The LZMA
// specification calls the dictionary capacity dictionary size.
const MaxDictCap = 1<<32 - 1

// slotEntries gives the number of entries in one slot of the hash table. If
// slotEntries is larger than 128 the representation of fields a and b in
// slot must be reworked.
const slotEntries = 24

// The minTableExponent give the minimum and maximum for the table exponent.
// The minimum is somehow arbitrary but the maximum is limited by the
// memory requirements of the hash table.
const (
	minTableExponent = 9
	maxTableExponent = 20
)

// newRoller contains the function used to create an instance of the
// hash.Roller.
var newRoller = func(n int) hash.Roller { return hash.NewCyclicPoly(n) }

// slot defines the data structure for a slot in the hash table. The number of
// entries is given by slotEntries constant.
type slot struct {
	entries [slotEntries]uint32
	// start index; bit 7 set if non-empty
	a uint8
	// next entry to overwrite
	b uint8
}

const slotFilled uint8 = 0x80

// start returns the start index of the slot
func (s *slot) start() int {
	return int(s.a &^ slotFilled)
}

// end returns the end index of the slot
func (s *slot) end() int {
	return int(s.b)
}

// empty returns true if nothing is stored in the slot
func (s *slot) empty() bool {
	return s.a&slotFilled == 0
}

// PutEntry puts an entry into a slot.
func (s *slot) PutEntry(u uint32) {
	a, b := s.start(), s.end()
	s.entries[b] = u
	bp1 := (b + 1) % slotEntries
	if a == b && !s.empty() {
		a, b = bp1, bp1
	} else {
		b = bp1
	}
	s.a = slotFilled | uint8(a)
	s.b = uint8(b)
}

// Reset puts the slot back into a pristine condition.
func (s *slot) Reset() {
	s.a, s.b = 0, 0
}

// hashTable stores the hash table including the rolling hash method.
type hashTable struct {
	// actual hash table
	t []slot
	// capacity of the dictionary
	dictCap int
	// exponent used to compute the hash table size
	exp int
	// mask for computing the index for the hash table
	mask uint64
	// hashOffset
	hoff int64
	// length of the hashed word
	wordLen int
	// hash roller for computing the hash values for the Write
	// method
	wr hash.Roller
	// hash roller for computing arbitrary hashes
	hr hash.Roller
}

// hashTableExponent derives the hash table exponent from the history length.
func hashTableExponent(n uint32) int {
	e := 30 - u32.NLZ(n)
	switch {
	case e < minTableExponent:
		e = minTableExponent
	case e > maxTableExponent:
		e = maxTableExponent
	}
	return e
}

// newHashTable creates a new hash table for n-byte sequences.
func newHashTable(dictCap int, n int) (t *hashTable, err error) {
	if dictCap < 1 {
		return nil, errors.New(
			"newHashTable: dictCap must be larger than 1")
	}
	if dictCap > MaxDictCap {
		return nil, errors.New(
			"newHashTable: dictCap exceeds supported maximum")
	}
	exp := hashTableExponent(uint32(dictCap))
	if !(1 <= n && n <= 4) {
		return nil, errors.New("newHashTable: argument n out of range")
	}
	slotLen := 1 << uint(exp)
	if slotLen <= 0 {
		panic("newHashTable: exponent is too large")
	}
	t = &hashTable{
		t:       make([]slot, slotLen),
		dictCap: dictCap,
		exp:     exp,
		mask:    (uint64(1) << uint(exp)) - 1,
		hoff:    -int64(n),
		wordLen: n,
		wr:      newRoller(n),
		hr:      newRoller(n),
	}
	return t, nil
}

// Reset puts hashTable back into a pristine condition.
func (t *hashTable) Reset() {
	for i := range t.t {
		t.t[i].Reset()
	}
	t.hoff = -int64(t.wordLen)
	t.wr = newRoller(t.wordLen)
	t.hr = newRoller(t.wordLen)
}

// putEntry puts an entry into the hash table using the given hash.
func (t *hashTable) putEntry(h uint64, u uint32) {
	t.t[h&t.mask].PutEntry(u)
}

// WriteByte converts a single byte into a hash and puts them into the hash
// table.
func (t *hashTable) WriteByte(b byte) error {
	h := t.wr.RollByte(b)
	t.hoff++
	if t.hoff >= 0 {
		t.putEntry(h, uint32(t.hoff))
	}
	return nil
}

// Write converts the bytes provided into hash tables and stores the
// abbreviated offsets into the hash table. The function will never return an
// error.
func (t *hashTable) Write(p []byte) (n int, err error) {
	for _, b := range p {
		t.WriteByte(b)
	}
	return len(p), nil
}

// dist computes the distance for uint32 value. Note that on a 32-bit
// platform might return negative value.
func (t *hashTable) dist(u uint32) int {
	d := (t.hoff & (1<<32 - 1)) - int64(u)
	if d < 0 {
		d += 1 << 32
	}
	return int(d) + t.wordLen
}

// getMatches puts the distances found for a specific hash into the
// distances array.
func (t *hashTable) getMatches(h uint64) (distances []int) {
	// get the slot for the hash
	s := &t.t[h&t.mask]
	if s.empty() {
		return nil
	}
	distances = make([]int, 0, slotEntries)
	appendDistances := func(p []uint32) {
		for _, u := range p {
			d := t.dist(u)
			if !(0 < d && d <= t.dictCap) {
				continue
			}
			distances = append(distances, d)
		}
	}
	a, b := s.start(), s.end()
	if a >= b {
		appendDistances(s.entries[a:])
		appendDistances(s.entries[:b])
	} else {
		appendDistances(s.entries[a:b])
	}
	return distances
}

// hash computes the rolling hash for the word stored in p. For correct
// results its length must be equal to t.wordLen.
func (t *hashTable) hash(p []byte) uint64 {
	var h uint64
	for _, b := range p {
		h = t.hr.RollByte(b)
	}
	return h
}

// Matches returns the distances of potential matches. Those matches
// must be checked. The byte slice p must have word length of the hash
// table.
func (t *hashTable) Matches(p []byte) (distances []int, err error) {
	if len(p) != t.wordLen {
		return nil, fmt.Errorf(
			"Matches: byte slice must have length %d", t.wordLen)
	}
	h := t.hash(p)
	return t.getMatches(h), nil
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 builder

import (
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"mynewt.apache.org/newt/newt/parse"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)

func (t *TargetBuilder) Load(extraJtagCmd string) error {

	err := t.PrepBuild()

	if err != nil {
		return err
	}

	if t.LoaderBuilder != nil {
		err = t.AppBuilder.Load(1, extraJtagCmd)
		if err == nil {
			err = t.LoaderBuilder.Load(0, extraJtagCmd)
		}
	} else {
		err = t.AppBuilder.Load(0, extraJtagCmd)
	}

	return err
}

func Load(binBaseName string, bspPkg *pkg.BspPackage,
	extraEnvSettings map[string]string) error {

	if bspPkg.DownloadScript == "" {
		return nil
	}

	bspPath := bspPkg.BasePath()

	sortedKeys := make([]string, 0, len(extraEnvSettings))
	for k, _ := range extraEnvSettings {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Strings(sortedKeys)

	env := []string{}
	for _, key := range sortedKeys {
		env = append(env, fmt.Sprintf("%s=%s", key, extraEnvSettings[key]))
	}

	coreRepo := project.GetProject().FindRepo("apache-mynewt-core")
	env = append(env, fmt.Sprintf("CORE_PATH=%s", coreRepo.Path()))
	env = append(env, fmt.Sprintf("BSP_PATH=%s", bspPath))
	env = append(env, fmt.Sprintf("BIN_BASENAME=%s", binBaseName))
	env = append(env, fmt.Sprintf("BIN_ROOT=%s", BinRoot()))

	// bspPath, binBaseName are passed in command line for backwards
	// compatibility
	cmd := []string{
		bspPkg.DownloadScript,
		bspPath,
		binBaseName,
	}

	util.StatusMessage(util.VERBOSITY_VERBOSE, "Load command: %s\n",
		strings.Join(cmd, " "))
	util.StatusMessage(util.VERBOSITY_VERBOSE, "Environment:\n")
	for _, v := range env {
		util.StatusMessage(util.VERBOSITY_VERBOSE, "* %s\n", v)
	}
	if _, err := util.ShellCommand(cmd, env); err != nil {
		return err
	}
	util.StatusMessage(util.VERBOSITY_VERBOSE, "Successfully loaded image.\n")

	return nil
}

func (b *Builder) Load(imageSlot int, extraJtagCmd string) error {
	if b.appPkg == nil {
		return util.NewNewtError("app package not specified")
	}

	/* Populate the package list and feature sets. */
	err := b.targetBuilder.PrepBuild()
	if err != nil {
		return err
	}

	envSettings := map[string]string{
		"IMAGE_SLOT": strconv.Itoa(imageSlot),
		"FEATURES":   b.FeatureString(),
	}
	if extraJtagCmd != "" {
		envSettings["EXTRA_JTAG_CMD"] = extraJtagCmd
	}
	settings := b.cfg.SettingValues()

	var flashTargetArea string
	if parse.ValueIsTrue(settings["BOOT_LOADER"]) {
		envSettings["BOOT_LOADER"] = "1"

		flashTargetArea = "FLASH_AREA_BOOTLOADER"
		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"Loading bootloader\n")
	} else {
		if imageSlot == 0 {
			flashTargetArea = "FLASH_AREA_IMAGE_0"
		} else if imageSlot == 1 {
			flashTargetArea = "FLASH_AREA_IMAGE_1"
		}
		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"Loading %s image into slot %d\n", b.buildName, imageSlot+1)
	}

	bspPkg := b.targetBuilder.bspPkg
	tgtArea := bspPkg.FlashMap.Areas[flashTargetArea]
	if tgtArea.Name == "" {
		return util.NewNewtError(fmt.Sprintf("No flash target area %s\n",
			flashTargetArea))
	}
	envSettings["FLASH_OFFSET"] = "0x" + strconv.FormatInt(int64(tgtArea.Offset), 16)
	envSettings["FLASH_AREA_SIZE"] = "0x" + strconv.FormatInt(int64(tgtArea.Size), 16)

	// Add all syscfg settings to the environment with the MYNEWT_VAL_ prefix.
	for k, v := range settings {
		envSettings["MYNEWT_VAL_"+k] = v
	}

	// Convert the binary path from absolute to relative.  This is required for
	// compatibility with unix-in-windows environemnts (e.g., cygwin).
	binPath := util.TryRelPath(b.AppBinBasePath())
	if err := Load(binPath, b.targetBuilder.bspPkg, envSettings); err != nil {
		return err
	}

	return nil
}

func (t *TargetBuilder) Debug(extraJtagCmd string, reset bool, noGDB bool) error {
	if err := t.PrepBuild(); err != nil {
		return err
	}

	if t.LoaderBuilder == nil {
		return t.AppBuilder.Debug(extraJtagCmd, reset, noGDB)
	}
	return t.LoaderBuilder.Debug(extraJtagCmd, reset, noGDB)
}

func (b *Builder) debugBin(binPath string, extraJtagCmd string, reset bool,
	noGDB bool) error {
	/*
	 * Populate the package list and feature sets.
	 */
	err := b.targetBuilder.PrepBuild()
	if err != nil {
		return err
	}

	bspPath := b.bspPkg.rpkg.Lpkg.BasePath()
	binBaseName := binPath
	featureString := b.FeatureString()

	coreRepo := project.GetProject().FindRepo("apache-mynewt-core")
	envSettings := []string{
		fmt.Sprintf("CORE_PATH=%s", coreRepo.Path()),
		fmt.Sprintf("BSP_PATH=%s", bspPath),
		fmt.Sprintf("BIN_BASENAME=%s", binBaseName),
		fmt.Sprintf("FEATURES=%s", featureString),
	}
	if extraJtagCmd != "" {
		envSettings = append(envSettings,
			fmt.Sprintf("EXTRA_JTAG_CMD=%s", extraJtagCmd))
	}
	if reset == true {
		envSettings = append(envSettings, fmt.Sprintf("RESET=true"))
	}
	if noGDB == true {
		envSettings = append(envSettings, fmt.Sprintf("NO_GDB=1"))
	}

	os.Chdir(project.GetProject().Path())

	// bspPath, binBaseName are passed in command line for backwards
	// compatibility
	cmdLine := []string{
		b.targetBuilder.bspPkg.DebugScript, bspPath, binBaseName,
	}

	fmt.Printf("%s\n", cmdLine)
	return util.ShellInteractiveCommand(cmdLine, envSettings)
}

func (b *Builder) Debug(extraJtagCmd string, reset bool, noGDB bool) error {
	if b.appPkg == nil {
		return util.NewNewtError("app package not specified")
	}

	// Convert the binary path from absolute to relative.  This is required for
	// Windows compatibility.
	binPath := util.TryRelPath(b.AppBinBasePath())
	return b.debugBin(binPath, extraJtagCmd, reset, noGDB)
}
<commit_msg>Run 'newt load' as an interactive shell command<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 builder

import (
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"mynewt.apache.org/newt/newt/parse"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)

func (t *TargetBuilder) Load(extraJtagCmd string) error {

	err := t.PrepBuild()

	if err != nil {
		return err
	}

	if t.LoaderBuilder != nil {
		err = t.AppBuilder.Load(1, extraJtagCmd)
		if err == nil {
			err = t.LoaderBuilder.Load(0, extraJtagCmd)
		}
	} else {
		err = t.AppBuilder.Load(0, extraJtagCmd)
	}

	return err
}

func Load(binBaseName string, bspPkg *pkg.BspPackage,
	extraEnvSettings map[string]string) error {

	if bspPkg.DownloadScript == "" {
		return nil
	}

	bspPath := bspPkg.BasePath()

	sortedKeys := make([]string, 0, len(extraEnvSettings))
	for k, _ := range extraEnvSettings {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Strings(sortedKeys)

	env := []string{}
	for _, key := range sortedKeys {
		env = append(env, fmt.Sprintf("%s=%s", key, extraEnvSettings[key]))
	}

	coreRepo := project.GetProject().FindRepo("apache-mynewt-core")
	env = append(env, fmt.Sprintf("CORE_PATH=%s", coreRepo.Path()))
	env = append(env, fmt.Sprintf("BSP_PATH=%s", bspPath))
	env = append(env, fmt.Sprintf("BIN_BASENAME=%s", binBaseName))
	env = append(env, fmt.Sprintf("BIN_ROOT=%s", BinRoot()))

	// bspPath, binBaseName are passed in command line for backwards
	// compatibility
	cmd := []string{
		bspPkg.DownloadScript,
		bspPath,
		binBaseName,
	}

	util.StatusMessage(util.VERBOSITY_VERBOSE, "Load command: %s\n",
		strings.Join(cmd, " "))
	util.StatusMessage(util.VERBOSITY_VERBOSE, "Environment:\n")
	for _, v := range env {
		util.StatusMessage(util.VERBOSITY_VERBOSE, "* %s\n", v)
	}
	if err := util.ShellInteractiveCommand(cmd, env); err != nil {
		return err
	}
	util.StatusMessage(util.VERBOSITY_VERBOSE, "Successfully loaded image.\n")

	return nil
}

func (b *Builder) Load(imageSlot int, extraJtagCmd string) error {
	if b.appPkg == nil {
		return util.NewNewtError("app package not specified")
	}

	/* Populate the package list and feature sets. */
	err := b.targetBuilder.PrepBuild()
	if err != nil {
		return err
	}

	envSettings := map[string]string{
		"IMAGE_SLOT": strconv.Itoa(imageSlot),
		"FEATURES":   b.FeatureString(),
	}
	if extraJtagCmd != "" {
		envSettings["EXTRA_JTAG_CMD"] = extraJtagCmd
	}
	settings := b.cfg.SettingValues()

	var flashTargetArea string
	if parse.ValueIsTrue(settings["BOOT_LOADER"]) {
		envSettings["BOOT_LOADER"] = "1"

		flashTargetArea = "FLASH_AREA_BOOTLOADER"
		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"Loading bootloader\n")
	} else {
		if imageSlot == 0 {
			flashTargetArea = "FLASH_AREA_IMAGE_0"
		} else if imageSlot == 1 {
			flashTargetArea = "FLASH_AREA_IMAGE_1"
		}
		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"Loading %s image into slot %d\n", b.buildName, imageSlot+1)
	}

	bspPkg := b.targetBuilder.bspPkg
	tgtArea := bspPkg.FlashMap.Areas[flashTargetArea]
	if tgtArea.Name == "" {
		return util.NewNewtError(fmt.Sprintf("No flash target area %s\n",
			flashTargetArea))
	}
	envSettings["FLASH_OFFSET"] = "0x" + strconv.FormatInt(int64(tgtArea.Offset), 16)
	envSettings["FLASH_AREA_SIZE"] = "0x" + strconv.FormatInt(int64(tgtArea.Size), 16)

	// Add all syscfg settings to the environment with the MYNEWT_VAL_ prefix.
	for k, v := range settings {
		envSettings["MYNEWT_VAL_"+k] = v
	}

	// Convert the binary path from absolute to relative.  This is required for
	// compatibility with unix-in-windows environemnts (e.g., cygwin).
	binPath := util.TryRelPath(b.AppBinBasePath())
	if err := Load(binPath, b.targetBuilder.bspPkg, envSettings); err != nil {
		return err
	}

	return nil
}

func (t *TargetBuilder) Debug(extraJtagCmd string, reset bool, noGDB bool) error {
	if err := t.PrepBuild(); err != nil {
		return err
	}

	if t.LoaderBuilder == nil {
		return t.AppBuilder.Debug(extraJtagCmd, reset, noGDB)
	}
	return t.LoaderBuilder.Debug(extraJtagCmd, reset, noGDB)
}

func (b *Builder) debugBin(binPath string, extraJtagCmd string, reset bool,
	noGDB bool) error {
	/*
	 * Populate the package list and feature sets.
	 */
	err := b.targetBuilder.PrepBuild()
	if err != nil {
		return err
	}

	bspPath := b.bspPkg.rpkg.Lpkg.BasePath()
	binBaseName := binPath
	featureString := b.FeatureString()

	coreRepo := project.GetProject().FindRepo("apache-mynewt-core")
	envSettings := []string{
		fmt.Sprintf("CORE_PATH=%s", coreRepo.Path()),
		fmt.Sprintf("BSP_PATH=%s", bspPath),
		fmt.Sprintf("BIN_BASENAME=%s", binBaseName),
		fmt.Sprintf("FEATURES=%s", featureString),
	}
	if extraJtagCmd != "" {
		envSettings = append(envSettings,
			fmt.Sprintf("EXTRA_JTAG_CMD=%s", extraJtagCmd))
	}
	if reset == true {
		envSettings = append(envSettings, fmt.Sprintf("RESET=true"))
	}
	if noGDB == true {
		envSettings = append(envSettings, fmt.Sprintf("NO_GDB=1"))
	}

	os.Chdir(project.GetProject().Path())

	// bspPath, binBaseName are passed in command line for backwards
	// compatibility
	cmdLine := []string{
		b.targetBuilder.bspPkg.DebugScript, bspPath, binBaseName,
	}

	fmt.Printf("%s\n", cmdLine)
	return util.ShellInteractiveCommand(cmdLine, envSettings)
}

func (b *Builder) Debug(extraJtagCmd string, reset bool, noGDB bool) error {
	if b.appPkg == nil {
		return util.NewNewtError("app package not specified")
	}

	// Convert the binary path from absolute to relative.  This is required for
	// Windows compatibility.
	binPath := util.TryRelPath(b.AppBinBasePath())
	return b.debugBin(binPath, extraJtagCmd, reset, noGDB)
}
<|endoftext|>


<commit_before>// +build !confonly

package tls

import (
	"crypto/tls"
	"crypto/x509"
	"strings"
	"sync"
	"time"

	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol/tls/cert"
	"v2ray.com/core/transport/internet"
)

var (
	globalSessionCache = tls.NewLRUClientSessionCache(128)
)

const exp8357 = "experiment:8357"

// ParseCertificate converts a cert.Certificate to Certificate.
func ParseCertificate(c *cert.Certificate) *Certificate {
	certPEM, keyPEM := c.ToPEM()
	return &Certificate{
		Certificate: certPEM,
		Key:         keyPEM,
	}
}

func (c *Config) loadSelfCertPool() (*x509.CertPool, error) {
	root := x509.NewCertPool()
	for _, cert := range c.Certificate {
		if !root.AppendCertsFromPEM(cert.Certificate) {
			return nil, newError("failed to append cert").AtWarning()
		}
	}
	return root, nil
}

// BuildCertificates builds a list of TLS certificates from proto definition.
func (c *Config) BuildCertificates() []tls.Certificate {
	certs := make([]tls.Certificate, 0, len(c.Certificate))
	for _, entry := range c.Certificate {
		if entry.Usage != Certificate_ENCIPHERMENT {
			continue
		}
		keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
		if err != nil {
			newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog()
			continue
		}
		certs = append(certs, keyPair)
	}
	return certs
}

func isCertificateExpired(c *tls.Certificate) bool {
	if c.Leaf == nil && len(c.Certificate) > 0 {
		if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
			c.Leaf = pc
		}
	}

	// If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate.
	return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(-time.Minute))
}

func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) {
	parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key)
	if err != nil {
		return nil, newError("failed to parse raw certificate").Base(err)
	}
	newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain))
	if err != nil {
		return nil, newError("failed to generate new certificate for ", domain).Base(err)
	}
	newCertPEM, newKeyPEM := newCert.ToPEM()
	cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
	return &cert, err
}

func (c *Config) getCustomCA() []*Certificate {
	certs := make([]*Certificate, 0, len(c.Certificate))
	for _, certificate := range c.Certificate {
		if certificate.Usage == Certificate_AUTHORITY_ISSUE {
			certs = append(certs, certificate)
		}
	}
	return certs
}

func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	var access sync.RWMutex

	return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		domain := hello.ServerName
		certExpired := false

		access.RLock()
		certificate, found := c.NameToCertificate[domain]
		access.RUnlock()

		if found {
			if !isCertificateExpired(certificate) {
				return certificate, nil
			}
			certExpired = true
		}

		if certExpired {
			newCerts := make([]tls.Certificate, 0, len(c.Certificates))

			access.Lock()
			for _, certificate := range c.Certificates {
				if !isCertificateExpired(&certificate) {
					newCerts = append(newCerts, certificate)
				}
			}

			c.Certificates = newCerts
			access.Unlock()
		}

		var issuedCertificate *tls.Certificate

		// Create a new certificate from existing CA if possible
		for _, rawCert := range ca {
			if rawCert.Usage == Certificate_AUTHORITY_ISSUE {
				newCert, err := issueCertificate(rawCert, domain)
				if err != nil {
					newError("failed to issue new certificate for ", domain).Base(err).WriteToLog()
					continue
				}

				access.Lock()
				c.Certificates = append(c.Certificates, *newCert)
				issuedCertificate = &c.Certificates[len(c.Certificates)-1]
				access.Unlock()
				break
			}
		}

		if issuedCertificate == nil {
			return nil, newError("failed to create a new certificate for ", domain)
		}

		access.Lock()
		c.BuildNameToCertificate()
		access.Unlock()

		return issuedCertificate, nil
	}
}

func (c *Config) IsExperiment8357() bool {
	return strings.HasPrefix(c.ServerName, exp8357)
}

func (c *Config) parseServerName() string {
	if c.IsExperiment8357() {
		return c.ServerName[len(exp8357):]
	}

	return c.ServerName
}

// GetTLSConfig converts this Config into tls.Config.
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
	root, err := c.getCertPool()
	if err != nil {
		newError("failed to load system root certificate").AtError().Base(err).WriteToLog()
	}

	config := &tls.Config{
		ClientSessionCache:     globalSessionCache,
		RootCAs:                root,
		SessionTicketsDisabled: c.DisableSessionResumption,
	}
	if c == nil {
		return config
	}

	for _, opt := range opts {
		opt(config)
	}

	if !c.AllowInsecureCiphers && len(config.CipherSuites) == 0 {
		// use tls cipher suites from cryto/tls
		config.CipherSuites = []uint16{}
		for _, s := range tls.CipherSuites() {
			config.CipherSuites = append(config.CipherSuites, s.ID)
		}
	}

	config.InsecureSkipVerify = c.AllowInsecure
	config.Certificates = c.BuildCertificates()
	config.BuildNameToCertificate()

	caCerts := c.getCustomCA()
	if len(caCerts) > 0 {
		config.GetCertificate = getGetCertificateFunc(config, caCerts)
	}

	if sn := c.parseServerName(); len(sn) > 0 {
		config.ServerName = sn
	}

	if len(c.NextProtocol) > 0 {
		config.NextProtos = c.NextProtocol
	}
	if len(config.NextProtos) == 0 {
		config.NextProtos = []string{"http/1.1"}
	}

	return config
}

// Option for building TLS config.
type Option func(*tls.Config)

// WithDestination sets the server name in TLS config.
func WithDestination(dest net.Destination) Option {
	return func(config *tls.Config) {
		if dest.Address.Family().IsDomain() && config.ServerName == "" {
			config.ServerName = dest.Address.Domain()
		}
	}
}

// WithNextProto sets the ALPN values in TLS config.
func WithNextProto(protocol ...string) Option {
	return func(config *tls.Config) {
		if len(config.NextProtos) == 0 {
			config.NextProtos = protocol
		}
	}
}

// ConfigFromStreamSettings fetches Config from stream settings. Nil if not found.
func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
	if settings == nil {
		return nil
	}
	config, ok := settings.SecuritySettings.(*Config)
	if !ok {
		return nil
	}
	return config
}
<commit_msg>let crypto/tls choose the proper ciphers<commit_after>// +build !confonly

package tls

import (
	"crypto/tls"
	"crypto/x509"
	"strings"
	"sync"
	"time"

	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol/tls/cert"
	"v2ray.com/core/transport/internet"
)

var (
	globalSessionCache = tls.NewLRUClientSessionCache(128)
)

const exp8357 = "experiment:8357"

// ParseCertificate converts a cert.Certificate to Certificate.
func ParseCertificate(c *cert.Certificate) *Certificate {
	certPEM, keyPEM := c.ToPEM()
	return &Certificate{
		Certificate: certPEM,
		Key:         keyPEM,
	}
}

func (c *Config) loadSelfCertPool() (*x509.CertPool, error) {
	root := x509.NewCertPool()
	for _, cert := range c.Certificate {
		if !root.AppendCertsFromPEM(cert.Certificate) {
			return nil, newError("failed to append cert").AtWarning()
		}
	}
	return root, nil
}

// BuildCertificates builds a list of TLS certificates from proto definition.
func (c *Config) BuildCertificates() []tls.Certificate {
	certs := make([]tls.Certificate, 0, len(c.Certificate))
	for _, entry := range c.Certificate {
		if entry.Usage != Certificate_ENCIPHERMENT {
			continue
		}
		keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
		if err != nil {
			newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog()
			continue
		}
		certs = append(certs, keyPair)
	}
	return certs
}

func isCertificateExpired(c *tls.Certificate) bool {
	if c.Leaf == nil && len(c.Certificate) > 0 {
		if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
			c.Leaf = pc
		}
	}

	// If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate.
	return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(-time.Minute))
}

func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) {
	parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key)
	if err != nil {
		return nil, newError("failed to parse raw certificate").Base(err)
	}
	newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain))
	if err != nil {
		return nil, newError("failed to generate new certificate for ", domain).Base(err)
	}
	newCertPEM, newKeyPEM := newCert.ToPEM()
	cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
	return &cert, err
}

func (c *Config) getCustomCA() []*Certificate {
	certs := make([]*Certificate, 0, len(c.Certificate))
	for _, certificate := range c.Certificate {
		if certificate.Usage == Certificate_AUTHORITY_ISSUE {
			certs = append(certs, certificate)
		}
	}
	return certs
}

func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	var access sync.RWMutex

	return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		domain := hello.ServerName
		certExpired := false

		access.RLock()
		certificate, found := c.NameToCertificate[domain]
		access.RUnlock()

		if found {
			if !isCertificateExpired(certificate) {
				return certificate, nil
			}
			certExpired = true
		}

		if certExpired {
			newCerts := make([]tls.Certificate, 0, len(c.Certificates))

			access.Lock()
			for _, certificate := range c.Certificates {
				if !isCertificateExpired(&certificate) {
					newCerts = append(newCerts, certificate)
				}
			}

			c.Certificates = newCerts
			access.Unlock()
		}

		var issuedCertificate *tls.Certificate

		// Create a new certificate from existing CA if possible
		for _, rawCert := range ca {
			if rawCert.Usage == Certificate_AUTHORITY_ISSUE {
				newCert, err := issueCertificate(rawCert, domain)
				if err != nil {
					newError("failed to issue new certificate for ", domain).Base(err).WriteToLog()
					continue
				}

				access.Lock()
				c.Certificates = append(c.Certificates, *newCert)
				issuedCertificate = &c.Certificates[len(c.Certificates)-1]
				access.Unlock()
				break
			}
		}

		if issuedCertificate == nil {
			return nil, newError("failed to create a new certificate for ", domain)
		}

		access.Lock()
		c.BuildNameToCertificate()
		access.Unlock()

		return issuedCertificate, nil
	}
}

func (c *Config) IsExperiment8357() bool {
	return strings.HasPrefix(c.ServerName, exp8357)
}

func (c *Config) parseServerName() string {
	if c.IsExperiment8357() {
		return c.ServerName[len(exp8357):]
	}

	return c.ServerName
}

// GetTLSConfig converts this Config into tls.Config.
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
	root, err := c.getCertPool()
	if err != nil {
		newError("failed to load system root certificate").AtError().Base(err).WriteToLog()
	}

	config := &tls.Config{
		ClientSessionCache:     globalSessionCache,
		RootCAs:                root,
		SessionTicketsDisabled: c.DisableSessionResumption,
	}
	if c == nil {
		return config
	}

	for _, opt := range opts {
		opt(config)
	}

	if !c.AllowInsecureCiphers && len(config.CipherSuites) == 0 {
		// crypto/tls will use the proper ciphers
		config.CipherSuites = nil
	}

	config.InsecureSkipVerify = c.AllowInsecure
	config.Certificates = c.BuildCertificates()
	config.BuildNameToCertificate()

	caCerts := c.getCustomCA()
	if len(caCerts) > 0 {
		config.GetCertificate = getGetCertificateFunc(config, caCerts)
	}

	if sn := c.parseServerName(); len(sn) > 0 {
		config.ServerName = sn
	}

	if len(c.NextProtocol) > 0 {
		config.NextProtos = c.NextProtocol
	}
	if len(config.NextProtos) == 0 {
		config.NextProtos = []string{"http/1.1"}
	}

	return config
}

// Option for building TLS config.
type Option func(*tls.Config)

// WithDestination sets the server name in TLS config.
func WithDestination(dest net.Destination) Option {
	return func(config *tls.Config) {
		if dest.Address.Family().IsDomain() && config.ServerName == "" {
			config.ServerName = dest.Address.Domain()
		}
	}
}

// WithNextProto sets the ALPN values in TLS config.
func WithNextProto(protocol ...string) Option {
	return func(config *tls.Config) {
		if len(config.NextProtos) == 0 {
			config.NextProtos = protocol
		}
	}
}

// ConfigFromStreamSettings fetches Config from stream settings. Nil if not found.
func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
	if settings == nil {
		return nil
	}
	config, ok := settings.SecuritySettings.(*Config)
	if !ok {
		return nil
	}
	return config
}
<|endoftext|>


<commit_before>package flv

import (
	"bytes"
	"fmt"
	"github.com/metachord/amf.go/amf0"
	"io"
	"os"
)

type Header struct {
	Version uint16
	Body    []byte
}

type Frame interface {
	WriteFrame(io.Writer) error
	GetStream() uint32
	GetDts() uint32
	String() string
}

type CFrame struct {
	Stream      uint32
	Dts         uint32
	Type        TagType
	Flavor      Flavor
	Position    int64
	Body        []byte
	PrevTagSize uint32
}

type VideoFrame struct {
	CFrame
	CodecId VideoCodec
	Width   uint16
	Height  uint16
}

type AudioFrame struct {
	CFrame
	CodecId  AudioCodec
	Rate     uint32
	BitSize  AudioSize
	Channels AudioType
}

type MetaFrame struct {
	CFrame
}

func (f CFrame) WriteFrame(w io.Writer) error {
	bl := uint32(len(f.Body))
	var err error
	err = writeType(w, f.Type)
	if err != nil {
		return err
	}
	err = writeBodyLength(w, bl)
	err = writeDts(w, f.Dts)
	err = writeStream(w, f.Stream)
	err = writeBody(w, f.Body)
	prevTagSize := bl + uint32(TAG_HEADER_LENGTH)
	err = writePrevTagSize(w, prevTagSize)
	return nil
}

func (f CFrame) GetStream() uint32 {
	return f.Stream
}
func (f CFrame) GetDts() uint32 {
	return f.Dts
}

func writeType(w io.Writer, t TagType) error {
	_, err := w.Write([]byte{byte(t)})
	return err
}

func writeBodyLength(w io.Writer, bl uint32) error {
	_, err := w.Write([]byte{byte(bl >> 16), byte((bl >> 8) & 0xFF), byte(bl & 0xFF)})
	return err
}

func writeDts(w io.Writer, dts uint32) error {
	_, err := w.Write([]byte{byte((dts >> 16) & 0xFF), byte((dts >> 8) & 0xFF), byte(dts & 0xFF)})
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{byte((dts >> 24) & 0xFF)})
	return err
}

func writeStream(w io.Writer, stream uint32) error {
	_, err := w.Write([]byte{byte(stream >> 16), byte((stream >> 8) & 0xFF), byte(stream & 0xFF)})
	return err
}

func writeBody(w io.Writer, body []byte) error {
	_, err := w.Write(body)
	return err
}

func writePrevTagSize(w io.Writer, prevTagSize uint32) error {
	_, err := w.Write([]byte{byte((prevTagSize >> 24) & 0xFF), byte((prevTagSize >> 16) & 0xFF), byte((prevTagSize >> 8) & 0xFF), byte(prevTagSize & 0xFF)})
	return err
}

func (f VideoFrame) String() string {
	s := ""
	switch f.Flavor {
	case KEYFRAME:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes} keyframe", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	default:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	}
	return s
}

func (f AudioFrame) String() string {
	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%d,%s,%s}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Rate, f.BitSize, f.Channels)
}

func (f MetaFrame) String() string {
	buf := bytes.NewReader(f.CFrame.Body)
	dec := amf0.NewDecoder(buf)
	evName, err := dec.Decode()
	mds := ""
	if err == nil {
		switch evName {
		case amf0.StringType("onMetaData"):
			md, err := dec.Decode()
			if err == nil {

				var ea map[amf0.StringType]interface{}
				switch md := md.(type) {
				case *amf0.EcmaArrayType:
					ea = *md
				case *amf0.ObjectType:
					ea = *md
				}
				for k, v := range ea {
					mds += fmt.Sprintf("%v=%+v;", k, v)
				}
			}
		}
	}

	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, mds)
}

type FlvReader struct {
	InFile *os.File
	width  uint16
	height uint16
}

func NewReader(inFile *os.File) *FlvReader {
	return &FlvReader{
		InFile: inFile,
		width:  0,
		height: 0,
	}
}

type FlvWriter struct {
	OutFile *os.File
}

func NewWriter(outFile *os.File) *FlvWriter {
	return &FlvWriter{
		OutFile: outFile,
	}
}

func (frReader *FlvReader) ReadHeader() (*Header, error) {
	header := make([]byte, HEADER_LENGTH+4)
	_, err := frReader.InFile.Read(header)
	if err != nil {
		return nil, err
	}

	sig := header[0:3]
	if bytes.Compare(sig, []byte(SIG)) != 0 {
		return nil, fmt.Errorf("bad file format")
	}
	version := (uint16(header[3]) << 8) | (uint16(header[4]) << 0)
	//skip := header[4:5]
	//offset := header[5:9]
	//next_id := header[9:13]

	return &Header{Version: version, Body: header}, nil
}

func (frWriter *FlvWriter) WriteHeader(header *Header) error {
	_, err := frWriter.OutFile.Write(header.Body)
	if err != nil {
		return err
	}
	return nil
}

func (frReader *FlvReader) ReadFrame() (fr Frame, e error) {

	var n int
	var err error

	curPos, _ := frReader.InFile.Seek(0, os.SEEK_CUR)

	tagHeaderB := make([]byte, TAG_HEADER_LENGTH)
	n, err = frReader.InFile.Read(tagHeaderB)
	if n == 0 {
		return nil, nil
	}
	if TagSize(n) != TAG_HEADER_LENGTH {
		return nil, fmt.Errorf("bad tag length: %d", n)
	}
	if err != nil {
		return nil, err
	}

	tagType := TagType(tagHeaderB[0])
	bodyLen := (uint32(tagHeaderB[1]) << 16) | (uint32(tagHeaderB[2]) << 8) | (uint32(tagHeaderB[3]) << 0)
	ts := (uint32(tagHeaderB[4]) << 16) | (uint32(tagHeaderB[5]) << 8) | (uint32(tagHeaderB[6]) << 0)
	tsExt := uint32(tagHeaderB[7])
	stream := (uint32(tagHeaderB[8]) << 16) | (uint32(tagHeaderB[9]) << 8) | (uint32(tagHeaderB[10]) << 0)

	var dts uint32
	dts = (tsExt << 24) | ts

	bodyBuf := make([]byte, bodyLen)
	n, err = frReader.InFile.Read(bodyBuf)
	if err != nil {
		return nil, err
	}

	prevTagSizeB := make([]byte, PREV_TAG_SIZE_LENGTH)
	n, err = frReader.InFile.Read(prevTagSizeB)
	if err != nil {
		return nil, err
	}
	prevTagSize := (uint32(prevTagSizeB[0]) << 24) | (uint32(prevTagSizeB[1]) << 16) | (uint32(prevTagSizeB[2]) << 8) | (uint32(prevTagSizeB[3]) << 0)

	pFrame := CFrame{
		Stream:      stream,
		Dts:         dts,
		Type:        tagType,
		Position:    curPos,
		Body:        bodyBuf,
		PrevTagSize: prevTagSize,
	}

	switch tagType {
	case TAG_TYPE_META:
		pFrame.Flavor = METADATA
		return MetaFrame{CFrame: pFrame}, nil
	case TAG_TYPE_VIDEO:
		resFrame := VideoFrame{CFrame: pFrame}
		if len(bodyBuf) == 0 {
			return resFrame, nil
		}
		vft := VideoFrameType(uint8(bodyBuf[0]) >> 4)
		codecId := VideoCodec(uint8(bodyBuf[0]) & 0x0F)
		switch vft {
		case VIDEO_FRAME_TYPE_KEYFRAME:
			pFrame.Flavor = KEYFRAME
			switch codecId {
			case VIDEO_CODEC_ON2VP6:
				hHelper := (uint16(bodyBuf[1]) >> 4) & 0x0F
				wHelper := uint16(bodyBuf[1]) & 0x0F
				w := uint16(bodyBuf[5])
				h := uint16(bodyBuf[6])

				frReader.width = w*16 - wHelper
				frReader.height = h*16 - hHelper
			}
		default:
			pFrame.Flavor = FRAME
		}
		resFrame.CodecId = codecId
		resFrame.Width = frReader.width
		resFrame.Height = frReader.height
		return resFrame, nil
	case TAG_TYPE_AUDIO:
		pFrame.Flavor = FRAME
		resFrame := AudioFrame{CFrame: pFrame}
		if len(bodyBuf) == 0 {
			return resFrame, nil
		}
		resFrame.CodecId = AudioCodec(uint8(bodyBuf[0]) >> 4)
		resFrame.Rate = audioRate(AudioRate((uint8(bodyBuf[0]) >> 2) & 0x03))
		resFrame.BitSize = AudioSize((uint8(bodyBuf[0]) >> 1) & 0x01)
		resFrame.Channels = AudioType(uint8(bodyBuf[0]) & 0x01)
		return resFrame, nil
	}
	return nil, nil
}

func audioRate(ar AudioRate) uint32 {
	var ret uint32
	switch ar {
	case AUDIO_RATE_5_5:
		ret = 5500
	case AUDIO_RATE_11:
		ret = 11000
	case AUDIO_RATE_22:
		ret = 22000
	case AUDIO_RATE_44:
		ret = 44000
	default:
		ret = 0
	}
	return ret
}

func (frWriter *FlvWriter) WriteFrame(fr Frame) (e error) {
	return fr.WriteFrame(frWriter.OutFile)
}
<commit_msg>Revert "Handle frames with empty body"<commit_after>package flv

import (
	"bytes"
	"fmt"
	"github.com/metachord/amf.go/amf0"
	"io"
	"os"
)

type Header struct {
	Version uint16
	Body    []byte
}

type Frame interface {
	WriteFrame(io.Writer) error
	GetStream() uint32
	GetDts() uint32
	String() string
}

type CFrame struct {
	Stream      uint32
	Dts         uint32
	Type        TagType
	Flavor      Flavor
	Position    int64
	Body        []byte
	PrevTagSize uint32
}

type VideoFrame struct {
	CFrame
	CodecId VideoCodec
	Width   uint16
	Height  uint16
}

type AudioFrame struct {
	CFrame
	CodecId  AudioCodec
	Rate     uint32
	BitSize  AudioSize
	Channels AudioType
}

type MetaFrame struct {
	CFrame
}

func (f CFrame) WriteFrame(w io.Writer) error {
	bl := uint32(len(f.Body))
	var err error
	err = writeType(w, f.Type)
	if err != nil {
		return err
	}
	err = writeBodyLength(w, bl)
	err = writeDts(w, f.Dts)
	err = writeStream(w, f.Stream)
	err = writeBody(w, f.Body)
	prevTagSize := bl + uint32(TAG_HEADER_LENGTH)
	err = writePrevTagSize(w, prevTagSize)
	return nil
}

func (f CFrame) GetStream() uint32 {
	return f.Stream
}
func (f CFrame) GetDts() uint32 {
	return f.Dts
}

func writeType(w io.Writer, t TagType) error {
	_, err := w.Write([]byte{byte(t)})
	return err
}

func writeBodyLength(w io.Writer, bl uint32) error {
	_, err := w.Write([]byte{byte(bl >> 16), byte((bl >> 8) & 0xFF), byte(bl & 0xFF)})
	return err
}

func writeDts(w io.Writer, dts uint32) error {
	_, err := w.Write([]byte{byte((dts >> 16) & 0xFF), byte((dts >> 8) & 0xFF), byte(dts & 0xFF)})
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{byte((dts >> 24) & 0xFF)})
	return err
}

func writeStream(w io.Writer, stream uint32) error {
	_, err := w.Write([]byte{byte(stream >> 16), byte((stream >> 8) & 0xFF), byte(stream & 0xFF)})
	return err
}

func writeBody(w io.Writer, body []byte) error {
	_, err := w.Write(body)
	return err
}

func writePrevTagSize(w io.Writer, prevTagSize uint32) error {
	_, err := w.Write([]byte{byte((prevTagSize >> 24) & 0xFF), byte((prevTagSize >> 16) & 0xFF), byte((prevTagSize >> 8) & 0xFF), byte(prevTagSize & 0xFF)})
	return err
}

func (f VideoFrame) String() string {
	s := ""
	switch f.Flavor {
	case KEYFRAME:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes} keyframe", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	default:
		s = fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%dx%d,%d bytes}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Width, f.Height, len(f.CFrame.Body))
	}
	return s
}

func (f AudioFrame) String() string {
	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s\t{%d,%s,%s}", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, f.CodecId, f.Rate, f.BitSize, f.Channels)
}

func (f MetaFrame) String() string {
	buf := bytes.NewReader(f.CFrame.Body)
	dec := amf0.NewDecoder(buf)
	evName, err := dec.Decode()
	mds := ""
	if err == nil {
		switch evName {
		case amf0.StringType("onMetaData"):
			md, err := dec.Decode()
			if err == nil {

				var ea map[amf0.StringType]interface{}
				switch md := md.(type) {
				case *amf0.EcmaArrayType:
					ea = *md
				case *amf0.ObjectType:
					ea = *md
				}
				for k, v := range ea {
					mds += fmt.Sprintf("%v=%+v;", k, v)
				}
			}
		}
	}

	return fmt.Sprintf("%10d\t%d\t%d\t%s\t%s", f.CFrame.Stream, f.CFrame.Dts, f.CFrame.Position, f.CFrame.Type, mds)
}

type FlvReader struct {
	InFile *os.File
	width  uint16
	height uint16
}

func NewReader(inFile *os.File) *FlvReader {
	return &FlvReader{
		InFile: inFile,
		width:  0,
		height: 0,
	}
}

type FlvWriter struct {
	OutFile *os.File
}

func NewWriter(outFile *os.File) *FlvWriter {
	return &FlvWriter{
		OutFile: outFile,
	}
}

func (frReader *FlvReader) ReadHeader() (*Header, error) {
	header := make([]byte, HEADER_LENGTH+4)
	_, err := frReader.InFile.Read(header)
	if err != nil {
		return nil, err
	}

	sig := header[0:3]
	if bytes.Compare(sig, []byte(SIG)) != 0 {
		return nil, fmt.Errorf("bad file format")
	}
	version := (uint16(header[3]) << 8) | (uint16(header[4]) << 0)
	//skip := header[4:5]
	//offset := header[5:9]
	//next_id := header[9:13]

	return &Header{Version: version, Body: header}, nil
}

func (frWriter *FlvWriter) WriteHeader(header *Header) error {
	_, err := frWriter.OutFile.Write(header.Body)
	if err != nil {
		return err
	}
	return nil
}

func (frReader *FlvReader) ReadFrame() (fr Frame, e error) {

	var n int
	var err error

	curPos, _ := frReader.InFile.Seek(0, os.SEEK_CUR)

	tagHeaderB := make([]byte, TAG_HEADER_LENGTH)
	n, err = frReader.InFile.Read(tagHeaderB)
	if n == 0 {
		return nil, nil
	}
	if TagSize(n) != TAG_HEADER_LENGTH {
		return nil, fmt.Errorf("bad tag length: %d", n)
	}
	if err != nil {
		return nil, err
	}

	tagType := TagType(tagHeaderB[0])
	bodyLen := (uint32(tagHeaderB[1]) << 16) | (uint32(tagHeaderB[2]) << 8) | (uint32(tagHeaderB[3]) << 0)
	ts := (uint32(tagHeaderB[4]) << 16) | (uint32(tagHeaderB[5]) << 8) | (uint32(tagHeaderB[6]) << 0)
	tsExt := uint32(tagHeaderB[7])
	stream := (uint32(tagHeaderB[8]) << 16) | (uint32(tagHeaderB[9]) << 8) | (uint32(tagHeaderB[10]) << 0)

	var dts uint32
	dts = (tsExt << 24) | ts

	bodyBuf := make([]byte, bodyLen)
	n, err = frReader.InFile.Read(bodyBuf)
	if err != nil {
		return nil, err
	}

	prevTagSizeB := make([]byte, PREV_TAG_SIZE_LENGTH)
	n, err = frReader.InFile.Read(prevTagSizeB)
	if err != nil {
		return nil, err
	}
	prevTagSize := (uint32(prevTagSizeB[0]) << 24) | (uint32(prevTagSizeB[1]) << 16) | (uint32(prevTagSizeB[2]) << 8) | (uint32(prevTagSizeB[3]) << 0)

	pFrame := CFrame{
		Stream:      stream,
		Dts:         dts,
		Type:        tagType,
		Position:    curPos,
		Body:        bodyBuf,
		PrevTagSize: prevTagSize,
	}

	var resFrame Frame

	switch tagType {
	case TAG_TYPE_META:
		pFrame.Flavor = METADATA
		resFrame = MetaFrame{pFrame}
	case TAG_TYPE_VIDEO:
		vft := VideoFrameType(uint8(bodyBuf[0]) >> 4)
		codecId := VideoCodec(uint8(bodyBuf[0]) & 0x0F)
		switch vft {
		case VIDEO_FRAME_TYPE_KEYFRAME:
			pFrame.Flavor = KEYFRAME
			switch codecId {
			case VIDEO_CODEC_ON2VP6:
				hHelper := (uint16(bodyBuf[1]) >> 4) & 0x0F
				wHelper := uint16(bodyBuf[1]) & 0x0F
				w := uint16(bodyBuf[5])
				h := uint16(bodyBuf[6])

				frReader.width = w*16 - wHelper
				frReader.height = h*16 - hHelper
			}
		default:
			pFrame.Flavor = FRAME
		}
		resFrame = VideoFrame{CFrame: pFrame, CodecId: codecId, Width: frReader.width, Height: frReader.height}
	case TAG_TYPE_AUDIO:
		pFrame.Flavor = FRAME
		codecId := AudioCodec(uint8(bodyBuf[0]) >> 4)
		rate := audioRate(AudioRate((uint8(bodyBuf[0]) >> 2) & 0x03))
		bitSize := AudioSize((uint8(bodyBuf[0]) >> 1) & 0x01)
		channels := AudioType(uint8(bodyBuf[0]) & 0x01)
		resFrame = AudioFrame{CFrame: pFrame, CodecId: codecId, Rate: rate, BitSize: bitSize, Channels: channels}
	}

	return resFrame, nil
}

func audioRate(ar AudioRate) uint32 {
	var ret uint32
	switch ar {
	case AUDIO_RATE_5_5:
		ret = 5500
	case AUDIO_RATE_11:
		ret = 11000
	case AUDIO_RATE_22:
		ret = 22000
	case AUDIO_RATE_44:
		ret = 44000
	default:
		ret = 0
	}
	return ret
}

func (frWriter *FlvWriter) WriteFrame(fr Frame) (e error) {
	return fr.WriteFrame(frWriter.OutFile)
}
<|endoftext|>


<commit_before>package handlers

import (
	"errors"
	"io"
	"log"
	"net"
	"time"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

// Errors
var Timeout = errors.New("Action timed out.")
var InvalidLength = errors.New("Invalid length header.")

// Handle a client's request
func HandleRequest(conn net.Conn) {
	log.Println("Got a connection.")

	serial, err := sync(conn)
	if err != nil {
		readError(err)
		return
	}

	for {
		log.Println("Reading length header...")
		length_header, err := readBytes(conn, constants.LENGTH_HEADER_SIZE)
		if err != nil {
			readError(err)
			break
		}

		data_length := int(length_header[1])

		// Check that we got a length header
		if length_header[0] != 'L' || data_length == 0 {
			log.Println("Invalid length header.")
			log.Println("Re-syncing...")
			conn.Write([]byte(constants.HEAD))
		} else {
			log.Printf("Length: %d\n", length_header[1])

			// Get the rest of the packet
			data, err := readBytes(conn, data_length-constants.LENGTH_HEADER_SIZE)

			if err != nil {
				readError(err)

				log.Println("Re-syncing...")
				conn.Write([]byte(constants.HEAD))
				break
			}

			log.Println("Read data:")
			log.Println(string(data))

			log.Println("Sending ACK.")
			conn.Write([]byte(constants.ACK))

			log.Println("Sending OKAY.")
			conn.Write([]byte(constants.OKAY))
		}

	}

	conn.Write([]byte("Response"))
	conn.Close()
}

func sync(conn net.Conn) (serial int, err error) {
	log.Println("Sending HEAD...")
	err = writePacket(constants.HEAD)
	if err != nil {
		return
	}

	log.Println("Reading header...")
	data, err := readPacket(conn)
	if err != nil {
		return
	}

	// TODO Parse header to get serial
	serial = 0

	log.Println("Sending configuration...")

	log.Println("Sending OKAY.")
	err = writePacket(constants.OKAY)
	if err != nil {
		return
	}

	return
}

func writePacket(conn net.Conn, data []byte) (err error) {
	write_length, err := conn.Write(data)
	if err != nil {
		return
	}
	if write_length != len(constants.ACK) {
		err = io.ErrShortWrite
		return
	}

	return
}

func readPacket(conn net.Conn) (data []byte, err error) {
	log.Println("Reading length header...")
	length_header, err := readBytes(conn, constants.LENGTH_HEADER_SIZE)
	if err != nil {
		return
	}

	data_length := int(length_header[1])

	// Check that we got a length header
	if length_header[0] != 'L' || data_length == 0 {
		err = InvalidLength
		return
	}

	log.Printf("Length: %d\n", length_header[1])

	// Get the rest of the packet
	data, err := readBytes(conn, data_length-constants.LENGTH_HEADER_SIZE)

	if err != nil {
		return
	}

	log.Println("Read data:")
	log.Println(string(data))

	log.Println("Sending ACK.")
	err = writePacket(conn, []byte(constants.ACK))
	if err != nil {
		return
	}

	return
}

// readError checks the error and prints an appropriate friendly error message.
func readError(err error) {
	if err != io.EOF {
		log.Println("Read error:", err)
	} else {
		log.Println("Done reading bytes.")
	}
}

// readBytes reads the specified number of bytes from the connection with an appropriate time limit.
func readBytes(conn net.Conn, bytes int) (data []byte, err error) {
	// Setup channels
	data_channel := make(chan []byte, 1)
	error_channel := make(chan error, 1)

	// Initiate read in new go routine
	go func() {
		buffer := make([]byte, bytes)
		n, ierr := conn.Read(buffer)
		if ierr != nil {
			error_channel <- ierr
			return
		}
		if bytes != n {
			error_channel <- io.ErrShortWrite
			return
		}
		data_channel <- buffer
	}()

	// Receive result of read
	select {
	case data = <-data_channel:
		// Read resulted in data
	case err = <-error_channel:
		// Read resulted in an error
	case <-time.After(time.Second * constants.READ_TIME_LIMIT):
		// Read timed out
		err = Timeout
	}

	return
}
<commit_msg>Add missing conn argument from writePacket calls.<commit_after>package handlers

import (
	"errors"
	"io"
	"log"
	"net"
	"time"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

// Errors
var Timeout = errors.New("Action timed out.")
var InvalidLength = errors.New("Invalid length header.")

// Handle a client's request
func HandleRequest(conn net.Conn) {
	log.Println("Got a connection.")

	serial, err := sync(conn)
	if err != nil {
		readError(err)
		return
	}

	for {
		log.Println("Reading length header...")
		length_header, err := readBytes(conn, constants.LENGTH_HEADER_SIZE)
		if err != nil {
			readError(err)
			break
		}

		data_length := int(length_header[1])

		// Check that we got a length header
		if length_header[0] != 'L' || data_length == 0 {
			log.Println("Invalid length header.")
			log.Println("Re-syncing...")
			conn.Write([]byte(constants.HEAD))
		} else {
			log.Printf("Length: %d\n", length_header[1])

			// Get the rest of the packet
			data, err := readBytes(conn, data_length-constants.LENGTH_HEADER_SIZE)

			if err != nil {
				readError(err)

				log.Println("Re-syncing...")
				conn.Write([]byte(constants.HEAD))
				break
			}

			log.Println("Read data:")
			log.Println(string(data))

			log.Println("Sending ACK.")
			conn.Write([]byte(constants.ACK))

			log.Println("Sending OKAY.")
			conn.Write([]byte(constants.OKAY))
		}

	}

	conn.Write([]byte("Response"))
	conn.Close()
}

func sync(conn net.Conn) (serial int, err error) {
	log.Println("Sending HEAD...")
	err = writePacket(conn, constants.HEAD)
	if err != nil {
		return
	}

	log.Println("Reading header...")
	data, err := readPacket(conn)
	if err != nil {
		return
	}

	// TODO Parse header to get serial
	serial = 0

	log.Println("Sending configuration...")

	log.Println("Sending OKAY.")
	err = writePacket(conn, constants.OKAY)
	if err != nil {
		return
	}

	return
}

func writePacket(conn net.Conn, data []byte) (err error) {
	write_length, err := conn.Write(data)
	if err != nil {
		return
	}
	if write_length != len(constants.ACK) {
		err = io.ErrShortWrite
		return
	}

	return
}

func readPacket(conn net.Conn) (data []byte, err error) {
	log.Println("Reading length header...")
	length_header, err := readBytes(conn, constants.LENGTH_HEADER_SIZE)
	if err != nil {
		return
	}

	data_length := int(length_header[1])

	// Check that we got a length header
	if length_header[0] != 'L' || data_length == 0 {
		err = InvalidLength
		return
	}

	log.Printf("Length: %d\n", length_header[1])

	// Get the rest of the packet
	data, err := readBytes(conn, data_length-constants.LENGTH_HEADER_SIZE)

	if err != nil {
		return
	}

	log.Println("Read data:")
	log.Println(string(data))

	log.Println("Sending ACK.")
	err = writePacket(conn, []byte(constants.ACK))
	if err != nil {
		return
	}

	return
}

// readError checks the error and prints an appropriate friendly error message.
func readError(err error) {
	if err != io.EOF {
		log.Println("Read error:", err)
	} else {
		log.Println("Done reading bytes.")
	}
}

// readBytes reads the specified number of bytes from the connection with an appropriate time limit.
func readBytes(conn net.Conn, bytes int) (data []byte, err error) {
	// Setup channels
	data_channel := make(chan []byte, 1)
	error_channel := make(chan error, 1)

	// Initiate read in new go routine
	go func() {
		buffer := make([]byte, bytes)
		n, ierr := conn.Read(buffer)
		if ierr != nil {
			error_channel <- ierr
			return
		}
		if bytes != n {
			error_channel <- io.ErrShortWrite
			return
		}
		data_channel <- buffer
	}()

	// Receive result of read
	select {
	case data = <-data_channel:
		// Read resulted in data
	case err = <-error_channel:
		// Read resulted in an error
	case <-time.After(time.Second * constants.READ_TIME_LIMIT):
		// Read timed out
		err = Timeout
	}

	return
}
<|endoftext|>