<commit_before>package azurerm

import (
	"fmt"
	"net/http"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmLocalNetworkGateway() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmLocalNetworkGatewayCreate,
		Read:   resourceArmLocalNetworkGatewayRead,
		Update: resourceArmLocalNetworkGatewayCreate,
		Delete: resourceArmLocalNetworkGatewayDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"gateway_address": {
				Type:     schema.TypeString,
				Required: true,
			},

			"address_space": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
		},
	}
}

func resourceArmLocalNetworkGatewayCreate(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	ipAddress := d.Get("gateway_address").(string)

	// fetch the 'address_space_prefixes:
	prefixes := []string{}
	for _, pref := range d.Get("address_space").([]interface{}) {
		prefixes = append(prefixes, pref.(string))
	}

	gateway := network.LocalNetworkGateway{
		Name:     &name,
		Location: &location,
		LocalNetworkGatewayPropertiesFormat: &network.LocalNetworkGatewayPropertiesFormat{
			LocalNetworkAddressSpace: &network.AddressSpace{
				AddressPrefixes: &prefixes,
			},
			GatewayIPAddress: &ipAddress,
		},
	}

	_, err := lnetClient.CreateOrUpdate(resGroup, name, gateway, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error creating Azure ARM Local Network Gateway '%s': %s", name, err)
	}

	read, err := lnetClient.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmLocalNetworkGatewayRead(d, meta)
}

// resourceArmLocalNetworkGatewayRead goes ahead and reads the state of the corresponding ARM local network gateway.
func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	if name == "" {
		var pathString, sp string
		for key, value := range id.Path {
			pathString += fmt.Sprintf("%s'%s:%s'", sp, key, value)
			sp = ", "
		}
		return fmt.Errorf("Cannot find 'localNetworkGateways' in [%s], make sure it is specified in the ID parameter", pathString)
	}
	resGroup := id.ResourceGroup

	resp, err := lnetClient.Get(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading the state of Azure ARM local network gateway '%s': %s", name, err)
	}

	d.Set("resource_group_name", resGroup)
	d.Set("name", resp.Name)
	d.Set("location", resp.Location)
	d.Set("gateway_address", resp.LocalNetworkGatewayPropertiesFormat.GatewayIPAddress)

	prefs := []string{}
	if ps := *resp.LocalNetworkGatewayPropertiesFormat.LocalNetworkAddressSpace.AddressPrefixes; ps != nil {
		prefs = ps
	}
	d.Set("address_space", prefs)

	return nil
}

// resourceArmLocalNetworkGatewayDelete deletes the specified ARM local network gateway.
func resourceArmLocalNetworkGatewayDelete(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	resGroup := id.ResourceGroup

	_, err = lnetClient.Delete(resGroup, name, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error issuing Azure ARM delete request of local network gateway '%s': %s", name, err)
	}

	return nil
}
<commit_msg>Revert "Better error message for LNG import error."<commit_after>package azurerm

import (
	"fmt"
	"net/http"

	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmLocalNetworkGateway() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmLocalNetworkGatewayCreate,
		Read:   resourceArmLocalNetworkGatewayRead,
		Update: resourceArmLocalNetworkGatewayCreate,
		Delete: resourceArmLocalNetworkGatewayDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": locationSchema(),

			"resource_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"gateway_address": {
				Type:     schema.TypeString,
				Required: true,
			},

			"address_space": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
		},
	}
}

func resourceArmLocalNetworkGatewayCreate(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	name := d.Get("name").(string)
	location := d.Get("location").(string)
	resGroup := d.Get("resource_group_name").(string)
	ipAddress := d.Get("gateway_address").(string)

	// fetch the 'address_space_prefixes:
	prefixes := []string{}
	for _, pref := range d.Get("address_space").([]interface{}) {
		prefixes = append(prefixes, pref.(string))
	}

	gateway := network.LocalNetworkGateway{
		Name:     &name,
		Location: &location,
		LocalNetworkGatewayPropertiesFormat: &network.LocalNetworkGatewayPropertiesFormat{
			LocalNetworkAddressSpace: &network.AddressSpace{
				AddressPrefixes: &prefixes,
			},
			GatewayIPAddress: &ipAddress,
		},
	}

	_, err := lnetClient.CreateOrUpdate(resGroup, name, gateway, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error creating Azure ARM Local Network Gateway '%s': %s", name, err)
	}

	read, err := lnetClient.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	return resourceArmLocalNetworkGatewayRead(d, meta)
}

// resourceArmLocalNetworkGatewayRead goes ahead and reads the state of the corresponding ARM local network gateway.
func resourceArmLocalNetworkGatewayRead(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	if name == "" {
		return fmt.Errorf("Cannot find parameter 'localNetworkGateways' from '%s'", id.Path)
	}
	resGroup := id.ResourceGroup

	resp, err := lnetClient.Get(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading the state of Azure ARM local network gateway '%s': %s", name, err)
	}

	d.Set("resource_group_name", resGroup)
	d.Set("name", resp.Name)
	d.Set("location", resp.Location)
	d.Set("gateway_address", resp.LocalNetworkGatewayPropertiesFormat.GatewayIPAddress)

	prefs := []string{}
	if ps := *resp.LocalNetworkGatewayPropertiesFormat.LocalNetworkAddressSpace.AddressPrefixes; ps != nil {
		prefs = ps
	}
	d.Set("address_space", prefs)

	return nil
}

