diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d7fc6ed..2c21aab 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,13 +5,8 @@ on: branches: - master - dev - paths: - - .github/workflows/build.yaml - - build/* + - http-rest pull_request: - paths: - - .github/workflows/build.yaml - - build/* env: DOCKER_REPO: shenxn/protonmail-bridge @@ -31,7 +26,7 @@ jobs: uses: actions/checkout@master - name: Set version id: version - run: echo "::set-output name=version::`cat build/VERSION`" + run: echo "::set-output name=version::`cat VERSION`" - name: Set repo id: repo run: if [[ $GITHUB_REF == "refs/heads/master" ]]; then echo "::set-output name=repo::${DOCKER_REPO}"; else echo "::set-output name=repo::${DOCKER_REPO_DEV}"; fi @@ -49,8 +44,6 @@ jobs: - name: Build image without push to registry uses: docker/build-push-action@v2 with: - context: ./build - file: ./build/Dockerfile platforms: ${{ env.PLATFORMS }} push: true tags: localhost:5000/protonmail-bridge:latest diff --git a/.github/workflows/deb.yaml b/.github/workflows/deb.yaml deleted file mode 100644 index a529c66..0000000 --- a/.github/workflows/deb.yaml +++ /dev/null @@ -1,80 +0,0 @@ -name: pack from deb - -on: - push: - branches: - - master - - dev - paths: - - .github/workflows/deb.yaml - - deb/* - pull_request: - paths: - - .github/workflows/deb.yaml - - deb/* - -env: - DOCKER_REPO: shenxn/protonmail-bridge - DOCKER_REPO_DEV: ghcr.io/shenxn/protonmail-bridge-dev - -jobs: - deb: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@master - - name: Set version - id: version - run: echo "::set-output name=version::`cat deb/VERSION`" - - name: Set repo - id: repo - run: if [[ $GITHUB_REF == "refs/heads/master" ]]; then echo "::set-output name=repo::${DOCKER_REPO}"; else echo "::set-output name=repo::${DOCKER_REPO_DEV}"; fi - - name: Docker meta - id: docker_meta - uses: crazy-max/ghaction-docker-meta@v1 - with: - images: ${{ steps.repo.outputs.repo }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Build image without push - uses: docker/build-push-action@v2 - with: - context: ./deb - file: ./deb/Dockerfile - load: true - tags: protonmail-bridge:latest - - name: Scan image - id: scan - uses: anchore/scan-action@v2 - with: - image: protonmail-bridge:latest - fail-build: true - severity-cutoff: critical - acs-report-enable: true - - name: Upload Anchore scan SARIF report - uses: github/codeql-action/upload-sarif@v1 - with: - sarif_file: ${{ steps.scan.outputs.sarif }} - - name: Login to DockerHub - uses: docker/login-action@v1 - if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }} - with: - username: ${{ secrets.REGISTRY_USERNAME }} - password: ${{ secrets.REGISTRY_PASSWORD }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 - if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/dev' }} - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.CR_PAT }} - - name: Push image - uses: docker/build-push-action@v2 - with: - context: ./deb - file: ./deb/Dockerfile - tags: | - ${{ steps.repo.outputs.repo }}:latest - ${{ steps.repo.outputs.repo }}:${{ steps.version.outputs.version }} - labels: ${{ steps.docker_meta.outputs.labels }} - push: ${{ github.event_name != 'pull_request' }} diff --git a/.gitignore b/.gitignore index e69de29..722d5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +.vscode diff --git a/build/Dockerfile b/Dockerfile similarity index 65% rename from build/Dockerfile rename to Dockerfile index ae1c6f7..5a02ce3 100644 --- a/build/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ RUN apt-get update && apt-get install -y libsecret-1-dev # Build WORKDIR /build/ COPY build.sh VERSION /build/ +COPY http_rest_frontend /build/http_rest_frontend RUN bash build.sh FROM ubuntu:bionic @@ -16,13 +17,13 @@ EXPOSE 143/tcp # Install dependencies and protonmail bridge RUN apt-get update \ - && apt-get install -y --no-install-recommends socat pass libsecret-1-0 ca-certificates \ + && apt-get install -y --no-install-recommends socat pass libsecret-1-0 ca-certificates dbus \ && rm -rf /var/lib/apt/lists/* # Copy bash scripts -COPY gpgparams entrypoint.sh /protonmail/ +COPY gpgparams entrypoint.sh /srv/protonmail/ # Copy protonmail -COPY --from=build /build/proton-bridge/proton-bridge /protonmail/ +COPY --from=build /build/proton-bridge/proton-bridge /srv/protonmail/ -ENTRYPOINT ["bash", "/protonmail/entrypoint.sh"] +ENTRYPOINT ["bash", "/srv/protonmail/entrypoint.sh"] diff --git a/build/VERSION b/VERSION similarity index 100% rename from build/VERSION rename to VERSION diff --git a/build/build.sh b/build.sh similarity index 62% rename from build/build.sh rename to build.sh index 660ac4b..e09432c 100644 --- a/build/build.sh +++ b/build.sh @@ -9,14 +9,23 @@ git clone https://github.com/ProtonMail/proton-bridge.git cd proton-bridge git checkout v$VERSION +ls /build + +# Patch HTTP REST frontend +rm -rf internal/frontend/cli +cp -r /build/http_rest_frontend/cli internal/frontend/cli + # Build if ! make build-nogui ; then # If build fails it's probably because it is a 32bit # system and there was a overflow error on the parser # This is a workaround for this problem found at: # https://github.com/antlr/antlr4/issues/2433#issuecomment-774514106 - find $(go env GOPATH)/pkg/mod/github.com/\!proton\!mail/go-rfc5322*/ -type f -exec sed -i.bak 's/(1<: Remove the account from keychain. You can use index or account name as the parameter." + echo " list: Print list of your accounts." + echo " info : Print account configuration. You can use index or account name as the parameter." + echo " help: Print help messages." + echo " exit: Exit the CLI" +} + +account_list() { + curl ${URL_BASE}/accounts +} + +account_login() { + read -p "Username: " USERNAME + read -sp "Password: " PASSWORD + echo + read -p "2FA Code (leave empty if not set): " TWO_FACTOR + read -p "Mailbox Password (leave empty if not set): " MAILBOX_PASSWORD + + curl ${URL_BASE}/accounts -XPUT \ + --data-urlencode "username=${USERNAME}" \ + --data-urlencode "password=${PASSWORD}" \ + --data-urlencode "two-factor=${TWO_FACTOR}" \ + --data-urlencode "mailbox-password=${MAILBOX_PASSWORD}" +} + +account_delete() { + if [[ -z $1 ]]; then + echo "Error: delete requires one parameter, which is the index or account name." + else + read -p "Are you sure you want to delete account $1? " REPLY + if [[ $REPLY =~ ^[Yy] ]]; then + curl ${URL_BASE}/accounts/$1 -XDELETE + else + echo "Abort" + fi + fi +} + +account_info() { + if [[ -z $1 ]]; then + echo "Error: info requires one parameter, which is the index or account name." + else + curl ${URL_BASE}/accounts/$1 + fi +} + +echo "CLI to interacte with Proton Bridge HTTP REST interface" +print_help +while true; do + echo + read -p ">> " COMMAND ARG + case "$COMMAND" in + login) + account_login;; + delete) + account_delete $ARG;; + list) + account_list;; + info) + account_info $ARG;; + help) + print_help;; + exit) + exit 0;; + *) + echo "Invalid command" + esac +done diff --git a/deb/.dockerignore b/deb/.dockerignore deleted file mode 100644 index b762fe2..0000000 --- a/deb/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -* - -!.dockerignore -!VERSION -!entrypoint.sh -!install.sh -!gpgparams -!Dockerfile diff --git a/deb/Dockerfile b/deb/Dockerfile deleted file mode 100644 index d225daf..0000000 --- a/deb/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM ubuntu:bionic -LABEL maintainer="Xiaonan Shen " - -EXPOSE 25/tcp -EXPOSE 143/tcp - -WORKDIR /protonmail - -# Copy bash scripts -COPY gpgparams install.sh entrypoint.sh VERSION /protonmail/ - -# Install dependencies and protonmail bridge -RUN bash install.sh - -ENTRYPOINT ["bash", "/protonmail/entrypoint.sh"] diff --git a/deb/VERSION b/deb/VERSION deleted file mode 100644 index f17fc9b..0000000 --- a/deb/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.1.1-1 \ No newline at end of file diff --git a/deb/entrypoint.sh b/deb/entrypoint.sh deleted file mode 100644 index 13637e5..0000000 --- a/deb/entrypoint.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -set -ex - -# Initialize -if [[ $1 == init ]]; then - - # # Parse parameters - # TFP="" # Default empty two factor passcode - # shift # skip `init` - # while [[ $# -gt 0 ]]; do - # key="$1" - # case $key in - # -u|--username) - # USERNAME="$2" - # ;; - # -p|--password) - # PASSWORD="$2" - # ;; - # -t|--twofactor) - # TWOFACTOR="$2" - # ;; - # esac - # shift - # shift - # done - - # Initialize pass - gpg --generate-key --batch /protonmail/gpgparams - pass init pass-key - - # Login - protonmail-bridge --cli - -else - - # socat will make the conn appear to come from 127.0.0.1 - # ProtonMail Bridge currently expects that. - # It also allows us to bind to the real ports :) - socat TCP-LISTEN:25,fork TCP:127.0.0.1:1025 & - socat TCP-LISTEN:143,fork TCP:127.0.0.1:1143 & - - # Start protonmail - # Fake a terminal, so it does not quit because of EOF... - rm -f faketty - mkfifo faketty - cat faketty | protonmail-bridge --cli - -fi diff --git a/deb/gpgparams b/deb/gpgparams deleted file mode 100644 index 355568e..0000000 --- a/deb/gpgparams +++ /dev/null @@ -1,8 +0,0 @@ -%no-protection -%echo Generating a basic OpenPGP key -Key-Type: RSA -Key-Length: 2048 -Name-Real: pass-key -Expire-Date: 0 -%commit -%echo done diff --git a/deb/install.sh b/deb/install.sh deleted file mode 100644 index dbe3eb5..0000000 --- a/deb/install.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -set -ex - -VERSION=`cat VERSION` -DEB_FILE=protonmail-bridge_${VERSION}_amd64.deb - -# Install dependents -apt-get update -apt-get install -y --no-install-recommends socat pass ca-certificates - -# Build time dependencies -apt-get install -y wget binutils xz-utils - -# Repack deb (remove unnecessary dependencies) -mkdir deb -cd deb -wget -q https://protonmail.com/download/bridge/${DEB_FILE} -ar x -v ${DEB_FILE} -mkdir control -tar zxvf control.tar.gz -C control -sed -i "s/^Depends: .*$/Depends: libgl1, libc6, libsecret-1-0, libstdc++6, libgcc1/" control/control -cd control -tar zcvf ../control.tar.gz . -cd ../ -ar rcs -v ${DEB_FILE} debian-binary control.tar.gz data.tar.gz -cd ../ - -# Install protonmail bridge -apt-get install -y --no-install-recommends ./deb/${DEB_FILE} - -# Cleanup -apt-get purge -y wget binutils xz-utils -apt-get autoremove -y -rm -rf /var/lib/apt/lists/* -rm -rf deb diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..e25dd56 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +set -e + +# Generate gpg keys +if [ ! -f ${HOME}/.gnupg ]; then + echo "Generateing gpg keys..." + # set GNUPGHOME as a workaround for + # + # gpg-agent[106]: error binding socket to '/root/.gnupg/S.gpg-agent': File name too long + # + # when using docker volume mount + # + # ref: https://dev.gnupg.org/T2964 + # + export GNUPGHOME=/tmp/gnupg + mkdir ${GNUPGHOME} + chmod 700 ${GNUPGHOME} + gpg --generate-key --batch /srv/protonmail/gpgparams + pkill gpg-agent + mv ${GNUPGHOME} ${HOME}/.gnupg + export GNUPGHOME="" +fi + +# Initialize pass +if [ ! -f ${HOME}/.password-store/.gpg-id ]; then + echo "Initializing pass" + pass init pass-key +fi + +# socat will make the conn appear to come from 127.0.0.1 +# ProtonMail Bridge currently expects that. +# It also allows us to bind to the real ports :) +socat TCP-LISTEN:25,fork TCP:127.0.0.1:1025 & +socat TCP-LISTEN:143,fork TCP:127.0.0.1:1143 & + +/srv/protonmail/proton-bridge --cli $@ diff --git a/build/gpgparams b/gpgparams similarity index 100% rename from build/gpgparams rename to gpgparams diff --git a/http_rest_frontend/cli/accounts.go b/http_rest_frontend/cli/accounts.go new file mode 100644 index 0000000..500323d --- /dev/null +++ b/http_rest_frontend/cli/accounts.go @@ -0,0 +1,147 @@ +// Package cli provides HTTP interface of the bridge +package cli + +import ( + "context" + "fmt" + "io" + "net/http" + "strings" + + "github.com/ProtonMail/proton-bridge/internal/config/settings" + "github.com/ProtonMail/proton-bridge/internal/frontend/types" + "github.com/julienschmidt/httprouter" +) + +func (f *frontendCLI) loginAccount(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + if err := r.ParseForm(); err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "ParseForm() err: %v", err) + return + } + username := r.FormValue("username") + password := r.FormValue("password") + twoFactor := r.FormValue("two-factor") + mailboxPassword := r.FormValue("mailbox-password") + client, auth, err := f.bridge.Login(username, []byte(password)) + if err != nil { + f.processAPIError(err) + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprintln(w, "Server error:", err.Error()) + return + } + + if auth.HasTwoFactor() { + if twoFactor == "" { + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprintln(w, "2FA enabled for the account but a 2FA code was not provided.") + return + } + err = client.Auth2FA(context.Background(), twoFactor) + if err != nil { + f.processAPIError(err) + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprintln(w, "Server error:", err.Error()) + return + } + } + + if auth.HasMailboxPassword() { + if mailboxPassword == "" { + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprintln(w, "Two password mode enabled but a mailbox password was not provided.") + return + } + } else { + mailboxPassword = password + } + user, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword)) + if err != nil { + f.processAPIError(err) + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprintln(w, "Server error:", err.Error()) + return + } + fmt.Fprintf(w, "Account %s was added successfully.\n", user.Username()) + f.printAccountInfo(w, user) +} + +func (f *frontendCLI) deleteAccount(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + account := params.ByName("account") + user := f.getUserByIndexOrName(account) + if user == nil { + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, "Account %s does not exist.\n", account) + return + } + account = user.Username() + if err := f.bridge.DeleteUser(user.ID(), true); err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintln(w, "Cannot delete account: ", err) + return + } + fmt.Fprintf(w, "Account %s was deleted successfully.\n", account) +} + +func (f *frontendCLI) listAccounts(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + users := f.bridge.GetUsers() + if len(users) == 0 { + fmt.Fprintln(w, "No account found.") + return + } + spacing := "%-2d: %-20s (%-15s, %-15s)\n" + fmt.Fprintf(w, strings.ReplaceAll(spacing, "d", "s"), "#", "account", "status", "address mode") + for idx, user := range users { + connected := "disconnected" + if user.IsConnected() { + connected = "connected" + } + mode := "split" + if user.IsCombinedAddressMode() { + mode = "combined" + } + fmt.Fprintf(w, spacing, idx, user.Username(), connected, mode) + } +} + +func (f *frontendCLI) showAccountInfo(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + account := params.ByName("account") + user := f.getUserByIndexOrName(account) + if user == nil { + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, "Account %s does not exist.\n", account) + return + } + if !user.IsConnected() { + fmt.Fprintf(w, "Please login to %s to get email client configuration.\n", user.Username()) + return + } + f.printAccountInfo(w, user) +} + +func (f *frontendCLI) printAccountInfo(w io.Writer, user types.User) { + if user.IsCombinedAddressMode() { + f.printAccountAddressInfo(w, user, user.GetPrimaryAddress()) + } else { + for _, address := range user.GetAddresses() { + f.printAccountAddressInfo(w, user, address) + } + } +} + +func (f *frontendCLI) printAccountAddressInfo(w io.Writer, user types.User, address string) { + fmt.Fprintln(w, "Configuration for", address) + smtpSecurity := "STARTTLS" + if f.settings.GetBool(settings.SMTPSSLKey) { + smtpSecurity = "SSL" + } + fmt.Fprintf(w, "IMAP port: %d\nIMAP security: %s\nSMTP port: %d\nSMTP security: %s\nUsername: %s\nPassword: %s\n", + 143, + "STARTTLS", + 25, + smtpSecurity, + address, + user.GetBridgePassword(), + ) + fmt.Fprintln(w, "") +} diff --git a/http_rest_frontend/cli/accounts_util.go b/http_rest_frontend/cli/accounts_util.go new file mode 100644 index 0000000..2284d42 --- /dev/null +++ b/http_rest_frontend/cli/accounts_util.go @@ -0,0 +1,24 @@ +package cli + +import ( + "strconv" + + "github.com/ProtonMail/proton-bridge/internal/frontend/types" +) + +func (f *frontendCLI) getUserByIndexOrName(account string) types.User { + users := f.bridge.GetUsers() + numberOfAccounts := len(users) + if index, err := strconv.Atoi(account); err == nil { + if index < 0 || index >= numberOfAccounts { + return nil + } + return users[index] + } + for _, user := range users { + if user.Username() == account { + return user + } + } + return nil +} diff --git a/http_rest_frontend/cli/frontend.go b/http_rest_frontend/cli/frontend.go new file mode 100644 index 0000000..224de01 --- /dev/null +++ b/http_rest_frontend/cli/frontend.go @@ -0,0 +1,157 @@ +// Package cli provides HTTP interface of the bridge +package cli + +import ( + "context" + "fmt" + "net/http" + "os" + "strings" + + "github.com/ProtonMail/proton-bridge/internal/config/settings" + "github.com/ProtonMail/proton-bridge/internal/events" + "github.com/ProtonMail/proton-bridge/internal/frontend/types" + "github.com/ProtonMail/proton-bridge/internal/locations" + "github.com/ProtonMail/proton-bridge/internal/updater" + "github.com/ProtonMail/proton-bridge/pkg/listener" + "github.com/julienschmidt/httprouter" + "github.com/sirupsen/logrus" +) + +type frontendCLI struct { + *httprouter.Router + + locations *locations.Locations + settings *settings.Settings + eventListener listener.Listener + updater types.Updater + bridge types.Bridger + restarter types.Restarter +} + +func New( + panicHandler types.PanicHandler, + + locations *locations.Locations, + settings *settings.Settings, + eventListener listener.Listener, + updater types.Updater, + bridge types.Bridger, + restarter types.Restarter, +) *frontendCLI { + fe := &frontendCLI{ + Router: httprouter.New(), + locations: locations, + settings: settings, + eventListener: eventListener, + updater: updater, + bridge: bridge, + restarter: restarter, + } + + fe.PUT("/accounts", fe.loginAccount) + fe.GET("/accounts", fe.listAccounts) + fe.GET("/accounts/:account", fe.showAccountInfo) + fe.DELETE("/accounts/:account", fe.deleteAccount) + + return fe +} + +func (f *frontendCLI) loginWithEnv() { + if len(f.bridge.GetUsers()) > 0 { + fmt.Println("More than 0 accounts found. Skip auto login.") + return + } + username := os.Getenv("PROTON_USERNAME") + password := os.Getenv("PROTON_PASSWORD") + if username == "" { + logrus.Info("PROTON_USERNAME and PROTON_PASSWORD are not set. Skip auto login.") + return + } + client, auth, err := f.bridge.Login(username, []byte(password)) + if err != nil { + f.processAPIError(err) + logrus.WithError(err).Warn("Login failed.") + return + } + + if auth.HasTwoFactor() { + twoFactor := os.Getenv("PROTON_2FA") + if twoFactor == "" { + logrus.Warn("Login failed: 2FA enabled for the account but PROTON_2FA was not set.") + return + } + err = client.Auth2FA(context.Background(), twoFactor) + if err != nil { + f.processAPIError(err) + logrus.WithError(err).Warn("Login failed.") + return + } + } + + mailboxPassword := password + if auth.HasMailboxPassword() { + mailboxPassword = os.Getenv("PROTON_MAILBOX_PASSWORD") + if mailboxPassword == "" { + logrus.Warn("Login failed: Two password mode enabled but PROTON_MAILBOX_PASSWORD was not set.") + return + } + } + user, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword)) + if err != nil { + f.processAPIError(err) + logrus.WithError(err).Warn("Login failed.") + return + } + logrus.Infof("Account %s was added successfully.\n", user.Username()) + if strings.ToLower(os.Getenv("PROTON_PRINT_ACCOUNT_INFO")) != "off" { + f.printAccountInfo(os.Stdout, user) + } +} + +func (f *frontendCLI) watchEvents() { + errorCh := f.eventListener.ProvideChannel(events.ErrorEvent) + credentialsErrorCh := f.eventListener.ProvideChannel(events.CredentialsErrorEvent) + internetOffCh := f.eventListener.ProvideChannel(events.InternetOffEvent) + internetOnCh := f.eventListener.ProvideChannel(events.InternetOnEvent) + addressChangedCh := f.eventListener.ProvideChannel(events.AddressChangedEvent) + addressChangedLogoutCh := f.eventListener.ProvideChannel(events.AddressChangedLogoutEvent) + logoutCh := f.eventListener.ProvideChannel(events.LogoutEvent) + certIssue := f.eventListener.ProvideChannel(events.TLSCertIssue) + for { + select { + case errorDetails := <-errorCh: + fmt.Println("Bridge failed:", errorDetails) + case <-credentialsErrorCh: + f.notifyCredentialsError() + case <-internetOffCh: + f.notifyInternetOff() + case <-internetOnCh: + f.notifyInternetOn() + case address := <-addressChangedCh: + fmt.Printf("Address changed for %s. You may need to reconfigure your email client.", address) + case address := <-addressChangedLogoutCh: + f.notifyLogout(address) + case userID := <-logoutCh: + user, err := f.bridge.GetUser(userID) + if err != nil { + return + } + f.notifyLogout(user.Username()) + case <-certIssue: + f.notifyCertIssue() + } + } +} + +func (f *frontendCLI) Loop() error { + f.loginWithEnv() + http.ListenAndServe(":1080", f) + return nil +} + +func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {} +func (f *frontendCLI) WaitUntilFrontendIsReady() {} +func (f *frontendCLI) SetVersion(version updater.VersionInfo) {} +func (f *frontendCLI) NotifySilentUpdateInstalled() {} +func (f *frontendCLI) NotifySilentUpdateError(err error) {} diff --git a/http_rest_frontend/cli/utils.go b/http_rest_frontend/cli/utils.go new file mode 100644 index 0000000..a6f736f --- /dev/null +++ b/http_rest_frontend/cli/utils.go @@ -0,0 +1,57 @@ +package cli + +import ( + "github.com/ProtonMail/proton-bridge/pkg/pmapi" + "github.com/sirupsen/logrus" +) + +func (f *frontendCLI) processAPIError(err error) { + switch err { + case pmapi.ErrNoConnection: + f.notifyInternetOff() + case pmapi.ErrUpgradeApplication: + f.notifyNeedUpgrade() + } +} + +func (f *frontendCLI) notifyInternetOff() { + logrus.Warn("Internet connection is not available.") +} + +func (f *frontendCLI) notifyInternetOn() { + logrus.Info("Internet connection is available again.") +} + +func (f *frontendCLI) notifyLogout(address string) { + logrus.Infof("Account %s is disconnected. Login to continue using this account with email client.", address) +} + +func (f *frontendCLI) notifyNeedUpgrade() { + logrus.Info("Upgrade needed. Please download and install the newest version of application.") +} + +func (f *frontendCLI) notifyCredentialsError() { + logrus.Error(`ProtonMail Bridge is not able to detect a supported password manager +(secret-service or pass). Please install and set up a supported password manager +and restart the application. +`) +} + +func (f *frontendCLI) notifyCertIssue() { + // Print in 80-column width. + logrus.Error(`Connection security error: Your network connection to Proton services may +be insecure. + +Description: +ProtonMail Bridge was not able to establish a secure connection to Proton +servers due to a TLS certificate error. This means your connection may +potentially be insecure and susceptible to monitoring by third parties. + +Recommendation: +* If you trust your network operator, you can continue to use ProtonMail + as usual. +* If you don't trust your network operator, reconnect to ProtonMail over a VPN + (such as ProtonVPN) which encrypts your Internet connection, or use + a different network to access ProtonMail. +`) +}