<commit_before>package alioss

import (
    "testing"
    "path"
    "os"
    "log"
    "time"
    "math/rand"
    "fmt"
    "github.com/kardianos/osext"
    "path/filepath"
    "github.com/aliyun/aliyun-oss-go-sdk/oss"
)

var AliRegion string
var AliBucket string
var AliKeyId string
var AliSecretKey string

func init() {
    AliRegion = os.Getenv("ALI_REGION")
    AliBucket = os.Getenv("ALI_BUCKET")
    AliKeyId = os.Getenv("ALI_ACCESS_KEY_ID")
    AliSecretKey = os.Getenv("ALI_SECRET_ACCESS_KEY")

}

func TestResumeUpload(t *testing.T) {
    if AliRegion == "" || AliBucket == "" || AliKeyId == "" || AliSecretKey == "" {
		t.Fatal("Environment variables ALI_REGION or ALI_BUCKET or ALI_ACCESS_KEY_ID or ALI_SECRET_ACCESS_KEY are not defined")
	}

	ali, err := oss.New(AliRegion, AliKeyId, AliSecretKey)
	if err != nil {
		t.Fatalf("Failed to create OSS client: %s", err)
	}

	aliSvc := AliOss{
		Log:    log.New(os.Stdout, "testing: ", log.LstdFlags),
		Svc:    ali,
		Region: AliRegion,
		Bucket: AliBucket,
	}

    bucket, err := aliSvc.Svc.Bucket(aliSvc.Bucket)
    if err != nil {
		t.Fatalf("Failed to instant bucket: %s", err)
	}

    testFile := createTestFile(2 * DefaultUploadPartSize)

    imur, err := bucket.InitiateMultipartUpload(filepath.Base(testFile))

    unfUploads, err := aliSvc.ListUnfinishedUploads()
    if err != nil {
		t.Fatalf("Failed to list unfinished uploads: %s", err)
	}
    unfUploadFound := false
    for _, upload := range unfUploads {
        t.Log(upload.Key, upload.UploadID)
        if upload.Key == filepath.Base(testFile) {
            unfUploadFound = true
        }
    }
    if !unfUploadFound {
        t.Fatalf("Failed to find unfinished upload %s to %s", testFile, filepath.Base(testFile))
    }

    err = aliSvc.ResumeUpload(testFile, filepath.Base(testFile), imur.UploadID)
    if err != nil {
		t.Fatalf("Failed to resume upload %s to %s: %s", testFile, filepath.Base(testFile), err)
	}

    err = aliSvc.Delete(filepath.Base(testFile))
	if err != nil {
		t.Fatalf("Failed to delete file %s: %s", filepath.Base(testFile), err)
	}
}

func TestBasic(t *testing.T) {
	if AliRegion == "" || AliBucket == "" || AliKeyId == "" || AliSecretKey == "" {
		t.Fatal("Environment variables ALI_REGION or ALI_BUCKET or ALI_ACCESS_KEY_ID or ALI_SECRET_ACCESS_KEY are not defined")
	}

	ali, err := oss.New(AliRegion, AliKeyId, AliSecretKey)
	if err != nil {
		t.Fatalf("Failed to create OSS client: %s", err)
	}

	aliSvc := AliOss{
		Log:    log.New(os.Stdout, "testing: ", log.LstdFlags),
		Svc:    ali,
		Region: AliRegion,
		Bucket: AliBucket,
	}

	err = aliSvc.IsRegionValid(AliRegion)
	if err != nil {
		return
	}

	bucketList, err := aliSvc.GetBucketFilesList("")
	if err != nil {
		t.Fatalf("Failed to get bucket list: %s", err)
		return
	}
	for _, rFile := range bucketList {
		aliSvc.Log.Println(rFile.Type, rFile.Key)
	}

    subFolder := "testFolder"

	if subFolder != "" {
		err = aliSvc.CreateFolder(subFolder)
		if err != nil {
			t.Fatalf("Failed to create folder %s: %s", subFolder, err)
			return
		}
	}
	testFile := createTestFile(1024)
	testFileDownloaded := testFile + ".downloaded"
	testFileUploadedSuccess := false

	err = aliSvc.Upload(testFile, subFolder)
	if err != nil {
		t.Fatalf("Failed to upload file %s to %s: %s", testFile, subFolder+filepath.Base(testFile), err)

	}

	for i := 0; i <= 5; i++ {
		bucketList, err = aliSvc.GetBucketFilesList(subFolder)
		if err != nil {
			t.Fatalf("Failed to get bucket list: %s", err)

		}
		for _, rFile := range bucketList {
			aliSvc.Log.Println(rFile.Type, rFile.Key)
			if rFile.Key == subFolder+"/"+filepath.Base(testFile) {
				testFileUploadedSuccess = true
			}
		}
		if testFileUploadedSuccess {
			break
		}
	}

	if !testFileUploadedSuccess {
		t.Fatalf("Failed to upload file %s to %s: File does not exists on remote storage", testFile, subFolder+"/"+filepath.Base(testFile))
	}

    err = os.Remove(testFile)
    if err != nil {
        t.Fatalf("Failed to remove uploaded file %s: %s", testFile, err)
    }

	err = aliSvc.Download(subFolder+"/"+filepath.Base(testFile), testFileDownloaded)
	if err != nil {
		t.Fatalf("Failed to download file %s to %s: %s", subFolder+filepath.Base(testFile), testFileDownloaded, err)

	}
	if _, err := os.Stat(testFileDownloaded); os.IsNotExist(err) {
		t.Fatalf("Failed to download file %s to %s: File does not exists", subFolder+"/"+filepath.Base(testFile), testFileDownloaded)

	}
	err = os.Remove(testFileDownloaded)
    if err != nil {
        t.Fatalf("Failed to remove downloaded file %s: %s", testFileDownloaded, err)
    }

	err = aliSvc.Delete(subFolder + "/" + filepath.Base(testFile))
	if err != nil {
		t.Fatalf("Failed to delete file %s: %s", subFolder+filepath.Base(testFile), err)
	}
}

func createTestFile(size int64) string {
    binaryDir, err := osext.ExecutableFolder()
	if err != nil {
		panic(fmt.Errorf("Failed to get binary folder: %s", err))
	}
	testFilePath := path.Join(binaryDir, "test_"+getRandomString(10)+".txt")
	testFile, err := os.OpenFile(testFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
	if err != nil {
		panic(fmt.Errorf("Failed to create test file %s: %s", testFilePath, err))
	}
	defer func() { _ = testFile.Close() }()

    signature := []byte("test upload file")
    sigLen := 2 * len(signature) // We will write signature twice
    size = size - int64(sigLen)

	_, err = testFile.Write(signature)
	if err != nil {
		panic(fmt.Errorf("Failed to write first signature to test file %s: %s", testFilePath, err))
	}

    chunkSize := int64(1024 * 1024 * 25)

	for {
		if size <= chunkSize {
			s := make([]byte, size)
			_, err := testFile.Write(s)
            if err != nil {
                panic(fmt.Errorf("Failed to write last chunk to test file %s: %s", testFilePath, err))
            }

            _, err = testFile.Write(signature)
            if err != nil {
                panic(fmt.Errorf("Failed to write second signature to test file %s: %s", testFilePath, err))
            }

			return testFilePath
		}

		size = size - chunkSize

		s := make([]byte, chunkSize)

		_, err := testFile.Write(s)
		if err != nil {
			panic(fmt.Errorf("Failed to write test file %s: %s", testFilePath, err))
		}
	}
}

func getRandomString(n int) string {
	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	const (
		letterIdxBits = 6                    // 6 bits to represent a letter index
		letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
		letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
	)

	var src = rand.NewSource(time.Now().UnixNano())

	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}

	return string(b)
}<commit_msg>Add testing ResumeDownload<commit_after>package alioss

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/kardianos/osext"
	"io"
	"log"
	"math/rand"
	"os"
	"path"
	"path/filepath"
	"testing"
	"time"
)

var AliRegion string
var AliBucket string
var AliKeyId string
var AliSecretKey string

func init() {
	AliRegion = os.Getenv("ALI_REGION")
	AliBucket = os.Getenv("ALI_BUCKET")
	AliKeyId = os.Getenv("ALI_ACCESS_KEY_ID")
	AliSecretKey = os.Getenv("ALI_SECRET_ACCESS_KEY")

}

func TestResumeUpload(t *testing.T) {
	if AliRegion == "" || AliBucket == "" || AliKeyId == "" || AliSecretKey == "" {
		t.Fatal("Environment variables ALI_REGION or ALI_BUCKET or ALI_ACCESS_KEY_ID or ALI_SECRET_ACCESS_KEY are not defined")
	}

	ali, err := oss.New(AliRegion, AliKeyId, AliSecretKey)
	if err != nil {
		t.Fatalf("Failed to create OSS client: %s", err)
	}

	aliSvc := AliOss{
		Log:    log.New(os.Stdout, "testing: ", log.LstdFlags),
		Svc:    ali,
		Region: AliRegion,
		Bucket: AliBucket,
	}

	bucket, err := aliSvc.Svc.Bucket(aliSvc.Bucket)
	if err != nil {
		t.Fatalf("Failed to instant bucket: %s", err)
	}

	testFile := createTestFile(2 * DefaultUploadPartSize)
	testFileDownloaded := testFile + ".downloaded"

	imur, err := bucket.InitiateMultipartUpload(filepath.Base(testFile))

	unfUploads, err := aliSvc.ListUnfinishedUploads()
	if err != nil {
		t.Fatalf("Failed to list unfinished uploads: %s", err)
	}
	unfUploadFound := false
	for _, upload := range unfUploads {
		t.Log(upload.Key, upload.UploadID)
		if upload.Key == filepath.Base(testFile) {
			unfUploadFound = true
		}
	}
	if !unfUploadFound {
		t.Fatalf("Failed to find unfinished upload %s to %s", testFile, filepath.Base(testFile))
	}

	err = aliSvc.ResumeUpload(testFile, filepath.Base(testFile), imur.UploadID)
	if err != nil {
		t.Fatalf("Failed to resume upload %s to %s: %s", testFile, filepath.Base(testFile), err)
	}

	f, err := os.Create(testFileDownloaded)
	if err != nil {
		t.Fatalf("Failed to create empty file %s for download %s: %s", testFileDownloaded, filepath.Base(testFile), err)
	}
	f.Close()

	err = aliSvc.ResumeDownload(filepath.Base(testFile), testFileDownloaded)
	if err != nil {
		t.Fatalf("Failed to resume download %s to %s: %s", filepath.Base(testFile), testFileDownloaded, err)
	}

	md5test, err := md5sum(testFile)
	if err != nil {
		t.Fatalf("Failed to get MD5 of %s: %s", testFile, err)
	}

	md5downloaded, err := md5sum(testFile)
	if err != nil {
		t.Fatalf("Failed to get MD5 of %s: %s", testFileDownloaded, err)
	}

	if md5test != md5downloaded {
		t.Fatalf("Failed to match MD5 of %s(%s) and %s(%s)", testFile, md5test, testFileDownloaded, md5downloaded)
	}

	err = aliSvc.Delete(filepath.Base(testFile))
	if err != nil {
		t.Fatalf("Failed to delete file %s from remote storage: %s", filepath.Base(testFile), err)
	}

	err = os.Remove(testFile)
	if err != nil {
		t.Fatalf("Failed to delete file %s: %s", testFile, err)
	}

	err = os.Remove(testFileDownloaded)
	if err != nil {
		t.Fatalf("Failed to delete file %s: %s", testFileDownloaded, err)
	}

}

