Skip to content

Commit

Permalink
Import rest of butler exeprops code, clean up a bit, add test file wi…
Browse files Browse the repository at this point in the history
…th icon and manifest
  • Loading branch information
fasterthanlime committed Mar 26, 2018
1 parent 6dbab94 commit dbd9d23
Show file tree
Hide file tree
Showing 19 changed files with 716 additions and 30 deletions.
18 changes: 18 additions & 0 deletions langs.go
@@ -0,0 +1,18 @@
package pelican

// see
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd318693(v=vs.85).aspx
func isLanguageWhitelisted(key string) bool {
localeID := key[:4]
primaryLangID := localeID[2:]

switch primaryLangID {
// neutral
case "00":
return true
// english
case "09":
return true
}
return false
}
90 changes: 90 additions & 0 deletions manifest.go
@@ -0,0 +1,90 @@
package pelican

import (
"encoding/json"

"github.com/go-errors/errors"
)

type node = map[string]interface{}

func visit(n node, key string, f func(c node)) {
if c, ok := n[key].(node); ok {
f(c)
}
}

func visitMany(n node, key string, f func(c node)) {
if cs, ok := n[key].([]node); ok {
for _, c := range cs {
f(c)
}
}
if c, ok := n[key].(node); ok {
f(c)
}
}

func getString(n node, key string, f func(s string)) {
if s, ok := n[key].(string); ok {
f(s)
}
}

func interpretManifest(info *PeInfo, manifest []byte) error {
intermediate := make(node)
err := json.Unmarshal([]byte(manifest), &intermediate)
if err != nil {
return errors.Wrap(err, 0)
}

assInfo := &AssemblyInfo{}

interpretIdentity := func(id node, f func(id *AssemblyIdentity)) {
ai := &AssemblyIdentity{}
getString(id, "-name", func(s string) { ai.Name = s })
getString(id, "-version", func(s string) { ai.Version = s })
getString(id, "-type", func(s string) { ai.Type = s })

getString(id, "-processorArchitecture", func(s string) { ai.ProcessorArchitecture = s })
getString(id, "-publicKeyToken", func(s string) { ai.PublicKeyToken = s })
getString(id, "-language", func(s string) { ai.Language = s })
f(ai)
}

visit(intermediate, "assembly", func(assembly node) {
visit(assembly, "assemblyIdentity", func(id node) {
interpretIdentity(id, func(ai *AssemblyIdentity) {
assInfo.Identity = ai
})
})

getString(assembly, "description", func(s string) { assInfo.Description = s })

visit(assembly, "trustInfo", func(ti node) {
visit(ti, "security", func(sec node) {
visit(sec, "requestedPrivileges", func(rp node) {
visit(rp, "requestedExecutionLevel", func(rel node) {
getString(rel, "-level", func(s string) {
assInfo.RequestedExecutionLevel = s
})
})
})
})
})

visit(assembly, "dependency", func(dep node) {
visitMany(dep, "dependentAssembly", func(da node) {
visit(da, "assemblyIdentity", func(id node) {
interpretIdentity(id, func(ai *AssemblyIdentity) {
info.DependentAssemblies = append(info.DependentAssemblies, ai)
})
})
})
})
})

info.AssemblyInfo = assInfo

return nil
}
35 changes: 19 additions & 16 deletions probe.go
Expand Up @@ -5,37 +5,40 @@ import (

"github.com/go-errors/errors"
"github.com/itchio/wharf/eos"
"github.com/itchio/wharf/state"
)

type Arch string

const (
Arch386 = "386"
ArchAmd64 = "amd64"
)

type PeInfo struct {
Arch Arch
}

type ProbeParams struct {
// nothing yet
Consumer *state.Consumer
}

// Probe retrieves information about an PE file
func Probe(file eos.File, params *ProbeParams) (*PeInfo, error) {
if params == nil {
return nil, errors.New("params must be set")
}
consumer := params.Consumer

pf, err := pe.NewFile(file)
if err != nil {
return nil, errors.Wrap(err, 0)
}

res := &PeInfo{}
info := &PeInfo{
VersionProperties: make(map[string]string),
}

switch pf.Machine {
case pe.IMAGE_FILE_MACHINE_I386:
res.Arch = Arch386
info.Arch = "386"
case pe.IMAGE_FILE_MACHINE_AMD64:
res.Arch = ArchAmd64
info.Arch = "amd64"
}
return res, nil

sect := pf.Section(".rsrc")
if sect != nil {
parseResources(consumer, info, sect)
}

return info, nil
}
78 changes: 64 additions & 14 deletions probe_test.go
Expand Up @@ -5,54 +5,104 @@ import (

"github.com/itchio/pelican"
"github.com/itchio/wharf/eos"
"github.com/itchio/wharf/state"
"github.com/stretchr/testify/assert"
)

