RFC3921

本文的英文原文來自RFC 3921

網絡工作組 Saint-Andre, Ed.
申請討論: 3921 Jabber軟件基金會
類別: 標準跟蹤 2004年10月
可擴展的消息和出席信息協議 (XMPP): 即時消息和出席信息

關於本文的說明

本文爲互聯網社區定義了一個互聯網標準跟蹤協議,並且申請討論協議和提出了改進的建議。請參照“互聯網官方協議標準”的最新版本(STD 1)獲得這個協議的標準化進程和狀態。本文可以不受限制的分發。

版權聲明

本文版權屬於互聯網社區 (C) The Internet Society (2004).

摘要

本文定義了可擴展消息和出席信息協議(XMPP)的核心功能的擴展和應用,XMPP提供了RFC 2779 定義的基本的即時消息和出席信息功能。

目錄

緒論

概覽

XMPP是一個流化XML[XML]元素的協議,用於準實時的交換消息和出席信息。XMPP的核心功能定義在Extensible Messaging and Presence Protocol (XMPP): Core [XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]. 這些功能 -- 主要是 XML流, 使用 TLS和SASL,以及流的根元素之下的<message/>, <presence/>, 和 <iq/> 子元素 -- 爲各種類型的準實時應用提供了一個構造基礎, 它可以被放在覈心的頂層,使用特定XML名字空間[XML-NAMES]發送特定的應用數據. 本文描述XMPP核心功能的擴展和應用,XMPP核心功能提供了RFC 2779 [IMP-REQS]定義的基本的即時消息和出席信息功能。

需求

爲了達到本文的目的, 基本的即時消息和出席信息應用的需求定義在[IMP-REQS],它是一個高階的規定,一個用戶必須完成以下用例:
  • 和其他用戶交換消息
  • 和其他用戶交換出席信息
  • 管理和其他用戶之間的訂閱和被訂閱
  • 管理聯繫人列表中的條目(在 XMPP 中這被稱爲 "roster")
  • 屏蔽和特定的其他用戶之間的通信(出或入)
這些功能領域的詳細定義在[IMP-REQS]中, 感興趣的用戶可以直接閱讀原文關於需求方面的內容。
[IMP-REQS]也規定出席信息服務必須從即時消息服務中分離; 例如, 它必須可能用這個協議來提供一個出席信息服務,一個即時消息服務,或同時提供兩者. 儘管本文假定實現和部署希望提供統一的即時消息和出席信息服務, 但沒有要求一個服務必須同時提供出席信息服務和即時消息服務, 並且協議也提供了把出席信息服務和即時消息服務分離成爲獨立服務的可能性.
注意: 雖然基於XMPP的即時消息和出席信息符合[IMP-REQS]的要求,但它不是特意爲那個協議設計的,因爲基礎協議是在RFC 2779成文之前通過Jabber開放源代碼社區的一個開放的開發過程發展出來的. 也請注意儘管在Jabber社區發展的協議中定義了許多其他方面的功能,但是這些協議不包含在本文之中,因爲它們不是[IMP-REQS]所要求的.

術語

本文繼承了 [XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]定義的術語.
大寫關鍵字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 和 "OPTIONAL" 在本文中的含義定義在 BCP 14,RFC 2119 [TERMS].

XML 節的語法

符合'jabber:client'和'jabber:server'名字空間的XML節的基本語義和通用屬性已經在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]中定義了. 無論如何, 這些名字空間也定義了一些其他的子元素, 比如通用屬性'type'的值, 對於即時消息和出席信息應用就是特殊的. 因而, 在選擇用於這類應用的特定用例之前, 我們在這裏需要先描述一下XML節的語法, 用來補充[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]中的討論.

消息語法

符合'jabber:client' or 'jabber:server'名字空間的消息節用於"推" 信息到另一個實體. 在即時消息應用中通常的用法是包含,一個單獨的消息,在一個聊天會話中的消息,一個多用戶聊天室的上下文中的消息,標題或其他警告和錯誤的消息,

消息的類型

一個消息節的'type' 屬性是建議的(RECOMMENDED); 如果包含了它,它指明這個消息的會話上下文,從而提供一個關於表達的線索(例如, 在一個GUI中). 如果包含了它, 'type' 屬性必須(MUST)是以下的值之一 :
  • chat -- 消息是在一對一聊天會話的語境被髮送. 一個兼容的客戶端應該(SHOULD)在一個允許兩個實體進行一對一聊天的界面中顯示消息,包括適當的會話歷史.
  • error -- 發生了一個和上次發送者發送的消息有關的錯誤(關於節錯誤語法的詳細信息, 參考 [XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]). 一個兼容客戶端應該(SHOULD)在一個適當的界面展示它以通知發送者這個錯誤的種類.
  • groupchat -- 消息是在一個多用戶聊天環境的語境下發送的(類似[IRC]). 一個兼容客戶端應該(SHOULD)在允許多對多聊天的界面顯示這個消息,包括, 包括這個聊天室的名冊和適當的會話歷史. 基於XMPP的羣聊協議的完整定義超出了本文的範圍.
  • headline -- 一個消息可能是由一個遞送或廣播內容的自動化服務生成的(新聞, 體育, 市場信息, RSS feeds, 等等.). 這個消息是不需要回復的, 一個兼容客戶端應該(SHOULD) 在一個適當的和單獨消息,聊天會話,或羣聊會話不同的界面顯示這個消息(例如, 不給接收者提供回覆能力).
  • normal -- 這個消息是一個在一對一會話或羣聊會話之外的單獨消息, 並且它希望接收者能夠回覆.一個兼容客戶端應該(SHOULD)在一個允許接收者回復的界面顯示這個消息, 但不需要會話歷史.
一個 IM 應用應該(SHOULD)支持所有前述的消息類型;如果一個應用接收了一個沒有'type'屬性的消息或這個應用不理解'type'屬性的值, 它必須(MUST)認爲這個消息是一個 "normal" 類型(如,"normal" 是缺省的). "error"類型必須(MUST)僅僅在應答一個和從別的實體接收到的消息有關的錯誤時生成.
儘管'type'屬性是可選的(OPTIONAL), 處於禮貌原因對於消息的任何回覆總是和原來的消息同一類型;此外, 一些特殊的應用(例如, 一個多用戶聊天服務) 可以(MAY)根據它們的判斷強制特定消息類型的使用(例如, type='groupchat').

子元素

正如 擴展名字空間extended namespaces(第二章第四節)所述, 一個消息節可以(MAY)包含任何適當名字空間的子元素.
和缺省名字空間聲明一致, 缺省消息節的名字空間是'jabber:client' 或 'jabber:server', 定義了某幾個允許的消息節的子元素. 如果消息節的類型是 "error", 它必須(MUST)包含一個<error/>子元素; 詳細情況, 見[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]. 否則, 消息節可以(MAY)包含以下子元素的任何一種並且無需顯式地聲明名字空間:
  1. <subject/>
  2. <body/>
  3. <thread/>
主題
<subject/> 元素包含了人類可讀的 XML 字符數據指明這個消息的主題. <subject/>元素不能(MUST NOT)擁有任何屬性, 除了'xml:lang'屬性. <subject/> 元素可以(MAY)包含多個實例用於爲同一主題提供備用版本, 但是僅在每個實例的擁有的'xml:lang'屬性的值互不相同的時候纔可以. <subject/> 元素不能(MUST NOT)包含混合的內容(定義在 [XML]第三章第二節第二小節).
主體
<body/> 元素包含人類可讀的XML字符數據表達消息的文本內容; 這個子元素通常會有但是是可選的(OPTIONAL). <body/>元素不能(MUST NOT)擁有任何屬性, 除非是'xml:lang'屬性. <body/> 元素可以(MAY)包含多個實例用於爲同一主體提供備用版本, 但是僅在每個實例的擁有的'xml:lang'屬性的值互不相同的時候纔可以. <body/>元素不能(MUST NOT)包含混合的內容(定義在 [XML]第三章第二節第二小節).
線索
<thread/> 元素包含非人類可讀的XML字符數據表達一個標識符用於跟蹤兩個實體之間的一個會話線索(有時相當於一個"即時消息會話"). <thread/>元素的值是由發送者生成的並且應該(SHOULD)在任何回覆中拷貝回來. 如果使用了它, 它必須(MUST)在這個流的會話線索中是唯一的並且必須(MUST)和那個會話相一致(一個從同一個全JID但不同線索ID接收到消息的客戶端必須(MUST)假定這個有問題的消息存在於已有的會話線索之外. <thread/>元素的使用是可選的(OPTIONAL)並且不是用於標識獨立的消息,而是標識會話. 一個消息節不能(MUST NOT)包含超過一個的<thread/>元素. <thread/>元素不能(MUST NOT)擁有任何屬性. <thread/>屬性的值必須(MUST)被實體處理成不透明的; 不能從它得到任何語義學上的含義,並且只能對它做精確的比較. <thread/>元素不能(MUST NOT)包含混合內容(定義在 [XML]第三章第二節第二小節).

出席信息語法

符合'jabber:client' 或 'jabber:server'名字空間的出席信息節用於表達一個實體當前的網絡可用性(離線或在線, 包括之後的各種亞狀態和可選的用戶名義的描述性文本), 並且通知其他實體它的可用性. 出席信息節也用於協商和管理對於其他實體的出席信息的訂閱.

出席信息的類型

出席信息節的'type'屬性是可選的(OPTIONAL). 一個不擁有任何'type'屬性的出席信息節用來通知服務器發送者已經在線並且可以進行通信了, 'type' 屬性表示缺乏可用性, 請求管理對其他實體的出席信息的訂閱, 請求其他實體的當前出席信息, 或發生了和上次發出的出席信息節有關的錯誤. 如果包含了它, 'type'屬性必須(MUST)擁有以下值之一:
  • unavailable -- 通知實體將不可通信.
  • subscribe -- 發送者希望訂閱接收者的出席信息.
  • subscribed -- 發送者允許接收者接收他們的出席信息.
  • unsubscribe -- 發送者取消訂閱另一個實體的出席信息.
  • unsubscribed -- 訂閱者的請求被拒絕或以前的訂閱被取消.
  • probe -- 對一個實體當前的出席信息的請求; 只應(SHOULD)由服務器代替一個用戶生成.
  • error -- 處理或遞送之前發送的出席信息節的時候發生了錯誤.
關於出席信息語義學的詳細信息和基於XMPP的即時消息和出席信息應用程序的訂閱模式,參考 交換出席信息Exchanging Presence Information(第五章) 和 管理訂閱Managing Subscriptions(第六章).

子元素

如 擴展名字空間extended namespaces(第二章第四節)所述, 一個出席信息節可以(MAY)包含任何適當名字空間的子元素.
和缺省名字空間聲明一致, 缺省出席信息節的名字空間是'jabber:client' 或 'jabber:server', 定義了某幾個允許的出席信息節的子元素. 如果出席信息節的類型是 "error", 它必須(MUST)包含一個<error/>子元素; 詳細情況, 見[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]. 如果出席信息節不擁有'type'屬性,它可以(MAY)包含以下任何子元素(注意<status/>子元素可以(MAY)在一個類型爲"unavailable"或"subscribe"(出於歷史原因)的出席信息中被髮送):
  1. <show/>
  2. <status/>
  3. <priority/>
展示
可選的(OPTIONAL)<show/>元素包含非人類可讀的XML字符數據表達一個特定的實體或資源的特定的可用性狀態. 一個出席信息節不能(MUST NOT)包含多於一個<show/>元素. <show/>元素不能(MUST NOT)擁有任何屬性. 如果提供了, 這個XML字符數據值必須(MUST)是以下之一(額外的可用性類型可以通過出席信息的適當名字空間來定義):
  • away -- 實體或資源臨時離開.
  • chat -- 實體或資源在聊天中是激活的.
  • dnd -- 實體或資源是忙(dnd = "不要打擾").
  • xa -- 實體或資源是長時間的離開(xa = "長時間離開").
如果沒有提供<show/>元素, 實體被假定是在線和可用的.
狀態
可選的(OPTIONAL)<status/>元素包含XML字符數據表達一個可用性狀態的自然語言描述. 它通常用於聯合show元素以提供可用性狀態的詳細描述(例如, "會議中"). <status/>元素不能(MUST NOT)擁有任何屬性,除了'xml:lang'屬性. <status/>元素可以(MAY)包含多個實例但是每個實例的'xml:lang'屬性值必須各不相同.
優先權
可選的(OPTIONAL)<priority/>元素包含非人類可讀的XML字符數據指明資源的優先級別. 這個值必須(MUST)是一個介於-128和+127之間的數字. 一個出席信息小節不能(MUST NOT)包含超過一個的<priority/>元素. <priority/>元素不能(MUST NOT)擁有任何屬性. 如果沒有優先權被提供,一個服務器應該(SHOULD)認爲優先級是零. 關於即時消息和出席信息系統中節路由的優先級的語義, 參考 處理XML節的服務器規則Server Rules for Handling XML Stanzas(第十一章).

IQ語法

IQ節提供一個結構化的請求-應答機制. 這個機制的基本語義學(例如, 'id'屬性是必需的(REQUIRED))定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920], 然而完成特定用例所需要的特定語義的所有案例定義在擴展名字空間extended namespace(第二章第四節)之中(注意'jabber:client'和'jabber:server'名字空間沒有定義除通用的<error/>子元素之外的任何IQ節子元素). 本文定義了兩個這樣的名字空間,一個用於 名冊管理Roster Management(第七章)而另一個用於 屏蔽通信Blocking Communication(第十章); 無論如何, 一個IQ節可以(MAY)包含符合任何擴展名字空間的結構化信息.

擴展名字空間

因爲在"jabber:client"或"jabber:server"名字空間中定義的三個XML節類型(也包括它們的屬性和子元素)提供了一個基本功能級用於消息和出席信息, XMPP使用XML名字空間來擴展節用於提供額外的功能, 所以一個消息或出席信息節可以(MAY)包含一個或更多可選的子元素表達擴展消息含義的內容(例如, 一個XHTML格式版本的消息主體), 並且一個IQ節可以(MAY)包含一個這樣的子元素. 這個子元素可以(MAY)有任何名字並且可以(MUST)擁有一個'xmlns'名字空間聲明(不同於"jabber:client", "jabber:server", 或"http://etherx.jabber.org/streams")定義所有包含在子元素中的數據.
對於任何特定的擴展名字空間的支持在任何實現中的一部分是可選的(OPTIONAL)(除了在這裏定義的擴展名字空間以外). 如果一個實體不理解這樣一個名字空間, 實體被期望的行爲依賴於這個實體是(1) 接收者 或 (2) 一個正在路由到接收者的實體
接收者: 如果一個接收者接收了一個包含不理解的子元素的節, 它應該(SHOULD)忽略那個特定的XML數據,例如, 它應該(SHOULD)不處理它或不向用戶或相關的應用程序(如果有的話)顯示它. 具體來說:
  • 如果一個實體接收了一個消息或出席信息節包含一個不理解的名字空間, 在節的未知名字空間的這部分應該(SHOULD)被忽略.
  • 如果一個實體接收了一個消息節中僅有的一個子元素是不理解的, 它必須(MUST)忽略整個節.
  • 如果一個實體接收了一個類型"get"或"set"的IQ節包含一個不理解的子元素, 這個實體應該(SHOULD)返回一個類型爲"error"的<service-unavailable/>錯誤條件的IQ節.
路由: 如果一個路由實體(通常是一個服務器)處理一個包含它不理解的子元素的節, 它應該(SHOULD)原封不動地把它轉給接收者而忽略相關的XML數據.

會話的建立

絕大部分基於XMPP的消息和出席信息應用是由一個客戶端-服務器體系結構實現的,爲了參加期望的即時消息和出席信息活動,需要客戶端在服務器上建立一個會話. 無論如何, 在客戶端能夠建立一個即時消息和出席信息會話之前有很多前提必須(MUST)滿足. 它們是:
  1. 流驗證 -- 客戶端在嘗試建立一個會話或發送任何XML節之前必須(MUST)完成[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]中定義的流驗證.
  2. 資源綁定 -- 完成流驗證之後, 一個客戶端必須(MUST)綁定一個資源到流上,使得客戶端的地址符合<user@domain/resource>格式, 然後實體以[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]規定的術語來說就是一個 已連接的資源"connected resource".
如果一個服務器支持會話, 在完成一個[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]定義的流驗證之後它必須(MUST)在它向客戶端聲明的流特性中包含一個符合'urn:ietf:params:xml:ns:xmpp-session'名字空間的<session/>元素:
服務器向客戶端聲明會話確定特性:
   <stream:stream
       xmlns='jabber:client'
       xmlns:stream='http://etherx.jabber.org/streams'
       id='c2s_345'
       from='example.com'
       version='1.0'>
   <stream:features>
     <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
     <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
   </stream:features>
收到需要會話確立的通知之後(並且是在完成資源綁定之後), 客戶端如果想使用即時消息和出席信息功能必須(MUST)建立一個會話; 它向服務器發送一個符合'urn:ietf:params:xml:ns:xmpp-session'名字空間的類型爲"set"幷包含空的<session/>子元素的IQ節以完成這一步驟:
步驟 1: 客戶端向服務器請求會話:
   <iq to='example.com'
       type='set'
       id='sess_1'>
     <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
   </iq>
步驟 2: 服務器通知客戶端會話已經建立:
   <iq from='example.com'
       type='result'
       id='sess_1'/>