func TestBasic(t *testing.T) {
	if AliRegion == "" || AliBucket == "" || AliKeyId == "" || AliSecretKey == "" {
		t.Fatal("Environment variables ALI_REGION or ALI_BUCKET or ALI_ACCESS_KEY_ID or ALI_SECRET_ACCESS_KEY are not defined")
	}

	ali, err := oss.New(AliRegion, AliKeyId, AliSecretKey)
	if err != nil {
		t.Fatalf("Failed to create OSS client: %s", err)
	}

	aliSvc := AliOss{
		Log:    log.New(os.Stdout, "testing: ", log.LstdFlags),
		Svc:    ali,
		Region: AliRegion,
		Bucket: AliBucket,
	}

	err = aliSvc.IsRegionValid(AliRegion)
	if err != nil {
		return
	}

	bucketList, err := aliSvc.GetBucketFilesList("")
	if err != nil {
		t.Fatalf("Failed to get bucket list: %s", err)
		return
	}
	for _, rFile := range bucketList {
		aliSvc.Log.Println(rFile.Type, rFile.Key)
	}

	subFolder := "testFolder"

	if subFolder != "" {
		err = aliSvc.CreateFolder(subFolder)
		if err != nil {
			t.Fatalf("Failed to create folder %s: %s", subFolder, err)
			return
		}
	}
	testFile := createTestFile(1024)
	testFileDownloaded := testFile + ".downloaded"
	testFileUploadedSuccess := false

	err = aliSvc.Upload(testFile, subFolder)
	if err != nil {
		t.Fatalf("Failed to upload file %s to %s: %s", testFile, subFolder+filepath.Base(testFile), err)

	}

	for i := 0; i <= 5; i++ {
		bucketList, err = aliSvc.GetBucketFilesList(subFolder)
		if err != nil {
			t.Fatalf("Failed to get bucket list: %s", err)

		}
		for _, rFile := range bucketList {
			aliSvc.Log.Println(rFile.Type, rFile.Key)
			if rFile.Key == subFolder+"/"+filepath.Base(testFile) {
				testFileUploadedSuccess = true
			}
		}
		if testFileUploadedSuccess {
			break
		}
	}

	if !testFileUploadedSuccess {
		t.Fatalf("Failed to upload file %s to %s: File does not exists on remote storage", testFile, subFolder+"/"+filepath.Base(testFile))
	}

	err = os.Remove(testFile)
	if err != nil {
		t.Fatalf("Failed to remove uploaded file %s: %s", testFile, err)
	}

	err = aliSvc.Download(subFolder+"/"+filepath.Base(testFile), testFileDownloaded)
	if err != nil {
		t.Fatalf("Failed to download file %s to %s: %s", subFolder+filepath.Base(testFile), testFileDownloaded, err)

	}
	if _, err := os.Stat(testFileDownloaded); os.IsNotExist(err) {
		t.Fatalf("Failed to download file %s to %s: File does not exists", subFolder+"/"+filepath.Base(testFile), testFileDownloaded)

	}
	err = os.Remove(testFileDownloaded)
	if err != nil {
		t.Fatalf("Failed to remove downloaded file %s: %s", testFileDownloaded, err)
	}

	err = aliSvc.Delete(subFolder + "/" + filepath.Base(testFile))
	if err != nil {
		t.Fatalf("Failed to delete file %s: %s", subFolder+filepath.Base(testFile), err)
	}
}

func createTestFile(size int64) string {
	binaryDir, err := osext.ExecutableFolder()
	if err != nil {
		panic(fmt.Errorf("Failed to get binary folder: %s", err))
	}
	testFilePath := path.Join(binaryDir, "test_"+getRandomString(10)+".txt")
	testFile, err := os.OpenFile(testFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
	if err != nil {
		panic(fmt.Errorf("Failed to create test file %s: %s", testFilePath, err))
	}
	defer func() { _ = testFile.Close() }()

	signature := []byte("test upload file")
	sigLen := 2 * len(signature) // We will write signature twice
	size = size - int64(sigLen)

	_, err = testFile.Write(signature)
	if err != nil {
		panic(fmt.Errorf("Failed to write first signature to test file %s: %s", testFilePath, err))
	}

	chunkSize := int64(1024 * 1024 * 25)

	for {
		if size <= chunkSize {
			s := make([]byte, size)
			_, err := testFile.Write(s)
			if err != nil {
				panic(fmt.Errorf("Failed to write last chunk to test file %s: %s", testFilePath, err))
			}

			_, err = testFile.Write(signature)
			if err != nil {
				panic(fmt.Errorf("Failed to write second signature to test file %s: %s", testFilePath, err))
			}

			return testFilePath
		}

		size = size - chunkSize

		s := make([]byte, chunkSize)

		_, err := testFile.Write(s)
		if err != nil {
			panic(fmt.Errorf("Failed to write test file %s: %s", testFilePath, err))
		}
	}
}

func getRandomString(n int) string {
	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	const (
		letterIdxBits = 6                    // 6 bits to represent a letter index
		letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
		letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
	)

	var src = rand.NewSource(time.Now().UnixNano())

	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}

	return string(b)
}