// resourceArmLocalNetworkGatewayDelete deletes the specified ARM local network gateway.
func resourceArmLocalNetworkGatewayDelete(d *schema.ResourceData, meta interface{}) error {
	lnetClient := meta.(*ArmClient).localNetConnClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["localNetworkGateways"]
	resGroup := id.ResourceGroup

	_, err = lnetClient.Delete(resGroup, name, make(chan struct{}))
	if err != nil {
		return fmt.Errorf("Error issuing Azure ARM delete request of local network gateway '%s': %s", name, err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package bookshelf

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"

	"github.com/go-sql-driver/mysql"
)

var createTableStatements = []string{
	`CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET = 'utf8' DEFAULT COLLATE 'utf8_general_ci';`,
	`USE library;`,
	`CREATE TABLE IF NOT EXISTS books (
		id INT UNSIGNED NOT NULL AUTO_INCREMENT,
		title VARCHAR(255) NULL,
		author VARCHAR(255) NULL,
		publishedDate VARCHAR(255) NULL,
		imageUrl VARCHAR(255) NULL,
		description TEXT NULL,
		createdBy VARCHAR(255) NULL,
		createdById VARCHAR(255) NULL,
		PRIMARY KEY (id)
	)`,
}

// mysqlDB persists books to a MySQL instance.
type mysqlDB struct {
	conn *sql.DB

	list   *sql.Stmt
	listBy *sql.Stmt
	insert *sql.Stmt
	get    *sql.Stmt
	update *sql.Stmt
	delete *sql.Stmt
}

// Ensure mysqlDB conforms to the BookDatabase interface.
var _ BookDatabase = &mysqlDB{}

type MySQLConfig struct {
	// Optional.
	Username, Password string

	// Host of the MySQL instance.
	//
	// If set, UnixSocket should be unset.
	Host string

	// Port of the MySQL instance.
	//
	// If set, UnixSocket should be unset.
	Port int

	// UnixSocket is the filepath to a unix socket.
	//
	// If set, Host and Port should be unset.
	UnixSocket string
}

// dataStoreName returns a connection string suitable for sql.Open.
func (c MySQLConfig) dataStoreName(databaseName string) string {
	var cred string
	// [username[:password]@]
	if c.Username != "" {
		cred = c.Username
		if c.Password != "" {
			cred = cred + ":" + c.Password
		}
		cred = cred + "@"
	}

	if c.UnixSocket != "" {
		return fmt.Sprintf("%sunix(%s)/%s", cred, c.UnixSocket, databaseName)
	}
	return fmt.Sprintf("%stcp([%s]:%d)/%s", cred, c.Host, c.Port, databaseName)
}

// newMySQLDB creates a new BookDatabase backed by a given MySQL server.
func newMySQLDB(config MySQLConfig) (BookDatabase, error) {
	// Check database and table exists. If not, create it.
	if err := config.ensureTableExists(); err != nil {
		return nil, err
	}

	conn, err := sql.Open("mysql", config.dataStoreName("library"))
	if err != nil {
		return nil, fmt.Errorf("mysql: could not get a connection: %v", err)
	}
	if err := conn.Ping(); err != nil {
		conn.Close()
		return nil, fmt.Errorf("mysql: could not establish a good connection: %v", err)
	}

	db := &mysqlDB{
		conn: conn,
	}

	// Prepared statements. The actual SQL queries are in the code near the
	// relevant method (e.g. addBook).
	if db.list, err = conn.Prepare(listStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare list: %v", err)
	}
	if db.listBy, err = conn.Prepare(listByStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare listBy: %v", err)
	}
	if db.get, err = conn.Prepare(getStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare get: %v", err)
	}
	if db.insert, err = conn.Prepare(insertStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare insert: %v", err)
	}
	if db.update, err = conn.Prepare(updateStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare update: %v", err)
	}
	if db.delete, err = conn.Prepare(deleteStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare delete: %v", err)
	}

	return db, nil
}

// Close closes the database, freeing up any resources.
func (db *mysqlDB) Close() {
	db.conn.Close()
}

// rowScanner is implemented by sql.Row and sql.Rows
type rowScanner interface {
	Scan(dest ...interface{}) error
}

// scanBook reads a book from a sql.Row or sql.Rows
func scanBook(s rowScanner) (*Book, error) {
	var (
		id            int64
		title         sql.NullString
		author        sql.NullString
		publishedDate sql.NullString
		imageURL      sql.NullString
		description   sql.NullString
		createdBy     sql.NullString
		createdByID   sql.NullString
	)
	if err := s.Scan(&id, &title, &author, &publishedDate, &imageURL,
		&description, &createdBy, &createdByID); err != nil {
		return nil, err
	}

	book := &Book{
		ID:            id,
		Title:         title.String,
		Author:        author.String,
		PublishedDate: publishedDate.String,
		ImageURL:      imageURL.String,
		Description:   description.String,
		CreatedBy:     createdBy.String,
		CreatedByID:   createdByID.String,
	}
	return book, nil
}

const listStatement = `SELECT * FROM books ORDER BY title`

// ListBooks returns a list of books, ordered by title.
func (db *mysqlDB) ListBooks() ([]*Book, error) {
	rows, err := db.list.Query()
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var books []*Book
	for rows.Next() {
		book, err := scanBook(rows)
		if err != nil {
			return nil, fmt.Errorf("mysql: could not read row: %v", err)
		}

		books = append(books, book)
	}

	return books, nil
}

const listByStatement = `
  SELECT * FROM books
  WHERE createdById = ? ORDER BY title`

// ListBooksCreatedBy returns a list of books, ordered by title, filtered by
// the user who created the book entry.
func (db *mysqlDB) ListBooksCreatedBy(userID string) ([]*Book, error) {
	if userID == "" {
		return db.ListBooks()
	}

	rows, err := db.listBy.Query(userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var books []*Book
	for rows.Next() {
		book, err := scanBook(rows)
		if err != nil {
			return nil, fmt.Errorf("mysql: could not read row: %v", err)
		}

		books = append(books, book)
	}

	return books, nil
}

const getStatement = "SELECT * FROM books WHERE id = ?"

// GetBook retrieves a book by its ID.
func (db *mysqlDB) GetBook(id int64) (*Book, error) {
	book, err := scanBook(db.get.QueryRow(id))
	if err == sql.ErrNoRows {
		return nil, fmt.Errorf("mysql: could not find book with id %d", id)
	}
	if err != nil {
		return nil, fmt.Errorf("mysql: could not get book: %v", err)
	}
	return book, nil
}

const insertStatement = `
  INSERT INTO books (
    title, author, publishedDate, imageUrl, description, createdBy, createdById
  ) VALUES (?, ?, ?, ?, ?, ?, ?)`

// AddBook saves a given book, assigning it a new ID.
func (db *mysqlDB) AddBook(b *Book) (id int64, err error) {
	r, err := execAffectingOneRow(db.insert, b.Title, b.Author, b.PublishedDate,
		b.ImageURL, b.Description, b.CreatedBy, b.CreatedByID)
	if err != nil {
		return 0, err
	}

	lastInsertID, err := r.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("mysql: could not get last insert ID: %v", err)
	}
	return lastInsertID, nil
}

const deleteStatement = `DELETE FROM books WHERE id = ?`

// DeleteBook removes a given book by its ID.
func (db *mysqlDB) DeleteBook(id int64) error {
	if id == 0 {
		return errors.New("memorydb: book with unassigned ID passed into deleteBook")
	}
	_, err := execAffectingOneRow(db.delete, id)
	return err
}

const updateStatement = `
  UPDATE books
  SET title=?, author=?, publishedDate=?, imageUrl=?, description=?,
      createdBy=?, createdById=?
  WHERE id = ?`

// UpdateBook updates the entry for a given book.
func (db *mysqlDB) UpdateBook(b *Book) error {
	if b.ID == 0 {
		return errors.New("memorydb: book with unassigned ID passed into updateBook")
	}

	_, err := execAffectingOneRow(db.update, b.Title, b.Author, b.PublishedDate,
		b.ImageURL, b.Description, b.CreatedBy, b.CreatedByID, b.ID)
	return err
}

// ensureTableExists checks the table exists. If not, it creates it.
func (config MySQLConfig) ensureTableExists() error {
	conn, err := sql.Open("mysql", config.dataStoreName(""))
	if err != nil {
		return fmt.Errorf("mysql: could not get a connection: %v", err)
	}
	defer conn.Close()

	// Check the connection.
	if conn.Ping() == driver.ErrBadConn {
		return fmt.Errorf("mysql: could not connect to the database. " +
			"could be bad address, or this address is not whitelisted for access.")
	}

	if _, err := conn.Exec("USE library"); err != nil {
		// MySQL error 1049 is "database does not exist"
		if mErr, ok := err.(*mysql.MySQLError); ok && mErr.Number == 1049 {
			return createTable(conn)
		}
	}

	if _, err := conn.Exec("DESCRIBE books"); err != nil {
		// MySQL error 1146 is "table does not exist"
		if mErr, ok := err.(*mysql.MySQLError); ok && mErr.Number == 1146 {
			return createTable(conn)
		}
		// Unknown error.
		return fmt.Errorf("mysql: could not connect to the database: %v", err)
	}
	return nil
}

// createTable creates the table, and if necessary, the database.
func createTable(conn *sql.DB) error {
	for _, stmt := range createTableStatements {
		_, err := conn.Exec(stmt)
		if err != nil {
			return err
		}
	}
	return nil
}

// execAffectingOneRow executes a given statement, expecting one row to be affected.
func execAffectingOneRow(stmt *sql.Stmt, args ...interface{}) (sql.Result, error) {
	r, err := stmt.Exec(args...)
	if err != nil {
		return r, fmt.Errorf("mysql: could not execute statement: %v", err)
	}
	rowsAffected, err := r.RowsAffected()
	if err != nil {
		return r, fmt.Errorf("mysql: could not get rows affected: %v", err)
	} else if rowsAffected != 1 {
		return r, fmt.Errorf("mysql: expected 1 row affected, got %d", rowsAffected)
	}
	return r, nil
}
<commit_msg>getting-started/bookshelf: fix error message for mysql db (#304)<commit_after>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package bookshelf

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"

	"github.com/go-sql-driver/mysql"
)

var createTableStatements = []string{
	`CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET = 'utf8' DEFAULT COLLATE 'utf8_general_ci';`,
	`USE library;`,
	`CREATE TABLE IF NOT EXISTS books (
		id INT UNSIGNED NOT NULL AUTO_INCREMENT,
		title VARCHAR(255) NULL,
		author VARCHAR(255) NULL,
		publishedDate VARCHAR(255) NULL,
		imageUrl VARCHAR(255) NULL,
		description TEXT NULL,
		createdBy VARCHAR(255) NULL,
		createdById VARCHAR(255) NULL,
		PRIMARY KEY (id)
	)`,
}

// mysqlDB persists books to a MySQL instance.
type mysqlDB struct {
	conn *sql.DB

	list   *sql.Stmt
	listBy *sql.Stmt
	insert *sql.Stmt
	get    *sql.Stmt
	update *sql.Stmt
	delete *sql.Stmt
}

// Ensure mysqlDB conforms to the BookDatabase interface.
var _ BookDatabase = &mysqlDB{}

type MySQLConfig struct {
	// Optional.
	Username, Password string

	// Host of the MySQL instance.
	//
	// If set, UnixSocket should be unset.
	Host string

	// Port of the MySQL instance.
	//
	// If set, UnixSocket should be unset.
	Port int

	// UnixSocket is the filepath to a unix socket.
	//
	// If set, Host and Port should be unset.
	UnixSocket string
}

// dataStoreName returns a connection string suitable for sql.Open.
func (c MySQLConfig) dataStoreName(databaseName string) string {
	var cred string
	// [username[:password]@]
	if c.Username != "" {
		cred = c.Username
		if c.Password != "" {
			cred = cred + ":" + c.Password
		}
		cred = cred + "@"
	}

	if c.UnixSocket != "" {
		return fmt.Sprintf("%sunix(%s)/%s", cred, c.UnixSocket, databaseName)
	}
	return fmt.Sprintf("%stcp([%s]:%d)/%s", cred, c.Host, c.Port, databaseName)
}

// newMySQLDB creates a new BookDatabase backed by a given MySQL server.
func newMySQLDB(config MySQLConfig) (BookDatabase, error) {
	// Check database and table exists. If not, create it.
	if err := config.ensureTableExists(); err != nil {
		return nil, err
	}

	conn, err := sql.Open("mysql", config.dataStoreName("library"))
	if err != nil {
		return nil, fmt.Errorf("mysql: could not get a connection: %v", err)
	}
	if err := conn.Ping(); err != nil {
		conn.Close()
		return nil, fmt.Errorf("mysql: could not establish a good connection: %v", err)
	}

	db := &mysqlDB{
		conn: conn,
	}

	// Prepared statements. The actual SQL queries are in the code near the
	// relevant method (e.g. addBook).
	if db.list, err = conn.Prepare(listStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare list: %v", err)
	}
	if db.listBy, err = conn.Prepare(listByStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare listBy: %v", err)
	}
	if db.get, err = conn.Prepare(getStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare get: %v", err)
	}
	if db.insert, err = conn.Prepare(insertStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare insert: %v", err)
	}
	if db.update, err = conn.Prepare(updateStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare update: %v", err)
	}
	if db.delete, err = conn.Prepare(deleteStatement); err != nil {
		return nil, fmt.Errorf("mysql: prepare delete: %v", err)
	}

	return db, nil
}

// Close closes the database, freeing up any resources.
func (db *mysqlDB) Close() {
	db.conn.Close()
}

// rowScanner is implemented by sql.Row and sql.Rows
type rowScanner interface {
	Scan(dest ...interface{}) error
}

// scanBook reads a book from a sql.Row or sql.Rows
func scanBook(s rowScanner) (*Book, error) {
	var (
		id            int64
		title         sql.NullString
		author        sql.NullString
		publishedDate sql.NullString
		imageURL      sql.NullString
		description   sql.NullString
		createdBy     sql.NullString
		createdByID   sql.NullString
	)
	if err := s.Scan(&id, &title, &author, &publishedDate, &imageURL,
		&description, &createdBy, &createdByID); err != nil {
		return nil, err
	}

	book := &Book{
		ID:            id,
		Title:         title.String,
		Author:        author.String,
		PublishedDate: publishedDate.String,
		ImageURL:      imageURL.String,
		Description:   description.String,
		CreatedBy:     createdBy.String,
		CreatedByID:   createdByID.String,
	}
	return book, nil
}

const listStatement = `SELECT * FROM books ORDER BY title`

// ListBooks returns a list of books, ordered by title.
func (db *mysqlDB) ListBooks() ([]*Book, error) {
	rows, err := db.list.Query()
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var books []*Book
	for rows.Next() {
		book, err := scanBook(rows)
		if err != nil {
			return nil, fmt.Errorf("mysql: could not read row: %v", err)
		}

		books = append(books, book)
	}

	return books, nil
}

const listByStatement = `
  SELECT * FROM books
  WHERE createdById = ? ORDER BY title`

// ListBooksCreatedBy returns a list of books, ordered by title, filtered by
// the user who created the book entry.
func (db *mysqlDB) ListBooksCreatedBy(userID string) ([]*Book, error) {
	if userID == "" {
		return db.ListBooks()
	}

	rows, err := db.listBy.Query(userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var books []*Book
	for rows.Next() {
		book, err := scanBook(rows)
		if err != nil {
			return nil, fmt.Errorf("mysql: could not read row: %v", err)
		}

		books = append(books, book)
	}

	return books, nil
}

const getStatement = "SELECT * FROM books WHERE id = ?"

// GetBook retrieves a book by its ID.
func (db *mysqlDB) GetBook(id int64) (*Book, error) {
	book, err := scanBook(db.get.QueryRow(id))
	if err == sql.ErrNoRows {
		return nil, fmt.Errorf("mysql: could not find book with id %d", id)
	}
	if err != nil {
		return nil, fmt.Errorf("mysql: could not get book: %v", err)
	}
	return book, nil
}

const insertStatement = `
  INSERT INTO books (
    title, author, publishedDate, imageUrl, description, createdBy, createdById
  ) VALUES (?, ?, ?, ?, ?, ?, ?)`

// AddBook saves a given book, assigning it a new ID.
func (db *mysqlDB) AddBook(b *Book) (id int64, err error) {
	r, err := execAffectingOneRow(db.insert, b.Title, b.Author, b.PublishedDate,
		b.ImageURL, b.Description, b.CreatedBy, b.CreatedByID)
	if err != nil {
		return 0, err
	}

	lastInsertID, err := r.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("mysql: could not get last insert ID: %v", err)
	}
	return lastInsertID, nil
}

const deleteStatement = `DELETE FROM books WHERE id = ?`

// DeleteBook removes a given book by its ID.
func (db *mysqlDB) DeleteBook(id int64) error {
	if id == 0 {
		return errors.New("mysql: book with unassigned ID passed into deleteBook")
	}
	_, err := execAffectingOneRow(db.delete, id)
	return err
}

const updateStatement = `
  UPDATE books
  SET title=?, author=?, publishedDate=?, imageUrl=?, description=?,
      createdBy=?, createdById=?
  WHERE id = ?`

// UpdateBook updates the entry for a given book.
func (db *mysqlDB) UpdateBook(b *Book) error {
	if b.ID == 0 {
		return errors.New("mysql: book with unassigned ID passed into updateBook")
	}

	_, err := execAffectingOneRow(db.update, b.Title, b.Author, b.PublishedDate,
		b.ImageURL, b.Description, b.CreatedBy, b.CreatedByID, b.ID)
	return err
}

// ensureTableExists checks the table exists. If not, it creates it.
func (config MySQLConfig) ensureTableExists() error {
	conn, err := sql.Open("mysql", config.dataStoreName(""))
	if err != nil {
		return fmt.Errorf("mysql: could not get a connection: %v", err)
	}
	defer conn.Close()

	// Check the connection.
	if conn.Ping() == driver.ErrBadConn {
		return fmt.Errorf("mysql: could not connect to the database. " +
			"could be bad address, or this address is not whitelisted for access.")
	}

	if _, err := conn.Exec("USE library"); err != nil {
		// MySQL error 1049 is "database does not exist"
		if mErr, ok := err.(*mysql.MySQLError); ok && mErr.Number == 1049 {
			return createTable(conn)
		}
	}

	if _, err := conn.Exec("DESCRIBE books"); err != nil {
		// MySQL error 1146 is "table does not exist"
		if mErr, ok := err.(*mysql.MySQLError); ok && mErr.Number == 1146 {
			return createTable(conn)
		}
		// Unknown error.
		return fmt.Errorf("mysql: could not connect to the database: %v", err)
	}
	return nil
}

// createTable creates the table, and if necessary, the database.
func createTable(conn *sql.DB) error {
	for _, stmt := range createTableStatements {
		_, err := conn.Exec(stmt)
		if err != nil {
			return err
		}
	}
	return nil
}

// execAffectingOneRow executes a given statement, expecting one row to be affected.
func execAffectingOneRow(stmt *sql.Stmt, args ...interface{}) (sql.Result, error) {
	r, err := stmt.Exec(args...)
	if err != nil {
		return r, fmt.Errorf("mysql: could not execute statement: %v", err)
	}
	rowsAffected, err := r.RowsAffected()
	if err != nil {
		return r, fmt.Errorf("mysql: could not get rows affected: %v", err)
	} else if rowsAffected != 1 {
		return r, fmt.Errorf("mysql: expected 1 row affected, got %d", rowsAffected)
	}
	return r, nil
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	log "github.com/Sirupsen/logrus"
	"github.com/astaxie/beego"
	"github.com/cosminrentea/franz/models"
	"strconv"
)

type MessageController struct {
	beego.Controller
}

// Example:
//
//   req: GET /message/
//   res: 200 {"Messages": [
//          {"ID": 1, "Title": "Learn Go", "Done": false},
//          {"ID": 2, "Title": "Buy bread", "Done": true}
//        ]}
func (this *MessageController) ListMessages() {
	log.Debug("ListMessages")
	res := struct{
		Messages []*models.Message
	}{
		models.DefaultMessageList.All(),
	}
	this.Data["json"] = res
	this.ServeJSON()
}

// Examples:
//
//   req: POST /message/ {"Title": ""}
//   res: 400 empty title
//
//   req: POST /message/ {"Title": "Buy bread from us now!"}
//   res: 200
func (this *MessageController) NewMessage() {
	log.Debug("NewMessage")
	req := struct{ Title string }{}
	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
		this.outputError(400, "empty")
		return
	}
	t, err := models.NewMessage(req.Title)
	if err != nil {
		this.outputError(400, err.Error())
		return
	}
	log.Debug("NewMessage send")
	err = models.DefaultMessageList.Send(t)
	if err != nil {
		this.outputError(400, err.Error())
		return
	}
	log.Debug("NewMessage save")
	models.DefaultMessageList.Save(t)
}

func (this *MessageController) outputError(status int, msg string) {
	this.Ctx.Output.SetStatus(status)
	this.Ctx.Output.Body([]byte(msg))
}

// Examples:
//
//   req: GET /message/1
//   res: 200 {"ID": 1, "Title": "Buy bread", "Done": true}
//
//   req: GET /message/42
//   res: 404 message not found
func (this *MessageController) GetMessage() {
	id := this.Ctx.Input.Param(":id")
	log.WithField("id", id).Debug("GetMessage")
	intid, _ := strconv.ParseInt(id, 10, 64)
	t, found := models.DefaultMessageList.Find(intid)
	log.WithField("id", id).WithField("found", found).Debug("GetMessage")
	if !found {
		this.Ctx.Output.SetStatus(404)
		this.Ctx.Output.Body([]byte("message not found"))
		return
	}
	this.Data["json"] = t
	this.ServeJSON()
}

// Example:
//
//   req: PUT /message/1 {"ID": 1, "Title": "Learn Go", "Done": true}
//   res: 200
//
//   req: PUT /message/2 {"ID": 2, "Title": "Learn Go", "Done": true}
//   res: 400 inconsistent message IDs
func (this *MessageController) UpdateMessage() {
	id := this.Ctx.Input.Param(":id")
	log.WithField("id", id).Debug("UpdateMessage")
	intid, _ := strconv.ParseInt(id, 10, 64)
	var t models.Message
	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &t); err != nil {
		this.Ctx.Output.SetStatus(400)
		this.Ctx.Output.Body([]byte(err.Error()))
		return
	}
	if t.ID != intid {
		this.Ctx.Output.SetStatus(400)
		this.Ctx.Output.Body([]byte("inconsistent message IDs"))
		return
	}
	if _, ok := models.DefaultMessageList.Find(intid); !ok {
		this.Ctx.Output.SetStatus(400)
		this.Ctx.Output.Body([]byte("message not found"))
		return
	}
	models.DefaultMessageList.Save(&t)
}
<commit_msg>minor refactoring<commit_after>package controllers