建立會話之後, 一個 已連接的資源([XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]術語)就被稱爲一個 激活的資源"active resource".
許多錯誤條件是可能的. 例如, 服務器可能遭遇一個內部條件阻礙了它建立會話, 用戶名或授權身份可能缺乏建立會話的許可, 或同一個名字相關的這個資源ID已經有一個激活的資源.
如果服務器遭到一個內部條件阻礙了它建立會話, 它必須(MUST)返回一個錯誤.
步驟 2 (替代): 服務器應答一個錯誤(內部服務器錯誤):
   <iq from='example.com' type='error' id='sess_1'>
     <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
     <error type='wait'>
       <internal-server-error
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
如果用戶名或資源不被允許建立一個會話, 服務器必須(MUST)返回一個錯誤(例如, 被禁止).
步驟 2 (替代): 服務器應答錯誤(用戶名或資源不被允許建立一個會話):
   <iq from='example.com' type='error' id='sess_1'>
     <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
     <error type='auth'>
       <forbidden
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
如果同一名字已經存在一個激活的資源,服務器必須(MUST) (1) 終止這個激活的資源並允許新請求的會話, 或者 (2) 不允許新申請的會話並繼續激活的資源. 服務器做哪一步取決於具體的實現, 儘管建議的(RECOMMENDED)實現 情景 #1. 在 情景 #1, 服務器應該(SHOULD)發送一個<conflict/>流錯誤給激活的資源, 終止用於這個激活的資源的XML流和相關的TCP連接, 並返回一個類型爲"result" 的IQ節(表示成功)給新申請的會話. 在 情景 #2, 服務器應該(SHOULD)發送一個<conflict/>節錯誤給新申請的會話但是繼續那個連接的XML流使得新申請的會話在發送另一個會話建立申請之前有機會協商出一個不衝突的資源ID.
步驟 2 (替代): 服務器通知現有的激活的資源 資源衝突(情景 #1):
   <stream:error>
     <conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
   </stream:error>
   </stream:stream>
步驟 2 (替代): 服務器通知新申請的的會話資源衝突(情景 #2):
   <iq from='example.com' type='error' id='sess_1'>
     <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
     <error type='cancel'>
       <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
建立一個會話之後, 客戶端應該(SHOULD)按以下描述來發送初始化出席信息並請求它的名冊, 儘管這些動作是可選的(OPTIONAL).
注意: 在允許建立即時消息和出席信息會話之前, 一個服務器可能(MAY)需要先提供帳號. 可能的提供帳號的方法包括由服務器管理員新建帳號以及使用'jabber:iq:register'名字空間進行帶內帳號註冊; 後一個方法超出了本文的範圍, 但是記錄在[JEP-0077](譯者注:這個協議已改名爲 XEP-0077), 由 Jabber Software Foundation [JSF]發行(譯者注:這個組織也已改名爲XSF).

交換消息

交換消息是XMPP的一個基本用途並且隨之而來的是一個用戶生成一個發給另一個實體的消息節. 正如 用於處理XML節的服務器規則Server Rules for Handling XML Stanzas(第十一章)中所定義的 , 發送者的服務器負責遞送消息給預定的接收者(如果接收者在同一個服務器上)或路由消息給接收者的服務器(如果接收者在不同的服務器上).
關於消息節的語法和它們已定義的屬性和子元素信息, 參考 消息語法Message Syntax(第二章第一節).

指明一個預定的接收者

一個即時消息客戶端應該(SHOULD)通過提供一個JID或<message/>節中不同於發送者的'to'屬性來指定一個消息的預定接收者. 如果這個消息是在回覆之前接收到的消息,而接收到的消息是從JID格式爲<user@domain/resource>(例如,在一個聊天會話的上下文中)實體發來的, 這個回覆消息的'to'地址的值應該(SHOULD)是<user@domain/resource>而不是<user@domain>,除非發送者知道(通過出席信息)預定的接收者的資源將不再可用. 如果消息是在任何現存的聊天會話或接收到的消息之外被髮送的,'to'地址的值應該(SHOULD)格式爲<user@domain>而不是<user@domain/resource>.

指定一個消息類型

大家知道, 對於一個消息節來說擁有'type'屬性(它的值代表了消息的會話上下文(參見 Type(第二章第一節第一小節)))是建議的(RECOMMENDED).
以下例子展示一個'type'屬性的合法值:
例子: 一個已定義類型的消息:
   <message
       to='[email protected]'
       from='[email protected]/balcony'
       type='chat'
       xml:lang='en'>
     <body>Wherefore art thou, Romeo?</body>
   </message>

指定一個消息主體

一個消息節可以(MAY)(並且經常會)包含一個<body/>子元素,它的XML字符數據表達消息的主要含義(見 Body(第二章第一節第二小節第二小小節)).
例子: 一個帶主體的消息:
   <message
       to='[email protected]'
       from='[email protected]/balcony'
       type='chat'
       xml:lang='en'>
     <body>Wherefore art thou, Romeo?</body>
     <body xml:lang='cz'>Pro&#x010D;e&#x017D; jsi ty, Romeo?</body>
   </message>

指定一個消息主題

一個消息節可以(MAY)包含一個或多個<subject/>子元素指明消息的主題(見 Subject(第二章第一節第二小節第一小小節)).
例子: 一個帶主題的消息:
   <message
       to='[email protected]'
       from='[email protected]/balcony'
       type='chat'
       xml:lang='en'>
     <subject>I implore you!</subject>
     <subject
         xml:lang='cz'>&#x00DA;p&#x011B;nliv&#x011B; prosim!</subject>
     <body>Wherefore art thou, Romeo?</body>
     <body xml:lang='cz'>Pro&#x010D;e&#x017D; jsi ty, Romeo?</body>
   </message>

指定一個會話線索

一個消息可以(MAY)包含一個<thread/>子元素指定消息處於哪個會話線索, 用於跟蹤會話(見 Thread(第二章第一節第二小節第三小小節)).
例子: 一個帶線索的會話:
   <message
       to='[email protected]/orchard'
       from='[email protected]/balcony'
       type='chat'
       xml:lang='en'>
     <body>Art thou not Romeo, and a Montague?</body>
     <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
   </message>
   <message
       to='[email protected]/balcony'
       from='[email protected]/orchard'
       type='chat'
       xml:lang='en'>
     <body>Neither, fair saint, if either thee dislike.</body>
     <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
   </message>
   <message
       to='[email protected]/orchard'
       from='[email protected]/balcony'
       type='chat'
       xml:lang='en'>
     <body>How cam'st thou hither, tell me, and wherefore?</body>
     <thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
   </message>

交換出席信息

交換出席信息通過使用出席信息節直接和XMPP相關. 無論如何, 我們看到在這裏和消息處理形成一個對比:儘管一個客戶端可以(MAY)通過包含一個'to'地址直接給另一個實體發送出席信息, 通常出席信息通知(例如,不包含'type'的或類型爲"unavailable"的並且沒有'to'地址的出席信息節) 被客戶端發送給它的服務器然後由服務器廣播給任何訂閱了發送實體的出席信息的實體(在RFC 2778 [IMP-MODEL]術語中, 這些實體稱爲訂閱者). 這個廣播模式不適用於和訂閱相關的節或類型爲"error"的出席信息, 而僅適用於以上定義的出席信息通知. (注意: 雖然出席信息可以(MAY)由一個自動化服務代替用戶提供, 通常它還是由用戶的客戶端提供.)
關於出席信息節的語法以及它們的已定義的屬性和子元素的信息, 參考[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920].

客戶端和服務器出席信息職責

初始化出席信息

建立起一個會話之後, 一個客戶端應該(SHOULD)發送初始化出席信息給服務器來通知它的通信可用性.如這裏定義的, 初始化出席信息節 (1) 必須(MUST) 不擁有'to'地址(這表示它是由服務器代替客戶端發送的廣播) 並且 (2) 必須(MUST) 不擁有'type'屬性(者表示擁護的可用性). 在發送初始化出席信息之後, 一個激活的資源被稱爲 可用的資源"available resource".
從一個客戶端接收到初始化出席信息之後, 如果這個用戶沒有一個或更多的已存在的可用資源(如果這個用戶已經有一個或更多可用的資源, 服務器明顯不需要發送出席信息探測, 因爲它已經擁有需要的信息),用戶的服務器必須(MUST)做以下的步驟:
  1. 從用戶的全JID(例如,<[email protected]/resource>)發送出席信息探針(例如, 'type'屬性值爲'probe'的出席信息節)給已被這個用戶訂閱了的所有聯繫人以確定它們是否可用; 這些聯繫人就是那些顯示在用戶的名冊中的JID並且'subscription'屬性值爲"to"或"both"(注意: 用戶的服務器不能(MUST NOT)發送出席信息探針給用戶已經屏蔽入站出席信息通知的聯繫人, 具體的描述在 屏蔽入站出席信息通知Blocking Inbound Presence Notifications(第十章第十節).)
  2. 從用戶的全JID (e.g.,<[email protected]/resource>)廣播初始化出席信息給所有訂閱了該用戶的出席信息的聯繫人; 這些聯繫人就是那些顯示在用戶的名冊中的JID並且'subscription'屬性值爲"from"或"both"(注意: 用戶的服務器不能(MUST NOT)發送出席信息探針給用戶已經屏蔽出站出席信息通知的聯繫人, 具體的描述在 屏蔽出站出席信息通知Blocking Outbound Presence Notifications(第十章第十一節).)
另外, 用戶的服務器必須(MUST)從用戶的新的可用的資源向用戶任何現存的可用的資源(如果有的話)廣播初始化出席信息.
從用戶接收到初始化出席信息之後, 聯繫人的服務器必須(MUST)遞送這個用戶的出席信息節給所有聯繫人的可用資源相應的全JID(<[email protected]/resource>), 但是僅適用於用戶在聯繫人名冊中並且訂閱狀態爲"to"或"both"並且聯繫人的純JID或全JID沒有被屏蔽入站出席信息通知(定義在 屏蔽入站出席信息通知Blocking Inbound Presence Notifications(第十章第十節)).
如果用戶的服務器接收到一個類型爲"error"的出席信息節,而這個節是用來回復服務器代替用戶向聯繫人發送的初始化出席信息, 它不應該(SHOULD NOT)發送更多的出席信息更新給那個聯繫人(直到並且除非它從這個聯繫人接收到一個出席信息節).

出席信息廣播

發送初始化出席信息之後, 用戶可以(MAY)在任何時候更新它的出席信息,方法是在會話期間發送一個沒有'to'地址也沒有'type'屬性的出席信息節或'type'屬性值爲"unavailable"的出席信息節.(注意:一個用戶的客戶端不應該(SHOULD NOT)發送一個出席信息更新來自行廣播用戶出席信息和可用性的改變信息.)
如果出席信息節缺乏'type'屬性(例如, 表達可用性), 用戶的服務器必須(MUST)廣播那個出席信息節的全XML給所有聯繫人(滿足以下三點) (1) 它們在用戶的聯繫人名冊中並且訂閱類型是"from"或"both", (2) 用戶對於這些聯繫人沒有屏蔽出站出席信息通知, 並且 (3) 服務器在用戶的會話期間沒有從它們那裏接收到出席信息錯誤(同樣適用於這個用戶的其他可用的資源).
如果出席信息節的'type'屬性值是"unavailable", 用戶的服務器必須(MUST)廣播那個出席信息節的全XML給所有符合以上描述的實體, 也適用於用戶曾經在會話過程中直接發送了可用出席信息的任何實體(如果用戶還沒來得及直接發送不可用出席信息給那個實體).

出席信息調查

從用戶接收到一個出席信息調查之後, 聯繫人的服務器應該(SHOULD)應答如下:
  1. 如果用戶聯繫人的名冊中的狀態不是 "From", "From + Pending Out", 或 "Both" (定義在 訂閱狀態Subscription States(第九章)), 聯繫人的服務器必須(MUST)返回一個類型爲"error"的出席信息節應答這個出席信息調查 (無論如何, 如果一個服務器從這個服務器的主機名的子域或其他信任的服務接收到一個出席信息調查, 它可以(MAY)提供這個用戶的出席信息給那個實體). 具體來說:
    1. 如果用戶在聯繫人的名冊中的訂閱狀態是 "None", "None + Pending Out", 或 "To" (或根本不在聯繫人的名冊中), 聯繫人的服務器必須(MUST)返回一個<forbidden/>節錯誤應答這個出席信息調查.
    2. 如果用戶在聯繫人的名冊中的訂閱狀態是 "None + Pending In", "None + Pending Out/In", 或 "To + Pending In", 聯繫人的服務器必須(MUST)返回一個<not-authorized/>節錯誤應答這個出席信息調查.
  2. 其次, 如果聯繫人對這個用戶的純JID或全JID屏蔽了出席信息通知(使用缺省列表或激活列表,定義在 屏蔽出站出席信息通知Blocking Outbound Presence Notifications (第十章第十一節)), 服務器不能(MUST NOT)應答這個出席信息調查.
  3. 然後, 如果聯繫人沒有可用的資源, 服務器必須(MUST) 要麼 (1) 應答這個出席信息調查, 向這個用戶發送服務器從聯繫人接收到的最後的類型爲"unavailable"的出席信息節的全XML, 或 (2) 不應答.
  4. 最後, 如果聯繫人至少有一個可用的資源, 服務器必須(MUST)應答這個出席信息調查, 向這個用戶發送服務器從聯繫人的每一個可用的資源收到的最後的沒有'to'屬性的出席信息節的全XML (再一次的,對於每一個會話都要強制服從隱私列表).

直接出席信息

一個用戶可以(MAY)直接發送出席信息給另一個實體 (例如, 一個出席信息節,包含'to'屬性並且值爲另一個實體的JID並且沒有'type'屬性或'type'屬性值爲"unavailable"). 可能出現三種情形:
  1. 如果用戶在已經發送過初始化出席信息廣播之後,發送不可用信息廣播之前,直接發送出席信息給它的名冊中一個訂閱狀態爲"from" 或 "both"的聯繫人, 這個用戶的服務器必須(MUST)路由或遞送這個出席信息節的全XML(服從隱私列表)但是不應該(SHOULD NOT) 根據出席信息廣播修改聯繫人的狀態(例如, 它應該(SHOULD)在任何接下來的由用戶初始化的出席信息廣播包含這個聯繫人的JID).
  2. 如果用戶在已經發送過初始化出席信息廣播之後,發送不可用信息廣播之前,直接發送出席信息給一個不在用戶名冊中的實體並且其訂閱狀態爲"from" 或 "both", 這個用戶的服務器必須(MUST)路由或遞送這個出席信息節的全XML(服從隱私列表)但是不能(MUST NOT) 根據可用性的出席信息廣播來修改這個聯繫人的狀態(例如, 它不能(MUST NOT)在任何接下來的由用戶初始化的可用性的出席信息廣播中包含這個聯繫人的JID); 無論如何, 如果無法從用戶的可用的資源直接發送出席信息, 用戶的服務器必須( MUST)廣播不可用出席信息給那個實體(如果這個用戶還沒有直接發送不可用出席信息給那個實體).
  3. 如果用戶不是在已經發送過初始化出席信息廣播之後,或在發送不可用信息廣播之前,直接發送出席信息(例如, 資源激活了但是還不可用), 用戶的服務器必須(MUST)認爲用戶向其直接發送出席信息的這個實體視爲上述第二種情形中的那個實體,採用相同的處理方式.

不可用出席信息

在和一個服務器結束它的會話之前, 客戶端應該(SHOULD)雅緻地成爲不可用的,發送一個最後的沒有'to'屬性並且'type'屬性值爲"unavailable"的出席信息節(可選的, 最後的出席信息節可以(MAY)包含一個或多個<status/>元素以指明爲什麼用戶不再可用). 無論如何, 用戶的服務器不能(MUST NOT)依賴於從一個可用的資源接收最後的出席信息, 因爲資源可能意外的變成不可用或可能被服務器判定超時. 如果用戶的資源之一因爲任何原因成爲不可用的(包括雅緻的或粗魯的), 用戶的服務器必須(MUST)廣播不可用出席信息給所有如下的聯繫人 (1) 在用戶的名冊中並且訂閱類型爲 "from" 或 "both", (2) 用戶沒有對它們屏蔽出站出席信息的聯繫人, 以及 (3) 用戶會話期間,服務器沒有從它們那裏收到出席信息錯誤的聯繫人; 用戶的服務器也必須(MUST)發送不可用出席信息節給這個用戶的任何其他可用的資源, 以及任何用戶的資源曾經在會話期間直接向其發送過出席信息的實體(如果用戶還沒有直接發送不可用出席信息給那個實體). 在直接發送或廣播不可用出席信息之後發送的任何沒有'type'屬性也沒有'to'屬性的出席信息節必須(MUST)由服務器廣播給所有訂閱者.

出席信息訂閱

一個訂閱請求就是一個'type'屬性值爲"subscribe"的出席信息節. 如果訂閱請求被髮送給一個即時消息聯繫人, 在'to'屬性中提供的JID的格式應該(SHOULD)是<[email protected]>而不是<[email protected]/resource>, 因爲用戶期望的結果通常是從聯繫人的所有資源接收到出席信息, 而不僅是'to'屬性中的特定資源.
一個用戶的服務器不能(MUST NOT)代替用戶自動批准訂閱請求. 所有訂閱申請必須(MUST)直接發給用戶的客戶端, 具體來說就是這一用戶的一個或多個可用的資源. 如果當訂閱申請被用戶的服務器接收到的時候沒有這個用戶的可用資源, 用戶的服務器必須(MUST)保持這個訂閱申請的記錄並且在用戶下次建立一個可用的資源時遞送這個訂閱申請, 直到這個用戶批准或拒絕這個請求. 如果如果當訂閱申請被用戶的服務器接收到的時候這個用戶有多於一個的可用資源, 用戶的服務器必須(MUST)廣播這個訂閱申請給所有可用的資源(根據 處理XML節的服務器規則Server Rules for Handling XML Stanzas(第十一章)). (注意: 如果一個激活的資源還沒有提供初始化出席信息, 服務器不能(MUST NOT)認爲它是可用的並且因而不能(MUST NOT)發送訂閱申請給它.) 無論如何, 如果用戶從一個它已授權可以看到用戶的出席信息的聯繫人那裏收到一個類型爲"subscribe"的出席信息節(例如, 當一個聯繫人重新同步訂閱狀態的時候),用戶的服務器應該(SHOULD)代替用戶自動應答. 另外, 用戶的服務器可以(MAY)基於一個特定實現的法則(例如, 無論何時當用戶的一個新的資源可用的時候, 或在一段特定長度的時間過去之後)選擇重新發送一個未批准的未決訂閱申請給這個聯繫人; 這有助於恢復可能和原始訂閱申請有關的瞬間的,無聲的錯誤.

指明可用性狀態

一個客戶端可以(MAY)使用<show/>元素提供關於可用性狀態的更多信息(參見 Show (第二章第二節第二小節第一小小節)).
例子: 可用性狀態:
   <presence>
 
     <show>dnd</show>
 
   </presence>

指明詳細的可用性狀態信息

通過聯合<show/>元素, 客戶端使用<status/>元素可以(MAY)提供詳細的可用性狀態信息(參見Status (第二章第二節第二小節第二小小節)).
例子: 詳細的狀態信息:
   <presence xml:lang='en'>
     <show>dnd</show>
     <status>Wooing Juliet</status>
     <status xml:lang='cz'>Ja dvo&#x0159;&#x00ED;m Juliet</status>
   </presence>

指明出席信息優先級

客戶端可以(MAY)使用<priority/>元素爲它的資源提供優先級(參見 Priority (第二章第二節第二小節第三小小節)).
例子: 出席信息優先級:
   <presence xml:lang='en'>
     <show>dnd</show>
     <status>Wooing Juliet</status>
     <status xml:lang='cz'>Ja dvo&#x0159;&#x00ED;m Juliet</status>
     <priority>1</priority>
   </presence>

出席信息例子

本章的例子用於闡明上述和出席信息相關的協議. 用戶是 [email protected], 他有一個可用的資源, 資源ID爲 "orchard", 並且他的名冊中有以下這些人:
  • [email protected] (subscription="both" 並且她有兩個可用的資源, 一個資源名爲"chamber" 而另一個資源名爲 "balcony")
例子 1: 用戶發送初始化出席信息:
   <presence/>
例子 2: 用戶的服務器代替用戶發送出席信息調查給 subscription="to" 和 subscription="both" 的聯繫人的可用資源:
   <presence
       type='probe'
       from='[email protected]/orchard'
       to='[email protected]'/>
   <presence
       type='probe'
       from='[email protected]/orchard'
       to='[email protected]'/>
例子 3: 用戶的服務器代替用戶發送初始化出席信息給 subscription="from" 和 subscription="both"的聯繫人的可用資源:
   <presence
       from='[email protected]/orchard'
       to='[email protected]'/>
   <presence
       from='[email protected]/orchard'
       to='[email protected]'/>
例子 4: 聯繫人的服務器代替所有可用的資源應答出席信息調查:
   <presence
       from='[email protected]/balcony'
       to='[email protected]/orchard'
       xml:lang='en'>
     <show>away</show>
     <status>be right back</status>
     <priority>0</priority>
   </presence>
   <presence
       from='[email protected]/chamber'
       to='[email protected]/orchard'>
     <priority>1</priority>
   </presence>
   <presence
       from='[email protected]/pda'
       to='[email protected]/orchard'
       xml:lang='en'>
     <show>dnd</show>
     <status>gallivanting</status>
   </presence>
例子 5: 聯繫人的服務器遞送用戶的初始化出席信息給所有可用的資源或返回錯誤給用戶:
   <presence
       from='[email protected]/orchard'
       to='[email protected]/chamber'/>
   <presence
       from='[email protected]/orchard'
       to='[email protected]/balcony'/>
   <presence
       type='error'
       from='[email protected]'
       to='[email protected]/orchard'>
     <error type='cancel'>
       <gone xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </presence>
例子 6: 用戶直接發送出席信息給另一個不在他的名冊中的用戶:
   <presence
       from='[email protected]/orchard'
       to='[email protected]'
       xml:lang='en'>
     <show>dnd</show>
     <status>courting Juliet</status>
     <priority>0</priority>
   </presence>
例子 7: 用戶發送更新的可用出席信息用於廣播:
   <presence xml:lang='en'>
     <show>away</show>
     <status>I shall return!</status>
     <priority>1</priority>
   </presence>
例子 8: 用戶的服務器僅向一個聯繫人廣播更新的出席信息 (不是那些返回錯誤的聯繫人,也不是那些用戶直接向其發送出席信息的聯繫人):
   <presence
       from='[email protected]/orchard'
       to='[email protected]'
       xml:lang='en'>
     <show>away</show>
     <status>I shall return!</status>
     <priority>1</priority>
   </presence>
例子 9: 聯繫人的服務器遞送更新的出席信息給聯繫人所有可用的資源:
   [to "balcony" resource...]
   <presence
       from='[email protected]/orchard'
       to='[email protected]'
       xml:lang='en'>
     <show>away</show>
     <status>I shall return!</status>
     <priority>1</priority>
   </presence>
   [to "chamber" resource...]
   <presence
       from='[email protected]/orchard'
       to='[email protected]'
       xml:lang='en'>
     <show>away</show>
     <status>I shall return!</status>
     <priority>1</priority>
   </presence>
例子 10: 聯繫人的資源之一廣播最後出席信息:
   <presence from='[email protected]/balcony' type='unavailable'/>
例子 11: 聯繫人的服務器發送不可用出席信息給用戶:
   <presence
       type='unavailable'
       from='[email protected]/balcony'
       to='[email protected]/orchard'/>
例子 12: 用戶發送最後出席信息:
   <presence from='[email protected]/orchard'
             type='unavailable'
             xml:lang='en'>
     <status>gone home</status>
   </presence>
例子 13: 用戶的服務器廣播不可用出席信息給聯繫人,包括用戶直接向其發送出席信息的那個人:
   <presence
       type='unavailable'
       from='[email protected]/orchard'
       to='[email protected]'
       xml:lang='en'>
     <status>gone home</status>
   </presence>
   <presence
       from='[email protected]/orchard'
       to='[email protected]'
       xml:lang='en'>
     <status>gone home</status>
   </presence>

管理訂閱

爲了保護即時消息用戶和任何其他實體的隱私, 出席信息和可用性信息僅向用戶已批准的其他實體披露. 當一個用戶同意其他用戶可以看到它的出席信息, 這個實體被稱爲對於用戶的出席信息有一個訂閱. 訂閱超越了會話; 實際上, 它一直存在直到訂閱者取消訂閱或被訂閱者取消曾經授權的訂閱爲止. 在XMPP中訂閱是通過發送包含特定屬性的出席信息節來管理的.
注意: 在訂閱和名冊之間有重要的交互; 這些定義在 名冊條目和出席信息訂閱的集成Integration of Roster Items and Presence Subscriptions (第八章), 而且讀者必須參考那一章才能完整地理解出席信息訂閱.

請求一個訂閱

對另一個實體的出席信息的訂閱請求是由發送一個類型爲"subscribe"的出席信息節來開始的.
例子: 發送一個訂閱請求:
   <presence to='[email protected]' type='subscribe'/>
關於客戶端和服務器在訂閱請求中的職責, 參考 出席信息訂閱Presence Subscriptions(第五章第一節第六小節).

處理一個訂閱請求

當一個客戶端從另一個實體接收到一個訂閱請求, 它必須(MUST)批准這個請求(發送一個類型爲"subscribed"的出席信息節)或拒絕這個請求(發送一個類型爲"unsubscribed"的出席信息節).
例子: 批准一個訂閱請求:
   <presence to='[email protected]' type='subscribed'/>
例子: 拒絕一個出席信息訂閱的請求:
   <presence to='[email protected]' type='unsubscribed'/>

從另一個實體取消一個訂閱

如果一個用戶想取消一個曾經允許的訂閱請求, 它發送一個類型爲"unsubscribed"的出席信息節.
例子: 取消一個曾經允許的訂閱請求:
   <presence to='[email protected]' type='unsubscribed'/>

取消對於另一個實體的出席信息的訂閱

如果用戶想取消對於另一個實體的出席信息的訂閱, 它發送一個類型爲"unsubscribe"的出席信息節.
例子: 取消對一個實體的出席信息的訂閱:
   <presence to='[email protected]' type='unsubscribe'/>

名冊管理

在XMPP中, 一個人的聯繫人列表被稱爲名冊(roster), 它包括任意數量的特定名冊條目, 每個名冊條目被一個唯一的JID(通常格式是<contact@domain>)所標識. 一個用戶的名冊由用戶的服務器代替用戶儲存從而這個用戶可以從任何資源訪問名冊信息.
注意: 在名冊和訂閱之間有重要的交互; 這些定義在 名冊條目和出席信息訂閱的集成Integration of Roster Items and Presence Subscriptions (第八章), 而且讀者必須參考那一章才能完整地理解名冊管理.

語法和語義

名冊使用IQ節來管理, 具體來說就是符合'jabber:iq:roster'名字空間的<query/>子元素的含義.這個<query/>元素可以(MAY)包含一個或更多<item/>子元素, 每個描述一個唯一的名冊條目或曰 聯繫人"contact".
每個名冊條目的"key"或者說唯一標識符就是一個JID,封裝在<item/>元素的'jid'屬性(它是必需的(REQUIRED))之中. 如果這個條目是和另一個(人類)即時消息用戶相關的,'jid'屬性的值的格式應該(SHOULD)是<user@domain>.
和一個名冊條目相關的出席信息訂閱的狀態從<item/>元素的'subscription'屬性可以得到.這個屬性允許的值包括:
  • "none" -- 這個用戶沒有對這個聯繫人出席信息的訂閱, 這個聯繫人也沒有訂閱用戶的出席信息
  • "to" -- 這個用戶訂閱了這個聯繫人的出席信息, 但是這個聯繫人沒有訂閱用戶的出席信息
  • "from" -- 這個聯繫人訂閱了用戶的出席信息, 但是這個用戶沒有訂閱這個聯繫人的出席信息
  • "both" -- 用戶和聯繫人互相訂閱了對方的出席信息
每個<item/>條目可以(MAY)包含一個'name'屬性, 它設置和這個JID相關的"nickname", 取決於用戶(而不是聯繫人). 'name'屬性的值是不透明的.
每個<item/>條目可以(MAY)包含一個或多個<group/>子元素,用於把名冊條目收集到多個類別之中. <group/>子元素的XML字符數據是不透明的.

商業規則

在一個名冊"set"中一個服務器必須(MUST)忽略任何'to'地址, 並且必須(MUST)認爲任何名冊"set"是應用於發送者的. 爲了更多的安全性, 一個客戶端應該(SHOULD)檢查"roster push"(包含一個名冊條目的類型爲"set"的輸入IQ)的"from"地址以保證它來自一個信任的源; 具體的, 這個節必須(MUST)沒有 'from'屬性(例如, 從服務器隱含的) 或'from'屬性的值匹配用戶的純JID(格式爲<user@domain>)或全JID(格式爲<user@domain/resource>); 否則, 客戶端應該(SHOULD)忽略這個"roster push".

登錄時接收一個人的名冊

在連接到服務器併成爲一個激活的資源之後, 一個客戶端應該(SHOULD)在發送初始化出席信息之前請求名冊(無論如何, 因爲可能不是所有的資源都想接收名冊, 例如, 一個帶寬受限的連接, 客戶端對於名冊的請求是可選的(OPTIONAL)). 如果一個可用的資源在一個會話期間沒有請求名冊, 服務器不能(MUST NOT)向它發送出席信息訂閱以及相關的名冊更新.
例子: 客戶端向服務器請求當前的名冊:
   <iq from='[email protected]/balcony' type='get' id='roster_1'>
     <query xmlns='jabber:iq:roster'/>
   </iq>
例子: 客戶端從服務器收到名冊:
   <iq to='[email protected]/balcony' type='result' id='roster_1'>
     <query xmlns='jabber:iq:roster'>
       <item jid='[email protected]'
             name='Romeo'
             subscription='both'>
         <group>Friends</group>
       </item>
       <item jid='[email protected]'
             name='Mercutio'
             subscription='from'>
         <group>Friends</group>
       </item>
       <item jid='[email protected]'
             name='Benvolio'
             subscription='both'>
         <group>Friends</group>
       </item>
     </query>
   </iq>

增加一個名冊條目

任何時候, 一個用戶可以(MAY)增加一個條目到他或她的名冊.
例子: 客戶端添加一個新的條目:
   <iq from='[email protected]/balcony' type='set' id='roster_2'>
     <query xmlns='jabber:iq:roster'>
       <item jid='[email protected]'
             name='Nurse'>
         <group>Servants</group>
       </item>
     </query>
   </iq>
服務器必須(MUST)在持久信息存儲機構中更新名冊信息,並且也要向這個用戶的所有已請求名冊的可用資源推送這一改變. 這個"名冊推送"包括一個類型爲"set"的IQ節,從服務器發送給客戶端,使用戶的所有可用資源保持和基於服務器的名冊信息的同步.
例子: 服務器 (1) 推送更新的名冊信息給所有已請求名冊的可用資源 並且 (2) 以一個IO結果應答發送的資源:
   <iq to='[email protected]/balcony'
       type='set'
       id='a78b4q6ha463'>
     <query xmlns='jabber:iq:roster'>
       <item jid='[email protected]'
             name='Nurse'
             subscription='none'>
         <group>Servants</group>
       </item>
     </query>
   </iq>
   <iq to='[email protected]/chamber'
       type='set'
       id='a78b4q6ha464'>
     <query xmlns='jabber:iq:roster'>
       <item jid='[email protected]'
             name='Nurse'
             subscription='none'>
         <group>Servants</group>
       </item>
     </query>
   </iq>
   <iq to='[email protected]/balcony' type='result' id='roster_2'/>
正如IQ節類型(定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920])的語義所要求的,每個接收到了名冊推送的資源必須(MUST)應答一個類型爲"result"(或 "error")的IQ節.
例子: 資源應答一個IQ結果給服務器:
   <iq from='[email protected]/balcony'
       to='example.com'
       type='result'
       id='a78b4q6ha463'/>
   <iq from='[email protected]/chamber'
       to='example.com'
       type='result'
       id='a78b4q6ha464'/>

更新名冊條目

更新一個已有的名冊條目(例如, 改變組) 的方法和增加一個新的名冊條目是一樣的, 換言之, 在IQ set 節中發送名冊條目給服務器.
例子: 用戶更新名冊條目(增加組):
   <iq from='[email protected]/chamber' type='set' id='roster_3'>
     <query xmlns='jabber:iq:roster'>
       <item jid='[email protected]'
             name='Romeo'
             subscription='both'>
         <group>Friends</group>
         <group>Lovers</group>
       </item>
     </query>
   </iq>
正如增加一個名冊條目, 當更新一個名冊條目時服務器必須(MUST)在持久信息存儲機構中更新名冊信息, 並且也要初始化一個名冊推送給這個用戶的所有已請求名冊的可用資源.

刪除一個名冊條目

任何時候, 用戶可以(MAY)從他或她的名冊中刪除一個條目,只要發送一個 IQ set 給服務器並確保其'subscription'屬性值爲"remove" (如果從一個客戶端接收到'subscription'屬性的任何其他值,一個兼容的服務器必須(MUST)忽略它).
例子: 客戶端移除一個條目:
   <iq from='[email protected]/balcony' type='set' id='roster_4'>
     <query xmlns='jabber:iq:roster'>
       <item jid='[email protected]' subscription='remove'/>
     </query>
   </iq>
和增加一個名冊條目一樣, 刪除一個名冊條目的時候服務器必須(MUST)在持久信息存儲機構中更新名冊信息,初始化一個名冊推送給這個用戶的所有已請求名冊的可用資源(伴隨着把'subscription'屬性值設爲"remove"),並且發送一個 IQ result 給初始化資源.
關於這個命令的含義的更多信息, 見 移除一個名冊條目並取消所有訂閱Removing a Roster Item and Cancelling All Subscriptions (第八章第六節).

名冊條目和出席信息訂閱的集成

概覽

關於用戶從或向別的聯繫人訂閱出席信息,一個即時消息用戶通常希望在名冊條目和出席信息訂閱之間有某些層次的集成. 本章描述了在XMPP即時消息應用中必須(MUST)支持的那些層次的集成.
有四種主要的訂閱狀態:
  • None -- 這個用戶沒有對這個聯繫人出席信息的訂閱, 這個聯繫人也沒有訂閱用戶的出席信息
  • To -- 這個用戶訂閱了這個聯繫人的出席信息, 但是這個聯繫人沒有訂閱用戶的出席信息
  • From -- 這個聯繫人訂閱了用戶的出席信息, 但是這個用戶沒有訂閱這個聯繫人的出席信息
  • Both -- 用戶和聯繫人互相訂閱了對方的出席信息(例如, 聯合'from' 和 'to')
這些狀態的每一個都被反射到用戶和聯繫人雙方的名冊中, 從而導致持久的訂閱狀態.
在以下的子章節中將敘述這些訂閱狀態如何爲了完成特定的已定義的用例而進行交互. 關於服務器和客戶端處理所有訂閱狀態的細節 (包括處於以上所列的狀態之外的未決狀態)在 訂閱狀態Subscription States(第九章).
服務器不能(MUST NOT)發送出席信息訂閱請求或名冊推送給不可用的資源, 也不能給沒有已請求的名冊的可用資源.
在名冊推送中'from'和'to'地址是可選的(OPTIONAL); 如果包含了, 它們的值應該(SHOULD)是那個會話的資源的全JID. 一個客戶端必須(MUST)以一個類型爲"result"的IQ節來承認每個名冊推送(爲了暫時的原因, 這些節不顯示在以下的例子中但是按[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]定義的IQ語義學的規定它們是必需的).

用戶向聯繫人訂閱

以下描述一個用戶向一個聯繫人訂閱的過程, 包括名冊條目和訂閱狀態之間的互動.
1. 爲了能夠在用戶的客戶端界面處理聯繫人以及在服務器跟蹤訂閱, 用戶的客戶端應該(SHOULD)爲新的名冊條目執行一個"roster set". 這個請求包括髮送一個類型爲'set'的IQ節並擁有符合'jabber:iq:roster'名字空間的<query/>子元素, 它(<query/>元素)再包含一個<item/>子元素來定義新的名冊條目; 這個<item/>元素必須(MUST)擁有一個'jid'屬性, 可以(MAY)擁有一個'name'屬性, 不能(MUST NOT)擁有一個'subscription'屬性, 並且可以(MAY)包含一個或多個<group/>子元素:
   <iq type='set' id='set1'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
2. 作爲結果, 這個用戶的服務器 (1) 必須(MUST)爲這個新的名冊條目初始化一個名冊推送給這個用戶的所有已經請求名冊的可用資源, 其'subscription'屬性的值爲 "none"; 並且 (2) 必須(MUST)以一個 IQ result 應答發送的資源表明名冊設置成功了:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <iq type='result' id='set1'/>
3. 如果用戶想向這個聯繫人請求出席信息的訂閱, 用戶的客戶端必須(MUST)發送一個類型爲'subscribe'的出席信息節給聯繫人:
   <presence to='[email protected]' type='subscribe'/>
4. 作爲結果, 用戶的服務器必須(MUST)初始化第二個名冊推送給這個用戶的所有已經請求名冊的可用資源,把這個聯繫人設置成'none'訂閱狀態的未決子狀態; 這個未決子狀態是由名冊條目中包含的ask='subscribe'屬性所指示的:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           ask='subscribe'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
注意: 如果用戶在發送訂閱請求之前沒有新建一個名冊條目, 服務器必須(MUST)現在代替用戶新建一個,然後發送一個名冊推送給這個用戶的所有已經請求名冊的可用資源, 不含以上所示的'name'屬性和<group/>子元素.
5. 用戶的服務器也必須(MUST)把這個類型爲"subscribe"的出席信息節的'from'地址設置爲用戶的純JID(例如, <[email protected]>)(如果用戶提供了設置爲用戶的全JID的'from'地址, 服務器應該(SHOULD)移除資源ID). 如果聯繫人和用戶在不同的主機上, 用戶的服務器必須(MUST)路由這個出席信息節到聯繫人的服務器來遞送到這個聯繫人(這種情形的假定貫穿本文; 無論如何, 如果聯繫人在同一臺主機, 那麼服務器可以簡單地直接遞送出席信息節):
   <presence
       from='[email protected]'
       to='[email protected]'
       type='subscribe'/>
注意:如果用戶的服務器從聯繫人的服務器收到了一個類型爲"error"的出席信息節, 它必須(MUST)這個錯誤節給用戶, 用戶的客戶端可以(MAY)確定那個錯誤是否對於上次用戶發出的"subscribe"類型的出席信息節(例如, 通過跟蹤'id'屬性)的應答,然後選擇重新發送"subscribe"請求還是發送一個"unsubscribe"類型的出席信息節給聯繫人以恢復到它的上一個狀態.
6. 接收到指向聯繫人的"subscribe"類型的出席信息節之後, 這個聯繫人的服務器必須(MUST)決定是否至少有一個已請求名冊的聯繫人的可用資源. 如果是, 它必須(MUST)遞送這個訂閱請求給這個聯繫人(如果不是, 聯繫人的服務器必須(MUST)離線存儲這個訂閱請求用於遞送 when this condition is next met; 通常這是通過增加一個關於這個聯繫人的名冊條目到用戶名冊中來實現的, 伴隨着一個 "None + Pending In"的狀態(定義在 訂閱狀態Subscription States (第九章)), 無論如何一個服務器不應該(SHOULD NOT)在那種狀態下推送或遞送名冊條目給聯繫人). 不論何時訂閱請求被遞送到了, 聯繫人必須決定是否批准它(根據聯繫人的配置選項, 聯繫人的客戶端可以(MAY)批准或拒絕訂閱請求而無需向聯繫人顯示). 這裏我們假定這個 "happy path", 即聯繫人批准了訂閱請求(替代的拒絕訂閱請求的流程定義在第八章第二節第一小節). 在這種情形下, 這個聯繫人的客戶端 (1) 應該(SHOULD) 執行一個roster set 爲這個用戶指明期望的暱稱和組(如果有的話); 並且 (2) 必須(MUST)發送一個"subscribed"類型的出席信息節給這個用戶以批准這個訂閱請求.
   <iq type='set' id='set2'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence to='[email protected]' type='subscribed'/>
7. 作爲結果, 聯繫人的服務器 (1) 必須(MUST) 初始化一個名冊推送給所有聯繫人已請求名冊的可用資源, 包含一個關於那個用戶的名冊條目,並且其訂閱狀態爲'from'(甚至聯繫人不執行roster set,服務器也必須(MUST)發送它); (2) 必須(MUST)返回一個 IQ result 給發送的資源表示名冊設置(roster set)成功了; (3) 必須(MUST)路由這個"subscribed"類型的出席信息節給用戶, 首先把'from'地址設爲聯繫人的純JID(<[email protected]>); 然後 (4) 必須(MUST)從所有聯繫人的可用資源向用戶發送可用的出席信息:
   <iq type='set' to='[email protected]/resource'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='from'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <iq type='result' to='[email protected]/resource' id='set2'/>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='subscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'/>
注意: 如果聯繫人的服務器從用戶的服務器收到一個"error"類型的出席信息節, 它必須(MUST)遞送這個錯誤節給聯繫人, 聯繫人的客戶端可以(MAY)確定那個錯誤是否對於上次聯繫人發出的"subscribe"類型的出席信息節(例如, 通過跟蹤'id'屬性)的應答,然後選擇重新發送"subscribe"請求還是發送一個"unsubscribe"類型的出席信息節給用戶以恢復到它的上一個狀態.
8. 接收到一個指向用戶的"subscribed"類型的出席信息節之後, 用戶的服務器必須(MUST)首先檢查在用戶名冊中的這個聯繫人的狀態是: (a) subscription='none' and ask='subscribe' 還是 (b) subscription='from' and ask='subscribe'. 如果聯繫人不是以上述的狀態在用戶的名冊中,用戶的服務器必須(MUST)安靜的忽略這個"subscribed"類型的出席信息節(例如, 服務器不能(MUST NOT)路由它到用戶, 修改用戶的名冊, 或生成一個名冊推送到用戶的可用資源). 如果聯繫人以上述任何一種狀態存在於用戶的名冊中, 用戶的服務器 (1) 必須(MUST)從聯繫人向用戶遞送這個"subscribed"類型的出席信息節; (2)必須(MUST)初始化一個名冊推送給所有已請求名冊的這個用戶的可用資源,包含一個關於這個聯繫人的更新的名冊條目,同時其'subscription'屬性值設置爲"to"; 並且 (3) 必須(MUST)從每一個聯繫人的可用資源向每一個用戶的可用資源遞送服務器接收到的可用的出席信息節:
   <presence
       to='[email protected]'
       from='[email protected]'
       type='subscribed'/>
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='to'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]/resource'
       to='[email protected]/resource'/>
