<commit_before>/*
 * Copyright (c) 2017 AlexRuzin (stan.ruzin@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package gofs

import (
    "testing"
    "time"
)

func TestIOSanity(t *testing.T) {
	out("[+] Running Standard I/O Sanity Test...")

    header := create_db("<path>")
    if header == nil {
        t.Errorf("error: Failed to obtain header")
    }
	out("[+] Test 1 PASS")

    // The root file "/" must at least exist
    if file := header.create("/"); file == nil || file.filename != "/" {
        t.Errorf("error: Failed to return root handle")
    }
	out("[+] Test 2 PASS")

    /*
     * Try to delete the root file "/"
     */
    if header.delete("/") == 0 {
        t.Errorf("error: Cannot delete root -- critical")
    }
	out("[+] Test 3 PASS")

    /*
     * Attempt to write to a nonexistant file
     */
	var data = []byte{ 1, 2 }
    if header.write("/folder5/folder5/file5", data) != STATUS_ERROR {
        t.Errorf("error: Cannot write to a nonexistant file")
    }
	out("[+] Test 4 PASS")

    /*
     * Attempt to create a new file0
     */
    if file := header.create("/folder0/folder0/file0"); file == nil {
        t.Errorf("error: file0 cannot be created")
    }
	out("[+] Test 5.0 PASS")

    /*
     * Attempt to create a new file0
     */
    if file := header.create("/folder0/folder0/file0"); file != nil {
        t.Errorf("error: file0 cannot be created twice")
    }
	out("[+] Test 5.1 PASS")

    /*
     * Write some data into file0
     */
    data = []byte{ 1, 2, 3, 4 }
    if header.write("/folder0/folder0/file0", data) != 0 {
        t.Errorf("error: Failed to write data in file0")
    }
	out("[+] Test 6 PASS")

    /*
     * Attempt to create a new file3
     */
    if file := header.create("/folder1/folder0/file3"); file == nil {
        t.Errorf("error: file3 cannot be created")
    }
	out("[+] Test 7 PASS")

    /*
     * Write some data into file3
     */
    var data2 = []byte{ 1, 2, 3, 4, 5, 6, 7 }
    if header.write("/folder1/folder0/file3", data2) != 0 {
        t.Errorf("error: Failed to write data in file3")
    }
	out("[+] Test 8 PASS")

    /*
     * Read the written data from file0 and compare
     */
    output_data := header.read("/folder0/folder0/file0")
    if output_data == nil || len(output_data) != len(data) || header.t_size - 7 /* len(file3) */ != uint(len(data)) {
        t.Errorf("error: Failed to read data from file0")
    }
	out("[+] Test 9 PASS")

    /*
     * Read the written data from file3 and compare
     */
    output_data = header.read("/folder1/folder0/file3")
    if output_data == nil || len(output_data) != len(data2) || header.t_size - 4 /* len(file0) */ != uint(len(data2)) {
        t.Errorf("error: Failed to read data from file3")
    }
	out("[+] Test 10 PASS")

    /*
     * Write other data to file0
     */
    data = []byte{ 1, 2, 3 }
    if header.write("/folder0/folder0/file0", data) != 0 {
        t.Errorf("error: Failed to write data in file1")
    }
	out("[+] Test 11 PASS")

    /*
     * Read the new data from file0
     */
    output_data = header.read("/folder0/folder0/file0")
    if output_data == nil || len(output_data) != len(data) {
        t.Errorf("error: Failed to read data from file1")
    }
	out("[+] Test 12 PASS")

    /*
     * Attempt to create a new file5. This will be a blank file
     */
    if file := header.create("/folder2/file5"); file == nil {
        t.Errorf("error: file3 cannot be created")
    }
	out("[+] Test 13 PASS")

    /*
     * Delete file0 -- complete this
     */

	/*
	 * Create just a folder
	 */
    if file := header.create("/folder2/file5/"); file == nil {
        t.Errorf("error: folder file5 cannot be created")
    }
	out("[+] Test 15 PASS")

     /*
      * Unmount/commit database to file
      */
    if header.unmount_db(nil) != 0 {
        t.Errorf("error: Failed to commit database")
    }
	out("[+] Test 16 PASS")

    time.Sleep(10000)
}
<commit_msg>Fixed major panic() on closed channel bug<commit_after>/*
 * Copyright (c) 2017 AlexRuzin (stan.ruzin@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package gofs

import (
	"testing"
	"time"
)

func TestIOSanity(t *testing.T) {
	out("[+] Running Standard I/O Sanity Test...")

	header := create_db("<path>")
	if header == nil {
		t.Errorf("error: Failed to obtain header")
	}
	out("[+] Test 1 PASS")

	// The root file "/" must at least exist
	if file := header.create("/"); file == nil || file.filename != "/" {
		t.Errorf("error: Failed to return root handle")
	}
	out("[+] Test 2 PASS")

	/*
	 * Try to delete the root file "/"
	 */
	if header.delete("/") == STATUS_OK {
		t.Errorf("error: Cannot delete root -- critical")
	}
	out("[+] Test 3 PASS")

	/*
	 * Attempt to write to a nonexistant file
	 */
	var data = []byte{1, 2}
	if header.write("/folder5/folder5/file5", data) != STATUS_ERROR {
		t.Errorf("error: Cannot write to a nonexistant file")
	}
	out("[+] Test 4 PASS")

	/*
	 * Attempt to create a new file0
	 */
	if file := header.create("/folder0/folder0/file0"); file == nil {
		t.Errorf("error: file0 cannot be created")
	}
	out("[+] Test 5.0 PASS")

	/*
	 * Attempt to create a new file0

	if file := header.create("/folder0/folder0/file0"); file != nil {
		t.Errorf("error: file0 cannot be created twice")
	}
	out("[+] Test 5.1 PASS")*/

	/*
	 * Write some data into file0
	 */
	data = []byte{1, 2, 3, 4}
	if header.write("/folder0/folder0/file0", data) != 0 {
		t.Errorf("error: Failed to write data in file0")
	}
	out("[+] Test 6 PASS")

	/*
	 * Attempt to create a new file3
	 */
	if file := header.create("/folder1/folder0/file3"); file == nil {
		t.Errorf("error: file3 cannot be created")
	}
	out("[+] Test 7 PASS")

	/*
	 * Write some data into file3
	 */
	var data2 = []byte{1, 2, 3, 4, 5, 6, 7}
	if header.write("/folder1/folder0/file3", data2) != 0 {
		t.Errorf("error: Failed to write data in file3")
	}
	out("[+] Test 8 PASS")

	/*
	 * Read the written data from file0 and compare
	 */
	output_data := header.read("/folder0/folder0/file0")
	if output_data == nil || len(output_data) != len(data) || header.t_size-7 /* len(file3) */ != uint(len(data)) {
		t.Errorf("error: Failed to read data from file0")
	}
	out("[+] Test 9 PASS")

	/*
	 * Read the written data from file3 and compare
	 */
	output_data = header.read("/folder1/folder0/file3")
	if output_data == nil || len(output_data) != len(data2) || header.t_size-4 /* len(file0) */ != uint(len(data2)) {
		t.Errorf("error: Failed to read data from file3")
	}
	out("[+] Test 10 PASS")

	/*
	 * Write other data to file0
	 */
	data = []byte{1, 2, 3}
	if header.write("/folder0/folder0/file0", data) != 0 {
		t.Errorf("error: Failed to write data in file1")
	}
	out("[+] Test 11 PASS")

	/*
	 * Read the new data from file0
	 */
	output_data = header.read("/folder0/folder0/file0")
	if output_data == nil || len(output_data) != len(data) {
		t.Errorf("error: Failed to read data from file1")
	}
	out("[+] Test 12 PASS")

	/*
	 * Attempt to create a new file5. This will be a blank file
	 */
	if file := header.create("/folder2/file5"); file == nil {
		t.Errorf("error: file3 cannot be created")
	}
	out("[+] Test 13 PASS")

	/*
	 * Delete file0 -- complete this
	 */

	/*
	 * Create just a folder
	 */
	if file := header.create("/folder2/file5/"); file == nil {
		t.Errorf("error: folder file5 cannot be created")
	}
	out("[+] Test 15 PASS")

	/*
	 * Unmount/commit database to file
	 */
	if header.unmount_db(nil) != 0 {
		t.Errorf("error: Failed to commit database")
	}
	out("[+] Test 16 PASS")

	time.Sleep(10000)
}
<|endoftext|>


<commit_before>/*
Package epub generates valid EPUB 3.0 files with additional EPUB 2.0 table of
contents (as seen here: https://github.com/bmaupin/epub-samples) for maximum
compatibility.

Basic usage:

	// Create a new EPUB
	e := epub.NewEpub("My title")

	// Set the author
	e.SetAuthor("Hingle McCringleberry")

	// Add a section
	section1Body := `
Section 1

This is a paragraph.
`
	e.AddSection(section1Body, "Section 1", "", "")

	// Write the EPUB
	err = e.Write("My EPUB.epub")
	if err != nil {
		// handle error
	}

*/
package epub

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/gofrs/uuid"
)

// ErrFilenameAlreadyUsed is thrown by AddCSS, AddFont, AddImage, or AddSection
// if the same filename is used more than once
var ErrFilenameAlreadyUsed = errors.New("Filename already used")

// ErrRetrievingFile is thrown by AddCSS, AddFont, or AddImage if there was a
// problem retrieving the source file that was provided
var ErrRetrievingFile = errors.New("Error retrieving file from source")

// FileRetrevalError is thrown by Write in the case of a media file added with
// AddImage, AddFont, AddCSS being unable to be read.
type FileRetrevalError struct {
	File string
	Err  error
}

// Error implements the error interface.
func (e *FileRetrevalError) Error() string {
	return fmt.Sprintf("Error retrieving %q from source: %+v", e.File, e.Err)
}

// Folder names used for resources inside the EPUB
const (
	CSSFolderName   = "css"
	FontFolderName  = "fonts"
	ImageFolderName = "images"
)

const (
	cssFileFormat          = "css%04d%s"
	defaultCoverBody       = `<img src="%s" alt="Cover Image" />`
	defaultCoverCSSContent = `body {
  background-color: #FFFFFF;
  margin-bottom: 0px;
  margin-left: 0px;
  margin-right: 0px;
  margin-top: 0px;
  text-align: center;
}
img {
  max-height: 100%;
  max-width: 100%;
}
`
	defaultCoverCSSFilename   = "cover.css"
	defaultCoverCSSSource     = "cover.css"
	defaultCoverImgFormat     = "cover%s"
	defaultCoverXhtmlFilename = "cover.xhtml"
	defaultEpubLang           = "en"
	fontFileFormat            = "font%04d%s"
	imageFileFormat           = "image%04d%s"
	sectionFileFormat         = "section%04d.xhtml"
	urnUUIDPrefix             = "urn:uuid:"
)

