CLDP
Chinese Linux Documentation Project

首頁(H)HOWTO(T)指南(G)FAQ(F)手冊頁(M)Linux電子報(L)LinuxFocus(S)


如何用 e-mail 傳送百科全書
Graham Jenkins /曾慧涵 譯

Linux電子報首頁目錄FAQ
讓 Linux 更加有趣呦!

目錄

  1. 在什麼情況下會需要 email 一份百科全書?
  2. 如何包裝百科全書?
  3. 另一種包裝百科全書的方法
  4. 百科全書的包裝工具
  5. 類似功能的軟體
  6. 作者介紹

1. 在什麼情況下會需要 email 一份百科全書?

好吧, 也許你不是要寄百科全書, 而是一部電影, 或是一個壓縮過的巨大目錄. 不用說, 你會用 FTP 來上傳它, 除非你的電腦只連接到一個內部 LAN 的網域, 而沒法用 FTP 連接到外界. 或者有可能你的收件者的電腦因為安全因素無法使用 FTP. 在這種情況下, 你可以將你要傳的東西編碼成一串 ASCII 字元, 用 e-mail 來傳送. 你可以用 'uuencode' 軟體, 或是 RFC 2045 "Multipurpose Internet Mail Extensions (MIME) Part One" 中敘述的 Base64 Content Transfer Encoding 來進行編碼.

2. 如何包裝百科全書?

在現實世界裡, 你可以將直接將一套百科全書弄成一個包裹寄出 - 只要送件員能夠接受那份包裹的大小及重量, 這就不失為一個好方法. 但有時候, 你必須將百科全書拆裝成好幾份較能讓人接受的大小重量.

同理可證, 在 email 一份百科全書時, 必須確定 e-mail 的訊息內容大小不會超出在傳送過程中經過的任何檢驗. 必要時, 必需將訊息拆成適當的大小, 其方法請參考 RFC 2046 "Multipurpose Internet Mail Extensions (MIME) Part Two".

總而言之, 我們可以順著 RFC 2045 跟 RFC 2046 的指示, 將整套百科全書做 Base64 的編碼並拆成許多部份. 我們要寄出的內容有一部份會看起來像這樣:

  From grahjenk@au1.ibm.com Tue Dec 31 13:14:34 2002
  Content-Disposition: inline
  Content-Transfer-Encoding: 7bit
  Content-Type: message/partial; id="300870"; number="1"
  Subject: Graham's Encylopedia
  
  owF1Vb+P3EQUPhLRrBSFlHQjRYCQsthe/1q7CNrbREjocnvK3hHREM3ac7fWeWfM
  zPh2L38ASomEIro0SNBBg2hBSDTwR0BBEwqQaFJF8J499tob0EjW7rzv+96b772x
   ...
  szJb9DUMvKdRUIV+RY5Xu3UkRQqvJCzdzHtHoQL36Ke6elnYLgwH8MfxCU9ymq1Y
  
  --
  From grahjenk@au1.ibm.com Tue Dec 31 13:14:34 2002
  Content-Disposition: inline
  Content-Transfer-Encoding: 7bit
  Content-Type: message/partial; id="300870"; number="9"; total="9"
  Subject: Graham's Encyclopedia
  
  dc45xuruv3m3e8z/OGRD6lxz13GC5m0XbXvcWlyFW4vxbSSK5KEoTOIIuxTFs2JK
  UnZKy1wTAV9TWr2dev7WrLbXkeOHUVQnjuyXEptwm3hBgfT43auvVh/v5mt+48pb
  n+09Hf7+5Nvyx5tf/fP4o+PJ398Xf958cW3v6ejzL17/9YPfPs4unv08efvr68O/
  njz/Fw==
  
  --

3. 另一種包裝百科全書的方法

對一個收件者來說, 要將上面所描述的各個部份依照正確順序組裝, 去除標題內容並將需要的部份輸入 Base64 解碼程式, 並不是一件容易的事. 若他使用的是舊的 Unix 機器, 他可能根本沒有 Base64 解碼器. 而若是使用 Microsoft 的機器, 他也可能無法正確的編輯訊息內容.

所以, 另一種可行的方法是: 將百科全書拆成有編號的區塊, 並分別 uuencode. 大部份的解碼工具都聰明到懂得自動去除標題列. 即使用 Microsoft Outlook 也沒問題.