9. 接收到"subscribed"類型的出席信息節之後, 用戶應該(SHOULD)承認接收到了訂閱狀態通知,

要麼發送一個"subscribe"類型的出席信息節給聯繫人證實它, 要麼發送一個"unsubscribe"類型的出席信息節給聯繫人否認它;這個步驟不一定影響訂閱狀態(見 訂閱狀態Subscription States(第九章)的細節), 但是會讓用戶用戶的服務器知道它必須(MUST)不再發送訂閱狀態改變通知給用戶(見第九章第四節).

從用戶這方面看, 現在存在一個向聯繫人的出席信息的訂閱; 從聯繫人的方面看, 現在存在一個從用戶的來的訂閱.

替代流程: 聯繫人拒絕訂閱請求

以上活動流程展示了關於用戶向聯繫人的訂閱請求的 "happy path" . 如果聯繫人拒絕用戶的訂閱請求,那麼主要的替代流程如下所述.
1. 如果聯繫人想拒絕這個請求, 聯繫人的客戶端必須(MUST)發送一個"unsubscribed"類型的出席信息節給用戶(取代第八章第二節中步驟6發送的 "subscribed"類型的出席信息節):
   <presence to='[email protected]' type='unsubscribed'/>
2. 作爲結果, 聯繫人的服務器必須(MUST)路由這個"unsubscribed"類型的出席信息節給用戶,首先把'from'地址設爲聯繫人的純JID(<[email protected]>):
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
注意: 如果聯繫人的服務器之前把用戶添加到了聯繫人的名冊中用來跟蹤, 這時它必須(MUST)移除這個相關的條目.
3. 接收到指向用戶的"unsubscribed"類型出席信息節之後, 用戶的服務器 (1) 必須(MUST)地送那個出席信息節給用戶 並且 (2) 必須(MUST) 初始化一個名冊推送給這個用戶的所有已請求名冊的可用資源, 包含一個關於這個聯繫人的一個更新條目,其'subscription'屬性設爲"none"並且沒有'ask'屬性:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
4. 接收到類型爲"unsubscribed"出席信息節之後, 用戶應該(SHOULD)承認收到訂閱狀態通知, 要麼發送一個"unsubscribe"類型的出席信息節給聯繫人證實它, 要麼發送一個"subscribe"類型的出席信息節給聯繫人否認它; 這一步驟不影響訂閱狀態(見 訂閱狀態Subscription States(第九章)的細節), 但是讓用戶的服務器知道它必須(MUST)不再發送訂閱狀態改變的通知給用戶(見第九章第四節).
作爲這一行爲的結果, 聯繫人現在在用戶的名冊中, 狀態爲"none",而用戶根本不在聯繫人的名冊中.