func md5sum(filePath string) (result string, err error) {
	file, err := os.Open(filePath)
	if err != nil {
		return
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		return
	}

	result = hex.EncodeToString(hash.Sum(nil))
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package amass

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/amass/core"
	"github.com/OWASP/Amass/amass/utils"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `

        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.

`

const (
	// Version is used to display the current version of Amass.
	Version = "3.0.17"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// TrustedTag returns true when the tag parameter is of a type that should be trusted even
// facing DNS wildcards.
func TrustedTag(tag string) bool {
	if tag == core.DNS || tag == core.CERT || tag == core.ARCHIVE || tag == core.AXFR {
		return true
	}
	return false
}

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided core.Output data.
func UpdateSummaryData(output *core.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass v"
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76
	version := "Version " + Version
	desc := "In-depth DNS Enumeration and Network Mapping"

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(version))
	y.Fprintln(color.Error, version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(desc))
	y.Fprintf(color.Error, "%s\n\n\n", desc)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a core.Output.
func OutputLineParts(out *core.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []core.AddressInfo, ipv4, ipv6 bool) []core.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []core.AddressInfo
	for _, addr := range addrs {
		if utils.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if utils.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<commit_msg>version 3.0.18 release<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package amass

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/amass/core"
	"github.com/OWASP/Amass/amass/utils"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `

        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.

`

const (
	// Version is used to display the current version of Amass.
	Version = "3.0.18"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// TrustedTag returns true when the tag parameter is of a type that should be trusted even
// facing DNS wildcards.
func TrustedTag(tag string) bool {
	if tag == core.DNS || tag == core.CERT || tag == core.ARCHIVE || tag == core.AXFR {
		return true
	}
	return false
}

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided core.Output data.
func UpdateSummaryData(output *core.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass v"
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76
	version := "Version " + Version
	desc := "In-depth DNS Enumeration and Network Mapping"

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(version))
	y.Fprintln(color.Error, version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(desc))
	y.Fprintf(color.Error, "%s\n\n\n", desc)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a core.Output.
func OutputLineParts(out *core.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []core.AddressInfo, ipv4, ipv6 bool) []core.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []core.AddressInfo
	for _, addr := range addrs {
		if utils.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if utils.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<|endoftext|>


<commit_before>// Copyright (C) 2011-12 Qtrac Ltd.
//
// This program or package and any associated files are licensed under the
// Apache License, Version 2.0 (the "License"); you may not use these files
// except in compliance with the License. You can get 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.

// The approach taken here was inspired by an example on the gonuts mailing
// list by Roger Peppe.
/*
  REVISION HISTORY
  ----------------
  20 Mar 20 -- Made comparisons case insensitive.  And decided to make this cgrepi.go.
                 And then I figured I could not improve performance by using more packages.
                 But I can change the side effect of displaying altered case.
  21 Mar 20 -- Another ack name change.  My plan is to reproduce the function of ack, but on windows not require
                 the complex installation that I cannot do at work.
                 I'll use multiple processes for the grep work.  For the dir walking I'll just do that in main.
  30 Mar 20 -- Started work on extracting the extensions from a slice of input filenames.  And will assume .txt extension if none is provided.
*/
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"time"
)

const LastAltered = "30 Mar 2020"

type Result struct {
	filename string
	lino     int
	line     string
}

func main() {
	//	runtime.GOMAXPROCS(runtime.NumCPU()) // Use all the machine's cores
	log.SetFlags(0)
	var timeoutOpt *int = flag.Int("timeout", 0, "seconds < 240, where 0 means max timeout of 240 sec.")
	flag.Parse()
	if *timeoutOpt < 0 || *timeoutOpt > 240 {
		log.Fatalln("timeout must be in the range [0,240] seconds")
	}
	if *timeoutOpt == 0 {
		*timeoutOpt = 240
	}
	args := flag.Args()
	if len(args) < 1 {
		log.Fatalln("a regexp to match must be specified")
	}
	pattern := args[0]
	pattern = strings.ToLower(pattern)

	extensions := make([]string, 0, 100)
	if flag.NArg() < 2 {
		extensions = append(extensions, ".txt")
	} else if runtime.GOOS == "linux" {
		files := args[1:]
		if len(files) > 1 {
			extensions = extractExtensions(files)
		}
	} else {
		extensions = args[1:]
	}
    fmt.Println(" NArg", flag.NArg(), ", pattern=",pattern, ", extensions=", extensions)
/*
	for i, ext := range extensions { // validate extensions, as this is likely forgotten to be needed.
		if !strings.ContainsAny(ext, ".") {
			extensions[i] = "." + ext
			fmt.Println(" Added dot to extension to give", extensions[i])
		}
	}

	for _, ext := range extensions {
		if len(ext) != 4 {
			fmt.Println(" Need dotted extensions only.  Not filenames, not wildcards.  A missing dot will be prepended.  Is", ext, "an extension?")
			fmt.Print(" Proceed? ")
			ans := ""
			_, err := fmt.Scanln(&ans)
			if err != nil {
				log.Fatalln(" Error from ScanLn.  It figures.", err)
			}
			ans = strings.ToUpper(ans)
			if !strings.Contains(ans, "Y") {
				os.Exit(1)
			}
		}
	}
*/
	//	for _, ext := range extensions {   It works, so I can remove this.
	//		fmt.Println(" debug for dot ext.  Ext is ", ext)
	//	}

	startDirectory, _ := os.Getwd() // startDirectory is a string
	fmt.Println()
	fmt.Printf(" Another ack, written in Go.  Last altered %s, and will start in %s.", LastAltered, startDirectory)
	fmt.Println()
	fmt.Println()
	DirAlreadyWalked := make(map[string]bool, 500)
	DirAlreadyWalked[".git"] = true // ignore .git and its subdir's

	t0 := time.Now()
	tfinal := t0.Add(time.Duration(*timeoutOpt) * time.Second)
	// walkfunc closure
	var filepathwalkfunction filepath.WalkFunc = func(fpath string, fi os.FileInfo, err error) error {
		if err != nil {
			fmt.Printf(" Error from walk is %v. \n ", err)
			return nil
		}

		if fi.IsDir() {
			if DirAlreadyWalked[fpath] {
				return filepath.SkipDir
			} else {
				DirAlreadyWalked[fpath] = true
			}
		} else if fi.Mode().IsRegular() {
			for _, ext := range extensions {
				if strings.HasSuffix(fpath, ext) { // only search thru indicated extensions.  Especially not thru binary or swap files.
					if lineRx, err := regexp.Compile(pattern); err != nil { // this is the regex compile line.
						log.Fatalf("invalid regexp: %s\n", err)
					} else {
						//fullname := fpath + string(filepath.Separator) + fi.Name()  Turns out that fpath is the full file name path.
						grepFile(lineRx, fpath)
					}
				}
			}
		}
		//log.Println(" Need to debug this.  Filepath is", fpath, ", fi is", fi.Name(), fi.IsDir())
		now := time.Now()
		if now.After(tfinal) {
			log.Fatalln(" Time up.  Elapsed is", time.Since(t0))
		}
		return nil
	}

	err := filepath.Walk(startDirectory, filepathwalkfunction)

	if err != nil {
		log.Fatalln(" Error from filepath.walk is", err, ".  Elapsed time is", time.Since(t0))
	}

	elapsed := time.Since(t0)
	fmt.Println(" Elapsed time is", elapsed)
	fmt.Println()
} // end main

func grepFile(lineRx *regexp.Regexp, fpath string) {
	file, err := os.Open(fpath)
	if err != nil {
		log.Printf("grepFile os.Open error : %s\n", err)
		return
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	for lino := 1; ; lino++ {
		line, err := reader.ReadBytes('\n')
		line = bytes.TrimRight(line, "\n\r")

		// this is the change I made to make every comparison case insensitive.  Side effect of output is not original case.
		linestr := string(line)
		linestr = strings.ToLower(linestr)
		linelowercase := []byte(linestr)

		if lineRx.Match(linelowercase) {
			fmt.Printf("%s:%d:%s \n", fpath, lino, string(line))
		}
		if err != nil {
			if err != io.EOF {
				log.Printf("error from reader.ReadBytes in grepfile:%d: %s\n", lino, err)
			}
			break // just exit when hit EOF condition.
		}
	}
} // end grepFile

func extractExtensions(files []string) []string {

	var extensions sort.StringSlice
	extensions = make([]string, 0, 100)
	for _, file := range files {
		ext := filepath.Ext(file)
		extensions = append(extensions, ext)
	}
	if len(extensions) > 1 {
		extensions.Sort()
		for i := range extensions {
			if i == 0 {
				continue
			}
			if strings.EqualFold(extensions[i-1], extensions[i]) {
				extensions[i] = ""
			}
		}
		fmt.Println(" in extractExtensions before sort:", extensions)
		sort.Sort(sort.Reverse(extensions))
		// sort.Sort(sort.Reverse(sort.IntSlice(s)))
		trimmedExtensions := make([]string, 0, len(extensions))
		for _, ext := range extensions {
			if ext != "" {
				trimmedExtensions = append(trimmedExtensions, ext)
			}
		}
		fmt.Println(" in extractExtensions after sort trimmedExtensions:", trimmedExtensions)
		fmt.Println()
		return trimmedExtensions
	}
	fmt.Println(" in extractExtensions without a sort:", extensions)
	fmt.Println()
	return extensions

} // end extractExtensions
<commit_msg>03/31/2020 07:37:39 AM anack/anack.go -- it works on linux.<commit_after>// Copyright (C) 2011-12 Qtrac Ltd.
//
// This program or package and any associated files are licensed under the
// Apache License, Version 2.0 (the "License"); you may not use these files
// except in compliance with the License. You can get 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.

// The approach taken here was inspired by an example on the gonuts mailing
// list by Roger Peppe.
/*
  REVISION HISTORY
  ----------------
  20 Mar 20 -- Made comparisons case insensitive.  And decided to make this cgrepi.go.
                 And then I figured I could not improve performance by using more packages.
                 But I can change the side effect of displaying altered case.
  21 Mar 20 -- Another ack name change.  My plan is to reproduce the function of ack, but on windows not require
                 the complex installation that I cannot do at work.
                 I'll use multiple processes for the grep work.  For the dir walking I'll just do that in main.
  30 Mar 20 -- Started work on extracting the extensions from a slice of input filenames.  And will assume .txt extension if none is provided.
*/
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"time"
)

const LastAltered = "31 Mar 2020"

type Result struct {
	filename string
	lino     int
	line     string
}

func main() {
	//	runtime.GOMAXPROCS(runtime.NumCPU()) // Use all the machine's cores
	log.SetFlags(0)
	var timeoutOpt *int = flag.Int("timeout", 0, "seconds < 240, where 0 means max timeout of 240 sec.")
	flag.Parse()
	if *timeoutOpt < 0 || *timeoutOpt > 240 {
		log.Fatalln("timeout must be in the range [0,240] seconds")
	}
	if *timeoutOpt == 0 {
		*timeoutOpt = 240
	}
	args := flag.Args()
	if len(args) < 1 {
		log.Fatalln("a regexp to match must be specified")
	}
	pattern := args[0]
	pattern = strings.ToLower(pattern)

	extensions := make([]string, 0, 100)
	if flag.NArg() < 2 {
		extensions = append(extensions, ".txt")
	} else if runtime.GOOS == "linux" {
		files := args[1:]
		if len(files) > 1 {
			extensions = extractExtensions(files)
		}
	} else {
		extensions = args[1:]
	}
    //fmt.Println(", pattern=",pattern, ", extensions=", extensions)
/*
	for i, ext := range extensions { // validate extensions, as this is likely forgotten to be needed.
		if !strings.ContainsAny(ext, ".") {
			extensions[i] = "." + ext
			fmt.Println(" Added dot to extension to give", extensions[i])
		}
	}

	for _, ext := range extensions {
		if len(ext) != 4 {
			fmt.Println(" Need dotted extensions only.  Not filenames, not wildcards.  A missing dot will be prepended.  Is", ext, "an extension?")
			fmt.Print(" Proceed? ")
			ans := ""
			_, err := fmt.Scanln(&ans)
			if err != nil {
				log.Fatalln(" Error from ScanLn.  It figures.", err)
			}
			ans = strings.ToUpper(ans)
			if !strings.Contains(ans, "Y") {
				os.Exit(1)
			}
		}
	}
*/
	//	for _, ext := range extensions {   It works, so I can remove this.
	//		fmt.Println(" debug for dot ext.  Ext is ", ext)
	//	}

	startDirectory, _ := os.Getwd() // startDirectory is a string
	fmt.Println()
	fmt.Printf(" Another ack, written in Go.  Last altered %s, and will start in %s, pattern-%s, extensions=%v. \n\n\n ",
		LastAltered, startDirectory,pattern, extensions)

	DirAlreadyWalked := make(map[string]bool, 500)
	DirAlreadyWalked[".git"] = true // ignore .git and its subdir's

	t0 := time.Now()
	tfinal := t0.Add(time.Duration(*timeoutOpt) * time.Second)
	// walkfunc closure
	var filepathwalkfunction filepath.WalkFunc = func(fpath string, fi os.FileInfo, err error) error {
		if err != nil {
			fmt.Printf(" Error from walk is %v. \n ", err)
			return nil
		}

		if fi.IsDir() {
			if DirAlreadyWalked[fpath] {
				return filepath.SkipDir
			} else {
				DirAlreadyWalked[fpath] = true
			}
		} else if fi.Mode().IsRegular() {
			for _, ext := range extensions {
				if strings.HasSuffix(fpath, ext) { // only search thru indicated extensions.  Especially not thru binary or swap files.
					if lineRx, err := regexp.Compile(pattern); err != nil { // this is the regex compile line.
						log.Fatalf("invalid regexp: %s\n", err)
					} else {
						//fullname := fpath + string(filepath.Separator) + fi.Name()  Turns out that fpath is the full file name path.
						grepFile(lineRx, fpath)
					}
				}
			}
		}
		//log.Println(" Need to debug this.  Filepath is", fpath, ", fi is", fi.Name(), fi.IsDir())
		now := time.Now()
		if now.After(tfinal) {
			log.Fatalln(" Time up.  Elapsed is", time.Since(t0))
		}
		return nil
	}

	err := filepath.Walk(startDirectory, filepathwalkfunction)

	if err != nil {
		log.Fatalln(" Error from filepath.walk is", err, ".  Elapsed time is", time.Since(t0))
	}

	elapsed := time.Since(t0)
	fmt.Println(" Elapsed time is", elapsed)
	fmt.Println()
} // end main

func grepFile(lineRx *regexp.Regexp, fpath string) {
	file, err := os.Open(fpath)
	if err != nil {
		log.Printf("grepFile os.Open error : %s\n", err)
		return
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	for lino := 1; ; lino++ {
		line, err := reader.ReadBytes('\n')
		line = bytes.TrimRight(line, "\n\r")

		// this is the change I made to make every comparison case insensitive.  Side effect of output is not original case.
		linestr := string(line)
		linestr = strings.ToLower(linestr)
		linelowercase := []byte(linestr)

		if lineRx.Match(linelowercase) {
			fmt.Printf("%s:%d:%s \n", fpath, lino, string(line))
		}
		if err != nil {
			if err != io.EOF {
				log.Printf("error from reader.ReadBytes in grepfile:%d: %s\n", lino, err)
			}
			break // just exit when hit EOF condition.
		}
	}
} // end grepFile

func extractExtensions(files []string) []string {

	var extensions sort.StringSlice
	extensions = make([]string, 0, 100)
	for _, file := range files {
		ext := filepath.Ext(file)
		extensions = append(extensions, ext)
	}
	if len(extensions) > 1 {
		extensions.Sort()
		for i := range extensions {
			if i == 0 {
				continue
			}
			if strings.EqualFold(extensions[i-1], extensions[i]) {
				extensions[i-1] = ""  // This needs to be [i-1] because when it was [i] it interferred w/ the next iteration.
			}
		}
		//fmt.Println(" in extractExtensions before sort:", extensions)
		sort.Sort(sort.Reverse(extensions))
		// sort.Sort(sort.Reverse(sort.IntSlice(s)))
		trimmedExtensions := make([]string, 0, len(extensions))
		for _, ext := range extensions {
			if ext != "" {
				trimmedExtensions = append(trimmedExtensions, ext)
			}
		}
		//fmt.Println(" in extractExtensions after sort trimmedExtensions:", trimmedExtensions)
		//fmt.Println()
		return trimmedExtensions
	}
	//fmt.Println(" in extractExtensions without a sort:", extensions)
	//fmt.Println()
	return extensions

} // end extractExtensions
<|endoftext|>


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

package app

import (
	"bytes"
	"fmt"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"os"
	"path"
	"time"
)

const (
	ChanSize    = 10
	runAttempts = 5
)

type message struct {
	app     *App
	success chan bool
}

var env chan message = make(chan message, ChanSize)

var environConfPath = path.Join(os.ExpandEnv("${HOME}"), ".juju", "environments.yaml")

type cmd struct {
	cmd    string
	result chan cmdResult
	u      Unit
}

type cmdResult struct {
	err    error
	output []byte
}

var cmds chan cmd = make(chan cmd)

func init() {
	go collectEnvVars()
	go runCommands()
}

func runCommands() {
	for cmd := range cmds {
		buf := new(bytes.Buffer)
		err := cmd.u.Command(buf, buf, cmd.cmd)
		if cmd.result != nil {
			r := cmdResult{output: buf.Bytes(), err: err}
			cmd.result <- r
		}
	}
}

func runCmd(command string, msg message) {
	c := cmd{
		u:      *msg.app.unit(),
		cmd:    command,
		result: make(chan cmdResult),
	}
	cmds <- c
	var r cmdResult
	r = <-c.result
	for i := 0; r.err != nil && i < runAttempts; i++ {
		cmds <- c
		r = <-c.result
	}
	log.Printf("running %s on %s, output:\n %s", command, msg.app.Name, string(r.output))
	if msg.success != nil {
		msg.success <- r.err == nil
	}
}

func collectEnvVars() {
	for e := range env {
		cmd := "cat > /home/application/apprc <<END\n"
		cmd += fmt.Sprintf("# generated by tsuru at %s\n", time.Now().Format(time.RFC822Z))
		for k, v := range e.app.Env {
			cmd += fmt.Sprintf(`export %s="%s"`+"\n", k, v.Value)
		}
		cmd += "END\n"
		runCmd(cmd, e)
	}
}

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}
<commit_msg>api/app: unexport constant<commit_after>// Copyright 2012 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"bytes"
	"fmt"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"os"
	"path"
	"time"
)

const (
	chanSize    = 10
	runAttempts = 5
)

type message struct {
	app     *App
	success chan bool
}

var env chan message = make(chan message, chanSize)

var environConfPath = path.Join(os.ExpandEnv("${HOME}"), ".juju", "environments.yaml")

type cmd struct {
	cmd    string
	result chan cmdResult
	u      Unit
}

type cmdResult struct {
	err    error
	output []byte
}

var cmds chan cmd = make(chan cmd)

func init() {
	go collectEnvVars()
	go runCommands()
}

func runCommands() {
	for cmd := range cmds {
		buf := new(bytes.Buffer)
		err := cmd.u.Command(buf, buf, cmd.cmd)
		if cmd.result != nil {
			r := cmdResult{output: buf.Bytes(), err: err}
			cmd.result <- r
		}
	}
}

func runCmd(command string, msg message) {
	c := cmd{
		u:      *msg.app.unit(),
		cmd:    command,
		result: make(chan cmdResult),
	}
	cmds <- c
	var r cmdResult
	r = <-c.result
	for i := 0; r.err != nil && i < runAttempts; i++ {
		cmds <- c
		r = <-c.result
	}
	log.Printf("running %s on %s, output:\n %s", command, msg.app.Name, string(r.output))
	if msg.success != nil {
		msg.success <- r.err == nil
	}
}

func collectEnvVars() {
	for e := range env {
		cmd := "cat > /home/application/apprc <<END\n"
		cmd += fmt.Sprintf("# generated by tsuru at %s\n", time.Now().Format(time.RFC822Z))
		for k, v := range e.app.Env {
			cmd += fmt.Sprintf(`export %s="%s"`+"\n", k, v.Value)
		}
		cmd += "END\n"
		runCmd(cmd, e)
	}
}

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/xml"
	"fmt"

	"github.com/heroicyang/wechat-qy/base"
)

// 接收事件类型
const (
	SubscribeEvent       = "subscribe"
	UnsubscribeEvent     = "unsubscribe"
	LocationEvent        = "LOCATION"
	MenuClickEvent       = "CLICK"
	MenuViewEvent        = "VIEW"
	ScanCodePushEvent    = "scancode_push"
	ScanCodeWaitMsgEvent = "scancode_waitmsg"
	PicSysPhotoEvent     = "pic_sysphoto"
	PicPhotoOrAlbumEvent = "pic_photo_or_album"
	PicWeiXinEvent       = "pic_weixin"
	LocationSelectEvent  = "location_select"
	EnterAgentEvent      = "enter_agent"
	BatchJobResultEvent  = "batch_job_result"
)

// RecvBaseData 描述接收到的各类消息或事件的公共结构
type RecvBaseData struct {
	ToUserName   string
	FromUserName string
	CreateTime   int
	MsgType      MessageType
	AgentID      int64
}

// RecvTextMessage 描述接收到的文本类型消息结构
type RecvTextMessage struct {
	RecvBaseData
	MsgID   uint64 `xml:"MsgId"`
	Content string
}

// RecvImageMessage 描述接收到的图片类型消息结构
type RecvImageMessage struct {
	RecvBaseData
	MsgID   uint64 `xml:"MsgId"`
	PicURL  string `xml:"PicUrl"`
	MediaID string `xml:"MediaId"`
}

// RecvVoiceMessage 描述接收到的语音类型消息结构
type RecvVoiceMessage struct {
	RecvBaseData
	MsgID   uint64 `xml:"MsgId"`
	MediaID string `xml:"MediaId"`
	Format  string
}

// RecvVideoMessage 描述接收到的视频类型消息结构
type RecvVideoMessage struct {
	RecvBaseData
	MsgID        uint64 `xml:"MsgId"`
	MediaID      string `xml:"MediaId"`
	ThumbMediaID string `xml:"ThumbMediaId"`
}

// RecvLocationMessage 描述接收到的地理位置类型消息结构
type RecvLocationMessage struct {
	RecvBaseData
	MsgID     uint64  `xml:"MsgId"`
	LocationX float64 `xml:"Location_X"`
	LocationY float64 `xml:"Location_Y"`
	Scale     int
	Label     string
}

// RecvSubscribeEvent 描述成员关注/取消关注事件的结构
type RecvSubscribeEvent struct {
	RecvBaseData
	Event string
}

// RecvLocationEvent 描述上报地理位置事件的结构
type RecvLocationEvent struct {
	RecvBaseData
	Event     string
	Latitude  float64
	Longitude float64
	Precision float64
}

// RecvMenuEvent 描述菜单事件的结构
type RecvMenuEvent struct {
	RecvBaseData
	Event    string
	EventKey string
}

// ScanCodeInfo 描述扫码事件的相关内容结构
type ScanCodeInfo struct {
	ScanType   string
	ScanResult string
}

// RecvScanCodeEvent 描述扫码推/扫码推事件且弹出“消息接收中”提示框类型事件的结构
type RecvScanCodeEvent struct {
	RecvBaseData
	Event        string
	EventKey     string
	ScanCodeInfo ScanCodeInfo
}

// SendPicMD5Sum 描述发图事件中单个图片的 MD5 信息
type SendPicMD5Sum struct {
	PicMd5Sum string
}

// SendPicItem 描述发图事件中单个图片信息结构
type SendPicItem struct {
	Item SendPicMD5Sum `xml:"item"`
}

// SendPicsInfo 描述发图事件的图片信息结构
type SendPicsInfo struct {
	Count   int64
	PicList []SendPicItem
}

// RecvPicEvent 描述发图事件的结构
type RecvPicEvent struct {
	RecvBaseData
	Event        string
	EventKey     string
	SendPicsInfo SendPicsInfo
}

// SendLocationInfo 描述弹出地理位置选择器事件中地理位置信息结构
type SendLocationInfo struct {
	LocationX float64 `xml:"Location_X"`
	LocationY float64 `xml:"Location_Y"`
	Scale     int
	Label     string
	PoiName   string `xml:"Poiname"`
}

// RecvLocationSelectEvent 描述弹出地理位置选择器事件的结构
type RecvLocationSelectEvent struct {
	RecvBaseData
	Event            string
	EventKey         string
	SendLocationInfo SendLocationInfo
}

// RecvEnterAgentEvent 描述成员进入应用事件的结构
type RecvEnterAgentEvent struct {
	RecvBaseData
	Event    string
	EventKey string
}

// JobResultInfo 描述异步任务完成事件中任务完成情况信息
type JobResultInfo struct {
	JobID   string `xml:"JobId"`
	JobType string
	ErrCode int
	ErrMsg  string
}

// RecvBatchJobResultEvent 描述异步任务完成事件的结构
type RecvBatchJobResultEvent struct {
	RecvBaseData
	Event    string
	BatchJob []JobResultInfo
}

// RespBaseData 描述被动响应消息的公共结构
type RespBaseData struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   base.CDATAText
	FromUserName base.CDATAText
	CreateTime   int
	MsgType      base.CDATAText
}

// RespTextMessage 描述被动响应的文本消息的结构
type RespTextMessage struct {
	RespBaseData
	Content base.CDATAText
}

// RespMedia 描述被动响应的媒体内容结构
type RespMedia struct {
	MediaID base.CDATAText `xml:"MediaId"`
}

// RespImageMessage 描述被动相应的图片消息结构
type RespImageMessage struct {
	RespBaseData
	Image RespMedia
}

// RespVoiceMessage 描述被动相应的语音消息结构
type RespVoiceMessage struct {
	RespBaseData
	Voice RespMedia
}

// RespVideoMedia 描述被动相应的视频媒体内容结构
type RespVideoMedia struct {
	MediaID     base.CDATAText `xml:"MediaId"`
	Title       base.CDATAText
	Description base.CDATAText
}

// RespVideoMessage 描述被动相应的语音消息结构
type RespVideoMessage struct {
	RespBaseData
	Video RespVideoMedia
}

// RespArticle 描述被动响应的图文消息结构
type RespArticle struct {
	Title       base.CDATAText
	Description base.CDATAText
	PicURL      base.CDATAText `xml:"PicUrl"`
	URL         base.CDATAText `xml:"Url"`
}

// RespArticleItem 描述被动响应的图文消息结构的包裹
type RespArticleItem struct {
	Item RespArticle `xml:"item"`
}

// RespNewsMessage 描述被动相应的图文消息结构
type RespNewsMessage struct {
	RespBaseData
	ArticleCount int
	Articles     []RespArticleItem
}

type recvMsgHandler struct {
	api *API
}

func (h *recvMsgHandler) Parse(body []byte, signature, timestamp, nonce string) (interface{}, error) {
	var err error

	reqBody := &base.RecvHTTPReqBody{}
	if err = xml.Unmarshal(body, reqBody); err != nil {
		return nil, err
	}

	if signature != h.api.MsgCrypter.GetSignature(timestamp, nonce, reqBody.Encrypt) {
		return nil, fmt.Errorf("validate signature error")
	}

	origData, corpID, err := h.api.MsgCrypter.Decrypt(reqBody.Encrypt)
	if err != nil {
		return nil, err
	}

	if corpID != h.api.CorpID {
		return nil, fmt.Errorf("the request is from corp[%s], not from corp[%s]", corpID, h.api.CorpID)
	}

	probeData := &struct {
		MsgType MessageType
		Event   string
	}{}

	if err = xml.Unmarshal(origData, probeData); err != nil {
		return nil, err
	}

	var data interface{}
	switch probeData.MsgType {
	case TextMsg:
		data = &RecvTextMessage{}
	case ImageMsg:
		data = &RecvImageMessage{}
	case VoiceMsg:
		data = &RecvVoiceMessage{}
	case VideoMsg:
		data = &RecvVideoMessage{}
	case LocationMsg:
		data = &RecvLocationMessage{}
	case EventMsg:
		switch probeData.Event {
		case SubscribeEvent, UnsubscribeEvent:
			data = &RecvSubscribeEvent{}
		case LocationEvent:
			data = &RecvLocationEvent{}
		case MenuClickEvent, MenuViewEvent:
			data = &RecvMenuEvent{}
		case ScanCodePushEvent, ScanCodeWaitMsgEvent:
			data = &RecvScanCodeEvent{}
		case PicSysPhotoEvent, PicPhotoOrAlbumEvent, PicWeiXinEvent:
			data = &RecvPicEvent{}
		case LocationSelectEvent:
			data = &RecvLocationSelectEvent{}
		case EnterAgentEvent:
			data = &RecvEnterAgentEvent{}
		case BatchJobResultEvent:
			data = &RecvBatchJobResultEvent{}
		default:
			return nil, fmt.Errorf("unknown event type: %s", probeData.Event)
		}
	default:
		return nil, fmt.Errorf("unknown message type: %s", probeData.MsgType)
	}

	if err = xml.Unmarshal(origData, data); err != nil {
		return nil, err
	}

	return data, nil
}

func (h *recvMsgHandler) Response(message []byte) ([]byte, error) {
	msgEncrypt, err := h.api.MsgCrypter.Encrypt(string(message))
	if err != nil {
		return nil, err
	}

	nonce := base.GenerateNonce()
	timestamp := base.GenerateTimestamp()
	signature := h.api.MsgCrypter.GetSignature(fmt.Sprintf("%d", timestamp), nonce, msgEncrypt)

	resp := &base.RecvHTTPRespBody{
		Encrypt:      base.StringToCDATA(msgEncrypt),
		MsgSignature: base.StringToCDATA(signature),
		TimeStamp:    timestamp,
		Nonce:        base.StringToCDATA(nonce),
	}

	return xml.MarshalIndent(resp, " ", "  ")
}

// NewRecvMsgHandler 方法用于创建消息接收处理器的实例
func (a *API) NewRecvMsgHandler() *recvMsgHandler {
	return &recvMsgHandler{a}
}
<commit_msg>fix bug<commit_after>package api

import (
	"encoding/xml"
	"fmt"

	"github.com/heroicyang/wechat-qy/base"
)

// 接收事件类型
const (
	SubscribeEvent       = "subscribe"
	UnsubscribeEvent     = "unsubscribe"
	LocationEvent        = "LOCATION"
	MenuClickEvent       = "CLICK"
	MenuViewEvent        = "VIEW"
	ScanCodePushEvent    = "scancode_push"
	ScanCodeWaitMsgEvent = "scancode_waitmsg"
	PicSysPhotoEvent     = "pic_sysphoto"
	PicPhotoOrAlbumEvent = "pic_photo_or_album"
	PicWeiXinEvent       = "pic_weixin"
	LocationSelectEvent  = "location_select"
	EnterAgentEvent      = "enter_agent"
	BatchJobResultEvent  = "batch_job_result"
)

// RecvBaseData 描述接收到的各类消息或事件的公共结构
type RecvBaseData struct {
	ToUserName   string
	FromUserName string
	CreateTime   int
	MsgType      MessageType
	AgentID      int64
}

// RecvTextMessage 描述接收到的文本类型消息结构
type RecvTextMessage struct {
	RecvBaseData
	MsgID   uint64 `xml:"MsgId"`
	Content string
}

// RecvImageMessage 描述接收到的图片类型消息结构
type RecvImageMessage struct {
	RecvBaseData
	MsgID   uint64 `xml:"MsgId"`
	PicURL  string `xml:"PicUrl"`
	MediaID string `xml:"MediaId"`
}

// RecvVoiceMessage 描述接收到的语音类型消息结构
type RecvVoiceMessage struct {
	RecvBaseData
	MsgID   uint64 `xml:"MsgId"`
	MediaID string `xml:"MediaId"`
	Format  string
}

// RecvVideoMessage 描述接收到的视频类型消息结构
type RecvVideoMessage struct {
	RecvBaseData
	MsgID        uint64 `xml:"MsgId"`
	MediaID      string `xml:"MediaId"`
	ThumbMediaID string `xml:"ThumbMediaId"`
}

// RecvLocationMessage 描述接收到的地理位置类型消息结构
type RecvLocationMessage struct {
	RecvBaseData
	MsgID     uint64  `xml:"MsgId"`
	LocationX float64 `xml:"Location_X"`
	LocationY float64 `xml:"Location_Y"`
	Scale     int
	Label     string
}

// RecvSubscribeEvent 描述成员关注/取消关注事件的结构
type RecvSubscribeEvent struct {
	RecvBaseData
	Event string
}

// RecvLocationEvent 描述上报地理位置事件的结构
type RecvLocationEvent struct {
	RecvBaseData
	Event     string
	Latitude  float64
	Longitude float64
	Precision float64
}

// RecvMenuEvent 描述菜单事件的结构
type RecvMenuEvent struct {
	RecvBaseData
	Event    string
	EventKey string
}

// ScanCodeInfo 描述扫码事件的相关内容结构
type ScanCodeInfo struct {
	ScanType   string
	ScanResult string
}

// RecvScanCodeEvent 描述扫码推/扫码推事件且弹出“消息接收中”提示框类型事件的结构
type RecvScanCodeEvent struct {
	RecvBaseData
	Event        string
	EventKey     string
	ScanCodeInfo ScanCodeInfo
}

// SendPicMD5Sum 描述发图事件中单个图片的 MD5 信息
type SendPicMD5Sum struct {
	PicMd5Sum string
}

// SendPicItem 描述发图事件中单个图片信息结构
type SendPicItem struct {
	Item SendPicMD5Sum `xml:"item"`
}

// SendPicsInfo 描述发图事件的图片信息结构
type SendPicsInfo struct {
	Count   int64
	PicList []SendPicItem
}

// RecvPicEvent 描述发图事件的结构
type RecvPicEvent struct {
	RecvBaseData
	Event        string
	EventKey     string
	SendPicsInfo SendPicsInfo
}

// SendLocationInfo 描述弹出地理位置选择器事件中地理位置信息结构
type SendLocationInfo struct {
	LocationX float64 `xml:"Location_X"`
	LocationY float64 `xml:"Location_Y"`
	Scale     int
	Label     string
	PoiName   string `xml:"Poiname"`
}

// RecvLocationSelectEvent 描述弹出地理位置选择器事件的结构
type RecvLocationSelectEvent struct {
	RecvBaseData
	Event            string
	EventKey         string
	SendLocationInfo SendLocationInfo
}

// RecvEnterAgentEvent 描述成员进入应用事件的结构
type RecvEnterAgentEvent struct {
	RecvBaseData
	Event    string
	EventKey string
}

// JobResultInfo 描述异步任务完成事件中任务完成情况信息
type JobResultInfo struct {
	JobID   string `xml:"JobId"`
	JobType string
	ErrCode int
	ErrMsg  string
}

// RecvBatchJobResultEvent 描述异步任务完成事件的结构
type RecvBatchJobResultEvent struct {
	RecvBaseData
	Event    string
	BatchJob JobResultInfo
}

// RespBaseData 描述被动响应消息的公共结构
type RespBaseData struct {
	XMLName      xml.Name `xml:"xml"`
	ToUserName   base.CDATAText
	FromUserName base.CDATAText
	CreateTime   int
	MsgType      base.CDATAText
}

// RespTextMessage 描述被动响应的文本消息的结构
type RespTextMessage struct {
	RespBaseData
	Content base.CDATAText
}

// RespMedia 描述被动响应的媒体内容结构
type RespMedia struct {
	MediaID base.CDATAText `xml:"MediaId"`
}

// RespImageMessage 描述被动相应的图片消息结构
type RespImageMessage struct {
	RespBaseData
	Image RespMedia
}

// RespVoiceMessage 描述被动相应的语音消息结构
type RespVoiceMessage struct {
	RespBaseData
	Voice RespMedia
}

// RespVideoMedia 描述被动相应的视频媒体内容结构
type RespVideoMedia struct {
	MediaID     base.CDATAText `xml:"MediaId"`
	Title       base.CDATAText
	Description base.CDATAText
}

// RespVideoMessage 描述被动相应的语音消息结构
type RespVideoMessage struct {
	RespBaseData
	Video RespVideoMedia
}

// RespArticle 描述被动响应的图文消息结构
type RespArticle struct {
	Title       base.CDATAText
	Description base.CDATAText
	PicURL      base.CDATAText `xml:"PicUrl"`
	URL         base.CDATAText `xml:"Url"`
}

// RespArticleItem 描述被动响应的图文消息结构的包裹
type RespArticleItem struct {
	Item RespArticle `xml:"item"`
}

// RespNewsMessage 描述被动相应的图文消息结构
type RespNewsMessage struct {
	RespBaseData
	ArticleCount int
	Articles     []RespArticleItem
}

type recvMsgHandler struct {
	api *API
}

func (h *recvMsgHandler) Parse(body []byte, signature, timestamp, nonce string) (interface{}, error) {
	var err error

	reqBody := &base.RecvHTTPReqBody{}
	if err = xml.Unmarshal(body, reqBody); err != nil {
		return nil, err
	}

	if signature != h.api.MsgCrypter.GetSignature(timestamp, nonce, reqBody.Encrypt) {
		return nil, fmt.Errorf("validate signature error")
	}

	origData, corpID, err := h.api.MsgCrypter.Decrypt(reqBody.Encrypt)
	if err != nil {
		return nil, err
	}

	if corpID != h.api.CorpID {
		return nil, fmt.Errorf("the request is from corp[%s], not from corp[%s]", corpID, h.api.CorpID)
	}

	probeData := &struct {
		MsgType MessageType
		Event   string
	}{}

	if err = xml.Unmarshal(origData, probeData); err != nil {
		return nil, err
	}

	var data interface{}
	switch probeData.MsgType {
	case TextMsg:
		data = &RecvTextMessage{}
	case ImageMsg:
		data = &RecvImageMessage{}
	case VoiceMsg:
		data = &RecvVoiceMessage{}
	case VideoMsg:
		data = &RecvVideoMessage{}
	case LocationMsg:
		data = &RecvLocationMessage{}
	case EventMsg:
		switch probeData.Event {
		case SubscribeEvent, UnsubscribeEvent:
			data = &RecvSubscribeEvent{}
		case LocationEvent:
			data = &RecvLocationEvent{}
		case MenuClickEvent, MenuViewEvent:
			data = &RecvMenuEvent{}
		case ScanCodePushEvent, ScanCodeWaitMsgEvent:
			data = &RecvScanCodeEvent{}
		case PicSysPhotoEvent, PicPhotoOrAlbumEvent, PicWeiXinEvent:
			data = &RecvPicEvent{}
		case LocationSelectEvent:
			data = &RecvLocationSelectEvent{}
		case EnterAgentEvent:
			data = &RecvEnterAgentEvent{}
		case BatchJobResultEvent:
			data = &RecvBatchJobResultEvent{}
		default:
			return nil, fmt.Errorf("unknown event type: %s", probeData.Event)
		}
	default:
		return nil, fmt.Errorf("unknown message type: %s", probeData.MsgType)
	}

	if err = xml.Unmarshal(origData, data); err != nil {
		return nil, err
	}

	return data, nil
}

func (h *recvMsgHandler) Response(message []byte) ([]byte, error) {
	msgEncrypt, err := h.api.MsgCrypter.Encrypt(string(message))
	if err != nil {
		return nil, err
	}

	nonce := base.GenerateNonce()
	timestamp := base.GenerateTimestamp()
	signature := h.api.MsgCrypter.GetSignature(fmt.Sprintf("%d", timestamp), nonce, msgEncrypt)

	resp := &base.RecvHTTPRespBody{
		Encrypt:      base.StringToCDATA(msgEncrypt),
		MsgSignature: base.StringToCDATA(signature),
		TimeStamp:    timestamp,
		Nonce:        base.StringToCDATA(nonce),
	}

	return xml.MarshalIndent(resp, " ", "  ")
}

// NewRecvMsgHandler 方法用于创建消息接收处理器的实例
func (a *API) NewRecvMsgHandler() *recvMsgHandler {
	return &recvMsgHandler{a}
}
<|endoftext|>


<commit_before>// Copyright 2018 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// https://studygolang.com
// Author: polaris	polaris@studygolang.com

package app

import (
	"logic"

	"github.com/labstack/echo"
	"github.com/polaris1119/goutils"

	. "http"
)

type IndexController struct{}

// 注册路由
func (self IndexController) RegisterRoute(g *echo.Group) {
	g.GET("/home", self.Home)
	g.GET("/stat/site", self.WebsiteStat)
}

// Home 首页
func (IndexController) Home(ctx echo.Context) error {
	if len(logic.WebsiteSetting.IndexNavs) == 0 {
		return success(ctx, nil)
	}

	tab := ctx.QueryParam("tab")
	if tab == "" {
		tab = GetFromCookie(ctx, "INDEX_TAB")
	}

	if tab == "" {
		tab = logic.WebsiteSetting.IndexNavs[0].Tab
	}
	curPage := goutils.MustInt(ctx.QueryParam("p"), 1)
	paginator := logic.NewPaginatorWithPerPage(curPage, perPage)

	data := logic.DefaultIndex.FindData(ctx, tab, paginator)

	SetCookie(ctx, "INDEX_TAB", data["tab"].(string))

	data["all_nodes"] = logic.GenNodes()

	if tab == "all" {
		data["total"] = paginator.GetTotal()

	}
	return success(ctx, nil)
}

// WebsiteStat 网站统计信息
func (IndexController) WebsiteStat(ctx echo.Context) error {
	articleTotal := logic.DefaultArticle.Total()
	projectTotal := logic.DefaultProject.Total()
	topicTotal := logic.DefaultTopic.Total()
	cmtTotal := logic.DefaultComment.Total()
	resourceTotal := logic.DefaultResource.Total()
	bookTotal := logic.DefaultGoBook.Total()
	userTotal := logic.DefaultUser.Total()

	data := map[string]interface{}{
		"article":  articleTotal,
		"project":  projectTotal,
		"topic":    topicTotal,
		"resource": resourceTotal,
		"book":     bookTotal,
		"comment":  cmtTotal,
		"user":     userTotal,
	}

	return success(ctx, data)
}
<commit_msg>忘记返回数据了<commit_after>// Copyright 2018 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// https://studygolang.com
// Author: polaris	polaris@studygolang.com

package app

import (
	"logic"

	"github.com/labstack/echo"
	"github.com/polaris1119/goutils"

	. "http"
)

type IndexController struct{}

// 注册路由
func (self IndexController) RegisterRoute(g *echo.Group) {
	g.GET("/home", self.Home)
	g.GET("/stat/site", self.WebsiteStat)
}

// Home 首页
func (IndexController) Home(ctx echo.Context) error {
	if len(logic.WebsiteSetting.IndexNavs) == 0 {
		return success(ctx, nil)
	}

	tab := ctx.QueryParam("tab")
	if tab == "" {
		tab = GetFromCookie(ctx, "INDEX_TAB")
	}

	if tab == "" {
		tab = logic.WebsiteSetting.IndexNavs[0].Tab
	}
	curPage := goutils.MustInt(ctx.QueryParam("p"), 1)
	paginator := logic.NewPaginatorWithPerPage(curPage, perPage)

	data := logic.DefaultIndex.FindData(ctx, tab, paginator)

	SetCookie(ctx, "INDEX_TAB", data["tab"].(string))

	data["all_nodes"] = logic.GenNodes()

	if tab == "all" {
		data["total"] = paginator.GetTotal()

	}
	return success(ctx, data)
}

// WebsiteStat 网站统计信息
func (IndexController) WebsiteStat(ctx echo.Context) error {
	articleTotal := logic.DefaultArticle.Total()
	projectTotal := logic.DefaultProject.Total()
	topicTotal := logic.DefaultTopic.Total()
	cmtTotal := logic.DefaultComment.Total()
	resourceTotal := logic.DefaultResource.Total()
	bookTotal := logic.DefaultGoBook.Total()
	userTotal := logic.DefaultUser.Total()

	data := map[string]interface{}{
		"article":  articleTotal,
		"project":  projectTotal,
		"topic":    topicTotal,
		"resource": resourceTotal,
		"book":     bookTotal,
		"comment":  cmtTotal,
		"user":     userTotal,
	}

	return success(ctx, data)
}
<|endoftext|>


<commit_before>package interfaces

import (
	"time"
)

type FieldType int

const (
	StringType FieldType = iota
	IntType
	FloatType
)

type Field struct {
	Name  string
	Value interface{}
	Type  FieldType
}

type Point struct {
	Fields []*Field
}

type Timeseries struct {
	Name   string
	Points []*Point
}

type WriteRequest struct {
	Timeseries []*Timeseries
}

type Operation int

const (
	EqualOperation Operation = iota
	NotEqualOperation
	GreaterThanOperation
	GreaterThanOrEqualOperation
	LessThanOperation
	LessThanOrEqualOperation
)

type Condition interface {
	IsCondition()
}

type CombineOperation int

// definition of combining operators with order of precedence
const (
	NotOperation CombineOperation = iota // First has to be nil
	AndOperation
	OrOperation
)

type CombiningCondition struct {
	First     *Condition
	CombineOp CombineOperation
	Second    *Condition
}

type ComparisonCondition struct {
	FieldName string
	Op        Operation
	Value     interface{}
}

// TODO: applying functions and joining time series
type ReadRequest struct {
	Timeseries   string
	IsRegex      bool // is the timeseries name a regex?
	StartTime    time.Time
	EndTime      time.Time
	IsContinuous bool
	Conditions   []*Condition
}

type StorageEngineProcessingI interface {
	WritePoints(request *WriteRequest) error
	ReadPoints(request *ReadRequest, yield func(pts []*Point) error) error
}

type StorageEngineConsensusI interface {
	// TODO: figure out the requirements of this interface. Probably the following
	// 1. Transfer part(s) of the ring to other node(s)
	// 2. Give up ownership of part(s) of the ring
	// 3. Take ownership of part(s) of the ring
}
<commit_msg>normalize the field names so we don't have to repeat them with every point.<commit_after>package interfaces

import (
	"time"
)

type FieldType int

const (
	StringType FieldType = iota
	IntType
	FloatType
)

type Values []interface{}

type Point struct {
	Fields []string
	Types  []FieldType
	Values []Values
}

type Timeseries struct {
	Name   string
	Points []*Point
}

type WriteRequest struct {
	Timeseries []*Timeseries
}

type Operation int

const (
	EqualOperation Operation = iota
	NotEqualOperation
	GreaterThanOperation
	GreaterThanOrEqualOperation
	LessThanOperation
	LessThanOrEqualOperation
)

type Condition interface {
	IsCondition()
}

type CombineOperation int

// definition of combining operators with order of precedence
const (
	NotOperation CombineOperation = iota // First has to be nil
	AndOperation
	OrOperation
)

type CombiningCondition struct {
	First     *Condition
	CombineOp CombineOperation
	Second    *Condition
}

type ComparisonCondition struct {
	FieldName string
	Op        Operation
	Value     interface{}
}

// TODO: applying functions and joining time series
type ReadRequest struct {
	Timeseries   string
	IsRegex      bool // is the timeseries name a regex?
	StartTime    time.Time
	EndTime      time.Time
	IsContinuous bool
	Conditions   []*Condition
}

type StorageEngineProcessingI interface {
	WritePoints(request *WriteRequest) error
	ReadPoints(request *ReadRequest, yield func(pts []*Point) error) error
}

type StorageEngineConsensusI interface {
	// TODO: figure out the requirements of this interface. Probably the following
	// 1. Transfer part(s) of the ring to other node(s)
	// 2. Give up ownership of part(s) of the ring
	// 3. Take ownership of part(s) of the ring
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.

package app

import (
	"encoding/csv"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"github.com/nu7hatch/gouuid"
)

func PlayersNew(res http.ResponseWriter, req *http.Request) {
	o := &Options{LoggedIn: true}
	render(res, "players/new", o)
}

func PlayersCreate(res http.ResponseWriter, req *http.Request) {
	// Get a ne uuid.
	id, err := uuid.NewV4()
	if err != nil {
		http.Redirect(res, req, "/", http.StatusNotFound)
		return
	}

	// Try to create a new user and redirect properly.
	p := &Player{
		Id:         id.String(),
		Name:       req.FormValue("name"),
		Created_at: time.Now(),
	}
	Db.Insert(p)
	http.Redirect(res, req, "/", http.StatusFound)
}

func PlayersShow(res http.ResponseWriter, req *http.Request) {
	// Get the user to be shown.
	params := mux.Vars(req)
	players, _ := getStats(params["id"], true)

	// Let's make sure that the user exists.
	if len(players) == 0 {
		http.Redirect(res, req, "/", http.StatusFound)
		return
	}

	// Prepare parameters and generate the HTML code.
	o := &Options{One: players[0]}
	s, _ := store.Get(req, sessionName)
	id := s.Values["userId"]
	if IsUserLogged(id) {
		o.LoggedIn = true
		o.JS = true
		o.Download = "/players/" + params["id"] + "/csv"
	}
	render(res, "players/show", o)
}

func PlayersUpdate(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	query := "update players set name=$1 where id=$2"
	Db.Exec(query, req.FormValue("name"), params["id"])
	http.Redirect(res, req, "/", http.StatusFound)
}

func PlayersDelete(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	Db.Exec("delete from players where id=$1 and name=$2",
		params["id"], req.FormValue("name"))
	http.Redirect(res, req, "/", http.StatusFound)
}

func fetchRating(rating string) (int, error) {
	r, err := strconv.Atoi(rating)
	if err != nil {
		return 0, err
	}

	if r >= 0 && r <= 10 {
		return r, nil
	}
	return 0, errors.New("Invalid rating!")
}

func PlayersRate(res http.ResponseWriter, req *http.Request) {
	// Get the rating.
	params := mux.Vars(req)
	rating, err := fetchRating(req.FormValue("rating"))
	if err != nil {
		url := fmt.Sprintf("/players/%v/rate?error=true", params["id"])
		http.Redirect(res, req, url, http.StatusFound)
		return
	}

	// Insert the new rating.
	r := &Rating{
		Value:      rating,
		Player_id:  params["id"],
		Created_at: time.Now(),
	}
	e := Db.Insert(r)

	// Redirect.
	url := fmt.Sprintf("/players/%v/rate", params["id"])
	if e != nil {
		url += "?error=true"
	}
	http.Redirect(res, req, url, http.StatusFound)
}

func PlayersRated(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	p := &Options{Id: params["id"]}
	if req.FormValue("error") == "true" {
		p.Error = true
	}
	render(res, "players/rated", p)
}

func PlayersCsv(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	players, _ := getStats(params["id"], true)

	// Let's make sure that the user exists.
	if len(players) == 0 {
		http.Redirect(res, req, "/", http.StatusFound)
		return
	}

	// Write the CSV.
	writeCsv(res, players[0].Name, players)
}

func writeCsv(res http.ResponseWriter, name string, players []*ExtPlayer) {
	// Set the headers for CSV.
	res.Header().Set("Content-Type", "text/csv")
	cd := "attachment;filename=" + name + ".csv"
	res.Header().Set("Content-Disposition", cd)

	// Finally write the data.
	w := csv.NewWriter(res)
	for _, v := range players {
		min, max := strconv.Itoa(v.Min), strconv.Itoa(v.Max)
		w.Write([]string{v.Name, min, max, v.Avg})

		data := []string{v.Name}
		for _, r := range v.Ratings {
			data = append(data, strconv.Itoa(r.Value))
			data = append(data, fmtDate(r.Created_at))
		}
		w.Write(data)
		w.Write([]string{}) // Extra line.
	}
	w.Flush()
}
<commit_msg>Another typo.<commit_after>// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.

package app

import (
	"encoding/csv"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"github.com/nu7hatch/gouuid"
)

func PlayersNew(res http.ResponseWriter, req *http.Request) {
	o := &Options{LoggedIn: true}
	render(res, "players/new", o)
}

func PlayersCreate(res http.ResponseWriter, req *http.Request) {
	// Get a new uuid.
	id, err := uuid.NewV4()
	if err != nil {
		http.Redirect(res, req, "/", http.StatusNotFound)
		return
	}

	// Try to create a new user and redirect properly.
	p := &Player{
		Id:         id.String(),
		Name:       req.FormValue("name"),
		Created_at: time.Now(),
	}
	Db.Insert(p)
	http.Redirect(res, req, "/", http.StatusFound)
}

func PlayersShow(res http.ResponseWriter, req *http.Request) {
	// Get the user to be shown.
	params := mux.Vars(req)
	players, _ := getStats(params["id"], true)

	// Let's make sure that the user exists.
	if len(players) == 0 {
		http.Redirect(res, req, "/", http.StatusFound)
		return
	}

	// Prepare parameters and generate the HTML code.
	o := &Options{One: players[0]}
	s, _ := store.Get(req, sessionName)
	id := s.Values["userId"]
	if IsUserLogged(id) {
		o.LoggedIn = true
		o.JS = true
		o.Download = "/players/" + params["id"] + "/csv"
	}
	render(res, "players/show", o)
}

func PlayersUpdate(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	query := "update players set name=$1 where id=$2"
	Db.Exec(query, req.FormValue("name"), params["id"])
	http.Redirect(res, req, "/", http.StatusFound)
}

func PlayersDelete(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	Db.Exec("delete from players where id=$1 and name=$2",
		params["id"], req.FormValue("name"))
	http.Redirect(res, req, "/", http.StatusFound)
}

func fetchRating(rating string) (int, error) {
	r, err := strconv.Atoi(rating)
	if err != nil {
		return 0, err
	}

	if r >= 0 && r <= 10 {
		return r, nil
	}
	return 0, errors.New("Invalid rating!")
}

func PlayersRate(res http.ResponseWriter, req *http.Request) {
	// Get the rating.
	params := mux.Vars(req)
	rating, err := fetchRating(req.FormValue("rating"))
	if err != nil {
		url := fmt.Sprintf("/players/%v/rate?error=true", params["id"])
		http.Redirect(res, req, url, http.StatusFound)
		return
	}

	// Insert the new rating.
	r := &Rating{
		Value:      rating,
		Player_id:  params["id"],
		Created_at: time.Now(),
	}
	e := Db.Insert(r)

	// Redirect.
	url := fmt.Sprintf("/players/%v/rate", params["id"])
	if e != nil {
		url += "?error=true"
	}
	http.Redirect(res, req, url, http.StatusFound)
}

func PlayersRated(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	p := &Options{Id: params["id"]}
	if req.FormValue("error") == "true" {
		p.Error = true
	}
	render(res, "players/rated", p)
}

func PlayersCsv(res http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	players, _ := getStats(params["id"], true)

	// Let's make sure that the user exists.
	if len(players) == 0 {
		http.Redirect(res, req, "/", http.StatusFound)
		return
	}

	// Write the CSV.
	writeCsv(res, players[0].Name, players)
}

func writeCsv(res http.ResponseWriter, name string, players []*ExtPlayer) {
	// Set the headers for CSV.
	res.Header().Set("Content-Type", "text/csv")
	cd := "attachment;filename=" + name + ".csv"
	res.Header().Set("Content-Disposition", cd)

	// Finally write the data.
	w := csv.NewWriter(res)
	for _, v := range players {
		min, max := strconv.Itoa(v.Min), strconv.Itoa(v.Max)
		w.Write([]string{v.Name, min, max, v.Avg})

		data := []string{v.Name}
		for _, r := range v.Ratings {
			data = append(data, strconv.Itoa(r.Value))
			data = append(data, fmtDate(r.Created_at))
		}
		w.Write(data)
		w.Write([]string{}) // Extra line.
	}
	w.Flush()
}
<|endoftext|>


<commit_before>package web

import (
	"v2ray.com/core/app"
	"v2ray.com/core/common"
)

const (
	APP_ID = app.ID(8)
)

type Server interface {
	common.Releasable
	Handle()
}
<commit_msg>name confict<commit_after>package web

import (
	"v2ray.com/core/app"
	"v2ray.com/core/common"
)

const (
	APP_ID = app.ID(8)
)

type WebServer interface {
	common.Releasable
	Handle()
}
<|endoftext|>


<commit_before>package web

import (
	"crypto/tls"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var (
	_app  *Application
	_once sync.Once
)

// Data Data
type Data interface{}

// Callback function
type Callback func(ctx *Context) (Data, error)

// PanicCallback function
type PanicCallback func(http.ResponseWriter, *http.Request, interface{})

// Application is type of a web.Application
type Application struct {
	trees          map[string]*node
	formReader     Reader
	formDataReader Reader
	binaryReader   Reader
	binaryWriter   Writer
	logger         *log.Logger
	panic          PanicCallback
	paramsPool     sync.Pool
	maxParams      uint16
	extension      string

	NotFound http.Handler
}

// CreateApplication return a singleton web.Application
func CreateApplication() *Application {
	_once.Do(func() {
		_app = &Application{}
	})
	return _app
}

// app return web.Application
func app() *Application {
	if _app == nil {
		return CreateApplication()
	}
	return _app
}

// SetFormReader set formReader
func (app *Application) SetFormReader(formReader Reader) {
	app.formReader = formReader
}

// SetFormDataReader set formDataReader
func (app *Application) SetFormDataReader(formDataReader Reader) {
	app.formDataReader = formDataReader
}

// SetBinaryReader set binaryReader
func (app *Application) SetBinaryReader(binaryReader Reader) {
	app.binaryReader = binaryReader
}

// SetBinaryWriter set binaryWriter
func (app *Application) SetBinaryWriter(binaryWriter Writer) {
	app.binaryWriter = binaryWriter
}

// SetLogger set Logger
func (app *Application) SetLogger(logger *log.Logger) {
	app.logger = logger
}

// SetPanic set set Panic
func (app *Application) SetPanic(panic PanicCallback) {
	app.panic = panic
}

// SetExtension set Extension
func (app *Application) SetExtension(ext string) {
	if !strings.HasPrefix(ext, ".") {
		ext = "." + ext
	}
	app.extension = ext
}

// Use Add the given callback function to this application.middlewares.
func (app *Application) Use(path string, callback Callback) {

}

// On add event
func (app *Application) On(name string, cb Callback) {

}

// Get method
func (app *Application) Get(path string, cb Callback) {
	app.addRoute(http.MethodGet, path, cb)
}

// Head method
func (app *Application) Head(path string, cb Callback) {
	app.addRoute(http.MethodHead, path, cb)
}

// Post method
func (app *Application) Post(path string, cb Callback) {
	app.addRoute(http.MethodPost, path, cb)
}

// Put method
func (app *Application) Put(path string, cb Callback) {
	app.addRoute(http.MethodPut, path, cb)
}

// Patch method
func (app *Application) Patch(path string, cb Callback) {
	app.addRoute(http.MethodPatch, path, cb)
}

// Delete method
func (app *Application) Delete(path string, cb Callback) {
	app.addRoute(http.MethodDelete, path, cb)
}

// Options method
func (app *Application) Options(path string, cb Callback) {
	app.addRoute(http.MethodOptions, path, cb)
}

func (app *Application) addRoute(method, path string, cb Callback) {

	if method == "" {
		panic("method must not be empty")
	}

	if len(path) < 1 || path[0] != '/' {
		panic("path must begin with '/' in path '" + path + "'")
	}

	if cb == nil {
		panic("callback must not be nil")
	}

	if app.trees == nil {
		app.trees = make(map[string]*node)
	}

	root := app.trees[method]

	if root == nil {
		root = new(node)
		app.trees[method] = root
	}

	root.addRoute(path, cb)

	if pc := countParams(path); pc > app.maxParams {
		app.maxParams = pc
	}
}

// ServeFiles ("/src/*filepath", http.Dir("/var/www"))
func (app *Application) ServeFiles(path string, root http.FileSystem) {
	if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
		panic("path must end with /*filepath in path '" + path + "'")
	}

	fileServer := http.FileServer(root)

	app.Get(path, func(ctx *Context) (Data, error) {
		ctx.R.URL.Path = ctx.Param("filepath")
		fileServer.ServeHTTP(ctx.W, ctx.R)
		return nil, nil
	})
}

// ServeHTTP w, r
func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer app.recv(w, r)

	path := r.URL.Path

	if filepath.Ext(path) != app.extension {
		http.NotFound(w, r)
		return
	}

	if root := app.trees[r.Method]; root != nil {

		if callback, params, _ := root.getValue(path, app.getParams); callback != nil {

			ctx := createContext(w, r, params)

			val, err := callback(ctx)

			app.putParams(params)

			if err != nil {

				switch err {
				case ErrUnauthorized:
					ctx.SetStatus(http.StatusUnauthorized)
				case ErrForbidden:
					ctx.SetStatus(http.StatusForbidden)
				default:
					ctx.SetStatus(http.StatusBadRequest)
				}

				app.logf("%s %s %d %s %s %d %v", r.RemoteAddr, r.Host, ctx.UserID(), r.Method, path, ctx.Status(), err)

				if err := ctx.Write(err.Error()); err != nil {
					app.logf("ctx.write err: %v", err)
				}

				return
			}

			if val != nil {
				if err := ctx.Write(val); err != nil {
					app.logf("ctx.write err: %v", err)
				}
			}

			return
		}
	}

	if app.NotFound != nil {
		app.NotFound.ServeHTTP(w, r)
	} else {
		http.NotFound(w, r)
	}
}

// ListenAndServe Serve with options on addr
func (app *Application) ListenAndServe(addr string, fns ...func(*http.Server)) error {

	l, err := net.Listen("tcp", addr)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(addr, l, fns...)
}

// ListenAndServeTLS Serve with tls and options on addr
func (app *Application) ListenAndServeTLS(addr string, tlsConfig *tls.Config, fns ...func(*http.Server)) error {

	l, err := tls.Listen("tcp", addr, tlsConfig)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(addr, l, fns...)
}

func (app *Application) serve(addr string, listener net.Listener, fns ...func(*http.Server)) error {

	mux := http.NewServeMux()

	mux.Handle("/", app)

	srv := &http.Server{
		Handler: mux,
	}

	for _, fn := range fns {
		fn(srv)
	}

	if app.paramsPool.New == nil && app.maxParams > 0 {
		app.paramsPool.New = func() interface{} {
			ps := make(Params, 0, app.maxParams)
			return &ps
		}
	}

	if err := srv.Serve(listener); err != nil {
		return err
	}

	if err := srv.Close(); err != nil {
		return err
	}

	return nil
}

// Inspect method
func (app *Application) Inspect() string {
	return ""
}

// logf write log
func (app *Application) logf(format string, v ...interface{}) {
	if app.logger == nil {
		app.logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	}

	app.logger.Printf(format, v...)
}

func (app *Application) getParams() *Params {
	ps := app.paramsPool.Get().(*Params)
	*ps = (*ps)[0:0] // reset slice
	return ps
}

func (app *Application) putParams(ps *Params) {
	if ps != nil {
		app.paramsPool.Put(ps)
	}
}

func (app *Application) recv(w http.ResponseWriter, r *http.Request) {
	if rcv := recover(); rcv != nil {
		if app.panic != nil {
			app.panic(w, r, rcv)
		} else {
			app.logf("%s %s %s %s rcv: %v", r.RemoteAddr, r.Host, r.Method, r.URL.Path, rcv)
		}
	}
}
<commit_msg>added network<commit_after>package web

import (
	"crypto/tls"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var (
	_app  *Application
	_once sync.Once
)

// Data Data
type Data interface{}

// Callback function
type Callback func(ctx *Context) (Data, error)

// PanicCallback function
type PanicCallback func(http.ResponseWriter, *http.Request, interface{})

// Application is type of a web.Application
type Application struct {
	trees          map[string]*node
	formReader     Reader
	formDataReader Reader
	binaryReader   Reader
	binaryWriter   Writer
	logger         *log.Logger
	panic          PanicCallback
	paramsPool     sync.Pool
	maxParams      uint16
	extension      string

	NotFound http.Handler
}

// CreateApplication return a singleton web.Application
func CreateApplication() *Application {
	_once.Do(func() {
		_app = &Application{}
	})
	return _app
}

// app return web.Application
func app() *Application {
	if _app == nil {
		return CreateApplication()
	}
	return _app
}

// SetFormReader set formReader
func (app *Application) SetFormReader(formReader Reader) {
	app.formReader = formReader
}

// SetFormDataReader set formDataReader
func (app *Application) SetFormDataReader(formDataReader Reader) {
	app.formDataReader = formDataReader
}

// SetBinaryReader set binaryReader
func (app *Application) SetBinaryReader(binaryReader Reader) {
	app.binaryReader = binaryReader
}

// SetBinaryWriter set binaryWriter
func (app *Application) SetBinaryWriter(binaryWriter Writer) {
	app.binaryWriter = binaryWriter
}

// SetLogger set Logger
func (app *Application) SetLogger(logger *log.Logger) {
	app.logger = logger
}

// SetPanic set set Panic
func (app *Application) SetPanic(panic PanicCallback) {
	app.panic = panic
}

// SetExtension set Extension
func (app *Application) SetExtension(ext string) {
	if !strings.HasPrefix(ext, ".") {
		ext = "." + ext
	}
	app.extension = ext
}

// Use Add the given callback function to this application.middlewares.
func (app *Application) Use(path string, callback Callback) {

}

// On add event
func (app *Application) On(name string, cb Callback) {

}

// Get method
func (app *Application) Get(path string, cb Callback) {
	app.addRoute(http.MethodGet, path, cb)
}

// Head method
func (app *Application) Head(path string, cb Callback) {
	app.addRoute(http.MethodHead, path, cb)
}

// Post method
func (app *Application) Post(path string, cb Callback) {
	app.addRoute(http.MethodPost, path, cb)
}

// Put method
func (app *Application) Put(path string, cb Callback) {
	app.addRoute(http.MethodPut, path, cb)
}

// Patch method
func (app *Application) Patch(path string, cb Callback) {
	app.addRoute(http.MethodPatch, path, cb)
}

// Delete method
func (app *Application) Delete(path string, cb Callback) {
	app.addRoute(http.MethodDelete, path, cb)
}

// Options method
func (app *Application) Options(path string, cb Callback) {
	app.addRoute(http.MethodOptions, path, cb)
}

func (app *Application) addRoute(method, path string, cb Callback) {

	if method == "" {
		panic("method must not be empty")
	}

	if len(path) < 1 || path[0] != '/' {
		panic("path must begin with '/' in path '" + path + "'")
	}

	if cb == nil {
		panic("callback must not be nil")
	}

	if app.trees == nil {
		app.trees = make(map[string]*node)
	}

	root := app.trees[method]

	if root == nil {
		root = new(node)
		app.trees[method] = root
	}

	root.addRoute(path, cb)

	if pc := countParams(path); pc > app.maxParams {
		app.maxParams = pc
	}
}

// ServeFiles ("/src/*filepath", http.Dir("/var/www"))
func (app *Application) ServeFiles(path string, root http.FileSystem) {
	if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
		panic("path must end with /*filepath in path '" + path + "'")
	}

	fileServer := http.FileServer(root)

	app.Get(path, func(ctx *Context) (Data, error) {
		ctx.R.URL.Path = ctx.Param("filepath")
		fileServer.ServeHTTP(ctx.W, ctx.R)
		return nil, nil
	})
}

// ServeHTTP w, r
func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer app.recv(w, r)

	path := r.URL.Path

	if filepath.Ext(path) != app.extension {
		http.NotFound(w, r)
		return
	}

	if root := app.trees[r.Method]; root != nil {

		if callback, params, _ := root.getValue(path, app.getParams); callback != nil {

			ctx := createContext(w, r, params)

			val, err := callback(ctx)

			app.putParams(params)

			if err != nil {

				switch err {
				case ErrUnauthorized:
					ctx.SetStatus(http.StatusUnauthorized)
				case ErrForbidden:
					ctx.SetStatus(http.StatusForbidden)
				default:
					ctx.SetStatus(http.StatusBadRequest)
				}

				app.logf("%s %s %d %s %s %d %v", r.RemoteAddr, r.Host, ctx.UserID(), r.Method, path, ctx.Status(), err)

				if err := ctx.Write(err.Error()); err != nil {
					app.logf("ctx.write err: %v", err)
				}

				return
			}

			if val != nil {
				if err := ctx.Write(val); err != nil {
					app.logf("ctx.write err: %v", err)
				}
			}

			return
		}
	}

	if app.NotFound != nil {
		app.NotFound.ServeHTTP(w, r)
	} else {
		http.NotFound(w, r)
	}
}

