From 007314815b5349fdaed60f6e0cca1984d0724d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Randsborg=20B=C3=B8lstad?= <69513590+fredrikrab@users.noreply.github.com> Date: Sun, 1 Dec 2024 05:35:00 +0100 Subject: [PATCH] Initial commit --- .github/workflows/release.yml | 37 +++++ .gitignore | 23 +++ .goreleaser.yaml | 53 +++++++ LICENSE | 13 ++ README.md | 96 ++++++++++++ client.go | 282 ++++++++++++++++++++++++++++++++++ demo.gif | Bin 0 -> 61998 bytes go.mod | 13 ++ go.sum | 12 ++ main.go | 189 +++++++++++++++++++++++ 10 files changed, 718 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .goreleaser.yaml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 client.go create mode 100644 demo.gif create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..92f13b7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Validate Tag Format + run: | + echo "Validating tag $GITHUB_REF..." + if [[ ! "$GITHUB_REF" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[0-9]+)*)?$ ]]; then + echo "Invalid tag format: $GITHUB_REF" + echo "Expected format: vMAJOR.MINOR.PATCH (e.g. v1.0.0)" + echo "(Pre-release tags are also valid, e.g. v1.0.0-alpha)" + exit 1 + fi + - name: Set up Go + uses: actions/setup-go@v5 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v5 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9de09af --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Created by https://www.toptal.com/developers/gitignore/api/go + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Go workspace file +go.work + +# End of https://www.toptal.com/developers/gitignore/api/go + +# Build files +masked_fastmail +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..287e791 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,53 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# vim: set ts=2 sw=2 tw=0 fo=cnqoj +project_name: masked_fastmail +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + ldflags: + - -s -w + - "-X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" + +archives: + - format: tar.gz + # this name template makes the OS and Arch compatible with the results of `uname`. + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + # use zip for windows archives + format_overrides: + - goos: windows + format: zip + +changelog: + sort: asc + groups: + - title: Features + regexp: "^feat:" + order: 0 + - title: Bug Fixes + regexp: "^fix:" + order: 1 + - title: Others + order: 999 + filters: + exclude: + - "^docs:" + - "^test:" + - "^ci:" + - "^chore:" + - "^refactor:" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba340d3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +BSD 3-Clause License + +Copyright 2024 Fredrik Randsborg Bølstad + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dded54c --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Masked Fastmail + +A simple CLI tool for managing [Fastmail masked email aliases](https://www.fastmail.com/features/masked-email/). +Easily create new aliases for websites or manage existing ones. + +## Features + +- Get or create masked email addresses for domains +- Aliases are automatically copied to clipboard +- Enable/disable/delete aliases + +## Usage + +![demo](./demo.gif) + +```text +Usage: + masked_fastmail (no flags) + manage_fastmail [flags] + +Flags: + --delete delete alias (bounce messages) + -d, --disable disable alias (send to trash) + -e, --enable enable alias + -h, --help show this message + -v, --version show version information +``` + +The following environment variables must be set: + +```shell +export FASTMAIL_ACCOUNT_ID=your_account_id +export FASTMAIL_API_KEY=your_api_key +``` + +### Examples + +#### Get or create alias + +A new alias will only be created if one does not already exist. +In either case, the alias is automatically copied to the clipboard.[^1] + +[^1]: Copying is done with [Clipboard for Go](https://pkg.go.dev/github.com/atotto/clipboard#section-readme) and should work on all platforms. + +```shell +masked_fastmail example.com +``` + +#### Enable an existing alias + +New Fastmail aliases are initialized to `pending`, and are set to `enabled` once they receive their first email. +However, they get automatically deleted if no email is received within 24 hours. +Some services may not send a timely welcome email, in which case it's helpful to manually enable the alias. + +```shell +masked_fastmail --enable user.1234@fastmail.com +``` + +#### Disable an alias + +This causes all new new emails to be moved to trash. + +```shell +masked_fastmail --disable user.1234@fastmail.com +``` + +## Installation + +### Option 1: Download a pre-built binary + +Download the latest release from the [releases page](https://github.com/fredrikrab/masked_fastmail/releases). + +### Option 2: Use `go install` + +```shell +go install github.com/fredrikrab/masked_fastmail@latest +``` + +### Option 3: Build from source + +1. Clone the repository +2. Run `go build -o masked_fastmail` + +#### Prerequisites + +- Go 1.22+ +- Fastmail API credentials + +#### API documentation + +- The API documentation can be found at [https://www.fastmail.com/dev/maskedemail](https://www.fastmail.com/dev/maskedemail) +- It's also helpful to review the [JMAP protocol](https://jmap.io/crash-course.html) + +## License + +BSD 3-Clause License diff --git a/client.go b/client.go new file mode 100644 index 0000000..9eed5b2 --- /dev/null +++ b/client.go @@ -0,0 +1,282 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" +) + +// JMAP API endpoints and methods +const ( + apiURL = "https://api.fastmail.com/jmap/api" + maskedEmailNamespace = "https://www.fastmail.com/dev/maskedemail" + methodGet = "MaskedEmail/get" + methodSet = "MaskedEmail/set" +) + +// ErrAliasNotFound is returned when an alias cannot be found +var ErrAliasNotFound = errors.New("alias not found") + +// ErrUpdateFailed is returned when an alias update fails +var ErrUpdateFailed = errors.New("failed to update alias") + +type FastmailClient struct { + AccountID string + Token string +} + +// getMaskedEmail performs a MaskedEmail/get request with the given properties +func (fc *FastmailClient) getMaskedEmail(properties []string) ([]MaskedEmailInfo, error) { + payload := fc.buildRequest(methodCall{ + name: methodGet, + arguments: struct { + AccountID string `json:"accountId"` + Properties []string `json:"properties"` + }{ + AccountID: fc.AccountID, + Properties: properties, + }, + clientID: nil, + }) + + response, err := fc.sendRequest(payload) + if err != nil { + return nil, err + } + + var responseData struct { + List []MaskedEmailInfo `json:"list"` + } + if err := json.Unmarshal(response.MethodResponses[0][1], &responseData); err != nil { + return nil, fmt.Errorf("failed to unmarshal response data: %w", err) + } + + return responseData.List, nil +} + +// setMaskedEmail performs a MaskedEmail/set request with the given updates or creates +func (fc *FastmailClient) setMaskedEmail(create, update map[string]interface{}) (*MaskedEmailResponse, error) { + args := struct { + Create map[string]interface{} `json:"create,omitempty"` + Update map[string]interface{} `json:"update,omitempty"` + AccountID string `json:"accountId"` + }{ + AccountID: fc.AccountID, + Create: create, + Update: update, + } + + payload := fc.buildRequest(methodCall{ + name: methodSet, + arguments: args, + clientID: nil, + }) + + return fc.sendRequest(payload) +} + +type MaskedEmailRequest struct { + Using []string `json:"using"` + MethodCalls [][]json.RawMessage `json:"methodCalls"` +} + +type MaskedEmailResponse struct { + MethodResponses [][]json.RawMessage `json:"methodResponses"` +} + +// AliasState represents the possible states of a masked email +type AliasState string + +const ( + AliasPending AliasState = "pending" + AliasEnabled AliasState = "enabled" + AliasDisabled AliasState = "disabled" + AliasDeleted AliasState = "deleted" +) + +type MaskedEmailInfo struct { + Email string `json:"email"` + ForDomain string `json:"forDomain"` + State AliasState `json:"state"` + ID string `json:"id"` +} + +// methodCall represents a JMAP method call +type methodCall struct { + arguments interface{} + clientID interface{} + name string +} + +func (fc *FastmailClient) buildRequest(calls ...methodCall) *MaskedEmailRequest { + methodCalls := make([][]json.RawMessage, len(calls)) + + for i, call := range calls { + name, _ := json.Marshal(call.name) + args, _ := json.Marshal(call.arguments) + clientID, _ := json.Marshal(call.clientID) + + methodCalls[i] = []json.RawMessage{name, args, clientID} + } + + return &MaskedEmailRequest{ + Using: []string{"https://www.fastmail.com/dev/maskedemail"}, + MethodCalls: methodCalls, + } +} + +// NewFastmailClient creates a new client for interacting with the Fastmail API. +// It requires FASTMAIL_ACCOUNT_ID and FASTMAIL_API_KEY environment variables to be set. +func NewFastmailClient() (*FastmailClient, error) { + accountID := os.Getenv("FASTMAIL_ACCOUNT_ID") + token := os.Getenv("FASTMAIL_API_KEY") + + if accountID == "" { + return nil, errors.New("FASTMAIL_ACCOUNT_ID environment variable must be set") + } + if token == "" { + return nil, errors.New("FASTMAIL_API_KEY environment variable must be set") + } + + return &FastmailClient{ + AccountID: accountID, + Token: token, + }, nil +} + +func (fc *FastmailClient) sendRequest(payload *MaskedEmailRequest) (*MaskedEmailResponse, error) { + jsonPayload, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonPayload)) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", fc.Token)) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var result MaskedEmailResponse + err = json.Unmarshal(body, &result) + if err != nil { + return nil, err + } + + return &result, nil +} + +func (fc *FastmailClient) GetAliases(domain string) ([]MaskedEmailInfo, error) { + maskedEmails, err := fc.getMaskedEmail([]string{"email", "forDomain", "state"}) + if err != nil { + return nil, err + } + + var filteredAliases []MaskedEmailInfo + for _, alias := range maskedEmails { + if alias.ForDomain == domain && alias.State != AliasDeleted { + filteredAliases = append(filteredAliases, alias) + } + } + + return filteredAliases, nil +} + +func (fc *FastmailClient) CreateAlias(domain string) (*MaskedEmailInfo, error) { + create := map[string]interface{}{ + "MaskedEmail": map[string]string{ + "forDomain": domain, + "description": domain, + }, + } + + response, err := fc.setMaskedEmail(create, nil) + if err != nil { + return nil, err + } + + var createdAlias struct { + Created struct { + MaskedEmail MaskedEmailInfo `json:"MaskedEmail"` + } `json:"created"` + } + + err = json.Unmarshal(response.MethodResponses[0][1], &createdAlias) + if err != nil { + return nil, err + } + + return &createdAlias.Created.MaskedEmail, nil +} + +// GetAliasByEmail returns a specific alias by its email address +// GetAliasByEmail retrieves a specific alias by its email address. +// Returns ErrAliasNotFound if the alias doesn't exist. +func (fc *FastmailClient) GetAliasByEmail(email string) (*MaskedEmailInfo, error) { + aliases, err := fc.getMaskedEmail([]string{"email", "state", "forDomain", "id"}) + if err != nil { + return nil, fmt.Errorf("failed to get aliases: %w", err) + } + + for _, alias := range aliases { + if alias.Email == email { + return &alias, nil + } + } + + return nil, fmt.Errorf("%w: %s", ErrAliasNotFound, email) +} + +// UpdateAliasStatus changes the state of an existing alias. +// Returns an error if the alias is already in the requested state or if the update fails. +func (fc *FastmailClient) UpdateAliasStatus(alias *MaskedEmailInfo, state AliasState) error { + // Print current state for user feedback + fmt.Printf("Setting '%s' for '%s' to '%s'\n", alias.Email, alias.ForDomain, state) + + if state == alias.State { + return fmt.Errorf("'%s' is already '%s'", alias.Email, state) + } + + update := map[string]interface{}{ + alias.ID: map[string]string{ + "state": string(state), + }, + } + + response, err := fc.setMaskedEmail(nil, update) + if err != nil { + return fmt.Errorf("failed to update alias: %w", err) + } + + // Verify the update was successful + var updateResponse struct { + Updated map[string]interface{} `json:"updated"` + } + if err := json.Unmarshal(response.MethodResponses[0][1], &updateResponse); err != nil { + return fmt.Errorf("failed to parse response: %w", err) + } + + if _, ok := updateResponse.Updated[alias.ID]; !ok { + return fmt.Errorf("server did not confirm the update") + } + + fmt.Println("Success") + return nil +} diff --git a/demo.gif b/demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..4a71fdb14d05cc414957d28686bf57d46a54e3fc GIT binary patch literal 61998 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 kHTwLTgnv!KzsB8P (no flags) + manage_fastmail `, + Short: "Manage masked email aliases", + Long: `A command-line tool to manage Fastmail.com masked email addresses. +Requires FASTMAIL_ACCOUNT_ID and FASTMAIL_API_KEY environment variables to be set.`, + Example: ` # Create or get alias for a website: + masked_fastmail example.com + + # Enable an existing alias: + masked_fastmail --enable user.1234@fastmail.com`, + + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cmd *cobra.Command, args []string) error { + showVersion, _ := cmd.Flags().GetBool("version") + if showVersion { + fmt.Printf("Version:\t%s\nCommit:\t\t%s\nBuild date:\t%s\n", version, commit, date) + return nil + } + return runMaskedFastmail(cmd, args) + }, + } + + rootCmd.Flags().BoolP("version", "v", false, "show version information") + rootCmd.Flags().BoolP("enable", "e", false, "enable alias") + rootCmd.Flags().BoolP("disable", "d", false, "disable alias (send to trash)") + rootCmd.Flags().Bool("delete", false, "delete alias (bounce messages)") + + // Make flags mutually exclusive + rootCmd.MarkFlagsMutuallyExclusive("enable", "disable", "delete") + + // Add completion support + rootCmd.CompletionOptions.DisableDefaultCmd = true + + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(1) + } +} + +// selectPreferredAlias selects the best alias based on state priority +// Priority order: enabled > pending > disabled > deleted +// Returns nil if the input slice is empty. +func selectPreferredAlias(aliases []MaskedEmailInfo) *MaskedEmailInfo { + if len(aliases) == 0 { + return nil + } + + // Validate all states are recognized + for _, alias := range aliases { + if _, ok := statePriority[alias.State]; !ok { + // Log warning but continue with known states + fmt.Fprintf(os.Stderr, "Warning: unknown alias state: %s\n", alias.State) + } + } + + selected := &aliases[0] + selectedPriority := statePriority[selected.State] + + for i := 1; i < len(aliases); i++ { + priority := statePriority[aliases[i].State] + if priority < selectedPriority { + selected = &aliases[i] + selectedPriority = priority + } + } + + return selected +} + +// runMaskedFastmail is the main command handler for the CLI application. +// It handles both alias creation/lookup and state management operations. +func runMaskedFastmail(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("exactly one URL or alias must be specified\n\n%s", cmd.UsageString()) + } + + // Note: We can remove this validation since we're using cobra's MarkFlagsMutuallyExclusive + + client, err := NewFastmailClient() + if err != nil { + return fmt.Errorf("failed to initialize client: %w", err) + } + + identifier := args[0] + + // Check for state update flags + enable, _ := cmd.Flags().GetBool("enable") + disable, _ := cmd.Flags().GetBool("disable") + delete, _ := cmd.Flags().GetBool("delete") + + if enable || disable || delete { + return handleStateUpdate(client, identifier, enable, disable, delete) + } + return handleAliasCreation(client, identifier) +} + +// handleStateUpdate manages the state changes of existing aliases +func handleStateUpdate(client *FastmailClient, identifier string, enable, disable, delete bool) error { + var newState AliasState + switch { + case enable: + newState = AliasEnabled + case disable: + newState = AliasDisabled + case delete: + newState = AliasDeleted + } + + // Get current state + targetAlias, err := client.GetAliasByEmail(identifier) + if err != nil { + return fmt.Errorf("failed to get alias: %w", err) + } + + err = client.UpdateAliasStatus(targetAlias, newState) + if err != nil { + return fmt.Errorf("failed to update alias status: %w", err) + } + return nil +} + +// Handle get/create alias +func handleAliasCreation(client *FastmailClient, identifier string) error { + aliases, err := client.GetAliases(identifier) + if err != nil { + return fmt.Errorf("failed to get aliases: %w", err) + } + selectedAlias := selectPreferredAlias(aliases) + + if selectedAlias == nil { + // Create new alias + fmt.Printf("No aliases found for %s, creating new one...\n", identifier) + newAlias, err := client.CreateAlias(identifier) + if err != nil { + return fmt.Errorf("failed to create alias: %w", err) + } + selectedAlias = newAlias + } else if len(aliases) > 1 { + fmt.Printf("Found %d aliases for %s:\n", len(aliases), identifier) + for _, alias := range aliases { + fmt.Printf("- %s (state: %s)\n", alias.Email, alias.State) + } + fmt.Println("\nSelected alias:") + } + + fmt.Printf("%s (state: %s)", selectedAlias.Email, selectedAlias.State) + if err := copyToClipboard(selectedAlias.Email); err != nil { + fmt.Fprintf(os.Stderr, "\nWarning: Could not copy to clipboard: %v\n", err) + } else { + fmt.Println(" (copied to clipboard)") + } + return nil +} + +// copyToClipboard attempts to copy the given text to the system clipboard +func copyToClipboard(text string) error { + if err := clipboard.WriteAll(text); err != nil { + return fmt.Errorf("clipboard operation failed: %w", err) + } + return nil +}