建立一個相互的訂閱

用戶和聯繫人可以在前述"happy path"的基礎上建立一個相互的訂閱(例如, 一個"both"的訂閱類型). 流程如下.
1. 如果聯繫人想建立一個相互的訂閱, 聯繫人必須(MUST)發送一個訂閱請求給用戶(視聯繫人的配置選項而定, 聯繫人的客戶端可以(MAY)自動發送它):
   <presence to='[email protected]' type='subscribe'/>
2. 作爲結果, 聯繫人的服務器 (1) 必須(MUST)初始化一個名冊推送給聯繫人的所有已請求名冊的可用資源, 伴隨着用戶仍在'from'訂閱狀態但同時有一個未決的'to'訂閱狀態(通過在名冊條目中包含一個ask='subscribe'的屬性來指示); 並且 (2) 必須(MUST)路由這個"subscribe"類型的出席信息節給用戶(先把'from'地址設爲聯繫人的純JID(<[email protected]>)):
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='from'
           ask='subscribe'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='subscribe'/>
注意: 如果聯繫人的服務器從用戶的服務器收到一個"error"類型的出席信息節, 它必須(MUST)遞送這個錯誤節給聯繫人, 它的客戶端可以(MAY)確定這個錯誤是用來應答上次發送的"subscribe"類型的出席信息節(換言之, 通過跟蹤'id'屬性) 並且選擇重發這個"subscribe"請求還是發送一個"unsubscribe"類型的出席信息節給用戶以把名冊恢復到它的前一個狀態.
3. 接收到指向用戶的"subscribe"類型出席信息節之後, 用戶的服務器必須確定是否至少有一個已請求名冊可用資源. 如果是, 用戶的服務器必須(MUST)遞送這個訂閱請求給用戶(如果不是, 它必須(MUST)離線存儲這個訂閱請求等這種情形再次發生時遞送). 無論何時訂閱請求被遞送了, 用戶必須決定是否批准它(視用戶的配置選項而定, 用戶的客戶端可以(MAY)批准或拒絕這個訂閱請求而不需要向用戶顯示). 這裏我們假定這是"happy path",用戶批准了訂閱請求(替代的拒絕訂閱請求的流程定義在第八章第三節第一小節). 在這種情形下, 用戶的客戶端必須(MUST)發送一個"subscribed"類型的出席信息節給聯繫人表示批准了訂閱請求.
   <presence to='[email protected]' type='subscribed'/>
4. 作爲結果, 用戶的服務器 (1) 必須(MUST)初始化一個名冊推送給用戶的所有已請求名冊的可用資源, 包含一個關於聯繫人的名冊條目,其'subscription'屬性設爲"both"; (2) 必須(MUST)路由這個"subscribed"類型的出席信息節給聯繫人(先把'from'地址設爲用戶的純JID<[email protected]>)); 並且 (3) 必須(MUST)向聯繫人發送它從用戶的每個可用資源收到的最近一次出席信息節的全XML(不帶'to'屬性)(強制每個會話遵守隱私列表):
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='both'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='subscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'/>
注意: 如果用戶的服務器從聯繫人的服務器接收到一個"error"類型的出席信息節, 它必須(MUST)遞送這個錯誤節給用戶, 它客戶端可以(MAY)確定這個錯誤是用來應答上次發出去的"subscribed"類型的出席信息節(換言之, 通過跟蹤'id'屬性) 並且選擇重發這個訂閱請求還是發送一個"unsubscribed"類型的出席信息節給聯繫人以把名冊恢復到上次的狀態.
5. 接收到指向聯繫人的"subscribed"類型的出席信息節之後, 聯繫人的服務器必須(MUST)首先檢查用戶在聯繫人的名冊中的狀態是否以下狀態之一: (a) subscription='none' and ask='subscribe' 或 (b) subscription='from' and ask='subscribe'. 如果用戶不是以上述兩種狀態之一存在於聯繫人的名冊中, 聯繫人的服務器必須(MUST)安靜地忽略這個"subscribed"類型的出席信息節(例如, 它不能(MUST NOT)路由它給聯繫人, 修改聯繫人的名冊, 或生成一個名冊推送給聯繫人的可用資源). 如果用戶以上述兩種狀態之一存在於聯繫人的名冊中, 聯繫人的服務器 (1) 必須(MUST)從用戶向聯繫人遞送這個"subscribed"類型的出席信息節; (2) 必須(MUST)初始化一個名冊推送給這個聯繫人的所有已請求名冊的可用資源, 包含一個關於這個用戶的更新的名冊條目,其'subscription'屬性值設爲"both"; 並且 (3) 必須(MUST)向這個聯繫人的每個可用資源遞送它從這個用戶的每個資源收到的可用出席信息節:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='subscribed'/>
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='both'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]/resource'
       to='[email protected]/resource'/>
6. 收到"subscribed"類型的出席信息節之後, 聯繫人應該(SHOULD)承認收到訂閱請求通知,要麼發送一個"subscribe"的出席信息節給用戶證實它,要麼發送一個"unsubscribe"類型的出席信息節給用戶否認它; 這一步驟不影響訂閱狀態(細節見 訂閱狀態Subscription States(第九章)), 但是讓聯繫人的服務器知道它必須(MUST)不再發送訂閱狀態變更通知給聯繫人(見第九章第四節).
用戶和聯繫人現在有了對雙方的出席信息的一個相互訂閱 -- 換言之, 這個訂閱類型爲 "both".

替代流程: 用戶拒絕訂閱請求

以上活動流程展示了關於聯繫人對用戶的訂閱請求的 "happy path". 如果用戶拒絕了聯繫人的訂閱請求,其主要流程如下.
1. 如果用戶想拒絕請求, 用戶的客戶端必須(MUST)發送一個"unsubscribed"類型的出席信息節給聯繫人(替代第八章第三節中的第三步中所發送的"subscribed"類型出席信息節):
   <presence to='[email protected]' type='unsubscribed'/>
2. 作爲結果, 用戶的服務器必須(MUST)路由這個"unsubscribed"類型的出席信息節給聯繫人(首先把'from'地址設爲用戶的純JID(<[email protected]>)):
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
3. 接收到指向聯繫人的"unsubscribed"類型的出席信息節之後, 聯繫人的服務器 (1) 必須(MUST)遞送這個出席信息節給聯繫人; 並且 (2) 必須(MUST)初始化一個名冊推送給這個聯繫人的所有已請求名冊的可用資源, 包含關於這個用戶的更新的名冊條目,其'subscription'屬性的值設爲"from"並且沒有'ask'屬性:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='from'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
4. 接收到"unsubscribed"類型的出席信息節之後, 聯繫人應該(SHOULD)承認收到那個訂閱狀態通知,要麼向用戶發送一個"unsubscribe"類型的出席信息節以證實它,要麼向用戶發送一個"subscribe"類型的出席信息以否認它; 這個步驟不會影響訂閱狀態(詳見 訂閱狀態Subscription States(第九章)),但是讓聯繫人的服務器知道它必須(MUST)不再發送訂閱狀態變更通知給聯繫人(見第九章第四節).
作爲這一活動的結果, 訂閱狀態沒有任何改變; 換言之, 聯繫人在用戶的名冊中的訂閱狀態爲"to"並且用戶在聯繫人的名冊中的訂閱狀態爲"from".

取消訂閱

在訂閱了一個聯繫人的出席信息之後的任何時候, 一個用戶可以(MAY)取消訂閱. 在所有實例中用戶發送來執行這一動作的XML是相同的, 接下來的訂閱狀態根據發出取消訂閱命令時獲得的訂閱狀態的情況而不同. 兩種可能的情節描述如下.

情形 #1: 當訂閱不是相互的時候取消訂閱

在第一種情形, 用戶有一個向聯繫人的出席信息的訂閱但是聯繫人沒有對用戶的出席信息的訂閱(換言之, 訂閱不是相互的).
1. 如果用戶想取消對聯繫人的出席信息的訂閱, 用戶必須(MUST)發送一個"unsubscribe"類型的出席信息節給聯繫人:
   <presence to='[email protected]' type='unsubscribe'/>
2. 作爲一個結果, 用戶的服務器 (1) 必須(MUST) 發送一個名冊推送給這個用戶的所有已請求名冊的可用資源,包含一個關於這個聯繫人的更新名冊條目,其'subscription'屬性設爲"none"; 並且 (2) 必須(MUST)路由這個"unsubscribe"類型的出席信息節給聯繫人(首先把'from'地址設爲用戶的純JID(<[email protected]>)):
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribe'/>
3. 接收到指向聯繫人的"unsubscribe"類型出席信息節之後, 聯繫人的服務器 (1) 必須(MUST)初始化一個名冊推送給這個聯繫人的所有已請求名冊的可用資源, 包含一個關於這個用戶的名冊條目,其'subscription'屬性值設爲"none" (如果聯繫人不可用或未曾請求名冊, 聯繫人的服務器必須(MUST)修改名冊條目並在下次聯繫人請求名冊時發送那個已修改的條目); 並且 (2) 必須(MUST)遞送這個"unsubscribe"狀態改變通知給聯繫人:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribe'/>
4. 接收到"unsubscribe"類型的出席信息節之後, 聯繫人應該(SHOULD)承認收到那個訂閱狀態通知,要麼發送一個"unsubscribed"類型的出席信息節給用戶以證實它,要麼發送一個"subscribed"類型的出席信息節給用戶否認它; 這個步驟不影響訂閱狀態(詳見 訂閱狀態Subscription States (第九章)), 但是讓聯繫人的服務器知道它必須(MUST)不再發送訂閱狀態變更通知給聯繫人(見第九章第四節).
5. 聯繫人的服務器接着 (1) 必須(MUST)發送一個"unsubscribed"類型的出席信息節給用戶;並且 (2) 應該(SHOULD)向用戶發送從這個聯繫人的所有可用資源收到的不可用出席信息:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
6. 當用戶的服務器收到類型爲"unsubscribed" 和 "unavailable"的出席信息節, 它必須(MUST)遞送它們給用戶:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
7. 接收到"unsubscribed"類型的出席信息節之後, 用戶應該(SHOULD)承認收到那個訂閱狀態變更通知,要麼向聯繫人發送一個"unsubscribe"類型的出席信息節以證實它,要麼向聯繫人發送一個"subscribe"的出席信息節以否認它;這步驟不影響訂閱狀態(詳見 訂閱狀態Subscription States(第九章)), 但是讓用戶的服務器知道它必須(MUST)不在發送訂閱狀態變更通知給用戶(見第九章第四節).

情形 #2: 當訂閱是相互的時候取消訂閱

在第二種情形下, 用戶有一個向聯繫人的出席信息的訂閱並且聯繫人也有一個向用戶的出席信息的訂閱(換言之, 訂閱是相互的).
1. 如果用戶想從聯繫人的出席信息取消訂閱, 用戶必須(MUST)發送一個"unsubscribe"類型的出席信息節給聯繫人:
   <presence to='[email protected]' type='unsubscribe'/>