為每個區塊編號的訣竅是將它們命名為淺顯易懂的序列 (例如 'cat'), 接著, 將這個序列輸入到一個 pipe 或是輸出檔(為了執行解壓縮或是 untar 功能). 輸出的部份會看起來像這樣:

  From grahjenk@au1.ibm.com Tue Dec 31 13:49:07 2002
  Subject: encyclo part 1/ size/sum 1024/16571
  
  begin 644 001_encyclo
  M<F]O=#IX.C`Z,3I3=7!E<BU5<V5R.B\Z+W-B:6XO<V@*9&%E;6]N.G@Z,3HQ
  M.CHO.@IB:6XZ>#HR.C(Z.B]U<W(O8FEN.@IS>7,Z>#HS.C,Z.B\Z"F%D;3IX
   ...
  M8W)E<',Z+V)I;B]K<V@*=V-O8F%T8V@Z>#HU,#(X.#HQ.D%L97@@=&AE(%=A
  B;FME<CHO97AP;W)T+VAO;64O=V-O8F%T8V@Z+V)I;B]K<P``
  `
  end
  
  --
  From grahjenk@au1.ibm.com Tue Dec 31 13:49:07 2002
  Subject: encyclo part 2/2 size/sum 945/12218
  
  begin 644 002_encyclo
  M:`IC-S0S-#0P.G@Z-38T-C,Z-3`P-#I!;F1R97<@3'5O;F<Z+VAO;64O861M
  M;W!E<F%T;W(Z+V5X<&]R="]H;VUE+V]P8U]O<#HO8FEN+W-H"F,Y,34W.3DZ
  M>#HU,#(Y,#HQ.CHO:&]M92]A9&UI;B]C.3$U-SDY.B]U<W(O8FEN+V)A<V@*
  `
  end
  
  --

注意到我們現在使用的都是大寫字元, 還包括了一些括號和其他符號. 某些符號在其他字元集中會有 mapping 上的不相容性. 這就是為什麼 RFC 2045 建議使用 Base64 而非 'uuencode'.

4. 百科全書的包裝工具

這裡可取得包裝工具. 為了簡易起見, 我們使用上面所述的另一種包裝方法. 包裝工具由來已久, 大多以 C 語言寫成; 有一部份使用 Bourne-Shell 版本. 這些工具通常使用暫存檔案.

你可以使用 Perl 撰寫一個非常典雅, 且不使用任何暫存檔的包裝程式. 這個程式既簡單又具可攜性. 我們要做的正是這樣的程式:

#!/usr/local/bin/perl -w
# @(#) filemail.pl      Breaks incoming stream into parts, then encodes
#                       each part and e-mails it to designated recipient.
#                       Vers. 2.05; Graham Jenkins, IBM GSA, December 2002.

use strict;             # Parts are encoded and sent via a double-buffer scheme.
use File::Basename;     # Uuencoding is used to reduce module dependence.
my  $PSize = 700;       # Default (input) part-size.
my  ($Count,$Sum,$Size,$Total,$InpBuf,$InpLen,$OutBuf,$j);

if ($#ARGV eq 2) { if ($ARGV[0] =~ m/^-\d+$/ ) { $PSize=0-$ARGV[0]; shift } } 

die "Usage: cat file  |".basename($0)." [-KbPerPart] destination filename\n".
    " e.g.: tar cf - .|".basename($0)." -64 smith\@popser.acme.com mydir.tar\n".
    "(Note: default un-encoded part size = $PSize","kb)\n"  if ($#ARGV ne 1);

open(INFILE,"-") || die "Can't read input!\n";
$Count = 0; $Total = "";# Loop until no further input available.

do { $InpLen = read(INFILE, $InpBuf, 1024 * $PSize);
     $Total  = $Count if $InpLen lt 1;
     do { $Size = length($OutBuf); 
          print STDERR "$ARGV[1] part $Count/$Total => $ARGV[0] $Size bytes\n";
          $Sum  = unpack("%32C*", $OutBuf);
          foreach $j (1,2) {$Sum = ($Sum & 0xffff) + int($Sum/0x10000)}
          open(PIPE, "| Mail -s" .
            "'$ARGV[1] part $Count/$Total size/sum $Size/$Sum' $ARGV[0]");
          $j = $Count ; while (length($j) < 3 ) { $j = "0" . $j }
          $j = dirname($ARGV[1])."/".$j if dirname($ARGV[1]) ne "."; 
          print PIPE "begin 644 ",$j,"_", basename($ARGV[1]),"\n",
            pack("u",$OutBuf),"\`\nend\n";
          close(PIPE)                                   } if $Count gt 0;
     $Count++; $OutBuf = $InpBuf                          } until $InpLen lt 1;

在這個程式中, Perl 的 'read' 敘述使得我們能在目標字串中取出一定數目的 byte. 如程式所示, 我們只是不停地從標準輸入中進行讀取直到 '$InpBuf' 傳回空字串. 每次取得一個非空白字串, 我們 uuencode '$OutBuf' 的內容(不管內容為何)並將它推入一個郵件程式中. 然後我們將 '$InpBuf' 的內容存入 '$OutBuf', 並進行下一次的動作./p>

Perl 使用 'pack' 敘述 (參數為 'u') 來進行字串的 uuencode 動作; 因此, 不需要任何額外的 modules. 我們使用 'unpack' 敘述的特性來計算每一個被傳送的區塊的 checksum.

你應該已經觀察到: 實際上, 我們在 Unix/Linux 的 'Mail' 程式裡開啟了一個 pipe 以進行郵件傳輸. 若要增加可攜性, 可安裝並使用 Net::STMP module.

解碼後的區塊大小的預設上限是 700kb 上限, 但我們可以另行自訂大小.

5. 類似功能的軟體

也許某些讀者已經發現以上訊息拆解的方法和 "Secure Printing with PGP" 中所述相同. 有興趣的讀者可至 "CPAN Scripts Repository" 取得更新過的版本軟體. 這些軟體使用 RFC 建議的 "Base64-encode then split" 程序.

較早的一篇文章 "A Linux Client for the Brother Internet Print Protocol" 中包含了一個使用 "split then send parts" 程序的 shell script; 它使用的編碼方式亦為 Base64.

6. 作者介紹

Graham 是澳洲 IBM Global Services 的一位 Unix 專家, 現居墨爾本. 他發展了在各種硬體平臺上工作的企業和開放系統.

版權宣告

Copyright © 2003, Graham Jenkins. Copying license Published in Issue 86 of Linux Gazette, January 2003


首頁(H)HOWTO(T)指南(G)FAQ(F)手冊頁(M)Linux電子報(L)LinuxFocus(S)