From e58f56afe32b294c1af5ae1960a5b0796512907b Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Sun, 18 Sep 2022 03:44:57 -0500 Subject: [PATCH 1/4] Fix #220 --- src/appimaged/desktop.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/appimaged/desktop.go b/src/appimaged/desktop.go index 1b199ed4..39134575 100644 --- a/src/appimaged/desktop.go +++ b/src/appimaged/desktop.go @@ -58,6 +58,7 @@ func writeDesktopFile(ai AppImage) { desktopRdr, _ := ai.ExtractFileReader("*.desktop") defer desktopRdr.Close() //cleaning the desktop file so it can be parsed properly + //TODO: find better desktop file parser that doesn't mistake semicolons for comments. var desktop []byte buf := bufio.NewReader(desktopRdr) for err == nil { @@ -126,7 +127,7 @@ func writeDesktopFile(ai AppImage) { } // Actions - var actions []string + actions := strings.Split(cfg.Section("Desktop Entry").Key("Actions").String(), ";") if isWritable(ai.Path) { // Add "Move to Trash" action From c4371c8b56201e231ccdb9f9940ea0d263aa1bf4 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Sun, 18 Sep 2022 03:54:01 -0500 Subject: [PATCH 2/4] Fix desktop file in memory --- src/appimaged/desktop.go | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/appimaged/desktop.go b/src/appimaged/desktop.go index 39134575..adc06906 100644 --- a/src/appimaged/desktop.go +++ b/src/appimaged/desktop.go @@ -7,7 +7,6 @@ package main import ( "bufio" "bytes" - "io/ioutil" "log" "os" "path/filepath" @@ -245,15 +244,18 @@ func writeDesktopFile(ai AppImage) { if *verbosePtr { log.Println("desktop: Saving to", desktopcachedir+"/"+filename) } - err = cfg.SaveTo(desktopcachedir + "/" + filename) + buf := new(bytes.Buffer) + cfg.WriteTo(buf) + out := fixDesktopFile(buf.Bytes()) + os.Remove(desktopcachedir + "/" + filename) + deskFil, err := os.Create(desktopcachedir + "/" + filename) if err != nil { log.Printf("Fail to write file: %v", err) + return } - - err = fixDesktopFile(desktopcachedir + "/" + filename) + _, err = deskFil.Write(out) if err != nil { - helpers.PrintError("desktop fixDesktopFile", err) - os.Exit(1) + log.Printf("Fail to write file: %v", err) } } @@ -264,20 +266,12 @@ func isWritable(path string) bool { // Really ugly workaround for // https://github.com/go-ini/ini/issues/90 -func fixDesktopFile(path string) error { - input, err := ioutil.ReadFile(path) - if err != nil { - return err - } +func fixDesktopFile(input []byte) []byte { var output []byte if bytes.Contains(input, []byte("=`")) { output = bytes.Replace(input, []byte("=`"), []byte("="), -1) output = bytes.Replace(output, []byte("`\n"), []byte("\n"), -1) } output = bytes.ReplaceAll(output, []byte(";"), []byte(";")) - - if err = ioutil.WriteFile(path, output, 0755); err != nil { - return err - } - return nil + return output } From 18a76b0ded067d56f8b568506aac4ba8b65b0291 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Mon, 19 Sep 2022 00:36:04 -0500 Subject: [PATCH 3/4] Fixed #220 Implemented fix mention in #110 Fixed issues caused by squashfs v0.6+ doing things more correctly Changes build script so zig is not permenently installed and is cleaned --- scripts/build.sh | 21 +++++---- src/appimaged/appimaged.go | 9 ---- src/appimaged/desktop.go | 89 +++++++++++++++----------------------- src/goappimage/appimage.go | 35 ++++++++++----- 4 files changed, 72 insertions(+), 82 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 29acb4d3..544ed209 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -218,14 +218,6 @@ if [ $GITHUB_ACTIONS ]; then sudo apt-get install --yes wget file fi -# Install zig, it comes with musl libc -if [ ! -e /usr/local/bin/zig ]; then - wget -c -q "https://ziglang.org/builds/zig-linux-x86_64-0.10.0-dev.2112+0df28f9d4.tar.xz" - tar xf zig-linux-*-*.tar.xz - sudo mv zig-linux-*/* /usr/local/bin/ - which zig -fi - if [ -z $BUILDTOOL ]; then BUILDTOOL=(appimaged appimagetool mkappimage) fi @@ -249,6 +241,17 @@ fi mkdir -p $BUILDDIR || true cd $BUILDDIR +# Install zig, it comes with musl libc +if [ ! -e $BUILDDIR/zig ]; then + wget -c -q "https://ziglang.org/builds/zig-linux-x86_64-0.10.0-dev.2112+0df28f9d4.tar.xz" + tar xf zig-linux-*.tar.xz + rm zig-linux-*.tar.xz + mv zig-linux-* zig + CLEANUP+=($BUILDDIR/zig) +fi + +PATH=$BUILDDIR/zig:$PATH + # We always want the amd64 appimagetool built first so that other AppImages can be built. # If this isn't wanted, we clean it up afterwards build amd64 appimagetool @@ -267,7 +270,7 @@ if [ -z $BUILDINGAPPIMAGETOOL ]; then CLEANUP+=($BUILDDIR/appimagetool-$VERSION-x86_64.AppImage) fi -if [ -z $DONTCLEAN]; then +if [ -z $DONTCLEAN ]; then for file in ${CLEANUP[@]}; do echo $file rm -rf $file || true diff --git a/src/appimaged/appimaged.go b/src/appimaged/appimaged.go index 281c89ea..d0ecec9b 100644 --- a/src/appimaged/appimaged.go +++ b/src/appimaged/appimaged.go @@ -252,15 +252,6 @@ func moveDesktopFiles(ai *AppImage) error { if !integrate { return nil } - desktopcachedir := xdg.CacheHome + "/applications/" // FIXME: Do not hardcode here and in other places - - err := os.Rename(desktopcachedir+"/appimagekit_"+ai.md5+".desktop", ai.desktopfilepath) - if err != nil { - return err - } - if *verbosePtr { - log.Println("main: Moved ", desktopcachedir+"/appimagekit_"+ai.md5+".desktop to", xdg.DataHome+"/applications/") - } if !ai.startup { // If one single application has been integrated, then the user probably cares about it diff --git a/src/appimaged/desktop.go b/src/appimaged/desktop.go index adc06906..5341c1c0 100644 --- a/src/appimaged/desktop.go +++ b/src/appimaged/desktop.go @@ -5,7 +5,6 @@ package main // but eventually may be rewritten to do things natively in Go. import ( - "bufio" "bytes" "log" "os" @@ -15,7 +14,6 @@ import ( "golang.org/x/sys/unix" - "github.com/adrg/xdg" "github.com/probonopd/go-appimage/internal/helpers" "gopkg.in/ini.v1" ) @@ -26,24 +24,14 @@ import ( // for a while func writeDesktopFile(ai AppImage) { - filename := "appimagekit_" + ai.md5 + ".desktop" - // log.Println(md5s) // XDG directories // log.Println(xdg.DataHome) // log.Println(xdg.DataDirs) // log.Println(xdg.ConfigHome) // log.Println(xdg.ConfigDirs) - desktopcachedir := xdg.CacheHome + "/applications/" // FIXME: Do not hardcode here and in other places - - err := os.MkdirAll(desktopcachedir, os.ModePerm) - if err != nil { - log.Printf("desktop: %v", err) - } // log.Println(xdg.RuntimeDir) - var cfg *ini.File ini.PrettyFormat = false - startingPoint := false //An easy way to tell if extracting the desktop file worked. arg0abs, err := filepath.Abs(os.Args[0]) // FIXME: KDE seems to have a problem when the AppImage is on a partition of which the disklabel contains "_"? @@ -51,41 +39,17 @@ func writeDesktopFile(ai AppImage) { if err != nil { log.Println(err) } - if ai.Desktop != nil { - //Start with a fresh copy of the desktop file so we don't make edits to ai.Desktop - desktopRdr, _ := ai.ExtractFileReader("*.desktop") - defer desktopRdr.Close() - //cleaning the desktop file so it can be parsed properly - //TODO: find better desktop file parser that doesn't mistake semicolons for comments. - var desktop []byte - buf := bufio.NewReader(desktopRdr) - for err == nil { - var line string - line, err = buf.ReadString('\n') - if strings.Contains(line, ";") { - line = strings.ReplaceAll(line, ";", ";") //replacing it with a fullwidth semicolon (unicode FF1B) - } - desktop = append(desktop, line...) - } - cfg, err = ini.Load(desktop) - if err == nil { - startingPoint = true - } - //TODO: check if the thumbnail is already present and only extract it and set it's value if it isn't - } + //Create a copy of the desktop file to edit. + deskCopy := new(bytes.Buffer) + ai.Desktop.WriteTo(deskCopy) + cfg, _ := ini.Load(deskCopy) - if !startingPoint { - cfg = ini.Empty() - cfg.Section("Desktop Entry").Key("Type").SetValue("Application") + if !cfg.Section("Desktop Entry").HasKey("Name") { cfg.Section("Desktop Entry").Key("Name").SetValue(ai.Name) - } else { - if !cfg.Section("Desktop Entry").HasKey("Name") { - cfg.Section("Desktop Entry").Key("Name").SetValue(ai.Name) - } - if !cfg.Section("Desktop Entry").HasKey("Type") { - cfg.Section("Desktop Entry").Key("Type").SetValue("Application") - } + } + if !cfg.Section("Desktop Entry").HasKey("Type") { + cfg.Section("Desktop Entry").Key("Type").SetValue("Application") } thumbnail := ThumbnailsDirNormal + ai.md5 + ".png" cfg.Section("Desktop Entry").Key("Icon").SetValue(thumbnail) @@ -125,8 +89,28 @@ func writeDesktopFile(ai AppImage) { cfg.Section("Desktop Entry").Key(helpers.UpdateInformationKey).SetValue("\"" + ui + "\"") } // Actions - - actions := strings.Split(cfg.Section("Desktop Entry").Key("Actions").String(), ";") + var actions []string + if strings.TrimSpace(cfg.Section("Desktop Entry").Key("Actions").String()) != "" { + actions = strings.Split(cfg.Section("Desktop Entry").Key("Actions").String(), ";") + for i := 0; i < len(actions); i++ { + if actions[i] == "" { + actions = append(actions[:i], actions[i+1:]...) + } + } + } + for _, a := range actions { + sec := cfg.Section("Desktop Action " + a) + exec := sec.Key("Exec").String() + if exec != "" { + if strings.HasPrefix(exec, "\"") { + if strings.Contains(exec[1:], "\"") { + exec = exec[1 : strings.Index(exec[1:], "\"")+1] + } + } + spl := strings.Split(exec, " ") + sec.Key("Exec").SetValue(arg0abs + " wrap \"" + ai.Path + "\" " + strings.Join(spl[1:], " ")) + } + } if isWritable(ai.Path) { // Add "Move to Trash" action @@ -235,22 +219,19 @@ func writeDesktopFile(ai AppImage) { cfg.Section("Desktop Action FirejailOverlayTmpfs").Key("Exec").SetValue("firejail --env=DESKTOPINTEGRATION=appimaged --noprofile --overlay-tmpfs --appimage \"" + ai.Path + "\"") } - as := "" - for _, action := range actions { - as = as + action + ";" - } + as := strings.Join(actions, ";") cfg.Section("Desktop Entry").Key("Actions").SetValue(as) if *verbosePtr { - log.Println("desktop: Saving to", desktopcachedir+"/"+filename) + log.Println("desktop: Saving to", ai.desktopfilepath) } buf := new(bytes.Buffer) cfg.WriteTo(buf) out := fixDesktopFile(buf.Bytes()) - os.Remove(desktopcachedir + "/" + filename) - deskFil, err := os.Create(desktopcachedir + "/" + filename) + os.Remove(ai.desktopfilepath) + deskFil, err := os.Create(ai.desktopfilepath) if err != nil { - log.Printf("Fail to write file: %v", err) + log.Printf("Fail to create file: %v", err) return } _, err = deskFil.Write(out) diff --git a/src/goappimage/appimage.go b/src/goappimage/appimage.go index 4459a893..47d72423 100644 --- a/src/goappimage/appimage.go +++ b/src/goappimage/appimage.go @@ -41,8 +41,8 @@ type AppImage struct { // NewAppImage creates an AppImage object from the location defined by path. // Returns an error if the given path is not an appimage, or is a temporary file. // In all instances, will still return the AppImage. -func NewAppImage(path string) (*AppImage, error) { - ai := AppImage{Path: path, imageType: -1} +func NewAppImage(path string) (ai *AppImage, err error) { + ai = &AppImage{Path: path, imageType: -1} // If we got a temp file, exit immediately // E.g., ignore typical Internet browser temporary files used during download if strings.HasSuffix(path, ".temp") || @@ -51,24 +51,35 @@ func NewAppImage(path string) (*AppImage, error) { strings.HasSuffix(path, ".partial") || strings.HasSuffix(path, ".zs-old") || strings.HasSuffix(path, ".crdownload") { - return &ai, errors.New("given path is a temporary file") + return ai, errors.New("given path is a temporary file") } ai.imageType = ai.determineImageType() // Don't waste more time if the file is not actually an AppImage if ai.imageType < 0 { - return &ai, errors.New("given path is NOT an AppImage") + return ai, errors.New("given path is NOT an AppImage") } if ai.imageType > 1 { ai.offset = helpers.CalculateElfSize(ai.Path) } - err := ai.populateReader(true, false) + err = ai.populateReader(true, false) if err != nil { - return &ai, err + return } //try to load up the desktop file for some information. - desktopFil, err := ai.reader.FileReader("*.desktop") + var desk string + files := ai.reader.ListFiles(".") + for _, f := range files { + if strings.HasSuffix(f, ".desktop") { + desk = f + break + } + } + if desk == "" { + return ai, errors.New("cannot find desktop file") + } + desktopFil, err := ai.reader.FileReader(desk) if err != nil { - return nil, err + return } //cleaning the desktop file so it can be parsed properly @@ -83,7 +94,7 @@ func NewAppImage(path string) (*AppImage, error) { ai.Desktop, err = ini.Load(desktop) if err != nil { - return nil, err + return } ai.Name = ai.Desktop.Section("Desktop Entry").Key("Name").Value() @@ -96,7 +107,7 @@ func NewAppImage(path string) (*AppImage, error) { } ai.UpdateInfo, _ = helpers.ReadUpdateInfo(ai.Path) - return &ai, nil + return } func (ai AppImage) calculateNiceName() string { @@ -173,6 +184,10 @@ func (ai AppImage) Type() int { return ai.imageType } +func (ai AppImage) ListFiles(folder string) []string { + return ai.reader.ListFiles(folder) +} + // ExtractFile extracts a file from from filepath (which may contain * wildcards) in an AppImage to the destinationdirpath. // // If resolveSymlinks is true, if the filepath specified is a symlink, the actual file is extracted in it's place. From 8b18b82fb5ed6bb7efd3ad0615726123782454be Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Mon, 19 Sep 2022 00:57:37 -0500 Subject: [PATCH 4/4] Linked wrapped command's out and in to stdout and stdin. Fixes #217 --- src/appimaged/appwrapper.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/appimaged/appwrapper.go b/src/appimaged/appwrapper.go index ad40831b..593ae688 100644 --- a/src/appimaged/appwrapper.go +++ b/src/appimaged/appwrapper.go @@ -27,6 +27,8 @@ func appwrap() { } cmd := exec.Command(os.Args[2], os.Args[3:]...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout var out bytes.Buffer cmd.Stderr = &out