2. 作爲一個結果, 用戶的服務器 (1) 必須(MUST)發送一個名冊推送給這個用戶的所有已請求名冊的可用資源,包含一個關於這個聯繫人的更新名冊條目,其'subscription'屬性值設爲"from"; 並且 (2) 必須(MUST)路由這個"unsubscribe"類型的出席信息節給這個聯繫人( 首先把'from'地址設爲這個用戶的純 JID(<[email protected]>):
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='from'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribe'/>
3. 接收到指向聯繫人的"unsubscribe"類型的出席信息節之後, 聯繫人的服務器 (1) 必須(MUST)初始化一個名冊推送給這個聯繫人的所有已請求名冊的可用資源, 包含一個關於這個用戶的名冊條目,其'subscription'屬性值設爲"to" (如果聯繫人不可用或未曾請求名冊, 聯繫人的服務去必須(MUST)修改這個名冊條目並且等下次聯繫人請求名冊的時候再發送這個修改過的名冊條目); 並且 (2) 必須(MUST)遞送這個"unsubscribe"狀態變更通知給聯繫人:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='to'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribe'/>
4. 接收到這個"unsubscribe"類型的出席信息節之後, 聯繫人應該(SHOULD)承認收到了那個訂閱狀態通知,要麼向用戶發送一個"unsubscribed"類型的出席信息節以證實它,要麼向用戶發送一個"subscribed"類型的出席信息節以否認它; 這個步驟不影響訂閱狀態(詳見 訂閱狀態Subscription States(第九章)), 但是讓聯繫人的服務器知道它必須(MUST)不再發送訂閱狀態變更通知給聯繫人(見第九章第四節).
5. 聯繫人的服務器然後 (1) 必須(MUST)發送一個"unsubscribed"類型的出席信息節給用戶; 並且 (2) 應該(SHOULD)向用戶發送它從聯繫人的所有可用資源收到的不可用出席信息:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
6. 當用戶的服務器收到"unsubscribed"和"unavailable"類型的出席信息節, 它必須(MUST)遞送它們給用戶:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
7. 接收到"unsubscribed"類型的出席信息節之後, 用戶應該(SHOULD)承認收到了那個訂閱狀態的通知,要麼向聯繫人發送一個"unsubscribe"類型的出席信息節以證實它,要麼向聯繫人發送一個"subscribe"類型的出席信息節以否認它;這個步驟不影響訂閱狀態(詳見 訂閱狀態Subscription States(第九章)), 但是讓用戶的服務器知道它必須(MUST)不在發送訂閱狀態變更通知給用戶(見第九章第四節).
注意: 顯然這不會導致名冊條目從用戶的名冊移除, 並且聯繫人仍然有一個對用戶的出席信息的訂閱.爲了完全取消雙向的訂閱並完全從用戶的名冊中移除名冊條目, 用戶應該(SHOULD)使用subscription='remove'(定義在 移除一個名冊條目並取消所有訂閱項Removing a Roster Item and Cancelling All Subscriptions (第八章第六節))更新名冊條目.

取消一個訂閱項

在批准來自一個用戶的任何訂閱請求之後的任何時候, 一個聯繫人可以(MAY)取消那個訂閱項. 聯繫人在所有實例中執行這個動作中發送的XML是相同的, 接下來的訂閱狀態根據取消命令發出當時所獲得的訂閱狀態而有所不同. 所有可能的情節描述如下.

情形 #1: 當訂閱不是相互的時候取消訂閱項

在第一種情形下, 用戶有一個對聯繫人的出席信息的訂閱但是聯繫人沒有對於用戶的出席信息的訂閱(換言之, 訂閱還不是相互的).
1. 如果聯繫人想取消用戶的訂閱項, 聯繫人必須(MUST)發送一個"unsubscribed"類型的出席信息節給用戶:
   <presence to='[email protected]' type='unsubscribed'/>
2. 作爲一個結果, 聯繫人的服務器 (1) 必須(MUST)發送一個名冊推送給這個聯繫人的所有已請求名冊的可用資源, 包含一個關於這個用戶的更新的名冊條目,其'subscription'屬性值設爲"none"; (2) 必須(MUST)路由這個"unsubscribed"類型的出席信息節給用戶(首先把'from'地址設爲聯繫人的純JID(<[email protected]>)); 並且 (3) 應該(SHOULD)向用戶發送它從聯繫人的所有可用資源收到的不可用出席信息:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
3. 接收到指向用戶的"unsubscribed"類型的出席信息節之後, 用戶的服務器 (1) 必須(MUST)初始化一個名冊推送給這個用戶的所有已請求名冊的可用資源, 包含一個關於這個聯繫人的名冊條目更新,其'subscription'屬性值設爲"none"(如果用戶不可用或未曾請求名冊, 用戶的服務器必須(MUST)修改這個名冊條目並且等下次用戶請求名冊的時候發送修改過的名冊條目); (2) 必須(MUST)遞送這個"unsubscribed"狀態改變通知給這個用戶的所有可用資源; 並且 (3) 必須(MUST)向這個用戶的所有可用資源遞送不可用出席信息:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
4. 接收到"unsubscribed"類型的出席信息節之後, 用戶應該(SHOULD)承認收到了那個訂閱狀態通知,要麼向聯繫人發送一個"unsubscribe"出席信息節以證實它,要麼向聯繫人發送一個"subscribe"類型的出席信息節以否認它;這個步驟不影響訂閱狀態(詳見 訂閱狀態Subscription States(第九章)), 但是讓服務器知道它必須(MUST)不再發送訂閱狀態變更通知給用戶(見第九章第四節).

情形 #2: 當訂閱項是相互的時候取消

在這種情形下, 用戶有一個對聯繫人的出席信息的訂閱並且聯繫人也有一個對用戶的出席信息的訂閱(換言之, 訂閱是相互的).
1. 如果聯繫人想取消用戶的訂閱, 聯繫人必須(MUST)發送一個"unsubscribed"類型的出席信息節給用戶:
   <presence to='[email protected]' type='unsubscribed'/>
2. 作爲結果, 聯繫人的服務器 (1) 必須(MUST)發送一個名冊推送給這個聯繫人的所有已請求名冊的可用資源, 包含關於這個用戶的一個更新的名冊條目,其'subscription'屬性值設爲"to"; (2) 必須(MUST)路由這個"unsubscribed"類型的出席信息節給用戶(首先把'from'地址設爲聯繫人的純JID(<[email protected]>)); 並且 (3) 應該(SHOULD)向這個用戶的所有可用資源發送它從聯繫人的所有可用資源收到的不可用出席信息:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='to'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
3. 接收到指向用戶的"unsubscribed"類型出席信息節之後, 用戶的服務器 (1) 必須(MUST)初始化一個名冊推送給這個用戶的所有已請求名冊的可用資源, 包含關於這個聯繫人的更新的名冊條目,其'subscription'屬性值設爲"from"(如果這個用戶不可用或未曾請求名冊, 用戶的服務器必須(MUST)修改這個名冊條目並且等下次用戶請求名冊的時候發送修改過的條目給它); 並且(2) 必須(MUST)遞送這個"unsubscribed"狀態變更通知給用戶的所有可用資源; 並且 (3) 必須(MUST)向這個用戶的所有可用資源遞送這個不可用出席信息:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='from'
           name='MyContact'>
         <group>MyBuddies</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
4. 接收到這個"unsubscribed"類型的出席信息節之後, 用戶應該(SHOULD)承認收到了那個訂閱狀態通知,要麼向聯繫人發送一個"unsubscribe"類型的出席信息節以證實它,要麼向聯繫人發送一個"subscribe"類型的出席信息節以否認它; 這一步驟不影響訂閱狀態(詳見 訂閱狀態Subscription States (第九章)), 但是讓用戶的服務器知道它必須(MUST)不再發送訂閱狀態變更通知給用戶(見第九章第四節).
注意: 顯然這不會使得名冊條目從聯繫人的名冊中移除, 並且聯繫人仍然有一個對用戶的出席信息的訂閱. 爲了完全雙向的取消一個相互的訂閱並且從聯繫人的名冊中完全移除這個名冊條目, 聯繫人應該以subscription='remove'(定義在 移除一個名冊條目並取消所有訂閱項Removing a Roster Item and Cancelling All Subscriptions (第八章第六節))更新名冊條目.

移除一個名冊條目並取消所有訂閱項

因爲在雙向完整移除一個名冊條目和取消所有訂閱的過程中可能有很多步驟, 名冊管理協議包含一個"shortcut"方法來做這件事. 無論當前的訂閱狀態是什麼, 這個過程可以通過發送一個roster set(包含一個用於這個聯繫人的條目,其'subscription'屬性值設爲"remove")來初始化:
   <iq type='set' id='remove1'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='remove'/>
     </query>
   </iq>
當用戶從他或她的名冊中移除一個聯繫人(通過把'subscription'屬性值設爲"remove"), 用戶的服務器 (1) 必須(MUST)自動取消用戶和聯繫人之間的任何現存的出席信息訂閱項(包括相應的'to'和'from'); (2) 必須(MUST)從用戶的名冊移除這個名冊條目並且通知這個用戶的所有已請求名冊的可用資源這個名冊條目被移除了; (3) 必須(MUST)通知初始化的資源移除成功了; 並且 (4) 應該(SHOULD)向聯繫人發送它從這個用戶的所有可用資源收到的不可用出席信息:
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribe'/>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='remove'/>
     </query>
   </iq>
   <iq type='result' id='remove1'/>
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
收到"unsubscribe"類型的出席信息後, 聯繫人的服務器 (1) 必須(MUST)初始化一個名冊推送給這個聯繫人的所有已請求名冊的可用資源,包含關於這個用戶的一個更新的名冊條目,其'subscription'屬性值設爲"to"(如果這個聯繫人不可用或未曾請求名冊, 聯繫人的服務器必須(MUST)修改這個名冊條目並且等下次聯繫人請求名冊的時候發送這個修改過的條目給它); 並且 (2) 也必須(MUST)遞送這個"unsubscribe"狀態變更通知給這個聯繫人的所有可用資源:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='to'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribe'/>
收到這個"unsubscribed"類型的出席信息節之後, 聯繫人的服務器 (1) 必須(MUST)初始化一個名冊推送給這個聯繫人的所有已請求名冊的可用資源,包含一個關於這個用戶的更新的名冊條目,其'subscription'屬性值設爲"none"(如果這個聯繫人不可用或未曾請求名冊, 聯繫人的服務器必須(MUST)修改名冊條目並且等下次聯繫人請求名冊的時候把修改過的條目發送給它); 並且 (2) 也必須(MUST)遞送這個"unsubscribe"狀態改變通知給這個聯繫人的所有可用資源:
   <iq type='set'>
     <query xmlns='jabber:iq:roster'>
       <item
           jid='[email protected]'
           subscription='none'
           name='SomeUser'>
         <group>SomeGroup</group>
       </item>
     </query>
   </iq>
   <presence
       from='[email protected]'
       to='[email protected]'
       type='unsubscribed'/>
接收到指向聯繫人的"unavailable"出席信息節之後, 聯繫人的服務器必須(MUST)遞送這個不可用出席信息給這個用戶的所有可用資源:
   <presence
       from='[email protected]/resource'
       to='[email protected]'
       type='unavailable'/>
注意: 當用戶從用戶的名冊中移除聯繫人的時候, 這個聯繫人的名冊最後狀態是用戶仍然在聯繫人名冊中但是訂閱狀態爲"none"; 爲了完全移除關於這個用戶的名冊條目, 聯繫人也需要發送一個名冊移除請求.

訂閱狀態

本章提供關於訂閱狀態以及和訂閱相關的出席信息節(換言之,類型爲"subscribe", "subscribed", "unsubscribe",和 "unsubscribed"的出席信息節)的服務器處理過程的詳細信息.

已定義的狀態

有九種可能的訂閱狀態, 從用戶的(不是聯繫人的)角度描述如下:
  1. "None" = 聯繫人和用戶互相沒有被對方訂閱, 並且也都沒有從對方那裏請求一個訂閱
  2. "None + Pending Out" = 聯繫人和用戶互相沒有被對方訂閱, 用戶已經向聯繫人發送了一個訂閱請求但還沒有收到回覆
  3. "None + Pending In" = 聯繫人和用戶互相沒有被對方訂閱, 聯繫人已經向用戶發送了一個訂閱請求但還沒有收到回覆(注意: 在這種狀態下聯繫人的服務器不應該(SHOULD NOT)推送或遞送名冊條目, 但是應該(SHOULD)等待,直到聯繫人的訂閱請求已經從用戶那裏得到批准)
  4. "None + Pending Out/In" = 聯繫人和用戶互相沒有被對方訂閱, 聯繫人已經向用戶發送了一個訂閱請求但還沒有收到回覆, 用戶已經向聯繫人發送了一個訂閱請求但還沒有收到回覆
  5. "To" = 用戶已訂閱聯繫人(單向)
  6. "To + Pending In" = 用戶已訂閱聯繫人, 聯繫人已經向用戶發送了一個訂閱請求但還沒有收到回覆
  7. "From" = 聯繫人已訂閱用戶(單向)
  8. "From + Pending Out" = 聯繫人已訂閱用戶(單向), 用戶已經向聯繫人發送了一個訂閱請求但還沒有收到回覆
  9. "Both" = 用戶和聯繫人互相被對方訂閱了(雙向)

出站出席信息訂閱節的服務器處理過程

出站出席信息訂閱節使用戶能管理他或她對聯繫人的出席信息的訂閱(通過"subscribe"和"unsubscribe"類型), 並且管理聯繫人對用戶的出席信息的訪問(通過"subscribed"和"unsubscribed"類型).
因爲用戶的服務器和聯繫人的服務器有可能失去對於訂閱狀態的同步, 用戶的服務器必須(MUST)毫無例外地路由所有"subscribe"或"unsubscribe"類型的出站出席信息節給聯繫人,使用戶能在需要的時候重新同步他或她的對聯繫人的出席信息的訂閱.
如果從用戶的角度來看,一個"subscribed"或"unsubscribed"類型的出席信息節不會導致一個訂閱狀態的變更,用戶的服務器不應該(SHOULD NOT)路由這個節到聯繫人那裏,並且不能(MUST NOT)做出一個狀態變更. 如果這個節導致一個訂閱狀態的變更, 用戶的服務器必須(MUST)路由這個節到聯繫人,並且必須(MUST)做出相應的狀態變更. 這些規則總結如下這些表.
表 1: 推薦的出站"subscribed"節的處理
當前狀態 路由? 新狀態
"None" 狀態不變
"None + Pending Out" 狀態不變
"None + Pending In" "From"
"None + Pending Out/In" "From + Pending Out"
"To" 狀態不變
"To + Pending In" "Both"
"From" 狀態不變
"From + Pending Out" 狀態不變
"Both" 狀態不變
表 2: 推薦的出站"unsubscribed"節處理
當前狀態 路由? 新狀態
"None" 狀態不變
"None + Pending Out" 狀態不變
"None + Pending In" "None"
"None + Pending Out/In" "None + Pending Out"
"To" 狀態不變
"To + Pending In" "To"
"From" "None"
"From + Pending Out" "None + Pending Out"
"Both" "To"

入站出席信息訂閱節的服務器處理過程

入站出席信息訂閱節從用戶請求一個訂閱相關的動作(通過"subscribe"類型), 通知用戶由聯繫人所做的訂閱狀態相關的動作(通過"unsubscribe"類型),或使聯繫人能夠管理用戶對聯繫人的出席信息的訪問(通過"subscribed"和"unsubscribed"類型).
當用戶的服務器爲用戶從聯繫人那裏接收到一個訂閱請求(換言之, 一個"subscribe"類型的出席信息節), 如果用戶未曾允許聯繫人訪問用戶的出席信息或者沒有未決的入站訂閱請求, 它必須(MUST)遞送那個請求給用戶;無論如何, 如果有一個未決的入站訂閱請求, 用戶的服務器不應該(SHOULD NOT)遞送這個新的請求, 因爲上一個訂閱請求可能已經被記錄下來了. 如果用戶已經允許聯繫人訪問用戶的出席信息,用戶的服務器應該(SHOULD)對一個從聯繫人發來的"subscribe"類型的入站出席信息節自動回覆(通過代替用戶向聯繫人發送一個"subscribed"類型的出席信息節); 這個規則使得聯繫人可以在需要的時候重新同步訂閱狀態. 這些規則總結如下面這些表.
表 3: 推薦的入站"subscribe"節處理
當前狀態 遞送? 新狀態
"None" "None + Pending In"
"None + Pending Out" "None + Pending Out/In"
"None + Pending In" 狀態不變
"None + Pending Out/In" 狀態不變
"To" "To + Pending In"
"To + Pending In" 狀態不變
"From" 否 * 狀態不變
"From + Pending Out" 否 * 狀態不變
"Both" 否 * 狀態不變
  • 服務器應該(SHOULD)以"subscribed"節自動回覆
當用戶的服務器爲用戶從聯繫人那裏收到一個"unsubscribe"類型的出席信息節, 如果從用戶的角度看這個節會導致一個訂閱狀態變更,那麼用戶的服務器應該(SHOULD)代替用戶自動應答(發送一個"unsubscribed"類型的出席信息節給聯繫人), 必須(MUST)遞送這個"unsubscribe"節給用戶,並且必須(MUST)改變狀態. 如果不會導致訂閱狀態變更, 用戶的服務器不應該(SHOULD NOT)遞送這個節並且不能(MUST NOT)改變狀態. 這些規則總結如下表.
表 4: 推薦的入站"unsubscribe"節處理
當前狀態 遞送? 新狀態
"None" 狀態不變
"None + Pending Out" 狀態不變
"None + Pending In" 是 * "None"
"None + Pending Out/In" 是 * "None + Pending Out"
"To" 狀態不變
"To + Pending In" 是 * "To"
"From" 是 * "None"
"From + Pending Out" 是 * "None + Pending Out
"Both" 是 * "To"
  • 服務器應該(SHOULD)以"unsubscribed"節自動應答
當用戶的服務器爲用戶從聯繫人那裏收到一個"subscribed"類型的出席信息節, 如果沒有一個爲訪問聯繫人的出席信息的未決的出站請求,它不能(MUST NOT)遞送這個節給用戶並且不能(MUST NOT)改變訂閱狀態. 如果有一個爲了訪問聯繫人的出席信息的未決的出站請求並且這個"subscribed"類型的入站出席信息請求會導致一個訂閱狀態的改變,用戶的服務器必須(MUST)遞送這個節給用戶並且必須(MUST)改變訂閱狀態. 如果用戶已經有授權可以訪問聯繫人的出席信息, 這個"subscribed"類型的入站出席信息節不導致一個訂閱狀態的變更;從而用戶的服務器不應該(SHOULD NOT)遞送這個節給用戶並且不能(MUST NOT)改變訂閱狀態. 這些規則總結如下表.
表 5: 推薦的入站"subscribed"節處理
當前狀態 遞送? 新狀態
"None" 狀態不變
"None + Pending Out" "To"
"None + Pending In" 狀態不變
"None + Pending Out/In" "To + Pending In"
"To" 狀態不變
"To + Pending In" 狀態不變
"From" 狀態不變
"From + Pending Out" "Both"
"Both" 狀態不變
當用戶的服務器爲用戶從聯繫人那裏收到了一個"unsubscribed"類型的出席信息節, 如果有一個爲了訪問聯繫人的出席信息的未決的出站請求或者用戶當前已經有授權可以訪問聯繫人的出席信息,它必須(MUST)遞送這個節給用戶並且必須(MUST)改變訂閱狀態. 否則, 用戶的服務器不應該(SHOULD NOT)遞送這個節並且不能(MUST NOT)改變訂閱狀態. 這些規則總結如下表.
表 6: 推薦的入站"unsubscribed"節處理
當前狀態 遞送? 新狀態
"None" 狀態不變
"None + Pending Out" "None"
"None + Pending In" 狀態不變
"None + Pending Out/In" "None + Pending In"
"To" "None"
"To + Pending In" "None + Pending In"
"From" 狀態不變
"From + Pending Out" "From"
"Both" "From"

服務器遞送和客戶端承認訂閱請求以及狀態變更通知

當一個服務器收到一個"subscribe"類型的入站出席信息節(換言之, 一個訂閱請求)或"subscribed"類型,"unsubscribe"類型, 或"unsubscribed"類型(換言之, 一個訂閱狀態變更通知), 除了發送適當的名冊推送(或當下次名冊被一個可用資源請求時發送更新的名冊), 它必須(MUST)遞送這個請求或通知給預定的接收者至少一次. 一個服務器可以(MAY)要求接收者的回執以承認接收到了所有狀態變更通知(並且必須(MUST)要求承認訂閱請求的情形, 換言之,類型的出席信息節"subscribe"). 爲了要求回執, 一個服務器應該(SHOULD)在每次接收者登陸的時候發送這個請求或通知給它, 直到這個接收者承認收到這個通知(通過證實"affirming"或禁止"denying"這個通知),如下表:
表 7: 訂閱狀態變更通知的承認
節類型 接受 禁止
subscribe subscribed unsubscribed
subscribed subscribe unsubscribe
unsubscribe unsubscribed subscribed
unsubscribed unsubscribe subscribe
顯然, 根據前述的訂閱狀態圖表, 一些回執節將被路由到聯繫人並且導致狀態的變更, 而其他的則不會. 無論如何, 任何這樣的節必須(MUST)導致服務器不再發送訂閱狀態變更通知給用戶.
因爲在接收到roster set(其'subscription'屬性值設爲"remove"(見 移除一個名冊條目並且取消所有訂閱項 Removing a Roster Item and Cancelling All Subscriptions (第八章第六節)))之後,用戶的服務器必須(MUST)自動生成"unsubscribe"和"unsubscribed"類型的出站出席信息節,服務器必須(MUST)把一個名冊移除請求視爲發送所有這些出席信息節,以決定是否繼續向用戶發送"subscribe"或"subscribed"類型的訂閱狀態變更通知.

屏蔽通信

大多數即時消息系統已發現有必要實現一些方法來爲用戶屏蔽來自某些特定的其他用戶的通信(這在[IMP-REQS]的第五章第一節第五小節, 第五章第一節第十五小節,第五章第三節第二小節, 和第五章第四節第十小節中也有要求). 在XMPP中這是由管理某人的隱私列表來實現的(使用'jabber:iq:privacy'名字空間).
服務器端的隱私列表使得以下用例能夠完成:
  • 接收某人的隱私列表.
  • 增加, 移除, 和 編輯某人的隱私列表.
  • 設置, 改變, 或 取消 激活的列表.
  • 設置, 改變, 或 取消 缺省的列表 (換言之, 缺省激活的那個列表).
  • 基於JID, group, 或 subscription 類型(或全局的) 允許或屏蔽消息.
  • 允許或屏蔽入站出席信息通知,基於 JID, group, 或 subscription 類型 (或全局的).
  • 允許或屏蔽出站出席信息通知,基於 JID, group, 或 subscription 類型 (或全局的).
  • 允許或屏蔽 IQ 節, 基於 JID, group,或 subscription 類型(或全局的).
  • 允許或屏蔽所有通信, 基於 JID, group, 或 subscription 類型(或全局的).
注意: 出席信息通知不包括出席信息訂閱,只包括廣播給那些已訂閱了用戶出席信息的實體的出席信息. 因而這包括沒有'type'屬性或只包含type='unavailable'的的出席信息節.

語法和語義

一個用戶可以(MAY)定義一個或更多的隱私列表, 它們由用戶的服務器保存. 每個<list/>元素包含一個或多個格式爲<item/>元素的規則, 並且每個<item/>元素使用屬性來定義一個隱私規則類型, 一個適用於規則的特定值, 相應的動作, 和處理順序相應的條目位置.
語法如下:
   <iq>
     <query xmlns='jabber:iq:privacy'>
       <list name='foo'>
         <item
             type='[jid|group|subscription]'
             value='bar'
             action='[allow|deny]'
             order='unsignedInt'>
           [<message/>]
           [<presence-in/>]
           [<presence-out/>]
           [<iq/>]
         </item>
       </list>
     </query>
   </iq>
如果類型是"jid", 那麼'value'屬性必須(MUST)包含一個合法的Jabber ID. JIDs 應該(SHOULD)滿足以下順序:
  1. <user@domain/resource> (僅爲匹配的資源)
  2. <user@domain> (任何匹配的資源)
  3. <domain/resource> (僅匹配的資源)
  4. <domain> (匹配這個域本身, 正如任何 user@domain, domain/resource, 或 包含一個子域的地址)
如果類型爲"group", 那麼'value'屬性應該(SHOULD)包含組在用戶的名冊中的名字. (如果一個客戶端嘗試更新, 新建, 或刪除一個不在用戶名冊中的組的列表條目, 服務器應該(SHOULD)返回給客戶端一個<item-not-found/>節錯誤.)
如果類型是"subscription", 那麼'value'屬性必須(MUST)是"both", "to", "from", 或"none" (定義在 名冊語法和語義Roster Syntax and Semantics (第七章第一節))中的一個人, 在這裏 "none" 包括對於用戶來說完全未知和根本不在用戶名冊中的實體.
如果沒有包含'type'屬性, 這個規則提供 失敗"fall-through" 情景.
'action'屬性必須(MUST)被包含並且它的值必須(MUST)是 允許"allow"或 禁止"deny".
'order'屬性必須(MUST)被包含並且它的值必須(MUST)是一個在列表的所有條目中具有唯一性的非負整數. (如果一個客戶端嘗試以一個非唯一的order值建立或更新一個列表, 服務器必須(MUST)返回給客戶端一個<bad-request/>節錯誤.)
<item/>元素可以(MAY)包含一個或更多子元素,使得一個實體可以指明更多的細微控制包括屏蔽哪些種類的節(換言之, 不只是簡單地屏蔽所有節). 允許的子元素包括:
  • <message/> -- 屏蔽引入消息節
  • <iq/> -- 屏蔽引入 IQ 節
  • <presence-in/> -- 屏蔽引入出席信息通知
  • <presence-out/> -- 屏蔽外出出席信息通知
在"jabber:iq:privacy'名字空間之內, 一個"set"類型的IQ節的<query/>子元素不能(MUST NOT)包含超過一個子元素(換言之, 這個節必須(MUST)只包含一個<active/>元素, 一個<default/>元素, 或一個<list/>元素); 如果一個發送中的實體違反了這個規則, 接收中的實體必須(MUST)返回一個 return a <bad-request/>節錯誤.
當一個客戶端增加或更新一個隱私列表, <list/>元素應該(SHOULD)包含至少一個<item/>子元素; 當一個客戶端移除一個隱私列表的時候, <list/>元素不能(MUST NOT)包含任何<item/>子元素.
當一個客戶端更新一個隱私列表的時候, 它必須包含所有想得到的條目(換言之, 不是一個"delta").

商業規則

  1. 如果有一個爲某會話設置的激活的列表, 它隻影響爲其激活的那個會話, 並且只在那一個會話的持續期間有效; 服務器必須(MUST)只應用激活列表,並且不能(MUST NOT)應用缺省列表(換言之, 列表沒有層次"layering").
  2. 缺省列表總體適用於用戶, 並且如果沒有爲一個節指向的目標session/resource設置激活列表,或用戶當前沒有會話,它會被處理.
  3. 如果沒有爲一個會話設置激活列表(或用戶當前沒有會話), 並且沒有缺省列表,那麼所有節應該被(SHOULD BE)接受或由服務器代替用戶做適當的處理(遵守 用於處理XML節的服務器規則 Server Rules for Handling XML Stanzas (第十一章)).
  4. 隱私列表必須(MUST)是第一個由服務器應用的遞送規則, 替代 (1) 定義在 用於處理XML節的服務器規則Server Rules for Handling XML Stanzas (第十一章)的路由和遞送規則, 以及 (2)和訂閱相關的出席信息節的處理(和相應的名冊推送的生成) 定義在 名冊條目和出席信息訂閱的集成Integration of Roster Items and Presence Subscriptions (第八章).
  5. 服務器處理隱私列表條目的順序是很重要的. 列表條目必須(MUST)按照每個<item/>的'order'屬性的整數值的升序來處理.
  6. 一旦節和一個隱私列表規則匹配, 服務器必須(MUST)按照這個規則適當地處理這個節,然後終止處理.
  7. 如果在一個列表中沒有提供一個fall-through的條目, fall-through 動作被假定爲 允許"allow".
  8. 如果一個用戶爲一個激活列表更新定義, 之後的基於那個激活列表的操作必須(MUST)使用更新的定義(爲了那些激活列表正應用的所有資源).
  9. 如果在用戶的會話期間,在激活的或缺省的列表中定義的名冊條目中的訂閱狀態改變了,或名冊組改變了,接下來基於那個列表的處理必須(MUST)考慮計入這個已改變的狀態或組(對那個列表當前應用的所有資源).
  10. 當一個規則的定義修改了的時候, 服務器必須(MUST)發送一個類型爲"set"的IQ節給所有已連接的資源, 包括一個只有一個<list/>子元素的<query/>元素, 其'name'屬性設爲已修改的隱私列表的名字. 這些 隱私列表推送("privacy list pushes")遵守和用於名冊管理的名冊推送("roster pushes")同樣的語義 , 除了被推送給已連接的資源的列表名字本身(不是完整的列表定義或那個"delta"). 是否接受這個修改了的列表定義最終由接收中的資源來決定, 儘管如果這個列表正在應用於一個已連接的資源,它應該(SHOULD)這樣做.
  11. 當一個已連接的資源嘗試移除一個列表或指定一個新的缺省列表,而那個列表應用於一個已連接的資源而不是正在發送的資源, 服務器必須(MUST)返回一個<conflict/>錯誤給發送中的資源並且不能(MUST NOT)執行這個請求的改變.

接收某人的隱私列表

例子: 客戶端向服務器請求隱私列表的名字:
   <iq from='[email protected]/orchard' type='get' id='getlist1'>
     <query xmlns='jabber:iq:privacy'/>
   </iq>
例子: 服務器發送隱私列表的名字給客戶端, 激活列表和缺省列表放在前面:
   <iq type='result' id='getlist1' to='[email protected]/orchard'>
     <query xmlns='jabber:iq:privacy'>
       <active name='private'/>
       <default name='public'/>
       <list name='public'/>
       <list name='private'/>
       <list name='special'/>
     </query>
   </iq>
例子: 客戶端向服務器請求一個隱私列表:
   <iq from='[email protected]/orchard' type='get' id='getlist2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='public'/>
     </query>
   </iq>
例子: 服務器發送一個隱私列表給客戶端:
   <iq type='result' id='getlist2' to='[email protected]/orchard'>
     <query xmlns='jabber:iq:privacy'>
       <list name='public'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='1'/>
         <item action='allow' order='2'/>
       </list>
     </query>
   </iq>
例子: 客戶端向服務器請求另一個隱私列表:
   <iq from='[email protected]/orchard' type='get' id='getlist3'>
     <query xmlns='jabber:iq:privacy'>
       <list name='private'/>
     </query>
   </iq>
例子: 服務器發送另一個隱私列表給客戶端:
   <iq type='result' id='getlist3' to='[email protected]/orchard'>
     <query xmlns='jabber:iq:privacy'>
       <list name='private'>
         <item type='subscription'
               value='both'
               action='allow'
               order='10'/>
         <item action='deny' order='15'/>
       </list>
     </query>
   </iq>
例子: 客戶端再向服務器請求另一個隱私列表:
   <iq from='[email protected]/orchard' type='get' id='getlist4'>
     <query xmlns='jabber:iq:privacy'>
       <list name='special'/>
     </query>
   </iq>
例子: 服務器再發送另一個隱私列表給客戶端:
   <iq type='result' id='getlist4' to='[email protected]/orchard'>
     <query xmlns='jabber:iq:privacy'>
       <list name='special'>
         <item type='jid'
               value='[email protected]'
               action='allow'
               order='6'/>
         <item type='jid'
               value='[email protected]'
               action='allow'
               order='7'/>
         <item type='jid'
               value='[email protected]'
               action='allow'
               order='42'/>
         <item action='deny' order='666'/>
       </list>
     </query>
   </iq>
在這個例子中, 用戶有三個列表: (1) 'public', 它允許所有人的通信,除了一個指定的實體(這是缺省列表); (2) 'private', 它只允許和這個用戶有雙向訂閱的聯繫人的通信(這是激活的列表); 還有 (3) 'special', 它只允許三個指定的實體通信.
如果用戶嘗試接收一個列表但是這個列表的名字不存在, 服務器必須(MUST)返回一個<item-not-found/>節錯誤給用戶:
例子: 客戶端嘗試接收不存在的列表:
   <iq to='[email protected]/orchard' type='error' id='getlist5'>
     <query xmlns='jabber:iq:privacy'>
       <list name='The Empty Set'/>
     </query>
     <error type='cancel'>
       <item-not-found
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
用戶在一次只被允許接收一個列表. 如果用戶嘗試同一個請求中接收超過一個列表, 服務器必須(MUST)返回一個<bad request/>節錯誤給用戶:
例子: 客戶端嘗試接收多於一個的列表:
   <iq to='[email protected]/orchard' type='error' id='getlist6'>
     <query xmlns='jabber:iq:privacy'>
       <list name='public'/>
       <list name='private'/>
       <list name='special'/>
     </query>
     <error type='modify'>
       <bad-request
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>

管理激活列表

爲了設置或改變服務器當前應用的激活列表, 用戶必須(MUST)發送一個類型爲"set"的IQ節,包含一個符合'jabber:iq:privacy'名字空間的<query/>元素,這個元素<query/>又包含一個空的<active/>子元素,而這個<active/>擁有一個'name'屬性,'name'屬性值則設爲期望的列表名.
例子: 客戶端請求激活列表變更:
   <iq from='[email protected]/orchard' type='set' id='active1'>
     <query xmlns='jabber:iq:privacy'>
       <active name='special'/>
     </query>
   </iq>
服務器必須(MUST)在返回結果給客戶端之前激活並應用這個已請求的列表.
例子: 服務器承認激活列表變更成功:
   <iq type='result' id='active1' to='[email protected]/orchard'/>
如果用戶嘗試設置一個激活列表但是列表名不存在, 服務器必須(MUST)返回一個<item-not-found/>節錯誤給用戶:
例子: 客戶端嘗試設置一個不存在的列表作爲激活列表:
   <iq to='[email protected]/orchard' type='error' id='active2'>
     <query xmlns='jabber:iq:privacy'>
       <active name='The Empty Set'/>
     </query>
     <error type='cancel'>
       <item-not-found
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
爲了取消使用任何激活列表, 已連接的資源必須(MUST)發送一個空的<active/>元素,並且不帶'name'屬性.
例子: 客戶端取消使用激活列表:
   <iq from='[email protected]/orchard' type='set' id='active3'>
     <query xmlns='jabber:iq:privacy'>
       <active/>
     </query>
   </iq>
例子: 服務器承認取消任何激活列表成功:
   <iq type='result' id='active3' to='[email protected]/orchard'/>

管理缺省列表

爲了改變它的缺省列表(它對用戶來說是全局應用的, 不只是發送中的資源), 用戶必須(MUST)發送一個類型爲"set"的IQ節, 它包含一個符合'jabber:iq:privacy'名字空間的<query/>元素,這個<query/>元素包含一個空的<default/>子元素,這個<default/>子元素擁有一個'name'屬性,這個'name'屬性的值設爲期望的列表名.
例子: 用戶請求變更缺省列表:
   <iq from='[email protected]/orchard' type='set' id='default1'>
     <query xmlns='jabber:iq:privacy'>
       <default name='special'/>
     </query>
   </iq>
例子: 服務器承認缺省列表變更成功:
   <iq type='result' id='default1' to='[email protected]/orchard'/>
如果用戶嘗試變更一個缺省列表但是這個缺省列表正在由至少一個已連接的但不是當前發送中的這個資源使用着,服務器必須(MUST)返回一個<conflict/>節錯誤給發送中的資源:
例子: 客戶端嘗試改變一個缺省列表但是這個列表正在被另一個資源使用:
   <iq to='[email protected]/orchard' type='error' id='default1'>
     <query xmlns='jabber:iq:privacy'>
       <default name='special'/>
     </query>
     <error type='cancel'>
       <conflict
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
如果用戶嘗試設置一個缺省列表但是這個列表的名字不存在, 服務器必須(MUST)返回一個<item-not-found/>節錯誤給用戶:
例子: 客戶端嘗試設置一個不存在的列表作爲缺省列表:
   <iq to='[email protected]/orchard' type='error' id='default1'>
     <query xmlns='jabber:iq:privacy'>
       <default name='The Empty Set'/>
     </query>
     <error type='cancel'>
       <item-not-found
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>
爲了取消使用缺省列表(換言之, 任何時候都使用域的節路由規則), 用戶必須(MUST)發送一個空的不帶'name'屬性的<default/>元素.
例子: 客戶端取消使用缺省列表:
   <iq from='[email protected]/orchard' type='set' id='default2'>
     <query xmlns='jabber:iq:privacy'>
       <default/>
     </query>
   </iq>
例子: 服務器承認成功地取消了任何缺省列表:
   <iq type='result' id='default2' to='[email protected]/orchard'/>
如果一個已連接的資源嘗試取消一個用戶全局的缺省列表但是這個缺省列表正在應用於另一個已連接的資源,服務器必須(MUST)返回一個<conflict/>錯誤給發送中的資源:
例子: 客戶端嘗試取消一個缺省列表但是這個列表正在被另一個資源使用:
   <iq to='[email protected]/orchard' type='error' id='default3'>
     <query xmlns='jabber:iq:privacy'>
       <default/>
     </query>
     <error type='cancel'>
       <conflict
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>

編輯一個隱私列表

爲了編輯一個隱私列表, 用戶必須(MUST)一個類型爲"set"的IQ節,包含一個符合'jabber:iq:privacy'名字空間的<query/>元素,這個<query/>元素包含一個擁有一個<list/>子元素,這個<list/>子元素擁有一個'name'屬性,這個'name'屬性的值設爲用戶想編輯的列表名. 這個<list/>元素必須(MUST)包含一個或多個<item/>元素, 它們包含了列表中的所有元素以指明用戶期望的對列表的變更(不是the "delta").
例子: 客戶端編輯隱私列表:
   <iq from='[email protected]/orchard' type='set' id='edit1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='public'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='3'/>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='5'/>
         <item action='allow' order='68'/>
       </list>
     </query>
   </iq>
例子: 服務器承認列表編輯成功:
   <iq type='result' id='edit1' to='[email protected]/orchard'/>
注意: 任何給定的條目的'order'屬性值不是固定的. 因而在前述的例子中如果用戶想在"[email protected]"條目和"[email protected]"條目之間增加4個條目, 用戶的客戶端必須(MUST)在向服務器提交列表之前對相關的條目重新編號.
服務器必須(MUST)現在發送一個 隱私列表推送"privacy list push"給所有已連接的資源:
例子: 基於列表編輯的隱私列表推送:
   <iq to='[email protected]/orchard' type='set' id='push1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='public'/>
     </query>
   </iq>
   <iq to='[email protected]/home' type='set' id='push2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='public'/>
     </query>
   </iq>
按照定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]的IQ節語義, 每個已連接的子元素必須(MUST)返回一個如下的 IQ result 給服務器:
例子: 承認收到一個隱私列表推送:
   <iq from='[email protected]/orchard'
       type='result'
       id='push1'/>
   <iq from='[email protected]/home'
       type='result'
       id='push2'/>