// ListenAndServe Serve with options on addr
func (app *Application) ListenAndServe(network string, addr string, fns ...func(*http.Server)) error {

	l, err := net.Listen(network, addr)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(l, fns...)
}

// ListenAndServeTLS Serve with tls and options on addr
func (app *Application) ListenAndServeTLS(network string, addr string, tlsConfig *tls.Config, fns ...func(*http.Server)) error {

	l, err := tls.Listen(network, addr, tlsConfig)

	if err != nil {
		return err
	}

	defer l.Close()

	return app.serve(l, fns...)
}

func (app *Application) serve(listener net.Listener, fns ...func(*http.Server)) error {

	mux := http.NewServeMux()

	mux.Handle("/", app)

	srv := &http.Server{
		Handler: mux,
	}

	for _, fn := range fns {
		fn(srv)
	}

	if app.paramsPool.New == nil && app.maxParams > 0 {
		app.paramsPool.New = func() interface{} {
			ps := make(Params, 0, app.maxParams)
			return &ps
		}
	}

	if err := srv.Serve(listener); err != nil {
		return err
	}

	if err := srv.Close(); err != nil {
		return err
	}

	return nil
}

// Inspect method
func (app *Application) Inspect() string {
	return ""
}

// logf write log
func (app *Application) logf(format string, v ...interface{}) {
	if app.logger == nil {
		app.logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	}

	app.logger.Printf(format, v...)
}

func (app *Application) getParams() *Params {
	ps := app.paramsPool.Get().(*Params)
	*ps = (*ps)[0:0] // reset slice
	return ps
}

func (app *Application) putParams(ps *Params) {
	if ps != nil {
		app.paramsPool.Put(ps)
	}
}

func (app *Application) recv(w http.ResponseWriter, r *http.Request) {
	if rcv := recover(); rcv != nil {
		if app.panic != nil {
			app.panic(w, r, rcv)
		} else {
			app.logf("%s %s %s %s rcv: %v", r.RemoteAddr, r.Host, r.Method, r.URL.Path, rcv)
		}
	}
}
<|endoftext|>