func testProbeParams(t *testing.T) *pelican.ProbeParams {
return &pelican.ProbeParams{
Consumer: &state.Consumer{
OnMessage: func(level string, message string) {
t.Logf("[%s] %s", level, message)
},
},
}
}

func Test_NotPeFile(t *testing.T) {
f, err := eos.Open("./testdata/hello.c")
f, err := eos.Open("./testdata/hello/hello.c")
assert.NoError(t, err)
defer f.Close()

_, err = pelican.Probe(f, nil)
_, err = pelican.Probe(f, testProbeParams(t))
assert.Error(t, err)
}

func Test_Hello32Mingw(t *testing.T) {
f, err := eos.Open("./testdata/hello32-mingw.exe")
f, err := eos.Open("./testdata/hello/hello32-mingw.exe")
assert.NoError(t, err)
defer f.Close()

res, err := pelican.Probe(f, nil)
info, err := pelican.Probe(f, testProbeParams(t))
assert.NoError(t, err)
assert.EqualValues(t, pelican.Arch386, res.Arch)
assert.EqualValues(t, pelican.Arch386, info.Arch)
}

func Test_Hello32Msvc(t *testing.T) {
f, err := eos.Open("./testdata/hello32-msvc.exe")
f, err := eos.Open("./testdata/hello/hello32-msvc.exe")
assert.NoError(t, err)
defer f.Close()

res, err := pelican.Probe(f, nil)
info, err := pelican.Probe(f, testProbeParams(t))
assert.NoError(t, err)
assert.EqualValues(t, pelican.Arch386, res.Arch)
assert.EqualValues(t, pelican.Arch386, info.Arch)
}

func Test_Hello64Mingw(t *testing.T) {
f, err := eos.Open("./testdata/hello64-mingw.exe")
f, err := eos.Open("./testdata/hello/hello64-mingw.exe")
assert.NoError(t, err)
defer f.Close()

res, err := pelican.Probe(f, nil)
info, err := pelican.Probe(f, testProbeParams(t))
assert.NoError(t, err)
assert.EqualValues(t, pelican.ArchAmd64, res.Arch)
assert.EqualValues(t, pelican.ArchAmd64, info.Arch)
}

func Test_Hello64Msvc(t *testing.T) {
f, err := eos.Open("./testdata/hello64-msvc.exe")
f, err := eos.Open("./testdata/hello/hello64-msvc.exe")
assert.NoError(t, err)
defer f.Close()

info, err := pelican.Probe(f, testProbeParams(t))
assert.NoError(t, err)
assert.EqualValues(t, pelican.ArchAmd64, info.Arch)
}

func assertResources(t *testing.T, info *pelican.PeInfo) {
vp := info.VersionProperties
assert.EqualValues(t, "itch corp.", vp["CompanyName"])
assert.EqualValues(t, "Test PE file for pelican", vp["FileDescription"])
assert.EqualValues(t, "3.14", vp["FileVersion"])
assert.EqualValues(t, "resourceful", vp["InternalName"])
assert.EqualValues(t, "(c) 2018 itch corp.", vp["LegalCopyright"])

// totally a mistake, but leaving this as a reminder that
// not everything is worth fixing
assert.EqualValues(t, "butler", vp["ProductName"])

assert.EqualValues(t, "6.28", vp["ProductVersion"])
}

func Test_Resourceful32Mingw(t *testing.T) {
f, err := eos.Open("./testdata/resourceful/resourceful32-mingw.exe")
assert.NoError(t, err)
defer f.Close()

info, err := pelican.Probe(f, testProbeParams(t))
assert.NoError(t, err)
assert.EqualValues(t, pelican.Arch386, info.Arch)

assertResources(t, info)
}

func Test_Resourceful64Mingw(t *testing.T) {
f, err := eos.Open("./testdata/resourceful/resourceful64-mingw.exe")
assert.NoError(t, err)
defer f.Close()

res, err := pelican.Probe(f, nil)
info, err := pelican.Probe(f, testProbeParams(t))
assert.NoError(t, err)
assert.EqualValues(t, pelican.ArchAmd64, res.Arch)
assert.EqualValues(t, pelican.ArchAmd64, info.Arch)

assertResources(t, info)
}

0 comments on commit dbd9d23

Please sign in to comment.