github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/usersession/userd/privileged_desktop_launcher_internal_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package userd_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/release"
    33  	"github.com/snapcore/snapd/strutil"
    34  	"github.com/snapcore/snapd/testutil"
    35  	"github.com/snapcore/snapd/usersession/userd"
    36  )
    37  
    38  type privilegedDesktopLauncherInternalSuite struct {
    39  	testutil.BaseTest
    40  }
    41  
    42  var _ = Suite(&privilegedDesktopLauncherInternalSuite{})
    43  
    44  var mockFileSystem = []string{
    45  	"/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop",
    46  	"/var/lib/snapd/desktop/applications/multipass_multipass-gui.desktop",
    47  	"/var/lib/snapd/desktop/applications/cevelop_cevelop.desktop",
    48  	"/var/lib/snapd/desktop/applications/egmde-confined-desktop_egmde-confined-desktop.desktop",
    49  	"/var/lib/snapd/desktop/applications/classic-snap-analyzer_classic-snap-analyzer.desktop",
    50  	"/var/lib/snapd/desktop/applications/vlc_vlc.desktop",
    51  	"/var/lib/snapd/desktop/applications/gnome-calculator_gnome-calculator.desktop",
    52  	"/var/lib/snapd/desktop/applications/mir-kiosk-kodi_mir-kiosk-kodi.desktop",
    53  	"/var/lib/snapd/desktop/applications/gnome-characters_gnome-characters.desktop",
    54  	"/var/lib/snapd/desktop/applications/clion_clion.desktop",
    55  	"/var/lib/snapd/desktop/applications/gnome-system-monitor_gnome-system-monitor.desktop",
    56  	"/var/lib/snapd/desktop/applications/inkscape_inkscape.desktop",
    57  	"/var/lib/snapd/desktop/applications/gnome-logs_gnome-logs.desktop",
    58  	"/var/lib/snapd/desktop/applications/foo-bar/baz.desktop",
    59  	"/var/lib/snapd/desktop/applications/baz/foo-bar.desktop",
    60  
    61  	// A desktop file ID provided by a snap may be shadowed by the
    62  	// host system.
    63  	"/usr/share/applications/shadow-test.desktop",
    64  	"/var/lib/snapd/desktop/applications/shadow-test.desktop",
    65  }
    66  
    67  var chromiumDesktopFile = `[Desktop Entry]
    68  X-SnapInstanceName=chromium
    69  Version=1.0
    70  Name=Chromium Web Browser
    71  Name[ast]=Restolador web Chromium
    72  Name[bg]=Уеб четец Chromium
    73  Name[bn]=ক্রোমিয়াম ওয়েব ব্রাউজার
    74  Name[bs]=Chromium web preglednik
    75  Name[ca]=Navegador web Chromium
    76  Name[ca@valencia]=Navegador web Chromium
    77  Name[da]=Chromium netbrowser
    78  Name[de]=Chromium-Webbrowser
    79  Name[en_AU]=Chromium Web Browser
    80  Name[eo]=Kromiumo retfoliumilo
    81  Name[es]=Navegador web Chromium
    82  Name[et]=Chromiumi veebibrauser
    83  Name[eu]=Chromium web-nabigatzailea
    84  Name[fi]=Chromium-selain
    85  Name[fr]=Navigateur Web Chromium
    86  Name[gl]=Navegador web Chromium
    87  Name[he]=דפדפן האינטרנט כרומיום
    88  Name[hr]=Chromium web preglednik
    89  Name[hu]=Chromium webböngésző
    90  Name[hy]=Chromium ոստայն զննարկիչ
    91  Name[ia]=Navigator del web Chromium
    92  Name[id]=Peramban Web Chromium
    93  Name[it]=Browser web Chromium
    94  Name[ja]=Chromium ウェブ・ブラウザ
    95  Name[ka]=ვებ ბრაუზერი Chromium
    96  Name[ko]=Chromium 웹 브라우저
    97  Name[kw]=Peurel wias Chromium
    98  Name[ms]=Pelayar Web Chromium
    99  Name[nb]=Chromium nettleser
   100  Name[nl]=Chromium webbrowser
   101  Name[pt_BR]=Navegador de Internet Chromium
   102  Name[ro]=Navigator Internet Chromium
   103  Name[ru]=Веб-браузер Chromium
   104  Name[sl]=Chromium spletni brskalnik
   105  Name[sv]=Webbläsaren Chromium
   106  Name[ug]=Chromium توركۆرگۈ
   107  Name[vi]=Trình duyệt Web Chromium
   108  Name[zh_CN]=Chromium 网页浏览器
   109  Name[zh_HK]=Chromium 網頁瀏覽器
   110  Name[zh_TW]=Chromium 網頁瀏覽器
   111  GenericName=Web Browser
   112  GenericName[ar]=متصفح الشبكة
   113  GenericName[ast]=Restolador web
   114  GenericName[bg]=Уеб браузър
   115  GenericName[bn]=ওয়েব ব্রাউজার
   116  GenericName[bs]=Web preglednik
   117  GenericName[ca]=Navegador web
   118  GenericName[ca@valencia]=Navegador web
   119  GenericName[cs]=WWW prohlížeč
   120  GenericName[da]=Browser
   121  GenericName[de]=Web-Browser
   122  GenericName[el]=Περιηγητής ιστού
   123  GenericName[en_AU]=Web Browser
   124  GenericName[en_GB]=Web Browser
   125  GenericName[eo]=Retfoliumilo
   126  GenericName[es]=Navegador web
   127  GenericName[et]=Veebibrauser
   128  GenericName[eu]=Web-nabigatzailea
   129  GenericName[fi]=WWW-selain
   130  GenericName[fil]=Web Browser
   131  GenericName[fr]=Navigateur Web
   132  GenericName[gl]=Navegador web
   133  GenericName[gu]=વેબ બ્રાઉઝર
   134  GenericName[he]=דפדפן אינטרנט
   135  GenericName[hi]=वेब ब्राउज़र
   136  GenericName[hr]=Web preglednik
   137  GenericName[hu]=Webböngésző
   138  GenericName[hy]=Ոստայն զննարկիչ
   139  GenericName[ia]=Navigator del Web
   140  GenericName[id]=Peramban Web
   141  GenericName[it]=Browser web
   142  GenericName[ja]=ウェブ・ブラウザ
   143  GenericName[ka]=ვებ ბრაუზერი
   144  GenericName[kn]=ಜಾಲ ವೀಕ್ಷಕ
   145  GenericName[ko]=웹 브라우저
   146  GenericName[kw]=Peurel wias
   147  GenericName[lt]=Žiniatinklio naršyklė
   148  GenericName[lv]=Tīmekļa pārlūks
   149  GenericName[ml]=വെബ് ബ്രൌസര്‍
   150  GenericName[mr]=वेब ब्राऊजर
   151  GenericName[ms]=Pelayar Web
   152  GenericName[nb]=Nettleser
   153  GenericName[nl]=Webbrowser
   154  GenericName[or]=ଓ୍ବେବ ବ୍ରାଉଜର
   155  GenericName[pl]=Przeglądarka WWW
   156  GenericName[pt]=Navegador Web
   157  GenericName[pt_BR]=Navegador web
   158  GenericName[ro]=Navigator de Internet
   159  GenericName[ru]=Веб-браузер
   160  GenericName[sk]=WWW prehliadač
   161  GenericName[sl]=Spletni brskalnik
   162  GenericName[sr]=Интернет прегледник
   163  GenericName[sv]=Webbläsare
   164  GenericName[ta]=இணைய உலாவி
   165  GenericName[te]=మహాతల అన్వేషి
   166  GenericName[th]=เว็บเบราว์เซอร์
   167  GenericName[tr]=Web Tarayıcı
   168  GenericName[ug]=توركۆرگۈ
   169  GenericName[uk]=Навігатор Тенет
   170  GenericName[vi]=Bộ duyệt Web
   171  GenericName[zh_CN]=网页浏览器
   172  GenericName[zh_HK]=網頁瀏覽器
   173  GenericName[zh_TW]=網頁瀏覽器
   174  Comment=Access the Internet
   175  Comment[ar]=الدخول إلى الإنترنت
   176  Comment[ast]=Accesu a Internet
   177  Comment[bg]=Достъп до интернет
   178  Comment[bn]=ইন্টারনেটে প্রবেশ করুন
   179  Comment[bs]=Pristup internetu
   180  Comment[ca]=Accediu a Internet
   181  Comment[ca@valencia]=Accediu a Internet
   182  Comment[cs]=Přístup k internetu
   183  Comment[da]=Få adgang til internettet
   184  Comment[de]=Internetzugriff
   185  Comment[el]=Πρόσβαση στο Διαδίκτυο
   186  Comment[en_AU]=Access the Internet
   187  Comment[en_GB]=Access the Internet
   188  Comment[eo]=Akiri interreton
   189  Comment[es]=Acceda a Internet
   190  Comment[et]=Pääs Internetti
   191  Comment[eu]=Sartu Internetera
   192  Comment[fi]=Käytä internetiä
   193  Comment[fil]=I-access ang Internet
   194  Comment[fr]=Accéder à Internet
   195  Comment[gl]=Acceda a Internet
   196  Comment[gu]=ઇંટરનેટ ઍક્સેસ કરો
   197  Comment[he]=גישה לאינטרנט
   198  Comment[hi]=इंटरनेट तक पहुंच स्थापित करें
   199  Comment[hr]=Pristupite Internetu
   200  Comment[hu]=Az internet elérése
   201  Comment[hy]=Մուտք համացանց
   202  Comment[ia]=Accede a le Interrete
   203  Comment[id]=Akses Internet
   204  Comment[it]=Accesso a Internet
   205  Comment[ja]=インターネットにアクセス
   206  Comment[ka]=ინტერნეტში შესვლა
   207  Comment[kn]=ಇಂಟರ್ನೆಟ್ ಅನ್ನು ಪ್ರವೇಶಿಸಿ
   208  Comment[ko]=인터넷에 연결합니다
   209  Comment[kw]=Hedhes an Kesrosweyth
   210  Comment[lt]=Interneto prieiga
   211  Comment[lv]=Piekļūt internetam
   212  Comment[ml]=ഇന്റര്‍‌നെറ്റ് ആക്‌സസ് ചെയ്യുക
   213  Comment[mr]=इंटरनेटमध्ये प्रवेश करा
   214  Comment[ms]=Mengakses Internet
   215  Comment[nb]=Bruk internett
   216  Comment[nl]=Verbinding maken met internet
   217  Comment[or]=ଇଣ୍ଟର୍ନେଟ୍ ପ୍ରବେଶ କରନ୍ତୁ
   218  Comment[pl]=Skorzystaj z internetu
   219  Comment[pt]=Aceder à Internet
   220  Comment[pt_BR]=Acessar a internet
   221  Comment[ro]=Accesați Internetul
   222  Comment[ru]=Доступ в Интернет
   223  Comment[sk]=Prístup do siete Internet
   224  Comment[sl]=Dostop do interneta
   225  Comment[sr]=Приступите Интернету
   226  Comment[sv]=Surfa på Internet
   227  Comment[ta]=இணையத்தை அணுகுதல்
   228  Comment[te]=ఇంటర్నెట్‌ను ఆక్సెస్ చెయ్యండి
   229  Comment[th]=เข้าถึงอินเทอร์เน็ต
   230  Comment[tr]=İnternet'e erişin
   231  Comment[ug]=ئىنتېرنېت زىيارىتى
   232  Comment[uk]=Доступ до Інтернету
   233  Comment[vi]=Truy cập Internet
   234  Comment[zh_CN]=访问互联网
   235  Comment[zh_HK]=連線到網際網路
   236  Comment[zh_TW]=連線到網際網路
   237  Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium %U
   238  Terminal=false
   239  Type=Application
   240  Icon=/snap/chromium/1193/chromium.png
   241  Categories=Network;WebBrowser;
   242  MimeType=text/html;text/xml;application/xhtml_xml;x-scheme-handler/http;x-scheme-handler/https;
   243  StartupNotify=true
   244  StartupWMClass=chromium
   245  Actions=NewWindow;Incognito;TempProfile;
   246  
   247  [Desktop Action NewWindow]
   248  Name=Open a New Window
   249  Name[ast]=Abrir una Ventana Nueva
   250  Name[bg]=Отваряне на Нов прозорец
   251  Name[bn]=একটি নতুন উইন্ডো খুলুন
   252  Name[bs]=Otvori novi prozor
   253  Name[ca]=Obre una finestra nova
   254  Name[ca@valencia]=Obri una finestra nova
   255  Name[da]=Åbn et nyt vindue
   256  Name[de]=Ein neues Fenster öffnen
   257  Name[en_AU]=Open a New Window
   258  Name[eo]=Malfermi novan fenestron
   259  Name[es]=Abrir una ventana nueva
   260  Name[et]=Ava uus aken
   261  Name[eu]=Ireki leiho berria
   262  Name[fi]=Avaa uusi ikkuna
   263  Name[fr]=Ouvrir une nouvelle fenêtre
   264  Name[gl]=Abrir unha nova xanela
   265  Name[he]=פתיחת חלון חדש
   266  Name[hy]=Բացել նոր պատուհան
   267  Name[ia]=Aperi un nove fenestra
   268  Name[it]=Apri una nuova finestra
   269  Name[ja]=新しいウィンドウを開く
   270  Name[ka]=ახალი ფანჯრის გახსნა
   271  Name[kw]=Egery fenester noweth
   272  Name[ms]=Buka Tetingkap Baru
   273  Name[nb]=Åpne et nytt vindu
   274  Name[nl]=Nieuw venster openen
   275  Name[pt_BR]=Abre uma nova janela
   276  Name[ro]=Deschide o fereastră nouă
   277  Name[ru]=Открыть новое окно
   278  Name[sl]=Odpri novo okno
   279  Name[sv]=Öppna ett nytt fönster
   280  Name[ug]=يېڭى كۆزنەك ئاچ
   281  Name[uk]=Відкрити нове вікно
   282  Name[vi]=Mở cửa sổ mới
   283  Name[zh_CN]=打开新窗口
   284  Name[zh_TW]=開啟新視窗
   285  Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium
   286  
   287  [Desktop Action Incognito]
   288  Name=Open a New Window in incognito mode
   289  Name[ast]=Abrir una ventana nueva en mou incógnitu
   290  Name[bg]=Отваряне на нов прозорец в режим \"инкогнито\"
   291  Name[bn]=একটি নতুন উইন্ডো খুলুন ইনকোগনিটো অবস্থায়
   292  Name[bs]=Otvori novi prozor u privatnom modu
   293  Name[ca]=Obre una finestra nova en mode d'incògnit
   294  Name[ca@valencia]=Obri una finestra nova en mode d'incògnit
   295  Name[de]=Ein neues Fenster im Inkognito-Modus öffnen
   296  Name[en_AU]=Open a New Window in incognito mode
   297  Name[eo]=Malfermi novan fenestron nekoniĝeble
   298  Name[es]=Abrir una ventana nueva en modo incógnito
   299  Name[et]=Ava uus aken tundmatus olekus
   300  Name[eu]=Ireki leiho berria isileko moduan
   301  Name[fi]=Avaa uusi ikkuna incognito-tilassa
   302  Name[fr]=Ouvrir une nouvelle fenêtre en mode navigation privée
   303  Name[gl]=Abrir unha nova xanela en modo de incógnito
   304  Name[he]=פתיחת חלון חדש במצב גלישה בסתר
   305  Name[hy]=Բացել նոր պատուհան ծպտյալ աշխատակերպում
   306  Name[ia]=Aperi un nove fenestra in modo incognite
   307  Name[it]=Apri una nuova finestra in modalità incognito
   308  Name[ja]=新しいシークレット ウィンドウを開く
   309  Name[ka]=ახალი ფანჯრის ინკოგნიტოდ გახსნა
   310  Name[kw]=Egry fenester noweth en modh privedh
   311  Name[ms]=Buka Tetingkap Baru dalam mod menyamar
   312  Name[nl]=Nieuw venster openen in incognito-modus
   313  Name[pt_BR]=Abrir uma nova janela em modo anônimo
   314  Name[ro]=Deschide o fereastră nouă în mod incognito
   315  Name[ru]=Открыть новое окно в режиме инкогнито
   316  Name[sl]=Odpri novo okno v načinu brez beleženja
   317  Name[sv]=Öppna ett nytt inkognitofönster
   318  Name[ug]=يوشۇرۇن ھالەتتە يېڭى كۆزنەك ئاچ
   319  Name[uk]=Відкрити нове вікно у приватному режимі
   320  Name[vi]=Mở cửa sổ mới trong chế độ ẩn danh
   321  Name[zh_CN]=以隐身模式打开新窗口
   322  Name[zh_TW]=以匿名模式開啟新視窗
   323  Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium --incognito
   324  
   325  [Desktop Action TempProfile]
   326  Name=Open a New Window with a temporary profile
   327  Name[ast]=Abrir una ventana nueva con perfil temporal
   328  Name[bg]=Отваряне на Нов прозорец с временен профил
   329  Name[bn]=সাময়িক প্রোফাইল সহ একটি নতুন উইন্ডো খুলুন
   330  Name[bs]=Otvori novi prozor pomoću privremenog profila
   331  Name[ca]=Obre una finestra nova amb un perfil temporal
   332  Name[ca@valencia]=Obri una finestra nova amb un perfil temporal
   333  Name[de]=Ein neues Fenster mit einem temporären Profil öffnen
   334  Name[en_AU]=Open a New Window with a temporary profile
   335  Name[eo]=Malfermi novan fenestron portempe
   336  Name[es]=Abrir una ventana nueva con perfil temporal
   337  Name[et]=Ava uus aken ajutise profiiliga
   338  Name[eu]=Ireki leiho berria behin-behineko profil batekin
   339  Name[fi]=Avaa uusi ikkuna käyttäen väliaikaista profiilia
   340  Name[fr]=Ouvrir une nouvelle fenêtre avec un profil temporaire
   341  Name[gl]=Abrir unha nova xanela con perfil temporal
   342  Name[he]=פתיחת חלון חדש עם פרופיל זמני
   343  Name[hy]=Բացել նոր պատուհան ժամանակավոր հատկագրով
   344  Name[ia]=Aperi un nove fenestra con un profilo provisori
   345  Name[it]=Apri una nuova finestra con un profilo temporaneo
   346  Name[ja]=一時プロファイルで新しいウィンドウを開く
   347  Name[ka]=ახალი ფანჯრის გახსნა დროებით პროფილში
   348  Name[kw]=Egery fenester noweth gen profil dres prys
   349  Name[ms]=Buka Tetingkap Baru dengan profil sementara
   350  Name[nb]=Åpne et nytt vindu med en midlertidig profil
   351  Name[nl]=Nieuw venster openen met een tijdelijk profiel
   352  Name[pt_BR]=Abrir uma nova janela com um perfil temporário
   353  Name[ro]=Deschide o fereastră nouă cu un profil temporar
   354  Name[ru]=Открыть новое окно с временным профилем
   355  Name[sl]=Odpri novo okno z začasnim profilom
   356  Name[sv]=Öppna ett nytt fönster med temporär profil
   357  Name[ug]=ۋاقىتلىق سەپلىمە ھۆججەت بىلەن يېڭى كۆزنەك ئاچ
   358  Name[vi]=Mở cửa sổ mới với hồ sơ tạm
   359  Name[zh_CN]=以临时配置文件打开新窗口
   360  Name[zh_TW]=以暫時性個人身分開啟新視窗
   361  Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium --temp-profile
   362  `
   363  
   364  func existsOnMockFileSystem(desktop_file string) (bool, bool, error) {
   365  	existsOnMockFileSystem := strutil.ListContains(mockFileSystem, desktop_file)
   366  	return existsOnMockFileSystem, existsOnMockFileSystem, nil
   367  }
   368  
   369  func (s *privilegedDesktopLauncherInternalSuite) mockEnv(key, value string) {
   370  	old := os.Getenv(key)
   371  	os.Setenv(key, value)
   372  	s.AddCleanup(func() {
   373  		os.Setenv(key, old)
   374  	})
   375  }
   376  
   377  func (s *privilegedDesktopLauncherInternalSuite) TestDesktopFileSearchPath(c *C) {
   378  	s.mockEnv("HOME", "/home/user")
   379  	s.mockEnv("XDG_DATA_HOME", "")
   380  	s.mockEnv("XDG_DATA_DIRS", "")
   381  
   382  	// Default search path
   383  	c.Check(userd.DesktopFileSearchPath(), DeepEquals, []string{
   384  		"/home/user/.local/share/applications",
   385  		"/usr/local/share/applications",
   386  		"/usr/share/applications",
   387  	})
   388  
   389  	// XDG_DATA_HOME will override the first path
   390  	s.mockEnv("XDG_DATA_HOME", "/home/user/share")
   391  	c.Check(userd.DesktopFileSearchPath(), DeepEquals, []string{
   392  		"/home/user/share/applications",
   393  		"/usr/local/share/applications",
   394  		"/usr/share/applications",
   395  	})
   396  
   397  	// XDG_DATA_DIRS changes the remaining paths
   398  	s.mockEnv("XDG_DATA_DIRS", "/usr/share:/var/lib/snapd/desktop")
   399  	c.Check(userd.DesktopFileSearchPath(), DeepEquals, []string{
   400  		"/home/user/share/applications",
   401  		"/usr/share/applications",
   402  		"/var/lib/snapd/desktop/applications",
   403  	})
   404  }
   405  
   406  func (s *privilegedDesktopLauncherInternalSuite) TestDesktopFileIDToFilenameSucceedsWithValidId(c *C) {
   407  	restore := userd.MockRegularFileExists(existsOnMockFileSystem)
   408  	defer restore()
   409  	s.mockEnv("XDG_DATA_DIRS", "/usr/local/share:/usr/share:/var/lib/snapd/desktop")
   410  
   411  	var desktopIdTests = []struct {
   412  		id     string
   413  		expect string
   414  	}{
   415  		{"mir-kiosk-scummvm_mir-kiosk-scummvm.desktop", "/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop"},
   416  		{"foo-bar-baz.desktop", "/var/lib/snapd/desktop/applications/foo-bar/baz.desktop"},
   417  		{"baz-foo-bar.desktop", "/var/lib/snapd/desktop/applications/baz/foo-bar.desktop"},
   418  		{"shadow-test.desktop", "/usr/share/applications/shadow-test.desktop"},
   419  	}
   420  
   421  	for _, test := range desktopIdTests {
   422  		actual, err := userd.DesktopFileIDToFilename(test.id)
   423  		c.Assert(err, IsNil)
   424  		c.Assert(actual, Equals, test.expect)
   425  	}
   426  }
   427  
   428  func (s *privilegedDesktopLauncherInternalSuite) TestDesktopFileIDToFilenameFailsWithInvalidId(c *C) {
   429  	restore := userd.MockRegularFileExists(existsOnMockFileSystem)
   430  	defer restore()
   431  	s.mockEnv("XDG_DATA_DIRS", "/usr/local/share:/usr/share:/var/lib/snapd/desktop")
   432  
   433  	var desktopIdTests = []string{
   434  		"mir-kiosk-scummvm-mir-kiosk-scummvm.desktop",
   435  		"bar-foo-baz.desktop",
   436  		"bar-baz-foo.desktop",
   437  		"foo-bar_foo-bar.desktop",
   438  		// special path segments cannot be smuggled inside desktop IDs
   439  		"bar-..-vlc_vlc.desktop",
   440  		"foo-bar/baz.desktop",
   441  		"bar/../vlc_vlc.desktop",
   442  		"../applications/vlc_vlc.desktop",
   443  		// Other invalid desktop IDs
   444  		"---------foo-bar-baz.desktop",
   445  		"foo-bar-baz.desktop-foo-bar",
   446  		"not-a-dot-desktop",
   447  		"以临时配置文件打开新-non-ascii-here-too.desktop",
   448  	}
   449  
   450  	for _, id := range desktopIdTests {
   451  		_, err := userd.DesktopFileIDToFilename(id)
   452  		c.Check(err, ErrorMatches, `cannot find desktop file for ".*"`, Commentf(id))
   453  	}
   454  }
   455  
   456  func (s *privilegedDesktopLauncherInternalSuite) TestVerifyDesktopFileLocation(c *C) {
   457  	restore := userd.MockRegularFileExists(existsOnMockFileSystem)
   458  	defer restore()
   459  	s.mockEnv("XDG_DATA_DIRS", "/usr/local/share:/usr/share:/var/lib/snapd/desktop")
   460  
   461  	// Resolved desktop files belonging to snaps will pass verification:
   462  	filename, err := userd.DesktopFileIDToFilename("mir-kiosk-scummvm_mir-kiosk-scummvm.desktop")
   463  	c.Assert(err, IsNil)
   464  	err = userd.VerifyDesktopFileLocation(filename)
   465  	c.Check(err, IsNil)
   466  
   467  	// Desktop IDs belonging to host system apps fail:
   468  	filename, err = userd.DesktopFileIDToFilename("shadow-test.desktop")
   469  	c.Assert(err, IsNil)
   470  	err = userd.VerifyDesktopFileLocation(filename)
   471  	c.Check(err, ErrorMatches, "only launching snap applications from /var/lib/snapd/desktop/applications is supported")
   472  }
   473  
   474  func (s *privilegedDesktopLauncherInternalSuite) TestParseExecCommandSucceedsWithValidEntry(c *C) {
   475  	var testCases = []struct {
   476  		cmd    string
   477  		expect []string
   478  	}{
   479  		// valid with no exec variables
   480  		{"env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop /snap/bin/mir-kiosk-scummvm %U",
   481  			[]string{"env", "BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop", "/snap/bin/mir-kiosk-scummvm"}},
   482  		// valid with literal '%' and no exec variables
   483  		{"/snap/bin/foo -f %%bar", []string{"/snap/bin/foo", "-f", "%bar"}},
   484  		{"/snap/bin/foo -f %%bar %%baz", []string{"/snap/bin/foo", "-f", "%bar", "%baz"}},
   485  		// valid where quoted strings are passed through
   486  		{"/snap/bin/foo '-f %U'", []string{"/snap/bin/foo", "-f %U"}},
   487  		{"/snap/bin/foo '-f %%bar'", []string{"/snap/bin/foo", "-f %%bar"}},
   488  		{"/snap/bin/foo '-f %U %%bar'", []string{"/snap/bin/foo", "-f %U %%bar"}},
   489  		{"/snap/bin/foo \"'-f bar'\"", []string{"/snap/bin/foo", "'-f bar'"}},
   490  		{"/snap/bin/foo '\"-f bar\"'", []string{"/snap/bin/foo", "\"-f bar\""}},
   491  		// valid with exec variables stripped out
   492  		{"/snap/bin/foo -f %U", []string{"/snap/bin/foo", "-f"}},
   493  		{"/snap/bin/foo -f %U %i", []string{"/snap/bin/foo", "-f", "--icon", "/snap/chromium/1193/chromium.png"}},
   494  		{"/snap/bin/foo -f %U bar", []string{"/snap/bin/foo", "-f", "bar"}},
   495  		{"/snap/bin/foo -f %U bar %i", []string{"/snap/bin/foo", "-f", "bar", "--icon", "/snap/chromium/1193/chromium.png"}},
   496  		// valid with mixture of literal '%' and exec variables
   497  		{"/snap/bin/foo -f %U %%bar", []string{"/snap/bin/foo", "-f", "%bar"}},
   498  		{"/snap/bin/foo -f %U %i %%bar", []string{"/snap/bin/foo", "-f", "--icon", "/snap/chromium/1193/chromium.png", "%bar"}},
   499  		{"/snap/bin/foo -f %U %%bar %i", []string{"/snap/bin/foo", "-f", "%bar", "--icon", "/snap/chromium/1193/chromium.png"}},
   500  		{"/snap/bin/foo -f %%bar %U %i", []string{"/snap/bin/foo", "-f", "%bar", "--icon", "/snap/chromium/1193/chromium.png"}},
   501  	}
   502  
   503  	for _, test := range testCases {
   504  		actual, err := userd.ParseExecCommand(test.cmd, "/snap/chromium/1193/chromium.png")
   505  		comment := Commentf("cmd=%s", test.cmd)
   506  		c.Check(err, IsNil, comment)
   507  		c.Check(actual, DeepEquals, test.expect, comment)
   508  	}
   509  }
   510  
   511  func (s *privilegedDesktopLauncherInternalSuite) TestParseExecCommandFailsWithInvalidEntry(c *C) {
   512  	var testCases = []struct {
   513  		cmd string
   514  		err string
   515  	}{
   516  		// Commands may be rejected for bad quoting
   517  		{`/snap/bin/foo "unclosed double quote`, "EOF found when expecting closing quote"},
   518  		{`/snap/bin/foo 'unclosed single quote`, "EOF found when expecting closing quote"},
   519  
   520  		// Or use of unexpected unknown variables
   521  		{"/snap/bin/foo %z", `cannot run "/snap/bin/foo %z" due to use of "%z"`},
   522  		{"/snap/bin/foo %", `cannot run "/snap/bin/foo %" due to use of "%"`},
   523  	}
   524  
   525  	for _, test := range testCases {
   526  		_, err := userd.ParseExecCommand(test.cmd, "/snap/chromium/1193/chromium.png")
   527  		comment := Commentf("cmd=%s", test.cmd)
   528  		c.Check(err, ErrorMatches, test.err, comment)
   529  	}
   530  }
   531  
   532  func (s *privilegedDesktopLauncherInternalSuite) testReadExecCommandFromDesktopFileWithValidContent(c *C, desktopFileContent string) {
   533  	desktopFile := filepath.Join(c.MkDir(), "test.desktop")
   534  
   535  	// We need to correct the embedded path to the desktop file before writing the file
   536  	fileContent := strings.Replace(desktopFileContent, "/var/lib/snapd/desktop/applications/chromium_chromium.desktop", desktopFile, -1)
   537  	err := ioutil.WriteFile(desktopFile, []byte(fileContent), 0644)
   538  	c.Assert(err, IsNil)
   539  
   540  	exec, icon, err := userd.ReadExecCommandFromDesktopFile(desktopFile)
   541  	c.Assert(err, IsNil)
   542  
   543  	c.Check(exec, Equals, fmt.Sprintf("env BAMF_DESKTOP_FILE_HINT=%s %s/chromium %%U", desktopFile, dirs.SnapBinariesDir))
   544  	c.Check(icon, Equals, "/snap/chromium/1193/chromium.png")
   545  }
   546  
   547  func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithValidContentPathSnap(c *C) {
   548  	// pick a system known to use /snap/bin for launcher symlinks
   549  	restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu"})
   550  	defer restore()
   551  	dirs.SetRootDir("/")
   552  	defer dirs.SetRootDir("/")
   553  	s.testReadExecCommandFromDesktopFileWithValidContent(c, chromiumDesktopFile)
   554  }
   555  
   556  func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithValidContentPathVarLibSnapd(c *C) {
   557  	// pick a system known to use /var/lib/snapd/bin for launcher symlinks
   558  	restore := release.MockReleaseInfo(&release.OS{ID: "fedora"})
   559  	defer restore()
   560  	dirs.SetRootDir("/")
   561  	defer dirs.SetRootDir("/")
   562  
   563  	// fix the Exec= line
   564  	fileContentWithVarLibSnapBin := strings.Replace(chromiumDesktopFile, " /snap/bin/chromium", " /var/lib/snapd/snap/bin/chromium", -1)
   565  	s.testReadExecCommandFromDesktopFileWithValidContent(c, fileContentWithVarLibSnapBin)
   566  }
   567  
   568  func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithInvalidExec(c *C) {
   569  	desktopFile := filepath.Join(c.MkDir(), "test.desktop")
   570  
   571  	err := ioutil.WriteFile(desktopFile, []byte(chromiumDesktopFile), 0644)
   572  	c.Assert(err, IsNil)
   573  
   574  	_, _, err = userd.ReadExecCommandFromDesktopFile(desktopFile)
   575  	c.Assert(err, ErrorMatches, `desktop file ".*" has an unsupported 'Exec' value: .*`)
   576  }
   577  
   578  func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithNoDesktopEntry(c *C) {
   579  	desktopFile := filepath.Join(c.MkDir(), "test.desktop")
   580  
   581  	// We need to correct the embedded path to the desktop file before writing the file
   582  	fileContent := strings.Replace(chromiumDesktopFile, "/var/lib/snapd/desktop/applications/chromium_chromium.desktop", desktopFile, -1)
   583  	fileContent = strings.Replace(fileContent, "[Desktop Entry]", "[garbage]", -1)
   584  
   585  	err := ioutil.WriteFile(desktopFile, []byte(fileContent), 0644)
   586  	c.Assert(err, IsNil)
   587  
   588  	_, _, err = userd.ReadExecCommandFromDesktopFile(desktopFile)
   589  	c.Assert(err, ErrorMatches, `desktop file ".*" has an unsupported 'Exec' value: ""`)
   590  }
   591  
   592  func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCOmmandFromDesktopFileMultipleDesktopEntrySections(c *C) {
   593  	desktopFile := filepath.Join(c.MkDir(), "test.desktop")
   594  	c.Assert(ioutil.WriteFile(desktopFile, []byte(`[Desktop Entry]
   595  Exec=foo
   596  
   597  [Desktop Entry]
   598  Exec=bar
   599  `), 0644), IsNil)
   600  
   601  	_, _, err := userd.ReadExecCommandFromDesktopFile(desktopFile)
   602  	c.Check(err, ErrorMatches, `desktop file ".*" has multiple \[Desktop Entry\] sections`)
   603  }
   604  
   605  func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithNoFile(c *C) {
   606  	desktopFile := filepath.Join(c.MkDir(), "test.desktop")
   607  
   608  	_, _, err := userd.ReadExecCommandFromDesktopFile(desktopFile)
   609  	c.Assert(err, ErrorMatches, `open .*: no such file or directory`)
   610  }