// Epub implements an EPUB file.
type Epub struct {
	author string
	cover  *epubCover
	// The key is the css filename, the value is the css source
	css map[string]string
	// The key is the font filename, the value is the font source
	fonts      map[string]string
	identifier string
	// The key is the image filename, the value is the image source
	images map[string]string
	// Language
	lang string
	// Page progression direction
	ppd string
	// The package file (package.opf)
	pkg      *pkg
	sections []epubSection
	title    string
	// Table of contents
	toc *toc
}

type epubCover struct {
	cssFilename   string
	cssTempFile   string
	imageFilename string
	xhtmlFilename string
}

type epubSection struct {
	filename string
	xhtml    *xhtml
}

// NewEpub returns a new Epub.
func NewEpub(title string) *Epub {
	e := &Epub{}
	e.cover = &epubCover{
		cssFilename:   "",
		cssTempFile:   "",
		imageFilename: "",
		xhtmlFilename: "",
	}
	e.css = make(map[string]string)
	e.fonts = make(map[string]string)
	e.images = make(map[string]string)
	e.pkg = newPackage()
	e.toc = newToc()
	// Set minimal required attributes
	e.SetIdentifier(urnUUIDPrefix + uuid.Must(uuid.NewV4()).String())
	e.SetLang(defaultEpubLang)
	e.SetTitle(title)

	return e
}

// AddCSS adds a CSS file to the EPUB and returns a relative path to the CSS
// file that can be used in EPUB sections in the format:
// ../CSSFolderName/internalFilename
//
// The CSS source should either be a URL or a path to a local file; in either
// case, the CSS file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the CSS file in the EPUB
// and must be unique among all CSS files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddCSS(source string, internalFilename string) (string, error) {
	return addMedia(source, internalFilename, cssFileFormat, CSSFolderName, e.css)
}

// AddFont adds a font file to the EPUB and returns a relative path to the font
// file that can be used in EPUB sections in the format:
// ../FontFolderName/internalFilename
//
// The font source should either be a URL or a path to a local file; in either
// case, the font file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the font file in the EPUB
// and must be unique among all font files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddFont(source string, internalFilename string) (string, error) {
	return addMedia(source, internalFilename, fontFileFormat, FontFolderName, e.fonts)
}

// AddImage adds an image to the EPUB and returns a relative path to the image
// file that can be used in EPUB sections in the format:
// ../ImageFolderName/internalFilename
//
// The image source should either be a URL or a path to a local file; in either
// case, the image file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the image file in the EPUB
// and must be unique among all image files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddImage(source string, imageFilename string) (string, error) {
	return addMedia(source, imageFilename, imageFileFormat, ImageFolderName, e.images)
}

// AddSection adds a new section (chapter, etc) to the EPUB and returns a
// relative path to the section that can be used from another section (for
// links).
//
// The body must be valid XHTML that will go between the  tags of the
// section XHTML file. The content will not be validated.
//
// The title will be used for the table of contents. The section will be shown
// in the table of contents in the same order it was added to the EPUB. The
// title is optional; if no title is provided, the section will not be added to
// the table of contents.
//
// The internal filename will be used when storing the section file in the EPUB
// and must be unique among all section files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
//
// The internal path to an already-added CSS file (as returned by AddCSS) to be
// used for the section is optional.
func (e *Epub) AddSection(body string, sectionTitle string, internalFilename string, internalCSSPath string) (string, error) {
	// Generate a filename if one isn't provided
	if internalFilename == "" {
		internalFilename = fmt.Sprintf(sectionFileFormat, len(e.sections)+1)
	}

	for _, section := range e.sections {
		if section.filename == internalFilename {
			return "", ErrFilenameAlreadyUsed
		}
	}

	x := newXhtml(body)
	x.setTitle(sectionTitle)

	if internalCSSPath != "" {
		x.setCSS(internalCSSPath)
	}

	s := epubSection{
		filename: internalFilename,
		xhtml:    x,
	}
	e.sections = append(e.sections, s)

	return internalFilename, nil
}

// Author returns the author of the EPUB.
func (e *Epub) Author() string {
	return e.author
}

// Identifier returns the unique identifier of the EPUB.
func (e *Epub) Identifier() string {
	return e.identifier
}

// Lang returns the language of the EPUB.
func (e *Epub) Lang() string {
	return e.lang
}

// Ppd returns the page progression direction of the EPUB.
func (e *Epub) Ppd() string {
	return e.ppd
}

// SetAuthor sets the author of the EPUB.
func (e *Epub) SetAuthor(author string) {
	e.author = author
	e.pkg.setAuthor(author)
}

// SetCover sets the cover page for the EPUB using the provided image source and
// optional CSS.
//
// The internal path to an already-added image file (as returned by AddImage) is
// required.
//
// The internal path to an already-added CSS file (as returned by AddCSS) to be
// used for the cover is optional. If the CSS path isn't provided, default CSS
// will be used.
func (e *Epub) SetCover(internalImagePath string, internalCSSPath string) {
	// If a cover already exists
	if e.cover.xhtmlFilename != "" {
		// Remove the xhtml file
		for i, section := range e.sections {
			if section.filename == e.cover.xhtmlFilename {
				e.sections = append(e.sections[:i], e.sections[i+1:]...)
				break
			}
		}

		// Remove the image
		delete(e.images, e.cover.imageFilename)

		// Remove the CSS
		delete(e.css, e.cover.cssFilename)

		if e.cover.cssTempFile != "" {
			os.Remove(e.cover.cssTempFile)
		}
	}

	e.cover.imageFilename = filepath.Base(internalImagePath)

	// Use default cover stylesheet if one isn't provided
	if internalCSSPath == "" {
		// Create a temporary file to hold the default cover CSS
		tempFile, err := ioutil.TempFile("", tempDirPrefix)
		if err != nil {
			panic(fmt.Sprintf("Error creating temp file: %s", err))
		}
		defer func() {
			if err := tempFile.Close(); err != nil {
				panic(fmt.Sprintf("Error closing temp file: %s", err))
			}
		}()
		e.cover.cssTempFile = tempFile.Name()

		// Write the default cover CSS to the temp file
		if _, err = tempFile.WriteString(defaultCoverCSSContent); err != nil {
			panic(fmt.Sprintf("Error writing CSS file: %s", err))
		}

		internalCSSPath, err = e.AddCSS(e.cover.cssTempFile, defaultCoverCSSFilename)
		// If that doesn't work, generate a filename
		if err == ErrFilenameAlreadyUsed {
			coverCSSFilename := fmt.Sprintf(
				cssFileFormat,
				len(e.css)+1,
				".css",
			)

			internalCSSPath, err = e.AddCSS(e.cover.cssTempFile, coverCSSFilename)
			if err == ErrFilenameAlreadyUsed {
				// This shouldn't cause an error
				panic(fmt.Sprintf("Error adding default cover CSS file: %s", err))
			}
		}
		if err != nil && err != ErrFilenameAlreadyUsed {
			panic(err)
		}
	}
	e.cover.cssFilename = filepath.Base(internalCSSPath)

	coverBody := fmt.Sprintf(defaultCoverBody, internalImagePath)
	// Title won't be used since the cover won't be added to the TOC
	// First try to use the default cover filename
	coverPath, err := e.AddSection(coverBody, "", defaultCoverXhtmlFilename, internalCSSPath)
	// If that doesn't work, generate a filename
	if err == ErrFilenameAlreadyUsed {
		coverPath, err = e.AddSection(coverBody, "", "", internalCSSPath)
		if err == ErrFilenameAlreadyUsed {
			// This shouldn't cause an error since we're not specifying a filename
			panic(fmt.Sprintf("Error adding default cover XHTML file: %s", err))
		}
	}
	e.cover.xhtmlFilename = filepath.Base(coverPath)
}

// SetIdentifier sets the unique identifier of the EPUB, such as a UUID, DOI,
// ISBN or ISSN. If no identifier is set, a UUID will be automatically
// generated.
func (e *Epub) SetIdentifier(identifier string) {
	e.identifier = identifier
	e.pkg.setIdentifier(identifier)
	e.toc.setIdentifier(identifier)
}

// SetLang sets the language of the EPUB.
func (e *Epub) SetLang(lang string) {
	e.lang = lang
	e.pkg.setLang(lang)
}

// SetPpd sets the page progression direction of the EPUB.
func (e *Epub) SetPpd(direction string) {
	e.ppd = direction
	e.pkg.setPpd(direction)
}

// SetTitle sets the title of the EPUB.
func (e *Epub) SetTitle(title string) {
	e.title = title
	e.pkg.setTitle(title)
	e.toc.setTitle(title)
}

// Title returns the title of the EPUB.
func (e *Epub) Title() string {
	return e.title
}

// Add a media file to the EPUB and return the path relative to the EPUB section
// files
func addMedia(source string, internalFilename string, mediaFileFormat string, mediaFolderName string, mediaMap map[string]string) (string, error) {
	// Make sure the source file is valid before proceeding
	if isFileSourceValid(source) == false {
		return "", ErrRetrievingFile
	}

	if internalFilename == "" {
		// If a filename isn't provided, use the filename from the source
		internalFilename = filepath.Base(source)
		// If that's already used, try to generate a unique filename
		if _, ok := mediaMap[internalFilename]; ok {
			internalFilename = fmt.Sprintf(
				mediaFileFormat,
				len(mediaMap)+1,
				strings.ToLower(filepath.Ext(source)),
			)
		}
	}

	if _, ok := mediaMap[internalFilename]; ok {
		return "", ErrFilenameAlreadyUsed
	}

	mediaMap[internalFilename] = source

	return filepath.Join(
		"..",
		mediaFolderName,
		internalFilename,
	), nil
}

func isFileSourceValid(source string) bool {
	u, err := url.Parse(source)
	if err != nil {
		return false
	}

	var r io.ReadCloser
	var resp *http.Response
	// If it's a URL
	if u.Scheme == "http" || u.Scheme == "https" {
		resp, err = http.Get(source)
		if err != nil {
			return false
		}
		r = resp.Body

		// Otherwise, assume it's a local file
	} else {
		r, err = os.Open(source)
	}
	if err != nil {
		return false
	}
	defer func() {
		if err := r.Close(); err != nil {
			panic(err)
		}
	}()

	return true
}
<commit_msg>Add documentation for FileRetrevalError's newly-public fields.<commit_after>/*
Package epub generates valid EPUB 3.0 files with additional EPUB 2.0 table of
contents (as seen here: https://github.com/bmaupin/epub-samples) for maximum
compatibility.

Basic usage:

	// Create a new EPUB
	e := epub.NewEpub("My title")

	// Set the author
	e.SetAuthor("Hingle McCringleberry")

	// Add a section
	section1Body := `
Section 1

This is a paragraph.
`
	e.AddSection(section1Body, "Section 1", "", "")

	// Write the EPUB
	err = e.Write("My EPUB.epub")
	if err != nil {
		// handle error
	}

*/
package epub

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/gofrs/uuid"
)

// ErrFilenameAlreadyUsed is thrown by AddCSS, AddFont, AddImage, or AddSection
// if the same filename is used more than once
var ErrFilenameAlreadyUsed = errors.New("Filename already used")