增加一個新的隱私列表

增加一個新的列表和編輯一個現有的列表使用的協議是相同的. 如果列表名和現有的列表名吻合, 這個增加新列表的請求將覆寫那個舊的列表. 正如編輯列表一樣, 服務器也必須(MUST)發送一個 隱私列表推送"privacy list push" 給所有已連接的資源.

移除一個隱私列表

爲了移除一個隱私列表, 用戶必須(MUST)發送一個類型爲"set"的IQ節,包含一個符合'jabber:iq:privacy'名字空間的<query/>元素,這個<query/>元素包含一個空的<list/>子元素,這個<list/>子元素擁有一個'name'屬性,這個'name'屬性的值設爲用戶想移除的列表名.
例子: 客戶端移除一個隱私列表:
   <iq from='[email protected]/orchard' type='set' id='remove1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='private'/>
     </query>
   </iq>
例子: 服務器承認成功地移除列表:
   <iq type='result' id='remove1' to='[email protected]/orchard'/>
如果一個用戶嘗試移除一個列表而這個列表正在被應用於至少一個和發送中的資源不同的已連接的資源, 服務器必須(MUST)返回一個<conflict/>節錯誤給用戶; 換言之, 用戶在嘗試移除它之前必須(MUST)先設置另一個列表成爲激活或缺省列表. 如果用戶嘗試移除一個列表但是列表名字不存在, 服務器必須(MUST)返回一個<item-not-found/>節錯誤給用戶. 如果用戶嘗試在同一個請求中移除超過一個的列表, 服務器必須(MUST)反回一個<bad request/>節錯誤給用戶.

屏蔽消息

服務器端的隱私列表使得一個用戶可以基於實體的JID,名冊組,或訂閱狀態(或全局地)來屏蔽從其他實體引入的消息. 以下例子闡明這個協議. (注意: 爲了精簡, "result"類型的IQ節沒有在以下例子中顯示, 隱私列表推送也沒有顯示.)
例子: 基於JID的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='msg1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='message-jid-example'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='3'>
           <message/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會接收到從特定JID發來的消息.
例子: 基於名冊組的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='msg2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='message-group-example'>
         <item type='group'
               value='Enemies'
               action='deny'
               order='4'>
           <message/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從指定名冊組中的任何實體發來的消息.
例子: 基於訂閱狀態的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='msg3'>
     <query xmlns='jabber:iq:privacy'>
       <list name='message-sub-example'>
         <item type='subscription'
               value='none'
               action='deny'
               order='5'>
           <message/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從任何指定訂閱狀態的實體發來的消息.
例子: 全局的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='msg4'>
     <query xmlns='jabber:iq:privacy'>
       <list name='message-global-example'>
         <item action='deny' order='6'>
           <message/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從任何其他用戶發來的消息.

屏蔽入站出席信息通知

服務器端的隱私列表使得用戶可以基於實體的JID,名冊組,或訂閱狀態(或全局地)屏蔽來自其他實體的入站出席信息通知. 以下例子闡明瞭這個協議.
注意: 出席信息通知不包括出席信息訂閱,只是把出席信息廣播給當前已訂閱某個聯繫人的出席信息的用戶. 所以它只包括沒有'type'屬性的或type='unavailable'的出席信息節.
例子: 基於JID的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presin1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presin-jid-example'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='7'>
           <presence-in/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從指定JID發來的出席信息通知.
