diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65a01c868..541da69c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,10 +31,10 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-2019, macos-12] - arch: [-amd64] + arch: [amd64] include: - os: windows-2019 - arch: -386 + arch: 386 ext: ".exe" - os: windows-2019 ext: ".exe" @@ -104,11 +104,11 @@ jobs: env: GOARCH: 386 # 32bit architecture (for support) run: task go:build-win - if: matrix.os == 'windows-2019' && matrix.arch == '-386' + if: matrix.os == 'windows-2019' && matrix.arch == '386' - name: Build the Agent for win64 run: task go:build-win # GOARCH=amd64 by default on the runners - if: matrix.os == 'windows-2019' && matrix.arch == '-amd64' + if: matrix.os == 'windows-2019' && matrix.arch == 'amd64' - name: Build the Agent for macos env: @@ -121,7 +121,7 @@ jobs: # this will create `public/` dir with compressed full bin (/-.gz) and a json file - name: Create autoupdate files run: go-selfupdate ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} - if: matrix.arch != '-386' && steps.prerelease.outputs.IS_PRE != 'true' + if: matrix.arch != '386' && steps.prerelease.outputs.IS_PRE != 'true' # for now we do not distribute m1 build, this is a workaround for now - name: Copy autoupdate file for darwin-arm64 (m1 arch) @@ -132,8 +132,8 @@ jobs: if: matrix.os == 'macos-12' && steps.prerelease.outputs.IS_PRE != 'true' - name: Create autoupdate files for win32 - run: go-selfupdate -platform windows${{ matrix.arch }} ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} - if: matrix.arch == '-386' && matrix.os == 'windows-2019' && steps.prerelease.outputs.IS_PRE != 'true' + run: go-selfupdate -platform windows-${{ matrix.arch }} ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} + if: matrix.arch == '386' && matrix.os == 'windows-2019' && steps.prerelease.outputs.IS_PRE != 'true' - name: Upload autoupdate files to Arduino downloads servers run: | @@ -144,7 +144,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: ${{ env.PROJECT_NAME }}-${{ matrix.os }}${{ matrix.arch }} + name: ${{ env.PROJECT_NAME }}-${{ matrix.os }}-${{ matrix.arch }} path: | ${{ env.PROJECT_NAME }}* if-no-files-found: error @@ -334,9 +334,6 @@ jobs: INSTALLBUILDER_PATH: "/opt/installbuilder-22.10.0/bin/builder" INSTALLER_VARS: "project.outputDirectory=$PWD project.version=${GITHUB_REF##*/} workspace=$PWD realname=Arduino_Create_Agent" # vars passed to installbuilder to install https certs automatically - CERT_INSTALL: "ask_certificates_install=CI" # win(edge),mac(safari) - NO_CERT_INSTALL: "ask_certificates_install=CS" # linux - CHOICE_CERT_INSTALL: "ask_certificates_install=CC" # win,mac:(ff,chrome) CREATE_OSX_BUNDLED_MG: 0 # tell installbuilder to not create the DMG, gon will take care of that later # installbuilder will read this vars automatically (defined in installer.xml): INSTALLER_CERT_WINDOWS_PASSWORD: ${{ secrets.INSTALLER_CERT_WINDOWS_PASSWORD }} @@ -348,29 +345,27 @@ jobs: fail-fast: false # if one os is failing continue nonetheless matrix: # used to generate installers for different OS and not for runs-on os: [ubuntu-20.04, windows-2019, macos-12] - arch: [-amd64] + arch: [amd64] include: - os: ubuntu-20.04 install-builder-name: linux executable-path: artifacts/linux-amd64/ + installer-extension: .run artifact-name: arduino-create-agent-ubuntu-20.04-amd64 - os: windows-2019 - arch: -386 - browser: edge + arch: 386 install-builder-name: windows executable-path: artifacts/windows/ extension: .exe installer-extension: .exe artifact-name: arduino-create-agent-windows-2019-386 - os: windows-2019 - browser: edge install-builder-name: windows executable-path: artifacts/windows/ extension: .exe installer-extension: .exe artifact-name: arduino-create-agent-windows-2019-amd64 - os: macos-12 - browser: safari install-builder-name: osx executable-path: artifacts/macos/ArduinoCreateAgent.app installer-extension: .app @@ -425,32 +420,19 @@ jobs: run: echo "${{ secrets.INSTALLER_CERT_MAC_P12 }}" | base64 --decode > ${{ env.INSTALLER_CERT_MAC_P12 }} if: matrix.os == 'macos-12' - # win(edge),mac(safari) -> CERT_INSTALL and win,mac:(ff,chrome) -> CHOICE_CERT_INSTALL # installbuilder reads the env vars with certs paths and use it to sign the installer. - - name: Launch Bitrock installbuilder-20 with CERT_INSTALL && CHOICE_CERT_INSTALL + - name: Launch Bitrock installbuilder run: | - ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} ${{ env.CERT_INSTALL }} - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-CI${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-${{matrix.browser}}${{matrix.installer-extension}} - ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} ${{ env.CHOICE_CERT_INSTALL }} - cp -vr ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-CC${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-chrome${{matrix.installer-extension}} - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-CC${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-firefox${{matrix.installer-extension}} - rm -r ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-C* - if: matrix.os == 'windows-2019' || matrix.os == 'macos-12' - - # linux - - name: Launch Bitrock installbuilder-20 with NO_CERT_INSTALL - run: | - ${{ env.INSTALLBUILDER_PATH }} build installer.xml linux-x64 --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} ${{ env.NO_CERT_INSTALL }} - cp -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.run ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-chrome.run - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.run ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-firefox.run - cp -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-chrome.tar.gz - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-firefox.tar.gz + ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} architecture=${{ matrix.arch }} + + - name: Generate archive + run: tar -czvf ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-${{ matrix.arch }}-installer.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-${{ matrix.arch }}-installer${{matrix.installer-extension}} if: matrix.os == 'ubuntu-20.04' - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: ArduinoCreateAgent-${{ matrix.install-builder-name }}${{ matrix.arch }} + name: ArduinoCreateAgent-${{ matrix.install-builder-name }}-${{ matrix.arch }} path: ArduinoCreateAgent* if-no-files-found: error @@ -459,20 +441,19 @@ jobs: needs: package strategy: matrix: - browser: [safari, firefox, chrome] - arch: [-amd64] + arch: [amd64] runs-on: macos-12 steps: - name: Download artifact uses: actions/download-artifact@v3 with: - name: ArduinoCreateAgent-osx${{ matrix.arch }} + name: ArduinoCreateAgent-osx-${{ matrix.arch }} path: ArduinoCreateAgent-osx # zip artifacts do not mantain executable permission - name: Make executable - run: chmod -v +x ArduinoCreateAgent-osx/ArduinoCreateAgent-${GITHUB_REF##*/}-osx${{ matrix.arch }}-installer-${{ matrix.browser }}.app/Contents/MacOS/* + run: chmod -v +x ArduinoCreateAgent-osx/ArduinoCreateAgent-${GITHUB_REF##*/}-osx-${{ matrix.arch }}-installer.app/Contents/MacOS/* - name: Import Code-Signing Certificates run: | @@ -502,7 +483,7 @@ jobs: # gon does not allow env variables in config file (https://github.com/mitchellh/gon/issues/20) run: | cat > gon.config_installer.hcl <. // Generate a self-signed X.509 certificate for a TLS server. Outputs to // 'cert.pem' and 'key.pem' and will overwrite existing files. -package main +package certificates import ( "crypto/ecdsa" @@ -23,6 +34,7 @@ import ( "text/template" "time" + "github.com/arduino/arduino-create-agent/config" "github.com/arduino/go-paths-helper" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" @@ -115,14 +127,8 @@ func generateSingleCertificate(isCa bool) (*x509.Certificate, error) { BasicConstraintsValid: true, } - hosts := strings.Split(host, ",") - for _, h := range hosts { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - } + template.IPAddresses = append(template.IPAddresses, net.ParseIP("127.0.0.1")) + template.DNSNames = append(template.DNSNames, "localhost") if isCa { template.IsCA = true @@ -133,10 +139,10 @@ func generateSingleCertificate(isCa bool) (*x509.Certificate, error) { return &template, nil } -// migrateCertificatesGeneratedWithOldAgentVersions checks if certificates generated +// MigrateCertificatesGeneratedWithOldAgentVersions checks if certificates generated // with an old version of the Agent needs to be migrated to the current certificates // directory, and performs the migration if needed. -func migrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { +func MigrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { if certsDir.Join("ca.cert.pem").Exist() { // The new certificates are already set-up, nothing to do return @@ -160,11 +166,8 @@ func migrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { } } -func generateCertificates(certsDir *paths.Path) { - certsDir.Join("ca.cert.pem").Remove() - certsDir.Join("ca.key.pem").Remove() - certsDir.Join("cert.pem").Remove() - certsDir.Join("key.pem").Remove() +// GenerateCertificates will generate the required certificates useful for a HTTPS connection on localhost +func GenerateCertificates(certsDir *paths.Path) { // Create the key for the certification authority caKey, err := generateKey("P256") @@ -259,7 +262,8 @@ func generateCertificates(certsDir *paths.Path) { } } -func certHandler(c *gin.Context) { +// CertHandler will expone the certificate (we do not know why this was required) +func CertHandler(c *gin.Context) { if strings.Contains(c.Request.UserAgent(), "Firefox") { c.Header("content-type", "application/x-x509-ca-cert") c.File("ca.cert.cer") @@ -270,15 +274,19 @@ func certHandler(c *gin.Context) { }) } -func deleteCertHandler(c *gin.Context) { - DeleteCertificates(getCertificatesDir()) +// DeleteCertHandler will delete the certificates +func DeleteCertHandler(c *gin.Context) { + DeleteCertificates(config.GetCertificatesDir()) } // DeleteCertificates will delete the certificates func DeleteCertificates(certDir *paths.Path) { + certDir.Join("ca.key.pem").Remove() certDir.Join("ca.cert.pem").Remove() certDir.Join("ca.cert.cer").Remove() - certDir.Join("ca.key.pem").Remove() + certDir.Join("key.pem").Remove() + certDir.Join("cert.pem").Remove() + certDir.Join("cert.cer").Remove() } const noFirefoxTemplateHTML = ` diff --git a/certificates/install_darwin.go b/certificates/install_darwin.go new file mode 100644 index 000000000..765257b91 --- /dev/null +++ b/certificates/install_darwin.go @@ -0,0 +1,87 @@ +// Copyright 2023 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package certificates + +//inspired by https://stackoverflow.com/questions/12798950/ios-install-ssl-certificate-programmatically + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Cocoa +#import + +const char *installCert(const char *path) { + NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO]; + NSData *rootCertData = [NSData dataWithContentsOfURL:url]; + + OSStatus err = noErr; + SecCertificateRef rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData); + + CFTypeRef result; + + NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: + (id)kSecClassCertificate, kSecClass, + rootCert, kSecValueRef, + nil]; + + err = SecItemAdd((CFDictionaryRef)dict, &result); + + if (err == noErr) { + NSLog(@"Install root certificate success"); + } else if (err == errSecDuplicateItem) { + NSString *errString = [@"duplicate root certificate entry. Error: " stringByAppendingFormat:@"%d", err]; + NSLog(@"%@", errString); + return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]];; + } else { + NSString *errString = [@"install root certificate failure. Error: " stringByAppendingFormat:@"%d", err]; + NSLog(@"%@", errString); + return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; + } + + NSDictionary *newTrustSettings = @{(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot]}; + err = SecTrustSettingsSetTrustSettings(rootCert, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(newTrustSettings)); + if (err != errSecSuccess) { + NSString *errString = [@"Could not change the trust setting for a certificate. Error: " stringByAppendingFormat:@"%d", err]; + NSLog(@"%@", errString); + return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; + } + + return ""; +} + +*/ +import "C" +import ( + "errors" + "os/exec" + + log "github.com/sirupsen/logrus" + + "github.com/arduino/go-paths-helper" +) + +// InstallCertificate will install the certificates in the system keychain on macos, +// if something goes wrong will show a dialog with the error and return an error +func InstallCertificate(cert *paths.Path) error { + log.Infof("Installing certificate: %s", cert) + p := C.installCert(C.CString(cert.String())) + s := C.GoString(p) + if len(s) != 0 { + oscmd := exec.Command("osascript", "-e", "display dialog \""+s+"\" buttons \"OK\" with title \"Error installing certificates\"") + _ = oscmd.Run() + return errors.New(s) + } + return nil +} diff --git a/certificates/install_default.go b/certificates/install_default.go new file mode 100644 index 000000000..2a1cf794f --- /dev/null +++ b/certificates/install_default.go @@ -0,0 +1,32 @@ +// Copyright 2023 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//go:build !darwin + +package certificates + +import ( + "errors" + + log "github.com/sirupsen/logrus" + + "github.com/arduino/go-paths-helper" +) + +// InstallCertificate won't do anything on unsupported Operative Systems +func InstallCertificate(cert *paths.Path) error { + log.Warn("platform not supported for the certificate install") + return errors.New("platform not supported for the certificate install") +} diff --git a/config.go b/config/config.go similarity index 71% rename from config.go rename to config/config.go index 182e24c52..303aadce8 100644 --- a/config.go +++ b/config/config.go @@ -13,9 +13,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package main +package config import ( + // we need this for the config ini in this package _ "embed" "os" @@ -23,13 +24,19 @@ import ( log "github.com/sirupsen/logrus" ) -// getCertificatesDir return the directory where SSL certificates are saved -func getCertificatesDir() *paths.Path { - return getDataDir() +// GetCertificatesDir return the directory where SSL certificates are saved +func GetCertificatesDir() *paths.Path { + return GetDataDir() } -// getDataDir returns the full path to the default Arduino Create Agent data directory. -func getDataDir() *paths.Path { +// CertsExist checks if the certs have already been generated +func CertsExist() bool { + certFile := GetCertificatesDir().Join("cert.pem") + return certFile.Exist() //if the certFile is not present we assume there are no certs +} + +// GetDataDir returns the full path to the default Arduino Create Agent data directory. +func GetDataDir() *paths.Path { userDir, err := os.UserHomeDir() if err != nil { log.Panicf("Could not get user dir: %s", err) @@ -41,17 +48,22 @@ func getDataDir() *paths.Path { return dataDir } -// getLogsDir return the directory where logs are saved -func getLogsDir() *paths.Path { - logsDir := getDataDir().Join("logs") +// GetLogsDir return the directory where logs are saved +func GetLogsDir() *paths.Path { + logsDir := GetDataDir().Join("logs") if err := logsDir.MkdirAll(); err != nil { log.Panicf("Can't create logs dir: %s", err) } return logsDir } -// getDefaultConfigDir returns the full path to the default Arduino Create Agent configuration directory. -func getDefaultConfigDir() *paths.Path { +// LogsIsEmpty checks if the folder containing crash-reports is empty +func LogsIsEmpty() bool { + return GetLogsDir().NotExist() // if the logs directory is empty we assume there are no crashreports +} + +// GetDefaultConfigDir returns the full path to the default Arduino Create Agent configuration directory. +func GetDefaultConfigDir() *paths.Path { // UserConfigDir returns the default root directory to use // for user-specific configuration data. Users should create // their own application-specific subdirectory within this @@ -82,10 +94,10 @@ func getDefaultConfigDir() *paths.Path { //go:embed config.ini var configContent []byte -// generateConfig function will take a directory path as an input +// GenerateConfig function will take a directory path as an input // and will write the default config,ini file to that directory, // it will panic if something goes wrong -func generateConfig(destDir *paths.Path) *paths.Path { +func GenerateConfig(destDir *paths.Path) *paths.Path { configPath := destDir.Join("config.ini") // generate the config.ini file directly in destDir diff --git a/config.ini b/config/config.ini similarity index 100% rename from config.ini rename to config/config.ini diff --git a/hub.go b/hub.go index f59c6460e..003d3d959 100755 --- a/hub.go +++ b/hub.go @@ -26,6 +26,9 @@ import ( "strconv" "strings" + cert "github.com/arduino/arduino-create-agent/certificates" + "github.com/arduino/arduino-create-agent/config" + "github.com/arduino/arduino-create-agent/upload" log "github.com/sirupsen/logrus" ) @@ -182,7 +185,7 @@ func checkCmd(m []byte) { } else if strings.HasPrefix(sl, "downloadtool") { // Always delete root certificates when we receive a downloadtool command // Useful if the install procedure was not followed strictly (eg. manually) - DeleteCertificates(getCertificatesDir()) + cert.DeleteCertificates(config.GetCertificatesDir()) go func() { args := strings.Split(s, " ") var tool, toolVersion, pack, behaviour string diff --git a/main.go b/main.go index f6bc3afd6..1011ccefe 100755 --- a/main.go +++ b/main.go @@ -31,6 +31,8 @@ import ( "time" cors "github.com/andela/gin-cors" + cert "github.com/arduino/arduino-create-agent/certificates" + "github.com/arduino/arduino-create-agent/config" "github.com/arduino/arduino-create-agent/systray" "github.com/arduino/arduino-create-agent/tools" "github.com/arduino/arduino-create-agent/updater" @@ -127,17 +129,17 @@ func main() { // Generate certificates if *genCert { - generateCertificates(getCertificatesDir()) + cert.GenerateCertificates(config.GetCertificatesDir()) os.Exit(0) } // Check if certificates made with Agent <=1.2.7 needs to be moved over the new location - migrateCertificatesGeneratedWithOldAgentVersions(getCertificatesDir()) + cert.MigrateCertificatesGeneratedWithOldAgentVersions(config.GetCertificatesDir()) // Launch main loop in a goroutine go loop() // SetupSystray is the main thread - configDir := getDefaultConfigDir() + configDir := config.GetDefaultConfigDir() Systray = systray.Systray{ Hibernate: *hibernate, Version: version + "-" + commit, @@ -167,7 +169,7 @@ func loop() { // Instantiate Tools Tools = tools.Tools{ - Directory: getDataDir().String(), + Directory: config.GetDataDir().String(), IndexURL: *indexURL, Logger: func(msg string) { mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg} @@ -178,7 +180,7 @@ func loop() { Tools.Init(requiredToolsAPILevel) // Let's handle the config - configDir := getDefaultConfigDir() + configDir := config.GetDefaultConfigDir() var configPath *paths.Path // see if the env var is defined, if it is take the config from there, this will override the default path @@ -207,7 +209,7 @@ func loop() { } } if configPath == nil { - configPath = generateConfig(configDir) + configPath = config.GenerateConfig(configDir) } // Parse the config.ini @@ -316,7 +318,7 @@ func loop() { if *crashreport { logFilename := "crashreport_" + time.Now().Format("20060102150405") + ".log" // handle logs directory creation - logsDir := getLogsDir() + logsDir := config.GetLogsDir() logFile, err := os.OpenFile(logsDir.Join(logFilename).String(), os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0644) if err != nil { log.Print("Cannot create file used for crash-report") @@ -348,6 +350,7 @@ func loop() { extraOrigins = append(extraOrigins, "http://localhost:"+port) extraOrigins = append(extraOrigins, "https://localhost:"+port) extraOrigins = append(extraOrigins, "http://127.0.0.1:"+port) + extraOrigins = append(extraOrigins, "https://127.0.0.1:"+port) } r.Use(cors.Middleware(cors.Config{ @@ -363,8 +366,8 @@ func loop() { r.LoadHTMLFiles("templates/nofirefox.html") r.GET("/", homeHandler) - r.GET("/certificate.crt", certHandler) - r.DELETE("/certificate.crt", deleteCertHandler) + r.GET("/certificate.crt", cert.CertHandler) + r.DELETE("/certificate.crt", cert.DeleteCertHandler) r.POST("/upload", uploadHandler) r.GET("/socket.io/", socketHandler) r.POST("/socket.io/", socketHandler) @@ -376,12 +379,12 @@ func loop() { r.POST("/update", updateHandler) // Mount goa handlers - goa := v2.Server(getDataDir().String()) + goa := v2.Server(config.GetDataDir().String()) r.Any("/v2/*path", gin.WrapH(goa)) go func() { // check if certificates exist; if not, use plain http - certsDir := getCertificatesDir() + certsDir := config.GetCertificatesDir() if certsDir.Join("cert.pem").NotExist() { log.Error("Could not find HTTPS certificate. Using plain HTTP only.") return diff --git a/systray/systray_real.go b/systray/systray_real.go index 106306542..74ed12c67 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -21,12 +21,14 @@ package systray import ( "os" - "os/user" + "runtime" log "github.com/sirupsen/logrus" + cert "github.com/arduino/arduino-create-agent/certificates" + "github.com/arduino/arduino-create-agent/config" + "github.com/arduino/arduino-create-agent/icon" - "github.com/arduino/go-paths-helper" "github.com/getlantern/systray" "github.com/go-ini/ini" "github.com/skratchdot/open-golang/open" @@ -61,7 +63,16 @@ func (s *Systray) start() { // Remove crash-reports mRmCrashes := systray.AddMenuItem("Remove crash reports", "") - s.updateMenuItem(mRmCrashes, s.CrashesIsEmpty()) + s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) + + mGenCerts := systray.AddMenuItem("Generate and Install HTTPS certificates", "HTTPS Certs") + // On linux/windows chrome/firefox/edge(chromium) the agent works without problems on plain HTTP, + // so we disable the menuItem to generate/install the certificates + if runtime.GOOS != "darwin" { + s.updateMenuItem(mGenCerts, true) + } else { + s.updateMenuItem(mGenCerts, config.CertsExist()) + } // Add pause/quit mPause := systray.AddMenuItem("Pause Agent", "") @@ -82,8 +93,17 @@ func (s *Systray) start() { case <-mConfig.ClickedCh: _ = open.Start(s.currentConfigFilePath.String()) case <-mRmCrashes.ClickedCh: - s.RemoveCrashes() - s.updateMenuItem(mRmCrashes, s.CrashesIsEmpty()) + RemoveCrashes() + s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) + case <-mGenCerts.ClickedCh: + certDir := config.GetCertificatesDir() + cert.GenerateCertificates(certDir) + err := cert.InstallCertificate(certDir.Join("ca.cert.cer")) + // if something goes wrong during the cert install we remove them, so the user is able to retry + if err != nil { + cert.DeleteCertificates(certDir) + } + s.Restart() case <-mPause.ClickedCh: s.Pause() case <-mQuit.ClickedCh: @@ -102,15 +122,9 @@ func (s *Systray) updateMenuItem(item *systray.MenuItem, disable bool) { } } -// CrashesIsEmpty checks if the folder containing crash-reports is empty -func (s *Systray) CrashesIsEmpty() bool { - logsDir := getLogsDir() - return logsDir.NotExist() // if the logs directory is empty we assume there are no crashreports -} - // RemoveCrashes removes the crash-reports from `logs` folder -func (s *Systray) RemoveCrashes() { - logsDir := getLogsDir() +func RemoveCrashes() { + logsDir := config.GetLogsDir() pathErr := logsDir.RemoveAll() if pathErr != nil { log.Errorf("Cannot remove crashreports: %s", pathErr) @@ -119,14 +133,6 @@ func (s *Systray) RemoveCrashes() { } } -// getLogsDir simply returns the folder containing the logs -func getLogsDir() *paths.Path { - usr, _ := user.Current() - usrDir := paths.New(usr.HomeDir) // The user folder, on linux/macos /home// - agentDir := usrDir.Join(".arduino-create") - return agentDir.Join("logs") -} - // starthibernate creates a systray icon with menu options to resume/quit the agent func (s *Systray) startHibernate() { systray.SetIcon(icon.GetIconHiber())