Looking at the latest release (1.2) zip package - how can I unzip a file that was password protected (using 7zip, AES-256 encoding)? I don't see where/how to add in that information. A simple example would be great!
The archive/zip package seems to only provide basic zip functionality.
I would use 7zip to unzip password protected zip files using the os/exec package.
Online 7-zip user guide
The best guide for understanding 7zip is 7-zip.chm, which is in the zip file for the windows command line.
The following code isn't optimal but it shows you how to get the job done.
Code for extracting a password protected zip using 7zip
func extractZipWithPassword() {
fmt.Printf("Unzipping `%s` to directory `%s`\n", zip_path, extract_path)
commandString := fmt.Sprintf(`7za e %s -o%s -p"%s" -aoa`, zip_path, extract_path, zip_password)
commandSlice := strings.Fields(commandString)
fmt.Println(commandString)
c := exec.Command(commandSlice[0], commandSlice[1:]...)
e := c.Run()
checkError(e)
}
Example Program
// Shows how to extract an passsword encrypted zip file using 7zip.
// By Larry Battle <https://github.com/LarryBattle>
// Answer to http://stackoverflow.com/questions/20330210/golang-1-2-unzip-password-protected-zip-file
// 7-zip.chm - http://sevenzip.sourceforge.jp/chm/cmdline/switches/index.htm
// Effective Golang - http://golang.org/doc/effective_go.html
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
var (
txt_content = "Sample file created."
txt_filename = "name.txt"
zip_filename = "sample.zip"
zip_password = "42"
zip_encryptType = "AES256"
base_path = "./"
test_path = filepath.Join(base_path, "test")
src_path = filepath.Join(test_path, "src")
extract_path = filepath.Join(test_path, "extracted")
extracted_txt_path = filepath.Join(extract_path, txt_filename)
txt_path = filepath.Join(src_path, txt_filename)
zip_path = filepath.Join(src_path, zip_filename)
)
var txt_fileSize int64
func checkError(e error) {
if e != nil {
panic(e)
}
}
func setupTestDir() {
fmt.Printf("Removing `%s`\n", test_path)
var e error
os.Remove(test_path)
fmt.Printf("Creating `%s`,`%s`\n", extract_path, src_path)
e = os.MkdirAll(src_path, os.ModeDir|os.ModePerm)
checkError(e)
e = os.MkdirAll(extract_path, os.ModeDir|os.ModePerm)
checkError(e)
}
func createSampleFile() {
fmt.Println("Creating", txt_path)
file, e := os.Create(txt_path)
checkError(e)
defer file.Close()
_, e = file.WriteString(txt_content)
checkError(e)
fi, e := file.Stat()
txt_fileSize = fi.Size()
}
func createZipWithPassword() {
fmt.Println("Creating", zip_path)
commandString := fmt.Sprintf(`7za a %s %s -p"%s" -mem=%s`, zip_path, txt_path, zip_password, zip_encryptType)
commandSlice := strings.Fields(commandString)
fmt.Println(commandString)
c := exec.Command(commandSlice[0], commandSlice[1:]...)
e := c.Run()
checkError(e)
}
func extractZipWithPassword() {
fmt.Printf("Unzipping `%s` to directory `%s`\n", zip_path, extract_path)
commandString := fmt.Sprintf(`7za e %s -o%s -p"%s" -aoa`, zip_path, extract_path, zip_password)
commandSlice := strings.Fields(commandString)
fmt.Println(commandString)
c := exec.Command(commandSlice[0], commandSlice[1:]...)
e := c.Run()
checkError(e)
}
func checkFor7Zip() {
_, e := exec.LookPath("7za")
if e != nil {
fmt.Println("Make sure 7zip is install and include your path.")
}
checkError(e)
}
func checkExtractedFile() {
fmt.Println("Reading", extracted_txt_path)
file, e := os.Open(extracted_txt_path)
checkError(e)
defer file.Close()
buf := make([]byte, txt_fileSize)
n, e := file.Read(buf)
checkError(e)
if !strings.Contains(string(buf[:n]), strings.Fields(txt_content)[0]) {
panic(fmt.Sprintf("File`%s` is corrupted.\n", extracted_txt_path))
}
}
func main() {
fmt.Println("# Setup")
checkFor7Zip()
setupTestDir()
createSampleFile()
createZipWithPassword()
fmt.Println("# Answer to question...")
extractZipWithPassword()
checkExtractedFile()
fmt.Println("Done.")
}
Output
# Setup
Removing `test`
Creating `test/extracted`,`test/src`
Creating test/src/name.txt
Creating test/src/sample.zip
7za a test/src/sample.zip test/src/name.txt -p"42" -mem=AES256
# Answer to question...
Unzipping `test/src/sample.zip` to directory `test/extracted`
7za e test/src/sample.zip -otest/extracted -p"42" -aoa
Reading test/extracted/name.txt
Done.
https://github.com/yeka/zip provides functionality to extract password protected zip file (AES & Zip Standard Encryption aka ZipCrypto).
Below is an example how to use it:
package main
import (
"os"
"io"
"github.com/yeka/zip"
)
func main() {
file := "file.zip"
password := "password"
r, err := zip.OpenReader(file)
if nil != err {
panic(err)
}
defer r.Close()
for _, f := range r.File {
f.SetPassword(password)
w, err := os.Create(f.Name)
if nil != err {
panic(err)
}
io.Copy(w, f)
w.Close()
}
}
The work is a fork from https://github.com/alexmullins/zip which add support for AES only.
If anyone else runs into this the extraction failing with a password error, try removing the quotes. In my case they were being escaped by go and was causing the extraction to fail.
Related
My goal is to create a PDF to CSV. In this case, converting journal entries from the PDF file into CSV file.
What I've tried:
use pdftotext from linux.
The Installation:
$ sudo apt-get install poppler-utils
The code:
package main
import (
"fmt"
"os/exec"
)
func main() {
body, err := exec.Command("pdftotext", "-layout", "-q", "-nopgbrk", "-enc", "UTF-8", "-eol", "unix", "/Volumes/T7Touch/Learn/e-statement-to-t-account/5725299769Jul2022.pdf", "-").Output()
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
Does it work?
The -layout option makes the output workable. The fmt.Println(string(output)) will print every journal entry per line.
row1column1 row1column2 row1column3
row2column1 row2column2 row2column2
Without the -layout option, the output will be not readable.
row1column1
row2column1
row1column2
row2column2
The problem is that this solution need to install poppler-utils to use pdftotext. Without it, it will throws error.
$ GOOS=darwin GOARCH=arm64 go build
$ ./main // will throw errors -> panic: exec: "pdftotext": executable file not found in $PATH
use https://github.com/ledongthuc/pdf
The code
package main
import (
"fmt"
"os"
"github.com/ledongthuc/pdf"
)
func main() {
content, err := readPdf(os.Args[1]) // Read local pdf file
if err != nil {
panic(err)
}
fmt.Println(content)
return
}
func readPdf(path string) (string, error) {
f, r, err := pdf.Open(path)
defer func() {
_ = f.Close()
}()
if err != nil {
return "", err
}
totalPage := r.NumPage()
for pageIndex := 1; pageIndex <= totalPage; pageIndex++ {
p := r.Page(pageIndex)
if p.V.IsNull() {
continue
}
rows, _ := p.GetTextByRow()
for _, row := range rows {
println(">>>> row: ", row.Position)
for _, word := range row.Content {
fmt.Println(word.S)
}
}
}
return "", nil
}
I intend to create pdf decoder that does exactly like $ pdftotext -layout without the need to do $ sudo apt-get install popper-utils. I believe that I can solve this if someone can help point out where to see pdftotext repository or RFC docs for PDF.
I am rather new to working with yaml and golang. Currently, I am creating a golang program that parses an rpm package to check for subsystem dependencies. It extends the go-rpmutils library.
So far this is the code I have within my main function to handle conditions:
func main() {
// Parse the rpm
rpm, err := rpmutils.ReadRpm("file.rpm")
if err != nil {
panic(err)
}
// Get RPM Deps
dependencies, err := rpm.Header.GetStrings(rpmutils.REQUIRENAME)
if err != nil {
panic(err)
}
// Check for specific dep condition
for _, p := range dependencies {
if strings.HasPrefix(p, "prefix1") && p != "string-including-prefix1" {
fmt.Printf("\t%s\n", p)
defer os.Exit(1)
}
}
}
I am able to output the dependencies but want to set up several if else conditions for when specific subsystem dependencies exist.
In a separate yaml file, I have:
allowed-deps:
-dep1
-dep2
-dep3
third-party-deps:
-dep4
-dep5
-dep6
internal-deps:
-dep7
-dep8
-dep9
I'd like to compare the value of var p from the for loop with the values in the yaml file. So for example:
if p only equals values from allowed-deps, print "successfully built rpm" and do not prompt os.Exit(1)
if p equals any of the third-party-deps, print "err msg for third-party deps" and os.Exit(1)
if p equals any internal-deps, print "another err mssg" and os.Exit(1)
How can I go about doing this?
You can use a YAML package (like https://github.com/go-yaml/yaml), load your file into a variable and check it on every step in the ifs that you propose. I would use maps as it seems that you will be checking very frequently the sets.
Here you have a simple example that I made using that package so you can see how to unmarshal your file, convert into maps, and check the maps: https://play.golang.org/p/t1GhUPvAQNQ
package main
import (
"fmt"
"github.com/go-yaml/yaml"
)
const str = `
allowed-deps:
- dep1
- dep2
- dep3
third-party-deps:
- dep4
- dep5
- dep6
internal-deps:
- dep7
- dep8
- dep9
`
type MyYAML struct {
AllowedDeps []string `yaml:"allowed-deps"`
ThirdPartyDeps []string `yaml:"third-party-deps"`
InternalDeps []string `yaml:"internal-deps"`
}
func main() {
var a MyYAML
err := yaml.Unmarshal([]byte(str), &a)
if err != nil {
panic(err)
}
// Build a map for every section.
allowedDeps := map[string]struct{}{}
thirdPartyDeps := map[string]struct{}{}
internalDeps := map[string]struct{}{}
for _, dep := range a.AllowedDeps {
allowedDeps[dep] = struct{}{}
}
for _, dep := range a.ThirdPartyDeps {
thirdPartyDeps[dep] = struct{}{}
}
for _, dep := range a.InternalDeps {
internalDeps[dep] = struct{}{}
}
// Some checking examples.
if _, ok := allowedDeps["dep1"]; ok {
fmt.Println("dep1 found")
}
if _, ok := thirdPartyDeps["dep1"]; ok {
fmt.Println("dep1 found")
}
if _, ok := internalDeps["dep8"]; ok {
fmt.Println("dep8 found")
}
}
package main
import (
"encoding/json"
"fmt"
"/something/models"
"os"
"path/filepath"
"runtime"
)
func WriteDeviceToFile(d chan *models.Device, fileName string) {
_, b, _, _ := runtime.Caller(0)
basepath := filepath.Dir(b)
filePath := basepath + "/dataFile/" + fileName
var f *os.File
var err error
f, _ = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0600)
defer f.Close()
for device := range d {
deviceB, err := json.Marshal(device)
fmt.Println(string(deviceB))
if err == nil {
if _, err = f.WriteString(string(deviceB)); err != nil {
panic(err)
}
} else {
panic(err)
}
}
}
func main() {
deviceChan := make(chan *models.Device)
go WriteDeviceToFile(deviceChan, "notalive.txt")
d := models.NewDevice("12346", "")
deviceChan <- d
d = models.NewDevice("abcd", "")
deviceChan <- d
close(deviceChan)
}
This only works with at least two devices sent to channel. With only one device in deviceChan, the function does not receive anything. Is the channel gone before the WriteDeviceToFile gets to it?
The program exits when main returns. Nothing prevents main from exiting before the files are written
such as, I write 'A' but in file it is '1000001' ,
how can I do ?
I have tried
buf := new(bytes.Buffer)
data := []int8{65, 80}
for _, i := range data {
binary.Write(buf, binary.LittleEndian, i)
fp.Write(buf.Bytes())
}
but I got string 'AP' in file not a binary code
I didn't really understand the question, but perhaps you want something like:
package main
import (
"fmt"
"log"
"os"
)
func main() {
f, err := os.OpenFile("out.txt", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
log.Fatal(err)
}
for _, v := range "AP" {
fmt.Fprintf(f, "%b\n", v)
}
f.Close()
}
which gives:
$ cat out.txt
1000001
1010000
Isn't this Golang program supposed to output a directory listing to stdout?
It compiles ok, but does nothing.
package main
import "exec"
func main() {
argv := []string{"-la"}
envv := []string{}
exec.Run("ls", argv, envv, "", exec.DevNull, exec.PassThrough, exec.MergeWithStdout)
}
this works:
package main
import "exec"
func main() {
cmd, err := exec.Run("/bin/ls", []string{"/bin/ls", "-la"}, []string{}, "", exec.DevNull, exec.PassThrough, exec.PassThrough)
if (err != nil) {
return
}
cmd.Close()
}
You could also do it in native go using: ioutil.ReadDir(dir), like so:
//listdir.go
package main
import (
"os"
"io/ioutil"
"fmt"
)
func ListDir(dir string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dir)
}
func main() {
dir := "./"
if len(os.Args) > 1 {
dir = os.Args[1]
}
fi, err := ListDir(dir)
if err != nil {
fmt.Println("Error", err)
}
for _, f := range fi {
d := "-"
if f.IsDir() { d = "d" }
fmt.Printf("%s %o %d %s %s\n", d, f.Mode() & 0777, f.Size(), f.ModTime().Format("Jan 2 15:04"), f.Name())
}
}
Checkout the documentation available for ioutil and os packages.
By default exec.Command will leave standard input, output and error connected to /dev/null. So, your 'ls' command is running fine but the output is just being thrown away. If you add:
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
before the exec.Run call then your output will go where you probably expect it.
exec.Run replaces your program with the one it executes -- it never returns to your app. This means that when 'cd' completes, it will exit as normal, and the only effect should be of changing the directory; 'ls' will never run.