import (
	"encoding/json"
	log "github.com/Sirupsen/logrus"
	"github.com/astaxie/beego"
	"github.com/cosminrentea/franz/models"
	"strconv"
)

type MessageController struct {
	beego.Controller
}

// Example:
//
//   req: GET /message/
//   res: 200 {"Messages": [
//          {"ID": 1, "Title": "Learn Go", "Done": false},
//          {"ID": 2, "Title": "Buy bread", "Done": true}
//        ]}
func (this *MessageController) ListMessages() {
	log.Debug("ListMessages")
	res := struct{
		Messages []*models.Message
	}{
		models.DefaultMessageList.All(),
	}
	this.Data["json"] = res
	this.ServeJSON()
}

// Examples:
//
//   req: POST /message/ {"Title": ""}
//   res: 400 empty title
//
//   req: POST /message/ {"Title": "Buy bread from us now!"}
//   res: 200
func (this *MessageController) NewMessage() {
	log.Debug("NewMessage")
	req := struct{ Title string }{}
	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
		this.output(400, "empty")
		return
	}
	t, err := models.NewMessage(req.Title)
	if err != nil {
		this.output(400, err.Error())
		return
	}
	log.Debug("NewMessage send")
	err = models.DefaultMessageList.Send(t)
	if err != nil {
		this.output(400, err.Error())
		return
	}
	log.Debug("NewMessage save")
	models.DefaultMessageList.Save(t)
}