// ErrRetrievingFile is thrown by AddCSS, AddFont, or AddImage if there was a
// problem retrieving the source file that was provided
var ErrRetrievingFile = errors.New("Error retrieving file from source")

// FileRetrevalError is thrown by Write in the case of a media file added with
// AddImage, AddFont, AddCSS being unable to be read.
type FileRetrevalError struct {
	// File is the file that we failed to retreive.
	File string

	// Err is the underlying error that we encountered attempting to retreive File.
	Err error
}

// Error implements the error interface.
func (e *FileRetrevalError) Error() string {
	return fmt.Sprintf("Error retrieving %q from source: %+v", e.File, e.Err)
}

// Folder names used for resources inside the EPUB
const (
	CSSFolderName   = "css"
	FontFolderName  = "fonts"
	ImageFolderName = "images"
)

const (
	cssFileFormat          = "css%04d%s"
	defaultCoverBody       = `<img src="%s" alt="Cover Image" />`
	defaultCoverCSSContent = `body {
  background-color: #FFFFFF;
  margin-bottom: 0px;
  margin-left: 0px;
  margin-right: 0px;
  margin-top: 0px;
  text-align: center;
}
img {
  max-height: 100%;
  max-width: 100%;
}
`
	defaultCoverCSSFilename   = "cover.css"
	defaultCoverCSSSource     = "cover.css"
	defaultCoverImgFormat     = "cover%s"
	defaultCoverXhtmlFilename = "cover.xhtml"
	defaultEpubLang           = "en"
	fontFileFormat            = "font%04d%s"
	imageFileFormat           = "image%04d%s"
	sectionFileFormat         = "section%04d.xhtml"
	urnUUIDPrefix             = "urn:uuid:"
)

// Epub implements an EPUB file.
type Epub struct {
	author string
	cover  *epubCover
	// The key is the css filename, the value is the css source
	css map[string]string
	// The key is the font filename, the value is the font source
	fonts      map[string]string
	identifier string
	// The key is the image filename, the value is the image source
	images map[string]string
	// Language
	lang string
	// Page progression direction
	ppd string
	// The package file (package.opf)
	pkg      *pkg
	sections []epubSection
	title    string
	// Table of contents
	toc *toc
}

type epubCover struct {
	cssFilename   string
	cssTempFile   string
	imageFilename string
	xhtmlFilename string
}

type epubSection struct {
	filename string
	xhtml    *xhtml
}

// NewEpub returns a new Epub.
func NewEpub(title string) *Epub {
	e := &Epub{}
	e.cover = &epubCover{
		cssFilename:   "",
		cssTempFile:   "",
		imageFilename: "",
		xhtmlFilename: "",
	}
	e.css = make(map[string]string)
	e.fonts = make(map[string]string)
	e.images = make(map[string]string)
	e.pkg = newPackage()
	e.toc = newToc()
	// Set minimal required attributes
	e.SetIdentifier(urnUUIDPrefix + uuid.Must(uuid.NewV4()).String())
	e.SetLang(defaultEpubLang)
	e.SetTitle(title)

	return e
}

// AddCSS adds a CSS file to the EPUB and returns a relative path to the CSS
// file that can be used in EPUB sections in the format:
// ../CSSFolderName/internalFilename
//
// The CSS source should either be a URL or a path to a local file; in either
// case, the CSS file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the CSS file in the EPUB
// and must be unique among all CSS files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddCSS(source string, internalFilename string) (string, error) {
	return addMedia(source, internalFilename, cssFileFormat, CSSFolderName, e.css)
}

// AddFont adds a font file to the EPUB and returns a relative path to the font
// file that can be used in EPUB sections in the format:
// ../FontFolderName/internalFilename
//
// The font source should either be a URL or a path to a local file; in either
// case, the font file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the font file in the EPUB
// and must be unique among all font files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddFont(source string, internalFilename string) (string, error) {
	return addMedia(source, internalFilename, fontFileFormat, FontFolderName, e.fonts)
}

// AddImage adds an image to the EPUB and returns a relative path to the image
// file that can be used in EPUB sections in the format:
// ../ImageFolderName/internalFilename
//
// The image source should either be a URL or a path to a local file; in either
// case, the image file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the image file in the EPUB
// and must be unique among all image files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddImage(source string, imageFilename string) (string, error) {
	return addMedia(source, imageFilename, imageFileFormat, ImageFolderName, e.images)
}

// AddSection adds a new section (chapter, etc) to the EPUB and returns a
// relative path to the section that can be used from another section (for
// links).
//
// The body must be valid XHTML that will go between the  tags of the
// section XHTML file. The content will not be validated.
//
// The title will be used for the table of contents. The section will be shown
// in the table of contents in the same order it was added to the EPUB. The
// title is optional; if no title is provided, the section will not be added to
// the table of contents.
//
// The internal filename will be used when storing the section file in the EPUB
// and must be unique among all section files. If the same filename is used more
// than once, ErrFilenameAlreadyUsed will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
//
// The internal path to an already-added CSS file (as returned by AddCSS) to be
// used for the section is optional.
func (e *Epub) AddSection(body string, sectionTitle string, internalFilename string, internalCSSPath string) (string, error) {
	// Generate a filename if one isn't provided
	if internalFilename == "" {
		internalFilename = fmt.Sprintf(sectionFileFormat, len(e.sections)+1)
	}

	for _, section := range e.sections {
		if section.filename == internalFilename {
			return "", ErrFilenameAlreadyUsed
		}
	}

	x := newXhtml(body)
	x.setTitle(sectionTitle)

	if internalCSSPath != "" {
		x.setCSS(internalCSSPath)
	}

	s := epubSection{
		filename: internalFilename,
		xhtml:    x,
	}
	e.sections = append(e.sections, s)

	return internalFilename, nil
}

// Author returns the author of the EPUB.
func (e *Epub) Author() string {
	return e.author
}

// Identifier returns the unique identifier of the EPUB.
func (e *Epub) Identifier() string {
	return e.identifier
}

// Lang returns the language of the EPUB.
func (e *Epub) Lang() string {
	return e.lang
}

// Ppd returns the page progression direction of the EPUB.
func (e *Epub) Ppd() string {
	return e.ppd
}

// SetAuthor sets the author of the EPUB.
func (e *Epub) SetAuthor(author string) {
	e.author = author
	e.pkg.setAuthor(author)
}

// SetCover sets the cover page for the EPUB using the provided image source and
// optional CSS.
//
// The internal path to an already-added image file (as returned by AddImage) is
// required.
//
// The internal path to an already-added CSS file (as returned by AddCSS) to be
// used for the cover is optional. If the CSS path isn't provided, default CSS
// will be used.
func (e *Epub) SetCover(internalImagePath string, internalCSSPath string) {
	// If a cover already exists
	if e.cover.xhtmlFilename != "" {
		// Remove the xhtml file
		for i, section := range e.sections {
			if section.filename == e.cover.xhtmlFilename {
				e.sections = append(e.sections[:i], e.sections[i+1:]...)
				break
			}
		}

		// Remove the image
		delete(e.images, e.cover.imageFilename)

		// Remove the CSS
		delete(e.css, e.cover.cssFilename)

		if e.cover.cssTempFile != "" {
			os.Remove(e.cover.cssTempFile)
		}
	}

	e.cover.imageFilename = filepath.Base(internalImagePath)

	// Use default cover stylesheet if one isn't provided
	if internalCSSPath == "" {
		// Create a temporary file to hold the default cover CSS
		tempFile, err := ioutil.TempFile("", tempDirPrefix)
		if err != nil {
			panic(fmt.Sprintf("Error creating temp file: %s", err))
		}
		defer func() {
			if err := tempFile.Close(); err != nil {
				panic(fmt.Sprintf("Error closing temp file: %s", err))
			}
		}()
		e.cover.cssTempFile = tempFile.Name()

		// Write the default cover CSS to the temp file
		if _, err = tempFile.WriteString(defaultCoverCSSContent); err != nil {
			panic(fmt.Sprintf("Error writing CSS file: %s", err))
		}

		internalCSSPath, err = e.AddCSS(e.cover.cssTempFile, defaultCoverCSSFilename)
		// If that doesn't work, generate a filename
		if err == ErrFilenameAlreadyUsed {
			coverCSSFilename := fmt.Sprintf(
				cssFileFormat,
				len(e.css)+1,
				".css",
			)

			internalCSSPath, err = e.AddCSS(e.cover.cssTempFile, coverCSSFilename)
			if err == ErrFilenameAlreadyUsed {
				// This shouldn't cause an error
				panic(fmt.Sprintf("Error adding default cover CSS file: %s", err))
			}
		}
		if err != nil && err != ErrFilenameAlreadyUsed {
			panic(err)
		}
	}
	e.cover.cssFilename = filepath.Base(internalCSSPath)

	coverBody := fmt.Sprintf(defaultCoverBody, internalImagePath)
	// Title won't be used since the cover won't be added to the TOC
	// First try to use the default cover filename
	coverPath, err := e.AddSection(coverBody, "", defaultCoverXhtmlFilename, internalCSSPath)
	// If that doesn't work, generate a filename
	if err == ErrFilenameAlreadyUsed {
		coverPath, err = e.AddSection(coverBody, "", "", internalCSSPath)
		if err == ErrFilenameAlreadyUsed {
			// This shouldn't cause an error since we're not specifying a filename
			panic(fmt.Sprintf("Error adding default cover XHTML file: %s", err))
		}
	}
	e.cover.xhtmlFilename = filepath.Base(coverPath)
}

// SetIdentifier sets the unique identifier of the EPUB, such as a UUID, DOI,
// ISBN or ISSN. If no identifier is set, a UUID will be automatically
// generated.
func (e *Epub) SetIdentifier(identifier string) {
	e.identifier = identifier
	e.pkg.setIdentifier(identifier)
	e.toc.setIdentifier(identifier)
}

// SetLang sets the language of the EPUB.
func (e *Epub) SetLang(lang string) {
	e.lang = lang
	e.pkg.setLang(lang)
}

// SetPpd sets the page progression direction of the EPUB.
func (e *Epub) SetPpd(direction string) {
	e.ppd = direction
	e.pkg.setPpd(direction)
}

// SetTitle sets the title of the EPUB.
func (e *Epub) SetTitle(title string) {
	e.title = title
	e.pkg.setTitle(title)
	e.toc.setTitle(title)
}

// Title returns the title of the EPUB.
func (e *Epub) Title() string {
	return e.title
}

