Merge branches 'feature/editor-tooling', 'feature/xep-0045', 'feature/xep-0060', 'feature/xep-0234', 'feature/xep-0321', 'feature/xep-0369', 'feature/xep-0388', 'feature/xep-0390', 'feature/protoxep-jet' and 'feature/protoxep-pubsub-cap'

hacx

@ -4,10 +4,12 @@
FROM xmppxsf/xeps-base:latest
ARG NCORES=1
ARG TARGETS="html pdf"
ARG TARGETS="html inbox-html inbox-xml pdf xeplist"
COPY *.xml xep.* *.css *.xsl *.js *.xsl Makefile /src/
COPY resources/*.pdf /src/resources/
COPY tools/*.py /src/tools/
COPY inbox/*.xml inbox/*.ent inbox/*.dtd /src/inbox/
WORKDIR /src
RUN OUTDIR=/var/www/html/extensions/ make -j$NCORES $TARGETS

@ -12,6 +12,22 @@ JSTARGETS=$(OUTDIR)/prettify.js
DO_XELATEX=cd $(OUTDIR); xelatex --interaction=nonstopmode -no-shell-escape "$(notdir $(basename $@)).tex" >/dev/null
xeps=$(wildcard *.xml)
proto_xeps=$(wildcard inbox/*.xml)
all_xeps=$(xeps) $(proto_xeps)
xep_xmls=$(patsubst %.xml,$(OUTDIR)/%.xml,$(xeps))
proto_xep_xmls=$(patsubst %.xml,$(OUTDIR)/%.xml,$(proto_xeps))
all_xep_xmls=$(xep_xmls) $(proto_xep_xmls)
xep_htmls=$(patsubst %.xml,$(OUTDIR)/%.html,$(xeps))
proto_xep_htmls=$(patsubst %.xml,$(OUTDIR)/%.html,$(proto_xeps))
all_xep_htmls=$(xep_htmls) $(proto_xep_htmls)
xep_pdfs=$(patsubst %.xml,$(OUTDIR)/%.pdf,$(xeps))
xep_refs=$(patsubst xep-%.xml, $(REFSDIR)/reference.XSF.XEP-%.xml, $(xeps))
xep_examples=$(patsubst xep-%.xml, $(EXAMPLESDIR)/%.xml, $(xeps))
.PHONY: help
help:
@ -33,17 +49,29 @@ help:
.PHONY: all
all: html
.PHONY: xeplist
xeplist: $(OUTDIR)/xeplist.xml
.PHONY: html
html: $(patsubst %.xml, $(OUTDIR)/%.html, $(wildcard *.xml))
html: $(xep_htmls)
.PHONY: xml
xml: $(xep_xmls)
.PHONY: inbox-html
inbox-html: $(proto_xep_htmls)
.PHONY: inbox-xml
inbox-xml: $(proto_xep_xmls)
.PHONY: pdf
pdf: $(patsubst %.xml, $(OUTDIR)/%.pdf, $(wildcard *.xml))
pdf: $(xep_pdfs)
.PHONY: refs
refs: $(patsubst xep-%.xml, $(REFSDIR)/reference.XSF.XEP-%.xml, $(wildcard *.xml))
refs: $(xep_refs)
.PHONY: examples
examples: $(patsubst xep-%.xml, $(EXAMPLESDIR)/%.xml, $(wildcard *.xml))
examples: $(xep_examples)
.PHONY: xep-%
xep-%: $(OUTDIR)/xep-%.html $(REFSDIR)/reference.XSF.XEP-%.xml $(OUTDIR)/xep-%.pdf $(EXAMPLESDIR)/%.xml;
@ -54,19 +82,29 @@ xep-%.html: $(OUTDIR)/xep-%.html ;
.PHONY: xep-%.pdf
xep-%.pdf: $(OUTDIR)/xep-%.pdf ;
$(all_xep_xmls): $(OUTDIR)/%.xml: %.xml
cp $< $@
$(OUTDIR)/xeplist.xml: $(wildcard *.xml) $(wildcard inbox/*.xml)
./tools/extract-metadata.py > $@
$(EXAMPLESDIR)/%.xml: xep-%.xml $(XMLDEPS) examples.xsl $(EXAMPLESDIR)
xsltproc --path $(CURDIR) examples.xsl "$<" > "$@" && echo "Finished building $@"
$(REFSDIR)/reference.XSF.XEP-%.xml: xep-%.xml $(XMLDEPS) ref.xsl $(REFSDIR)
xsltproc --path $(CURDIR) ref.xsl "$<" > "$@" && echo "Finished building $@"
$(OUTDIR)/%.html: %.xml $(XMLDEPS) $(HTMLDEPS)
$(all_xep_htmls): $(OUTDIR)/%.html: %.xml $(XMLDEPS) $(HTMLDEPS)
# we dont put it as a dependency to avoid a rebuild due to a timestamp
# change on the directory
mkdir -p $(OUTDIR)/inbox
xmllint --nonet --noout --noent --loaddtd --valid "$<"
# Check for non-data URIs
! xmllint --nonet --noout --noent --loaddtd --xpath "//img/@src[not(starts-with(., 'data:'))]" $< 2>/dev/null && true
# Actually build the HTML
xsltproc --path $(CURDIR) xep.xsl "$<" > "$@" && echo "Finished building $@"
xsltproc --path $(CURDIR) --param htmlbase "$(if $(findstring inbox,$<),'../','./')" xep.xsl "$<" > "$@" && echo "Finished building $@"
$(OUTDIR)/xmpp.pdf $(OUTDIR)/xmpp-text.pdf: $(OUTDIR)
cp "resources/$(notdir $@)" "$@"

@ -1,56 +0,0 @@
#!/bin/sh
# for all XEPs, generates HTML files and IETF-style reference, then copies XML file
# usage: ./all.sh
## LICENSE ##
#
# Copyright (c) 1999 - 2013 XMPP Standards Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
## END LICENSE ##
xeppath=/var/www/vhosts/xmpp.org/extensions
ls xep-0*.xml > tmp1.txt
sed s/^xep-// tmp1.txt > tmp2.txt
sed s/.xml$// tmp2.txt > nums.txt
rm tmp*.txt
while read f
do
xsltproc xep.xsl xep-$f.xml > $xeppath/xep-$f.html
xsltproc ref.xsl xep-$f.xml > $xeppath/refs/reference.XSF.XEP-$f.xml
xsltproc examples.xsl xep-$f.xml > $xeppath/examples/$f.xml
./gen.py $f
cp xep-$f.xml $xeppath/
./gen.py $f
done < nums.txt
rm nums.txt
xsltproc xep.xsl xep-README.xml > $xeppath/README.html
xsltproc xep.xsl xep-template.xml > $xeppath/template.html
cp *.dtd $xeppath/
cp *.ent $xeppath/
cp *.shtml $xeppath/
cp *.xsd $xeppath/
# END

@ -1,196 +0,0 @@
#!/usr/bin/env python
# File: announce.py
# Version: 0.9
# Description: a script for announcing XEPs
# Last Modified: 2016-10-03
# Author: Peter Saint-Andre (stpeter@jabber.org)
# License: public domain
# HowTo: ./announce.py xepnum
## LICENSE ##
#
# Copyright (c) 1999 - 2010 XMPP Standards Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
## END LICENSE ##
# IMPORTS:
#
import glob
import os
from select import select
import smtplib
import socket
from string import split,strip,join,find
import sys
import time
from xml.dom.minidom import parse,parseString,Document
def getText(nodelist):
thisText = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
thisText = thisText + node.data
return thisText
# get the seconds in the Unix era
now = int(time.time())
# READ IN ARGS:
#
# 1. XEP number
xepnum = sys.argv[1];
xepfile = 'xep-' + xepnum + '.xml'
# PARSE XEP HEADERS:
#
# - title
# - abstract
# - version
# - date
# - initials
# - remark
thexep = parse(xepfile)
xepNode = (thexep.getElementsByTagName("xep")[0])
headerNode = (xepNode.getElementsByTagName("header")[0])
titleNode = (headerNode.getElementsByTagName("title")[0])
title = getText(titleNode.childNodes)
abstractNode = (headerNode.getElementsByTagName("abstract")[0])
abstract = getText(abstractNode.childNodes)
statusNode = (headerNode.getElementsByTagName("status")[0])
xepstatus = getText(statusNode.childNodes)
typeNode = (headerNode.getElementsByTagName("type")[0])
xeptype = getText(typeNode.childNodes)
revNodes = headerNode.getElementsByTagName("revision")
revNode = revNodes[0]
versionNode = (revNode.getElementsByTagName("version")[0])
version = getText(versionNode.childNodes)
dateNode = (revNode.getElementsByTagName("date")[0])
date = getText(dateNode.childNodes)
initialsNode = (revNode.getElementsByTagName("initials")[0])
initials = getText(initialsNode.childNodes)
remNode = (revNode.getElementsByTagName("remark")[0])
# could be <p> or <ul>
testRemarkNode = remNode.firstChild.nodeName
# print testRemarkNode
if (testRemarkNode == "p"):
remarkNode = (remNode.getElementsByTagName("p")[0])
remark = getText(remarkNode.childNodes)
else:
remark = "[See revision history]"
# what kind of action are we taking?
xepflag = ""
if (version == "0.1" or version == "0.1.0"):
xepflag = "new"
elif ((version == "1.0" or version == "1.0.0") and xeptype == "Standards Track"):
xepflag = "draft"
elif ((version == "1.0" or version == "1.0.0") and xeptype != "Standards Track"):
xepflag = "active"
elif (version == "2.0" or version == "2.0.0"):
xepflag = "final"
elif (xepstatus == "Retracted"):
xepflag = "retract"
elif (xepstatus == "Deprecated"):
xepflag = "deprecate"
elif (xepstatus == "Obsolete"):
xepflag = "obsolete"
elif (xepstatus == "Deferred"):
xepflag = "defer"
# generate the diffs URL
if len(revNodes) > 1:
prevRevNode = revNodes[1]
prevVersionNode = (prevRevNode.getElementsByTagName("version")[0])
prevVersion = getText(prevVersionNode.childNodes)
diffs = 'https://xmpp.org/extensions/diff/api/xep/' + xepnum + '/diff/' + prevVersion + '/vs/' + version
else:
diffs = 'N/A'
## SEND MAIL:
#
# From: editor@xmpp.org
# To: standards@xmpp.org
# Subject: UPDATED: XEP-$xepnum ($title)
# [or "NEW..." if version 0.1]
# Body:
# Version $version of XEP-$xepnum ($title) is now available.
# Abstract: $abstract
# Changelog: $remark ($initials)
# Diff: $diffs ### no longer in use
# URL: https://xmpp.org/extensions/xep-$xepnum.html
fromaddr = "editor@xmpp.org"
# for testing...
# toaddrs = "stpeter@jabber.org"
# for real...
toaddrs = "standards@xmpp.org"
if xepflag == "new":
thesubject = 'NEW: XEP-'
elif xepflag == "draft":
thesubject = 'DRAFT: XEP-'
elif xepflag == "final":
thesubject = 'FINAL: XEP-'
elif xepflag == "active":
thesubject = 'ACTIVE: XEP-'
elif xepflag == "retract":
thesubject = 'RETRACTED: XEP-'
elif xepflag == "deprecate":
thesubject = 'DEPRECATED: XEP-'
elif xepflag == "obsolete":
thesubject = 'OBSOLETE: XEP-'
elif xepflag == "defer":
thesubject = 'DEFERRED: XEP-'
else:
thesubject = 'UPDATED: XEP-'
thesubject = thesubject + xepnum + ' (' + title + ')'
versionline = 'Version ' + version + ' of XEP-' + xepnum + ' (' + title + ') has been released.'
abstractline = 'Abstract: ' + abstract
changelogline = 'Changelog: ' + remark + ' (' + initials + ')'
diffsline = 'Diff: ' + diffs
urlline = 'URL: https://xmpp.org/extensions/xep-' + xepnum + '.html'
msg = "From: XMPP Extensions Editor <%s>\r\n" % fromaddr
msg = msg + "To: %s\r\n" % toaddrs
msg = msg + "Subject: %s\r\n" % thesubject
msg = msg + versionline
msg = msg + "\r\n\n"
msg = msg + abstractline
msg = msg + "\r\n\n"
msg = msg + changelogline
msg = msg + "\r\n\n"
msg = msg + diffsline
msg = msg + "\r\n\n"
msg = msg + urlline
msg = msg + "\r\n\n"
server = smtplib.SMTP('localhost')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()
# END

432
gen.py

@ -1,432 +0,0 @@
#!/usr/bin/env python
# File: gen.py
# Version: 0.2
# Description: a renewed XEP compilation tool
# Last Modified: 2009
# Author: Tobias Markmann (tm@ayena.de)
# HowTo: ./gen.py xep-####.xml
## LICENSE ##
#
# Copyright (c) 1999 - 2010 XMPP Standards Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
## END LICENSE ##
import pickle
import commands
import os
import re
import sys
import getopt
import glob
import tempfile
from xepinfo import XEPInfo
from xeputil import getLatestXEPFilename
from xml.dom.minidom import parse,parseString,Document,getDOMImplementation
# for serializing inline images
import base64
import urlparse
import urllib
XEPPATH = "/var/www/vhosts/xmpp.org/extensions"
CONFIGPATH = "/var/local/xsf"
verbose = False
fast = False
last_build = {}
files_to_delete = [];
def serializeInlineImage(output_dir, xep_nr, no, attrValue):
up = urlparse.urlparse(attrValue)
if up.scheme == 'data':
head, data = up.path.split(',')
bits = head.split(';')
mime_type = bits[0] if bits[0] else 'text/plain'
charset, b64 = 'ASCII', False
for bit in bits[1]:
if bit.startswith('charset='):
charset = bit[8:]
elif bit == 'base64':
b64 = True
# Do something smart with charset and b64 instead of assuming
plaindata = base64.b64decode(data)
# Do something smart with mime_type
if mime_type in ('image/png', 'image/jpeg'):
file_ext = mime_type.split('/')[1]
f = open(output_dir + '/' + 'inlineimage-' + xep_nr + '-' + str(no) + '.' + file_ext, 'wb')
f.write(plaindata)
elif up.scheme == 'http':
file_name, file_ext = os.path.splitext(up.path)
urllib.urlretrieve(attrValue, output_dir + '/' + 'inlineimage-' + xep_nr + '-' + str(no) + file_ext)
def serializeXEPInlineImages(output_dir, xep_nr, filename):
dom = parse(filename)
imgs = dom.getElementsByTagName('img')
for (no, img) in enumerate(imgs):
serializeInlineImage(output_dir, xep_nr, no, img.attributes["src"].value)
def getText(nodelist):
thisText = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
thisText = thisText + node.data
return thisText
def executeCommand( cmd ):
error, desc = commands.getstatusoutput( cmd )
return error, desc + "\n" + "executed cmd: " + cmd
## creates a HTML table (for the human reader) and XML table (for bots)
class XEPTable:
def __init__(self, filename, shortXMLfilename):
self.filename = filename
self.shortXMLfilename = shortXMLfilename
try:
self.tableFile = parse(filename)
except:
impl = getDOMImplementation()
self.tableFile = impl.createDocument(None, "table", None)
self.tableFile.getElementsByTagName("table")[0].setAttribute("class", "sortable")
self.tableFile.getElementsByTagName("table")[0].setAttribute("id", "xeplist")
self.tableFile.getElementsByTagName("table")[0].setAttribute("cellspacing", "0")
self.tableFile.getElementsByTagName("table")[0].setAttribute("cellpadding", "3")
self.tableFile.getElementsByTagName("table")[0].setAttribute("border", "1")
header = parseString(
'''<tr class='xepheader'>
<th align='left'>Number</th>
<th align='left'>Name</th>
<th align='left'>Type</th>
<th align='left'>Status</th>
<th align='left'>Date</th>
</tr>''')
self.tableFile.getElementsByTagName("table")[0].appendChild(header.getElementsByTagName("tr")[0])
try:
self.botsFile = parse(shortXMLfilename)
except:
impl = getDOMImplementation()
self.botsFile = impl.createDocument(None, "xeps", None)
def save(self):
f = open(self.filename, "wb")
self.tableFile.getElementsByTagName("table")[0].normalize()
f.write(self.tableFile.toxml())
f.close()
f = open(self.shortXMLfilename, "wb")
self.botsFile.getElementsByTagName("xeps")[0].normalize()
f.write(self.botsFile.toxml())
f.close()
def setXEP(self, info):
## set for HTML table
rows = self.tableFile.getElementsByTagName("tr")
xeprow = 0
for row in rows:
if row.getAttribute("id") == "xep" + info.getNr():
xeprow = row
break
if xeprow == 0:
xeprow = self.tableFile.createElement("tr")
self.tableFile.getElementsByTagName("table")[0].appendChild(xeprow)
self.tableFile.getElementsByTagName("table")[0].appendChild(self.tableFile.createTextNode('''
'''))
xeprow.setAttribute("id", "xep" + info.getNr())
xeprow.setAttribute("class", "tablebody XEP-" + info.getStatus())
else:
xeprow.setAttribute("class", "tablebody XEP-" + info.getStatus())
while(xeprow.hasChildNodes()):
xeprow.removeChild(xeprow.firstChild)
col = parseString('''<td valign='top'><a href='/extensions/xep-''' + info.getNr() + ".html'>XEP-" + info.getNr() + '''</a> <a href='/extensions/xep-''' + info.getNr() + '''.pdf'>(PDF)</a></td>''')
xeprow.appendChild(col.getElementsByTagName("td")[0])
col = parseString("<td valign='top'>" + info.getTitle() + "</td>")
xeprow.appendChild(col.getElementsByTagName("td")[0])
col = parseString("<td valign='top'>" + info.getType() + "</td>")
xeprow.appendChild(col.getElementsByTagName("td")[0])
col = parseString("<td valign='top'>" + info.getStatus() + "</td>")
xeprow.appendChild(col.getElementsByTagName("td")[0])
col = parseString("<td valign='top'>" + info.getDate() + "</td>")
xeprow.appendChild(col.getElementsByTagName("td")[0])
## set for bots file
xeps = self.botsFile.getElementsByTagName("xep")
xep = 0
for xeps_xep in xeps:
if xeps_xep.getElementsByTagName("number")[0].firstChild.data == info.getNr():
xep = xeps_xep
break
if xep == 0:
xep = self.botsFile.createElement("xep")
self.botsFile.getElementsByTagName("xeps")[0].appendChild(xep)
self.botsFile.getElementsByTagName("xeps")[0].appendChild(self.botsFile.createTextNode('''
'''))
else:
while(xep.hasChildNodes()):
xep.removeChild(xep.firstChild)
child = parseString("<number>" + info.getNr() + "</number>")
xep.appendChild(child.getElementsByTagName("number")[0])
child = parseString("<name>" + info.getTitle() + "</name>")
xep.appendChild(child.getElementsByTagName("name")[0])
child = parseString("<type>" + info.getType() + "</type>")
xep.appendChild(child.getElementsByTagName("type")[0])
child = parseString("<status>" + info.getStatus() + "</status>")
xep.appendChild(child.getElementsByTagName("status")[0])
child = parseString("<updated>" + info.getDate() + "</updated>")
xep.appendChild(child.getElementsByTagName("updated")[0])
child = parseString("<shortname>" + info.getShortname() + "</shortname>")
xep.appendChild(child.getElementsByTagName("shortname")[0])
child = parseString("<abstract>" + info.getAbstract() + "</abstract>")
xep.appendChild(child.getElementsByTagName("abstract")[0])
def filebase( filename ):
return os.path.splitext(os.path.basename(filename))[0]
def checkError( error, desc):
global verbose
if error != 0:
if verbose:
print "Error: ", desc
return False
return True
def fileHash( filename ):
f = open(filename, "rb")
import hashlib
h = hashlib.sha1()
h.update(f.read())
hash = h.hexdigest()
f.close()
return hash
def loadDict( filename ):
try:
f = open(filename, "rb")
di = pickle.load(f)
f.close()
return di
except:
print "failed loading dict."
return {}
def saveDict( filename, di ):
f = open(filename, "w")
pickle.dump(di, f)
f.close()
def buildXHTML( file, nr ):
error, desc = executeCommand("xsltproc xep.xsl " + file + " > " + XEPPATH + "/xep-" + nr + ".html")
if not checkError(error, desc):
return False
error, desc = executeCommand("xsltproc ref.xsl xep-" + nr + ".xml > " + XEPPATH + "/refs/reference.XSF.XEP-" + nr + ".xml")
if not checkError(error, desc):
return False
error, desc = executeCommand("xsltproc examples.xsl xep-" + nr + ".xml > " + XEPPATH + "/examples/" + nr + ".xml")
if not checkError(error, desc):
return False
error, desc = executeCommand(" cp xep-" + nr + ".xml " + XEPPATH + "/")
if not checkError(error, desc):
return False
return True
def buildPDF( file, nr ):
serializeXEPInlineImages("/tmp/xepbuilder", nr, file)
error, desc = executeCommand("xsltproc -o /tmp/xepbuilder/xep-" + nr + ".tex.xml xep2texml.xsl " + file)
if not checkError(error, desc):
return False
error, desc = executeCommand("texml -e utf8 /tmp/xepbuilder/xep-" + nr + ".tex.xml /tmp/xepbuilder/xep-" + nr + ".tex")
if not checkError(error, desc):
return False
#detect http urls and escape them to make them breakable
# this should match all urls in free text; not the urls in xml:ns or so..so no " or ' in front.
error, desc = executeCommand('''sed -i 's|\([\s"]\)\([^"]http://[^ "]*\)|\1\\path{\2}|g' /tmp/xepbuilder/xep-''' + nr + ".tex")
if not checkError(error, desc):
return False
#adjust references
error, desc = executeCommand('''sed -i 's|\\hyperref\[#\([^}]*\)\]|\\hyperref\[\1\]|g' /tmp/xepbuilder/xep-''' + nr + ".tex")
if error != 0:
if verbose == 1:
print "Error: ", desc
return False
error, desc = executeCommand('''sed -i 's|\\pageref{#\([^}]*\)}|\\pageref{\1}|g' /tmp/xepbuilder/xep-''' + nr + ".tex")
if not checkError(error, desc):
return False
olddir = os.getcwd()
os.chdir("/tmp/xepbuilder")
error, desc = executeCommand("xelatex -interaction=batchmode xep-" + nr + ".tex")
#if not checkError(error, desc):
# os.chdir(olddir)
# return False
#error, desc = executeCommand("xelatex -interaction=batchmode xep-" + nr + ".tex")
#if not checkError(error, desc):
# os.chdir(olddir)
# return False
os.chdir(olddir)
error, desc = executeCommand("cp /tmp/xepbuilder/xep-" + nr + ".pdf " + XEPPATH + "/")
if not checkError(error, desc):
return False
return True
def buildXEP( filename ):
nr = re.match("xep-(\d\d\d\d).xml", filename).group(1)
xepfilepath = getLatestXEPFilename("./", nr);
if not xepfilepath:
print "getLatestXEPContent (ERROR)"
return
files_to_delete.append(xepfilepath)
if not fast:
print "Building " + filename + ": ",
if buildXHTML( xepfilepath, nr ):
print "XHTML(OK) / ",
else:
print "XHTML(ERROR) / ",
if buildPDF( xepfilepath, nr ):
print "PDF(OK)"
else:
print "PDF(ERROR)"
else:
print "Building " + filename + " (FAST MODE)"
x = XEPTable(CONFIGPATH + "/extensions.xml", XEPPATH + "/xeps.xml")
xinfo = XEPInfo(xepfilepath, False)
x.setXEP( xinfo )
x.save()
def buildAll():
files = glob.glob('xep-????.xml')
files.sort(key=lambda x: x.lower())
for file in files:
buildXEP( file )
def makeBundle():
print "Creating the bundle...",
executeCommand("mkdir /tmp/xepbundle")
executeCommand("cp " + XEPPATH + "/*.pdf " + "/tmp/xepbundle")
executeCommand("tar -cf /tmp/xepbundle.tar -C /tmp xepbundle")
executeCommand("pbzip2 -f -9 /tmp/xepbundle.tar")
executeCommand("mv -f /tmp/xepbundle.tar.bz2 " + XEPPATH + "/xepbundle.tar.bz2")
executeCommand("rm -rfd /tmp/xepbundle")
print "DONE"
def usage():
print "gen.py: generate nice XHTML and beautiful PDF out of the XEP XML files"
print ""
print "Usage:"
print "gen.py xep-####.xml"
print ""
print "Options:"
print "-v Enable verbose output for debugging."
print "-a Build all available XEPs."
print "-f Fast; means no actual compiling is done."
def main(argv):
global verbose
global CONFIGPATH
global fast
buildall = False
try:
options, remainder = getopt.gnu_getopt(argv, "vaf")
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in options:
if opt in ('-v'):
verbose = True
elif opt in ('-a'):
buildall = True
elif opt in ('-f'):
fast = True
if len(remainder) > 0:
try:
xep = int(remainder[0])
xep = "xep-%04d.xml" % xep
except:
xep = remainder[0]
executeCommand("mkdir /tmp/xepbuilder")
executeCommand("cp ../images/xmpp.pdf /tmp/xepbuilder/xmpp.pdf")
executeCommand("cp ../images/xmpp-text.pdf /tmp/xepbuilder/xmpp-text.pdf")
executeCommand("cp -r deps/* /tmp/xepbuilder/")
executeCommand("cp xep.ent /tmp/xep.ent")
files_to_delete.append("/tmp/xep.ent")
if buildall:
buildAll()
else:
buildXEP( xep )
# remove xep temporary files
for filename in files_to_delete:
executeCommand("rm " + filename)
executeCommand("sed -e '1s/<?[^?]*?>//' " + CONFIGPATH + "/extensions.xml > " + XEPPATH + "/../includes/xeplist.txt")
executeCommand("rm -rfd /tmp/xepbuilder")
makeBundle()
if __name__ == "__main__":
main(sys.argv[1:])

@ -23,7 +23,7 @@
<dependencies>
<spec>XMPP Core</spec>
</dependencies>
<supersedes>XEP-0077</supersedes>
<supersedes><spec>XEP-0077</spec></supersedes>
<supersededby/>
<shortname>NOT_YET_ASSIGNED</shortname>
<author>
@ -72,11 +72,11 @@
<di>
<dt>Account Management</dt>
<dd>Gestion of a client XMPP account on a given server deployment. An account is being defined by the locale part of the JID, and by the authentication mechanisms available for this account.</dd>
<dt>In-Band</dt>
<dd>Capacity of acting directly through a XMPP stream. As a consequence, an In-Band Account Management MAY allow users to bootstrap the existence of their account, and oppositely to end it, or modify it, without ever having to use another medium (like the web for instance), or without the existence of a previous account.</dd>
<dt>Storage Mechanism</dt>
<dd>The internal logics of a server to store authentication data.</dd>
</di>
<di><dt>In-Band</dt>
<dd>Capacity of acting directly through a XMPP stream. As a consequence, an In-Band Account Management MAY allow users to bootstrap the existence of their account, and oppositely to end it, or modify it, without ever having to use another medium (like the web for instance), or without the existence of a previous account.</dd></di>
<di><dt>Storage Mechanism</dt>
<dd>The internal logics of a server to store authentication data.</dd></di>
</dl>
</section1>
<section1 topic='Use Cases' anchor='usecases'>
@ -481,13 +481,13 @@
<li>For a better user experience, it is important to provide &lt;challenge/> fallbacks rather than directly fail with a &lt;failure/> on the first user error. <cite>XEP-0158</cite> for instance proposes to fail directly on a wrong answer to a CAPTCHA challenge, while it is well known that many actual human users regularly fails, sometimes even several, captchas before succeeding one. As a consequence, in such a case or other similar challenge, it is better to return a new challenge (a new CAPTCHA for instance) rather than cancelling the negotiation, doing so eventually only after a few retries.</li>
<li>The storage mechanism does not mean necessarily that credentials have to be stored in the given form, in particular when the server estimates a storage mechanism being stronger than the one desired by the user, and still without losing any authentication possibility. For instance if a client was to exchange credentials in PLAIN, and if the server usually provides SCRAM-SHA1, SCRAM-SHA-256 and PLAIN authentication mechanisms, then it would be wise to store it as both SCRAM-SHA-1 and SCRAM-SHA-256 instead of PLAIN. This way the security of the password is highly renforced in the case of the database being stolen and the three authentication mechanisms are still available to this user. Of course it would have been even more secure for the client itself to exchange the credentials as SCRAM-SHA-1 and SCRAM-SHA-256 from the start, because by exchanging PLAIN data, possibility is given to a man-in-the-middle attack to steal directly the password during this one-time account registration.</li>
<li>Security may have a price. If you were to store credentials with SCRAM-SHA-256 only, and later you change to a client which supports only PLAIN and SCRAM-SHA-1, you may fall in a case where you force yourself to log in using PLAIN. For this reason, a good client implementation SHOULD be able to determine such edge case. In the previous case, if possible, it SHOULD have stored both SCRAM-SHA-1 and SCRAM-SHA-256 on registration or silently modify later its storage.</li>
<li>When registering a new account, considering that the user has obviously not been authenticated yet, the server MUST NOT rely on the 'from' value of the initiating stream. In particular:</li>
<li>When registering a new account, considering that the user has obviously not been authenticated yet, the server MUST NOT rely on the 'from' value of the initiating stream. In particular:
<ul>
<li>the server MUST NOT decide whether or not to propose the &lt;registration/> feature based on the existence of the JID filled in 'from'. Doing so would leak away information about the existence of a JID which could therefore be sent undesirable messages. Being consistent on showing the feature forces to try to register which can already eliminates part of the risk if the challenge provides some bot protection (for instance CAPTCHAs).
<br />A perfectly valid alternative would be to always provide the &lt;registration/> feature when no "from" is filled but never provide it when a "from is filled" (no matter it is an existing JID or not). This consistent logics does not leak information.</li>
<li>Do not modify the SASL authentication's mechanisms listed in the &lt;mechanisms/> feature depending on the 'from'. Even though a given JID might not be able to connect with some mechanisms because the credentials storage is incompatible, this would leak information on the kind of storage mechanism used for this user. This information would allow attackers to determine, then target, users whose storage would be weaker.
<br/>Of course this particular point might cause issues for users regularly changing their clients or log in from various computer. For instance the SCRAM-SHA-1 and SCRAM-SHA-256 storage are incompatible. If you first registered by specifying the SCRAM-SHA-256 storage, then on another client which does not support SCRAM-SHA-256 or even who supports it, but for some reason always try and gives priority to SCRAM-SHA-1, the user could be found in a situation where he never manages to authenticate while providing the right password. For this reason, it could be wiser for server deployments to choose compatible mechanisms, when possible. On client side, if they are provided a raw password, instead of pre-computed data for a specific mechanism, then they should intelligently try the various mechanisms, starting from the one they consider the stronger. Hence try SCRAM-SHA-256, then SCRAM-SHA-1 if the first failed, then only if both failed, tell the user that authentication failed (note that the client should not try PLAIN as a last fallback, because we remind that any SCRAM-* storage is compatible with the SASL PLAIN mechanism. Trying PLAIN would therefore be a security risk.</li>
</ul>
<li><p>the server MUST NOT decide whether or not to propose the &lt;registration/> feature based on the existence of the JID filled in 'from'. Doing so would leak away information about the existence of a JID which could therefore be sent undesirable messages. Being consistent on showing the feature forces to try to register which can already eliminates part of the risk if the challenge provides some bot protection (for instance CAPTCHAs).</p>
<p>A perfectly valid alternative would be to always provide the &lt;registration/> feature when no "from" is filled but never provide it when a "from is filled" (no matter it is an existing JID or not). This consistent logics does not leak information.</p></li>
<li><p>Do not modify the SASL authentication's mechanisms listed in the &lt;mechanisms/> feature depending on the 'from'. Even though a given JID might not be able to connect with some mechanisms because the credentials storage is incompatible, this would leak information on the kind of storage mechanism used for this user. This information would allow attackers to determine, then target, users whose storage would be weaker.</p>
<p>Of course this particular point might cause issues for users regularly changing their clients or log in from various computer. For instance the SCRAM-SHA-1 and SCRAM-SHA-256 storage are incompatible. If you first registered by specifying the SCRAM-SHA-256 storage, then on another client which does not support SCRAM-SHA-256 or even who supports it, but for some reason always try and gives priority to SCRAM-SHA-1, the user could be found in a situation where he never manages to authenticate while providing the right password. For this reason, it could be wiser for server deployments to choose compatible mechanisms, when possible. On client side, if they are provided a raw password, instead of pre-computed data for a specific mechanism, then they should intelligently try the various mechanisms, starting from the one they consider the stronger. Hence try SCRAM-SHA-256, then SCRAM-SHA-1 if the first failed, then only if both failed, tell the user that authentication failed (note that the client should not try PLAIN as a last fallback, because we remind that any SCRAM-* storage is compatible with the SASL PLAIN mechanism. Trying PLAIN would therefore be a security risk.</p></li>
</ul></li>
</ul>
</section1>
<section1 topic='IANA Considerations' anchor='iana'>

@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE xep SYSTEM 'xep.dtd' [
<!ENTITY % ents SYSTEM 'xep.ent'>
<!ENTITY rfc3920bis "<span class='ref'><link url='http://tools.ietf.org/html/draft-ietf-saintandre-rfc3920bis'>rfc3920bis</link></span> <note>RFC 3920: Extensible Messaging and Presence Protocol (XMPP): Core &lt;<link url='http://tools.ietf.org/html/draft-ietf-saintandre-rfc3920bis'>http://tools.ietf.org/html/draft-ietf-saintandre-rfc3920bis</link>&gt;.</note>" >
%ents;
]>
<?xml-stylesheet type='text/xsl' href='xep.xsl'?>

@ -8,14 +8,44 @@
]>
<?xml-stylesheet type='text/xsl' href='xep.xsl'?>
<xep>
<header xml:base="sections/header.xml">
<header>
<title>Buddycloud Channels</title>
<abstract>This document describes a profile and conventions for usage of the PubSub protocol in the context of a new type of communication.</abstract>
&LEGALNOTICE;
<number>xxxx</number>
<status>ProtoXEP</status>
<type>Standards Track</type>
<sig>Standards</sig>
<approver>Council</approver>
<dependencies>
<spec>XMPP Core</spec>
<spec>XEP-0059</spec>
<spec>XEP-0060</spec>
<spec>XEP-0313</spec>
<spec>XEP-0255</spec>
<spec>XEP-0107</spec>
</dependencies>
<supersedes/>
<supersededby/>
<shortname>NOT_YET_ASSIGNED</shortname>
<author>
<firstname>Simon</firstname>
<surname>Tennant</surname>
<email>simon@buddycloud.com</email>
<jid>simon@buddycloud.com</jid>
</author>
<author>
<firstname>Lloyd</firstname>
<surname>Watkin</surname>
<email>lloyd@evilprofessor.co.uk</email>
<jid>lloyd@evilprofessor.co.uk</jid>
</author>
<author>
<firstname>Ashley</firstname>
<surname>Ward</surname>
<email>ashley.ward@surevine.com</email>
<jid>ashley.ward@surevine.com</jid>
</author>
<revision>
<version>0.0.2</version>
<date>2014-04-29</date>
@ -24,92 +54,8 @@
<p>First draft.</p>
</remark>
</revision>
<shortname>NOT_YET_ASSIGNED</shortname>
<legal>
<copyright>This XMPP Extension Protocol is copyright (c) 1999 - 2014
by the XMPP Standards Foundation (XSF).
</copyright>
<permissions> Permission is hereby granted, free of charge, to any
person obtaining a copy of this specification (the
"Specification"), to make use of the Specification without
restriction, including without limitation the rights to implement
the Specification in a software program, deploy the Specification in
a network service, and copy, modify, merge, publish, translate,
distribute, sublicense, or sell copies of the Specification, and to
permit persons to whom the Specification is furnished to do so,
subject to the condition that the foregoing copyright notice and
this permission notice shall be included in all copies or
substantial portions of the Specification. Unless separate
permission is granted, modified works that are redistributed shall
not contain misleading information regarding the authors, title,
number, or publisher of the Specification, and shall not claim
endorsement of the modified works by the authors, any organization
or project to which the authors belong, or the XMPP Standards
Foundation.
</permissions>
<warranty> ## NOTE WELL: This Specification is provided on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, express or implied, including, without limitation, any
warranties or conditions of TITLE, NON-INFRINGEMENT,
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. In no event
shall the XMPP Standards Foundation or the authors of this
Specification be liable for any claim, damages, or other liability,
whether in an action of contract, tort, or otherwise, arising from,
out of, or in connection with the Specification or the
implementation, deployment, or other use of the Specification. ##
</warranty>
<liability> In no event and under no legal theory, whether in tort
(including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or
agreed to in writing, shall the XMPP Standards Foundation or any
author of this Specification be liable for damages, including any
direct, indirect, special, incidental, or consequential damages of
any character arising out of the use or inability to use the
Specification (including but not limited to damages for loss of
goodwill, work stoppage, computer failure or malfunction, or any and
all other commercial damages or losses), even if the XMPP Standards
Foundation or such author has been advised of the possibility of
such damages.
</liability>
<conformance>
This XMPP Extension Protocol has been contributed in full
conformance with the XSF's Intellectual Property Rights Policy (a
copy of which may be found at &lt;
<link url="http://xmpp.org/extensions/ipr-policy.shtml">http://xmpp.org/extensions/ipr-policy.shtml</link>
&gt; or obtained by writing to XSF, P.O. Box 1641, Denver, CO 80201
USA).
</conformance>
</legal>
<dependencies>
<spec>XMPP Core</spec>
<spec>XEP-0059</spec>
<spec>XEP-0060</spec>
<spec>XEP-0313</spec>
<spec>XEP-0255</spec>
<spec>XEP-0107</spec>
</dependencies>
<supersedes/>
<supersededby/>
<author xml:base="authors/simon-tennant.xml">
<firstname>Simon</firstname>
<surname>Tennant</surname>
<email>simon@buddycloud.com</email>
<jid>simon@buddycloud.com</jid>
</author>
<author xml:base="authors/lloyd-watkin.xml">
<firstname>Lloyd</firstname>
<surname>Watkin</surname>
<email>lloyd@evilprofessor.co.uk</email>
<jid>lloyd@evilprofessor.co.uk</jid>
</author>
<author xml:base="authors/ashley-ward.xml">
<firstname>Ashley</firstname>
<surname>Ward</surname>
<email>ashley.ward@surevine.com</email>
<jid>ashley.ward@surevine.com</jid>
</author>
</header>
<section1 topic="Buddycloud Introduction" anchor="Buddycloud Introduction" xml:base="sections/10.intro.xml">
<section1 topic="Buddycloud Introduction" anchor="Buddycloud Introduction">
<section2 topic="About Buddycloud" anchor="about-buddycloud">
<p>The Buddycloud project is a set of independently deployable services, that
inter-operate to create a rich collaboration service.
@ -188,7 +134,7 @@
</dl>
</section2>
</section1>
<section1 topic="Service Discovery" anchor="Finding-the-right-Buddycloud-server" xml:base="sections/30.discovery.xml">
<section1 topic="Service Discovery" anchor="Finding-the-right-Buddycloud-server">
<p>Each XMPP domain can have one Buddycloud server that serves user's channels.
Buddycloud clients and servers need to be able to discover the authoratative
Buddycloud server. find the
@ -196,7 +142,7 @@
<section2 topic="Discovery order" anchor="DISCOthenDNS">
<p>
To find the correct remote Buddycloud service for a domain, the Buddycloud
server should:
server should:</p>
<ol>
<li>Use a disco#items query against the XMPP service for the remote
Buddycloud domain.
@ -206,7 +152,6 @@
discovery method.
</li>
</ol>
</p>
</section2>
<section2 topic="Discovery using Disco" anchor="disco-discovery">
<p>The Buddycloud service first sends an items discovery request to the domain
@ -274,12 +219,12 @@
<p>
This example delegates all the Buddycloud service to an XMPP component
running the Buddycloud named
<i>buddycloud-component.verona.lit</i>
<em>buddycloud-component.verona.lit</em>
.
</p>
</section2>
</section1>
<section1 topic="Register" anchor="register" xml:base="sections/40.register.xml">
<section1 topic="Register" anchor="register">
<p>Upon connection to the buddycloud server a user should send a register
stanza.</p>
@ -300,15 +245,16 @@
</p>
</section1>
<section1 topic="Channel and Node Configuration" anchor="channel-config" xml:base="sections/50.node-config.xml">
<section1 topic="Channel and Node Configuration" anchor="channel-config">
<p>Node metadata is used to describe the channel to users. All nodes in a
channel have the same metadata and permission.
</p>
<section2 topic="Get Node Configuration" anchor="get-node-config"> - using disco-info
with the node specified - using <cite>XEP-0060</cite> 5.4 Discover Node Metadata
<section2 topic="Get Node Configuration" anchor="get-node-config">
<p>- using disco-info
with the node specified - using &xep0060; 5.4 Discover Node Metadata</p>
</section2>
<section2 topic="Set Node Configuration" anchor="set-node-config"> set Not sure what
goes here?
<section2 topic="Set Node Configuration" anchor="set-node-config"><p> set Not sure what
goes here?</p>
</section2>
<section2 topic="Default roles" anchor="default-roles">
<p> minimum setting/optional recommended fallbacks
@ -401,7 +347,6 @@
<p>Channel owners and moderators can also set the default affiliation for the
channel
</p>
<p>
<table caption="Channel Types">
<tr>
<th>Channel Type</th>
@ -429,8 +374,6 @@
</td>
</tr>
</table>
</p>
<p>
<table caption="Channel Access Models">
<tr>
<th>Access Model</th>
@ -455,7 +398,6 @@
</td>
</tr>
</table>
</p>
</section2>
<section2 topic="Well known nodes" anchor="well-known-nodes">
<p>Buddycloud is designed to be extended with new node and content types. To
@ -523,16 +465,14 @@
</table>
</section2>
</section1>
<section1 topic="Business Logic" anchor="biz-logic" xml:base="sections/55.application-logic.xml">
<section1 topic="Business Logic" anchor="biz-logic">
<section2 topic="PubSub for Humans" anchor="PubSub-for-humans">
<p> Buddycloud adapts <cite>XEP-0060</cite>'s machine-to-machine design goals with logic
and presets that work better in a social person-to-person and person-to-group
environment. For example, to discourage "glorifying the wicked", the list of
banned users is only presented to the channel's moderators.
</p>
<p>
<table caption="channel read permissions">
<thead>
<tr>
<th>Property</th>
<th>Access model</th>
@ -544,8 +484,6 @@
<th>Anonymous (e.g. web)</th>
<th>Banned users</th>
</tr>
</thead>
<tbody>
<tr>
<td>channel name</td>
<td>all</td>
@ -665,12 +603,8 @@
<td>no</td>
<td>no</td>
</tr>
</tbody>
</table>
</p>
<p>
<table caption="channel write permissions">
<thead>
<tr>
<th>Property</th>
<th>Producer</th>
@ -680,8 +614,6 @@
<th>Anonymous (e.g. web)</th>
<th>Banned users</th>
</tr>
</thead>
<tbody>
<tr>
<td>change channel name</td>
<td>only at creation time</td>
@ -745,9 +677,7 @@
<td>no</td>
<td>no</td>
</tr>
</tbody>
</table>
</p>
</section2>
<section2 topic="Maintain Similar Affiliations across Channel Nodes" anchor="rules-nodeaffiliations">
<p>A Buddycloud server MUST maintain similar affiliations and permissions for a subscribed
@ -762,7 +692,7 @@
</p>
</section2>
</section1>
<section1 topic="Pubsub Items" anchor="items" xml:base="sections/60.items.xml">
<section1 topic="Pubsub Items" anchor="items">
<p>Many of the item use cases follow those from <cite>XEP-0060</cite>. This section notes
the departures from the parent XEP and specific requirements.
@ -864,8 +794,8 @@
<iq from="buddycloud.capulet.lit" to="juliet@capulet.lit/bc-app" type="result" id="retractitem:32"/>
]]>
</example>
A retraction message is sent to all online clients, with an Atom tombstone to
replace the deleted post
<p>A retraction message is sent to all online clients, with an Atom tombstone to
replace the deleted post</p>
<example caption="The Buddycloud server sends retractions out to online clients">
<![CDATA[
<message from="buddycloud.capulet.lit" id="bc:MGV3B" to="benvolio@montague.lit">
@ -900,7 +830,7 @@
<strong>review</strong>
; extensions.
</p>
<section3 topic="Publishing" anchor="item-publishing"/>
<section3 topic="Publishing" anchor="item-publishing">
<p>The minimal payload for a publish request must be formatted as follows:
</p>
<example caption="The Entity publishes to a node">
@ -962,11 +892,11 @@
</li>
</ul>
</section4>
<section3 topic="Threading" anchor="item-threading"/>
</section3>
<section3 topic="Threading" anchor="item-threading">
<p>
Posts in Buddycloud can be formed into threads consisting of a parent post
and comments to a maximum thread depth of 1. Posts follow the
<a href="http://tools.ietf.org/html/rfc4685">ATOM threading specification</a>
and comments to a maximum thread depth of 1. Posts follow the &rfc4685;
and utilise the &amp;
<strong>thread</strong>
; namespace with the 'ref' attribute referring to the full global ID of the
@ -992,7 +922,7 @@
</entry>
]]>
</example>
<section4 topic="Error Cases" anchor="item-threading-errorcases"/>
<section4 topic="Error Cases" anchor="item-threading-errorcases">
<ul>
<li>
<em>parent-item-not-found</em>
@ -1003,7 +933,9 @@
: An attempt to comment on a comment will result in this error response
</li>
</ul>
<section3 topic="Referencing" anchor="item-referencing"/>
</section4>
</section3>
<section3 topic="Referencing" anchor="item-referencing">
<p>Within a single thread comments can reference other comments or the parent
item. This is for the purpose of making a comment to a post further back in
the thread.
@ -1052,6 +984,7 @@
</li>
</ul>
</section4>
</section3>
<section3 topic="Rating" anchor="item-rating">
<p>By
making use of the &amp;
@ -1119,13 +1052,13 @@
</section3>
</section2>
</section1>
<section1 topic="Channel subscriptions" anchor="subscriptions" xml:base="sections/70.subscriptions.xml">
<section1 topic="Channel subscriptions" anchor="subscriptions">
<p>Buddycloud clients follow <cite>XEP-0060</cite> subscription mechanisms for following and unfollowing a channel.</p>
</section1>
<section1 topic="Channel affiliations" anchor="affiliations" xml:base="sections/80.affiliations.xml">
<section1 topic="Channel affiliations" anchor="affiliations">
<section2 topic="Buddycloud to XEP-0060 mappings" anchor="affiliation-to-xep-0060-mappings">
<p>
Buddycloud channels build on XEP-0060's node affiliations.
Buddycloud channels build on XEP-0060's node affiliations.</p>
<table caption="Channel Affiliations">
<tr>
<th>XEP-0060 Affiliation</th>
@ -1183,10 +1116,9 @@
<td>RECOMMENDED</td>
</tr>
</table>
</p>
</section2>
</section1>
<section1 topic="Federation" anchor="federation" xml:base="sections/90.federation.xml">
<section1 topic="Federation" anchor="federation">
<section2 topic="Inbox" anchor="inbox">
</section2>
<section2 topic="Interaction With Other Services" anchor="Interaction-With-Other-Services">
@ -1204,7 +1136,7 @@
</p>
</section2>
</section1>
<section1 topic="Security Considerations" anchor="security-considerations" xml:base="sections/95.security.xml">
<section1 topic="Security Considerations" anchor="security-considerations">
<section2 topic="Server Trust" anchor="server-trust">
<p>
The Buddycloud server should make sure that the remote server

@ -0,0 +1,169 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE xep SYSTEM 'xep.dtd' [
<!ENTITY % ents SYSTEM 'xep.ent'>
%ents;
]>
<?xml-stylesheet type='text/xsl' href='xep.xsl'?>
<xep>
<header>
<title>Atomically Compare-And-Publish PubSub Items</title>
<abstract>This specification provides a mechanism to atomically Compare-And-Publish items to a PubSub node.</abstract>
&LEGALNOTICE;
<number>xxxx</number>
<status>ProtoXEP</status>
<type>Standards Track</type>
<sig>Standards</sig>
<approver>Council</approver>
<dependencies>
<spec>XEP-0030</spec>
<spec>XEP-0060</spec>
</dependencies>
<supersedes/>
<supersededby/>
<shortname>cap</shortname>
&flow;
<revision>
<version>0.0.1</version>
<date>2017-04-20</date>
<initials>fs</initials>
<remark><p>First draft.</p></remark>
</revision>
</header>
<section1 topic='Introduction' anchor='intro'>
<p>This specification provides a mechanism to atomically publish
items to a PubSub node depending on the item ID of the node's latest
item. This allows to prevent race conditions and avoids data
loss in certain situations.</p>
</section1>
<section1 topic='Discoverying Support' anchor='disco'>
<p>If an entity supports the Compared-And-Publish feature it MUST
advertise the fact by returning a &lt;feature/&gt; with the 'var'
attribute set to 'urn:xmpp:pubsub:cap:0' in response to a &xep0030;
query for information.</p>
</section1>
<section1 topic='Compare-And-Publish PubSub Items' anchor='cap'>
<p>In order to atomically compare-and-publish an item, a client
sends an &IQ; with a 'pubsub' element qualified by the
'urn:xmpp:pubsub:cap:0' namespace. The element MUST contain the same
attributes and elements as the &lt;publish/&gt; element defined in
&xep0060; <em>and</em> it MUST contain a previd attribute containing
an item ID.</p>
<p>The PubSub service MUST only publish the item if the node's
latest item ID is equal to the ID found in the 'previd'
attribute.</p>
<example caption='Publisher publishes an item using Compare-And-Publish'><![CDATA[
<iq type='set'
from='hamlet@denmark.lit/blogbot'
to='pubsub.shakespeare.lit'
id='compare-and-publish1'>
<pubsub xmlns='urn:xmpp:pubsub:cap:0'>
<compare-and-publish
node='princely_musings'
previd='1'>
<item>
<entry xmlns='http://www.w3.org/2005/Atom'>
<title>Soliloquy</title>
<summary>
To be, or not to be: that is the question:
Whether 'tis nobler in the mind to suffer
The slings and arrows of outrageous fortune,
Or to take arms against a sea of troubles,
And by opposing end them?
</summary>
<link rel='alternate' type='text/html'
href='http://denmark.lit/2003/12/13/atom03'/>
<id>tag:denmark.lit,2003:entry-32397</id>
<published>2003-12-13T18:30:02Z</published>
<updated>2003-12-13T18:30:02Z</updated>
</entry>
</item>
</publish>
</pubsub>
</iq>
]]></example>
<section2 topic='Successfully published an item using Compare-And-Publish'>
<p>If the 'previd' matched the latest item's ID and if the service
was able to successfully process the request then the protocol
continues as defined in <cite>XEP-0060 7.1.2</cite>.</p>
</section2>
<section2 topic='Could not publish because newest item ID did not match'>
<p>In case the Compare-And-Publish operation failed because the
latest node id is not the same as given in the 'previd' attribute
in the request, the server returns an &IQ; result with 'pubsub'
element qualified by the 'urn:xmpp:pubsub:cap:0' namespace which
contains a &lt;compare-and-publish-failed/&gt; element. The
element MUST have a 'id' attribute with the ID of the lastest
item.</p>
<example caption='Service returns IQ response notifying of failed Compare-And-Publish operation'><![CDATA[
<iq type='result'
from='pubsub.shakespeare.lit'
to='hamlet@denmark.lit/blogbot'
id='compare-and-ublish1'>
<pubsub xmlns='urn:xmpp:pubsub:cap:0'>
<compare-and-publish-failed id='2'/>
</pubsub>
</iq>
]]></example>
</section2>
<section2 topic='Error cases'>
<p>All other error cases are handled as specified in
<cite>XEP-0060 § 7.1.3</cite></p>
</section2>
</section1>
<section1 topic='Security Considerations' anchor='security'>
<p>This extension protocol does not add any further security
considerations to the ones mentioned in <cite>XEP-0060 §
14.</cite>.</p>
</section1>
<section1 topic='IANA Considerations' anchor='iana'>
<p>This document requires no interaction with the Internet Assigned
Numbers Authority (IANA).</p>
</section1>
<section1 topic='XMPP Registrar Considerations' anchor='registrar'>
<p>This specification defines the following XML namespaces:</p>
<ul>
<li>urn:xmpp:pubsub:cap:0</li>
</ul>
<code caption='Registry Submission'><![CDATA[
<var>
<name>urn:xmpp:pubsub:cap:0</name>
<desc>Indicates support for Compare-And-Publish</desc>
<doc>XEP-XXXX</doc>
</var>]]></code>
</section1>
<section1 topic='XML Schema' anchor='schema'>
<p>TODO: Add after the XEP leaves the 'experimental' state.</p>
</section1>
</xep>

@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE xep SYSTEM 'xep.dtd' [
<!ENTITY % ents SYSTEM 'xep.ent'>
<!ENTITY rfc3921bis "<span class='ref'><link url='http://tools.ietf.org/html/draft-ietf-saintandre-rfc3921bis'>rfc3921bis</link></span> <note>RFC 3921: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence &lt;<link url='http://tools.ietf.org/html/draft-ietf-saintandre-rfc3921bis'>http://tools.ietf.org/html/draft-ietf-saintandre-rfc3921bis</link>&gt;.</note>" >
%ents;
]>
<?xml-stylesheet type='text/xsl' href='xep.xsl'?>