bug(gmail): parse_message_headers uses case-sensitive match, drops CC/headers with non-canonical casing

Open Beginner friendly
#642 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
rust
Domain
cli

Research direction

Start in crates/google-workspace-cli/src/helpers/gmail/mod.rs at parse_message_headers, then compare its matching with get_part_header and extract_header. Reproduce with gws gmail +reply-all using a message whose headers have non-canonical casing, and consider the work done when From, To, Cc, Reply-To, Message-ID, and References are handled case-insensitively without dropping recipients or producing missing-header errors.

Written by the indexing model from the issue text.

Description

Description

gws gmail +reply-all silently drops CC recipients when the original message's Cc header is stored with non-canonical casing (e.g., "CC" instead of "Cc").

Reproduction

  1. Have a Gmail thread where the latest message has CC recipients and the header name is "CC" (all uppercase — common with Microsoft Exchange / Outlook)
  2. Run:
    gws gmail +reply-all --message-id <ID> --body 'test' --draft
    
  3. Expected: Draft includes all original To + CC recipients
  4. Actual: Draft only includes To recipients + original sender. All CC recipients are silently dropped.

Root Cause

parse_message_headers in crates/google-workspace-cli/src/helpers/gmail/mod.rs (line 254) uses exact case-sensitive string matching:

match name {
    "From" => ...
    "Reply-To" => ...
    "To" => ...
    "Cc" => ...                                          // ← only matches "Cc"
    "Message-ID" | "Message-Id" => ...                   // ← already handles 2 variants
    _ => {}                                              // ← "CC" falls through silently
}

The Gmail API preserves original header casing from the sending MTA. Per RFC 5322 §1.2.2, header field names are case-insensitive, so "CC", "Cc", and "cc" are all valid.

Scope

This affects all headers in the match block, not just Cc. If any header arrives in non-canonical casing:

Header Impact if missed
From Hard error — function returns Err("Message is missing From header")
Message-ID Hard error — Err("Message is missing Message-ID header")
To +reply-all misses To recipients
CC +reply-all drops CC recipients (this bug)
Reply-To Reply goes to wrong recipient
References Threading breaks

Internal Inconsistency

The same file already uses case-insensitive matching in get_part_header:

fn get_part_header<'a>(part: &'a Value, name: &str) -> Option<&'a str> {
    ...
    .find(|h| n.eq_ignore_ascii_case(name))    // ← correct approach
    ...
}

Suggested Fix

Normalize name before matching:

match name.to_ascii_lowercase().as_str() {
    "from" => ...
    "reply-to" => ...
    "to" => ...
    "cc" => ...
    "subject" => ...
    "date" => ...
    "message-id" => ...
    "references" => ...
    _ => {}
}

This aligns with the existing get_part_header pattern and the extract_header function (which also uses eq_ignore_ascii_case).

Related

  • #569 — CC header ordering issue (different bug, fixed)
  • PR #105 — Original +reply-all implementation
  • PR #634 (open) — Widens parse_message_headers to pub(super) for reuse; this bug would propagate

Environment

  • gws 0.22.3
  • macOS (arm64)
  • Original message sent from Microsoft Exchange (which emits "CC" in uppercase)
Dominant language
Rust
Stars
31.1k
Forks
1.8k
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from googleworkspace/cli

All issues in googleworkspace/cli

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.