跳到主要內容
節省工具箱Jason Tools
經驗分享

Zimbra 以指令取得郵件原始內容

Jason Cheng 發布
Zimbra 以指令取得郵件原始內容
本文目錄
  1. 先找到郵件 ID
  2. zmmailbox gm:只有整理過的文字
  3. zmsoap GetMsgRequest raw=1
  4. 透過 content servlet 下載
  5. 最簡單的方式:zmmailbox getRestURL
  6. 方法比較
  7. 注意事項
  8. 結論
  9. 參考資料

處理郵件問題時,常常需要拿到某封信的原始內容(完整的表頭、MIME 結構、附件),例如追查寄件來源、檢查 SPF/DKIM 結果,或是要把信件交給資安單位分析。

在 Zimbra 上用指令撈信件,第一個想到的通常是 zmmailbox 或 zmsoap,但實際用起來會遇到兩個狀況:zmmailbox 只顯示整理過的文字,而 zmsoap 的 GetMsgRequest 有時候會回傳內容、有時候只回傳一個網址。這篇整理取得郵件原始內容的幾種方法。

先找到郵件 ID

所有方法都需要郵件在信箱裡的 ID。如果知道 Message-ID,可以用 zmsoap 搜尋:

su - zimbra
zmsoap -z -m user@example.com -t mail SearchRequest \
    @types=message \
    @query='field[Message-ID]:"<1234567890.12.1762871492739.JavaMail.zimbra@example.com>"'
  • -z:以 Zimbra 管理者身分驗證(使用本機的 zimbra 帳號設定),不用輸入密碼
  • -m:要操作的信箱
  • -t mail:使用 mail 命名空間的 SOAP 請求

回應裡的 <m ... id="852474"> 就是郵件 ID:

<SearchResponse offset="0" more="0" sortBy="dateDesc" xmlns="urn:zimbraMail">
  <m s="2206" d="1762871492000" id="852474" l="5" cid="852587">
    <e p="User" a="user@example.com" t="f"/>
    <su>測試郵件</su>
  </m>
</SearchResponse>

也可以用一般的搜尋語法,例如依主旨、寄件者與日期:

zmmailbox -z -m user@example.com search -t message 'subject:"測試郵件" from:sender@example.com after:11/1/2025'

zmmailbox gm:只有整理過的文字

zmmailbox -z -m user@example.com gm 852261
Id: 852261
Folder: /Sent
Subject: Re: 測試郵件
From: User <user@example.com>
To: Partner <partner@example.org>
Date: Tue, 11 Nov 2025 09:05:42 +0800 (CST)
Flags: FROM_ME, ATTACHED
Size: 673.49 KB

(郵件內文)

gm(getMessage)顯示的是解析過的結果:主要表頭與純文字內文,看不到完整的表頭(Received、Authentication-Results 等),也沒有附件內容,拿來快速看信可以,要分析就不夠了。

zmsoap GetMsgRequest raw=1

zmsoap -z -m user@example.com -t mail GetMsgRequest m @id=852261 @raw=1

小封的信,回應裡會直接包含原始內容。但大一點的信會變成這樣:

<GetMsgResponse xmlns="urn:zimbraMail">
  <m s="689650" d="1762823142000" f="sa" id="852261" l="5" cid="764333">
    <content url="/service/content/get?id=852261"/>
  </m>
</GetMsgResponse>

這不是錯誤。Zimbra SOAP API 文件說明,raw=1 時如果郵件太大或內容不是純 ASCII,就改回傳 content servlet 的網址,要另外用這個網址去下載。上面這封是 673 KB、帶附件的信,就走了網址的方式;中文郵件即使不大也可能如此。

GetMsgRequest 也有一個 useContentUrl=1 參數,可以強制一律回傳網址,寫腳本時行為比較一致。

透過 content servlet 下載

拿到網址之後,要帶著該信箱使用者的驗證權杖去下載。

取得管理者權杖

ADMIN_TOKEN=$(zmsoap -a admin@example.com -p 'your-admin-password' AuthRequest \
    | sed -n 's:.*<authToken>\(.*\)</authToken>.*:\1:p')

注意:密碼直接寫在指令裡會留在 Shell 的歷史紀錄中,執行完記得清掉,或改成執行時才輸入。用完的權杖也不要外流,它在有效期間內等同管理者身分。

以管理者身分代理成該使用者

用 DelegateAuthRequest 取得目標信箱的使用者權杖,送到管理通訊埠 7071:

USER_TOKEN=$(curl -s -k \
  -H "Content-Type: application/soap+xml" \
  -d '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
        <soap:Header><context xmlns="urn:zimbra"><authToken>'"$ADMIN_TOKEN"'</authToken></context></soap:Header>
        <soap:Body><DelegateAuthRequest xmlns="urn:zimbraAdmin"><account by="name">user@example.com</account></DelegateAuthRequest></soap:Body>
      </soap:Envelope>' \
  https://localhost:7071/service/admin/soap/ \
  | sed -n 's:.*<authToken>\(.*\)</authToken>.*:\1:p')

下載原始郵件

curl -s -k -H "Cookie: ZM_AUTH_TOKEN=$USER_TOKEN" \
  "https://mail.example.com/service/content/get?id=852261&fmt=raw" > 852261.eml

網頁信箱的通訊埠如果改過(不是 443),網址要跟著改。下載下來的 .eml 就是完整的原始郵件,可以直接用郵件軟體開啟,或用文字編輯器看表頭。

最簡單的方式:zmmailbox getRestURL

其實不用自己處理權杖,zmmailbox 的 getRestURL(縮寫 gru)可以直接透過 Zimbra 的 REST 介面取得原始郵件:

zmmailbox -z -m user@example.com gru "//?id=852261" > 852261.eml

-z 會以管理者身分代理該信箱,大小、編碼都不用管。只是要撈一封信看原始內容的話,我現在都用這個方式;需要寫進腳本、和其他 SOAP 請求整合時,再用前面的 content servlet 做法。

方法比較

方法取得內容適合
zmmailbox gm解析過的表頭與文字快速看信
zmsoap GetMsgRequest raw=1小封信的原始內容,大封信只有網址寫腳本時判斷用
content servlet + 權杖完整原始內容腳本整合、遠端呼叫
zmmailbox gru完整原始內容手動取信最方便

注意事項

  • 隱私:能以管理者身分讀取任何人的信件,等於握有全公司的郵件內容。調閱信件前要有正當理由與授權程序,並留下紀錄
  • 權杖:ADMIN_TOKEN、USER_TOKEN 都等同登入狀態,不要寫進檔案或貼到工單裡
  • 憑證:範例裡的 curl -k 是略過憑證檢查,只適合在主機本機或測試環境使用

結論

GetMsgRequest 回傳網址不是壞掉,是 Zimbra 對大郵件與非 ASCII 內容的設計。要拿原始郵件,手動用 zmmailbox gru 最簡單,寫腳本則用 content servlet 搭配代理權杖。

參考資料

Jason Cheng

Jason Cheng

企業開源技術顧問,專注架構規劃與實務導入

關於作者 →

延伸閱讀