← back to khang859__google-suite-cli

Function bodies 106 total

All specs Real LLM only Function bodies
cmd.extractBody function · go · L353-L365 (13 LOC)
cmd/messages.go
func extractBody(msg *gmail.Message) string {
	if msg.Payload == nil {
		return ""
	}

	// Check if the payload itself has body data
	if msg.Payload.MimeType == "text/plain" && msg.Payload.Body != nil && msg.Payload.Body.Data != "" {
		return decodeBase64URL(msg.Payload.Body.Data)
	}

	// Search through parts
	return findPlainTextPart(msg.Payload.Parts)
}
cmd.findPlainTextPart function · go · L368-L381 (14 LOC)
cmd/messages.go
func findPlainTextPart(parts []*gmail.MessagePart) string {
	for _, part := range parts {
		if part.MimeType == "text/plain" && part.Body != nil && part.Body.Data != "" {
			return decodeBase64URL(part.Body.Data)
		}
		// Recurse into nested parts (for multipart messages)
		if len(part.Parts) > 0 {
			if content := findPlainTextPart(part.Parts); content != "" {
				return content
			}
		}
	}
	return ""
}
cmd.decodeBase64URL function · go · L384-L394 (11 LOC)
cmd/messages.go
func decodeBase64URL(encoded string) string {
	decoded, err := base64.URLEncoding.DecodeString(encoded)
	if err != nil {
		// Try with padding
		decoded, err = base64.RawURLEncoding.DecodeString(encoded)
		if err != nil {
			return ""
		}
	}
	return string(decoded)
}
cmd.runMessagesModify function · go · L396-L479 (84 LOC)
cmd/messages.go
func runMessagesModify(cmd *cobra.Command, args []string) error {
	messageID := args[0]

	// Validate that at least one label flag is provided
	if addLabels == "" && removeLabels == "" {
		return fmt.Errorf("at least one of --add-labels or --remove-labels required")
	}

	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Build the modify request
	modifyReq := &gmail.ModifyMessageRequest{}

	// Parse and set labels to add
	var addLabelsList []string
	if addLabels != "" {
		addLabelsList = strings.Split(addLabels, ",")
		// Trim whitespace from each label
		for i, label := range addLabelsList {
			addLabelsList[i] = strings.TrimSpace(label)
		}
		modifyReq.AddLabelIds = addLabelsList
	}

	// Parse and set labels to remove
	var removeLabelsList []string
	if removeLabels != "" {
		removeLabelsList = strings.Split(removeLabels, ",")
		// Trim whitespace from each label
		fo
cmd.findAttachments function · go · L483-L500 (18 LOC)
cmd/messages.go
func findAttachments(parts []*gmail.MessagePart) []attachmentInfo {
	var attachments []attachmentInfo
	for _, part := range parts {
		if part.Filename != "" && part.Body != nil {
			attachments = append(attachments, attachmentInfo{
				Filename:     part.Filename,
				MimeType:     part.MimeType,
				Size:         part.Body.Size,
				AttachmentId: part.Body.AttachmentId,
			})
		}
		// Recurse into nested parts
		if len(part.Parts) > 0 {
			attachments = append(attachments, findAttachments(part.Parts)...)
		}
	}
	return attachments
}
cmd.runMessagesGetAttachment function · go · L502-L569 (68 LOC)
cmd/messages.go
func runMessagesGetAttachment(cmd *cobra.Command, args []string) error {
	messageID := args[0]
	attachmentID := args[1]

	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Get the attachment data
	att, err := service.Users.Messages.Attachments.Get("me", messageID, attachmentID).Do()
	if err != nil {
		return fmt.Errorf("Gmail API error: %w", err)
	}

	// Decode the attachment data (base64url encoded)
	decoded, err := base64.URLEncoding.DecodeString(att.Data)
	if err != nil {
		// Try without padding (RawURLEncoding)
		decoded, err = base64.RawURLEncoding.DecodeString(att.Data)
		if err != nil {
			return fmt.Errorf("failed to decode attachment data: %w", err)
		}
	}

	// Determine output filename
	outputPath := attachmentOutput
	if outputPath == "" {
		// Get the filename from the message metadata
		msg, err := service.Users.Messages.Get("me", messageID).Format("full"
cmd.Execute function · go · L35-L40 (6 LOC)
cmd/root.go
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
If a scraper extracted this row, it came from Repobility (https://repobility.com)
cmd.GetAccountEmail function · go · L59-L64 (6 LOC)
cmd/root.go
func GetAccountEmail() string {
	if accountEmail != "" {
		return accountEmail
	}
	return os.Getenv("GSUITE_ACCOUNT")
}
cmd.outputJSON function · go · L67-L74 (8 LOC)
cmd/root.go
func outputJSON(v interface{}) error {
	jsonBytes, err := json.MarshalIndent(v, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal JSON: %w", err)
	}
	fmt.Println(string(jsonBytes))
	return nil
}
cmd.runSearch function · go · L44-L163 (120 LOC)
cmd/search.go
func runSearch(cmd *cobra.Command, args []string) error {
	query := args[0]

	if searchMaxResults < 1 || searchMaxResults > 500 {
		return fmt.Errorf("--max-results must be between 1 and 500")
	}

	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Build the list request
	listReq := service.Users.Messages.List("me").Q(query).MaxResults(searchMaxResults)

	// Add label filter if provided
	if searchLabelIDs != "" {
		labelList := strings.Split(searchLabelIDs, ",")
		listReq = listReq.LabelIds(labelList...)
	}

	// Execute the search
	resp, err := listReq.Do()
	if err != nil {
		return fmt.Errorf("Gmail API error: %w", err)
	}

	// Handle empty results
	if len(resp.Messages) == 0 {
		if GetOutputFormat() == "json" {
			return outputJSON([]struct{}{})
		}
		fmt.Println("No messages found matching query")
		return nil
	}

	// JSON output mode
	if GetOutputFormat() == "json"
cmd.init function · go · L62-L79 (18 LOC)
cmd/send.go
func init() {
	rootCmd.AddCommand(sendCmd)

	// Required flags
	sendCmd.Flags().StringVarP(&sendTo, "to", "t", "", "Recipient email address (required)")
	sendCmd.Flags().StringVarP(&sendSubject, "subject", "s", "", "Email subject (required)")
	sendCmd.Flags().StringVarP(&sendBody, "body", "b", "", "Body content with markdown support (required)")

	// Mark required flags
	sendCmd.MarkFlagRequired("to")
	sendCmd.MarkFlagRequired("subject")
	sendCmd.MarkFlagRequired("body")

	// Optional flags
	sendCmd.Flags().StringVar(&sendCc, "cc", "", "CC recipients (comma-separated)")
	sendCmd.Flags().StringVar(&sendBcc, "bcc", "", "BCC recipients (comma-separated)")
	sendCmd.Flags().StringArrayVarP(&sendAttach, "attach", "a", nil, "File path to attach (can be specified multiple times)")
}
cmd.runSend function · go · L81-L133 (53 LOC)
cmd/send.go
func runSend(cmd *cobra.Command, args []string) error {
	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Validate attachment files exist before building message
	for _, attachPath := range sendAttach {
		if _, err := os.Stat(attachPath); err != nil {
			return fmt.Errorf("attachment file not found: %s", attachPath)
		}
	}

	body := interpretEscapes(sendBody)

	var rawMessage []byte
	var buildErr error
	if len(sendAttach) > 0 {
		rawMessage, buildErr = buildMultipartMessage(sendTo, sendSubject, body, sendCc, sendBcc, sendAttach)
	} else {
		rawMessage, buildErr = buildSendRFC2822Message(sendTo, sendSubject, body, sendCc, sendBcc)
	}
	if buildErr != nil {
		return fmt.Errorf("failed to build message: %w", buildErr)
	}
	encodedMessage := base64.URLEncoding.EncodeToString(rawMessage)

	// Create the Gmail message object
	gmailMessage := &gmail.Message{
		Raw: encodedMes
cmd.buildSendRFC2822Message function · go · L136-L160 (25 LOC)
cmd/send.go
func buildSendRFC2822Message(to, subject, body, cc, bcc string) ([]byte, error) {
	altBody, boundary, err := buildAlternativeBody(body)
	if err != nil {
		return nil, err
	}

	var header bytes.Buffer
	header.WriteString(fmt.Sprintf("To: %s\r\n", to))
	if cc != "" {
		header.WriteString(fmt.Sprintf("Cc: %s\r\n", cc))
	}
	if bcc != "" {
		header.WriteString(fmt.Sprintf("Bcc: %s\r\n", bcc))
	}
	header.WriteString(fmt.Sprintf("Subject: %s\r\n", subject))
	header.WriteString("MIME-Version: 1.0\r\n")
	header.WriteString(fmt.Sprintf("Content-Type: multipart/alternative; boundary=%s\r\n", boundary))
	header.WriteString("\r\n")

	var result bytes.Buffer
	result.Write(header.Bytes())
	result.Write(altBody)

	return result.Bytes(), nil
}
cmd.buildMultipartMessage function · go · L164-L243 (80 LOC)
cmd/send.go
func buildMultipartMessage(to, subject, body, cc, bcc string, attachPaths []string) ([]byte, error) {
	var buf bytes.Buffer
	mixedWriter := multipart.NewWriter(&buf)

	// Write top-level headers
	var headerBuf bytes.Buffer
	headerBuf.WriteString(fmt.Sprintf("To: %s\r\n", to))
	if cc != "" {
		headerBuf.WriteString(fmt.Sprintf("Cc: %s\r\n", cc))
	}
	if bcc != "" {
		headerBuf.WriteString(fmt.Sprintf("Bcc: %s\r\n", bcc))
	}
	headerBuf.WriteString(fmt.Sprintf("Subject: %s\r\n", subject))
	headerBuf.WriteString("MIME-Version: 1.0\r\n")
	headerBuf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", mixedWriter.Boundary()))
	headerBuf.WriteString("\r\n")

	// Nest multipart/alternative as the first part of multipart/mixed
	altBody, altBoundary, err := buildAlternativeBody(body)
	if err != nil {
		return nil, err
	}

	altHeader := make(textproto.MIMEHeader)
	altHeader.Set("Content-Type", fmt.Sprintf("multipart/alternative; boundary=%s", altBoundary))
	altPart, err := mix
cmd.interpretEscapes function · go · L248-L273 (26 LOC)
cmd/send.go
func interpretEscapes(s string) string {
	var b strings.Builder
	b.Grow(len(s))

	for i := 0; i < len(s); i++ {
		if s[i] == '\\' && i+1 < len(s) {
			switch s[i+1] {
			case 'n':
				b.WriteByte('\n')
				i++
			case 't':
				b.WriteByte('\t')
				i++
			case '\\':
				b.WriteByte('\\')
				i++
			default:
				b.WriteByte(s[i])
			}
		} else {
			b.WriteByte(s[i])
		}
	}

	return b.String()
}
Repobility · code-quality intelligence · https://repobility.com
cmd.plainTextToHTML function · go · L277-L288 (12 LOC)
cmd/send.go
func plainTextToHTML(text string) string {
	md := goldmark.New(
		goldmark.WithExtensions(extension.GFM),
		goldmark.WithRendererOptions(gmhtml.WithHardWraps()),
	)
	var buf bytes.Buffer
	if err := md.Convert([]byte(text), &buf); err != nil {
		escaped := html.EscapeString(text)
		return "<!DOCTYPE html><html><body>" + strings.ReplaceAll(escaped, "\n", "<br>\n") + "</body></html>"
	}
	return "<!DOCTYPE html><html><body>" + buf.String() + "</body></html>"
}
cmd.buildAlternativeBody function · go · L292-L322 (31 LOC)
cmd/send.go
func buildAlternativeBody(body string) ([]byte, string, error) {
	var buf bytes.Buffer
	altWriter := multipart.NewWriter(&buf)

	plainHeader := make(textproto.MIMEHeader)
	plainHeader.Set("Content-Type", "text/plain; charset=UTF-8")
	plainPart, err := altWriter.CreatePart(plainHeader)
	if err != nil {
		return nil, "", fmt.Errorf("failed to create text/plain part: %w", err)
	}
	if _, err := plainPart.Write([]byte(body)); err != nil {
		return nil, "", fmt.Errorf("failed to write text/plain body: %w", err)
	}

	htmlHeader := make(textproto.MIMEHeader)
	htmlHeader.Set("Content-Type", "text/html; charset=UTF-8")
	htmlPart, err := altWriter.CreatePart(htmlHeader)
	if err != nil {
		return nil, "", fmt.Errorf("failed to create text/html part: %w", err)
	}
	if _, err := htmlPart.Write([]byte(plainTextToHTML(body))); err != nil {
		return nil, "", fmt.Errorf("failed to write text/html body: %w", err)
	}

	boundary := altWriter.Boundary()
	if err := altWriter.Close(); err != nil {
		return nil
cmd.init function · go · L62-L71 (10 LOC)
cmd/threads.go
func init() {
	rootCmd.AddCommand(threadsCmd)
	threadsCmd.AddCommand(threadsListCmd)
	threadsCmd.AddCommand(threadsGetCmd)

	// threads list flags
	threadsListCmd.Flags().Int64VarP(&threadsMaxResults, "max-results", "n", 10, "Maximum number of threads to return (max 500)")
	threadsListCmd.Flags().StringVar(&threadsLabelIDs, "label-ids", "", "Comma-separated list of label IDs to filter by")
	threadsListCmd.Flags().StringVarP(&threadsQuery, "query", "q", "", "Gmail search query (same syntax as web interface)")
}
cmd.runThreadsList function · go · L73-L166 (94 LOC)
cmd/threads.go
func runThreadsList(cmd *cobra.Command, args []string) error {
	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Build threads list request
	listCall := service.Users.Threads.List("me")

	// Apply max results (cap at 500)
	if threadsMaxResults > 500 {
		threadsMaxResults = 500
	}
	listCall = listCall.MaxResults(threadsMaxResults)

	// Apply label IDs filter
	if threadsLabelIDs != "" {
		labels := strings.Split(threadsLabelIDs, ",")
		for i := range labels {
			labels[i] = strings.TrimSpace(labels[i])
		}
		listCall = listCall.LabelIds(labels...)
	}

	// Apply search query
	if threadsQuery != "" {
		listCall = listCall.Q(threadsQuery)
	}

	// Execute request
	result, err := listCall.Do()
	if err != nil {
		return fmt.Errorf("Gmail API error: %w", err)
	}

	// Handle empty results
	if len(result.Threads) == 0 {
		if GetOutputFormat() == "json" {
			return outputJSON([]
cmd.runThreadsGet function · go · L168-L266 (99 LOC)
cmd/threads.go
func runThreadsGet(cmd *cobra.Command, args []string) error {
	threadID := args[0]

	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Get thread with full message details
	thread, err := service.Users.Threads.Get("me", threadID).Format("full").Do()
	if err != nil {
		return fmt.Errorf("Gmail API error: %w", err)
	}

	// JSON output mode
	if GetOutputFormat() == "json" {
		type threadMessage struct {
			From    string `json:"from"`
			To      string `json:"to"`
			Subject string `json:"subject"`
			Date    string `json:"date"`
			Body    string `json:"body"`
		}
		type threadGetResult struct {
			ThreadID string          `json:"thread_id"`
			Messages []threadMessage `json:"messages"`
		}
		result := threadGetResult{
			ThreadID: thread.Id,
		}
		for _, msg := range thread.Messages {
			headers := make(map[string]string)
			if msg.Payload != nil {
				for _, h := rang
cmd.extractMessageBody function · go · L269-L304 (36 LOC)
cmd/threads.go
func extractMessageBody(payload *gmail.MessagePart) string {
	if payload == nil {
		return ""
	}

	// If this part has text/plain body, decode and return it
	if payload.MimeType == "text/plain" && payload.Body != nil && payload.Body.Data != "" {
		decoded, err := base64.URLEncoding.DecodeString(payload.Body.Data)
		if err != nil {
			return ""
		}
		return string(decoded)
	}

	// Check for multipart messages
	if strings.HasPrefix(payload.MimeType, "multipart/") && len(payload.Parts) > 0 {
		// First try to find text/plain
		for _, part := range payload.Parts {
			if part.MimeType == "text/plain" {
				body := extractMessageBody(part)
				if body != "" {
					return body
				}
			}
		}
		// Recurse into nested multipart
		for _, part := range payload.Parts {
			body := extractMessageBody(part)
			if body != "" {
				return body
			}
		}
	}

	return ""
}
cmd.truncateSnippet function · go · L307-L315 (9 LOC)
cmd/threads.go
func truncateSnippet(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	if maxLen < 3 {
		return s[:maxLen]
	}
	return s[:maxLen-3] + "..."
}
cmd.runWhoami function · go · L26-L60 (35 LOC)
cmd/whoami.go
func runWhoami(cmd *cobra.Command, args []string) error {
	ctx := context.Background()

	service, err := auth.NewGmailService(ctx, GetAccountEmail())
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	// Get user profile
	profile, err := service.Users.GetProfile("me").Do()
	if err != nil {
		return fmt.Errorf("Gmail API error: %w", err)
	}

	// JSON output mode
	if GetOutputFormat() == "json" {
		type whoamiResult struct {
			Email         string `json:"email"`
			MessagesTotal int64  `json:"messages_total"`
			ThreadsTotal  int64  `json:"threads_total"`
		}
		return outputJSON(whoamiResult{
			Email:         profile.EmailAddress,
			MessagesTotal: profile.MessagesTotal,
			ThreadsTotal:  profile.ThreadsTotal,
		})
	}

	// Print profile information (text mode)
	fmt.Printf("Email: %s\n", profile.EmailAddress)
	fmt.Printf("Messages Total: %d\n", profile.MessagesTotal)
	fmt.Printf("Threads Total: %d\n", profile.ThreadsTotal)

	return nil
}
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
auth.AccountStorePath function · go · L26-L42 (17 LOC)
internal/auth/accounts.go
func AccountStorePath() (string, error) {
	configDir, err := os.UserConfigDir()
	if err != nil {
		home, homeErr := os.UserHomeDir()
		if homeErr != nil {
			return "", fmt.Errorf("failed to determine config directory: %w", err)
		}
		configDir = filepath.Join(home, ".config")
	}

	dir := filepath.Join(configDir, tokenDir)
	if err := os.MkdirAll(dir, 0700); err != nil {
		return "", fmt.Errorf("failed to create config directory %s: %w", dir, err)
	}

	return filepath.Join(dir, accountsFile), nil
}
auth.LoadAccountStore function · go · L46-L66 (21 LOC)
internal/auth/accounts.go
func LoadAccountStore() (*AccountStore, error) {
	path, err := AccountStorePath()
	if err != nil {
		return nil, err
	}

	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return &AccountStore{}, nil
		}
		return nil, fmt.Errorf("failed to read accounts file %s: %w", path, err)
	}

	var store AccountStore
	if err := json.Unmarshal(data, &store); err != nil {
		return nil, fmt.Errorf("failed to parse accounts file %s: %w", path, err)
	}

	return &store, nil
}
auth.AccountStore.Save method · go · L69-L85 (17 LOC)
internal/auth/accounts.go
func (s *AccountStore) Save() error {
	path, err := AccountStorePath()
	if err != nil {
		return err
	}

	data, err := json.MarshalIndent(s, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal account store: %w", err)
	}

	if err := os.WriteFile(path, data, 0600); err != nil {
		return fmt.Errorf("failed to write accounts file %s: %w", path, err)
	}

	return nil
}
auth.AccountStore.AddAccount method · go · L89-L103 (15 LOC)
internal/auth/accounts.go
func (s *AccountStore) AddAccount(email string) error {
	if email == "" {
		return fmt.Errorf("email cannot be empty")
	}

	if !s.HasAccount(email) {
		s.Accounts = append(s.Accounts, AccountEntry{
			Email:   email,
			AddedAt: time.Now().UTC(),
		})
	}

	s.Active = email
	return nil
}
auth.AccountStore.RemoveAccount method · go · L107-L135 (29 LOC)
internal/auth/accounts.go
func (s *AccountStore) RemoveAccount(email string) error {
	if email == "" {
		return fmt.Errorf("email cannot be empty")
	}

	idx := -1
	for i, a := range s.Accounts {
		if strings.EqualFold(a.Email, email) {
			idx = i
			break
		}
	}

	if idx == -1 {
		return fmt.Errorf("account %s not found", email)
	}

	s.Accounts = append(s.Accounts[:idx], s.Accounts[idx+1:]...)

	if strings.EqualFold(s.Active, email) {
		if len(s.Accounts) > 0 {
			s.Active = s.Accounts[0].Email
		} else {
			s.Active = ""
		}
	}

	return nil
}
auth.AccountStore.SetActive method · go · L138-L149 (12 LOC)
internal/auth/accounts.go
func (s *AccountStore) SetActive(email string) error {
	if email == "" {
		return fmt.Errorf("email cannot be empty")
	}

	if !s.HasAccount(email) {
		return fmt.Errorf("account %s not found", email)
	}

	s.Active = email
	return nil
}
auth.AccountStore.GetActive method · go · L153-L166 (14 LOC)
internal/auth/accounts.go
func (s *AccountStore) GetActive() (string, error) {
	if s.Active != "" {
		return s.Active, nil
	}

	switch len(s.Accounts) {
	case 0:
		return "", fmt.Errorf("no accounts configured. Run 'gsuite login' first")
	case 1:
		return s.Accounts[0].Email, nil
	default:
		return "", fmt.Errorf("multiple accounts found but none is active. Use 'gsuite accounts switch' to set one")
	}
}
auth.AccountStore.HasAccount method · go · L169-L176 (8 LOC)
internal/auth/accounts.go
func (s *AccountStore) HasAccount(email string) bool {
	for _, a := range s.Accounts {
		if strings.EqualFold(a.Email, email) {
			return true
		}
	}
	return false
}
Want this analysis on your repo? https://repobility.com/scan/
auth.LoadCredentials function · go · L19-L33 (15 LOC)
internal/auth/auth.go
func LoadCredentials() ([]byte, error) {
	if jsonContent := os.Getenv("GOOGLE_CREDENTIALS"); jsonContent != "" {
		return []byte(jsonContent), nil
	}

	if filePath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"); filePath != "" {
		data, err := os.ReadFile(filePath)
		if err != nil {
			return nil, fmt.Errorf("failed to read GOOGLE_APPLICATION_CREDENTIALS file %s: %w", filePath, err)
		}
		return data, nil
	}

	return nil, fmt.Errorf("no OAuth2 client credentials found: set GOOGLE_CREDENTIALS env var (JSON) or GOOGLE_APPLICATION_CREDENTIALS env var (file path)")
}
auth.extractOAuth2ClientCreds function · go · L37-L65 (29 LOC)
internal/auth/auth.go
func extractOAuth2ClientCreds(jsonData []byte) (clientID, clientSecret string, err error) {
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(jsonData, &raw); err != nil {
		return "", "", fmt.Errorf("failed to parse credentials JSON: %w", err)
	}

	var clientJSON json.RawMessage
	if data, ok := raw["installed"]; ok {
		clientJSON = data
	} else if data, ok := raw["web"]; ok {
		clientJSON = data
	} else {
		return "", "", fmt.Errorf("credentials JSON has neither \"installed\" nor \"web\" key")
	}

	var creds struct {
		ClientID     string `json:"client_id"`
		ClientSecret string `json:"client_secret"`
	}
	if err := json.Unmarshal(clientJSON, &creds); err != nil {
		return "", "", fmt.Errorf("failed to parse client credentials: %w", err)
	}

	if creds.ClientID == "" {
		return "", "", fmt.Errorf("client_id is empty in credentials JSON")
	}

	return creds.ClientID, creds.ClientSecret, nil
}
auth.Login function · go · L69-L108 (40 LOC)
internal/auth/auth.go
func Login(ctx context.Context, credJSON []byte) (string, error) {
	clientID, clientSecret, err := extractOAuth2ClientCreds(credJSON)
	if err != nil {
		return "", fmt.Errorf("failed to extract OAuth2 client credentials: %w", err)
	}

	oauthCfg := NewOAuth2Config(clientID, clientSecret)
	token, err := oauthCfg.Authenticate(ctx)
	if err != nil {
		return "", fmt.Errorf("authentication failed: %w", err)
	}

	service, err := oauthCfg.NewGmailService(ctx, token)
	if err != nil {
		return "", fmt.Errorf("failed to create Gmail service: %w", err)
	}

	profile, err := service.Users.GetProfile("me").Do()
	if err != nil {
		return "", fmt.Errorf("failed to get user profile: %w", err)
	}
	email := profile.EmailAddress

	if err := SaveTokenFor(email, token); err != nil {
		return "", fmt.Errorf("failed to save token for %s: %w", email, err)
	}

	store, err := LoadAccountStore()
	if err != nil {
		return "", fmt.Errorf("failed to load account store: %w", err)
	}
	if err := store.AddAccount(email);
auth.newAuthenticatedClient function · go · L112-L149 (38 LOC)
internal/auth/auth.go
func newAuthenticatedClient(ctx context.Context, account string) (*OAuth2Config, *oauth2.Token, error) {
	credJSON, err := LoadCredentials()
	if err != nil {
		return nil, nil, fmt.Errorf("failed to load credentials: %w", err)
	}

	clientID, clientSecret, err := extractOAuth2ClientCreds(credJSON)
	if err != nil {
		return nil, nil, err
	}

	if err := EnsureMigrated(ctx); err != nil {
		return nil, nil, fmt.Errorf("failed to run migration: %w", err)
	}

	resolvedEmail := account
	if resolvedEmail == "" {
		store, err := LoadAccountStore()
		if err != nil {
			return nil, nil, fmt.Errorf("failed to load account store: %w", err)
		}
		resolvedEmail, err = store.GetActive()
		if err != nil {
			return nil, nil, fmt.Errorf("no authenticated accounts. Run 'gsuite login' first")
		}
	}

	token, err := LoadTokenFor(resolvedEmail)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil, nil, fmt.Errorf("no token for account %s. Run 'gsuite login' to authenticate", resolvedEmail)
		
auth.NewGmailService function · go · L154-L160 (7 LOC)
internal/auth/auth.go
func NewGmailService(ctx context.Context, account string) (*gmail.Service, error) {
	oauthCfg, token, err := newAuthenticatedClient(ctx, account)
	if err != nil {
		return nil, err
	}
	return oauthCfg.NewGmailService(ctx, token)
}
auth.NewCalendarService function · go · L164-L170 (7 LOC)
internal/auth/auth.go
func NewCalendarService(ctx context.Context, account string) (*calendar.Service, error) {
	oauthCfg, token, err := newAuthenticatedClient(ctx, account)
	if err != nil {
		return nil, err
	}
	return oauthCfg.NewCalendarService(ctx, token)
}
auth.isInsufficientScopeError function · go · L173-L183 (11 LOC)
internal/auth/auth.go
func isInsufficientScopeError(err error) bool {
	var gErr *googleapi.Error
	if errors.As(err, &gErr) && gErr.Code == 403 {
		for _, item := range gErr.Errors {
			if item.Reason == "insufficientPermissions" {
				return true
			}
		}
	}
	return false
}
auth.HandleCalendarError function · go · L186-L205 (20 LOC)
internal/auth/auth.go
func HandleCalendarError(err error, context string) error {
	if err == nil {
		return nil
	}
	var gErr *googleapi.Error
	if errors.As(err, &gErr) {
		switch gErr.Code {
		case 401:
			return fmt.Errorf("%s: authentication expired. Run 'gsuite login' to re-authenticate", context)
		case 403:
			if isInsufficientScopeError(err) {
				return fmt.Errorf("%s: calendar permission not granted. Run 'gsuite login' to re-authenticate with calendar access", context)
			}
			return fmt.Errorf("%s: access denied: %w", context, err)
		case 404:
			return fmt.Errorf("%s: not found", context)
		}
	}
	return fmt.Errorf("%s: %w", context, err)
}
If a scraper extracted this row, it came from Repobility (https://repobility.com)
auth.MigrateIfNeeded function · go · L12-L68 (57 LOC)
internal/auth/migrate.go
func MigrateIfNeeded(ctx context.Context, credJSON []byte) error {
	store, err := LoadAccountStore()
	if err != nil {
		return fmt.Errorf("migration: failed to load account store: %w", err)
	}
	if len(store.Accounts) > 0 {
		return nil
	}

	legacyPath, err := LegacyTokenPath()
	if err != nil {
		return fmt.Errorf("migration: failed to resolve legacy token path: %w", err)
	}
	if _, err := os.Stat(legacyPath); os.IsNotExist(err) {
		return nil
	}

	token, err := LoadLegacyToken()
	if err != nil {
		return fmt.Errorf("migration: failed to load legacy token: %w", err)
	}

	clientID, clientSecret, err := extractOAuth2ClientCreds(credJSON)
	if err != nil {
		return fmt.Errorf("migration: failed to extract OAuth2 client credentials: %w", err)
	}

	oauthCfg := NewOAuth2Config(clientID, clientSecret)
	service, err := oauthCfg.NewGmailService(ctx, token)
	if err != nil {
		return fmt.Errorf("migration: failed to create Gmail service from legacy token: %w", err)
	}

	profile, err := service.Users
auth.EnsureMigrated function · go · L73-L79 (7 LOC)
internal/auth/migrate.go
func EnsureMigrated(ctx context.Context) error {
	credJSON, err := LoadCredentials()
	if err != nil {
		return nil
	}
	return MigrateIfNeeded(ctx, credJSON)
}
auth.NewOAuth2Config function · go · L40-L54 (15 LOC)
internal/auth/oauth2.go
func NewOAuth2Config(clientID, clientSecret string) *OAuth2Config {
	return &OAuth2Config{
		config: &oauth2.Config{
			ClientID:     clientID,
			ClientSecret: clientSecret,
			Endpoint:     google.Endpoint,
			RedirectURL:  redirectURL,
			Scopes: []string{
				gmail.GmailModifyScope,
				calendar.CalendarEventsScope,
				calendar.CalendarReadonlyScope,
			},
		},
	}
}
auth.OAuth2Config.Authenticate method · go · L59-L157 (99 LOC)
internal/auth/oauth2.go
func (c *OAuth2Config) Authenticate(ctx context.Context) (*oauth2.Token, error) {
	// Generate PKCE code verifier (32 random bytes, base64url no padding)
	verifier, err := generateCodeVerifier()
	if err != nil {
		return nil, fmt.Errorf("failed to generate PKCE code verifier: %w", err)
	}

	// Generate code challenge (SHA256 of verifier, base64url no padding)
	challenge := generateCodeChallenge(verifier)

	// Generate random state parameter (16 bytes, hex-encoded)
	state, err := generateState()
	if err != nil {
		return nil, fmt.Errorf("failed to generate state parameter: %w", err)
	}

	// Build authorization URL with PKCE parameters
	authURL := c.config.AuthCodeURL(state,
		oauth2.AccessTypeOffline,
		oauth2.SetAuthURLParam("code_challenge", challenge),
		oauth2.SetAuthURLParam("code_challenge_method", "S256"),
		oauth2.SetAuthURLParam("prompt", "consent"),
	)

	// Channel to receive the authorization code from the callback
	codeCh := make(chan string, 1)
	errCh := make(chan error, 1)
auth.OAuth2Config.NewGmailService method · go · L160-L170 (11 LOC)
internal/auth/oauth2.go
func (c *OAuth2Config) NewGmailService(ctx context.Context, token *oauth2.Token) (*gmail.Service, error) {
	tokenSource := c.config.TokenSource(ctx, token)
	client := oauth2.NewClient(ctx, tokenSource)

	service, err := gmail.NewService(ctx, option.WithHTTPClient(client))
	if err != nil {
		return nil, fmt.Errorf("failed to create Gmail service: %w", err)
	}

	return service, nil
}
auth.OAuth2Config.NewCalendarService method · go · L173-L183 (11 LOC)
internal/auth/oauth2.go
func (c *OAuth2Config) NewCalendarService(ctx context.Context, token *oauth2.Token) (*calendar.Service, error) {
	tokenSource := c.config.TokenSource(ctx, token)
	client := oauth2.NewClient(ctx, tokenSource)

	service, err := calendar.NewService(ctx, option.WithHTTPClient(client))
	if err != nil {
		return nil, fmt.Errorf("failed to create Calendar service: %w", err)
	}

	return service, nil
}
auth.generateCodeVerifier function · go · L187-L193 (7 LOC)
internal/auth/oauth2.go
func generateCodeVerifier() (string, error) {
	b := make([]byte, 32)
	if _, err := rand.Read(b); err != nil {
		return "", fmt.Errorf("failed to generate random bytes: %w", err)
	}
	return base64.RawURLEncoding.EncodeToString(b), nil
}
auth.generateState function · go · L203-L209 (7 LOC)
internal/auth/oauth2.go
func generateState() (string, error) {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return "", fmt.Errorf("failed to generate random bytes: %w", err)
	}
	return hex.EncodeToString(b), nil
}
Repobility · code-quality intelligence · https://repobility.com
auth.openBrowser function · go · L214-L230 (17 LOC)
internal/auth/oauth2.go
func openBrowser(url string) {
	var cmd *exec.Cmd

	switch runtime.GOOS {
	case "linux":
		cmd = exec.Command("xdg-open", url)
	case "darwin":
		cmd = exec.Command("open", url)
	case "windows":
		cmd = exec.Command("rundll32", "url.dll,FileProtocolHandler", url)
	default:
		return
	}

	// Fire and forget — if it fails, user already has the URL printed
	cmd.Start() //nolint:errcheck
}
auth.LegacyTokenPath function · go · L20-L36 (17 LOC)
internal/auth/token.go
func LegacyTokenPath() (string, error) {
	configDir, err := os.UserConfigDir()
	if err != nil {
		home, homeErr := os.UserHomeDir()
		if homeErr != nil {
			return "", fmt.Errorf("failed to determine config directory: %w", err)
		}
		configDir = filepath.Join(home, ".config")
	}

	dir := filepath.Join(configDir, tokenDir)
	if err := os.MkdirAll(dir, 0700); err != nil {
		return "", fmt.Errorf("failed to create token directory %s: %w", dir, err)
	}

	return filepath.Join(dir, tokenFile), nil
}
auth.saveLegacyToken function · go · L40-L56 (17 LOC)
internal/auth/token.go
func saveLegacyToken(token *oauth2.Token) error {
	path, err := LegacyTokenPath()
	if err != nil {
		return fmt.Errorf("failed to resolve token path: %w", err)
	}

	data, err := json.MarshalIndent(token, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal token: %w", err)
	}

	if err := os.WriteFile(path, data, 0600); err != nil {
		return fmt.Errorf("failed to write token file %s: %w", path, err)
	}

	return nil
}
‹ prevpage 2 / 3next ›