例子: 基於名冊組的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presin2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presin-group-example'>
         <item type='group'
               value='Enemies'
               action='deny'
               order='8'>
           <presence-in/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會從指定的名冊組中的任何實體收到出席信息通知.
例子: 基於訂閱狀態的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presin3'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presin-sub-example'>
         <item type='subscription'
               value='to'
               action='deny'
               order='9'>
           <presence-in/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會從指定訂閱狀態的任何實體收到出席信息通知.
例子: 全局的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presin4'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presin-global-example'>
         <item action='deny' order='11'>
           <presence-in/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會從任何其他實體收到出席信息通知.

屏蔽出站出席信息通知

服務器端的隱私列表使用戶能夠屏蔽發出到其他實體的出席信息通知(基於實體的JID, 名冊組, 或訂閱狀態 (或全局的)). 以下例子闡明瞭這個協議.
注意: 出席信息通知不包括出席信息訂閱,只把出席信息廣播給已訂閱了用戶的出席信息的聯繫人.所以 只包括沒有'type'屬性或type='unavailable'的出席信息節.
例子: 基於JID的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presout1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presout-jid-example'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='13'>
           <presence-out/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會給指定JID發送出席信息通知.
例子: 基於名冊組的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presout2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presout-group-example'>
         <item type='group'
               value='Enemies'
               action='deny'
               order='15'>
           <presence-out/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會向指定名冊組的任何實體發送出席信息通知.
例子: 基於訂閱狀態的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presout3'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presout-sub-example'>
         <item type='subscription'
               value='from'
               action='deny'
               order='17'>
           <presence-out/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會向指定訂閱狀態的任何實體發送出席信息通知.
例子: 全局的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='presout4'>
     <query xmlns='jabber:iq:privacy'>
       <list name='presout-global-example'>
         <item action='deny' order='23'>
           <presence-out/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會向任何其他用戶發送出席信息通知.

屏蔽IQ節

服務器端的隱私列表使用戶能夠屏蔽從其他實體進來的IQ節(基於實體的JID,名冊組, 或訂閱狀態(或全局地)). 以下例子闡明瞭這個協議.
例子: 基於JID的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='iq1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='iq-jid-example'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='29'>
            <iq/>
          </item>
       </list>
     </query>
   </iq>
作爲建立和應用這個列表的結果, 用戶將不會收到從指定JID發來的IQ節.
例子: 基於名冊組的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='iq2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='iq-group-example'>
         <item type='group'
               value='Enemies'
               action='deny'
               order='31'>
            <iq/>
          </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從指定名冊組的任何實體發來的IQ節.
例子: 基於訂閱狀態的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='iq3'>
     <query xmlns='jabber:iq:privacy'>
       <list name='iq-sub-example'>
         <item type='subscription'
               value='none'
               action='deny'
               order='17'>
            <iq/>
          </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從指定訂閱狀態的任何實體發來的IQ節.
例子: 全局的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='iq4'>
     <query xmlns='jabber:iq:privacy'>
       <list name='iq-global-example'>
         <item action='deny' order='1'>
           <iq/>
         </item>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到從任何其他用戶發來的IQ節.

屏蔽所有通信

服務器端的隱私列表使用戶能夠基於其他實體的JID,名冊組,或訂閱狀態(或全局的)屏蔽所有進來和出去的節. 注意那部包括訂閱相關的出席信息節, 它們被排除在外 (定義在 屏蔽入站出席信息通知Blocking Inbound Presence Notifications (第十章第十節)). 以下例子闡明瞭這個協議.
例子: 基於JID的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='all1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='all-jid-example'>
         <item type='jid'
               value='[email protected]'
               action='deny'
               order='23'/>
       </list>
     </query>
   </iq>
作爲建立和應用這個列表的結果, 用戶將不會收到和發送任何通信給指定JID.
例子: 基於名冊組的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='all2'>
     <query xmlns='jabber:iq:privacy'>
       <list name='all-group-example'>
         <item type='group'
               value='Enemies'
               action='deny'
               order='13'/>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到和發送和指定名冊組的任何實體的通信.
例子: 基於訂閱狀態的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='all3'>
     <query xmlns='jabber:iq:privacy'>
       <list name='all-sub-example'>
         <item type='subscription'
               value='none'
               action='deny'
               order='11'/>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到和發送和指定訂閱狀態的任何實體的通信.
例子: 全局的用戶屏蔽:
   <iq from='[email protected]/orchard' type='set' id='all4'>
     <query xmlns='jabber:iq:privacy'>
       <list name='all-global-example'>
         <item action='deny' order='7'/>
       </list>
     </query>
   </iq>
作爲建立和應用前述列表的結果, 用戶將不會收到和發送和任何其他用戶的通信.

已被屏蔽的實體嘗試和用戶通信

如果一個已被屏蔽的實體嘗試發送消息或出席信息給用戶, 用戶的服務器應該(SHOULD)安靜的丟掉這個節並且不能(MUST NOT)返回一個錯誤給發送的實體.
如果一個已被屏蔽的實體嘗試發送一個類型爲"get"或"set"的IQ節給用戶, 用戶的服務器必須(MUST)給發送的實體一個<service-unavailable/>節錯誤, 因爲這是一個客戶端不理解IQ get或set的名字空間的時候所發送的標準錯誤碼. 其他類型的IQ節應該(SHOULD)被服務器安靜的丟棄.
例子: 已被屏蔽的實體嘗試發送 IQ get:
   <iq type='get'
       to='[email protected]'
       from='[email protected]/pda'
       id='probing1'>
     <query xmlns='jabber:iq:version'/>
   </iq>
例子: 服務器返回一個錯誤給已被屏蔽的實體:
   <iq type='error'
       from='[email protected]'
       to='[email protected]/pda'
       id='probing1'>
     <query xmlns='jabber:iq:version'/>
     <error type='cancel'>
       <service-unavailable
           xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     </error>
   </iq>

高級啓發

當建立一個高級隱私啓發的表達式的時候, 客戶端應該(SHOULD)使用盡可能簡單的表達式.
例如, 啓發 "屏蔽不在我名冊中的任何用戶的通信" 可以使用以下任何一種方式來構造:
  • 允許任何來自我的名冊中的JID的通信 (換言之, 列出每個JID成爲單獨的列表條目), 但是屏蔽和其他任何人的通信
  • 允許任何來自我的名冊的某個組中的用戶的通信(換言之, 列出每個組作爲單獨的條目), 但是屏蔽和任何其他人的通信
  • 允許任何我的他(她)之間的訂閱狀態爲'both'或'to'或'from'的用戶的通信(換言之, 單獨列出每個訂閱狀態值), 但是屏蔽和任何其他人的通信
  • 屏蔽和任何訂閱狀態爲'none'的用戶的通信
最後一個表達式是最簡單的並且應該(SHOULD)被使用; 這種情形下將被髮送的XML如下:
   <iq type='set' id='heuristic1'>
     <query xmlns='jabber:iq:privacy'>
       <list name='heuristic-example'>
         <item type='subscription'
               value='none'
               action='deny'
               order='437'/>
       </list>
     </query>
   </iq>

服務器處理XML節的規則

用於服務器的基本路由和遞送規則定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]中. 本章定義附加的用於XMPP兼容的即時消息和出席信息服務器規則.

入站節

如果一個入站的節的'to'屬性的JID中的域標識符部分的主機名和服務器自身的主機名相同並且'to'屬性的JID的格式是<[email protected]>或<[email protected]/resource>, 服務器必須(MUST)首先強制應用任何隱私列表(第十章),然後服從以下定義的規則:
  1. 如果JID的格式是<user@domain/resource>並且有一個可用的資源和這個全JID吻合, 接受這得服務器必須(MUST)遞送這個節給那個資源.
  2. 然後如果JID的格式是<user@domain>或格式是<user@domain/resource>,並且和用戶相關的帳號不存在, 接收者的服務器 (a) 如果它是一個出席信息節,應該(SHOULD) 安靜的忽略這個節(換言之,既不遞送它也不返回一個錯誤), (b) 如果它是一個IQ節,必須(MUST)返回一個<service-unavailable/>節錯誤給發送者, 並且 (c) 如果它是一個消息節,應該(SHOULD)返回一個<service-unavailable/>節錯誤給發送者.
  3. 然後如果JID的格式是<user@domain/resource>並且沒有可用的資源和它的全JID匹配, 接收者的服務器 (a) 如果它是一個出席信息節,應該(SHOULD)安靜地忽略這個節(換言之, 既不遞送它也不返回一個錯誤), (b) 如果它是一個IQ節,必須(MUST)返回一個<service-unavailable/>節錯誤給發送者, 並且 (c) 如果它是一個消息節,應該(SHOULD)把這個節視爲發往<user@domain>.
  4. 然後如果JID的格式是<user@domain>並且這個用戶至少有一個可用的資源, 接收者的服務器必須(MUST)遵守以下規則:
    1. 對於消息節, 服務器應該(SHOULD)遞送這個節給高優先級的可用資源(如果這個資源沒有提供<priority/>元素的值, 服務器應該(SHOULD)認爲它提供的值爲零). 如果兩個或更多的可用資源有相同的優先級, 服務器可以(MAY)使用一些其他的規則(例如, 最近的連接時間, 最近的活動時間, 或由一些<show/>值的層次所定義的最高的可用性) 來從它們中間選擇,或可以(MAY)遞送這個消息到所有這些資源. 無論如何, 服務器不能(MUST NOT)這個節到一個優先級爲負數的可用資源; 如果唯一的一個可用資源的優先級是負數, 服務器應該(SHOULD)當成沒有可用資源一樣處理這個消息(定義在後面). 另外, 服務器不能(MUST NOT)重寫'to'屬性(換言之, 它必須(MUST)讓它保持<user@domain>而不是改成<user@domain/resource>).
    2. 對於類型不是"probe"的出席信息節, 服務器必須(MUST)遞送這個節給所有可用的資源;對於出席信息調查, 服務器應該(SHOULD)基於定義在 出席信息調查Presence Probes (第五章第一節第三小節)的規則來應答. 另外, 服務器不能(MUST NOT)重寫'to'屬性(換言之, 它必須(MUST)保持<user@domain>而不是改爲<user@domain/resource>).
    3. 對於IQ節, 服務器本身必須(MUST)代替用戶應答一個IQ result或一個IQ error, 並且不能(MUST NOT)遞送這個IQ節給任何可用的資源. 具體來說, 如果合格的名字空間的語義定義了一個服務器可以提供的應答, 服務器必須(MUST)代替用戶應答這個節; 如果沒有, 服務器必須(MUST)應答一個<service-unavailable/>節錯誤.
  5. 然後如果JID的格式爲<user@domain>並且沒有這個用戶的可用資源, 這個節如何處理依賴於節的類型:
    1. 對於類型爲"subscribe", "subscribed", "unsubscribe", 和"unsubscribed"的出席信息節, 服務器必須(MUST)維持這個節的一個記並且至少遞送這個節一次(也就是, 當這個用戶下次建立一個可用的資源的時候); 另外, 服務器必須(MUST)遞送類型爲"subscribe"的出席信息節直到用戶批准或拒絕這個訂閱請求爲止(參見 出席信息訂閱Presence Subscriptions (第五章第一節第六小節)).
    2. 對於所有其他的出席信息節, 服務器應該(SHOULD)安靜的忽略這個節,既不存儲它用於以後遞送也不代替用戶應答它.
    3. 對於消息節, 服務器可以(MAY)選擇代替用戶存儲這個節並且當用戶下次可用的時候遞送給他, 或通過一些其他的手段轉發這個消息給用戶(例如, 給用戶的郵箱). 無論如何, 如果離線消息存儲或消息轉發沒有激活, 服務器必須(MUST)返回發送者一個<service-unavailable/>節錯誤. (注意:離線信息存儲和消息轉發沒有定義在 XMPP, 因爲嚴格來說它們是實現和服務提供的問題.)
    4. 對於IQ節, 服務器本身必須(MUST)代替用戶應答一個IQ result或一個IQ error. 具體來說,如果合法的名字空間的語義定義了一個服務器可以提供的應答, 服務器必須(MUST)代替用戶應答這個節; 如果沒有, 服務器必須(MUST)應答一個<service-unavailable/>節錯誤.

出站節

如果出站節的'to'屬性的地址的域標識符部分的主機名和服務器自身的一個主機名吻合, 服務器必須(MUST) 根據 入站節Inbound Stanzas(第十一章第一節)的規則遞送這個節給一個本地實體.
如果出站節的'to'屬性的地址的域標識符部分的主機名不和服務器自身的一個主機名吻合, 服務器必須(MUST)嘗試路由這個節到外部域. 推薦的動作順序定義如下:
  1. 首先嚐試用一個"xmpp-server"服務和"tcp"協議的[SRV]服務解析這個外部的主機名,結果得到的資源記錄格式如"_xmpp-server._tcp.example.com.", 定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920].
  2. 如果"xmpp-server"地址記錄解析失敗, 嘗試解析"_im"或"_pres"[SRV]服務(定義在[IMP-SRV]), 使用"_im"服務用語<message/>節,使用"_pres"服務用語<presence/>節(如何處理<iq/>節取決於具體實現). 這樣得到的結果是一個或多個格式爲"_im.<proto>.example.com."或 "_pres.<proto>.example.com."的記錄, 這裏"<proto>"是一個註冊在 即時消息SRV協議標籤註冊表Instant Messaging SRV Protocol Label registry中的一個標籤,或者是 出席信息SRV協議標籤註冊表Presence SRV Protocol Label registry中的標籤: 要麼是"_xmpp",用於XMPP-aware的域,要麼是一些 IANA註冊的標籤IANA-registered label (例如,"_simple") 用於 non-XMPP-aware 的域.
  3. 如果這兩種SRV地址記錄解析都失敗了, 嘗試執行一個通用的 IPv4/IPv6 地址記錄解析來決定IP地址,使用"xmpp-server"端口號5269(已在IANA註冊, 定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]).
強烈鼓勵部署服務器的管理員們保持 _im._xmpp, _pres._xmpp, 和 _xmpp._tcp SRV 記錄正確同步, 因爲不同的實現可能在"xmpp-server"查找之前執行"_im"和"_pres"查找.

即時消息和出席信息兼容性需求

本章總結了即時消息和出席信息服務器和客戶端爲了保證兼容的實現而必須(MUST)支持的部分XMPP協議. 所有這些應用必須(MUST)遵守定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]的要求. 本章的文字定義了附加的用於即時消息和出席信息的兼容性需求; 注意定義在這裏的要求補充而不是替代核心需求. 也要注意一個服務器或客戶端可以(MAY)僅支持出席信息或即時消息, 如果只想支持出席信息服務或即時消息服務,可以不必同時支持兩個.

服務器

除了核心服務器兼容需求之外, 一個即時消息和出席信息服務器必須(MUST)還要支持以下協議:
  • 定義在本文中的所有服務器相關的即時消息和出席信息語法和語義, 包括代替客戶端廣播出席信息, 出席信息訂閱, 名冊存儲和處理, 隱私列表, 以及 IM-specific 路由和遞送規則

客戶端

除了核心的客戶端兼容性需求之外, 一個即時消息和出席信息客戶端還必須(MUST)支持以下協議:
  • 生成和處理由XML規劃定義的XML節的IM-specific語義, 包括消息和出席信息節以及它們的子元素的的'type'屬性
  • 所有本文定義的客戶端相關的即時消息語法和語義, 包括出席信息訂閱, 名冊管理, 和隱私列表
  • 端到端的對象加密(定義在 XMPP中的端到端對象加密End-to-End Object Encryption in the Extensible Messaging and Presence Protocol (XMPP) [XMPP-E2E])
一個客戶端也必須(MUST)處理編碼爲"im:" URIs的地址(定義在[CPIM]), 並且可以(MAY)移除"im:"scheme並把地址解析委託給服務器(定義在 出站節Outbound Stanzas(第十一章第二節).

國際化事項

關於國際化的考慮, 參考[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]相關章節.

安全性事項

XMPP的核心安全性事項定義在[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]相關章節.
附加的事項僅適用於分散定義在本文許多地方的XMPP即時消息和出席信息應用; 特別是:
  • 當一個服務器處理一個任何類型的入站節,這個節的預定接收者是和服務器的主機名相關的一個用戶,服務器必須(MUST)首先強制應用任何隱私列表(第十章),見 處理XML節的服務器規則Server Rules for Handling XML Stanzas(第十一章)).
  • 當一個服務器處理一個類型爲"probe"的入站出席信息節,這個節的預定接收者是和服務器的主機名相關的一個用戶, 如果這個發送者是一個由出席信息訂閱決定的未被授權接收那個信息的實體,服務器不能(MUST NOT)揭露這個用戶的出席信息(見 客戶端和服務器出席信息職責Client and Server Presence Responsibilities (第五章第一節)).
  • 當一個服務器處理一個任何類型的出站出席信息節,這個節沒有type屬性或type屬性值爲"unavailable", 爲了確保這個出席信息不被廣播給那些未被授權知道這個信息的實體, 它必須(MUST)服從客戶端和服務器出席信息職責Client and Server Presence Responsibilities (第五章第一節) 定義的規則 .
  • 當一個服務器生成一個錯誤節作爲不存在的用戶接收到的一個節的應答的時候, 使用<service-unavailable/>錯誤條件有助於防止著名的字典攻擊, 因爲這個錯誤和條件和其他一些錯誤條件相同,例如, 一個IQ子元素的名字空間不被理解, 或離線存儲或消息轉發不被一個域允許.

IANA事項

很多相關的IANA事項, 參考[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920]相關章節.

會話數據的XML名字空間名

以下爲XMPP中會話相關的數據定義了一個 URN 子名字空間. (這個名字空間名的格式遵循 IETF XML Registry [XML-REG].)
URI: urn:ietf:params:xml:ns:xmpp-session
Specification: RFC 3921
Description: This is the XML namespace name for session-related data in the Extensible Messaging and Presence Protocol (XMPP) as defined by RFC 3921.
Registrant Contact: IETF, XMPP Working Group, <[email protected]>

即時消息SRV協議標籤註冊

確定即時消息和出席信息地址[IMP-SRV],爲那些能提供遵守"_im"SRV服務標籤的服務定義了一個即時消息SRV協議標籤註冊表. 因爲XMPP是其中一個協議, IANA在適當的註冊項中註冊了"_xmpp"協議標籤,如下:
Protocol label: _xmpp
Specification: RFC 3921
Description: Instant messaging protocol label for the Extensible Messaging and Presence Protocol (XMPP) as defined by RFC 3921.
Registrant Contact: IETF, XMPP Working Group, <[email protected]>

出席信息SRV協議標籤註冊

確定即時消息和出席信息地址[IMP-SRV],爲那些能夠提供遵守"_pres"SRV服務標籤的服務定義了一個出席信息SRV協議標籤註冊項. 因爲XMPP是其中一個協議, IANA在適當的註冊項中註冊了"_xmpp"協議標籤,如下:
Protocol label: _xmpp
Specification: RFC 3921
Description: Presence protocol label for the Extensible Messaging and Presence Protocol (XMPP) as defined by RFC 3921.
Registrant Contact: IETF, XMPP Working Group, <[email protected]>

參考

標準參考