// Add a media file to the EPUB and return the path relative to the EPUB section
// files
func addMedia(source string, internalFilename string, mediaFileFormat string, mediaFolderName string, mediaMap map[string]string) (string, error) {
	// Make sure the source file is valid before proceeding
	if isFileSourceValid(source) == false {
		return "", ErrRetrievingFile
	}

	if internalFilename == "" {
		// If a filename isn't provided, use the filename from the source
		internalFilename = filepath.Base(source)
		// If that's already used, try to generate a unique filename
		if _, ok := mediaMap[internalFilename]; ok {
			internalFilename = fmt.Sprintf(
				mediaFileFormat,
				len(mediaMap)+1,
				strings.ToLower(filepath.Ext(source)),
			)
		}
	}

	if _, ok := mediaMap[internalFilename]; ok {
		return "", ErrFilenameAlreadyUsed
	}

	mediaMap[internalFilename] = source

	return filepath.Join(
		"..",
		mediaFolderName,
		internalFilename,
	), nil
}

func isFileSourceValid(source string) bool {
	u, err := url.Parse(source)
	if err != nil {
		return false
	}

	var r io.ReadCloser
	var resp *http.Response
	// If it's a URL
	if u.Scheme == "http" || u.Scheme == "https" {
		resp, err = http.Get(source)
		if err != nil {
			return false
		}
		r = resp.Body

		// Otherwise, assume it's a local file
	} else {
		r, err = os.Open(source)
	}
	if err != nil {
		return false
	}
	defer func() {
		if err := r.Close(); err != nil {
			panic(err)
		}
	}()

	return true
}
<|endoftext|>


<commit_before>package grok

import "testing"

func TestReverseList(t *testing.T) {
	var array = []string{"A", "B", "C", "D"}
	var expectedArray = []string{"D", "C", "B", "A"}
	arrayReversed := reverseList(array)
	if !sliceEquals(arrayReversed, expectedArray) {
		t.Fatalf("reversedList is %+v, expected : %+v", arrayReversed, expectedArray)
	}
}

func TestSortGraph(t *testing.T) {
	var g = graph{}
	g["7"] = []string{"11", "8"}
	g["5"] = []string{"11"}
	g["3"] = []string{"8", "10"}
	g["11"] = []string{"2", "9", "10"}
	g["8"] = []string{"9", "10"}
	g["2"] = []string{}
	g["9"] = []string{}
	g["10"] = []string{}

	validOrders := [][]string{
		[]string{"3", "5", "7", "8", "11", "10", "9", "2"},
		[]string{"3", "5", "7", "8", "11", "2", "10", "9"},
		[]string{"3", "5", "7", "8", "11", "9", "2", "10"},
		[]string{"7", "3", "8", "5", "11", "10", "9", "2"},
		[]string{"3", "5", "7", "11", "2", "8", "10", "9"},
		[]string{"5", "7", "11", "2", "3", "8", "10", "9"},
	}

	order, _ := sortGraph(g)

	for _, expectedOrder := range validOrders {
		if sliceEquals(order, expectedOrder) {
			return
		}
	}

	t.Fatalf("sorted graph is %+v, expected a order like: %+v", order, validOrders[0])
}

func sliceEquals(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}
<commit_msg>add graph cycle detection tests<commit_after>package grok

import "testing"

func TestReverseList(t *testing.T) {
	var array = []string{"A", "B", "C", "D"}
	var expectedArray = []string{"D", "C", "B", "A"}
	arrayReversed := reverseList(array)
	if !sliceEquals(arrayReversed, expectedArray) {
		t.Fatalf("reversedList is %+v, expected : %+v", arrayReversed, expectedArray)
	}
}

func TestSortGraph(t *testing.T) {
	var g = graph{}
	g["7"] = []string{"11", "8"}
	g["5"] = []string{"11"}
	g["3"] = []string{"8", "10"}
	g["11"] = []string{"2", "9", "10"}
	g["8"] = []string{"9", "10"}
	g["2"] = []string{}
	g["9"] = []string{}
	g["10"] = []string{}

	validOrders := [][]string{
		[]string{"3", "5", "7", "8", "11", "10", "9", "2"},
		[]string{"3", "5", "7", "8", "11", "2", "10", "9"},
		[]string{"3", "5", "7", "8", "11", "9", "2", "10"},
		[]string{"7", "3", "8", "5", "11", "10", "9", "2"},
		[]string{"3", "5", "7", "11", "2", "8", "10", "9"},
		[]string{"5", "7", "11", "2", "3", "8", "10", "9"},
	}

	order, cycle := sortGraph(g)
	if cycle != nil {
		t.Fatal("cycle detected while not expected")
	}
	for _, expectedOrder := range validOrders {
		if sliceEquals(order, expectedOrder) {
			return
		}
	}

	t.Fatalf("sorted graph is %+v, expected a order like: %+v", order, validOrders[0])
}

func TestSortGraphWithCycle(t *testing.T) {
	var g = graph{}
	g["7"] = []string{"11", "8"}
	g["5"] = []string{"11"}
	g["3"] = []string{"8", "10"}
	g["11"] = []string{"2", "9", "10"}
	g["8"] = []string{"9", "10"}
	g["2"] = []string{}
	g["9"] = []string{"3"}
	g["10"] = []string{}

	validCycles := [][]string{
		[]string{"3", "9", "8"},
		[]string{"8", "3", "9"},
		[]string{"9", "8", "3"},
	}

	_, cycle := sortGraph(g)

	if cycle == nil {
		t.Fatal("cycle not detected while sorting graph")
	}

	for _, expectedCycle := range validCycles {
		if sliceEquals(cycle, expectedCycle) {
			return
		}
	}

	t.Fatalf("cycle have %+v, expected %+v", cycle, validCycles[0])
}

