Openfire returns error 500 when retrieving privacy lists - xmpp

I'm using converse.js and trying to work with privacy lists. I'm trying to send this IQ:
<body rid="2993056535" xmlns="http://jabber.org/protocol/httpbind" sid="578oe88nm1">
<iq from="alex3#ofb2" type="set" id="active1" xmlns="jabber:client">
<query xmlns="jabber:iq:privacy"/>
</iq>
</body>
This is what server returns me:
<body xmlns="http://jabber.org/protocol/httpbind" ack="2993056535">
<iq xmlns="jabber:client" type="error" id="active1" to="alex3#ofb2/converse.js-90734653">
<query xmlns="jabber:iq:privacy"/>
<error code="500" type="wait">
<internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
</error>
</iq>
</body>
In the server logs I see this exception:
2016.02.19 12:49:00 org.jivesoftware.openfire.handler.IQHandler - Internal server error
java.lang.NullPointerException
at org.jivesoftware.openfire.handler.IQPrivacyHandler.handleIQ(IQPrivacyHandler.java:120)
at org.jivesoftware.openfire.handler.IQHandler.process(IQHandler.java:66)
at org.jivesoftware.openfire.IQRouter.handle(IQRouter.java:372)
at org.jivesoftware.openfire.IQRouter.route(IQRouter.java:115)
at org.jivesoftware.openfire.spi.PacketRouterImpl.route(PacketRouterImpl.java:78)
at org.jivesoftware.openfire.SessionPacketRouter.route(SessionPacketRouter.java:108)
at org.jivesoftware.openfire.SessionPacketRouter.route(SessionPacketRouter.java:67)
at org.jivesoftware.openfire.http.HttpSession.sendPendingPackets(HttpSession.java:639)
at org.jivesoftware.openfire.http.HttpSession$HttpPacketSender.run(HttpSession.java:1270)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
What's wrong?

The stacktrace points to these lines:
// Privacy list handling (create/edit/delete)
Element list = child.element("list");
String listName = list.attributeValue("name");
That is, it looks for a child element <list>, and crashes when it can't find it.
So why does it look for that? It turns out that this is the branch it takes if the IQ has type set (and neither a <default> nor <active> child element). However, as described in XEP-0016, to get the names of existing privacy lists you need to send an IQ with type get instead. Your request should look like this instead:
<iq from='romeo#example.net/orchard' type='get' id='getlist1'>
<query xmlns='jabber:iq:privacy'/>
</iq>

Related

How to get MAM message ID of sent messages?

I am using eJabberd server (MAM enabled) with client library Strophe.js. Client app is storing fixed amount of the messages in local storage.
All the messages, that I receive from the server include elements <archived/> and <stanza-id/>, which provide server-side generated IDs:
<message
xmlns="jabber:client" to="aaa#example.net/8667085700924567016834" from="aaa#example.net">
<result
xmlns="urn:xmpp:mam:2" id="1520510373346685">
<forwarded
xmlns="urn:xmpp:forward:0">
<message
xmlns="jabber:client" xml:lang="en" to="bbb#example.net" from="aaa#example.net/60965696931000870402419" type="chat">
<archived
xmlns="urn:xmpp:mam:tmp" by="aaa#example.net" id="1520510373346685"/>
<stanza-id
xmlns="urn:xmpp:sid:0" by="aaa#example.net" id="1520510373346685"/>
<body>asdf</body>
</message>
<delay
xmlns="urn:xmpp:delay" from="example.net" stamp="2018-03-08T11:59:33.346685Z"/>
</forwarded>
</result>
</message>
I use these IDs to get fixed amount of messages on each MAM call (using RSM paging - before/after/max attributes). For example:
params = {
"before": "1520510373346685",
"max": 10,
onMessage: <some handler>,
onComplete: <some handler>,
}
this.connection.mam.query(Strophe.getBareJidFromJid(this.myJid), params);
This gets me 10 messages before the message with stanza-id 1520510373346685.
However, the messages I send inherently do not have this ID, until I send them and retrieve from MAM.
I did not find any examples on getting the currently sent message ID from the server, and the Strophe does not provide any callbacks after the message was sent from the client and received by the server.
So, the question - is there any way to get the server-generated message ID (stanza-id/archived) for the currently sent message?
Maybe my own approach is wrong - should I generate my own IDs and add it to each message from the client?
There is currently no mechanism specified besides querying the archive. One prominent idea within the XMPP community to solve this in the future is to reflect the send carbon back to the original sender, which would include a stanza-id element with the ID assigned by the archive.

Does subscribed stanza trigger a callback?