func (this *MessageController) output(status int, msg string) {
	this.Ctx.Output.SetStatus(status)
	this.Ctx.Output.Body([]byte(msg))
}

// Examples:
//
//   req: GET /message/1
//   res: 200 {"ID": 1, "Title": "Buy bread", "Done": true}
//
//   req: GET /message/42
//   res: 404 message not found
func (this *MessageController) GetMessage() {
	id := this.Ctx.Input.Param(":id")
	log.WithField("id", id).Debug("GetMessage")
	intid, _ := strconv.ParseInt(id, 10, 64)
	t, found := models.DefaultMessageList.Find(intid)
	log.WithField("id", id).WithField("found", found).Debug("GetMessage")
	if !found {
		this.Ctx.Output.SetStatus(404)
		this.Ctx.Output.Body([]byte("message not found"))
		return
	}
	this.Data["json"] = t
	this.ServeJSON()
}

// Example:
//
//   req: PUT /message/1 {"ID": 1, "Title": "Learn Go", "Done": true}
//   res: 200
//
//   req: PUT /message/2 {"ID": 2, "Title": "Learn Go", "Done": true}
//   res: 400 inconsistent message IDs
func (this *MessageController) UpdateMessage() {
	id := this.Ctx.Input.Param(":id")
	log.WithField("id", id).Debug("UpdateMessage")
	intid, _ := strconv.ParseInt(id, 10, 64)
	var t models.Message
	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &t); err != nil {
		this.Ctx.Output.SetStatus(400)
		this.Ctx.Output.Body([]byte(err.Error()))
		return
	}
	if t.ID != intid {
		this.Ctx.Output.SetStatus(400)
		this.Ctx.Output.Body([]byte("inconsistent message IDs"))
		return
	}
	if _, ok := models.DefaultMessageList.Find(intid); !ok {
		this.Ctx.Output.SetStatus(400)
		this.Ctx.Output.Body([]byte("message not found"))
		return
	}
	models.DefaultMessageList.Save(&t)
}
<|endoftext|>


<commit_before>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec chan *LogRecord
	rot chan bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate   bool
	keep_old int
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	w.rec <- rec
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:      make(chan *LogRecord, LogBufferLength),
		rot:      make(chan bool),
		filename: fname,
		format:   "[%D %T] [%L] (%S) %M",
		rotate:   rotate,
	}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
					(w.daily && now.Day() != w.daily_opendate) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			// num := 1
			// fname := ""
			// for ; err == nil && num <= 999; num++ {
			// 	fname = w.filename + fmt.Sprintf(".%03d", num)
			// 	_, err = os.Lstat(fname)
			// }
			// // return error if the last file checked still existed
			// if err == nil {
			// 	return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			// }

			fname := w.filename + time.Now().Format(".20060102-15:04:05.oldlog")

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}

		// TODO 通过w.filename拿到所在目录，去那里查找
		if w.keep_old > 0 {
			log_dir := filepath.Dir(w.filename)

			oldlogs, err := ListLogs(log_dir, ".oldlog")
			if err == nil {
				overed := len(oldlogs) - w.keep_old
				if overed > 0 {
					for _, _log := range oldlogs[0:overed] {
						remove_old_log(_log)
					}
				}
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

func (w *FileLogWriter) SetRotateKeep(keep int) *FileLogWriter {
	w.keep_old = keep

	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}

func ListLogs(dirPth string, suffix string) (files []string, err error) {
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}

	sort.Slice(dir, func(i, j int) bool {
		if dir[i].ModTime().Before(dir[j].ModTime()) {
			return true
		} else {
			return false
		}
	})

	files = make([]string, 0, len(dir))

	for _, fi := range dir {
		if fi.IsDir() || !strings.HasSuffix(fi.Name(), suffix) {
			continue
		}

		files = append(files, fi.Name())
	}

	return
}

func remove_old_log(log string) {
	os.Remove(log)
}
<commit_msg>删除todo<commit_after>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec chan *LogRecord
	rot chan bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate   bool
	keep_old int
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	w.rec <- rec
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:      make(chan *LogRecord, LogBufferLength),
		rot:      make(chan bool),
		filename: fname,
		format:   "[%D %T] [%L] (%S) %M",
		rotate:   rotate,
	}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
					(w.daily && now.Day() != w.daily_opendate) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			// num := 1
			// fname := ""
			// for ; err == nil && num <= 999; num++ {
			// 	fname = w.filename + fmt.Sprintf(".%03d", num)
			// 	_, err = os.Lstat(fname)
			// }
			// // return error if the last file checked still existed
			// if err == nil {
			// 	return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			// }

			fname := w.filename + time.Now().Format(".20060102-15:04:05.oldlog")

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}

		if w.keep_old > 0 {
			log_dir := filepath.Dir(w.filename)

			oldlogs, err := ListLogs(log_dir, ".oldlog")
			if err == nil {
				overed := len(oldlogs) - w.keep_old
				if overed > 0 {
					for _, _log := range oldlogs[0:overed] {
						remove_old_log(_log)
					}
				}
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

func (w *FileLogWriter) SetRotateKeep(keep int) *FileLogWriter {
	w.keep_old = keep

	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}

func ListLogs(dirPth string, suffix string) (files []string, err error) {
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}

	sort.Slice(dir, func(i, j int) bool {
		if dir[i].ModTime().Before(dir[j].ModTime()) {
			return true
		} else {
			return false
		}
	})

	files = make([]string, 0, len(dir))

	for _, fi := range dir {
		if fi.IsDir() || !strings.HasSuffix(fi.Name(), suffix) {
			continue
		}

		files = append(files, fi.Name())
	}

	return
}

func remove_old_log(log string) {
	os.Remove(log)
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/

package resize

import (
	"image"
	"image/color"
	"math"
)

// color.RGBA64 as array
type rgba16 [4]uint16

// build RGBA from an arbitrary color
func toRGBA(c color.Color) rgba16 {
	r, g, b, a := c.RGBA()
	return rgba16{uint16(r), uint16(g), uint16(b), uint16(a)}
}

func clampToUint16(x float32) (y uint16) {
	y = uint16(x)
	if x < 0 {
		y = 0
	} else if x > float32(0xfffe) {
		y = 0xffff
	}
	return
}

type filterModel struct {
	src     image.Image
	size    int
	kernel  func(float32) float32
	tempRow []rgba16
	tempCol []rgba16
}

func (f *filterModel) convolution1d(x float32, p []rgba16) (c rgba16) {
	x -= float32(int(x))
	m := float32(f.size/2 - 1)

	var k float32
	var sum float32 = 0
	l := [4]float32{0.0, 0.0, 0.0, 0.0}

	for j := range p {
		k = f.kernel(x + m - float32(j))
		sum += k
		for i := range c {
			l[i] += float32(p[j][i]) * k
		}
	}
	for i := range c {
		c[i] = clampToUint16(l[i] / sum)
	}
	return
}

func (f *filterModel) Interpolate(x, y float32) color.RGBA64 {
	xf, yf := int(x)-f.size/2+1, int(y)-f.size/2+1

	for i := 0; i < f.size; i++ {
		for j := 0; j < f.size; j++ {
			f.tempRow[j] = toRGBA(f.src.At(xf+j, yf+i))
		}
		f.tempCol[i] = f.convolution1d(x, f.tempRow)
	}

	c := f.convolution1d(y, f.tempCol)
	return color.RGBA64{c[0], c[1], c[2], c[3]}
}

// Nearest-neighbor interpolation.
// Approximates a value by returning the value of the nearest point.
func NearestNeighbor(img image.Image) Filter {
	return &filterModel{img, 2, func(x float32) (y float32) {
		if x >= -0.5 && x < 0.5 {
			y = 1
		} else {
			y = 0
		}
		return
	}, make([]rgba16, 2), make([]rgba16, 2)}
}

// Bicubic interpolation
func Bilinear(img image.Image) Filter {
	return &filterModel{img, 2, func(x float32) float32 {
		return 1 - float32(math.Abs(float64(x)))
	}, make([]rgba16, 2), make([]rgba16, 2)}
}

// Bicubic interpolation
func Bicubic(img image.Image) Filter {
	return &filterModel{img, 4, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = absX*absX*(1.5*absX-2.5) + 1
		} else {
			y = absX*(absX*(2.5-0.5*absX)-4) + 2
		}
		return
	}, make([]rgba16, 4), make([]rgba16, 4)}
}

// Lanczos interpolation (a=2).
func Lanczos2(img image.Image) Filter {
	return &filterModel{img, 4, func(x float32) float32 {
		return float32(Sinc(float64(x))) * float32(Sinc(float64((x)/float32(2))))
	}, make([]rgba16, 4), make([]rgba16, 4)}
}