func sliceEquals(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>// Package errs provides a simple error package with stack traces.
package errs

import (
	"fmt"
	"io"
	"runtime"
)

// Namer is implemented by all errors returned in this package. It returns a
// name for the class of error it is, and a boolean indicating if the name is
// valid.
type Namer interface{ Name() (string, bool) }

// Causer is implemented by all errors returned in this package. It returns
// the underlying cause of the error, or nil if there is no underlying cause.
type Causer interface{ Cause() error }

// New returns an error not contained in any class. This is the same as calling
// fmt.Errorf(...) except it captures a stack trace on creation.
func New(format string, args ...interface{}) error {
	return (*Class).create(nil, 3, fmt.Errorf(format, args...))
}

// Wrap returns an error not contained in any class. It just associates a stack
// trace with the error. Wrap returns nil if err is nil.
func Wrap(err error) error {
	return (*Class).create(nil, 3, err)
}

// Unwrap returns the underlying error, if any, or just the error.
func Unwrap(err error) error {
	// we call Cause as much as possible. Since comparing arbitrary interfaces
	// with equality isn't panic safe, we only loop up to 100 times to ensure
	// that a poor implementation that loops does not cause a hang.
	for i := 0; err != nil && i < 100; i++ {
		causer, ok := err.(Causer)
		if !ok {
			break
		}

		// if the cause of some error is nil, we return it.
		nerr := causer.Cause()
		if nerr == nil {
			return err
		}
		err = nerr
	}

	return err
}

// Classes returns all the classes that have wrapped the error.
func Classes(err error) []*Class {
	if err, ok := err.(*errorT); ok && err != nil {
		return append([]*Class(nil), err.classes...)
	}
	return nil
}

//
// error classes
//

// Class represents a class of errors. You can construct errors, and check if
// errors are part of the class.
type Class string

// Has returns true if the passed in error was wrapped by this class.
func (c *Class) Has(err error) bool {
	if err, ok := err.(*errorT); ok {
		for _, k := range err.classes {
			if k == c {
				return true
			}
		}
	}
	return false
}

// New constructs an error with the format string that will be contained by
// this class. This is the same as calling Wrap(fmt.Errorf(...)).
func (c *Class) New(format string, args ...interface{}) error {
	return c.create(3, fmt.Errorf(format, args...))
}

// Wrap returns a new error based on the passed in error that is contained in
// this class. Wrap returns nil if err is nil.
func (c *Class) Wrap(err error) error {
	return c.create(3, err)
}

// create constructs the error, or just adds the class to the error, keeping
// track of the stack if it needs to construct it.
func (c *Class) create(depth int, err error) error {
	if err == nil {
		return nil
	}

	if err, ok := err.(*errorT); ok {
		if c != nil && err.outerClass() != c {
			err.classes = append(err.classes, c)
		}
		return err
	}

	var pcs [256]uintptr
	n := runtime.Callers(depth, pcs[:])

	var classes []*Class
	if c != nil {
		classes = []*Class{c}
	}

	return &errorT{
		classes: classes,
		err:     err,
		pcs:     pcs[:n:n],
	}
}

//
// errors
//

// errorT is the type of errors returned from this package.
type errorT struct {
	classes []*Class
	pcs     []uintptr
	err     error
}

var ( // ensure *errorT implements the helper interfaces.
	_ Namer  = (*errorT)(nil)
	_ Causer = (*errorT)(nil)
	_ error  = (*errorT)(nil)
)

// outerClass returns the outermost wrapping class of the error.
func (e *errorT) outerClass() *Class {
	if len(e.classes) == 0 {
		return nil
	}
	return e.classes[len(e.classes)-1]
}

// errorT implements the error interface.
func (e *errorT) Error() string {
	return fmt.Sprintf("%v", e)
}

// Format handles the formatting of the error. Using a "+" on the format string
// specifier will also write the stack trace.
func (e *errorT) Format(f fmt.State, c rune) {
	for i := len(e.classes) - 1; i >= 0; i-- {
		name := string(*e.classes[i])
		if len(name) > 0 {
			fmt.Fprintf(f, "%s: ", name)
		}
	}
	fmt.Fprintf(f, "%v", e.err)

	if f.Flag(int('+')) {
		summarizeStack(f, e.pcs)
	}
}

// Cause implements the interface wrapping errors are expected to implement
// to allow getting at underlying causes.
func (e *errorT) Cause() error {
	return e.err
}

// Name returns the name for the error, which is the first wrapping class.
func (e *errorT) Name() (string, bool) {
	outer := e.outerClass()
	if outer == nil {
		return "", false
	}
	return string(*outer), true
}

// summarizeStack writes stack line entries to the writer.
func summarizeStack(w io.Writer, pcs []uintptr) {
	frames := runtime.CallersFrames(pcs)
	for {
		frame, more := frames.Next()
		if !more {
			return
		}
		fmt.Fprintf(w, "\n\t%s:%d", frame.Function, frame.Line)
	}
}
<commit_msg>Do not print trailing column if no error message<commit_after>// Package errs provides a simple error package with stack traces.
package errs

import (
	"fmt"
	"io"
	"runtime"
)

// Namer is implemented by all errors returned in this package. It returns a
// name for the class of error it is, and a boolean indicating if the name is
// valid.
type Namer interface{ Name() (string, bool) }

// Causer is implemented by all errors returned in this package. It returns
// the underlying cause of the error, or nil if there is no underlying cause.
type Causer interface{ Cause() error }

// New returns an error not contained in any class. This is the same as calling
// fmt.Errorf(...) except it captures a stack trace on creation.
func New(format string, args ...interface{}) error {
	return (*Class).create(nil, 3, fmt.Errorf(format, args...))
}

// Wrap returns an error not contained in any class. It just associates a stack
// trace with the error. Wrap returns nil if err is nil.
func Wrap(err error) error {
	return (*Class).create(nil, 3, err)
}

// Unwrap returns the underlying error, if any, or just the error.
func Unwrap(err error) error {
	// we call Cause as much as possible. Since comparing arbitrary interfaces
	// with equality isn't panic safe, we only loop up to 100 times to ensure
	// that a poor implementation that loops does not cause a hang.
	for i := 0; err != nil && i < 100; i++ {
		causer, ok := err.(Causer)
		if !ok {
			break
		}

		// if the cause of some error is nil, we return it.
		nerr := causer.Cause()
		if nerr == nil {
			return err
		}
		err = nerr
	}

	return err
}

// Classes returns all the classes that have wrapped the error.
func Classes(err error) []*Class {
	if err, ok := err.(*errorT); ok && err != nil {
		return append([]*Class(nil), err.classes...)
	}
	return nil
}

//
// error classes
//

// Class represents a class of errors. You can construct errors, and check if
// errors are part of the class.
type Class string

// Has returns true if the passed in error was wrapped by this class.
func (c *Class) Has(err error) bool {
	if err, ok := err.(*errorT); ok {
		for _, k := range err.classes {
			if k == c {
				return true
			}
		}
	}
	return false
}

// New constructs an error with the format string that will be contained by
// this class. This is the same as calling Wrap(fmt.Errorf(...)).
func (c *Class) New(format string, args ...interface{}) error {
	return c.create(3, fmt.Errorf(format, args...))
}

// Wrap returns a new error based on the passed in error that is contained in
// this class. Wrap returns nil if err is nil.
func (c *Class) Wrap(err error) error {
	return c.create(3, err)
}

// create constructs the error, or just adds the class to the error, keeping
// track of the stack if it needs to construct it.
func (c *Class) create(depth int, err error) error {
	if err == nil {
		return nil
	}

	if err, ok := err.(*errorT); ok {
		if c != nil && err.outerClass() != c {
			err.classes = append(err.classes, c)
		}
		return err
	}

	var pcs [256]uintptr
	n := runtime.Callers(depth, pcs[:])

	var classes []*Class
	if c != nil {
		classes = []*Class{c}
	}

	return &errorT{
		classes: classes,
		err:     err,
		pcs:     pcs[:n:n],
	}
}

//
// errors
//

// errorT is the type of errors returned from this package.
type errorT struct {
	classes []*Class
	pcs     []uintptr
	err     error
}

var ( // ensure *errorT implements the helper interfaces.
	_ Namer  = (*errorT)(nil)
	_ Causer = (*errorT)(nil)
	_ error  = (*errorT)(nil)
)

// outerClass returns the outermost wrapping class of the error.
func (e *errorT) outerClass() *Class {
	if len(e.classes) == 0 {
		return nil
	}
	return e.classes[len(e.classes)-1]
}

// errorT implements the error interface.
func (e *errorT) Error() string {
	return fmt.Sprintf("%v", e)
}

// Format handles the formatting of the error. Using a "+" on the format string
// specifier will also write the stack trace.
func (e *errorT) Format(f fmt.State, c rune) {
	var printSeparator bool
	for i := len(e.classes) - 1; i >= 0; i-- {
		name := string(*e.classes[i])
		if len(name) > 0 {
			if printSeparator {
				fmt.Fprint(f, ": ")
			}
			printSeparator = true
			fmt.Fprint(f, name)
		}
	}
	if len(e.err.Error()) > 0 {
		if printSeparator {
			fmt.Fprint(f, ": ")
		}
		fmt.Fprintf(f, "%v", e.err)
	}

	if f.Flag(int('+')) {
		summarizeStack(f, e.pcs)
	}
}

// Cause implements the interface wrapping errors are expected to implement
// to allow getting at underlying causes.
func (e *errorT) Cause() error {
	return e.err
}

// Name returns the name for the error, which is the first wrapping class.
func (e *errorT) Name() (string, bool) {
	outer := e.outerClass()
	if outer == nil {
		return "", false
	}
	return string(*outer), true
}

// summarizeStack writes stack line entries to the writer.
func summarizeStack(w io.Writer, pcs []uintptr) {
	frames := runtime.CallersFrames(pcs)
	for {
		frame, more := frames.Next()
		if !more {
			return
		}
		fmt.Fprintf(w, "\n\t%s:%d", frame.Function, frame.Line)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"github.com/coreos/etcd/store"
	"github.com/coreos/etcd/web"
	"github.com/coreos/go-raft"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

//------------------------------------------------------------------------------
//
// Initialization
//
//------------------------------------------------------------------------------

var verbose bool

var machines string
var cluster []string

var address string
var clientPort int
var serverPort int
var webPort int

var serverCertFile string
var serverKeyFile string
var serverCAFile string

var clientCertFile string
var clientKeyFile string
var clientCAFile string

var dirPath string

var ignore bool

var maxSize int

func init() {
	flag.BoolVar(&verbose, "v", false, "verbose logging")

	flag.StringVar(&machines, "C", "", "the ip address and port of a existing machines in cluster, sepearate by comma")

	flag.StringVar(&address, "a", "0.0.0.0", "the ip address of the local machine")
	flag.IntVar(&clientPort, "c", 4001, "the port to communicate with clients")
	flag.IntVar(&serverPort, "s", 7001, "the port to communicate with servers")
	flag.IntVar(&webPort, "w", -1, "the port of web interface")

	flag.StringVar(&serverCAFile, "serverCAFile", "", "the path of the CAFile")
	flag.StringVar(&serverCertFile, "serverCert", "", "the cert file of the server")
	flag.StringVar(&serverKeyFile, "serverKey", "", "the key file of the server")

	flag.StringVar(&clientCAFile, "clientCAFile", "", "the path of the client CAFile")
	flag.StringVar(&clientCertFile, "clientCert", "", "the cert file of the client")
	flag.StringVar(&clientKeyFile, "clientKey", "", "the key file of the client")

	flag.StringVar(&dirPath, "d", "/tmp/", "the directory to store log and snapshot")

	flag.BoolVar(&ignore, "i", false, "ignore the old configuration, create a new node")

	flag.IntVar(&maxSize, "m", 1024, "the max size of result buffer")
}

// CONSTANTS
const (
	HTTP = iota
	HTTPS
	HTTPSANDVERIFY
)

const (
	SERVER = iota
	CLIENT
)

const (
	ELECTIONTIMTOUT  = 200 * time.Millisecond
	HEARTBEATTIMEOUT = 50 * time.Millisecond
)

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

type Info struct {
	Address    string `json:"address"`
	ServerPort int    `json:"serverPort"`
	ClientPort int    `json:"clientPort"`
	WebPort    int    `json:"webPort"`

	ServerCertFile string `json:"serverCertFile"`
	ServerKeyFile  string `json:"serverKeyFile"`
	ServerCAFile   string `json:"serverCAFile"`

	ClientCertFile string `json:"clientCertFile"`
	ClientKeyFile  string `json:"clientKeyFile"`
	ClientCAFile   string `json:"clientCAFile"`
}

//------------------------------------------------------------------------------
//
// Variables
//
//------------------------------------------------------------------------------

var raftServer *raft.Server
var raftTransporter transporter
var etcdStore *store.Store
var info *Info

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	flag.Parse()

	cluster = strings.Split(machines, ",")

	// Setup commands.
	registerCommands()

	// Read server info from file or grab it from user.
	if err := os.MkdirAll(dirPath, 0744); err != nil {
		fatal("Unable to create path: %v", err)
	}

	info = getInfo(dirPath)

	// secrity type
	st := securityType(SERVER)

	clientSt := securityType(CLIENT)

	if st == -1 || clientSt == -1 {
		fatal("Please specify cert and key file or cert and key file and CAFile or none of the three")
	}

	// Create etcd key-value store
	etcdStore = store.CreateStore(maxSize)

	startRaft(st)

	if webPort != -1 {
		// start web
		etcdStore.SetMessager(&storeMsg)
		go webHelper()
		go web.Start(raftServer, webPort)
	}

	startClientTransport(info.ClientPort, clientSt)

}

// Start the raft server
func startRaft(securityType int) {
	var err error

	raftName := fmt.Sprintf("%s:%d", info.Address, info.ServerPort)

	// Create transporter for raft
	raftTransporter = createTransporter(securityType)

	// Create raft server
	raftServer, err = raft.NewServer(raftName, dirPath, raftTransporter, etcdStore, nil)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// LoadSnapshot
	// err = raftServer.LoadSnapshot()

	// if err == nil {
	// 	debug("%s finished load snapshot", raftServer.Name())
	// } else {
	// 	debug(err)
	// }

	raftServer.Initialize()
	raftServer.SetElectionTimeout(ELECTIONTIMTOUT)
	raftServer.SetHeartbeatTimeout(HEARTBEATTIMEOUT)

	if raftServer.IsLogEmpty() {

		// start as a leader in a new cluster
		if len(cluster) == 0 {
			raftServer.StartLeader()

			time.Sleep(time.Millisecond * 20)

			// leader need to join self as a peer
			for {
				command := &JoinCommand{}
				command.Name = raftServer.Name()
				_, err := raftServer.Do(command)
				if err == nil {
					break
				}
			}
			debug("%s start as a leader", raftServer.Name())

			// start as a follower in a existing cluster
		} else {
			raftServer.StartFollower()

			for _, machine := range cluster {

				err := joinCluster(raftServer, machine)
				if err != nil {
					debug("cannot join to cluster via machine %s", machine)
				} else {
					break
				}
			}
			if err != nil {
				fatal("cannot join to cluster via all given machines!")
			}
			debug("%s success join to the cluster", raftServer.Name())
		}

	} else {
		// rejoin the previous cluster
		raftServer.StartFollower()
		debug("%s restart as a follower", raftServer.Name())
	}

	// open the snapshot
	// go server.Snapshot()

	// start to response to raft requests
	go startRaftTransport(info.ServerPort, securityType)

}

// Create transporter using by raft server
// Create http or https transporter based on
// wether the user give the server cert and key
func createTransporter(st int) transporter {
	t := transporter{}

	switch st {
	case HTTP:
		t.client = nil
		return t

	case HTTPS:
		fallthrough
	case HTTPSANDVERIFY:
		tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)

		if err != nil {
			fatal(fmt.Sprintln(err))
		}

		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				Certificates:       []tls.Certificate{tlsCert},
				InsecureSkipVerify: true,
			},
			DisableCompression: true,
		}

		t.client = &http.Client{Transport: tr}
		return t
	}

	// for complier
	return transporter{}
}