Is there a way to get a callback from a subscribed type stanza ? There's an XML response from server but Strophe does not trigger any callback.
This is what I'm sending:
<body rid='576795248' xmlns='http://jabber.org/protocol/httpbind' sid='e3552b8d'><presence to='user2#localhost' type='subscribed' xmlns='jabber:client' id='2:sendIQ'/></body>
And I'm getting:
<body xmlns='http://jabber.org/protocol/httpbind'><iq xmlns="jabber:client" type="set" id="842-543" to="user1#localhost"><query xmlns="jabber:iq:roster"><item jid="test2#localhost" subscription="both"/></query></iq></body>
As you can see, the server is correctly sending a response, but Strophe does not trigger any callback.
Thanks.
add a handler: addHandler(func, Strophe.NS.ROSTER, 'iq')

ColdFusion CFHTTP connection to remote web service

I have successfully connected to a remote webservice using SOAPUI (www.soapui.org). But, I am having trouble calling it successfully from CF9.2.
Here is my entire CFC function. There are dynamic vars but I've tested the output in the soapUI interface and it works:
<cffunction name="getOrganisation" access="remote" returnType="any" output="true">
<cfargument name="iPageNumber" type="any" required="false" default="0">
<cfargument name="iPageSize" type="any" required="false" default="0">
<cfargument name="bCurrentNamesOnly" type="boolean" required="false" default="1">
<cfargument name="bExcludeNotRtos" type="boolean" required="false" default="0">
<cfargument name="bExcludeRtoWithoutActiveRegistration" type="boolean" required="false" default="0">
<cfargument name="sFilter" type="any" required="false" default="">
<cfargument name="bIncludeCode" type="boolean" required="false" default="1">
<cfargument name="sRegistrationManagers" type="any" required="false" default="">
<cfargument name="sClassificationFilters" type="any" required="false" default="">
<cfargument name="sScheme" type="any" required="false" default="">
<cfset var endpoint = "https://ws.staging.training.gov.au/Deewr.Tga.WebServices/OrganisationService.svc/Organisation">
<cfsavecontent variable="soapBody">
<cfoutput>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://training.gov.au/services/"
xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<soapenv:Header/>
<soapenv:Body>
<ser:Search>
<ser:request>
<ser:PageNumber>#arguments.iPageNumber#</ser:PageNumber>
<ser:PageSize>#arguments.iPageSize#</ser:PageSize>
<ser:ClassificationFilters>
<ser:ClassificationFilter>
<ser:Scheme>#arguments.sScheme#</ser:Scheme>
<ser:Values>
<cfif len(arguments.sClassificationFilters)>
<cfloop list="#arguments.sClassificationFilters#" index="item">
<arr:string>#item#</arr:string>
</cfloop>
</cfif>
</ser:Values>
</ser:ClassificationFilter>
</ser:ClassificationFilters>
<ser:CurrentNamesOnly>#arguments.bCurrentNamesOnly#</ser:CurrentNamesOnly>
<ser:ExcludeNotRtos>#arguments.bExcludeNotRtos#</ser:ExcludeNotRtos>
<ser:ExcludeRtoWithoutActiveRegistration>#arguments.bExcludeRtoWithoutActiveRegistration#</ser:ExcludeRtoWithoutActiveRegistration>
<ser:Filter>#arguments.sFilter#</ser:Filter>
<ser:IncludeCode>#arguments.bIncludeCode#</ser:IncludeCode>
<ser:RegistrationManagers>
<cfif len(arguments.sRegistrationManagers)>
<cfloop list="#arguments.sRegistrationManagers#" index="item">
<arr:string>#item#</arr:string>
</cfloop>
</cfif>
</ser:RegistrationManagers>
</ser:request>
</ser:Search>
</soapenv:Body>
</soapenv:Envelope>
</cfoutput>
</cfsavecontent>
<cfhttp
url="#endpoint#"
method="post"
username="#variables.username#"
password="#variables.password#">
<cfhttpparam type="header" name="accept-encoding" value="no-compression" />
<cfhttpparam type="xml" value="#trim(soapBody)#"/>
</cfhttp>
<cfdump var="#cfhttp.FileContent#"><cfabort>
<cfreturn cfhttp.FileContent>
</cffunction>
Running this, I get the error:
An error occurred when verifying security for the message.
Following is the complete return xml
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:InvalidSecurity</faultcode>
<faultstring xml:lang="en-AU">An error occurred when verifying security for the message.</faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
So, it appears to be an authorisation issue.
Here is the SoapUI request screen:
So, how do I construct the cfhttp, or maybe cfinvoke, to emulate the soapUI call?
EDIT
SOAP Request XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://training.gov.au/services/" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<soapenv:Header/>
<soapenv:Body>
<ser:Search>
<ser:request>
<ser:PageNumber>0</ser:PageNumber>
<ser:PageSize>0</ser:PageSize>
<ser:ClassificationFilters>
<ser:ClassificationFilter>
<ser:Scheme></ser:Scheme>
<ser:Values>
<arr:string></arr:string>
</ser:Values>
</ser:ClassificationFilter>
</ser:ClassificationFilters>
<ser:CurrentNamesOnly>true</ser:CurrentNamesOnly>
<ser:ExcludeNotRtos>0</ser:ExcludeNotRtos>
<ser:ExcludeRtoWithoutActiveRegistration>0</ser:ExcludeRtoWithoutActiveRegistration>
<ser:Filter></ser:Filter>
<ser:IncludeCode>1</ser:IncludeCode>
<ser:RegistrationManagers>
<arr:string></arr:string>
</ser:RegistrationManagers>
</ser:request>
</ser:Search>
</soapenv:Body>
</soapenv:Envelope>
EDIT 2
Further information:
Here is the url directly to the service: https://ws.staging.training.gov.au/Deewr.Tga.WebServices/OrganisationService.svc
Below is a screenshot of some authentication related information from the docs
I'm thinking that you need to post the username and password in cfhttpparam tags, not as attributes in the cfhttp tag.
SOAPUI is a fantastic tool, and one I've bee using a lot recently when debugging SOAP requests or transferring them from static .wsdl files into a ColdFusion component.
The first thing to look at is the error message itself:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:InvalidSecurity</faultcode>
<faultstring xml:lang="en-AU">An error occurred when verifying security for the message. </faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
The faultcode node contains a link to the OASIS security namespace document:
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
Viewing that in the browser, you can see what security values are required and you can clarify the naming conventions. So, we can confirm from this that you do need to send through the following with the following format:
Username
Password
Without having access to the request you are testing in SOAPUI, we can also see that the Username and Password values are visible in the property window (your screen grab from the interface, above).
Are these values set within the XML of the request in SOAPUI? Perhaps in the SOAP request header, like so:
<soapenv:Header>
<authInfo xsi:type="soap:authentication">
<Username xsi:type="xsd:string">?</Username>
<Password xsi:type="xsd:string">?</Password>
</authInfo>
</soapenv:Header>
If that is the case, you will also need to include this header in your ColdFusion component when building the soapBody variable.
Also, can you view the URL of the SOAP request directly in the browser to see what variables it expects?
https://ws.staging.training.gov.au/Deewr.Tga.WebServices/OrganisationService.svc/Organisation
The overall structure of your CFC and soapBody looks good, so without having access to the file you are running in SOAPUI to see it running, debugging it and providing answers would be a little tricky.
If you can rule out all of the possibilities mentioned above and still have issues, let me know.
Yes, you were not originally passing a Oasis security header. The answers here so far probably aren't going to work however. Oasis standard can allow for many an varied combinations of user/password/nonce/digest/created and encoding attributes.
As you may or may not have found, the Axis 1 library (CF9 and lower user Axis 1 (versions 1.1-1.4); CF10 also has Axis 2 (Axis version 1.6)). And thus you must POST (cfhttp/http) to pass and receive an Oasis header. The Axis 1 library will cry about trying to understand the Oasis node in the header area.**
Since you are manually posting, you can simply construct your Oasis header in your header much like the example fro Matt Gifford.
Depending on what exactly the receiving party is expecting, you may will have something like this:
<wsse:Security soap:mustUnderstand="true" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<wsse:UsernameToken wsu:Id="UsernameToken-15">
<wsse:Username>
<!-- Removed-->
</wsse:Username>
<wsse:Password>
<!-- Removed-->
</wsse:Password>
<wsse:Nonce>
<!-- Removed-->
</wsse:Nonce>
<wsu:Created>2012-07-11T02:02:48.410Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
or this (from early cladding method pre-output):
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" >
<wsse:Username>#Arguments.szUserName#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordText">#Arguments.szPassword#</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
or this (final output):
<wsse:Security soapenv:mustUnderstand="1"xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-974900"xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>SuperJellyMan</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">jellybeanboom</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">SmVsbHlCZWFuQm9vbTk3NDkwMA==</wsse:Nonce>
<wsu:Created>2012-07-26T17:00:34Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
And so on and so forth. I have seen people invoke java libraries to do this more dynamically. I simply built a few simple methods to build my nonce and format my xml time stamp. But basically, it is pretty simple to build some kind of function or other helper methods to clad the values required in xml with the proper Oasis attributes and xsd references and then insert that in your header of your request of the manual post.
The tricky part is knowing exactly what they want in the Oasis security header if they do not tell you. The xsd simply shows what is acceptable according to Oasis standards, but not necessarily what they are using (and in what combination). If you can get that from them, you can pretty easily put that together from the Oasis docs.
Usually, the combination is User/Pwd (first example). If a Nonce is used, very often a Password Digest is also used (the password gets a ##PasswordDigest instead of ##PasswordText) and is an encoded hash of the created time and password (see Oasis docs for more explanation).
But, the last project I worked on had a plain text pwd plus a nonce with no digest. Not exactly logical since the nonce is supposed to make a fake request more difficult and use a non-clear text password which needs to be decoded by receiver and compared to validate... So it is totally up to the service on what they want/use/need.
You should be able to paste your concocted Oasis header into SoapUI and pretty quickly figure out what they might need/want from their responses to same.
If they have specified user/password (as text), you might find one of these examples (probably the second one) workable and not passing the nonce and created elements.
** Here is the Axis 1 fault for fun reference when Oasis is used with CF9 or less using actual SOAP methods (web service stub, rather than manual post):**
The fault returned when invoking the web service operation is: AxisFault faultCode: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}InvalidSecurity faultSubcode: faultString: An error occurred when verifying security for the message. faultActor: faultNode:
faultDetail: {http://xml.apache.org/axis/}stackTrace:An error occurred when verifying security for the message. at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:221) at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:128) at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source) at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source) at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source) at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source) at org.apache.xerces.parsers.XML11Conf...
[EDIT]
Here is a bit more info as I am working on another webservice that is similar.
Your [SOAP] xml with the cannot possibly be working. I use soapUI as well and unless I add in a security header values in their proper spot the service will return exactly the response you are receiving ("InvalidSecurity") so not very sure this xml is what you should be trying to replicate. I have also successfully built a simple cf based script version to build the header which properly generates the header and adds it to a createObject() invoked javastubbed webservice:
doc = xmlNew();
doc['Security'] = XmlElemNew(doc,'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'Security');
doc.Security['UsernameToken'] = XmlElemNew(doc, 'UsernameToken');
doc.Security.UsernameToken['Username'] = XmlElemNew(doc, 'Username');
doc.Security.UsernameToken.username.XmlText = "TESTERDUDE" ;
doc.Security.UsernameToken.username.XmlAttributes["xsi:type"] = "xsd:string";
doc.Security.UsernameToken['Password'] = XmlElemNew(doc, 'Password');
doc.Security.UsernameToken.password.XmlText = "YetAnotherPassword";
doc.Security.UsernameToken.password.XmlAttributes["xsi:type"] = "xsd:string";
doc.Security.UsernameToken.password.XmlAttributes["Type"] = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordDigest";
doc.Security.UsernameToken['Nonce'] = XmlElemNew(doc, 'Nonce');
doc.Security.UsernameToken.nonce.XmlText = "tKUH8ab3Rokm4t6IAlgcdg9yaEw="; // This would be generated if needed
doc.Security.UsernameToken.nonce.XmlAttributes["xsi:type"] = "xsd:string";
doc.Security.UsernameToken['Created'] = XmlElemNew(doc, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", 'Created');
doc.Security.UsernameToken.created.XmlText = "2010-08-10T10:52:42Z";
doc.Security.UsernameToken.created.XmlAttributes["xsi:type"] = "xsd:string";
addSOAPRequestHeader(ws, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", doc);
This properly generates [taken from the server side xml using getSoapRequest() which grabs the entire xml request] :
<soapenv:Header>
<Security soapenv:actor="" soapenv:mustUnderstand="0" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username xsi:type="xsd:string">TESTERDUDE</Username>
<Password xsi:type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">My Password is my Voice</Password>
<Nonce xsi:type="xsd:string">tKUH8ab3Rokm4t6IAlgcdg9yaEw=</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xsi:type="xsd:string">2010-08-10T10:52:42Z</Created>
</UsernameToken>
</Security>
</soapenv:Header>

What is the appropriate SOAP Action?

I am using Coldfusion 8. I have exceedingly limited experience with SOAP.
I am trying to send out a SOAP request. My SOAP body looks like this:
<cfsavecontent variable="SoapBody">
<cfoutput>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v2="http://www.a2zshow.com/DataServices/v2/" >
<soapenv:Header>
<a2zAuthenticationHeader xmlns="http://www.a2zshow.com/DataServices/v2/">
<Key>#Key#</Key>
<UserName>#UserName#</UserName>
<Password>#Password#</Password>
<InstallName>#InstallName#</InstallName>
</a2zAuthenticationHeader>
</soapenv:Header>
<soapenv:Body>
<DataService xmlns="http://www.a2zshow.com/DataServices/ExhibitorListRequest/v1">
<Request>
<a2zXMLRootNode>
<RequestHeader>
<RequestAttribute Search="NONE"/>
</RequestHeader>
<RequestCriteria>
<EventID>#EventID#</EventID>
</RequestCriteria>
</a2zXMLRootNode>
</Request>
</DataService>
</soapenv:Body>
</soapenv:Envelope>
</cfoutput>
</cfsavecontent>
My SOAP request looks like this:
<cfhttp
url="#PostURL#"
method="post"
result="httpResponse">
<cfhttpparam
type="header"
name="SOAPAction"
value="#SOAPAction#" />
<cfhttpparam
type="xml"
value="#trim(soapBody)#"/>
</cfhttp>
The SOAPAction variable is set like this:
SOAPAction = "http://www.a2zshow.com/getExhibitorList";
I think I am successfully posting to the right place, but I am getting an error message saying that client didn't recognize the SOAPAction. Here's my error:
soap:ClientServer did not recognize the value of HTTP Header SOAPAction: http://www.a2zshow.com/getExhibitorList.
This error seems to be telling me that I don't have the SOAPAction correct. The guy at the other end tells me that I have everything set up right and that it should be working. Is there something missing?
The error was in the vendor's SOAPAction. They gave me the wrong URL. Their documentation was created 8 years ago and they updated it in 2008.
I guess they would rather spend time making their product hard to use by confusing people with the wrong information than to help get something done.
Thanks for the help!

How to store pubsub payloads in DB

I am using XMPP pubsub.Everything is working fine.User can create node and interested user can subscribe to that node.But I want to store the publish action into DB.Because in my application many things are handled by pubsub like chat,feeds etc so on page refresh I want to persist chat.So I m looking for any openfire plugin which saves it into DB behind the scenes. Like openfire saves chat in case of MUC (Multiple User Chat).I dont want to manually send ajax request. So is there any way to log the publish items.
Update:
There was a problem in my form configuration in which persist items was set to false by default.So I configured the node as you suggested.But again I am facing some problem on page refresh.I will explain you with an example. In my application user A logs in and create a node and on successful creation it sends request to user B, now user B subscribe to the node created by user A.Now if any user does a page refresh I send an IQ stanza of type get to get all the missed events like the one you mentioned above.But I am getting error 400 subid-required.
<body rid='430432056' xmlns='http://jabber.org/protocol/httpbind' sid='dca8aafc'><iq to='pubsub.abc' type='get' xmlns='jabber:client' id='3408:sendIQ'><pubsub xmlns='http://jabber.org/protocol/pubsub'><items node='3821poU5zq7nhn1'/></pubsub></iq></body>
In response I am getting:
<body xmlns='http://jabber.org/protocol/httpbind'><iq type="error" id="3408:sendIQ" from="pubsub.abc" to="test#abc/dca8aafc"><pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="3821poU5zq7nhn1"/></pubsub><error code="400" type="modify"><bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/><subid-required xmlns="http://jabber.org/protocol/pubsub#errors"/></error></iq></body>
So can help me where I am getting wrong.Also I read the link http://xmpp.org/extensions/xep-0060.html#owner-configure point : 6.5.9.1 which says
If the requesting entity has multiple subscriptions to the node but does not specify a subscription ID, the service MUST return a error to the subscriber.So does it mean that I am subscribing to the same node again?
I checked if thats the case but I am subscribing only once.So just figuring out where things are getting wrong.
Per XMPP's XEP-0060, it is possible for Owner of the PubSub node to make items on the node expire in a very distant future.
When creating a node, you may want to set the following attributes to make items on the node never expire.
<field var='pubsub#persist_items' type='boolean' label='Persist items to storage'>
<value>1</value>
</field>
<field var='pubsub#max_items' type='text-single' label='Max # of items to persist'>
<value>999999</value>
</field>
<field var='pubsub#item_expire' type='text-single' label='Time after which to automatically purge items'>
<value>999999999999</value>
</field>
Source: http://xmpp.org/extensions/xep-0060.html#owner-configure
Then when you want to retrieve all the items, you can probably do this to retrieve the data:
<iq type='get'
from='francisco#denmark.lit/barracks'
to='pubsub.shakespeare.lit'
id='items1'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<items node='princely_musings'/>
</pubsub>
</iq>
Source: http://xmpp.org/extensions/xep-0060.html#subscriber-retrieve-requestall
Would this solve your problem?