3.5 地址的確認
在SASL(見第六章)握手之後(如果必要的話,也在資料綁定(見第七章)之後,正在接收信息的實體必須(MUST)確認初始實體的ID
對於服務器間的通信,在SASL握手時,如果沒有指明授權的ID,這個初始的實體應該(SHOULD)是經過認證實體(參見簡單認證和安全層協議[SASL]中的定義)授權的ID(見第六章)
對於客戶端和服務器的通信, 在SASL握手時, 如果沒有指明授權的ID, “純JID” (<node@domain>)應該(SHOULD)是經過認證實體(參見[SASL]中的定義)授權的ID, “全JID”(<node@domain/resource>)的資源ID部份應該(SHOULD)是由客戶端和服務器在資源綁定的時候商定的(參見第七章)
4.1 XML流 / XML節概覽
XML流(XML Stream)的定義: 一個XML流乃是一個容器, 包含了2個實體之間通過網絡交換XML元素, 一個XML流乃由一個<stream>標籤開始的(該標籤需包含適當的屬性及名字空間聲明), XML流的結束則是由</stream>標籤做為結束, 在XML流的整個生命週期中, 初始化它的實體可以通過流來發送大量的XML元素, 例如:TLS協議(第5章),SASL協議(第6章)或XML節(在這裡指的是符合預設名字空間的元素, 包括<message/>,<presence/>,<iq/>元素), “初始的XML流"由初始實體(通常是一個客戶端或服務器)和接收實體(通常為一個服務器)協議, 從接收實體來看, XML流就是那個初始實體對接收實體的 “會話”, 接收實體則必需回覆一個相同的應答流給初始實體
Ex:
<stream> è xml流開始
……………………………….
……………………………….
</stream> è xml流結束
XML節(XML Stanzas)的定義 : 一個XML節是一個實體通過XML流向另一個實體發送結構化信息中的一個離散語意單位, 一個XML節乃直接存於根元素<stream/>之下, 也就是說任何XML都是從一個XML流的下一級的某個OPEN標籤(如: <presence>)開始的, 並相對應到其CLOSE標籤(如: </presence>)做為結束, 一個XML節可以包含子元素(例如: 屬性, 元素, XML字符…..等資料)以表達整個完整的信息, 然而在定義的XML只限定於<message/>,</presence>,</iq>這3個元素, 其TLS / SASL / 服務器回撥所需發送的XML元素, 則不被視為一個XML節
Ex:
<stream> è xml流開始
………. è TLS協議 (可省略)
………. è SASL協議
<presence> è xml節開始
<show/> è xml節子元素
</presence> è xml節結束
<message> è xml節開始
<body/> è xml節子元素
</message> è xml節結束
……………………………….
……………………………….
</stream> è xml流結束
XML流與XML節圖示
---------------------------- <stream> ---------------------------- <presence> <show/> </presence> ---------------------------- <message to='foo'> <body/> </message> ---------------------------- <iq to='bar'> <query/> </iq> ---------------------------- … ---------------------------- </stream> ----------------------------
4.3 流的安全
在XMPP 1.0中, 對於流的安全性可使用TLS及SASL已做為初始實體(發送初始化流 Send Default XML Stream)與接收實體(發送應答流 Response XML Stream)已做為安全傳送機制, 若XML流在被真實驗證之前, 實體不該發送任何XML節(XML Stanza), 即使當一方於流被驗證之前發送了XML節, 另一方實體則不能接受這些節的傳入, 應該返回一個<not-authorized/>的錯誤訊息及終止當前雙方TCP連結的XML流
4.4 XML流(XML Stream)的屬性
屬性 初始實體(A) à 接收實體(B) 接收實體(B) à 初始實體(A)
to 接收實體(B)的主機名稱 可忽略
from 可省略 接收實體(B)的主機名稱
id 可省略 協議key值
xml:lang 預設語言 預設語言
version xmpp 1.0 xmpp 1.0
初始實體(A)可以為客戶端或伺服器端(Client à Server Or Server à Server) 初始實體(A) è 接收實體(B) <stream:stream
xmlns='jabber:client' Or xmlns='jabber:server'
xmlns:stream='http://etherx.jabber.org/streams'
to='B.Server.com'
version='1.0'>接收實體(B) è 初始實體(A) <stream:stream
xmlns='jabber:client' Or xmlns='jabber:server'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_123' à 協議key值
from='B.Server.com'
version='1.0'>
5.1 TLS的使用概覽
XMPP包含了一個可讓XML流被防止篡改及偷聽的安全方法, 類似SSL的觀念
TLS使用原則需在SASL使用之前被使用, TLS可以在2個網域之間確保通信之間的安全, 其TLS使用方式步驟如下(初始實體[可以為客戶端(Client)或伺服器端(Server)]稱呼為 A, 接收實體[伺服器端(Server)]稱呼為 B)
1 . A透過TCP連接, 發送一OPEN之XML流信息(需包含版本這個屬性)給B【Client à Server】 <stream:stream
xmlns='jabber:client' 客戶端 ßà 服務器 Or xmlns='jabber:server' 服務器 ßà 服務器
xmlns:stream='http://etherx.jabber.org/streams'
to='B.Server.com'
version='1.0'>
2. B也透過TCP連接, 發送一個XML流信息(需包含版本這個屬性)給A【Server à Client】<stream:stream
xmlns='jabber:client' 客戶端 ßà 服務器 Or xmlns='jabber:server' 服務器 ßà 服務器
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_123' Or id='s2s_123'
from='B.Server.com'
version='1.0'>
3. B向A提議STARTTLS的範圍(包含version屬性), 包含驗證機制及其它流特性【Server à Client】<stream:features>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'> è TLS協議
<required/>
</starttls>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> è SASL協議
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
<mechanism>EXTERNAL</mechanism>
</mechanisms>
</stream:features>
4. A發出STARTTLS協議以通知B, 希望開始啟用 TLS保護XML流(XML Stream)
【Client à Server】<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
5. B發出<proceed/>或<failure/>元素做為回應, 回應<failure/>元素者終止XML流及TCP連接
【Server à Client】<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
OR <failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
</stream:stream>
6. TLS協議成功, 則A需重新發送一個新OPEN的XML流信息給B(不需先行發送一個</stream>, 因為A與B必需確保原來的流在TLS協議成功後才被關閉)
【Client à Server】<stream:stream
xmlns='jabber:client' 客戶端 ßà 服務器 Or xmlns='jabber:Server' 服務器 ßà 服務器
xmlns:stream='http://etherx.jabber.org/streams'
to='B.Server.com'
version='1.0'>
7. B由A接收到一個新的XML流, 也需重新發送一個新的XML流給A以做為應答, 其重新應答可包含可用特性但不可包含STARTTLS特性【Server à Client】<stream:stream
xmlns='jabber:client' 客戶端 ßà 服務器 Or xmlns='jabber:Server' 服務器 ßà 服務器
xmlns:stream='http://etherx.jabber.org/streams'
from='example.com'
id='c2s_234' Or id='s2s_234'
version='1.0'>
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
<mechanism>EXTERNAL</mechanism>
</mechanisms>
</stream:features>
6.1 SASL概覽
XMPP提供一個可以驗證XML流(XML Stream)的方法, SASL(簡單驗證安全層), 需
注意的是SASL協議時, 在XML元素中所使用的任何資料皆需被編碼成BASE64(參考RFC3548第3章), 若在使用SASL驗證機制之前明訂需使用TLS,則接收實體不可在TLS協議之前提供可用的SASL驗證清單
7. 資源綁定
初始實體(A)在與和接收實體(B)完成SASL協議之後, 若初始實體(A)想要或需要綁定一個特定的資源到XML流上, 就需採用資源綁定協議, 而資源綁定協議只適用於 [客戶端] (初始實體(A)可以為客戶端或為伺服器), 客戶端必需擁有一個相關資源ID(可由接收實體(B)或初始實體(A)程序提供), 而在XML流上的地址是一個 [全JID] (<node@domain/resource>), 在SASL協議成功後接收實體(B)需回應一個新XML流(XML Stream), 接收實體(B)若需初始實體(A)綁定一個資源, 必需在新的XML流中包含一個空的<bind/>元素 接收實體(B) è 初始實體(A), B要求A需做binding resource機制 <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_345' from='B.Server.com' version='1.0'>
<stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
</stream:features>
初始實體(A)收到接收實體(B)發送的bind通知後, 初始實體(A)需發送一個 “set” 類型的IQ節(<iq/>)給接收實體(B)來綁定一個資源到xml流中
(A) . 初始實體(A)希望接收實體(B)生成一個資源ID, 則初始實體(A)可發送一個空的<bind/>元素的 “set” 類型IQ節(<iq/>) Client à Server <iq type='set' id='bind_1'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> è 初始實體(A)希望由接收實體(B)自行生一
個資源ID, 所以初始實體(A)送出一個空的bind元素
</iq>
(B) . 初始實體(A)自行指定資源ID, 則初始實體(A)可發送包含期望資源ID的 “set” 類型IQ節(<iq/>) Client à Server <iq type='set' id='bind_2'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<resource>someresource</resource> è 初始實體(A)自行指定期望綁定資源
</bind>
</iq>
接收實體(B)可接受初始實體(A)所提供的資源ID, 但接收實體(B)也可生成其資源ID去覆蓋它, 而在這種情況下, 接收實體(B)不可返回一個錯誤訊息給初始實體(A), 而需把其生成資源ID透過IQ節 “result” 類型返回給客戶端
當初始實體(A)自行提供綁定資源ID時, 可能會發生以下錯誤情況
1. 初始實體(A)所提供的資源ID, 接收實體(B)無法處理(例如:不符合資源字符規範) Server à Client <iq type='error' id='bind_2'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<resource>someresource</resource>
</bind>
<error type='modify'>
<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
2. 初始實體(A)是不被允許綁定一個資源 Server à Client <iq type='error' id='bind_2'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<resource>someresource</resource>
</bind>
<error type='cancel'>
<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
3. 初始實體(A)所提供的資源ID已被使用(初始實體(A)不允許同一個資源ID綁定多個連結) Server à Client <iq type='error' id='bind_2'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<resource>someresource</resource>
</bind>
<error type='cancel'>
<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
9. XML節(STANZAS)
在通過TLS,SASL,BIND之後, 就可發送XML節, 在 ‘jabber:client’ 和 ‘jabber:server’命名空間中定義了3種xml節(<message/>,<presence/>,<iq/>)而這3種xml節包含了5種型態通用屬性(to、from、id、type、xml:lang)
9.2.1 信(訊)息節(<message/>)
<message/>節類型可以被視為一個”push”機制, 運用於一個發送實體發送信息給一個接收實體(例如: email中的送信), <message/>節需明定一個信息接收者屬性(to), 一個<message/>節中的 “type” 屬性是被建議的, 如果包含了 “type” 屬性它指明這個信息的會話上下文, 其 “type” 屬性必需是以下值之一 (chat, error, groupchat, headline, normal), 其type屬性說明了這個信息的類型, 一個接收端接收了一個沒有type屬性的信息, 則需將該message視為normal(normal為預設值), 一個<message/>節可以包含任何適當的命名空間的子元素, 但若<message/>節是屬於type=error類型的其子元素必需包含一個<error/>子元素, <message/>節可以包含以下子元素任何一種並且無需聲明命名空間
屬性
to
指明該信息要發送給哪一個接收實體
必要的
from
id
type
指明該信息的訊息類型(chat, error, groupchat, headline, normal)
1. 傳遞內容未包含以noraml代替
2. type=error需包含<error/>子元素
可省略
xml:lang
1. <subject/>子元素(該元素不需聲明命名空間)
Subject(信(訊)息主題)子元素包含了人可讀取的xml字符資料, 其用於指明這個信息的主題, 該子元素不可有任何屬性(但xml:lang除外), subject子元素可運用xml:lang屬性將信息主題以各種不同語言版本呈現, 但每個subject子元素xml:lang屬性值需互不相同才可以
2. <body/>子元素(該元素不需聲明命名空間)
Body(信息內容)子元素同樣也屬於人可讀取的xml字符資料, 其子元素用於表達該信(訊)息的內容, 該子元素不可有任何屬性(但xml:lang除外), body子元素可運用xml:lang屬性將信息內容以各種不同語言版本呈現, 但每個body子元素xml:lang屬性值需互不相同才可以
3. <thread>
Thread(信(訊)息追踪線索)子元素為人無法判讀的xml字符資料, 用於追踪2個實體間的會話線索
子元素
xml字符資料
屬性
資料內容
說明
subject
可被人判讀
不可有屬性(xml:lang除外)
於<message/>節可有1~N個
必需
body
可被人判讀
不可有屬性(xml:lang除外)
於<message/>節可有1~N個
必需
thread
不可被判讀
不可有屬性
必需
9.2.2 出席信(訊)息節(<presence/>)
<presence/>節可以被視為一個廣播或出版訂閱機制, 用於多個實體接收某個已訂閱實體的信息, 通常一個發送實體不應該包含 “to” 屬性來發送一個出席信息節, 而該發送實體所連接的服務器應該廣播給那些有訂閱該發送實體當前的網路可用性(例如: 離線, 線上, 離開…..等等)的其它發送實體, <presence/>節的 “type”屬性是可被選擇性使用的(未包含type屬性的出席信(訊)息節<presence/>表示發送端已在線上並可進行通信), 其<presence/>節 “type”屬性值可包含如(unavailable, subscribe, subscribed, unsubscribe, unsubscribed, probe, error), 一個<presence/>節可以包含任何適當命名空間的子元素, 如果<presence/>節的type=error, 必需包含一個<error/>子元素, 一個<presence/>節若沒有包含 “type” 屬性的話, 它可以包含以下任何子元素
屬性
to
不必要
from
id
type
指明該信息的訊息類型(unavailable, subscribe, subscribed, unsubscribe, unsubscribed, probe, error)
1. 傳遞內容未包含視為已在線上並可進行通信, 及可包含子元素
2. type=error需包含<error/>子元素
可省略
xml:lang
1. <show/>
該子元素是選擇性的, 該子元素所包含的xml資料不可供人所閱讀, 該子元素所要表達的是一個實體或資源的可用性狀態, 一個<presence/>節只可包含一個<show/>子元素, 而該子元素不可擁有任何屬性, 其子元素的xml資料內容可使用以下說明中之一項(away、chat、dnd、xa), 若<presence/>節沒有包含<show/>子元素則該實體的狀態被視為 “在線上” 或 “可使用”
2. <status/>
該子元素是選擇性的, 其元素的xml資料乃用來表達<show/>子元素可用性更詳細的描述說明(例如: 會議中), 而<status/>子元素也不可擁有任何屬性, 但除了xml:lang除外
3. <priority/>
該子元素是選擇性的, 該子元素的xml資料不可被人所閱讀, 其用來指明資源的優先權級別, 該值需介於-128 ~ 127之間, 一個<presence/>節只可有一個<priority/>子元素, 而該子元素也不可擁有任何屬性, 若該子元素未被提供則其優先權級別視為0
子元素
xml字符資料
屬性
資料內容
說明
show
不可被判讀
不可有屬性
away、chat、dnd、xa
於<presence/>節只可有1個
選擇性
未提供以在線上或可使用替代
status
可被人判讀
不可有屬性(xml:lang除外)
於<presence/>節可有1~N個
選擇性
priority
不可被判讀
不可有屬性
於<presence/>節只可有1個
選擇性
未提供以0替代
9.2.3 IQ節(<iq/>)
<iq/>節是一個信息請求/查詢回應(Inof/Query)的一個機制, 類似http, <iq/>節使一個實體(A)向另一個實體(B)做出請求, (B)實體回應(A)實體結果, 請求和應答的資料是被定義在一個直的子元素命名空間聲名中, 並且由請求實體用 “id”屬性來做為跟踪彼此交互行為, IQ交互乃伴隨著一個結構化的資料交換通用模式, 例如: get/result(error) 或 set/result(error)
執行iq節, 需應用以下規則
1. <iq/>節中的 “id” 屬性是必需的
2. <iq/>節中的 “type” 屬性是必需的, 其屬性只可為以下內容之一項
甲、get – 表示這個<iq/>節是一個對信息或需求的請求
乙、set – 表示這個<iq/>節提供資料, 用於設置新的值, 或取代現有的值
丙、result – 表示對這個<iq/>節的請求(get)或提供資料(set), 所給予的應答
丁、error – 應答發生錯誤, 表示對這個<iq/>節的請求(get)或提供資料(set), 所給予的錯誤應答
3. 一個實體接收到 “get” 或 “set” 類型的<iq/>節, 該實體必需回應一個 “result “ 或 “error” 類型的<iq/>節, 以做為回應, 其回應的<iq/>節其 “id”屬性需與請求(get)或提供資料(set)的<iq/>節一致
4. 一個實體接收到 “result” 或 “error” 類型的<iq/>節, 不可發發送同樣的“result” 或 “error” 類型的<iq/>節做為應答
5. 一個 “get” or “set”類型的<iq/>節必需包含一個子元素以做為指明請求或提供資料的資料來源
6. 一個 “result” 類型的<iq/>節必需包含零或一個子元素做為指明回應的資料來源
7. 一個 “error” 類型的<iq/>節必需包含當初 “get” or “set”類型<iq/>節所傳送的子元素及一個<error/>子元素以做為回應
9.3 xml節的錯誤
節錯誤的處理方式類似xml流的錯誤, 但節(stanzas)錯誤是可被恢復的, 所以節錯誤也包含了用來暗示原來發送者可用什麼方式來補求節錯誤的方法
節(stanzas)錯誤的規則
1. 接收或處理實體接收到一個節錯誤時應該返回相同類型的節給發送實體(需回應相同類型節的type屬性值需要設置為 “error”
2. 接收或處理實體返回相同類型節給發送實體時, 需包含原發送的xml資料內容, 這樣可以讓發送實體去檢查錯誤的原因
3. 返回一個相同類型的節需包含<error/>子元素
4. 如果返回一個相同類型的節type屬性不為 “error” 則不可包含<error/>子元素
<stanza-kind to='sender' type='error'>
[RECOMMENDED to include sender XML here]
<error type='error-type'>
<defined-condition xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' xml:lang='langcode'>
OPTIONAL descriptive text
</text>
[OPTIONAL application-specific condition element]
</error>
</stanza-kind>
stanza – kind如<message/>, <presence/>, <iq/>節
error – type可包含如下值之一 (cancel, continue, modify, auth, wait)
defined-condition – <bad-request/>, <conflict/>, <feature-not-implemented/>,……
<unexpected-request/>等(可參考RFC-3920中的9.3.3已定義的條件)
2. 清算運用之前研討會分享作法實作
1.如何將perl 模組安裝到私有的目錄(luke於2006/11/15分享)
目前電腦教室及清算系統都將系統會使用到的模組的source code及install分別安裝至特定目錄位置, 讓se在安裝系統時可以把系統整個tar過去及解tar後就可執行installer的安裝過程
原始分享內容截錄
A.解開perl 模組(http://www.cpan.org/)
tar zxvf mmm.tgz
perl Makefile.PL PREFIX=/usr/local/Portal/perlmods LIB=/usr/local/Portal/perlmods
make
make install
B.程式裡的模組叫用方式
一. unshift(@INC,'/usr/local/Portal/perlmods');
二. use lib '/usr/local/Portal/perlmods' ;
2.如何替perl開發的軟體撰寫installer (luke於2006/11/15分享)
電腦教室及清算系統installer的內容大都是對系統會使用到的設定檔(conf file)及建置資料庫及匯入資料至資料……等一些預設性的工作所做的編寫, se在執行完installer後即可使用該系統, 不用再花費更多人力去建置一個系統所需預設的工作
3.upgrade 系統設定及運作原理 (luke於2006/11/15分享)
卡務,電腦教室及清算系統目前皆是採用luke提供的upgrade system做為程式bug修正及版本更新的傳遞upgrade管道, 只需把已修正過程式更新至server端的upgrade目錄下,即可透過upgrade程式將server端已修正的程式自動更新至client端
原始分享內容截錄
#要做簽章的起始目錄
computer_initdir: /usr/local/computer_pkg20060704_v1
computer_exclude: [dir]storage
computer_exclude: [dir]sync
computer_exclude: [dir]upgrade
computer_exclude: [dir]WOL
computer_exclude: [dir]conf
computer_exclude: [dir]temp
computer_exclude: [dir]test
computer_exclude: [dir]backup
computer_exclude: [dir]tmp
#額外需要做簽章的檔案(需要填實際的位置)
#iniDB目錄下的檔案不做upgrade處理, 另開發3支mysql檔案需做額外簽章
computer_additional: /usr/local/computer_pkg20060704_v1/iniDB/altertbl.mysql
computer_additional: /usr/local/computer_pkg20060704_v1/iniDB/dropdb.mysql
computer_additional: /usr/local/computer_pkg20060704_v1/iniDB/addsshtbl.mysql
computer_additional: /usr/local/computer_pkg20060704_v1/html/conf/action.conf
#執行指令
computer_cmds: /bin/chmod -R 750 /usr/local/computer/image;/bin/chown -R computer:computer /usr/local/computer/image
computer_cmds: /bin/chmod 750 /usr/local/computer/iniDB/altertbl.mysql;/bin/chmod 750 /usr/local/computer/iniDB/dropdb.mysql;/bin/chmod 750 /usr/local/computer/iniDB/addsshtbl.mysql
computer_cmds: /bin/chmod 750 /usr/local/computer/tools/addtbl.pl;
………………………………………………………………
………………………………………………………………
………………………………………………………………
#要做簽章的起始目錄
computerv2_initdir: /usr/local/computer_pkg20070302_v2
#[file] 檔案包含xx 不做簽章 [dir]目錄名稱包含yy 不做簽章
computerv2_exclude: [file].md5
computerv2_exclude: [file].bak
computerv2_exclude: [file].gz
computerv2_exclude: [file].cf
computerv2_exclude: [file].db
………………………………………………………………
………………………………………………………………
………………………………………………………………
4.Data::Dumper 的使用 (luke於2007/01/18分享)
在之前的卡務及電腦教室對於設定檔的資料, 皆是以share memory方式做為程式讀設定檔的方式, 而清算系統則是改採Data::Dumper做為設定檔讀取方式, 只要將設定檔格式先行轉換成hash(雜湊函數)方式存放於另一個檔案中, 即可以perl中以hash方式叫用設定檔內容
原始分享內容截錄
四.Data::Dumper 的使用
A.Data::Dumper 並非perl內建模組,請自行使用cpan 安裝,或是下載src回來後自行安裝
B.Data::Dumper 主要的用途是把資料結構轉換成檔案結構,反之亦然
C.
#exam3.pl
#!/usr/bin/perl
use Data::Dumper;
my %conf;
open(my $HANDLE,"system.cf");
while(<$HANDLE>){
$_=~tr/\r\n//d;
if ($_ eq ''){next;}
my ($p,$v)=split(': ',$_);
$conf{uc($p)}=$v;
}
close($HANDLE);
open(my $HANDLE2,">system.cache");
$Data::Dumper::Terse = 1;
print $HANDLE2 Dumper(\%conf);
close($HANDLE2);
#exam4.pl
#!/usr/bin/perl
use Data::Dumper;
my $conf;
my $code='';
{
local $/=undef;
open(my $HANDLE2,"system.cache");
my $cache=<$HANDLE2>;
close($HANDLE2);
$code='$conf=' . $cache;
}
print "=======my code========\n";
print $code;
print "=======================\n";
eval "$code";
foreach my $k(keys %{$conf}){
print "$k=>" . $conf->{$k}. "\n";
}
5.樣板的使用 (luke於2007/01/18分享)
在卡務及電腦教室並沒有所謂樣板的觀念在使用, 往往對於html皆是撰寫於程式中, 所以一旦畫面格式要更動修改, 皆需由rd自行修正, 容易造成rd要花多餘的時間來做這件事, 所以於清算系統則採用樣板模式, rd只需產生樣板所需的資料後套用至樣板中即可使用, 而清算所使用的方式乃是用xml及xsl做配搭, 由製作頁面的人員施作xsl, 由rd產生xml去配搭所對應的xsl再轉換成html即可將畫面顯示在web上, 一旦畫面格式有異動只需請頁面製作人員修正頁面, 若是異動(新增或刪除)部份資料, 則由rd去修正相對應的xml再告知頁面製作人員修正即可, 這樣比較有所謂的分工的觀念在裡面
目前清算系統部份取代方式乃是使用的是取代變數中的方法一
原始分享內容截錄
b.取代變數的做法
方法論一.
Step 1. 首先,我們把程式與網頁分開
參考範例:實際上的目錄規劃我們在上次的研討會提過,應該是
/usr/local/Product_Name/Function_Type
程式/網頁/設定檔
虛擬目錄
實體目錄
exam6.pl
/octopus/html/test/cgi-bin
/usr/local/octopus/html/test/cgi-bin
myhtml.htm
/usr/local/octopus/html/test/templet
system.cf
/usr/local/octopus/html/test/conf
Step 2. 使用架構去讀取外部網頁
Step 3. 使用perl 的強項regular expression 把變數做替換
#exam6.pl
#http://tcmail.program.com.tw/octopus/test/cgi-bin/exam6.pl?src=myhtml.htm
#!/usr/bin/perl
use strict;
my $buffer = $ENV{QUERY_STRING};
my %IN;
my @nv_pairs = split( /\&/, $buffer );
foreach my $nvp ( 0 .. $#nv_pairs )
{
$nv_pairs[$nvp] =~ tr/+/ /;
my ( $key, $keyword ) = split( /=/, $nv_pairs[$nvp], 2 );
$key =~ s#%(..)#pack("c",hex($1))#ge;
$key =~ tr/[A-Z]/[a-z]/;
$keyword =~ s/%(?:([0-9a-fA-F]{2})u([0-9a-fA-F]{4}))/defined($1)? chr hex($1) : chr hex($2)/ge;
$IN{$key} .= "\0" if ( defined( $IN{$key} ) );
$keyword =~ s/\`\ \"\'\>\<//gs;
$IN{$key} .= $keyword;
}
my %varhash;
$varhash{UID}='luke';
$varhash{PASSWD}='lukePASSWD';
my $htmlstr='';
{
local $/=undef;
open (my $HANDLE,"/usr/local/octopus/html/test/templet/" . $IN{src});
$htmlstr=<$HANDLE>;
close($HANDLE);
}
$htmlstr =~ s/\$([A-Z]+)/
my $value = $varhash{$1};
if(!defined $value) {
$value = "\$$1"; # leave it as is
}
$value;
/ge;
print "Content-Type: text/html; charset=big5\r\n\r\n";
print $htmlstr ;
#myhtml.htm
<html>
<body>
這樣好多了,不是嗎? 我的變數不會跟程式碼混在一起,<br>
而且當網頁要修改時,我們不需要大費周章把程式碼翻<br>
出來,而且你看, 我這邊以一樣可以使用取代的方式讓靜<br>
態的網頁呈現動態的資訊<br><br>
比如:變數 UID 我可以置換成$UID <br>
比如:變數 PASSWD 我可以置換成$PASSWD <br>
請注意我沒有這個變數 $NOVAR 的值<br><br>
真好,不是嗎,但當我有邏輯要在網頁運行時怎麼辦??<br>
</body>
</html>
方法論二.
將程式與設定分離,使用動態的CACHE/CONFIG 來呈現樣式
#exam7.pl
#http://tcmail.program.com.tw/octopus/test/cgi-bin/exam7.pl?src=myhtml.htm
#!/usr/bin/perl
use strict;
my $buffer = $ENV{QUERY_STRING};
my %IN;
my @nv_pairs = split( /\&/, $buffer );
foreach my $nvp ( 0 .. $#nv_pairs )
{
$nv_pairs[$nvp] =~ tr/+/ /;
my ( $key, $keyword ) = split( /=/, $nv_pairs[$nvp], 2 );
$key =~ s#%(..)#pack("c",hex($1))#ge;
$key =~ tr/[A-Z]/[a-z]/;
$keyword =~ s/%(?:([0-9a-fA-F]{2})u([0-9a-fA-F]{4}))/defined($1)? chr hex($1) : chr hex($2)/ge;
$IN{$key} .= "\0" if ( defined( $IN{$key} ) );
$keyword =~ s/\`\ \"\'\>\<//gs;
$IN{$key} .= $keyword;
}
my %varhash;
$varhash{UID}='luke';
$varhash{PASSWD}='lukePASSWD';
$varhash{EXISTS}='已經有';
my $htmlstr='';
{
local $/=undef;
open (my $HANDLE,"/usr/local/octopus/html/test/templet/" . $IN{src});
$htmlstr=<$HANDLE>;
close($HANDLE);
}
$htmlstr =~ s/\$([A-Z]+)/
my $value = $varhash{$1};
if(!defined $value) {
$value = "\$$1"; # leave it as is
}
$value;
/ge;
my %conf;
open(my $HANDLE,"/usr/local/octopus/html/test/conf/system.cf");
while(<$HANDLE>){
$_=~tr/\r\n//d;
i f ($_ eq ''){next;}
my ($p,$v)=split(': ',$_);
print "$p\t$v";
$conf{uc($p)}=$v;
}
close($HANDLE);
$htmlstr =~ s/\$([A-Z]+)/
my $value = $conf{$1};
if(!defined $value) {
$value = "\$$1"; # leave it as is
}
$value;
/ge;
print "Content-Type: text/html; charset=big5\r\n\r\n";
print $htmlstr ;
清算xml樣板套用方式 (standard.xml)
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="http://se6.program.com.tw/liquxsl/$APNAME0"?>
<liquidate>
<info>
<apname>$APNAME</apname>
<title>$TITLE</title>
<splitchk>$SPLITCHK</splitchk>
<nowpage>$NOWPAGE</nowpage>
<totalpage>$TOTALPAGE</totalpage>
<rows>$ROWS</rows>
<host>$HOST</host>
<action>$ACTION</action>
<src>$SRC</src>
<backaction>$BACKACTION</backaction>
<backsrc>$BACKSRC</backsrc>
</info>
<data>
$XMLRECORDSET
</data>
</liquidate>
6.清算對於json的運用
清算對於json的運用乃運用在角色及權限上, 將各個角色轉換成json模式存放, 再將該json檔案付予使用這個角色的權限, 並透過讀取json檔案的程式將內容讀取出來, 已做為程式在top及left的functions的顯示
清算json存放模式
Top function json檔案內容
var func0 = [
{
funcacid: '1',
funcadesc: '系統管理'
},
{
funcacid: '2',
funcadesc: '資料管理'
},
{
funcacid: '4',
funcadesc: '清算管理'
},
{
funcacid: '8',
funcadesc: '報表管理'
},
{
funcacid: '16',
funcadesc: '核銷帳務管理'
},
{
funcacid: '32',
funcadesc: '資料查詢管理'
},
];
Left function json檔案內容
var func1 = [
{
rank: '41',
funcdesc: '管理角色定義',
rightpage: 'http://se6.program.com.tw/liquidate/parserhtml.pl?src=roleslist.htm',
funcdescparent: '系統管理',
displaytype: '1'
},
{
rank: '42',
funcdesc: '管理權限設定',
rightpage: 'http://se6.program.com.tw/liquidate/parserhtml.pl?src=managerslist.htm',
funcdescparent: '系統管理',
displaytype: '1'
},
{
rank: '43',
funcdesc: 'IP存取設定',
rightpage: 'http://se6.program.com.tw/liquidate/parserhtml.pl?src=blackipslist.htm',
funcdescparent: '系統管理',
displaytype: '1'
},
];
讀取json的html檔案
Top Functions html原始檔案
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上頁框主功能頁</title>
<link href="../../liqu_image/css/top.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="$URL"></script>
<script type="text/javascript" src="../../liqu_image/js/frameName.js"></script>
<script type="text/javascript" src="../../liqu_image/js/topjsondata.js"></script>
<script type="text/javascript">
<!--
function initPage(){
createFun($VARIABLE);
}
window.onload = initPage;
//-->
</script>
</head>
<body>
<div class="bg01" align="center">
<div class="bgarea">
<div id="funArea" class="funArea"> </div>
</div>
</div>
<div class="locName">您現在位置:<span id="itemName"> </span></div>
</body>
</html>
Top Functions html web顯示內容
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上頁框主功能頁</title>
<link href="../../liqu_image/css/top.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://se6.program.com.tw/liquidate/getjsondata.pl?type=eA9t"></script>
<script type="text/javascript" src="../../liqu_image/js/frameName.js"></script>
<script type="text/javascript" src="../../liqu_image/js/topjsondata.js"></script>
<script type="text/javascript">
<!--
function initPage(){
createFun(func0);
}
window.onload = initPage;
//-->
</script>
</head>
<body>
<div class="bg01" align="center">
<div class="bgarea">
<div id="funArea" class="funArea"> </div>
</div>
</div>
<div class="locName">您現在位置:<span id="itemName"> </span></div>
</body>
</html>