// Start to listen and response raft command
func startRaftTransport(port int, st int) {

	// internal commands
	http.HandleFunc("/join", JoinHttpHandler)
	http.HandleFunc("/vote", VoteHttpHandler)
	http.HandleFunc("/log", GetLogHttpHandler)
	http.HandleFunc("/log/append", AppendEntriesHttpHandler)
	http.HandleFunc("/snapshot", SnapshotHttpHandler)
	http.HandleFunc("/client", ClientHttpHandler)

	switch st {

	case HTTP:
		fmt.Printf("raft server [%s] listen on http port %v\n", address, port)
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		fmt.Printf("raft server [%s] listen on https port %v\n", address, port)
		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil))

	case HTTPSANDVERIFY:

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  createCertPool(serverCAFile),
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		fmt.Printf("raft server [%s] listen on https port %v\n", address, port)
		err := server.ListenAndServeTLS(serverCertFile, serverKeyFile)

		if err != nil {
			log.Fatal(err)
		}
	}

}

// Start to listen and response client command
func startClientTransport(port int, st int) {
	// external commands
	http.HandleFunc("/"+version+"/keys/", Multiplexer)
	http.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
	http.HandleFunc("/"+version+"/list/", ListHttpHandler)
	http.HandleFunc("/"+version+"/testAndSet/", TestAndSetHttpHandler)
	http.HandleFunc("/leader", LeaderHttpHandler)

	switch st {

	case HTTP:
		fmt.Printf("etcd [%s] listen on http port %v\n", address, clientPort)
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		fmt.Printf("etcd [%s] listen on https port %v\n", address, clientPort)
		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)

	case HTTPSANDVERIFY:

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  createCertPool(clientCAFile),
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		fmt.Printf("etcd [%s] listen on https port %v\n", address, clientPort)
		err := server.ListenAndServeTLS(clientCertFile, clientKeyFile)

		if err != nil {
			log.Fatal(err)
			os.Exit(1)
		}
	}
}

//--------------------------------------
// Config
//--------------------------------------

// Get the security type
func securityType(source int) int {

	var keyFile, certFile, CAFile string

	switch source {

	case SERVER:
		keyFile = info.ServerKeyFile
		certFile = info.ServerCertFile
		CAFile = info.ServerCAFile

	case CLIENT:
		keyFile = info.ClientKeyFile
		certFile = info.ClientCertFile
		CAFile = info.ClientCAFile
	}

	// If the user do not specify key file, cert file and
	// CA file, the type will be HTTP
	if keyFile == "" && certFile == "" && CAFile == "" {

		return HTTP

	}

	if keyFile != "" && certFile != "" {
		if CAFile != "" {
			// If the user specify all the three file, the type
			// will be HTTPS with client cert auth
			return HTTPSANDVERIFY
		}
		// If the user specify key file and cert file but not
		// CA file, the type will be HTTPS without client cert
		// auth
		return HTTPS
	}

	// bad specification
	return -1
}

// Get the server info from previous conf file
// or from the user
func getInfo(path string) *Info {
	info := &Info{}

	// Read in the server info if available.
	infoPath := fmt.Sprintf("%s/info", path)

	// Delete the old configuration if exist
	if ignore {

		logPath := fmt.Sprintf("%s/log", path)
		snapshotPath := fmt.Sprintf("%s/snapshotPath", path)
		os.Remove(infoPath)
		os.Remove(logPath)
		os.RemoveAll(snapshotPath)

	}

	if file, err := os.Open(infoPath); err == nil {
		if content, err := ioutil.ReadAll(file); err != nil {
			fatal("Unable to read info: %v", err)
		} else {
			if err = json.Unmarshal(content, &info); err != nil {
				fatal("Unable to parse info: %v", err)
			}
		}
		file.Close()

	} else {
		// Otherwise ask user for info and write it to file.

		if address == "" {
			fatal("Please give the address of the local machine")
		}

		info.Address = address
		info.Address = strings.TrimSpace(info.Address)
		fmt.Println("address ", info.Address)

		info.ServerPort = serverPort
		info.ClientPort = clientPort
		info.WebPort = webPort

		info.ClientCAFile = clientCAFile
		info.ClientCertFile = clientCertFile
		info.ClientKeyFile = clientKeyFile

		info.ServerCAFile = serverCAFile
		info.ServerKeyFile = serverKeyFile
		info.ServerCertFile = serverCertFile

		// Write to file.
		content, _ := json.Marshal(info)
		content = []byte(string(content) + "\n")
		if err := ioutil.WriteFile(infoPath, content, 0644); err != nil {
			fatal("Unable to write info to file: %v", err)
		}
	}

	return info
}

// Create client auth certpool
func createCertPool(CAFile string) *x509.CertPool {
	pemByte, _ := ioutil.ReadFile(CAFile)

	block, pemByte := pem.Decode(pemByte)

	cert, err := x509.ParseCertificate(block.Bytes)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	certPool := x509.NewCertPool()

	certPool.AddCert(cert)

	return certPool
}

// Send join requests to the leader.
func joinCluster(s *raft.Server, serverName string) error {
	var b bytes.Buffer

	command := &JoinCommand{}
	command.Name = s.Name()

	json.NewEncoder(&b).Encode(command)

	// t must be ok
	t, ok := raftServer.Transporter().(transporter)

	if !ok {
		panic("wrong type")
	}

	debug("Send Join Request to %s", serverName)

	resp, err := t.Post(fmt.Sprintf("%s/join", serverName), &b)

	debug("Finish Join Request to %s", serverName)

	for {
		fmt.Println(err, resp)
		if err != nil {
			return fmt.Errorf("Unable to join: %v", err)
		}
		if resp != nil {
			defer resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				return nil
			}

			if resp.StatusCode == http.StatusTemporaryRedirect {
				fmt.Println("redirect")
				address = resp.Header.Get("Location")
				debug("Leader is %s", address)
				debug("Send Join Request to %s", address)
				json.NewEncoder(&b).Encode(command)
				resp, err = t.Post(fmt.Sprintf("%s/join", address), &b)
			}
		}

	}
	return fmt.Errorf("Unable to join: %v", err)
}

// Register commands to raft server
func registerCommands() {
	raft.RegisterCommand(&JoinCommand{})
	raft.RegisterCommand(&SetCommand{})
	raft.RegisterCommand(&GetCommand{})
	raft.RegisterCommand(&DeleteCommand{})
	raft.RegisterCommand(&WatchCommand{})
	raft.RegisterCommand(&ListCommand{})
	raft.RegisterCommand(&TestAndSetCommand{})
}
<commit_msg>clean up prints and fix len problem<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"github.com/coreos/etcd/store"
	"github.com/coreos/etcd/web"
	"github.com/coreos/go-raft"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

//------------------------------------------------------------------------------
//
// Initialization
//
//------------------------------------------------------------------------------

var verbose bool

var machines string
var cluster []string

var address string
var clientPort int
var serverPort int
var webPort int

var serverCertFile string
var serverKeyFile string
var serverCAFile string

var clientCertFile string
var clientKeyFile string
var clientCAFile string

var dirPath string

var ignore bool

var maxSize int

func init() {
	flag.BoolVar(&verbose, "v", false, "verbose logging")

	flag.StringVar(&machines, "C", "", "the ip address and port of a existing machines in cluster, sepearate by comma")

	flag.StringVar(&address, "a", "0.0.0.0", "the ip address of the local machine")
	flag.IntVar(&clientPort, "c", 4001, "the port to communicate with clients")
	flag.IntVar(&serverPort, "s", 7001, "the port to communicate with servers")
	flag.IntVar(&webPort, "w", -1, "the port of web interface")

	flag.StringVar(&serverCAFile, "serverCAFile", "", "the path of the CAFile")
	flag.StringVar(&serverCertFile, "serverCert", "", "the cert file of the server")
	flag.StringVar(&serverKeyFile, "serverKey", "", "the key file of the server")

	flag.StringVar(&clientCAFile, "clientCAFile", "", "the path of the client CAFile")
	flag.StringVar(&clientCertFile, "clientCert", "", "the cert file of the client")
	flag.StringVar(&clientKeyFile, "clientKey", "", "the key file of the client")

	flag.StringVar(&dirPath, "d", "/tmp/", "the directory to store log and snapshot")

	flag.BoolVar(&ignore, "i", false, "ignore the old configuration, create a new node")

	flag.IntVar(&maxSize, "m", 1024, "the max size of result buffer")
}

// CONSTANTS
const (
	HTTP = iota
	HTTPS
	HTTPSANDVERIFY
)

const (
	SERVER = iota
	CLIENT
)

const (
	ELECTIONTIMTOUT  = 200 * time.Millisecond
	HEARTBEATTIMEOUT = 50 * time.Millisecond
)

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

type Info struct {
	Address    string `json:"address"`
	ServerPort int    `json:"serverPort"`
	ClientPort int    `json:"clientPort"`
	WebPort    int    `json:"webPort"`

	ServerCertFile string `json:"serverCertFile"`
	ServerKeyFile  string `json:"serverKeyFile"`
	ServerCAFile   string `json:"serverCAFile"`

	ClientCertFile string `json:"clientCertFile"`
	ClientKeyFile  string `json:"clientKeyFile"`
	ClientCAFile   string `json:"clientCAFile"`
}

//------------------------------------------------------------------------------
//
// Variables
//
//------------------------------------------------------------------------------

var raftServer *raft.Server
var raftTransporter transporter
var etcdStore *store.Store
var info *Info

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	flag.Parse()

	cluster = strings.Split(machines, ",")

	// Setup commands.
	registerCommands()

	// Read server info from file or grab it from user.
	if err := os.MkdirAll(dirPath, 0744); err != nil {
		fatal("Unable to create path: %v", err)
	}

	info = getInfo(dirPath)

	// secrity type
	st := securityType(SERVER)

	clientSt := securityType(CLIENT)

	if st == -1 || clientSt == -1 {
		fatal("Please specify cert and key file or cert and key file and CAFile or none of the three")
	}

	// Create etcd key-value store
	etcdStore = store.CreateStore(maxSize)

	startRaft(st)

	if webPort != -1 {
		// start web
		etcdStore.SetMessager(&storeMsg)
		go webHelper()
		go web.Start(raftServer, webPort)
	}

	startClientTransport(info.ClientPort, clientSt)

}

// Start the raft server
func startRaft(securityType int) {
	var err error

	raftName := fmt.Sprintf("%s:%d", info.Address, info.ServerPort)

	// Create transporter for raft
	raftTransporter = createTransporter(securityType)

	// Create raft server
	raftServer, err = raft.NewServer(raftName, dirPath, raftTransporter, etcdStore, nil)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// LoadSnapshot
	// err = raftServer.LoadSnapshot()

	// if err == nil {
	// 	debug("%s finished load snapshot", raftServer.Name())
	// } else {
	// 	debug(err)
	// }

	raftServer.Initialize()
	raftServer.SetElectionTimeout(ELECTIONTIMTOUT)
	raftServer.SetHeartbeatTimeout(HEARTBEATTIMEOUT)

	if raftServer.IsLogEmpty() {

		// start as a leader in a new cluster
		if len(cluster) == 1 && cluster[0] == "" {
			raftServer.StartLeader()

			time.Sleep(time.Millisecond * 20)

			// leader need to join self as a peer
			for {
				command := &JoinCommand{}
				command.Name = raftServer.Name()
				_, err := raftServer.Do(command)
				if err == nil {
					break
				}
			}
			debug("%s start as a leader", raftServer.Name())

			// start as a follower in a existing cluster
		} else {
			raftServer.StartFollower()

			for _, machine := range cluster {

				err := joinCluster(raftServer, machine)
				if err != nil {
					debug("cannot join to cluster via machine %s", machine)
				} else {
					break
				}
			}
			if err != nil {
				fatal("cannot join to cluster via all given machines!")
			}
			debug("%s success join to the cluster", raftServer.Name())
		}

	} else {
		// rejoin the previous cluster
		raftServer.StartFollower()
		debug("%s restart as a follower", raftServer.Name())
	}

	// open the snapshot
	// go server.Snapshot()

	// start to response to raft requests
	go startRaftTransport(info.ServerPort, securityType)

}

