首頁(H)|HOWTO(T)|指南(G)|FAQ(F)|手冊頁(M)|Linux電子報(L)|LinuxFocus(S)
Linux電子報首頁|目錄|FAQ
讓 Linux 更加有趣呦!
好吧, 也許你不是要寄百科全書, 而是一部電影, 或是一個壓縮過的巨大目錄. 不用說, 你會用 FTP 來上傳它, 除非你的電腦只連接到一個內部 LAN 的網域, 而沒法用 FTP 連接到外界. 或者有可能你的收件者的電腦因為安全因素無法使用 FTP. 在這種情況下, 你可以將你要傳的東西編碼成一串 ASCII 字元, 用 e-mail 來傳送. 你可以用 'uuencode' 軟體, 或是 RFC 2045 "Multipurpose Internet Mail Extensions (MIME) Part One" 中敘述的 Base64 Content Transfer Encoding 來進行編碼.
在現實世界裡, 你可以將直接將一套百科全書弄成一個包裹寄出 - 只要送件員能夠接受那份包裹的大小及重量, 這就不失為一個好方法. 但有時候, 你必須將百科全書拆裝成好幾份較能讓人接受的大小重量.
同理可證, 在 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== --
對一個收件者來說, 要將上面所描述的各個部份依照正確順序組裝, 去除標題內容並將需要的部份輸入 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'.
在 這裡可取得包裝工具. 為了簡易起見, 我們使用上面所述的另一種包裝方法. 包裝工具由來已久, 大多以 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 上限, 但我們可以另行自訂大小.
也許某些讀者已經發現以上訊息拆解的方法和 "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.
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)