// Lanczos interpolation (a=3).
func Lanczos3(img image.Image) Filter {
	return &filterModel{img, 6, func(x float32) float32 {
		return float32(Sinc(float64(x))) * float32(Sinc(float64((x)/float32(3))))
	}, make([]rgba16, 6), make([]rgba16, 6)}
}
<commit_msg>Small simplification<commit_after>/*
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/

package resize

import (
	"image"
	"image/color"
	"math"
)

// color.RGBA64 as array
type rgba16 [4]uint16

// build rgba16 from an arbitrary color
func toRgba16(c color.Color) rgba16 {
	r, g, b, a := c.RGBA()
	return rgba16{uint16(r), uint16(g), uint16(b), uint16(a)}
}

func clampToUint16(x float32) (y uint16) {
	y = uint16(x)
	if x < 0 {
		y = 0
	} else if x > float32(0xfffe) {
		y = 0xffff
	}
	return
}

type filterModel struct {
	src     image.Image
	size    int
	kernel  func(float32) float32
	tempRow []rgba16
	tempCol []rgba16
}

func (f *filterModel) convolution1d(x float32, p []rgba16) (c rgba16) {
	var k float32
	var sum float32 = 0
	l := [4]float32{0.0, 0.0, 0.0, 0.0}

	for j := range p {
		k = f.kernel(x - float32(j))
		sum += k
		for i := range c {
			l[i] += float32(p[j][i]) * k
		}
	}
	for i := range c {
		c[i] = clampToUint16(l[i] / sum)
	}
	return
}

func (f *filterModel) Interpolate(x, y float32) color.RGBA64 {
	xf, yf := int(x)-f.size/2+1, int(y)-f.size/2+1
	x -= float32(xf)
	y -= float32(yf)

	for i := 0; i < f.size; i++ {
		for j := 0; j < f.size; j++ {
			f.tempRow[j] = toRgba16(f.src.At(xf+j, yf+i))
		}
		f.tempCol[i] = f.convolution1d(x, f.tempRow)
	}

	c := f.convolution1d(y, f.tempCol)
	return color.RGBA64{c[0], c[1], c[2], c[3]}
}

// Nearest-neighbor interpolation
func NearestNeighbor(img image.Image) Filter {
	return &filterModel{img, 2, func(x float32) (y float32) {
		if x >= -0.5 && x < 0.5 {
			y = 1
		} else {
			y = 0
		}
		return
	}, make([]rgba16, 2), make([]rgba16, 2)}
}

// Bilinear interpolation
func Bilinear(img image.Image) Filter {
	return &filterModel{img, 2, func(x float32) float32 {
		return 1 - float32(math.Abs(float64(x)))
	}, make([]rgba16, 2), make([]rgba16, 2)}
}

// Bicubic interpolation (with cubic hermite spline)
func Bicubic(img image.Image) Filter {
	return &filterModel{img, 4, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = absX*absX*(1.5*absX-2.5) + 1
		} else {
			y = absX*(absX*(2.5-0.5*absX)-4) + 2
		}
		return
	}, make([]rgba16, 4), make([]rgba16, 4)}
}

// Lanczos interpolation (a=2).
func Lanczos2(img image.Image) Filter {
	return &filterModel{img, 4, func(x float32) float32 {
		return float32(Sinc(float64(x))) * float32(Sinc(float64((x)/float32(2))))
	}, make([]rgba16, 4), make([]rgba16, 4)}
}

// Lanczos interpolation (a=3).
func Lanczos3(img image.Image) Filter {
	return &filterModel{img, 6, func(x float32) float32 {
		return float32(Sinc(float64(x))) * float32(Sinc(float64((x)/float32(3))))
	}, make([]rgba16, 6), make([]rgba16, 6)}
}
<|endoftext|>


<commit_before>package sudoku

import (
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		defer grid.Done()
		grid.Load(TEST_GRID)
		grid.HumanSolve()
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	steps := grid.HumanSolution()

	if steps == nil {
		t.Log("Human solution returned 0 techniques.")
		t.Fail()
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}

}

func TestHint(t *testing.T) {
	//TODO: explicitly test hints that end in a guess, too.
	hintTestHelper(t)
}

func hintTestHelper(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()

	grid.Load(TEST_GRID)

	steps := grid.Hint()

	if steps == nil || len(steps) == 0 {
		t.Error("No steps returned from Hint")
	}

	for count, step := range steps {
		if count == len(steps)-1 {
			//Last one
			if !step.Technique.IsFill() {
				t.Error("Non-fill step as last step in Hint: ", step.Technique.Name())
			}
		} else {
			//Not last one
			if step.Technique.IsFill() {
				t.Error("Fill step as non-last step in Hint: ", count, step.Technique.Name())
			}
		}
	}
}

func TestHumanSolveWithGuess(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	if !grid.LoadFromFile(puzzlePath("harddifficulty.sdk")) {
		t.Fatal("harddifficulty.sdk wasn't loaded")
	}

	steps := grid.HumanSolution()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		&SolveStep{
			techniquesByName["Only Legal Number"],
			CellSlice{
				grid.Cell(0, 0),
			},
			IntSlice{1},
			nil,
			nil,
			nil,
		},
		&SolveStep{
			techniquesByName["Pointing Pair Col"],
			CellSlice{
				grid.Cell(1, 0),
				grid.Cell(1, 1),
			},
			IntSlice{1, 2},
			CellSlice{
				grid.Cell(1, 3),
				grid.Cell(1, 4),
			},
			nil,
			nil,
		},
		&SolveStep{
			techniquesByName["Only Legal Number"],
			CellSlice{
				grid.Cell(2, 0),
			},
			IntSlice{2},
			nil,
			nil,
			nil,
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, we put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Next, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block.",
		"Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i])
			t.Fail()
		}
	}
}

//TODO: this is useful. Should we use this in other tests?
func cellRefsToCells(refs []cellRef, grid *Grid) CellSlice {
	var result CellSlice
	for _, ref := range refs {
		result = append(result, ref.Cell(grid))
	}
	return result
}

func TestTweakChainedStepsWeights(t *testing.T) {

	//TODO: test other, harder cases as well.
	grid := NewGrid()
	lastStep := &SolveStep{
		nil,
		cellRefsToCells([]cellRef{
			{0, 0},
		}, grid),
		nil,
		nil,
		nil,
		nil,
	}
	possibilities := []*SolveStep{
		{
			nil,
			cellRefsToCells([]cellRef{
				{1, 0},
			}, grid),
			nil,
			nil,
			nil,
			nil,
		},
		{
			nil,
			cellRefsToCells([]cellRef{
				{2, 2},
			}, grid),
			nil,
			nil,
			nil,
			nil,
		},
		{
			nil,
			cellRefsToCells([]cellRef{
				{7, 7},
			}, grid),
			nil,
			nil,
			nil,
			nil,
		},
	}
	weights := []float64{
		10.0,
		10.0,
		10.0,
	}

	tweakChainedStepsWeights(lastStep, possibilities, weights)

	lastWeight := 0.0
	for i, weight := range weights {
		if weight <= lastWeight {
			t.Error("Tweak Chained Steps Weights didn't tweak things in the right direction: ", weights, "at", i)
		}
		lastWeight = weight
	}
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	//We use the cheaper one for testing so it completes faster.
	difficulty := grid.calcluateDifficulty(false)

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 60)

	done := make(chan bool)

	go func() {
		//We use the cheaper one for testing so it completes faster
		_ = otherGrid.calcluateDifficulty(false)
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<commit_msg>Took a note on how we'll go about testing the Guess bit.<commit_after>package sudoku

import (
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		defer grid.Done()
		grid.Load(TEST_GRID)
		grid.HumanSolve()
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	steps := grid.HumanSolution()

	if steps == nil {
		t.Log("Human solution returned 0 techniques.")
		t.Fail()
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve()
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}

}

func TestHint(t *testing.T) {
	//TODO: explicitly test hints that end in a guess, too. I think the best way to do that
	//is once HumanSolve and its variants take a HumanSolveOptions struct, and have a
	//packagePrivate onlyGuessFirstRecursion field to use just for testing.

	hintTestHelper(t)
}

func hintTestHelper(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()

	grid.Load(TEST_GRID)

	steps := grid.Hint()

	if steps == nil || len(steps) == 0 {
		t.Error("No steps returned from Hint")
	}

	for count, step := range steps {
		if count == len(steps)-1 {
			//Last one
			if !step.Technique.IsFill() {
				t.Error("Non-fill step as last step in Hint: ", step.Technique.Name())
			}
		} else {
			//Not last one
			if step.Technique.IsFill() {
				t.Error("Fill step as non-last step in Hint: ", count, step.Technique.Name())
			}
		}
	}
}

func TestHumanSolveWithGuess(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	if !grid.LoadFromFile(puzzlePath("harddifficulty.sdk")) {
		t.Fatal("harddifficulty.sdk wasn't loaded")
	}

	steps := grid.HumanSolution()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		&SolveStep{
			techniquesByName["Only Legal Number"],
			CellSlice{
				grid.Cell(0, 0),
			},
			IntSlice{1},
			nil,
			nil,
			nil,
		},
		&SolveStep{
			techniquesByName["Pointing Pair Col"],
			CellSlice{
				grid.Cell(1, 0),
				grid.Cell(1, 1),
			},
			IntSlice{1, 2},
			CellSlice{
				grid.Cell(1, 3),
				grid.Cell(1, 4),
			},
			nil,
			nil,
		},
		&SolveStep{
			techniquesByName["Only Legal Number"],
			CellSlice{
				grid.Cell(2, 0),
			},
			IntSlice{2},
			nil,
			nil,
			nil,
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, we put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Next, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block.",
		"Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i])
			t.Fail()
		}
	}
}

//TODO: this is useful. Should we use this in other tests?
func cellRefsToCells(refs []cellRef, grid *Grid) CellSlice {
	var result CellSlice
	for _, ref := range refs {
		result = append(result, ref.Cell(grid))
	}
	return result
}

func TestTweakChainedStepsWeights(t *testing.T) {

	//TODO: test other, harder cases as well.
	grid := NewGrid()
	lastStep := &SolveStep{
		nil,
		cellRefsToCells([]cellRef{
			{0, 0},
		}, grid),
		nil,
		nil,
		nil,
		nil,
	}
	possibilities := []*SolveStep{
		{
			nil,
			cellRefsToCells([]cellRef{
				{1, 0},
			}, grid),
			nil,
			nil,
			nil,
			nil,
		},
		{
			nil,
			cellRefsToCells([]cellRef{
				{2, 2},
			}, grid),
			nil,
			nil,
			nil,
			nil,
		},
		{
			nil,
			cellRefsToCells([]cellRef{
				{7, 7},
			}, grid),
			nil,
			nil,
			nil,
			nil,
		},
	}
	weights := []float64{
		10.0,
		10.0,
		10.0,
	}

	tweakChainedStepsWeights(lastStep, possibilities, weights)

	lastWeight := 0.0
	for i, weight := range weights {
		if weight <= lastWeight {
			t.Error("Tweak Chained Steps Weights didn't tweak things in the right direction: ", weights, "at", i)
		}
		lastWeight = weight
	}
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.Load(TEST_GRID)

	//We use the cheaper one for testing so it completes faster.
	difficulty := grid.calcluateDifficulty(false)

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 60)

	done := make(chan bool)

	go func() {
		//We use the cheaper one for testing so it completes faster
		_ = otherGrid.calcluateDifficulty(false)
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 CloudAwan LLC
//
// 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 identity

import (
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	"github.com/cloudawan/cloudone_gui/controllers/utility/guimessagedisplay"
	"github.com/cloudawan/cloudone_utility/audit"
	"github.com/cloudawan/cloudone_utility/rbac"
	"github.com/cloudawan/cloudone_utility/restclient"
)

const (
	loginPageURL  = "/gui/login"
	logoutPageURL = "/gui/logout"
)

func FilterUser(ctx *context.Context) {
	if (ctx.Input.IsGet() || ctx.Input.IsPost()) && (ctx.Input.URL() == loginPageURL || ctx.Input.URL() == logoutPageURL) {
		// Don't redirect itself to prevent the circle
	} else {
		user, ok := ctx.Input.Session("user").(*rbac.User)

		if ok == false {
			if guiMessage := guimessagedisplay.GetGUIMessageFromContext(ctx); guiMessage != nil {
				guiMessage.AddDanger("Username or password is incorrect")
			}
			ctx.Redirect(302, loginPageURL)
		} else {
			// Authorize
			if user.HasPermission(componentName, ctx.Input.Method(), ctx.Input.URL()) == false {
				if guiMessage := guimessagedisplay.GetGUIMessageFromContext(ctx); guiMessage != nil {
					guiMessage.AddDanger("User is not authorized to this page. Please use another user with priviledge.")
				}
				ctx.Redirect(302, loginPageURL)
			}

			// Resource check is in another place since GUI doesn't place the resource name in url

			// Audit log
			go func() {
				sendAuditLog(ctx, user.Name, true)
			}()
		}
	}
}

func sendAuditLog(ctx *context.Context, userName string, saveParameter bool) (returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			returnedError = err.(error)
		}
	}()

	cloudoneAnalysisProtocol := beego.AppConfig.String("cloudoneAnalysisProtocol")
	cloudoneAnalysisHost := beego.AppConfig.String("cloudoneAnalysisHost")
	cloudoneAnalysisPort := beego.AppConfig.String("cloudoneAnalysisPort")

	tokenHeaderMap, tokenHeaderMapOK := ctx.Input.Session("tokenHeaderMap").(map[string]string)
	requestURI := ctx.Input.URI()
	method := ctx.Input.Method()
	path := ctx.Input.URL()
	remoteAddress := ctx.Request.RemoteAddr
	queryParameterMap := ctx.Request.Form

	if saveParameter == false {
		// Not to save parameter, such as password
		requestURI = path
		queryParameterMap = nil
	}

	// Header is not used since the header has no useful information for now
	// Body is not used since the backend component will record again.
	// Path is not used since the backend component will record again.
	auditLog := audit.CreateAuditLog(componentName, path, userName, remoteAddress, queryParameterMap, nil, method, requestURI, "", nil)

	if tokenHeaderMapOK {
		url := cloudoneAnalysisProtocol + "://" + cloudoneAnalysisHost + ":" + cloudoneAnalysisPort + "/api/v1/auditlogs"

		_, err := restclient.RequestPost(url, auditLog, tokenHeaderMap, false)
		if err != nil {
			if guiMessage := guimessagedisplay.GetGUIMessageFromContext(ctx); guiMessage != nil {
				guiMessage.AddDanger("Fail to send audit log with error " + err.Error())
			}
			return err
		}
	}

	return nil
}
<commit_msg>Add proxy ip to client ip<commit_after>// Copyright 2015 CloudAwan LLC
//
// 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 identity

import (
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	"github.com/cloudawan/cloudone_gui/controllers/utility/guimessagedisplay"
	"github.com/cloudawan/cloudone_utility/audit"
	"github.com/cloudawan/cloudone_utility/rbac"
	"github.com/cloudawan/cloudone_utility/restclient"
)

const (
	loginPageURL  = "/gui/login"
	logoutPageURL = "/gui/logout"
)

func FilterUser(ctx *context.Context) {
	if (ctx.Input.IsGet() || ctx.Input.IsPost()) && (ctx.Input.URL() == loginPageURL || ctx.Input.URL() == logoutPageURL) {
		// Don't redirect itself to prevent the circle
	} else {
		user, ok := ctx.Input.Session("user").(*rbac.User)

		if ok == false {
			if guiMessage := guimessagedisplay.GetGUIMessageFromContext(ctx); guiMessage != nil {
				guiMessage.AddDanger("Username or password is incorrect")
			}
			ctx.Redirect(302, loginPageURL)
		} else {
			// Authorize
			if user.HasPermission(componentName, ctx.Input.Method(), ctx.Input.URL()) == false {
				if guiMessage := guimessagedisplay.GetGUIMessageFromContext(ctx); guiMessage != nil {
					guiMessage.AddDanger("User is not authorized to this page. Please use another user with priviledge.")
				}
				ctx.Redirect(302, loginPageURL)
			}

			// Resource check is in another place since GUI doesn't place the resource name in url

			// Audit log
			go func() {
				sendAuditLog(ctx, user.Name, true)
			}()
		}
	}
}

func sendAuditLog(ctx *context.Context, userName string, saveParameter bool) (returnedError error) {
	defer func() {
		if err := recover(); err != nil {
			returnedError = err.(error)
		}
	}()

	cloudoneAnalysisProtocol := beego.AppConfig.String("cloudoneAnalysisProtocol")
	cloudoneAnalysisHost := beego.AppConfig.String("cloudoneAnalysisHost")
	cloudoneAnalysisPort := beego.AppConfig.String("cloudoneAnalysisPort")

	tokenHeaderMap, tokenHeaderMapOK := ctx.Input.Session("tokenHeaderMap").(map[string]string)
	requestURI := ctx.Input.URI()
	method := ctx.Input.Method()
	path := ctx.Input.URL()
	remoteAddress := ctx.Request.RemoteAddr
	queryParameterMap := ctx.Request.Form

	proxySlice := ctx.Input.Proxy()
	if proxySlice != nil && len(proxySlice) > 0 {
		proxySlice = append(proxySlice, remoteAddress)
		remoteAddress = fmt.Sprintf("%v", proxySlice)
	}

	if saveParameter == false {
		// Not to save parameter, such as password
		requestURI = path
		queryParameterMap = nil
	}

	// Header is not used since the header has no useful information for now
	// Body is not used since the backend component will record again.
	// Path is not used since the backend component will record again.
	auditLog := audit.CreateAuditLog(componentName, path, userName, remoteAddress, queryParameterMap, nil, method, requestURI, "", nil)

	if tokenHeaderMapOK {
		url := cloudoneAnalysisProtocol + "://" + cloudoneAnalysisHost + ":" + cloudoneAnalysisPort + "/api/v1/auditlogs"

		_, err := restclient.RequestPost(url, auditLog, tokenHeaderMap, false)
		if err != nil {
			if guiMessage := guimessagedisplay.GetGUIMessageFromContext(ctx); guiMessage != nil {
				guiMessage.AddDanger("Fail to send audit log with error " + err.Error())
			}
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package sockjs

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

type Service struct {
	Websocket    bool
	CookieNeeded bool
	Timeout      time.Duration
	StreamLimit  int
	Callback     func(receiveChan <-chan interface{}, sendChan chan<- interface{})

	iFrameContent      []byte
	iFrameETag         string
	sessions           map[string]*session
	sessionsMutex      sync.Mutex
	lastSessionCleanup time.Time
}

func NewService(jsFileUrl string, websocket, cookieNeeded bool, timeout time.Duration, streamLimit int, callback func(receiveChan <-chan interface{}, sendChan chan<- interface{})) *Service {
	iFrameContent := createIFrameContent(jsFileUrl)
	hash := md5.New()
	hash.Write(iFrameContent)
	iFrameETag := "\"" + hex.EncodeToString(hash.Sum(nil)) + "\""

	if streamLimit <= 0 {
		streamLimit = 128 * 1024
	}

	return &Service{
		Websocket:    websocket,
		CookieNeeded: cookieNeeded,
		Timeout:      timeout,
		StreamLimit:  streamLimit,
		Callback:     callback,

		iFrameContent:      iFrameContent,
		iFrameETag:         iFrameETag,
		sessions:           make(map[string]*session),
		sessionsMutex:      sync.Mutex{},
		lastSessionCleanup: time.Now(),
	}
}

func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println(err)
			debug.PrintStack()
		}
	}()

	path := r.URL.Path

	if r.Header.Get("Origin") != "" && r.Header.Get("Origin") != "null" {
		w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
	} else {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	w.Header().Set("Access-Control-Allow-Credentials", "true")
	h := r.Header.Get("Access-Control-Request-Headers")
	if h != "" {
		w.Header().Set("Access-Control-Allow-Headers", h)
	}

	if strings.HasPrefix(path, "/iframe") && strings.HasSuffix(path, ".html") && !strings.ContainsRune(path[1:], '/') {
		makeCachable(w)
		if r.Header.Get("If-None-Match") == s.iFrameETag {
			w.WriteHeader(http.StatusNotModified)
			return
		}

		w.Header().Set("ETag", s.iFrameETag)
		w.Header().Set("Content-Type", "text/html; charset=UTF-8")
		w.Write(s.iFrameContent)
		return

	} else if path == "" || path == "/" {
		w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
		w.Write([]byte("Welcome to SockJS!\n"))
		return

	} else if path == "/info" {
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, GET")
			return
		}

		makeNotCachable(w)
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		enc := json.NewEncoder(w)
		info := make(map[string]interface{})
		info["websocket"] = s.Websocket
		info["cookie_needed"] = s.CookieNeeded
		info["origins"] = []string{"*:*"}
		info["entropy"] = rand.Int31()
		enc.Encode(info)
		return

	} else if path == "/websocket" {
		s.serveRawWebsocket(w, r)
		return
	}

	parts := strings.Split(path, "/")[1:]
	if len(parts) != 3 || parts[0] == "" || strings.ContainsRune(parts[0], '.') || parts[1] == "" || strings.ContainsRune(parts[1], '.') {
		http.NotFound(w, r)
		return
	}

	if parts[2] == "websocket" {
		s.serveWebsocket(w, r)
		return
	}

	s.sessionsMutex.Lock()
	if s.lastSessionCleanup.Add(s.Timeout).Before(time.Now()) {
		for key, session := range s.sessions {
			if session.Broken || session.LastActivity.Add(s.Timeout).Before(time.Now()) {
				session.Close()
				delete(s.sessions, key)
			}
		}
		s.lastSessionCleanup = time.Now()
	}
	session := s.sessions[parts[1]]
	if session == nil {
		if parts[2] == "xhr_send" || parts[2] == "jsonp_send" {
			http.NotFound(w, r)
			s.sessionsMutex.Unlock()
			return
		}
		session = s.newSession()
		s.sessions[parts[1]] = session
	}
	session.LastActivity = time.Now()
	s.sessionsMutex.Unlock()

	if s.CookieNeeded {
		newCookie := r.Header.Get("Cookie")
		if newCookie != "" {
			session.Cookie = newCookie
		}
		w.Header().Set("Set-Cookie", session.Cookie+";path=/")
	}

	chunked := r.ProtoMinor == 1
	switch parts[2] {
	case "xhr_send":
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, POST")
			return
		}

		if r.ContentLength == 0 {
			http.Error(w, "Payload expected.", http.StatusInternalServerError)
			return
		}

		data := make([]byte, r.ContentLength)
		io.ReadFull(r.Body, data)
		if session.ReadMessages(data) {
			w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
			w.WriteHeader(http.StatusNoContent)
		} else {
			http.Error(w, "Broken JSON encoding.", http.StatusInternalServerError)
		}

	case "jsonp_send":
		var data []byte
		if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
			data = []byte(r.FormValue("d"))
		} else {
			data = make([]byte, r.ContentLength)
			io.ReadFull(r.Body, data)
		}
		if len(data) == 0 {
			http.Error(w, "Payload expected.", http.StatusInternalServerError)
			return
		}

		if session.ReadMessages(data) {
			w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("ok"))
		} else {
			http.Error(w, "Broken JSON encoding.", http.StatusInternalServerError)
		}

	case "xhr":
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, POST")
			return
		}

		w.Header().Set("Content-Type", "application/javascript; charset=UTF-8")
		w.WriteHeader(http.StatusOK)

		session.WriteFrames(w, false, chunked, nil, []byte{'\n'}, false)

	case "xhr_streaming":
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, POST")
			return
		}

		w.Header().Set("Content-Type", "application/javascript; charset=UTF-8")
		w.WriteHeader(http.StatusOK)

		prelude := make([]byte, 2049)
		for i := 0; i < 2048; i++ {
			prelude[i] = 'h'
		}
		prelude[2048] = '\n'
		w.Write(prelude)
		session.WriteFrames(w, true, chunked, nil, []byte{'\n'}, false)

	case "eventsource":
		w.Header().Set("Content-Type", "text/event-stream; charset=UTF-8")
		makeNotCachable(w)
		w.WriteHeader(http.StatusOK)

		w.Write([]byte("\r\n"))
		session.WriteFrames(w, true, chunked, []byte("data: "), []byte("\r\n\r\n"), false)

	case "htmlfile":
		callback := r.URL.Query().Get("c")
		if callback == "" {
			http.Error(w, `"callback" parameter required`, http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "text/html; charset=UTF-8")
		makeNotCachable(w)
		w.WriteHeader(http.StatusOK)

		w.Write(createHtmlfileContent(callback))
		session.WriteFrames(w, true, chunked, []byte("\r\n"), true)

	case "jsonp":
		callback := r.URL.Query().Get("c")
		if callback == "" {
			http.Error(w, `"callback" parameter required`, http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/javascript; charset=UTF-8")
		makeNotCachable(w)
		w.WriteHeader(http.StatusOK)

		session.WriteFrames(w, false, chunked, []byte(callback+"(\""), []byte("\");\r\n"), true)

	default:
		http.NotFound(w, r)
	}

}

func (s *Service) Close() {
	s.sessionsMutex.Lock()
	defer s.sessionsMutex.Unlock()
	for _, session := range s.sessions {
		session.Close()
	}
	s.sessions = make(map[string]*session)
}

func (s *Service) newSession() *session {
	sess := &session{
		Service:         s,
		ReceiveChan:     make(chan interface{}, 1024),
		SendChan:        make(chan interface{}, 1024),
		DoConnCheck:     make(chan bool),
		ConnCheckResult: make(chan bool),
		ReadSemaphore:   make(chan bool, 1),
		WriteOpenFrame:  true,
		Cookie:          "JSESSIONID=dummy",
	}
	go func() {
		s.Callback(sess.ReceiveChan, sess.SendChan)
		close(sess.SendChan)
	}()
	return sess
}

func makeCachable(w http.ResponseWriter) {
	cachingDuration := time.Hour * 24 * 365
	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(cachingDuration.Seconds())))
	w.Header().Set("Expires", time.Now().Add(cachingDuration).Format(time.RFC1123))
}

func makeNotCachable(w http.ResponseWriter) {
	w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
}

func answerOptions(w http.ResponseWriter, allowedMethods string) {
	makeCachable(w)
	w.Header().Set("Access-Control-Max-Age", "31536000")
	w.Header().Set("Access-Control-Allow-Methods", allowedMethods)
	w.WriteHeader(http.StatusNoContent)
}

func createIFrameContent(jsFileUrl string) []byte {
	return []byte(`<!DOCTYPE html>

  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

Don't panic!

This is a SockJS hidden iframe. It's used for cross domain magic.

`)
}

func createHtmlfileContent(callback string) []byte {
	body := []byte(`<!doctype html>

  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

Don't panic!

  `)
	content := make([]byte, 1025)
	copy(content, body)
	for i := len(body); i < len(content); i++ {
		content[i] = ' '
	}
	return content
}
<commit_msg>sockjs: Separated timeout goroutine.<commit_after>package sockjs

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

type Service struct {
	Websocket    bool
	CookieNeeded bool
	Timeout      time.Duration
	StreamLimit  int
	Callback     func(receiveChan <-chan interface{}, sendChan chan<- interface{})

	iFrameContent      []byte
	iFrameETag         string
	active             bool
	sessions           map[string]*session
	sessionsMutex      sync.Mutex
	lastSessionCleanup time.Time
}

func NewService(jsFileUrl string, websocket, cookieNeeded bool, timeout time.Duration, streamLimit int, callback func(receiveChan <-chan interface{}, sendChan chan<- interface{})) *Service {
	iFrameContent := createIFrameContent(jsFileUrl)
	hash := md5.New()
	hash.Write(iFrameContent)
	iFrameETag := "\"" + hex.EncodeToString(hash.Sum(nil)) + "\""

	if streamLimit <= 0 {
		streamLimit = 128 * 1024
	}

	s := &Service{
		Websocket:    websocket,
		CookieNeeded: cookieNeeded,
		Timeout:      timeout,
		StreamLimit:  streamLimit,
		Callback:     callback,

		iFrameContent: iFrameContent,
		iFrameETag:    iFrameETag,
		active:        true,
		sessions:      make(map[string]*session),
		sessionsMutex: sync.Mutex{},
	}

	go func() {
		for s.active {
			s.sessionsMutex.Lock()
			for key, session := range s.sessions {
				if session.Broken || session.LastActivity.Add(s.Timeout).Before(time.Now()) {
					session.Close()
					delete(s.sessions, key)
				}
			}
			s.sessionsMutex.Unlock()
			time.Sleep(s.Timeout)
		}
	}()

	return s
}

func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println(err)
			debug.PrintStack()
		}
	}()

	path := r.URL.Path

	if r.Header.Get("Origin") != "" && r.Header.Get("Origin") != "null" {
		w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
	} else {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	w.Header().Set("Access-Control-Allow-Credentials", "true")
	h := r.Header.Get("Access-Control-Request-Headers")
	if h != "" {
		w.Header().Set("Access-Control-Allow-Headers", h)
	}

	if strings.HasPrefix(path, "/iframe") && strings.HasSuffix(path, ".html") && !strings.ContainsRune(path[1:], '/') {
		makeCachable(w)
		if r.Header.Get("If-None-Match") == s.iFrameETag {
			w.WriteHeader(http.StatusNotModified)
			return
		}

		w.Header().Set("ETag", s.iFrameETag)
		w.Header().Set("Content-Type", "text/html; charset=UTF-8")
		w.Write(s.iFrameContent)
		return

	} else if path == "" || path == "/" {
		w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
		w.Write([]byte("Welcome to SockJS!\n"))
		return

	} else if path == "/info" {
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, GET")
			return
		}

		makeNotCachable(w)
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		enc := json.NewEncoder(w)
		info := make(map[string]interface{})
		info["websocket"] = s.Websocket
		info["cookie_needed"] = s.CookieNeeded
		info["origins"] = []string{"*:*"}
		info["entropy"] = rand.Int31()
		enc.Encode(info)
		return

	} else if path == "/websocket" {
		s.serveRawWebsocket(w, r)
		return
	}

	parts := strings.Split(path, "/")[1:]
	if len(parts) != 3 || parts[0] == "" || strings.ContainsRune(parts[0], '.') || parts[1] == "" || strings.ContainsRune(parts[1], '.') {
		http.NotFound(w, r)
		return
	}

	if parts[2] == "websocket" {
		s.serveWebsocket(w, r)
		return
	}

	s.sessionsMutex.Lock()
	if !s.active {
		s.sessionsMutex.Unlock()
		return
	}
	session := s.sessions[parts[1]]
	if session == nil {
		if parts[2] == "xhr_send" || parts[2] == "jsonp_send" {
			http.NotFound(w, r)
			s.sessionsMutex.Unlock()
			return
		}
		session = s.newSession()
		s.sessions[parts[1]] = session
	}
	session.LastActivity = time.Now()
	s.sessionsMutex.Unlock()

	if s.CookieNeeded {
		newCookie := r.Header.Get("Cookie")
		if newCookie != "" {
			session.Cookie = newCookie
		}
		w.Header().Set("Set-Cookie", session.Cookie+";path=/")
	}

	chunked := r.ProtoMinor == 1
	switch parts[2] {
	case "xhr_send":
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, POST")
			return
		}

		if r.ContentLength == 0 {
			http.Error(w, "Payload expected.", http.StatusInternalServerError)
			return
		}

		data := make([]byte, r.ContentLength)
		io.ReadFull(r.Body, data)
		if session.ReadMessages(data) {
			w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
			w.WriteHeader(http.StatusNoContent)
		} else {
			http.Error(w, "Broken JSON encoding.", http.StatusInternalServerError)
		}

	case "jsonp_send":
		var data []byte
		if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
			data = []byte(r.FormValue("d"))
		} else {
			data = make([]byte, r.ContentLength)
			io.ReadFull(r.Body, data)
		}
		if len(data) == 0 {
			http.Error(w, "Payload expected.", http.StatusInternalServerError)
			return
		}

		if session.ReadMessages(data) {
			w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("ok"))
		} else {
			http.Error(w, "Broken JSON encoding.", http.StatusInternalServerError)
		}

	case "xhr":
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, POST")
			return
		}

		w.Header().Set("Content-Type", "application/javascript; charset=UTF-8")
		w.WriteHeader(http.StatusOK)

		session.WriteFrames(w, false, chunked, nil, []byte{'\n'}, false)

	case "xhr_streaming":
		if r.Method == "OPTIONS" {
			answerOptions(w, "OPTIONS, POST")
			return
		}

		w.Header().Set("Content-Type", "application/javascript; charset=UTF-8")
		w.WriteHeader(http.StatusOK)

		prelude := make([]byte, 2049)
		for i := 0; i < 2048; i++ {
			prelude[i] = 'h'
		}
		prelude[2048] = '\n'
		w.Write(prelude)
		session.WriteFrames(w, true, chunked, nil, []byte{'\n'}, false)

	case "eventsource":
		w.Header().Set("Content-Type", "text/event-stream; charset=UTF-8")
		makeNotCachable(w)
		w.WriteHeader(http.StatusOK)

		w.Write([]byte("\r\n"))
		session.WriteFrames(w, true, chunked, []byte("data: "), []byte("\r\n\r\n"), false)

	case "htmlfile":
		callback := r.URL.Query().Get("c")
		if callback == "" {
			http.Error(w, `"callback" parameter required`, http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "text/html; charset=UTF-8")
		makeNotCachable(w)
		w.WriteHeader(http.StatusOK)

		w.Write(createHtmlfileContent(callback))
		session.WriteFrames(w, true, chunked, []byte("\r\n"), true)

	case "jsonp":
		callback := r.URL.Query().Get("c")
		if callback == "" {
			http.Error(w, `"callback" parameter required`, http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/javascript; charset=UTF-8")
		makeNotCachable(w)
		w.WriteHeader(http.StatusOK)

		session.WriteFrames(w, false, chunked, []byte(callback+"(\""), []byte("\");\r\n"), true)

	default:
		http.NotFound(w, r)
	}

}

func (s *Service) Close() {
	s.sessionsMutex.Lock()
	defer s.sessionsMutex.Unlock()
	for _, session := range s.sessions {
		session.Close()
	}
	s.active = false
	s.sessions = nil
}

func (s *Service) newSession() *session {
	sess := &session{
		Service:         s,
		ReceiveChan:     make(chan interface{}, 1024),
		SendChan:        make(chan interface{}, 1024),
		DoConnCheck:     make(chan bool),
		ConnCheckResult: make(chan bool),
		ReadSemaphore:   make(chan bool, 1),
		WriteOpenFrame:  true,
		Cookie:          "JSESSIONID=dummy",
	}
	go func() {
		s.Callback(sess.ReceiveChan, sess.SendChan)
		close(sess.SendChan)
	}()
	return sess
}

func makeCachable(w http.ResponseWriter) {
	cachingDuration := time.Hour * 24 * 365
	w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(cachingDuration.Seconds())))
	w.Header().Set("Expires", time.Now().Add(cachingDuration).Format(time.RFC1123))
}

func makeNotCachable(w http.ResponseWriter) {
	w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
}

func answerOptions(w http.ResponseWriter, allowedMethods string) {
	makeCachable(w)
	w.Header().Set("Access-Control-Max-Age", "31536000")
	w.Header().Set("Access-Control-Allow-Methods", allowedMethods)
	w.WriteHeader(http.StatusNoContent)
}

func createIFrameContent(jsFileUrl string) []byte {
	return []byte(`<!DOCTYPE html>

  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

Don't panic!

This is a SockJS hidden iframe. It's used for cross domain magic.

`)
}

func createHtmlfileContent(callback string) []byte {
	body := []byte(`<!doctype html>

  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

Don't panic!

  `)
	content := make([]byte, 1025)
	copy(content, body)
	for i := len(body); i < len(content); i++ {
		content[i] = ' '
	}
	return content
}
<|endoftext|>


<commit_before>// A simple tool for log rotation.
/*
   Copyright (C) 2016, Library of the University of Tübingen

   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 main

import "flag"
import "fmt"
import "os"
import "path"
import "path/filepath"
import "strconv"

// Handle command-line arguments.
func processFlags(maxRotationCount *int) {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s [--max-rotation-count max_rotations] log_base_names\n", path.Base(os.Args[0]))
		flag.PrintDefaults()
		os.Exit(1)
	}
	localMaxRotationCount := flag.Int("max-rotation-count", 5, "The maximum number of log file rotations.")
	flag.Parse()
	if *localMaxRotationCount < 1 {
		fmt.Fprintf(os.Stderr, "%s: max-rotation-count must be positive!\n", path.Base(os.Args[0]))
		os.Exit(1)
	}
	*maxRotationCount = *localMaxRotationCount
}

// Either return the log file names provided on the command-line, or, if there are none,
// return a list of files matching "*.log".
func getLogNames() []string {
	logNames := flag.Args()
	if len(logNames) == 0 {
		logNames, _ = filepath.Glob("*.log")
	}
	return logNames
}

func exists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func processName(logFileName string, maxRotationCount int) {
	if !exists(logFileName) {
		return
	} else {
		os.Remove(logFileName + "." + strconv.Itoa(maxRotationCount))
	}

	for countSuffix := maxRotationCount; countSuffix > 1; countSuffix-- {
		os.Rename(logFileName+"."+strconv.Itoa(countSuffix-1), logFileName+"."+strconv.Itoa(countSuffix))
	}
	os.Rename(logFileName, logFileName+".1")
}

func main() {
	var maxRotationCount int
	processFlags(&maxRotationCount)
	fmt.Printf("maxRotationCount = %d\n", maxRotationCount)
	logNames := getLogNames()
	for _, name := range logNames {
		processName(name, maxRotationCount)
	}
}
<commit_msg>Removed debugging output.<commit_after>// A simple tool for log rotation.
/*
   Copyright (C) 2016, Library of the University of Tübingen

   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 main

import "flag"
import "fmt"
import "os"
import "path"
import "path/filepath"
import "strconv"

// Handle command-line arguments.
func processFlags(maxRotationCount *int) {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "usage: %s [--max-rotation-count max_rotations] log_base_names\n", path.Base(os.Args[0]))
		flag.PrintDefaults()
		os.Exit(1)
	}
	localMaxRotationCount := flag.Int("max-rotation-count", 5, "The maximum number of log file rotations.")
	flag.Parse()
	if *localMaxRotationCount < 1 {
		fmt.Fprintf(os.Stderr, "%s: max-rotation-count must be positive!\n", path.Base(os.Args[0]))
		os.Exit(1)
	}
	*maxRotationCount = *localMaxRotationCount
}

// Either return the log file names provided on the command-line, or, if there are none,
// return a list of files matching "*.log".
func getLogNames() []string {
	logNames := flag.Args()
	if len(logNames) == 0 {
		logNames, _ = filepath.Glob("*.log")
	}
	return logNames
}

func exists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func processName(logFileName string, maxRotationCount int) {
	if !exists(logFileName) {
		return
	} else {
		os.Remove(logFileName + "." + strconv.Itoa(maxRotationCount))
	}

	for countSuffix := maxRotationCount; countSuffix > 1; countSuffix-- {
		os.Rename(logFileName+"."+strconv.Itoa(countSuffix-1), logFileName+"."+strconv.Itoa(countSuffix))
	}
	os.Rename(logFileName, logFileName+".1")
}

func main() {
	var maxRotationCount int
	processFlags(&maxRotationCount)
	logNames := getLogNames()
	for _, name := range logNames {
		processName(name, maxRotationCount)
	}
}
<|endoftext|>