// Create transporter using by raft server
// Create http or https transporter based on
// wether the user give the server cert and key
func createTransporter(st int) transporter {
	t := transporter{}

	switch st {
	case HTTP:
		t.client = nil
		return t

	case HTTPS:
		fallthrough
	case HTTPSANDVERIFY:
		tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)

		if err != nil {
			fatal(fmt.Sprintln(err))
		}

		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				Certificates:       []tls.Certificate{tlsCert},
				InsecureSkipVerify: true,
			},
			DisableCompression: true,
		}

		t.client = &http.Client{Transport: tr}
		return t
	}

	// for complier
	return transporter{}
}

// Start to listen and response raft command
func startRaftTransport(port int, st int) {

	// internal commands
	http.HandleFunc("/join", JoinHttpHandler)
	http.HandleFunc("/vote", VoteHttpHandler)
	http.HandleFunc("/log", GetLogHttpHandler)
	http.HandleFunc("/log/append", AppendEntriesHttpHandler)
	http.HandleFunc("/snapshot", SnapshotHttpHandler)
	http.HandleFunc("/client", ClientHttpHandler)

	switch st {

	case HTTP:
		fmt.Printf("raft server [%s] listen on http port %v\n", address, port)
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		fmt.Printf("raft server [%s] listen on https port %v\n", address, port)
		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%d", port), serverCertFile, serverKeyFile, nil))

	case HTTPSANDVERIFY:

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  createCertPool(serverCAFile),
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		fmt.Printf("raft server [%s] listen on https port %v\n", address, port)
		err := server.ListenAndServeTLS(serverCertFile, serverKeyFile)

		if err != nil {
			log.Fatal(err)
		}
	}

}

// Start to listen and response client command
func startClientTransport(port int, st int) {
	// external commands
	http.HandleFunc("/"+version+"/keys/", Multiplexer)
	http.HandleFunc("/"+version+"/watch/", WatchHttpHandler)
	http.HandleFunc("/"+version+"/list/", ListHttpHandler)
	http.HandleFunc("/"+version+"/testAndSet/", TestAndSetHttpHandler)
	http.HandleFunc("/leader", LeaderHttpHandler)

	switch st {

	case HTTP:
		fmt.Printf("etcd [%s] listen on http port %v\n", address, clientPort)
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))

	case HTTPS:
		fmt.Printf("etcd [%s] listen on https port %v\n", address, clientPort)
		http.ListenAndServeTLS(fmt.Sprintf(":%d", port), clientCertFile, clientKeyFile, nil)

	case HTTPSANDVERIFY:

		server := &http.Server{
			TLSConfig: &tls.Config{
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  createCertPool(clientCAFile),
			},
			Addr: fmt.Sprintf(":%d", port),
		}
		fmt.Printf("etcd [%s] listen on https port %v\n", address, clientPort)
		err := server.ListenAndServeTLS(clientCertFile, clientKeyFile)

		if err != nil {
			log.Fatal(err)
			os.Exit(1)
		}
	}
}

//--------------------------------------
// Config
//--------------------------------------

// Get the security type
func securityType(source int) int {

	var keyFile, certFile, CAFile string

	switch source {

	case SERVER:
		keyFile = info.ServerKeyFile
		certFile = info.ServerCertFile
		CAFile = info.ServerCAFile

	case CLIENT:
		keyFile = info.ClientKeyFile
		certFile = info.ClientCertFile
		CAFile = info.ClientCAFile
	}

	// If the user do not specify key file, cert file and
	// CA file, the type will be HTTP
	if keyFile == "" && certFile == "" && CAFile == "" {

		return HTTP

	}

	if keyFile != "" && certFile != "" {
		if CAFile != "" {
			// If the user specify all the three file, the type
			// will be HTTPS with client cert auth
			return HTTPSANDVERIFY
		}
		// If the user specify key file and cert file but not
		// CA file, the type will be HTTPS without client cert
		// auth
		return HTTPS
	}

	// bad specification
	return -1
}

// Get the server info from previous conf file
// or from the user
func getInfo(path string) *Info {
	info := &Info{}

	// Read in the server info if available.
	infoPath := fmt.Sprintf("%s/info", path)

	// Delete the old configuration if exist
	if ignore {

		logPath := fmt.Sprintf("%s/log", path)
		snapshotPath := fmt.Sprintf("%s/snapshotPath", path)
		os.Remove(infoPath)
		os.Remove(logPath)
		os.RemoveAll(snapshotPath)

	}

	if file, err := os.Open(infoPath); err == nil {
		if content, err := ioutil.ReadAll(file); err != nil {
			fatal("Unable to read info: %v", err)
		} else {
			if err = json.Unmarshal(content, &info); err != nil {
				fatal("Unable to parse info: %v", err)
			}
		}
		file.Close()

	} else {
		// Otherwise ask user for info and write it to file.

		if address == "" {
			fatal("Please give the address of the local machine")
		}

		info.Address = address
		info.Address = strings.TrimSpace(info.Address)
		fmt.Println("address ", info.Address)

		info.ServerPort = serverPort
		info.ClientPort = clientPort
		info.WebPort = webPort

		info.ClientCAFile = clientCAFile
		info.ClientCertFile = clientCertFile
		info.ClientKeyFile = clientKeyFile

		info.ServerCAFile = serverCAFile
		info.ServerKeyFile = serverKeyFile
		info.ServerCertFile = serverCertFile

		// Write to file.
		content, _ := json.Marshal(info)
		content = []byte(string(content) + "\n")
		if err := ioutil.WriteFile(infoPath, content, 0644); err != nil {
			fatal("Unable to write info to file: %v", err)
		}
	}

	return info
}

// Create client auth certpool
func createCertPool(CAFile string) *x509.CertPool {
	pemByte, _ := ioutil.ReadFile(CAFile)

	block, pemByte := pem.Decode(pemByte)

	cert, err := x509.ParseCertificate(block.Bytes)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	certPool := x509.NewCertPool()

	certPool.AddCert(cert)

	return certPool
}

// Send join requests to the leader.
func joinCluster(s *raft.Server, serverName string) error {
	var b bytes.Buffer

	command := &JoinCommand{}
	command.Name = s.Name()

	json.NewEncoder(&b).Encode(command)

	// t must be ok
	t, ok := raftServer.Transporter().(transporter)

	if !ok {
		panic("wrong type")
	}

	debug("Send Join Request to %s", serverName)

	resp, err := t.Post(fmt.Sprintf("%s/join", serverName), &b)

	for {
		if err != nil {
			return fmt.Errorf("Unable to join: %v", err)
		}
		if resp != nil {
			defer resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				return nil
			}
			if resp.StatusCode == http.StatusTemporaryRedirect {
				address = resp.Header.Get("Location")
				debug("Leader is %s", address)
				debug("Send Join Request to %s", address)
				json.NewEncoder(&b).Encode(command)
				resp, err = t.Post(fmt.Sprintf("%s/join", address), &b)
			} else {
				return fmt.Errorf("Unable to join")
			}
		}

	}
	return fmt.Errorf("Unable to join: %v", err)
}

// Register commands to raft server
func registerCommands() {
	raft.RegisterCommand(&JoinCommand{})
	raft.RegisterCommand(&SetCommand{})
	raft.RegisterCommand(&GetCommand{})
	raft.RegisterCommand(&DeleteCommand{})
	raft.RegisterCommand(&WatchCommand{})
	raft.RegisterCommand(&ListCommand{})
	raft.RegisterCommand(&TestAndSetCommand{})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
)

type value sexpr
type scope map[string]value
var global scope

func (v value) String() string {
	switch v.kind {
	case _ATOM:
		return v.data.(atom).String()
	case _CONS:
		return v.data.(cons).String()
	}
	panic("Reached the unreachable") // XXX
}

func (v cons) String() string {
	return "<cons>"
}

func (v atom) String() string {
	switch v.kind {
	case _NUMBER:
		return strconv.Ftoa64(v.data.(float64), 'G', -1)
	case _STRING:
		return fmt.Sprintf("\"%s\"", v.data.(string))
	case _NIL:
		return fmt.Sprintf("nil")
	case _SYMBOL:
		return v.data.(string)
	}
	return ""
}

func doEval(c chan sexpr) {
	for e := range c {
		fmt.Printf("%s\n", eval(e))
	}
}

func eval(e sexpr) value {
	// TODO perform syntax transformations
	switch e.kind {
	case _CONS:
		cons := e.data.(cons)
		car := eval(cons.car)
		cdr := cons.cdr
		// TODO
		fmt.Printf("(%s %s)\n", car, cdr)
		return car
	case _ATOM:
		a := e.data.(atom)
		switch a.kind {
		case _SYMBOL:
			return lookup(a.data.(string))
		case _NUMBER:
			return value(e)
		case _STRING:
			return value(e)
		case _NIL:
			return value(e)
		default:
			panic("Invalid kind of atom")
		}
	}
	panic("Invalid kind of sexpr")
}

func lookup(sym string) value {
	// TODO attempt to lookup in reflect

	v, ok := global[sym]
	if ok {
		return v
	}
	panic("undefined")
}
<commit_msg>Comments<commit_after>package main

import (
	"fmt"
	"strconv"
)

type value sexpr
type scope map[string]value
var global scope

func (v value) String() string {
	switch v.kind {
	case _ATOM:
		return v.data.(atom).String()
	case _CONS:
		return v.data.(cons).String()
	}
	panic("Reached the unreachable") // XXX
}

func (v cons) String() string {
	return "<cons>"
}

func (v atom) String() string {
	switch v.kind {
	case _NUMBER:
		return strconv.Ftoa64(v.data.(float64), 'G', -1)
	case _STRING:
		return fmt.Sprintf("\"%s\"", v.data.(string))
	case _NIL:
		return fmt.Sprintf("nil")
	case _SYMBOL:
		return v.data.(string)
	}
	return ""
}

func doEval(c chan sexpr) {
	for e := range c {
		fmt.Printf("%s\n", eval(e))
	}
}