[CPIM] Peterson, J., "Common Profile for Instant Messaging (CPIM)", RFC 3860, August 2004.
[IMP-REQS] Day, M., Aggarwal, S., Mohr, G., and J. Vincent, "Instant Messaging/Presence Protocol Requirements", RFC 2779, February 2000.
[IMP-SRV] Peterson, J., "Address Resolution for Instant Messaging and Presence", RFC 3861, August 2004.
[SRV] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for specifying the location of services (DNS SRV)", RFC 2782, February 2000.
[TERMS] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
[XML] Bray, T., Paoli, J., Sperberg-McQueen, C., and E. Maler, "Extensible Markup Language (XML) 1.0 (2nd ed)", W3C REC-xml, October 2000, <http://www.w3.org/TR/REC-xml>.
[XML-NAMES] Bray, T., Hollander, D., and A. Layman, "Namespaces in XML", W3C REC-xml-names, January 1999, <http://www.w3.org/TR/REC-xml-names>.
[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920] Saint-Andre, P., "Extensible Messaging and Presence Protocol (XMPP): Core", RFC 3920, October 2004.
[XMPP-E2E] Saint-Andre, P., "End-to-End Object Encryption in the Extensible Messaging and Presence Protocol (XMPP)", RFC 3923, October 2004.

信息參考

[IMP-MODEL] Day, M., Rosenberg, J., and H. Sugano, "A Model for Presence and Instant Messaging", RFC 2778, February 2000.
[IRC] Oikarinen, J. and D. Reed, "Internet Relay Chat Protocol", RFC 1459, May 1993.
[JEP-0054] Saint-Andre, P., "vcard-temp", JSF JEP 0054, March 2003.
[JEP-0077] Saint-Andre, P., "In-Band Registration", JSF JEP 0077, August 2004.
[JEP-0078] Saint-Andre, P., "Non-SASL Authentication", JSF JEP 0078, July 2004.
[JSF] Jabber Software Foundation, "Jabber Software Foundation", <http://www.jabber.org/>.
[VCARD] Dawson, F. and T. Howes, "vCard MIME Directory Profile", RFC 2426, September 1998.
[XML-REG] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, January 2004.

附錄 A. vCards

[IMP-REQS]的第三章第一節第三小節和第四章第一節第四小節要求可能爲其他用戶接收帶外的聯繫人信息(例如,電話號碼或電子郵件地址). 在Jabber社區中通常使用RFC 2426[VCARD]中vCard規範的XML來表達這類信息,但這超出了XMPP的範圍(這個協議的文檔包含在[JEP-0054], 由[JSF]發行).

譯者注: [JSF]已改名爲[XSF],[JEP-0054]已改名爲[XEP-0054]

附錄 B. XML規劃

接下來的XML規劃是描述性的, 不是標準化的. 規劃定義在XMPP的核心特性, 參考[XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920].

B.1 jabber:client

   <?xml version='1.0' encoding='UTF-8'?>
 
   <xs:schema
       xmlns:xs='http://www.w3.org/2001/XMLSchema'
       targetNamespace='jabber:client'
       xmlns='jabber:client'
       elementFormDefault='qualified'>
 
     <xs:import namespace='urn:ietf:params:xml:ns:xmpp-stanzas'/>
 
     <xs:element name='message'>
        <xs:complexType>
           <xs:sequence>
             <xs:choice minOccurs='0' maxOccurs='unbounded'>
               <xs:element ref='subject'/>
               <xs:element ref='body'/>
               <xs:element ref='thread'/>
             </xs:choice>
             <xs:any     namespace='##other'
                         minOccurs='0'
                         maxOccurs='unbounded'/>
             <xs:element ref='error'
                         minOccurs='0'/>
           </xs:sequence>
           <xs:attribute name='from'
                         type='xs:string'
                         use='optional'/>
           <xs:attribute name='id'
                         type='xs:NMTOKEN'
                         use='optional'/>
           <xs:attribute name='to'
                         type='xs:string'
                         use='optional'/>
           <xs:attribute name='type' use='optional' default='normal'>
             <xs:simpleType>
               <xs:restriction base='xs:NCName'>
                 <xs:enumeration value='chat'/>
                 <xs:enumeration value='error'/>
                 <xs:enumeration value='groupchat'/>
                 <xs:enumeration value='headline'/>
                 <xs:enumeration value='normal'/>
               </xs:restriction>
             </xs:simpleType>
           </xs:attribute>
           <xs:attribute ref='xml:lang' use='optional'/>
        </xs:complexType>
     </xs:element>
 
     <xs:element name='body'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:string'>
             <xs:attribute ref='xml:lang' use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='subject'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:string'>
             <xs:attribute ref='xml:lang' use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='thread' type='xs:NMTOKEN'/>
 
     <xs:element name='presence'>
       <xs:complexType>
         <xs:sequence>
           <xs:choice minOccurs='0' maxOccurs='unbounded'>
             <xs:element ref='show'/>
             <xs:element ref='status'/>
             <xs:element ref='priority'/>
           </xs:choice>
           <xs:any     namespace='##other'
                       minOccurs='0'
                       maxOccurs='unbounded'/>
           <xs:element ref='error'
                       minOccurs='0'/>
         </xs:sequence>
         <xs:attribute name='from'
                       type='xs:string'
                       use='optional'/>
         <xs:attribute name='id'
                       type='xs:NMTOKEN'
                       use='optional'/>
         <xs:attribute name='to'
                       type='xs:string'
                       use='optional'/>
         <xs:attribute name='type' use='optional'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='error'/>
               <xs:enumeration value='probe'/>
               <xs:enumeration value='subscribe'/>
               <xs:enumeration value='subscribed'/>
               <xs:enumeration value='unavailable'/>
               <xs:enumeration value='unsubscribe'/>
               <xs:enumeration value='unsubscribed'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute ref='xml:lang' use='optional'/>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='show'>
       <xs:simpleType>
         <xs:restriction base='xs:NCName'>
           <xs:enumeration value='away'/>
           <xs:enumeration value='chat'/>
           <xs:enumeration value='dnd'/>
           <xs:enumeration value='xa'/>
         </xs:restriction>
       </xs:simpleType>
     </xs:element>
 
     <xs:element name='status'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:string'>
             <xs:attribute ref='xml:lang' use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='priority' type='xs:byte'/>
 
     <xs:element name='iq'>
       <xs:complexType>
         <xs:sequence>
           <xs:any     namespace='##other'
                       minOccurs='0'/>
           <xs:element ref='error'
                       minOccurs='0'/>
         </xs:sequence>
         <xs:attribute name='from'
                       type='xs:string'
                       use='optional'/>
         <xs:attribute name='id'
                       type='xs:NMTOKEN'
                       use='required'/>
         <xs:attribute name='to'
                       type='xs:string'
                       use='optional'/>
         <xs:attribute name='type' use='required'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='error'/>
               <xs:enumeration value='get'/>
               <xs:enumeration value='result'/>
               <xs:enumeration value='set'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute ref='xml:lang' use='optional'/>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='error'>
       <xs:complexType>
         <xs:sequence  xmlns:err='urn:ietf:params:xml:ns:xmpp-stanzas'>
           <xs:group   ref='err:stanzaErrorGroup'/>
           <xs:element ref='err:text'
                       minOccurs='0'/>
         </xs:sequence>
         <xs:attribute name='code' type='xs:byte' use='optional'/>
         <xs:attribute name='type' use='required'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='auth'/>
               <xs:enumeration value='cancel'/>
               <xs:enumeration value='continue'/>
               <xs:enumeration value='modify'/>
               <xs:enumeration value='wait'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
       </xs:complexType>
     </xs:element>
 
   </xs:schema>

B.2 jabber:server

   <?xml version='1.0' encoding='UTF-8'?>
 
   <xs:schema
       xmlns:xs='http://www.w3.org/2001/XMLSchema'
       targetNamespace='jabber:server'
       xmlns='jabber:server'
       elementFormDefault='qualified'>
 
     <xs:import namespace='urn:ietf:params:xml:ns:xmpp-stanzas'/>
 
     <xs:element name='message'>
        <xs:complexType>
           <xs:sequence>
             <xs:choice minOccurs='0' maxOccurs='unbounded'>
               <xs:element ref='subject'/>
               <xs:element ref='body'/>
               <xs:element ref='thread'/>
             </xs:choice>
             <xs:any     namespace='##other'
                         minOccurs='0'
                         maxOccurs='unbounded'/>
             <xs:element ref='error'
                         minOccurs='0'/>
           </xs:sequence>
           <xs:attribute name='from'
                         type='xs:string'
                         use='required'/>
           <xs:attribute name='id'
                         type='xs:NMTOKEN'
                         use='optional'/>
           <xs:attribute name='to'
                         type='xs:string'
                         use='required'/>
           <xs:attribute name='type' use='optional' default='normal'>
             <xs:simpleType>
               <xs:restriction base='xs:NCName'>
                 <xs:enumeration value='chat'/>
                 <xs:enumeration value='error'/>
                 <xs:enumeration value='groupchat'/>
                 <xs:enumeration value='headline'/>
                 <xs:enumeration value='normal'/>
               </xs:restriction>
             </xs:simpleType>
           </xs:attribute>
           <xs:attribute ref='xml:lang' use='optional'/>
        </xs:complexType>
     </xs:element>
 
     <xs:element name='body'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:string'>
             <xs:attribute ref='xml:lang' use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='subject'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:string'>
             <xs:attribute ref='xml:lang' use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='thread' type='xs:NMTOKEN'/>
 
     <xs:element name='presence'>
       <xs:complexType>
         <xs:sequence>
           <xs:choice minOccurs='0' maxOccurs='unbounded'>
             <xs:element ref='show'/>
             <xs:element ref='status'/>
             <xs:element ref='priority'/>
           </xs:choice>
           <xs:any     namespace='##other'
                       minOccurs='0'
                       maxOccurs='unbounded'/>
           <xs:element ref='error'
                       minOccurs='0'/>
         </xs:sequence>
         <xs:attribute name='from'
                       type='xs:string'
                       use='required'/>
         <xs:attribute name='id'
                       type='xs:NMTOKEN'
                       use='optional'/>
         <xs:attribute name='to'
                       type='xs:string'
                       use='required'/>
         <xs:attribute name='type' use='optional'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='error'/>
               <xs:enumeration value='probe'/>
               <xs:enumeration value='subscribe'/>
               <xs:enumeration value='subscribed'/>
               <xs:enumeration value='unavailable'/>
               <xs:enumeration value='unsubscribe'/>
               <xs:enumeration value='unsubscribed'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute ref='xml:lang' use='optional'/>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='show'>
       <xs:simpleType>
         <xs:restriction base='xs:NCName'>
           <xs:enumeration value='away'/>
           <xs:enumeration value='chat'/>
           <xs:enumeration value='dnd'/>
           <xs:enumeration value='xa'/>
         </xs:restriction>
       </xs:simpleType>
     </xs:element>
 
     <xs:element name='status'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:string'>
             <xs:attribute ref='xml:lang' use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='priority' type='xs:byte'/>
 
     <xs:element name='iq'>
       <xs:complexType>
         <xs:sequence>
           <xs:any     namespace='##other'
                       minOccurs='0'/>
           <xs:element ref='error'
                       minOccurs='0'/>
         </xs:sequence>
         <xs:attribute name='from'
                       type='xs:string'
                       use='required'/>
         <xs:attribute name='id'
                       type='xs:NMTOKEN'
                       use='required'/>
         <xs:attribute name='to'
                       type='xs:string'
                       use='required'/>
         <xs:attribute name='type' use='required'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='error'/>
               <xs:enumeration value='get'/>
               <xs:enumeration value='result'/>
               <xs:enumeration value='set'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute ref='xml:lang' use='optional'/>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='error'>
       <xs:complexType>
         <xs:sequence  xmlns:err='urn:ietf:params:xml:ns:xmpp-stanzas'>
           <xs:group   ref='err:stanzaErrorGroup'/>
           <xs:element ref='err:text'
                       minOccurs='0'/>
         </xs:sequence>
         <xs:attribute name='code' type='xs:byte' use='optional'/>
         <xs:attribute name='type' use='required'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='auth'/>
               <xs:enumeration value='cancel'/>
               <xs:enumeration value='continue'/>
               <xs:enumeration value='modify'/>
               <xs:enumeration value='wait'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
       </xs:complexType>
     </xs:element>
 
   </xs:schema>

B.3 session

   <?xml version='1.0' encoding='UTF-8'?>
 
   <xs:schema
       xmlns:xs='http://www.w3.org/2001/XMLSchema'
       targetNamespace='urn:ietf:params:xml:ns:xmpp-session'
       xmlns='urn:ietf:params:xml:ns:xmpp-session'
       elementFormDefault='qualified'>
 
     <xs:element name='session' type='empty'/>
 
     <xs:simpleType name='empty'>
       <xs:restriction base='xs:string'>
         <xs:enumeration value=''/>
       </xs:restriction>
     </xs:simpleType>
 
   </xs:schema>

B.4 jabber:iq:privacy

   <?xml version='1.0' encoding='UTF-8'?>
 
   <xs:schema
       xmlns:xs='http://www.w3.org/2001/XMLSchema'
       targetNamespace='jabber:iq:privacy'
       xmlns='jabber:iq:privacy'
       elementFormDefault='qualified'>
 
     <xs:element name='query'>
       <xs:complexType>
         <xs:sequence>
           <xs:element ref='active'
                       minOccurs='0'/>
           <xs:element ref='default'
                       minOccurs='0'/>
           <xs:element ref='list'
                       minOccurs='0'
                       maxOccurs='unbounded'/>
         </xs:sequence>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='active'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:NMTOKEN'>
             <xs:attribute name='name'
                           type='xs:string'
                           use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='default'>
       <xs:complexType>
         <xs:simpleContent>
           <xs:extension base='xs:NMTOKEN'>
             <xs:attribute name='name'
                           type='xs:string'
                           use='optional'/>
           </xs:extension>
         </xs:simpleContent>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='list'>
       <xs:complexType>
         <xs:sequence>
           <xs:element ref='item'
                       minOccurs='0'
                       maxOccurs='unbounded'/>
         </xs:sequence>
         <xs:attribute name='name'
                       type='xs:string'
                       use='required'/>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='item'>
       <xs:complexType>
         <xs:sequence>
           <xs:element name='iq'
                       minOccurs='0'
                       type='empty'/>
           <xs:element name='message'
                       minOccurs='0'
                       type='empty'/>
           <xs:element name='presence-in'
                       minOccurs='0'
                       type='empty'/>
           <xs:element name='presence-out'
                       minOccurs='0'
                       type='empty'/>
         </xs:sequence>
         <xs:attribute name='action' use='required'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='allow'/>
               <xs:enumeration value='deny'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute name='order'
                       type='xs:unsignedInt'
                       use='required'/>
         <xs:attribute name='type' use='optional'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='group'/>
               <xs:enumeration value='jid'/>
               <xs:enumeration value='subscription'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute name='value'
                       type='xs:string'
                       use='optional'/>
       </xs:complexType>
     </xs:element>
 
     <xs:simpleType name='empty'>
       <xs:restriction base='xs:string'>
         <xs:enumeration value=''/>
       </xs:restriction>
     </xs:simpleType>
 
   </xs:schema>

B.5 jabber:iq:roster

   <?xml version='1.0' encoding='UTF-8'?>
 
   <xs:schema
       xmlns:xs='http://www.w3.org/2001/XMLSchema'
       targetNamespace='jabber:iq:roster'
       xmlns='jabber:iq:roster'
       elementFormDefault='qualified'>
 
     <xs:element name='query'>
       <xs:complexType>
         <xs:sequence>
           <xs:element ref='item'
                       minOccurs='0'
                       maxOccurs='unbounded'/>
         </xs:sequence>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='item'>
       <xs:complexType>
         <xs:sequence>
           <xs:element ref='group'
                       minOccurs='0'
                       maxOccurs='unbounded'/>
         </xs:sequence>
         <xs:attribute name='ask' use='optional'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='subscribe'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
         <xs:attribute name='jid' type='xs:string' use='required'/>
         <xs:attribute name='name' type='xs:string' use='optional'/>
         <xs:attribute name='subscription' use='optional'>
           <xs:simpleType>
             <xs:restriction base='xs:NCName'>
               <xs:enumeration value='both'/>
               <xs:enumeration value='from'/>
               <xs:enumeration value='none'/>
               <xs:enumeration value='remove'/>
               <xs:enumeration value='to'/>
             </xs:restriction>
           </xs:simpleType>
         </xs:attribute>
       </xs:complexType>
     </xs:element>
 
     <xs:element name='group' type='xs:string'/>
 
   </xs:schema>

附錄 C. Jabber IM Presence協議和XMPP之間的不同

本章是非標準的.
譯者注:附錄D對於新接觸XMPP的人沒有什麼意義,就不翻譯了,免得浪費時間。因爲現在RFC公佈已經很久了,以前的Jabber實現很多都進化到XMPP了。
XMPP has been adapted from the protocols originally developed in the Jabber open-source community, which can be thought of as "XMPP 0.9". Because there exists a large installed base of Jabber implementations and deployments, it may be helpful to specify the key differences between the relevant Jabber protocols and XMPP in order to expedite and encourage upgrades of those implementations and deployments to XMPP. This section summarizes the differences that relate specifically to instant messaging and presence applications, while the corresponding section of [XMPP-CORE|XMPP文檔列表/XMPP正式RFC標準/RFC3920] summarizes the differences that relate to all XMPP applications.

C.1 Session Establishment

The client-to-server authentication protocol developed in the Jabber community assumed that every client is an IM client and therefore initiated an IM session upon successful authentication and resource binding, which are performed simultaneously (documentation of this protocol is contained in [JEP-0078], published by the Jabber Software Foundation [JSF]). XMPP maintains a stricter separation between core functionality and IM functionality; therefore, an IM session is not created until the client specifically requests one using the protocol defined under Session Establishment (Section 3).

C.2 Privacy Lists

The Jabber community began to define a protocol for communications blocking (privacy lists) in late 2001, but that effort was deprecated once the XMPP Working Group was formed. Therefore the protocol defined under Blocking Communication (Section 10) is the only such protocol defined for use in the Jabber community.

貢獻者

XMPP的大部分核心方面是由1999年開始的Jabber開源社區開發的. 這個社區是由 Jeremie Miller建立的, 他於1999年1月發佈了最初版的jabber server源代碼. 主要的基礎協議的早期貢獻者還包括 Ryan Eatmon, Peter Millard, Thomas Muldowney,和 Dave Smith. XMPP工作組在即時消息和出席信息方面的工作主要集中在IM會話建立和通信屏蔽(隱私列表); 會話建立協議主要是由 Rob Norris 和 Joe Hildebrand 開發的, 隱私列表協議最初是由 Peter Millard.貢獻的

致謝

感謝許多在貢獻者名單之外的人們. 儘管很難提供一個完整的名單, 以下個人對於定義協議或評論標準提供了很多幫助:
Thomas Charron, Richard Dobson, Schuyler Heath, Jonathan Hogg, Craig Kaes, Jacek Konieczny, Lisa Dusseault, Alexey Melnikov, Keith Minkler, Julian Missig, Pete Resnick, Marshall Rose, Jean-Louis Seguineau, Alexey Shchepin, Iain Shigeoka, and David Waite. 也感謝 XMPP工作組的成員和 IETF 社區在本文的成文過程中一直提供的評論和反饋。

作者地址

Peter Saint-Andre (編輯)
Jabber Software Foundation
EMail: [email protected]

完整的版權聲明

Copyright (C) The Internet Society (2004).
This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights. This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/S HE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

知識產權

The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the IETF's procedures with respect to rights in IETF Documents can be found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf- [email protected].

感謝

目前爲RFC編輯活動提供資金的Internet Society.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章