// Evaluates an s-expression, including syntax transformations (macros).
func eval(e sexpr) value {
	// TODO perform syntax transformations
	switch e.kind {
	case _CONS:
		cons := e.data.(cons)
		car := eval(cons.car)
		cdr := cons.cdr
		// TODO
		fmt.Printf("(%s %s)\n", car, cdr)
		return car
	case _ATOM:
		a := e.data.(atom)
		switch a.kind {
		case _SYMBOL:
			return lookup(a.data.(string))
		case _NUMBER:
			return value(e)
		case _STRING:
			return value(e)
		case _NIL:
			return value(e)
		default:
			panic("Invalid kind of atom")
		}
	}
	panic("Invalid kind of sexpr")
}

// Performs lookup of symbols for values.
func lookup(sym string) value {
	// TODO attempt to lookup in reflect

	v, ok := global[sym]
	if ok {
		return v
	}
	panic("undefined")
}
<|endoftext|>


<commit_before>package uniltc

// order type
const (
	Bid = "bid"
	Ask = "ask"
)

// candle stick type
const (
	C1m = "1m"
	C5m = "5m"
	C1h = "1h"
	C1d = "1d"
)

// trading pairs
const (
	Pair_LTC_BTC = "LTC_BTC"
	Pair_LTC_USD = "LTC_USD"
	Pair_LTC_EUR = "LTC_EUR"
)

var (
	Stub = new(stub)
)

// divisions
const (
	Div_Total  = 1e8
	Div_Volume = 1e4
	Div_Rate   = 1e4
)

// Noted:
// Timestamp is the ticker last updated
// Last, High, Low, Buy, Sell, Volume, Average are equal to the real value * division
type ticker struct {
	Timestamp                                   int64
	Last, High, Low, Buy, Sell, Volume, Average float64
}

type history struct {
	Timestamp           int64
	Type                string
	Rate, Volume, Total float64
}

type order struct {
	OrderId             int64
	OrderType           string
	Rate, Volume, Total float64
}

type depth struct {
	Timestamp int64
	OrderBook map[string][]order
}

type candleStick struct {
	Timestamp                                                 int64
	Open, High, Low, Close, BidVolume, AskVolume, TotalVolume float64
}

type stub struct {
	// public api
	Ticker        func(pair string) (ticker, error)
	LastTrade     func(pair string) (history, error)
	OrderBook     func(pair string) (depth, error)
	OrderBookTopN func(pair string, n int) (depth, error)
	History       func(pair string, n int) ([]history, error)
	HistorySince  func(pair string, since int64) ([]history, error)
	CandleStick   func(pair string, candleStickType string) ([]candleStick, error)

	// private api
	ApiNewBidOrder    func(api_key string, res string, pair string, rate int64, amount int64) ([]order, interface{}, error)
	ApiNewAskOrder    func(api_key string, res string, pair string, rate int64, amount int64) ([]order, interface{}, error)
	ApiCancelBidOrder func(api_key string, res string, pair string, orderId int64) error
	ApiCancelAskOrder func(api_key string, res string, pair string, orderId int64) error
	ApiCurrentOrders  func(api_key string, res string, pair string) (map[string][]order, error)
	ApiHistoryTrades  func(api_key string, res string, pair string) ([]history, error)
}
<commit_msg>bug fixed<commit_after>package uniltc

// order type
const (
	Bid = "bid"
	Ask = "ask"
)

// candle stick type
const (
	C1m = "1m"
	C5m = "5m"
	C1h = "1h"
	C1d = "1d"
)

// trading pairs
const (
	Pair_LTC_BTC = "LTC_BTC"
	Pair_LTC_USD = "LTC_USD"
	Pair_LTC_EUR = "LTC_EUR"
)

var (
	Stub = new(stub)
)

// divisions
const (
	Div_Total  = 1e8
	Div_Volume = 1e4
	Div_Rate   = 1e4
)

// Noted:
// Timestamp is the ticker last updated
// Last, High, Low, Buy, Sell, Volume, Average are equal to the real value * division
type ticker struct {
	Timestamp                                   int64
	Last, High, Low, Buy, Sell, Volume, Average float64
}

type history struct {
	Timestamp           int64
	Type                string
	Rate, Volume, Total float64
}

type Order struct {
	OrderId             int64
	OrderType           string
	Rate, Volume, Total float64
}

type depth struct {
	Timestamp int64
	Depth     map[string][]Order
}

type candleStick struct {
	Timestamp                                                 int64
	Open, High, Low, Close, BidVolume, AskVolume, TotalVolume float64
}

type stub struct {
	// public api
	Ticker        func(pair string) (ticker, error)
	LastTrade     func(pair string) (history, error)
	OrderBook     func(pair string) (depth, error)
	OrderBookTopN func(pair string, n int) (depth, error)
	History       func(pair string, n int) ([]history, error)
	HistorySince  func(pair string, since int64) ([]history, error)
	CandleStick   func(pair string, candleStickType string) ([]candleStick, error)

	// private api
	ApiNewBidOrder    func(api_key string, res string, pair string, rate int64, amount int64) ([]Order, interface{}, error)
	ApiNewAskOrder    func(api_key string, res string, pair string, rate int64, amount int64) ([]Order, interface{}, error)
	ApiCancelBidOrder func(api_key string, res string, pair string, orderId int64) error
	ApiCancelAskOrder func(api_key string, res string, pair string, orderId int64) error
	ApiCurrentOrders  func(api_key string, res string, pair string) (map[string][]Order, error)
	ApiHistoryTrades  func(api_key string, res string, pair string) ([]history, error)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/exec"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
)

type ExecCommandInput struct {
	Profile  string
	Command  string
	Args     []string
	Keyring  keyring.Keyring
	Duration time.Duration
}

func ExecCommand(ui Ui, input ExecCommandInput) {
	provider, err := NewVaultProvider(input.Keyring, input.Profile, input.Duration)
	if err != nil {
		ui.Error.Fatal(err)
	}

	creds := credentials.NewCredentials(provider)
	val, err := creds.Get()
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
			ui.Error.Fatalf("No credentials found for profile %q", input.Profile)
		} else {
			ui.Error.Fatal(err)
		}
	}

	env := append(os.Environ(),
		"AWS_ACCESS_KEY_ID="+val.AccessKeyID,
		"AWS_SECRET_ACCESS_KEY="+val.SecretAccessKey,
	)

	if val.SessionToken != "" {
		env = append(env, "AWS_SESSION_TOKEN="+val.SessionToken)
	}

	cmd := exec.Command(input.Command, input.Args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = &logWriter{ui.Logger}
	cmd.Stderr = &logWriter{ui.Error}

	if err := cmd.Run(); err != nil {
		ui.Error.Fatal(err)
	}
}
<commit_msg>Set AWS_DEFAULT_PROFILE in subshell of exec<commit_after>package main

import (
	"os"
	"os/exec"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
)

type ExecCommandInput struct {
	Profile  string
	Command  string
	Args     []string
	Keyring  keyring.Keyring
	Duration time.Duration
}

func ExecCommand(ui Ui, input ExecCommandInput) {
	provider, err := NewVaultProvider(input.Keyring, input.Profile, input.Duration)
	if err != nil {
		ui.Error.Fatal(err)
	}

	creds := credentials.NewCredentials(provider)
	val, err := creds.Get()
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
			ui.Error.Fatalf("No credentials found for profile %q", input.Profile)
		} else {
			ui.Error.Fatal(err)
		}
	}

	env := append(os.Environ(),
		"AWS_ACCESS_KEY_ID="+val.AccessKeyID,
		"AWS_SECRET_ACCESS_KEY="+val.SecretAccessKey,
		"AWS_DEFAULT_PROFILE="+input.Profile,
	)

	if val.SessionToken != "" {
		env = append(env, "AWS_SESSION_TOKEN="+val.SessionToken)
	}

	cmd := exec.Command(input.Command, input.Args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = &logWriter{ui.Logger}
	cmd.Stderr = &logWriter{ui.Error}

	if err := cmd.Run(); err != nil {
		ui.Error.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"

	. "github.com/russross/codegrinder/common"
	"github.com/spf13/cobra"
)

func CommandList(cmd *cobra.Command, args []string) {
	mustLoadConfig(cmd)

	user := new(User)
	mustGetObject("/users/me", nil, user)
	assignments := []*Assignment{}
	mustGetObject(fmt.Sprintf("/users/%d/assignments", user.ID), nil, &assignments)
	if len(assignments) == 0 {
		log.Printf("no assignments found")
		log.Fatalf("you must start each assignment through Canvas before you can access it here")
	}

	var course *Course
	for _, asst := range assignments {
		if course == nil || asst.CourseID != course.ID {
			if course != nil {
				fmt.Println()
			}

			// fetch the course
			course = new(Course)
			mustGetObject(fmt.Sprintf("/courses/%d", asst.CourseID), nil, course)
			fmt.Println(course.Name)
			fmt.Println(dashes(len(course.Name)))
		}

		// fetch the problem
		problemSet := new(ProblemSet)
		mustGetObject(fmt.Sprintf("/problem_sets/%d", asst.ProblemSetID), nil, problemSet)
		fmt.Printf("id:%d %s (%s/%s)\n", asst.ID, asst.CanvasTitle, course.Label, problemSet.Unique)
	}
}

func dashes(n int) string {
	s := ""
	for i := 0; i < n; i++ {
		s += "-"
	}
	return s
}
<commit_msg>align columns in assignment lists<commit_after>package main

import (
	"fmt"
	"log"
	"strconv"

	. "github.com/russross/codegrinder/common"
	"github.com/spf13/cobra"
)

func CommandList(cmd *cobra.Command, args []string) {
	mustLoadConfig(cmd)

	user := new(User)
	mustGetObject("/users/me", nil, user)
	assignments := []*Assignment{}
	mustGetObject(fmt.Sprintf("/users/%d/assignments", user.ID), nil, &assignments)
	if len(assignments) == 0 {
		log.Printf("no assignments found")
		log.Fatalf("you must start each assignment through Canvas before you can access it here")
	}

	var course *Course

	// find the longest assignment ID, name
	longestID, longestName := 1, 1
	for _, asst := range assignments {
		if n := len(strconv.FormatInt(asst.ID, 10)); n > longestID {
			longestID = n
		}
		if n := len(asst.CanvasTitle); n > longestName {
			longestName = n
		}
	}
	for _, asst := range assignments {
		if course == nil || asst.CourseID != course.ID {
			if course != nil {
				fmt.Println()
			}

			// fetch the course
			course = new(Course)
			mustGetObject(fmt.Sprintf("/courses/%d", asst.CourseID), nil, course)
			fmt.Println(course.Name)
			fmt.Println(dashes(len(course.Name)))
		}

		// fetch the problem
		problemSet := new(ProblemSet)
		mustGetObject(fmt.Sprintf("/problem_sets/%d", asst.ProblemSetID), nil, problemSet)
		fmt.Printf("id:%-*d %-*s (%s/%s)\n", longestID, asst.ID, longestName, asst.CanvasTitle, course.Label, problemSet.Unique)
	}
}

func dashes(n int) string {
	s := ""
	for i := 0; i < n; i++ {
		s += "-"
	}
	return s
}
<|endoftext|>