diff options
author | Shuyi Chen <shuyichen@google.com> | 2013-05-22 14:51:55 -0700 |
---|---|---|
committer | Shuyi Chen <shuyichen@google.com> | 2013-05-22 17:19:30 -0700 |
commit | d7955ce24d294fb2014c59d11fca184471056f44 (patch) | |
tree | e260500b0b7639127038495d46a0ad6dcbb6d96c /src/org/jivesoftware/smackx/packet | |
parent | 8f4ce9ea0de51fee918bffe19c434612d6bbb2d7 (diff) | |
download | smack-idea133.tar.gz |
Add android smack source.HEADandroid-wear-5.0.0_r1android-sdk-4.4.2_r1.0.1android-sdk-4.4.2_r1android-l-preview_r2android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-cts-5.0_r9android-cts-5.0_r8android-cts-5.0_r7android-cts-5.0_r6android-cts-5.0_r5android-cts-5.0_r4android-cts-5.0_r3android-cts-4.4_r4android-cts-4.4_r1android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1android-5.0.2_r3android-5.0.2_r1android-5.0.1_r1android-5.0.0_r7android-5.0.0_r6android-5.0.0_r5.1android-5.0.0_r5android-5.0.0_r4android-5.0.0_r3android-5.0.0_r2android-5.0.0_r1android-4.4w_r1android-4.4_r1.2.0.1android-4.4_r1.2android-4.4_r1.1.0.1android-4.4_r1.1android-4.4_r1.0.1android-4.4_r1android-4.4_r0.9android-4.4_r0.8android-4.4_r0.7android-4.4.4_r2.0.1android-4.4.4_r2android-4.4.4_r1.0.1android-4.4.4_r1android-4.4.3_r1.1.0.1android-4.4.3_r1.1android-4.4.3_r1.0.1android-4.4.3_r1android-4.4.2_r2.0.1android-4.4.2_r2android-4.4.2_r1.0.1android-4.4.2_r1android-4.4.1_r1.0.1android-4.4.1_r1android-4.3_r3.1android-4.3_r3android-4.3_r2.3android-4.3_r2.2android-4.3_r2.1android-4.3_r2android-4.3_r1.1android-4.3_r1android-4.3_r0.9.1android-4.3_r0.9android-4.3.1_r1tools_r22.2mastermainlollipop-wear-releaselollipop-releaselollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-releaselollipop-devlollipop-cts-releasel-previewkitkat-wearkitkat-releasekitkat-mr2.2-releasekitkat-mr2.1-releasekitkat-mr2-releasekitkat-mr1.1-releasekitkat-mr1-releasekitkat-devkitkat-cts-releasekitkat-cts-devjb-mr2.0.0-releasejb-mr2.0-releasejb-mr2-releasejb-mr2-devidea133-weekly-releaseidea133
Change-Id: I49ce97136c17173c4ae3965c694af6e7bc49897d
Diffstat (limited to 'src/org/jivesoftware/smackx/packet')
33 files changed, 7029 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smackx/packet/AdHocCommandData.java b/src/org/jivesoftware/smackx/packet/AdHocCommandData.java new file mode 100755 index 0000000..bceffcd --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/AdHocCommandData.java @@ -0,0 +1,279 @@ +/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2005-2008 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smackx.commands.AdHocCommand;
+import org.jivesoftware.smackx.commands.AdHocCommand.Action;
+import org.jivesoftware.smackx.commands.AdHocCommand.SpecificErrorCondition;
+import org.jivesoftware.smackx.commands.AdHocCommandNote;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the state and the request of the execution of an adhoc command.
+ *
+ * @author Gabriel Guardincerri
+ */
+public class AdHocCommandData extends IQ {
+
+ /* JID of the command host */
+ private String id;
+
+ /* Command name */
+ private String name;
+
+ /* Command identifier */
+ private String node;
+
+ /* Unique ID of the execution */
+ private String sessionID;
+
+ private List<AdHocCommandNote> notes = new ArrayList<AdHocCommandNote>();
+
+ private DataForm form;
+
+ /* Action request to be executed */
+ private AdHocCommand.Action action;
+
+ /* Current execution status */
+ private AdHocCommand.Status status;
+
+ private ArrayList<AdHocCommand.Action> actions = new ArrayList<AdHocCommand.Action>();
+
+ private AdHocCommand.Action executeAction;
+
+ private String lang;
+
+ public AdHocCommandData() {
+ }
+
+ @Override
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<command xmlns=\"http://jabber.org/protocol/commands\"");
+ buf.append(" node=\"").append(node).append("\"");
+ if (sessionID != null) {
+ if (!sessionID.equals("")) {
+ buf.append(" sessionid=\"").append(sessionID).append("\"");
+ }
+ }
+ if (status != null) {
+ buf.append(" status=\"").append(status).append("\"");
+ }
+ if (action != null) {
+ buf.append(" action=\"").append(action).append("\"");
+ }
+
+ if (lang != null) {
+ if (!lang.equals("")) {
+ buf.append(" lang=\"").append(lang).append("\"");
+ }
+ }
+ buf.append(">");
+
+ if (getType() == Type.RESULT) {
+ buf.append("<actions");
+
+ if (executeAction != null) {
+ buf.append(" execute=\"").append(executeAction).append("\"");
+ }
+ if (actions.size() == 0) {
+ buf.append("/>");
+ } else {
+ buf.append(">");
+
+ for (AdHocCommand.Action action : actions) {
+ buf.append("<").append(action).append("/>");
+ }
+ buf.append("</actions>");
+ }
+ }
+
+ if (form != null) {
+ buf.append(form.toXML());
+ }
+
+ for (AdHocCommandNote note : notes) {
+ buf.append("<note type=\"").append(note.getType().toString()).append("\">");
+ buf.append(note.getValue());
+ buf.append("</note>");
+ }
+
+ // TODO ERRORS
+// if (getError() != null) {
+// buf.append(getError().toXML());
+// }
+
+ buf.append("</command>");
+ return buf.toString();
+ }
+
+ /**
+ * Returns the JID of the command host.
+ *
+ * @return the JID of the command host.
+ */
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the human name of the command
+ *
+ * @return the name of the command.
+ */
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the identifier of the command
+ *
+ * @return the node.
+ */
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ /**
+ * Returns the list of notes that the command has.
+ *
+ * @return the notes.
+ */
+ public List<AdHocCommandNote> getNotes() {
+ return notes;
+ }
+
+ public void addNote(AdHocCommandNote note) {
+ this.notes.add(note);
+ }
+
+ public void remveNote(AdHocCommandNote note) {
+ this.notes.remove(note);
+ }
+
+ /**
+ * Returns the form of the command.
+ *
+ * @return the data form associated with the command.
+ */
+ public DataForm getForm() {
+ return form;
+ }
+
+ public void setForm(DataForm form) {
+ this.form = form;
+ }
+
+ /**
+ * Returns the action to execute. The action is set only on a request.
+ *
+ * @return the action to execute.
+ */
+ public AdHocCommand.Action getAction() {
+ return action;
+ }
+
+ public void setAction(AdHocCommand.Action action) {
+ this.action = action;
+ }
+
+ /**
+ * Returns the status of the execution.
+ *
+ * @return the status.
+ */
+ public AdHocCommand.Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(AdHocCommand.Status status) {
+ this.status = status;
+ }
+
+ public List<Action> getActions() {
+ return actions;
+ }
+
+ public void addAction(Action action) {
+ actions.add(action);
+ }
+
+ public void setExecuteAction(Action executeAction) {
+ this.executeAction = executeAction;
+ }
+
+ public Action getExecuteAction() {
+ return executeAction;
+ }
+
+ public void setSessionID(String sessionID) {
+ this.sessionID = sessionID;
+ }
+
+ public String getSessionID() {
+ return sessionID;
+ }
+
+ public static class SpecificError implements PacketExtension {
+
+ public static final String namespace = "http://jabber.org/protocol/commands";
+
+ public SpecificErrorCondition condition;
+
+ public SpecificError(SpecificErrorCondition condition) {
+ this.condition = condition;
+ }
+
+ public String getElementName() {
+ return condition.toString();
+ }
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public SpecificErrorCondition getCondition() {
+ return condition;
+ }
+
+ public String toXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<").append(getElementName());
+ buf.append(" xmlns=\"").append(getNamespace()).append("\"/>");
+ return buf.toString();
+ }
+ }
+}
\ No newline at end of file diff --git a/src/org/jivesoftware/smackx/packet/AttentionExtension.java b/src/org/jivesoftware/smackx/packet/AttentionExtension.java new file mode 100644 index 0000000..d86fa41 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/AttentionExtension.java @@ -0,0 +1,100 @@ +/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2010 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A PacketExtension that implements XEP-0224: Attention
+ *
+ * This extension is expected to be added to message stanzas of type 'headline.'
+ * Please refer to the XEP for more implementation guidelines.
+ *
+ * @author Guus der Kinderen, guus.der.kinderen@gmail.com
+ * @see <a
+ * href="http://xmpp.org/extensions/xep-0224.html">XEP-0224: Attention</a>
+ */
+public class AttentionExtension implements PacketExtension {
+
+ /**
+ * The XML element name of an 'attention' extension.
+ */
+ public static final String ELEMENT_NAME = "attention";
+
+ /**
+ * The namespace that qualifies the XML element of an 'attention' extension.
+ */
+ public static final String NAMESPACE = "urn:xmpp:attention:0";
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.PacketExtension#getElementName()
+ */
+ public String getElementName() {
+ return ELEMENT_NAME;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace()
+ */
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.PacketExtension#toXML()
+ */
+ public String toXML() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("<").append(getElementName()).append(" xmlns=\"").append(
+ getNamespace()).append("\"/>");
+ return sb.toString();
+ }
+
+ /**
+ * A {@link PacketExtensionProvider} for the {@link AttentionExtension}. As
+ * Attention elements have no state/information other than the element name
+ * and namespace, this implementation simply returns new instances of
+ * {@link AttentionExtension}.
+ *
+ * @author Guus der Kinderen, guus.der.kinderen@gmail.com
+s */
+ public static class Provider implements PacketExtensionProvider {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.jivesoftware.smack.provider.PacketExtensionProvider#parseExtension
+ * (org.xmlpull.v1.XmlPullParser)
+ */
+ public PacketExtension parseExtension(XmlPullParser arg0)
+ throws Exception {
+ return new AttentionExtension();
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smackx/packet/ChatStateExtension.java b/src/org/jivesoftware/smackx/packet/ChatStateExtension.java new file mode 100644 index 0000000..ecc6acc --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/ChatStateExtension.java @@ -0,0 +1,73 @@ +/**
+ * $RCSfile$
+ * $Revision: 2407 $
+ * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smackx.ChatState;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Represents a chat state which is an extension to message packets which is used to indicate
+ * the current status of a chat participant.
+ *
+ * @author Alexander Wenckus
+ * @see org.jivesoftware.smackx.ChatState
+ */
+public class ChatStateExtension implements PacketExtension {
+
+ private ChatState state;
+
+ /**
+ * Default constructor. The argument provided is the state that the extension will represent.
+ *
+ * @param state the state that the extension represents.
+ */
+ public ChatStateExtension(ChatState state) {
+ this.state = state;
+ }
+
+ public String getElementName() {
+ return state.name();
+ }
+
+ public String getNamespace() {
+ return "http://jabber.org/protocol/chatstates";
+ }
+
+ public String toXML() {
+ return "<" + getElementName() + " xmlns=\"" + getNamespace() + "\" />";
+ }
+
+ public static class Provider implements PacketExtensionProvider {
+
+ public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
+ ChatState state;
+ try {
+ state = ChatState.valueOf(parser.getName());
+ }
+ catch (Exception ex) {
+ state = ChatState.active;
+ }
+ return new ChatStateExtension(state);
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smackx/packet/DataForm.java b/src/org/jivesoftware/smackx/packet/DataForm.java new file mode 100644 index 0000000..4d12892 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DataForm.java @@ -0,0 +1,312 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.Form; +import org.jivesoftware.smackx.FormField; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Represents a form that could be use for gathering data as well as for reporting data + * returned from a search. + * + * @author Gaston Dombiak + */ +public class DataForm implements PacketExtension { + + private String type; + private String title; + private List<String> instructions = new ArrayList<String>(); + private ReportedData reportedData; + private final List<Item> items = new ArrayList<Item>(); + private final List<FormField> fields = new ArrayList<FormField>(); + + public DataForm(String type) { + this.type = type; + } + + /** + * Returns the meaning of the data within the context. The data could be part of a form + * to fill out, a form submission or data results.<p> + * + * Possible form types are: + * <ul> + * <li>form -> This packet contains a form to fill out. Display it to the user (if your + * program can).</li> + * <li>submit -> The form is filled out, and this is the data that is being returned from + * the form.</li> + * <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li> + * <li>result -> Data results being returned from a search, or some other query.</li> + * </ul> + * + * @return the form's type. + */ + public String getType() { + return type; + } + + /** + * Returns the description of the data. It is similar to the title on a web page or an X + * window. You can put a <title/> on either a form to fill out, or a set of data results. + * + * @return description of the data. + */ + public String getTitle() { + return title; + } + + /** + * Returns an Iterator for the list of instructions that explain how to fill out the form and + * what the form is about. The dataform could include multiple instructions since each + * instruction could not contain newlines characters. Join the instructions together in order + * to show them to the user. + * + * @return an Iterator for the list of instructions that explain how to fill out the form. + */ + public Iterator<String> getInstructions() { + synchronized (instructions) { + return Collections.unmodifiableList(new ArrayList<String>(instructions)).iterator(); + } + } + + /** + * Returns the fields that will be returned from a search. + * + * @return fields that will be returned from a search. + */ + public ReportedData getReportedData() { + return reportedData; + } + + /** + * Returns an Iterator for the items returned from a search. + * + * @return an Iterator for the items returned from a search. + */ + public Iterator<Item> getItems() { + synchronized (items) { + return Collections.unmodifiableList(new ArrayList<Item>(items)).iterator(); + } + } + + /** + * Returns an Iterator for the fields that are part of the form. + * + * @return an Iterator for the fields that are part of the form. + */ + public Iterator<FormField> getFields() { + synchronized (fields) { + return Collections.unmodifiableList(new ArrayList<FormField>(fields)).iterator(); + } + } + + public String getElementName() { + return Form.ELEMENT; + } + + public String getNamespace() { + return Form.NAMESPACE; + } + + /** + * Sets the description of the data. It is similar to the title on a web page or an X window. + * You can put a <title/> on either a form to fill out, or a set of data results. + * + * @param title description of the data. + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Sets the list of instructions that explain how to fill out the form and what the form is + * about. The dataform could include multiple instructions since each instruction could not + * contain newlines characters. + * + * @param instructions list of instructions that explain how to fill out the form. + */ + public void setInstructions(List<String> instructions) { + this.instructions = instructions; + } + + /** + * Sets the fields that will be returned from a search. + * + * @param reportedData the fields that will be returned from a search. + */ + public void setReportedData(ReportedData reportedData) { + this.reportedData = reportedData; + } + + /** + * Adds a new field as part of the form. + * + * @param field the field to add to the form. + */ + public void addField(FormField field) { + synchronized (fields) { + fields.add(field); + } + } + + /** + * Adds a new instruction to the list of instructions that explain how to fill out the form + * and what the form is about. The dataform could include multiple instructions since each + * instruction could not contain newlines characters. + * + * @param instruction the new instruction that explain how to fill out the form. + */ + public void addInstruction(String instruction) { + synchronized (instructions) { + instructions.add(instruction); + } + } + + /** + * Adds a new item returned from a search. + * + * @param item the item returned from a search. + */ + public void addItem(Item item) { + synchronized (items) { + items.add(item); + } + } + + /** + * Returns true if this DataForm has at least one FORM_TYPE field which is + * hidden. This method is used for sanity checks. + * + * @return + */ + public boolean hasHiddenFormTypeField() { + boolean found = false; + for (FormField f : fields) { + if (f.getVariable().equals("FORM_TYPE") && f.getType() != null && f.getType().equals("hidden")) + found = true; + } + return found; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\" type=\"" + getType() +"\">"); + if (getTitle() != null) { + buf.append("<title>").append(getTitle()).append("</title>"); + } + for (Iterator<String> it=getInstructions(); it.hasNext();) { + buf.append("<instructions>").append(it.next()).append("</instructions>"); + } + // Append the list of fields returned from a search + if (getReportedData() != null) { + buf.append(getReportedData().toXML()); + } + // Loop through all the items returned from a search and append them to the string buffer + for (Iterator<Item> i = getItems(); i.hasNext();) { + Item item = i.next(); + buf.append(item.toXML()); + } + // Loop through all the form fields and append them to the string buffer + for (Iterator<FormField> i = getFields(); i.hasNext();) { + FormField field = i.next(); + buf.append(field.toXML()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + + /** + * + * Represents the fields that will be returned from a search. This information is useful when + * you try to use the jabber:iq:search namespace to return dynamic form information. + * + * @author Gaston Dombiak + */ + public static class ReportedData { + private List<FormField> fields = new ArrayList<FormField>(); + + public ReportedData(List<FormField> fields) { + this.fields = fields; + } + + /** + * Returns the fields returned from a search. + * + * @return the fields returned from a search. + */ + public Iterator<FormField> getFields() { + return Collections.unmodifiableList(new ArrayList<FormField>(fields)).iterator(); + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<reported>"); + // Loop through all the form items and append them to the string buffer + for (Iterator<FormField> i = getFields(); i.hasNext();) { + FormField field = i.next(); + buf.append(field.toXML()); + } + buf.append("</reported>"); + return buf.toString(); + } + } + + /** + * + * Represents items of reported data. + * + * @author Gaston Dombiak + */ + public static class Item { + private List<FormField> fields = new ArrayList<FormField>(); + + public Item(List<FormField> fields) { + this.fields = fields; + } + + /** + * Returns the fields that define the data that goes with the item. + * + * @return the fields that define the data that goes with the item. + */ + public Iterator<FormField> getFields() { + return Collections.unmodifiableList(new ArrayList<FormField>(fields)).iterator(); + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<item>"); + // Loop through all the form items and append them to the string buffer + for (Iterator<FormField> i = getFields(); i.hasNext();) { + FormField field = i.next(); + buf.append(field.toXML()); + } + buf.append("</item>"); + return buf.toString(); + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/DefaultPrivateData.java b/src/org/jivesoftware/smackx/packet/DefaultPrivateData.java new file mode 100644 index 0000000..b58fc6c --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DefaultPrivateData.java @@ -0,0 +1,137 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Default implementation of the PrivateData interface. Unless a PrivateDataProvider + * is registered with the PrivateDataManager class, instances of this class will be + * returned when getting private data.<p> + * + * This class provides a very simple representation of an XML sub-document. Each element + * is a key in a Map with its CDATA being the value. For example, given the following + * XML sub-document: + * + * <pre> + * <foo xmlns="http://bar.com"> + * <color>blue</color> + * <food>pizza</food> + * </foo></pre> + * + * In this case, getValue("color") would return "blue", and getValue("food") would + * return "pizza". This parsing mechanism mechanism is very simplistic and will not work + * as desired in all cases (for example, if some of the elements have attributes. In those + * cases, a custom {@link org.jivesoftware.smackx.provider.PrivateDataProvider} should be used. + * + * @author Matt Tucker + */ +public class DefaultPrivateData implements PrivateData { + + private String elementName; + private String namespace; + private Map<String, String> map; + + /** + * Creates a new generic private data object. + * + * @param elementName the name of the element of the XML sub-document. + * @param namespace the namespace of the element. + */ + public DefaultPrivateData(String elementName, String namespace) { + this.elementName = elementName; + this.namespace = namespace; + } + + /** + * Returns the XML element name of the private data sub-packet root element. + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return elementName; + } + + /** + * Returns the XML namespace of the private data sub-packet root element. + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return namespace; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(elementName).append(" xmlns=\"").append(namespace).append("\">"); + for (Iterator<String> i=getNames(); i.hasNext(); ) { + String name = i.next(); + String value = getValue(name); + buf.append("<").append(name).append(">"); + buf.append(value); + buf.append("</").append(name).append(">"); + } + buf.append("</").append(elementName).append(">"); + return buf.toString(); + } + + /** + * Returns an Iterator for the names that can be used to get + * values of the private data. + * + * @return an Iterator for the names. + */ + public synchronized Iterator<String> getNames() { + if (map == null) { + return Collections.<String>emptyList().iterator(); + } + return Collections.unmodifiableSet(map.keySet()).iterator(); + } + + /** + * Returns a value given a name. + * + * @param name the name. + * @return the value. + */ + public synchronized String getValue(String name) { + if (map == null) { + return null; + } + return (String)map.get(name); + } + + /** + * Sets a value given the name. + * + * @param name the name. + * @param value the value. + */ + public synchronized void setValue(String name, String value) { + if (map == null) { + map = new HashMap<String,String>(); + } + map.put(name, value); + } +}
\ No newline at end of file diff --git a/src/org/jivesoftware/smackx/packet/DelayInfo.java b/src/org/jivesoftware/smackx/packet/DelayInfo.java new file mode 100644 index 0000000..f404971 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DelayInfo.java @@ -0,0 +1,105 @@ +/** + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.packet; + +import java.util.Date; + +import org.jivesoftware.smack.util.StringUtils;
+ +/** + * A decorator for the {@link DelayInformation} class to transparently support + * both the new <b>Delay Delivery</b> specification <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a> and + * the old one <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a>. + * + * Existing code can be backward compatible. + * + * @author Robin Collier + */ +public class DelayInfo extends DelayInformation +{ + + DelayInformation wrappedInfo; + + /** + * Creates a new instance with given delay information. + * @param delay the delay information + */ + public DelayInfo(DelayInformation delay) + { + super(delay.getStamp()); + wrappedInfo = delay; + } + + @Override + public String getFrom() + { + return wrappedInfo.getFrom(); + } + + @Override + public String getReason() + { + return wrappedInfo.getReason(); + } + + @Override + public Date getStamp() + { + return wrappedInfo.getStamp(); + } + + @Override + public void setFrom(String from) + { + wrappedInfo.setFrom(from); + } + + @Override + public void setReason(String reason) + { + wrappedInfo.setReason(reason); + } + + @Override + public String getElementName() + { + return "delay"; + } + + @Override + public String getNamespace() + { + return "urn:xmpp:delay"; + } + + @Override + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\""); + buf.append(" stamp=\""); + buf.append(StringUtils.formatXEP0082Date(getStamp()));
+ buf.append("\""); + if (getFrom() != null && getFrom().length() > 0) { + buf.append(" from=\"").append(getFrom()).append("\""); + } + buf.append(">"); + if (getReason() != null && getReason().length() > 0) { + buf.append(getReason()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/DelayInformation.java b/src/org/jivesoftware/smackx/packet/DelayInformation.java new file mode 100644 index 0000000..b9ab485 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DelayInformation.java @@ -0,0 +1,149 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents timestamp information about data stored for later delivery. A DelayInformation will + * always includes the timestamp when the packet was originally sent and may include more + * information such as the JID of the entity that originally sent the packet as well as the reason + * for the delay.<p> + * + * For more information see <a href="http://xmpp.org/extensions/xep-0091.html">XEP-0091</a> + * and <a href="http://xmpp.org/extensions/xep-0203.html">XEP-0203</a>. + * + * @author Gaston Dombiak + */ +public class DelayInformation implements PacketExtension { + + /** + * Date format according to the obsolete XEP-0091 specification. + * XEP-0091 recommends to use this old format for date-time instead of + * the one specified in XEP-0082. + * <p> + * Date formats are not synchronized. Since multiple threads access the format concurrently, + * it must be synchronized externally. + */ + public static final DateFormat XEP_0091_UTC_FORMAT = new SimpleDateFormat( + "yyyyMMdd'T'HH:mm:ss"); + static { + XEP_0091_UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + private Date stamp; + private String from; + private String reason; + + /** + * Creates a new instance with the specified timestamp. + * @param stamp the timestamp + */ + public DelayInformation(Date stamp) { + super(); + this.stamp = stamp; + } + + /** + * Returns the JID of the entity that originally sent the packet or that delayed the + * delivery of the packet or <tt>null</tt> if this information is not available. + * + * @return the JID of the entity that originally sent the packet or that delayed the + * delivery of the packet. + */ + public String getFrom() { + return from; + } + + /** + * Sets the JID of the entity that originally sent the packet or that delayed the + * delivery of the packet or <tt>null</tt> if this information is not available. + * + * @param from the JID of the entity that originally sent the packet. + */ + public void setFrom(String from) { + this.from = from; + } + + /** + * Returns the timestamp when the packet was originally sent. The returned Date is + * be understood as UTC. + * + * @return the timestamp when the packet was originally sent. + */ + public Date getStamp() { + return stamp; + } + + /** + * Returns a natural-language description of the reason for the delay or <tt>null</tt> if + * this information is not available. + * + * @return a natural-language description of the reason for the delay or <tt>null</tt>. + */ + public String getReason() { + return reason; + } + + /** + * Sets a natural-language description of the reason for the delay or <tt>null</tt> if + * this information is not available. + * + * @param reason a natural-language description of the reason for the delay or <tt>null</tt>. + */ + public void setReason(String reason) { + this.reason = reason; + } + + public String getElementName() { + return "x"; + } + + public String getNamespace() { + return "jabber:x:delay"; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\""); + buf.append(" stamp=\""); + synchronized (XEP_0091_UTC_FORMAT) { + buf.append(XEP_0091_UTC_FORMAT.format(stamp)); + } + buf.append("\""); + if (from != null && from.length() > 0) { + buf.append(" from=\"").append(from).append("\""); + } + buf.append(">"); + if (reason != null && reason.length() > 0) { + buf.append(reason); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/DiscoverInfo.java b/src/org/jivesoftware/smackx/packet/DiscoverInfo.java new file mode 100644 index 0000000..ba873a9 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DiscoverInfo.java @@ -0,0 +1,507 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A DiscoverInfo IQ packet, which is used by XMPP clients to request and receive information + * to/from other XMPP entities.<p> + * + * The received information may contain one or more identities of the requested XMPP entity, and + * a list of supported features by the requested XMPP entity. + * + * @author Gaston Dombiak + */ +public class DiscoverInfo extends IQ { + + public static final String NAMESPACE = "http://jabber.org/protocol/disco#info"; + + private final List<Feature> features = new CopyOnWriteArrayList<Feature>(); + private final List<Identity> identities = new CopyOnWriteArrayList<Identity>(); + private String node; + + public DiscoverInfo() { + super(); + } + + /** + * Copy constructor + * + * @param d + */ + public DiscoverInfo(DiscoverInfo d) { + super(d); + + // Set node + setNode(d.getNode()); + + // Copy features + synchronized (d.features) { + for (Feature f : d.features) { + addFeature(f); + } + } + + // Copy identities + synchronized (d.identities) { + for (Identity i : d.identities) { + addIdentity(i); + } + } + } + + /** + * Adds a new feature to the discovered information. + * + * @param feature the discovered feature + */ + public void addFeature(String feature) { + addFeature(new Feature(feature)); + } + + /** + * Adds a collection of features to the packet. Does noting if featuresToAdd is null. + * + * @param featuresToAdd + */ + public void addFeatures(Collection<String> featuresToAdd) { + if (featuresToAdd == null) return; + for (String feature : featuresToAdd) { + addFeature(feature); + } + } + + private void addFeature(Feature feature) { + synchronized (features) { + features.add(feature); + } + } + + /** + * Returns the discovered features of an XMPP entity. + * + * @return an Iterator on the discovered features of an XMPP entity + */ + public Iterator<Feature> getFeatures() { + synchronized (features) { + return Collections.unmodifiableList(features).iterator(); + } + } + + /** + * Adds a new identity of the requested entity to the discovered information. + * + * @param identity the discovered entity's identity + */ + public void addIdentity(Identity identity) { + synchronized (identities) { + identities.add(identity); + } + } + + /** + * Adds identities to the DiscoverInfo stanza + * + * @param identitiesToAdd + */ + public void addIdentities(Collection<Identity> identitiesToAdd) { + if (identitiesToAdd == null) return; + synchronized (identities) { + identities.addAll(identitiesToAdd); + } + } + + /** + * Returns the discovered identities of an XMPP entity. + * + * @return an Iterator on the discoveted identities + */ + public Iterator<Identity> getIdentities() { + synchronized (identities) { + return Collections.unmodifiableList(identities).iterator(); + } + } + + /** + * Returns the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @return the node attribute that supplements the 'jid' attribute + */ + public String getNode() { + return node; + } + + /** + * Sets the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @param node the node attribute that supplements the 'jid' attribute + */ + public void setNode(String node) { + this.node = node; + } + + /** + * Returns true if the specified feature is part of the discovered information. + * + * @param feature the feature to check + * @return true if the requestes feature has been discovered + */ + public boolean containsFeature(String feature) { + for (Iterator<Feature> it = getFeatures(); it.hasNext();) { + if (feature.equals(it.next().getVar())) + return true; + } + return false; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"" + NAMESPACE + "\""); + if (getNode() != null) { + buf.append(" node=\""); + buf.append(StringUtils.escapeForXML(getNode())); + buf.append("\""); + } + buf.append(">"); + synchronized (identities) { + for (Identity identity : identities) { + buf.append(identity.toXML()); + } + } + synchronized (features) { + for (Feature feature : features) { + buf.append(feature.toXML()); + } + } + // Add packet extensions, if any are defined. + buf.append(getExtensionsXML()); + buf.append("</query>"); + return buf.toString(); + } + + /** + * Test if a DiscoverInfo response contains duplicate identities. + * + * @return true if duplicate identities where found, otherwise false + */ + public boolean containsDuplicateIdentities() { + List<Identity> checkedIdentities = new LinkedList<Identity>(); + for (Identity i : identities) { + for (Identity i2 : checkedIdentities) { + if (i.equals(i2)) + return true; + } + checkedIdentities.add(i); + } + return false; + } + + /** + * Test if a DiscoverInfo response contains duplicate features. + * + * @return true if duplicate identities where found, otherwise false + */ + public boolean containsDuplicateFeatures() { + List<Feature> checkedFeatures = new LinkedList<Feature>(); + for (Feature f : features) { + for (Feature f2 : checkedFeatures) { + if (f.equals(f2)) + return true; + } + checkedFeatures.add(f); + } + return false; + } + + /** + * Represents the identity of a given XMPP entity. An entity may have many identities but all + * the identities SHOULD have the same name.<p> + * + * Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * in order to get the official registry of values for the <i>category</i> and <i>type</i> + * attributes. + * + */ + public static class Identity implements Comparable<Identity> { + + private String category; + private String name; + private String type; + private String lang; // 'xml:lang; + + /** + * Creates a new identity for an XMPP entity. + * + * @param category the entity's category. + * @param name the entity's name. + * @deprecated As per the spec, the type field is mandatory and the 3 argument constructor should be used instead. + */ + public Identity(String category, String name) { + this.category = category; + this.name = name; + } + + /** + * Creates a new identity for an XMPP entity. + * 'category' and 'type' are required by + * <a href="http://xmpp.org/extensions/xep-0030.html#schemas">XEP-30 XML Schemas</a> + * + * @param category the entity's category (required as per XEP-30). + * @param name the entity's name. + * @param type the entity's type (required as per XEP-30). + */ + public Identity(String category, String name, String type) { + if ((category == null) || (type == null)) + throw new IllegalArgumentException("category and type cannot be null"); + + this.category = category; + this.name = name; + this.type = type; + } + + /** + * Returns the entity's category. To get the official registry of values for the + * 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * + * @return the entity's category. + */ + public String getCategory() { + return category; + } + + /** + * Returns the identity's name. + * + * @return the identity's name. + */ + public String getName() { + return name; + } + + /** + * Returns the entity's type. To get the official registry of values for the + * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * + * @return the entity's type. + */ + public String getType() { + return type; + } + + /** + * Sets the entity's type. To get the official registry of values for the + * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * + * @param type the identity's type. + * @deprecated As per the spec, this field is mandatory and the 3 argument constructor should be used instead. + */ + public void setType(String type) { + this.type = type; + } + + /** + * Sets the natural language (xml:lang) for this identity (optional) + * + * @param lang the xml:lang of this Identity + */ + public void setLanguage(String lang) { + this.lang = lang; + } + + /** + * Returns the identities natural language if one is set + * + * @return the value of xml:lang of this Identity + */ + public String getLanguage() { + return lang; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<identity"); + // Check if this packet has 'lang' set and maybe append it to the resulting string + if (lang != null) + buf.append(" xml:lang=\"").append(StringUtils.escapeForXML(lang)).append("\""); + // Category must always be set + buf.append(" category=\"").append(StringUtils.escapeForXML(category)).append("\""); + // Name must always be set + buf.append(" name=\"").append(StringUtils.escapeForXML(name)).append("\""); + // Check if this packet has 'type' set and maybe append it to the resulting string + if (type != null) { + buf.append(" type=\"").append(StringUtils.escapeForXML(type)).append("\""); + } + buf.append("/>"); + return buf.toString(); + } + + /** + * Check equality for Identity for category, type, lang and name + * in that order as defined by + * <a href="http://xmpp.org/extensions/xep-0115.html#ver-proc">XEP-0015 5.4 Processing Method (Step 3.3)</a> + * + */ + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj == this) + return true; + if (obj.getClass() != getClass()) + return false; + + DiscoverInfo.Identity other = (DiscoverInfo.Identity) obj; + if (!this.category.equals(other.category)) + return false; + + String otherLang = other.lang == null ? "" : other.lang; + String thisLang = lang == null ? "" : lang; + if (!otherLang.equals(thisLang)) + return false; + + // This safeguard can be removed once the deprecated constructor is removed. + String otherType = other.type == null ? "" : other.type; + String thisType = type == null ? "" : type; + if (!otherType.equals(thisType)) + return false; + + String otherName = other.name == null ? "" : other.name; + String thisName = name == null ? "" : other.name; + if (!thisName.equals(otherName)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = 1; + result = 37 * result + category.hashCode(); + result = 37 * result + (lang == null ? 0 : lang.hashCode()); + result = 37 * result + (type == null ? 0 : type.hashCode()); + result = 37 * result + (name == null ? 0 : name.hashCode()); + return result; + } + + /** + * Compares this identity with another one. The comparison order is: + * Category, Type, Lang. If all three are identical the other Identity is considered equal. + * Name is not used for comparision, as defined by XEP-0115 + * + * @param obj + * @return + */ + public int compareTo(DiscoverInfo.Identity other) { + String otherLang = other.lang == null ? "" : other.lang; + String thisLang = lang == null ? "" : lang; + + // This can be removed once the deprecated constructor is removed. + String otherType = other.type == null ? "" : other.type; + String thisType = type == null ? "" : type; + + if (category.equals(other.category)) { + if (thisType.equals(otherType)) { + if (thisLang.equals(otherLang)) { + // Don't compare on name, XEP-30 says that name SHOULD + // be equals for all identities of an entity + return 0; + } else { + return thisLang.compareTo(otherLang); + } + } else { + return thisType.compareTo(otherType); + } + } else { + return category.compareTo(other.category); + } + } + } + + /** + * Represents the features offered by the item. This information helps requestors determine + * what actions are possible with regard to this item (registration, search, join, etc.) + * as well as specific feature types of interest, if any (e.g., for the purpose of feature + * negotiation). + */ + public static class Feature { + + private String variable; + + /** + * Creates a new feature offered by an XMPP entity or item. + * + * @param variable the feature's variable. + */ + public Feature(String variable) { + if (variable == null) + throw new IllegalArgumentException("variable cannot be null"); + this.variable = variable; + } + + /** + * Returns the feature's variable. + * + * @return the feature's variable. + */ + public String getVar() { + return variable; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<feature var=\"").append(StringUtils.escapeForXML(variable)).append("\"/>"); + return buf.toString(); + } + + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj == this) + return true; + if (obj.getClass() != getClass()) + return false; + + DiscoverInfo.Feature other = (DiscoverInfo.Feature) obj; + return variable.equals(other.variable); + } + + @Override + public int hashCode() { + return 37 * variable.hashCode(); + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/DiscoverItems.java b/src/org/jivesoftware/smackx/packet/DiscoverItems.java new file mode 100644 index 0000000..f6a0941 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DiscoverItems.java @@ -0,0 +1,253 @@ +/** + * $RCSfile$ + * $Revision: 7071 $ + * $Date: 2007-02-12 08:59:05 +0800 (Mon, 12 Feb 2007) $ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A DiscoverItems IQ packet, which is used by XMPP clients to request and receive items + * associated with XMPP entities.<p> + * + * The items could also be queried in order to discover if they contain items inside. Some items + * may be addressable by its JID and others may require to be addressed by a JID and a node name. + * + * @author Gaston Dombiak + */ +public class DiscoverItems extends IQ { + + public static final String NAMESPACE = "http://jabber.org/protocol/disco#items"; + + private final List<Item> items = new CopyOnWriteArrayList<Item>(); + private String node; + + /** + * Adds a new item to the discovered information. + * + * @param item the discovered entity's item + */ + public void addItem(Item item) { + synchronized (items) { + items.add(item); + } + } + + /** + * Adds a collection of items to the discovered information. Does nothing if itemsToAdd is null + * + * @param itemsToAdd + */ + public void addItems(Collection<Item> itemsToAdd) { + if (itemsToAdd == null) return; + for (Item i : itemsToAdd) { + addItem(i); + } + } + + /** + * Returns the discovered items of the queried XMPP entity. + * + * @return an Iterator on the discovered entity's items + */ + public Iterator<DiscoverItems.Item> getItems() { + synchronized (items) { + return Collections.unmodifiableList(items).iterator(); + } + } + + /** + * Returns the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @return the node attribute that supplements the 'jid' attribute + */ + public String getNode() { + return node; + } + + /** + * Sets the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @param node the node attribute that supplements the 'jid' attribute + */ + public void setNode(String node) { + this.node = node; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"" + NAMESPACE + "\""); + if (getNode() != null) { + buf.append(" node=\""); + buf.append(StringUtils.escapeForXML(getNode())); + buf.append("\""); + } + buf.append(">"); + synchronized (items) { + for (Item item : items) { + buf.append(item.toXML()); + } + } + buf.append("</query>"); + return buf.toString(); + } + + /** + * An item is associated with an XMPP Entity, usually thought of a children of the parent + * entity and normally are addressable as a JID.<p> + * + * An item associated with an entity may not be addressable as a JID. In order to handle + * such items, Service Discovery uses an optional 'node' attribute that supplements the + * 'jid' attribute. + */ + public static class Item { + + /** + * Request to create or update the item. + */ + public static final String UPDATE_ACTION = "update"; + + /** + * Request to remove the item. + */ + public static final String REMOVE_ACTION = "remove"; + + private String entityID; + private String name; + private String node; + private String action; + + /** + * Create a new Item associated with a given entity. + * + * @param entityID the id of the entity that contains the item + */ + public Item(String entityID) { + this.entityID = entityID; + } + + /** + * Returns the entity's ID. + * + * @return the entity's ID. + */ + public String getEntityID() { + return entityID; + } + + /** + * Returns the entity's name. + * + * @return the entity's name. + */ + public String getName() { + return name; + } + + /** + * Sets the entity's name. + * + * @param name the entity's name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @return the node attribute that supplements the 'jid' attribute + */ + public String getNode() { + return node; + } + + /** + * Sets the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @param node the node attribute that supplements the 'jid' attribute + */ + public void setNode(String node) { + this.node = node; + } + + /** + * Returns the action that specifies the action being taken for this item. Possible action + * values are: "update" and "remove". Update should either create a new entry if the node + * and jid combination does not already exist, or simply update an existing entry. If + * "remove" is used as the action, the item should be removed from persistent storage. + * + * @return the action being taken for this item + */ + public String getAction() { + return action; + } + + /** + * Sets the action that specifies the action being taken for this item. Possible action + * values are: "update" and "remove". Update should either create a new entry if the node + * and jid combination does not already exist, or simply update an existing entry. If + * "remove" is used as the action, the item should be removed from persistent storage. + * + * @param action the action being taken for this item + */ + public void setAction(String action) { + this.action = action; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<item jid=\"").append(entityID).append("\""); + if (name != null) { + buf.append(" name=\"").append(StringUtils.escapeForXML(name)).append("\""); + } + if (node != null) { + buf.append(" node=\"").append(StringUtils.escapeForXML(node)).append("\""); + } + if (action != null) { + buf.append(" action=\"").append(StringUtils.escapeForXML(action)).append("\""); + } + buf.append("/>"); + return buf.toString(); + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/Header.java b/src/org/jivesoftware/smackx/packet/Header.java new file mode 100644 index 0000000..3fa8386 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/Header.java @@ -0,0 +1,59 @@ +/*
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * Represents a <b>Header</b> entry as specified by the <a href="http://xmpp.org/extensions/xep-031.html">Stanza Headers and Internet Metadata (SHIM)</a>
+
+ * @author Robin Collier
+ */
+public class Header implements PacketExtension
+{
+ private String name;
+ private String value;
+
+ public Header(String name, String value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public String getElementName()
+ {
+ return "header";
+ }
+
+ public String getNamespace()
+ {
+ return HeadersExtension.NAMESPACE;
+ }
+
+ public String toXML()
+ {
+ return "<header name='" + name + "'>" + value + "</header>";
+ }
+
+}
diff --git a/src/org/jivesoftware/smackx/packet/HeadersExtension.java b/src/org/jivesoftware/smackx/packet/HeadersExtension.java new file mode 100644 index 0000000..78564db --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/HeadersExtension.java @@ -0,0 +1,69 @@ +/**
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jivesoftware.smackx.packet;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+
+/**
+ * Extension representing a list of headers as specified in <a href="http://xmpp.org/extensions/xep-0131">Stanza Headers and Internet Metadata (SHIM)</a>
+ *
+ * @see Header
+ *
+ * @author Robin Collier
+ */
+public class HeadersExtension implements PacketExtension
+{
+ public static final String NAMESPACE = "http://jabber.org/protocol/shim";
+
+ private Collection<Header> headers = Collections.EMPTY_LIST;
+
+ public HeadersExtension(Collection<Header> headerList)
+ {
+ if (headerList != null)
+ headers = headerList;
+ }
+
+ public Collection<Header> getHeaders()
+ {
+ return headers;
+ }
+
+ public String getElementName()
+ {
+ return "headers";
+ }
+
+ public String getNamespace()
+ {
+ return NAMESPACE;
+ }
+
+ public String toXML()
+ {
+ StringBuilder builder = new StringBuilder("<" + getElementName() + " xmlns='" + getNamespace() + "'>");
+
+ for (Header header : headers)
+ {
+ builder.append(header.toXML());
+ }
+ builder.append("</" + getElementName() + '>');
+
+ return builder.toString();
+ }
+
+}
diff --git a/src/org/jivesoftware/smackx/packet/LastActivity.java b/src/org/jivesoftware/smackx/packet/LastActivity.java new file mode 100644 index 0000000..6f4f15a --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/LastActivity.java @@ -0,0 +1,164 @@ +/** + * $RCSfile$ + * $Revision: 2407 $ + * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import java.io.IOException; + +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.util.StringUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * A last activity IQ for retrieving information about the last activity associated with a Jabber ID. + * LastActivity (XEP-0012) allows for retrieval of how long a particular user has been idle and the + * message the specified when doing so. Use {@link org.jivesoftware.smackx.LastActivityManager} + * to get the last activity of a user. + * + * @author Derek DeMoro + */ +public class LastActivity extends IQ { + + public static final String NAMESPACE = "jabber:iq:last"; + + public long lastActivity = -1; + public String message; + + public LastActivity() { + setType(IQ.Type.GET); + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"" + NAMESPACE + "\""); + if (lastActivity != -1) { + buf.append(" seconds=\"").append(lastActivity).append("\""); + } + buf.append("></query>"); + return buf.toString(); + } + + + public void setLastActivity(long lastActivity) { + this.lastActivity = lastActivity; + } + + + private void setMessage(String message) { + this.message = message; + } + + /** + * Returns number of seconds that have passed since the user last logged out. + * If the user is offline, 0 will be returned. + * + * @return the number of seconds that have passed since the user last logged out. + */ + public long getIdleTime() { + return lastActivity; + } + + + /** + * Returns the status message of the last unavailable presence received from the user. + * + * @return the status message of the last unavailable presence received from the user + */ + public String getStatusMessage() { + return message; + } + + + /** + * The IQ Provider for LastActivity. + * + * @author Derek DeMoro + */ + public static class Provider implements IQProvider { + + public Provider() { + super(); + } + + public IQ parseIQ(XmlPullParser parser) throws XMPPException, XmlPullParserException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new XMPPException("Parser not in proper position, or bad XML."); + } + + LastActivity lastActivity = new LastActivity(); + String seconds = parser.getAttributeValue("", "seconds"); + String message = null; + try { + message = parser.nextText(); + } catch (IOException e1) { + // Ignore + } + if (seconds != null) { + try { + lastActivity.setLastActivity(Long.parseLong(seconds)); + } catch (NumberFormatException e) { + // Ignore + } + } + + if (message != null) { + lastActivity.setMessage(message); + } + return lastActivity; + } + } + + /** + * Retrieve the last activity of a particular jid. + * @param con the current Connection. + * @param jid the JID of the user. + * @return the LastActivity packet of the jid. + * @throws XMPPException thrown if a server error has occured. + * @deprecated This method only retreives the lapsed time since the last logout of a particular jid. + * Replaced by {@link org.jivesoftware.smackx.LastActivityManager#getLastActivity(Connection, String) getLastActivity} + */ + public static LastActivity getLastActivity(Connection con, String jid) throws XMPPException { + LastActivity activity = new LastActivity(); + jid = StringUtils.parseBareAddress(jid); + activity.setTo(jid); + + PacketCollector collector = con.createPacketCollector(new PacketIDFilter(activity.getPacketID())); + con.sendPacket(activity); + + LastActivity response = (LastActivity) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + if (response == null) { + throw new XMPPException("No response from server on status set."); + } + if (response.getError() != null) { + throw new XMPPException(response.getError()); + } + return response; + } +} diff --git a/src/org/jivesoftware/smackx/packet/MUCAdmin.java b/src/org/jivesoftware/smackx/packet/MUCAdmin.java new file mode 100644 index 0000000..75d17e0 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/MUCAdmin.java @@ -0,0 +1,237 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.jivesoftware.smack.packet.IQ; + +/** + * IQ packet that serves for kicking users, granting and revoking voice, banning users, + * modifying the ban list, granting and revoking membership and granting and revoking + * moderator privileges. All these operations are scoped by the + * 'http://jabber.org/protocol/muc#admin' namespace. + * + * @author Gaston Dombiak + */ +public class MUCAdmin extends IQ { + + private List<Item> items = new ArrayList<Item>(); + + /** + * Returns an Iterator for item childs that holds information about roles, affiliation, + * jids and nicks. + * + * @return an Iterator for item childs that holds information about roles, affiliation, + * jids and nicks. + */ + public Iterator<Item> getItems() { + synchronized (items) { + return Collections.unmodifiableList(new ArrayList<Item>(items)).iterator(); + } + } + + /** + * Adds an item child that holds information about roles, affiliation, jids and nicks. + * + * @param item the item child that holds information about roles, affiliation, jids and nicks. + */ + public void addItem(Item item) { + synchronized (items) { + items.add(item); + } + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"http://jabber.org/protocol/muc#admin\">"); + synchronized (items) { + for (int i = 0; i < items.size(); i++) { + Item item = items.get(i); + buf.append(item.toXML()); + } + } + // Add packet extensions, if any are defined. + buf.append(getExtensionsXML()); + buf.append("</query>"); + return buf.toString(); + } + + /** + * Item child that holds information about roles, affiliation, jids and nicks. + * + * @author Gaston Dombiak + */ + public static class Item { + private String actor; + private String reason; + private String affiliation; + private String jid; + private String nick; + private String role; + + /** + * Creates a new item child. + * + * @param affiliation the actor's affiliation to the room + * @param role the privilege level of an occupant within a room. + */ + public Item(String affiliation, String role) { + this.affiliation = affiliation; + this.role = role; + } + + /** + * Returns the actor (JID of an occupant in the room) that was kicked or banned. + * + * @return the JID of an occupant in the room that was kicked or banned. + */ + public String getActor() { + return actor; + } + + /** + * Returns the reason for the item child. The reason is optional and could be used to + * explain the reason why a user (occupant) was kicked or banned. + * + * @return the reason for the item child. + */ + public String getReason() { + return reason; + } + + /** + * Returns the occupant's affiliation to the room. The affiliation is a semi-permanent + * association or connection with a room. The possible affiliations are "owner", "admin", + * "member", and "outcast" (naturally it is also possible to have no affiliation). An + * affiliation lasts across a user's visits to a room. + * + * @return the actor's affiliation to the room + */ + public String getAffiliation() { + return affiliation; + } + + /** + * Returns the <room@service/nick> by which an occupant is identified within the context + * of a room. If the room is non-anonymous, the JID will be included in the item. + * + * @return the room JID by which an occupant is identified within the room. + */ + public String getJid() { + return jid; + } + + /** + * Returns the new nickname of an occupant that is changing his/her nickname. The new + * nickname is sent as part of the unavailable presence. + * + * @return the new nickname of an occupant that is changing his/her nickname. + */ + public String getNick() { + return nick; + } + + /** + * Returns the temporary position or privilege level of an occupant within a room. The + * possible roles are "moderator", "participant", and "visitor" (it is also possible to + * have no defined role). A role lasts only for the duration of an occupant's visit to + * a room. + * + * @return the privilege level of an occupant within a room. + */ + public String getRole() { + return role; + } + + /** + * Sets the actor (JID of an occupant in the room) that was kicked or banned. + * + * @param actor the actor (JID of an occupant in the room) that was kicked or banned. + */ + public void setActor(String actor) { + this.actor = actor; + } + + /** + * Sets the reason for the item child. The reason is optional and could be used to + * explain the reason why a user (occupant) was kicked or banned. + * + * @param reason the reason why a user (occupant) was kicked or banned. + */ + public void setReason(String reason) { + this.reason = reason; + } + + /** + * Sets the <room@service/nick> by which an occupant is identified within the context + * of a room. If the room is non-anonymous, the JID will be included in the item. + * + * @param jid the JID by which an occupant is identified within a room. + */ + public void setJid(String jid) { + this.jid = jid; + } + + /** + * Sets the new nickname of an occupant that is changing his/her nickname. The new + * nickname is sent as part of the unavailable presence. + * + * @param nick the new nickname of an occupant that is changing his/her nickname. + */ + public void setNick(String nick) { + this.nick = nick; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<item"); + if (getAffiliation() != null) { + buf.append(" affiliation=\"").append(getAffiliation()).append("\""); + } + if (getJid() != null) { + buf.append(" jid=\"").append(getJid()).append("\""); + } + if (getNick() != null) { + buf.append(" nick=\"").append(getNick()).append("\""); + } + if (getRole() != null) { + buf.append(" role=\"").append(getRole()).append("\""); + } + if (getReason() == null && getActor() == null) { + buf.append("/>"); + } + else { + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + if (getActor() != null) { + buf.append("<actor jid=\"").append(getActor()).append("\"/>"); + } + buf.append("</item>"); + } + return buf.toString(); + } + }; +} diff --git a/src/org/jivesoftware/smackx/packet/MUCInitialPresence.java b/src/org/jivesoftware/smackx/packet/MUCInitialPresence.java new file mode 100644 index 0000000..d3d2796 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/MUCInitialPresence.java @@ -0,0 +1,223 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * Represents extended presence information whose sole purpose is to signal the ability of + * the occupant to speak the MUC protocol when joining a room. If the room requires a password + * then the MUCInitialPresence should include one.<p> + * + * The amount of discussion history provided on entering a room (perhaps because the + * user is on a low-bandwidth connection or is using a small-footprint client) could be managed by + * setting a configured History instance to the MUCInitialPresence instance. + * @see MUCInitialPresence#setHistory(MUCInitialPresence.History). + * + * @author Gaston Dombiak + */ +public class MUCInitialPresence implements PacketExtension { + + private String password; + private History history; + + public String getElementName() { + return "x"; + } + + public String getNamespace() { + return "http://jabber.org/protocol/muc"; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\">"); + if (getPassword() != null) { + buf.append("<password>").append(getPassword()).append("</password>"); + } + if (getHistory() != null) { + buf.append(getHistory().toXML()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + + /** + * Returns the history that manages the amount of discussion history provided on + * entering a room. + * + * @return the history that manages the amount of discussion history provided on + * entering a room. + */ + public History getHistory() { + return history; + } + + /** + * Returns the password to use when the room requires a password. + * + * @return the password to use when the room requires a password. + */ + public String getPassword() { + return password; + } + + /** + * Sets the History that manages the amount of discussion history provided on + * entering a room. + * + * @param history that manages the amount of discussion history provided on + * entering a room. + */ + public void setHistory(History history) { + this.history = history; + } + + /** + * Sets the password to use when the room requires a password. + * + * @param password the password to use when the room requires a password. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * The History class controls the number of characters or messages to receive + * when entering a room. + * + * @author Gaston Dombiak + */ + public static class History { + + private int maxChars = -1; + private int maxStanzas = -1; + private int seconds = -1; + private Date since; + + /** + * Returns the total number of characters to receive in the history. + * + * @return total number of characters to receive in the history. + */ + public int getMaxChars() { + return maxChars; + } + + /** + * Returns the total number of messages to receive in the history. + * + * @return the total number of messages to receive in the history. + */ + public int getMaxStanzas() { + return maxStanzas; + } + + /** + * Returns the number of seconds to use to filter the messages received during that time. + * In other words, only the messages received in the last "X" seconds will be included in + * the history. + * + * @return the number of seconds to use to filter the messages received during that time. + */ + public int getSeconds() { + return seconds; + } + + /** + * Returns the since date to use to filter the messages received during that time. + * In other words, only the messages received since the datetime specified will be + * included in the history. + * + * @return the since date to use to filter the messages received during that time. + */ + public Date getSince() { + return since; + } + + /** + * Sets the total number of characters to receive in the history. + * + * @param maxChars the total number of characters to receive in the history. + */ + public void setMaxChars(int maxChars) { + this.maxChars = maxChars; + } + + /** + * Sets the total number of messages to receive in the history. + * + * @param maxStanzas the total number of messages to receive in the history. + */ + public void setMaxStanzas(int maxStanzas) { + this.maxStanzas = maxStanzas; + } + + /** + * Sets the number of seconds to use to filter the messages received during that time. + * In other words, only the messages received in the last "X" seconds will be included in + * the history. + * + * @param seconds the number of seconds to use to filter the messages received during + * that time. + */ + public void setSeconds(int seconds) { + this.seconds = seconds; + } + + /** + * Sets the since date to use to filter the messages received during that time. + * In other words, only the messages received since the datetime specified will be + * included in the history. + * + * @param since the since date to use to filter the messages received during that time. + */ + public void setSince(Date since) { + this.since = since; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<history"); + if (getMaxChars() != -1) { + buf.append(" maxchars=\"").append(getMaxChars()).append("\""); + } + if (getMaxStanzas() != -1) { + buf.append(" maxstanzas=\"").append(getMaxStanzas()).append("\""); + } + if (getSeconds() != -1) { + buf.append(" seconds=\"").append(getSeconds()).append("\""); + } + if (getSince() != null) { + SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + utcFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + buf.append(" since=\"").append(utcFormat.format(getSince())).append("\""); + } + buf.append("/>"); + return buf.toString(); + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/MUCOwner.java b/src/org/jivesoftware/smackx/packet/MUCOwner.java new file mode 100644 index 0000000..e33806e --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/MUCOwner.java @@ -0,0 +1,342 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; +import org.jivesoftware.smack.packet.IQ; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * IQ packet that serves for granting and revoking ownership privileges, granting + * and revoking administrative privileges and destroying a room. All these operations + * are scoped by the 'http://jabber.org/protocol/muc#owner' namespace. + * + * @author Gaston Dombiak + */ +public class MUCOwner extends IQ { + + private List<Item> items = new ArrayList<Item>(); + private Destroy destroy; + + /** + * Returns an Iterator for item childs that holds information about affiliation, + * jids and nicks. + * + * @return an Iterator for item childs that holds information about affiliation, + * jids and nicks. + */ + public Iterator<Item> getItems() { + synchronized (items) { + return Collections.unmodifiableList(new ArrayList<Item>(items)).iterator(); + } + } + + /** + * Returns a request to the server to destroy a room. The sender of the request + * should be the room's owner. If the sender of the destroy request is not the room's owner + * then the server will answer a "Forbidden" error. + * + * @return a request to the server to destroy a room. + */ + public Destroy getDestroy() { + return destroy; + } + + /** + * Sets a request to the server to destroy a room. The sender of the request + * should be the room's owner. If the sender of the destroy request is not the room's owner + * then the server will answer a "Forbidden" error. + * + * @param destroy the request to the server to destroy a room. + */ + public void setDestroy(Destroy destroy) { + this.destroy = destroy; + } + + /** + * Adds an item child that holds information about affiliation, jids and nicks. + * + * @param item the item child that holds information about affiliation, jids and nicks. + */ + public void addItem(Item item) { + synchronized (items) { + items.add(item); + } + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"http://jabber.org/protocol/muc#owner\">"); + synchronized (items) { + for (int i = 0; i < items.size(); i++) { + Item item = (Item) items.get(i); + buf.append(item.toXML()); + } + } + if (getDestroy() != null) { + buf.append(getDestroy().toXML()); + } + // Add packet extensions, if any are defined. + buf.append(getExtensionsXML()); + buf.append("</query>"); + return buf.toString(); + } + + /** + * Item child that holds information about affiliation, jids and nicks. + * + * @author Gaston Dombiak + */ + public static class Item { + + private String actor; + private String reason; + private String affiliation; + private String jid; + private String nick; + private String role; + + /** + * Creates a new item child. + * + * @param affiliation the actor's affiliation to the room + */ + public Item(String affiliation) { + this.affiliation = affiliation; + } + + /** + * Returns the actor (JID of an occupant in the room) that was kicked or banned. + * + * @return the JID of an occupant in the room that was kicked or banned. + */ + public String getActor() { + return actor; + } + + /** + * Returns the reason for the item child. The reason is optional and could be used to + * explain the reason why a user (occupant) was kicked or banned. + * + * @return the reason for the item child. + */ + public String getReason() { + return reason; + } + + /** + * Returns the occupant's affiliation to the room. The affiliation is a semi-permanent + * association or connection with a room. The possible affiliations are "owner", "admin", + * "member", and "outcast" (naturally it is also possible to have no affiliation). An + * affiliation lasts across a user's visits to a room. + * + * @return the actor's affiliation to the room + */ + public String getAffiliation() { + return affiliation; + } + + /** + * Returns the <room@service/nick> by which an occupant is identified within the context + * of a room. If the room is non-anonymous, the JID will be included in the item. + * + * @return the room JID by which an occupant is identified within the room. + */ + public String getJid() { + return jid; + } + + /** + * Returns the new nickname of an occupant that is changing his/her nickname. The new + * nickname is sent as part of the unavailable presence. + * + * @return the new nickname of an occupant that is changing his/her nickname. + */ + public String getNick() { + return nick; + } + + /** + * Returns the temporary position or privilege level of an occupant within a room. The + * possible roles are "moderator", "participant", and "visitor" (it is also possible to + * have no defined role). A role lasts only for the duration of an occupant's visit to + * a room. + * + * @return the privilege level of an occupant within a room. + */ + public String getRole() { + return role; + } + + /** + * Sets the actor (JID of an occupant in the room) that was kicked or banned. + * + * @param actor the actor (JID of an occupant in the room) that was kicked or banned. + */ + public void setActor(String actor) { + this.actor = actor; + } + + /** + * Sets the reason for the item child. The reason is optional and could be used to + * explain the reason why a user (occupant) was kicked or banned. + * + * @param reason the reason why a user (occupant) was kicked or banned. + */ + public void setReason(String reason) { + this.reason = reason; + } + + /** + * Sets the <room@service/nick> by which an occupant is identified within the context + * of a room. If the room is non-anonymous, the JID will be included in the item. + * + * @param jid the JID by which an occupant is identified within a room. + */ + public void setJid(String jid) { + this.jid = jid; + } + + /** + * Sets the new nickname of an occupant that is changing his/her nickname. The new + * nickname is sent as part of the unavailable presence. + * + * @param nick the new nickname of an occupant that is changing his/her nickname. + */ + public void setNick(String nick) { + this.nick = nick; + } + + /** + * Sets the temporary position or privilege level of an occupant within a room. The + * possible roles are "moderator", "participant", and "visitor" (it is also possible to + * have no defined role). A role lasts only for the duration of an occupant's visit to + * a room. + * + * @param role the new privilege level of an occupant within a room. + */ + public void setRole(String role) { + this.role = role; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<item"); + if (getAffiliation() != null) { + buf.append(" affiliation=\"").append(getAffiliation()).append("\""); + } + if (getJid() != null) { + buf.append(" jid=\"").append(getJid()).append("\""); + } + if (getNick() != null) { + buf.append(" nick=\"").append(getNick()).append("\""); + } + if (getRole() != null) { + buf.append(" role=\"").append(getRole()).append("\""); + } + if (getReason() == null && getActor() == null) { + buf.append("/>"); + } + else { + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + if (getActor() != null) { + buf.append("<actor jid=\"").append(getActor()).append("\"/>"); + } + buf.append("</item>"); + } + return buf.toString(); + } + }; + + /** + * Represents a request to the server to destroy a room. The sender of the request + * should be the room's owner. If the sender of the destroy request is not the room's owner + * then the server will answer a "Forbidden" error. + * + * @author Gaston Dombiak + */ + public static class Destroy { + private String reason; + private String jid; + + + /** + * Returns the JID of an alternate location since the current room is being destroyed. + * + * @return the JID of an alternate location. + */ + public String getJid() { + return jid; + } + + /** + * Returns the reason for the room destruction. + * + * @return the reason for the room destruction. + */ + public String getReason() { + return reason; + } + + /** + * Sets the JID of an alternate location since the current room is being destroyed. + * + * @param jid the JID of an alternate location. + */ + public void setJid(String jid) { + this.jid = jid; + } + + /** + * Sets the reason for the room destruction. + * + * @param reason the reason for the room destruction. + */ + public void setReason(String reason) { + this.reason = reason; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<destroy"); + if (getJid() != null) { + buf.append(" jid=\"").append(getJid()).append("\""); + } + if (getReason() == null) { + buf.append("/>"); + } + else { + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + buf.append("</destroy>"); + } + return buf.toString(); + } + + } +} diff --git a/src/org/jivesoftware/smackx/packet/MUCUser.java b/src/org/jivesoftware/smackx/packet/MUCUser.java new file mode 100644 index 0000000..bfcd67c --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/MUCUser.java @@ -0,0 +1,627 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents extended presence information about roles, affiliations, full JIDs, + * or status codes scoped by the 'http://jabber.org/protocol/muc#user' namespace. + * + * @author Gaston Dombiak + */ +public class MUCUser implements PacketExtension { + + private Invite invite; + private Decline decline; + private Item item; + private String password; + private Status status; + private Destroy destroy; + + public String getElementName() { + return "x"; + } + + public String getNamespace() { + return "http://jabber.org/protocol/muc#user"; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\">"); + if (getInvite() != null) { + buf.append(getInvite().toXML()); + } + if (getDecline() != null) { + buf.append(getDecline().toXML()); + } + if (getItem() != null) { + buf.append(getItem().toXML()); + } + if (getPassword() != null) { + buf.append("<password>").append(getPassword()).append("</password>"); + } + if (getStatus() != null) { + buf.append(getStatus().toXML()); + } + if (getDestroy() != null) { + buf.append(getDestroy().toXML()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + + /** + * Returns the invitation for another user to a room. The sender of the invitation + * must be an occupant of the room. The invitation will be sent to the room which in turn + * will forward the invitation to the invitee. + * + * @return an invitation for another user to a room. + */ + public Invite getInvite() { + return invite; + } + + /** + * Returns the rejection to an invitation from another user to a room. The rejection will be + * sent to the room which in turn will forward the refusal to the inviter. + * + * @return a rejection to an invitation from another user to a room. + */ + public Decline getDecline() { + return decline; + } + + /** + * Returns the item child that holds information about roles, affiliation, jids and nicks. + * + * @return an item child that holds information about roles, affiliation, jids and nicks. + */ + public Item getItem() { + return item; + } + + /** + * Returns the password to use to enter Password-Protected Room. A Password-Protected Room is + * a room that a user cannot enter without first providing the correct password. + * + * @return the password to use to enter Password-Protected Room. + */ + public String getPassword() { + return password; + } + + /** + * Returns the status which holds a code that assists in presenting notification messages. + * + * @return the status which holds a code that assists in presenting notification messages. + */ + public Status getStatus() { + return status; + } + + /** + * Returns the notification that the room has been destroyed. After a room has been destroyed, + * the room occupants will receive a Presence packet of type 'unavailable' with the reason for + * the room destruction if provided by the room owner. + * + * @return a notification that the room has been destroyed. + */ + public Destroy getDestroy() { + return destroy; + } + + /** + * Sets the invitation for another user to a room. The sender of the invitation + * must be an occupant of the room. The invitation will be sent to the room which in turn + * will forward the invitation to the invitee. + * + * @param invite the invitation for another user to a room. + */ + public void setInvite(Invite invite) { + this.invite = invite; + } + + /** + * Sets the rejection to an invitation from another user to a room. The rejection will be + * sent to the room which in turn will forward the refusal to the inviter. + * + * @param decline the rejection to an invitation from another user to a room. + */ + public void setDecline(Decline decline) { + this.decline = decline; + } + + /** + * Sets the item child that holds information about roles, affiliation, jids and nicks. + * + * @param item the item child that holds information about roles, affiliation, jids and nicks. + */ + public void setItem(Item item) { + this.item = item; + } + + /** + * Sets the password to use to enter Password-Protected Room. A Password-Protected Room is + * a room that a user cannot enter without first providing the correct password. + * + * @param string the password to use to enter Password-Protected Room. + */ + public void setPassword(String string) { + password = string; + } + + /** + * Sets the status which holds a code that assists in presenting notification messages. + * + * @param status the status which holds a code that assists in presenting notification + * messages. + */ + public void setStatus(Status status) { + this.status = status; + } + + /** + * Sets the notification that the room has been destroyed. After a room has been destroyed, + * the room occupants will receive a Presence packet of type 'unavailable' with the reason for + * the room destruction if provided by the room owner. + * + * @param destroy the notification that the room has been destroyed. + */ + public void setDestroy(Destroy destroy) { + this.destroy = destroy; + } + + /** + * Represents an invitation for another user to a room. The sender of the invitation + * must be an occupant of the room. The invitation will be sent to the room which in turn + * will forward the invitation to the invitee. + * + * @author Gaston Dombiak + */ + public static class Invite { + private String reason; + private String from; + private String to; + + /** + * Returns the bare JID of the inviter or, optionally, the room JID. (e.g. + * 'crone1@shakespeare.lit/desktop'). + * + * @return the room's occupant that sent the invitation. + */ + public String getFrom() { + return from; + } + + /** + * Returns the message explaining the invitation. + * + * @return the message explaining the invitation. + */ + public String getReason() { + return reason; + } + + /** + * Returns the bare JID of the invitee. (e.g. 'hecate@shakespeare.lit') + * + * @return the bare JID of the invitee. + */ + public String getTo() { + return to; + } + + /** + * Sets the bare JID of the inviter or, optionally, the room JID. (e.g. + * 'crone1@shakespeare.lit/desktop') + * + * @param from the bare JID of the inviter or, optionally, the room JID. + */ + public void setFrom(String from) { + this.from = from; + } + + /** + * Sets the message explaining the invitation. + * + * @param reason the message explaining the invitation. + */ + public void setReason(String reason) { + this.reason = reason; + } + + /** + * Sets the bare JID of the invitee. (e.g. 'hecate@shakespeare.lit') + * + * @param to the bare JID of the invitee. + */ + public void setTo(String to) { + this.to = to; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<invite "); + if (getTo() != null) { + buf.append(" to=\"").append(getTo()).append("\""); + } + if (getFrom() != null) { + buf.append(" from=\"").append(getFrom()).append("\""); + } + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + buf.append("</invite>"); + return buf.toString(); + } + } + + /** + * Represents a rejection to an invitation from another user to a room. The rejection will be + * sent to the room which in turn will forward the refusal to the inviter. + * + * @author Gaston Dombiak + */ + public static class Decline { + private String reason; + private String from; + private String to; + + /** + * Returns the bare JID of the invitee that rejected the invitation. (e.g. + * 'crone1@shakespeare.lit/desktop'). + * + * @return the bare JID of the invitee that rejected the invitation. + */ + public String getFrom() { + return from; + } + + /** + * Returns the message explaining why the invitation was rejected. + * + * @return the message explaining the reason for the rejection. + */ + public String getReason() { + return reason; + } + + /** + * Returns the bare JID of the inviter. (e.g. 'hecate@shakespeare.lit') + * + * @return the bare JID of the inviter. + */ + public String getTo() { + return to; + } + + /** + * Sets the bare JID of the invitee that rejected the invitation. (e.g. + * 'crone1@shakespeare.lit/desktop'). + * + * @param from the bare JID of the invitee that rejected the invitation. + */ + public void setFrom(String from) { + this.from = from; + } + + /** + * Sets the message explaining why the invitation was rejected. + * + * @param reason the message explaining the reason for the rejection. + */ + public void setReason(String reason) { + this.reason = reason; + } + + /** + * Sets the bare JID of the inviter. (e.g. 'hecate@shakespeare.lit') + * + * @param to the bare JID of the inviter. + */ + public void setTo(String to) { + this.to = to; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<decline "); + if (getTo() != null) { + buf.append(" to=\"").append(getTo()).append("\""); + } + if (getFrom() != null) { + buf.append(" from=\"").append(getFrom()).append("\""); + } + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + buf.append("</decline>"); + return buf.toString(); + } + } + + /** + * Item child that holds information about roles, affiliation, jids and nicks. + * + * @author Gaston Dombiak + */ + public static class Item { + private String actor; + private String reason; + private String affiliation; + private String jid; + private String nick; + private String role; + + /** + * Creates a new item child. + * + * @param affiliation the actor's affiliation to the room + * @param role the privilege level of an occupant within a room. + */ + public Item(String affiliation, String role) { + this.affiliation = affiliation; + this.role = role; + } + + /** + * Returns the actor (JID of an occupant in the room) that was kicked or banned. + * + * @return the JID of an occupant in the room that was kicked or banned. + */ + public String getActor() { + return actor == null ? "" : actor; + } + + /** + * Returns the reason for the item child. The reason is optional and could be used to + * explain the reason why a user (occupant) was kicked or banned. + * + * @return the reason for the item child. + */ + public String getReason() { + return reason == null ? "" : reason; + } + + /** + * Returns the occupant's affiliation to the room. The affiliation is a semi-permanent + * association or connection with a room. The possible affiliations are "owner", "admin", + * "member", and "outcast" (naturally it is also possible to have no affiliation). An + * affiliation lasts across a user's visits to a room. + * + * @return the actor's affiliation to the room + */ + public String getAffiliation() { + return affiliation; + } + + /** + * Returns the <room@service/nick> by which an occupant is identified within the context + * of a room. If the room is non-anonymous, the JID will be included in the item. + * + * @return the room JID by which an occupant is identified within the room. + */ + public String getJid() { + return jid; + } + + /** + * Returns the new nickname of an occupant that is changing his/her nickname. The new + * nickname is sent as part of the unavailable presence. + * + * @return the new nickname of an occupant that is changing his/her nickname. + */ + public String getNick() { + return nick; + } + + /** + * Returns the temporary position or privilege level of an occupant within a room. The + * possible roles are "moderator", "participant", and "visitor" (it is also possible to + * have no defined role). A role lasts only for the duration of an occupant's visit to + * a room. + * + * @return the privilege level of an occupant within a room. + */ + public String getRole() { + return role; + } + + /** + * Sets the actor (JID of an occupant in the room) that was kicked or banned. + * + * @param actor the actor (JID of an occupant in the room) that was kicked or banned. + */ + public void setActor(String actor) { + this.actor = actor; + } + + /** + * Sets the reason for the item child. The reason is optional and could be used to + * explain the reason why a user (occupant) was kicked or banned. + * + * @param reason the reason why a user (occupant) was kicked or banned. + */ + public void setReason(String reason) { + this.reason = reason; + } + + /** + * Sets the <room@service/nick> by which an occupant is identified within the context + * of a room. If the room is non-anonymous, the JID will be included in the item. + * + * @param jid the JID by which an occupant is identified within a room. + */ + public void setJid(String jid) { + this.jid = jid; + } + + /** + * Sets the new nickname of an occupant that is changing his/her nickname. The new + * nickname is sent as part of the unavailable presence. + * + * @param nick the new nickname of an occupant that is changing his/her nickname. + */ + public void setNick(String nick) { + this.nick = nick; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<item"); + if (getAffiliation() != null) { + buf.append(" affiliation=\"").append(getAffiliation()).append("\""); + } + if (getJid() != null) { + buf.append(" jid=\"").append(getJid()).append("\""); + } + if (getNick() != null) { + buf.append(" nick=\"").append(getNick()).append("\""); + } + if (getRole() != null) { + buf.append(" role=\"").append(getRole()).append("\""); + } + if (getReason() == null && getActor() == null) { + buf.append("/>"); + } + else { + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + if (getActor() != null) { + buf.append("<actor jid=\"").append(getActor()).append("\"/>"); + } + buf.append("</item>"); + } + return buf.toString(); + } + } + + /** + * Status code assists in presenting notification messages. The following link provides the + * list of existing error codes (@link http://www.jabber.org/jeps/jep-0045.html#errorstatus). + * + * @author Gaston Dombiak + */ + public static class Status { + private String code; + + /** + * Creates a new instance of Status with the specified code. + * + * @param code the code that uniquely identifies the reason of the error. + */ + public Status(String code) { + this.code = code; + } + + /** + * Returns the code that uniquely identifies the reason of the error. The code + * assists in presenting notification messages. + * + * @return the code that uniquely identifies the reason of the error. + */ + public String getCode() { + return code; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<status code=\"").append(getCode()).append("\"/>"); + return buf.toString(); + } + } + + /** + * Represents a notification that the room has been destroyed. After a room has been destroyed, + * the room occupants will receive a Presence packet of type 'unavailable' with the reason for + * the room destruction if provided by the room owner. + * + * @author Gaston Dombiak + */ + public static class Destroy { + private String reason; + private String jid; + + + /** + * Returns the JID of an alternate location since the current room is being destroyed. + * + * @return the JID of an alternate location. + */ + public String getJid() { + return jid; + } + + /** + * Returns the reason for the room destruction. + * + * @return the reason for the room destruction. + */ + public String getReason() { + return reason; + } + + /** + * Sets the JID of an alternate location since the current room is being destroyed. + * + * @param jid the JID of an alternate location. + */ + public void setJid(String jid) { + this.jid = jid; + } + + /** + * Sets the reason for the room destruction. + * + * @param reason the reason for the room destruction. + */ + public void setReason(String reason) { + this.reason = reason; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<destroy"); + if (getJid() != null) { + buf.append(" jid=\"").append(getJid()).append("\""); + } + if (getReason() == null) { + buf.append("/>"); + } + else { + buf.append(">"); + if (getReason() != null) { + buf.append("<reason>").append(getReason()).append("</reason>"); + } + buf.append("</destroy>"); + } + return buf.toString(); + } + + } +}
\ No newline at end of file diff --git a/src/org/jivesoftware/smackx/packet/MessageEvent.java b/src/org/jivesoftware/smackx/packet/MessageEvent.java new file mode 100644 index 0000000..5e146dc --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/MessageEvent.java @@ -0,0 +1,335 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Represents message events relating to the delivery, display, composition and cancellation of + * messages.<p> + * + * There are four message events currently defined in this namespace: + * <ol> + * <li>Offline<br> + * Indicates that the message has been stored offline by the intended recipient's server. This + * event is triggered only if the intended recipient's server supports offline storage, has that + * support enabled, and the recipient is offline when the server receives the message for delivery.</li> + * + * <li>Delivered<br> + * Indicates that the message has been delivered to the recipient. This signifies that the message + * has reached the recipient's XMPP client, but does not necessarily mean that the message has + * been displayed. This event is to be raised by the XMPP client.</li> + * + * <li>Displayed<br> + * Once the message has been received by the recipient's XMPP client, it may be displayed to the + * user. This event indicates that the message has been displayed, and is to be raised by the + * XMPP client. Even if a message is displayed multiple times, this event should be raised only + * once.</li> + * + * <li>Composing<br> + * In threaded chat conversations, this indicates that the recipient is composing a reply to a + * message. The event is to be raised by the recipient's XMPP client. A XMPP client is allowed + * to raise this event multiple times in response to the same request, providing the original + * event is cancelled first.</li> + * </ol> + * + * @author Gaston Dombiak + */ +public class MessageEvent implements PacketExtension { + + public static final String OFFLINE = "offline"; + public static final String COMPOSING = "composing"; + public static final String DISPLAYED = "displayed"; + public static final String DELIVERED = "delivered"; + public static final String CANCELLED = "cancelled"; + + private boolean offline = false; + private boolean delivered = false; + private boolean displayed = false; + private boolean composing = false; + private boolean cancelled = true; + + private String packetID = null; + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "x" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "x"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * According the specification the namespace is always "jabber:x:event" + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "jabber:x:event"; + } + + /** + * When the message is a request returns if the sender of the message requests to be notified + * when the receiver is composing a reply. + * When the message is a notification returns if the receiver of the message is composing a + * reply. + * + * @return true if the sender is requesting to be notified when composing or when notifying + * that the receiver of the message is composing a reply + */ + public boolean isComposing() { + return composing; + } + + /** + * When the message is a request returns if the sender of the message requests to be notified + * when the message is delivered. + * When the message is a notification returns if the message was delivered or not. + * + * @return true if the sender is requesting to be notified when delivered or when notifying + * that the message was delivered + */ + public boolean isDelivered() { + return delivered; + } + + /** + * When the message is a request returns if the sender of the message requests to be notified + * when the message is displayed. + * When the message is a notification returns if the message was displayed or not. + * + * @return true if the sender is requesting to be notified when displayed or when notifying + * that the message was displayed + */ + public boolean isDisplayed() { + return displayed; + } + + /** + * When the message is a request returns if the sender of the message requests to be notified + * when the receiver of the message is offline. + * When the message is a notification returns if the receiver of the message was offline. + * + * @return true if the sender is requesting to be notified when offline or when notifying + * that the receiver of the message is offline + */ + public boolean isOffline() { + return offline; + } + + /** + * When the message is a notification returns if the receiver of the message cancelled + * composing a reply. + * + * @return true if the receiver of the message cancelled composing a reply + */ + public boolean isCancelled() { + return cancelled; + } + + /** + * Returns the unique ID of the message that requested to be notified of the event. + * The packet id is not used when the message is a request for notifications + * + * @return the message id that requested to be notified of the event. + */ + public String getPacketID() { + return packetID; + } + + /** + * Returns the types of events. The type of event could be: + * "offline", "composing","delivered","displayed", "offline" + * + * @return an iterator over all the types of events of the MessageEvent. + */ + public Iterator<String> getEventTypes() { + ArrayList<String> allEvents = new ArrayList<String>(); + if (isDelivered()) { + allEvents.add(MessageEvent.DELIVERED); + } + if (!isMessageEventRequest() && isCancelled()) { + allEvents.add(MessageEvent.CANCELLED); + } + if (isComposing()) { + allEvents.add(MessageEvent.COMPOSING); + } + if (isDisplayed()) { + allEvents.add(MessageEvent.DISPLAYED); + } + if (isOffline()) { + allEvents.add(MessageEvent.OFFLINE); + } + return allEvents.iterator(); + } + + /** + * When the message is a request sets if the sender of the message requests to be notified + * when the receiver is composing a reply. + * When the message is a notification sets if the receiver of the message is composing a + * reply. + * + * @param composing sets if the sender is requesting to be notified when composing or when + * notifying that the receiver of the message is composing a reply + */ + public void setComposing(boolean composing) { + this.composing = composing; + setCancelled(false); + } + + /** + * When the message is a request sets if the sender of the message requests to be notified + * when the message is delivered. + * When the message is a notification sets if the message was delivered or not. + * + * @param delivered sets if the sender is requesting to be notified when delivered or when + * notifying that the message was delivered + */ + public void setDelivered(boolean delivered) { + this.delivered = delivered; + setCancelled(false); + } + + /** + * When the message is a request sets if the sender of the message requests to be notified + * when the message is displayed. + * When the message is a notification sets if the message was displayed or not. + * + * @param displayed sets if the sender is requesting to be notified when displayed or when + * notifying that the message was displayed + */ + public void setDisplayed(boolean displayed) { + this.displayed = displayed; + setCancelled(false); + } + + /** + * When the message is a request sets if the sender of the message requests to be notified + * when the receiver of the message is offline. + * When the message is a notification sets if the receiver of the message was offline. + * + * @param offline sets if the sender is requesting to be notified when offline or when + * notifying that the receiver of the message is offline + */ + public void setOffline(boolean offline) { + this.offline = offline; + setCancelled(false); + } + + /** + * When the message is a notification sets if the receiver of the message cancelled + * composing a reply. + * The Cancelled event is never requested explicitly. It is requested implicitly when + * requesting to be notified of the Composing event. + * + * @param cancelled sets if the receiver of the message cancelled composing a reply + */ + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + /** + * Sets the unique ID of the message that requested to be notified of the event. + * The packet id is not used when the message is a request for notifications + * + * @param packetID the message id that requested to be notified of the event. + */ + public void setPacketID(String packetID) { + this.packetID = packetID; + } + + /** + * Returns true if this MessageEvent is a request for notifications. + * Returns false if this MessageEvent is a notification of an event. + * + * @return true if this message is a request for notifications. + */ + public boolean isMessageEventRequest() { + return this.packetID == null; + } + + /** + * Returns the XML representation of a Message Event according the specification. + * + * Usually the XML representation will be inside of a Message XML representation like + * in the following examples:<p> + * + * Request to be notified when displayed: + * <pre> + * <message + * to='romeo@montague.net/orchard' + * from='juliet@capulet.com/balcony' + * id='message22'> + * <x xmlns='jabber:x:event'> + * <displayed/> + * </x> + * </message> + * </pre> + * + * Notification of displayed: + * <pre> + * <message + * from='romeo@montague.net/orchard' + * to='juliet@capulet.com/balcony'> + * <x xmlns='jabber:x:event'> + * <displayed/> + * <id>message22</id> + * </x> + * </message> + * </pre> + * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\">"); + // Note: Cancellation events don't specify any tag. They just send the packetID + + // Add the offline tag if the sender requests to be notified of offline events or if + // the target is offline + if (isOffline()) + buf.append("<").append(MessageEvent.OFFLINE).append("/>"); + // Add the delivered tag if the sender requests to be notified when the message is + // delivered or if the target notifies that the message has been delivered + if (isDelivered()) + buf.append("<").append(MessageEvent.DELIVERED).append("/>"); + // Add the displayed tag if the sender requests to be notified when the message is + // displayed or if the target notifies that the message has been displayed + if (isDisplayed()) + buf.append("<").append(MessageEvent.DISPLAYED).append("/>"); + // Add the composing tag if the sender requests to be notified when the target is + // composing a reply or if the target notifies that he/she is composing a reply + if (isComposing()) + buf.append("<").append(MessageEvent.COMPOSING).append("/>"); + // Add the id tag only if the MessageEvent is a notification message (not a request) + if (getPacketID() != null) + buf.append("<id>").append(getPacketID()).append("</id>"); + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/MultipleAddresses.java b/src/org/jivesoftware/smackx/packet/MultipleAddresses.java new file mode 100644 index 0000000..522250a --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/MultipleAddresses.java @@ -0,0 +1,205 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2006 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Packet extension that contains the list of addresses that a packet should be sent or was sent. + * + * @author Gaston Dombiak + */ +public class MultipleAddresses implements PacketExtension { + + public static final String BCC = "bcc"; + public static final String CC = "cc"; + public static final String NO_REPLY = "noreply"; + public static final String REPLY_ROOM = "replyroom"; + public static final String REPLY_TO = "replyto"; + public static final String TO = "to"; + + + private List<Address> addresses = new ArrayList<Address>(); + + /** + * Adds a new address to which the packet is going to be sent or was sent. + * + * @param type on of the static type (BCC, CC, NO_REPLY, REPLY_ROOM, etc.) + * @param jid the JID address of the recipient. + * @param node used to specify a sub-addressable unit at a particular JID, corresponding to + * a Service Discovery node. + * @param desc used to specify human-readable information for this address. + * @param delivered true when the packet was already delivered to this address. + * @param uri used to specify an external system address, such as a sip:, sips:, or im: URI. + */ + public void addAddress(String type, String jid, String node, String desc, boolean delivered, + String uri) { + // Create a new address with the specificed configuration + Address address = new Address(type); + address.setJid(jid); + address.setNode(node); + address.setDescription(desc); + address.setDelivered(delivered); + address.setUri(uri); + // Add the new address to the list of multiple recipients + addresses.add(address); + } + + /** + * Indicate that the packet being sent should not be replied. + */ + public void setNoReply() { + // Create a new address with the specificed configuration + Address address = new Address(NO_REPLY); + // Add the new address to the list of multiple recipients + addresses.add(address); + } + + /** + * Returns the list of addresses that matches the specified type. Examples of address + * type are: TO, CC, BCC, etc.. + * + * @param type Examples of address type are: TO, CC, BCC, etc. + * @return the list of addresses that matches the specified type. + */ + public List<Address> getAddressesOfType(String type) { + List<Address> answer = new ArrayList<Address>(addresses.size()); + for (Iterator<Address> it = addresses.iterator(); it.hasNext();) { + Address address = (Address) it.next(); + if (address.getType().equals(type)) { + answer.add(address); + } + } + + return answer; + } + + public String getElementName() { + return "addresses"; + } + + public String getNamespace() { + return "http://jabber.org/protocol/address"; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()); + buf.append(" xmlns=\"").append(getNamespace()).append("\">"); + // Loop through all the addresses and append them to the string buffer + for (Iterator<Address> i = addresses.iterator(); i.hasNext();) { + Address address = (Address) i.next(); + buf.append(address.toXML()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + + public static class Address { + + private String type; + private String jid; + private String node; + private String description; + private boolean delivered; + private String uri; + + private Address(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public String getJid() { + return jid; + } + + private void setJid(String jid) { + this.jid = jid; + } + + public String getNode() { + return node; + } + + private void setNode(String node) { + this.node = node; + } + + public String getDescription() { + return description; + } + + private void setDescription(String description) { + this.description = description; + } + + public boolean isDelivered() { + return delivered; + } + + private void setDelivered(boolean delivered) { + this.delivered = delivered; + } + + public String getUri() { + return uri; + } + + private void setUri(String uri) { + this.uri = uri; + } + + private String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<address type=\""); + // Append the address type (e.g. TO/CC/BCC) + buf.append(type).append("\""); + if (jid != null) { + buf.append(" jid=\""); + buf.append(jid).append("\""); + } + if (node != null) { + buf.append(" node=\""); + buf.append(node).append("\""); + } + if (description != null && description.trim().length() > 0) { + buf.append(" desc=\""); + buf.append(description).append("\""); + } + if (delivered) { + buf.append(" delivered=\"true\""); + } + if (uri != null) { + buf.append(" uri=\""); + buf.append(uri).append("\""); + } + buf.append("/>"); + return buf.toString(); + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/Nick.java b/src/org/jivesoftware/smackx/packet/Nick.java new file mode 100644 index 0000000..9a64eaa --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/Nick.java @@ -0,0 +1,112 @@ +/**
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2007 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.provider.PacketExtensionProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A minimalistic implementation of a {@link PacketExtension} for nicknames.
+ *
+ * @author Guus der Kinderen, guus.der.kinderen@gmail.com
+ * @see <a href="http://xmpp.org/extensions/xep-0172.html">XEP-0172: User Nickname</a>
+ */
+public class Nick implements PacketExtension {
+
+ public static final String NAMESPACE = "http://jabber.org/protocol/nick";
+
+ public static final String ELEMENT_NAME = "nick";
+
+ private String name = null;
+
+ public Nick(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The value of this nickname
+ *
+ * @return the nickname
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of this nickname
+ *
+ * @param name
+ * the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.PacketExtension#getElementName()
+ */
+ public String getElementName() {
+ return ELEMENT_NAME;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.PacketExtension#getNamespace()
+ */
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.PacketExtension#toXML()
+ */
+ public String toXML() {
+ final StringBuilder buf = new StringBuilder();
+
+ buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(
+ NAMESPACE).append("\">");
+ buf.append(getName());
+ buf.append("</").append(ELEMENT_NAME).append('>');
+
+ return buf.toString();
+ }
+
+ public static class Provider implements PacketExtensionProvider {
+
+ public PacketExtension parseExtension(XmlPullParser parser)
+ throws Exception {
+
+ parser.next();
+ final String name = parser.getText();
+
+ // Advance to end of extension.
+ while(parser.getEventType() != XmlPullParser.END_TAG) {
+ parser.next();
+ }
+
+ return new Nick(name);
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smackx/packet/OfflineMessageInfo.java b/src/org/jivesoftware/smackx/packet/OfflineMessageInfo.java new file mode 100644 index 0000000..5f9954d --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/OfflineMessageInfo.java @@ -0,0 +1,128 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * OfflineMessageInfo is an extension included in the retrieved offline messages requested by + * the {@link org.jivesoftware.smackx.OfflineMessageManager}. This extension includes a stamp + * that uniquely identifies the offline message. This stamp may be used for deleting the offline + * message. The stamp may be of the form UTC timestamps but it is not required to have that format. + * + * @author Gaston Dombiak + */ +public class OfflineMessageInfo implements PacketExtension { + + private String node = null; + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "offline" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "offline"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * According the specification the namespace is always "http://jabber.org/protocol/offline" + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "http://jabber.org/protocol/offline"; + } + + /** + * Returns the stamp that uniquely identifies the offline message. This stamp may + * be used for deleting the offline message. The stamp may be of the form UTC timestamps + * but it is not required to have that format. + * + * @return the stamp that uniquely identifies the offline message. + */ + public String getNode() { + return node; + } + + /** + * Sets the stamp that uniquely identifies the offline message. This stamp may + * be used for deleting the offline message. The stamp may be of the form UTC timestamps + * but it is not required to have that format. + * + * @param node the stamp that uniquely identifies the offline message. + */ + public void setNode(String node) { + this.node = node; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\">"); + if (getNode() != null) + buf.append("<item node=\"").append(getNode()).append("\"/>"); + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + + public static class Provider implements PacketExtensionProvider { + + /** + * Creates a new Provider. + * ProviderManager requires that every PacketExtensionProvider has a public, + * no-argument constructor + */ + public Provider() { + } + + /** + * Parses a OfflineMessageInfo packet (extension sub-packet). + * + * @param parser the XML parser, positioned at the starting element of the extension. + * @return a PacketExtension. + * @throws Exception if a parsing error occurs. + */ + public PacketExtension parseExtension(XmlPullParser parser) + throws Exception { + OfflineMessageInfo info = new OfflineMessageInfo(); + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("item")) + info.setNode(parser.getAttributeValue("", "node")); + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("offline")) { + done = true; + } + } + } + + return info; + } + + } +} diff --git a/src/org/jivesoftware/smackx/packet/OfflineMessageRequest.java b/src/org/jivesoftware/smackx/packet/OfflineMessageRequest.java new file mode 100644 index 0000000..1d9d096 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/OfflineMessageRequest.java @@ -0,0 +1,237 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.IQProvider; +import org.xmlpull.v1.XmlPullParser; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Represents a request to get some or all the offline messages of a user. This class can also + * be used for deleting some or all the offline messages of a user. + * + * @author Gaston Dombiak + */ +public class OfflineMessageRequest extends IQ { + + private List<Item> items = new ArrayList<Item>(); + private boolean purge = false; + private boolean fetch = false; + + /** + * Returns an Iterator for item childs that holds information about offline messages to + * view or delete. + * + * @return an Iterator for item childs that holds information about offline messages to + * view or delete. + */ + public Iterator<Item> getItems() { + synchronized (items) { + return Collections.unmodifiableList(new ArrayList<Item>(items)).iterator(); + } + } + + /** + * Adds an item child that holds information about offline messages to view or delete. + * + * @param item the item child that holds information about offline messages to view or delete. + */ + public void addItem(Item item) { + synchronized (items) { + items.add(item); + } + } + + /** + * Returns true if all the offline messages of the user should be deleted. + * + * @return true if all the offline messages of the user should be deleted. + */ + public boolean isPurge() { + return purge; + } + + /** + * Sets if all the offline messages of the user should be deleted. + * + * @param purge true if all the offline messages of the user should be deleted. + */ + public void setPurge(boolean purge) { + this.purge = purge; + } + + /** + * Returns true if all the offline messages of the user should be retrieved. + * + * @return true if all the offline messages of the user should be retrieved. + */ + public boolean isFetch() { + return fetch; + } + + /** + * Sets if all the offline messages of the user should be retrieved. + * + * @param fetch true if all the offline messages of the user should be retrieved. + */ + public void setFetch(boolean fetch) { + this.fetch = fetch; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<offline xmlns=\"http://jabber.org/protocol/offline\">"); + synchronized (items) { + for (int i = 0; i < items.size(); i++) { + Item item = items.get(i); + buf.append(item.toXML()); + } + } + if (purge) { + buf.append("<purge/>"); + } + if (fetch) { + buf.append("<fetch/>"); + } + // Add packet extensions, if any are defined. + buf.append(getExtensionsXML()); + buf.append("</offline>"); + return buf.toString(); + } + + /** + * Item child that holds information about offline messages to view or delete. + * + * @author Gaston Dombiak + */ + public static class Item { + private String action; + private String jid; + private String node; + + /** + * Creates a new item child. + * + * @param node the actor's affiliation to the room + */ + public Item(String node) { + this.node = node; + } + + public String getNode() { + return node; + } + + /** + * Returns "view" or "remove" that indicate if the server should return the specified + * offline message or delete it. + * + * @return "view" or "remove" that indicate if the server should return the specified + * offline message or delete it. + */ + public String getAction() { + return action; + } + + /** + * Sets if the server should return the specified offline message or delete it. Possible + * values are "view" or "remove". + * + * @param action if the server should return the specified offline message or delete it. + */ + public void setAction(String action) { + this.action = action; + } + + public String getJid() { + return jid; + } + + public void setJid(String jid) { + this.jid = jid; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<item"); + if (getAction() != null) { + buf.append(" action=\"").append(getAction()).append("\""); + } + if (getJid() != null) { + buf.append(" jid=\"").append(getJid()).append("\""); + } + if (getNode() != null) { + buf.append(" node=\"").append(getNode()).append("\""); + } + buf.append("/>"); + return buf.toString(); + } + } + + public static class Provider implements IQProvider { + + public IQ parseIQ(XmlPullParser parser) throws Exception { + OfflineMessageRequest request = new OfflineMessageRequest(); + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("item")) { + request.addItem(parseItem(parser)); + } + else if (parser.getName().equals("purge")) { + request.setPurge(true); + } + else if (parser.getName().equals("fetch")) { + request.setFetch(true); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("offline")) { + done = true; + } + } + } + + return request; + } + + private Item parseItem(XmlPullParser parser) throws Exception { + boolean done = false; + Item item = new Item(parser.getAttributeValue("", "node")); + item.setAction(parser.getAttributeValue("", "action")); + item.setJid(parser.getAttributeValue("", "jid")); + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("item")) { + done = true; + } + } + } + return item; + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/PEPEvent.java b/src/org/jivesoftware/smackx/packet/PEPEvent.java new file mode 100644 index 0000000..48f1de2 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/PEPEvent.java @@ -0,0 +1,105 @@ +/** + * $RCSfile: PEPEvent.java,v $ + * $Revision: 1.1 $ + * $Date: 2007/11/03 00:14:32 $ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents XMPP Personal Event Protocol packets.<p> + * + * The 'http://jabber.org/protocol/pubsub#event' namespace is used to publish personal events items from one client + * to subscribed clients (See XEP-163). + * + * @author Jeff Williams + */ +public class PEPEvent implements PacketExtension { + + PEPItem item; + + /** + * Creates a new empty roster exchange package. + * + */ + public PEPEvent() { + super(); + } + + /** + * Creates a new empty roster exchange package. + * + */ + public PEPEvent(PEPItem item) { + super(); + + this.item = item; + } + + public void addPEPItem(PEPItem item) { + this.item = item; + } + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "x" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "event"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * According the specification the namespace is always "jabber:x:roster" + * (which is not to be confused with the 'jabber:iq:roster' namespace + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "http://jabber.org/protocol/pubsub"; + } + + /** + * Returns the XML representation of a Personal Event Publish according the specification. + * + * Usually the XML representation will be inside of a Message XML representation like + * in the following example: + * <pre> + * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"> + * <subject>Any subject you want</subject> + * <body>This message contains roster items.</body> + * <x xmlns="jabber:x:roster"> + * <item jid="gato1@gato.home"/> + * <item jid="gato2@gato.home"/> + * </x> + * </message> + * </pre> + * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">"); + buf.append(item.toXML()); + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/PEPItem.java b/src/org/jivesoftware/smackx/packet/PEPItem.java new file mode 100644 index 0000000..c3ff6f4 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/PEPItem.java @@ -0,0 +1,92 @@ +/** + * $RCSfile: PEPItem.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/11/06 02:05:09 $ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents XMPP Personal Event Protocol packets.<p> + * + * The 'http://jabber.org/protocol/pubsub#event' namespace is used to publish personal events items from one client + * to subscribed clients (See XEP-163). + * + * @author Jeff Williams + */ +public abstract class PEPItem implements PacketExtension { + + String id; + abstract String getNode(); + abstract String getItemDetailsXML(); + + /** + * Creates a new PEPItem. + * + */ + public PEPItem(String id) { + super(); + this.id = id; + } + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "x" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "item"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "http://jabber.org/protocol/pubsub"; + } + + /** + * Returns the XML representation of a Personal Event Publish according the specification. + * + * Usually the XML representation will be inside of a Message XML representation like + * in the following example: + * <pre> + * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"> + * <subject>Any subject you want</subject> + * <body>This message contains roster items.</body> + * <x xmlns="jabber:x:roster"> + * <item jid="gato1@gato.home"/> + * <item jid="gato2@gato.home"/> + * </x> + * </message> + * </pre> + * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" id=\"").append(id).append("\">"); + buf.append(getItemDetailsXML()); + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/PEPPubSub.java b/src/org/jivesoftware/smackx/packet/PEPPubSub.java new file mode 100644 index 0000000..420ce61 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/PEPPubSub.java @@ -0,0 +1,95 @@ +/** + * $RCSfile: PEPPubSub.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/11/03 04:46:52 $ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; + +/** + * Represents XMPP PEP/XEP-163 pubsub packets.<p> + * + * The 'http://jabber.org/protocol/pubsub' namespace is used to publish personal events items from one client + * to subscribed clients (See XEP-163). + * + * @author Jeff Williams + */ +public class PEPPubSub extends IQ { + + PEPItem item; + + /** + * Creates a new PubSub. + * + */ + public PEPPubSub(PEPItem item) { + super(); + + this.item = item; + } + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "x" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "pubsub"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * According the specification the namespace is always "jabber:x:roster" + * (which is not to be confused with the 'jabber:iq:roster' namespace + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "http://jabber.org/protocol/pubsub"; + } + + /** + * Returns the XML representation of a Personal Event Publish according the specification. + * + * Usually the XML representation will be inside of a Message XML representation like + * in the following example: + * <pre> + * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"> + * <subject>Any subject you want</subject> + * <body>This message contains roster items.</body> + * <x xmlns="jabber:x:roster"> + * <item jid="gato1@gato.home"/> + * <item jid="gato2@gato.home"/> + * </x> + * </message> + * </pre> + * + */ + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">"); + buf.append("<publish node=\"").append(item.getNode()).append("\">"); + buf.append(item.toXML()); + buf.append("</publish>"); + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/PrivateData.java b/src/org/jivesoftware/smackx/packet/PrivateData.java new file mode 100644 index 0000000..3ddb7d5 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/PrivateData.java @@ -0,0 +1,52 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +/** + * Interface to represent private data. Each private data chunk is an XML sub-document + * with a root element name and namespace. + * + * @see org.jivesoftware.smackx.PrivateDataManager + * @author Matt Tucker + */ +public interface PrivateData { + + /** + * Returns the root element name. + * + * @return the element name. + */ + public String getElementName(); + + /** + * Returns the root element XML namespace. + * + * @return the namespace. + */ + public String getNamespace(); + + /** + * Returns the XML reppresentation of the PrivateData. + * + * @return the private data as XML. + */ + public String toXML(); +} diff --git a/src/org/jivesoftware/smackx/packet/RosterExchange.java b/src/org/jivesoftware/smackx/packet/RosterExchange.java new file mode 100644 index 0000000..ad59146 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/RosterExchange.java @@ -0,0 +1,181 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.Roster; +import org.jivesoftware.smack.RosterEntry; +import org.jivesoftware.smack.RosterGroup; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.RemoteRosterEntry; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Represents XMPP Roster Item Exchange packets.<p> + * + * The 'jabber:x:roster' namespace (which is not to be confused with the 'jabber:iq:roster' + * namespace) is used to send roster items from one client to another. A roster item is sent by + * adding to the <message/> element an <x/> child scoped by the 'jabber:x:roster' namespace. This + * <x/> element may contain one or more <item/> children (one for each roster item to be sent).<p> + * + * Each <item/> element may possess the following attributes:<p> + * + * <jid/> -- The id of the contact being sent. This attribute is required.<br> + * <name/> -- A natural-language nickname for the contact. This attribute is optional.<p> + * + * Each <item/> element may also contain one or more <group/> children specifying the + * natural-language name of a user-specified group, for the purpose of categorizing this contact + * into one or more roster groups. + * + * @author Gaston Dombiak + */ +public class RosterExchange implements PacketExtension { + + private List<RemoteRosterEntry> remoteRosterEntries = new ArrayList<RemoteRosterEntry>(); + + /** + * Creates a new empty roster exchange package. + * + */ + public RosterExchange() { + super(); + } + + /** + * Creates a new roster exchange package with the entries specified in roster. + * + * @param roster the roster to send to other XMPP entity. + */ + public RosterExchange(Roster roster) { + // Add all the roster entries to the new RosterExchange + for (RosterEntry rosterEntry : roster.getEntries()) { + this.addRosterEntry(rosterEntry); + } + } + + /** + * Adds a roster entry to the packet. + * + * @param rosterEntry a roster entry to add. + */ + public void addRosterEntry(RosterEntry rosterEntry) { + // Obtain a String[] from the roster entry groups name + List<String> groupNamesList = new ArrayList<String>(); + String[] groupNames; + for (RosterGroup group : rosterEntry.getGroups()) { + groupNamesList.add(group.getName()); + } + groupNames = groupNamesList.toArray(new String[groupNamesList.size()]); + + // Create a new Entry based on the rosterEntry and add it to the packet + RemoteRosterEntry remoteRosterEntry = new RemoteRosterEntry(rosterEntry.getUser(), + rosterEntry.getName(), groupNames); + + addRosterEntry(remoteRosterEntry); + } + + /** + * Adds a remote roster entry to the packet. + * + * @param remoteRosterEntry a remote roster entry to add. + */ + public void addRosterEntry(RemoteRosterEntry remoteRosterEntry) { + synchronized (remoteRosterEntries) { + remoteRosterEntries.add(remoteRosterEntry); + } + } + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "x" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "x"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * According the specification the namespace is always "jabber:x:roster" + * (which is not to be confused with the 'jabber:iq:roster' namespace + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "jabber:x:roster"; + } + + /** + * Returns an Iterator for the roster entries in the packet. + * + * @return an Iterator for the roster entries in the packet. + */ + public Iterator<RemoteRosterEntry> getRosterEntries() { + synchronized (remoteRosterEntries) { + List<RemoteRosterEntry> entries = Collections.unmodifiableList(new ArrayList<RemoteRosterEntry>(remoteRosterEntries)); + return entries.iterator(); + } + } + + /** + * Returns a count of the entries in the roster exchange. + * + * @return the number of entries in the roster exchange. + */ + public int getEntryCount() { + return remoteRosterEntries.size(); + } + + /** + * Returns the XML representation of a Roster Item Exchange according the specification. + * + * Usually the XML representation will be inside of a Message XML representation like + * in the following example: + * <pre> + * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"> + * <subject>Any subject you want</subject> + * <body>This message contains roster items.</body> + * <x xmlns="jabber:x:roster"> + * <item jid="gato1@gato.home"/> + * <item jid="gato2@gato.home"/> + * </x> + * </message> + * </pre> + * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\">"); + // Loop through all roster entries and append them to the string buffer + for (Iterator<RemoteRosterEntry> i = getRosterEntries(); i.hasNext();) { + RemoteRosterEntry remoteRosterEntry = i.next(); + buf.append(remoteRosterEntry.toXML()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java b/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java new file mode 100644 index 0000000..59bd98e --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/SharedGroupsInfo.java @@ -0,0 +1,92 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2005 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.packet;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * IQ packet used for discovering the user's shared groups and for getting the answer back
+ * from the server.<p>
+ *
+ * Important note: This functionality is not part of the XMPP spec and it will only work
+ * with Wildfire.
+ *
+ * @author Gaston Dombiak
+ */
+public class SharedGroupsInfo extends IQ {
+
+ private List<String> groups = new ArrayList<String>();
+
+ /**
+ * Returns a collection with the shared group names returned from the server.
+ *
+ * @return collection with the shared group names returned from the server.
+ */
+ public List<String> getGroups() {
+ return groups;
+ }
+
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<sharedgroup xmlns=\"http://www.jivesoftware.org/protocol/sharedgroup\">");
+ for (Iterator<String> it=groups.iterator(); it.hasNext();) {
+ buf.append("<group>").append(it.next()).append("</group>");
+ }
+ buf.append("</sharedgroup>");
+ return buf.toString();
+ }
+
+ /**
+ * Internal Search service Provider.
+ */
+ public static class Provider implements IQProvider {
+
+ /**
+ * Provider Constructor.
+ */
+ public Provider() {
+ super();
+ }
+
+ public IQ parseIQ(XmlPullParser parser) throws Exception {
+ SharedGroupsInfo groupsInfo = new SharedGroupsInfo();
+
+ boolean done = false;
+ while (!done) {
+ int eventType = parser.next();
+ if (eventType == XmlPullParser.START_TAG && parser.getName().equals("group")) {
+ groupsInfo.getGroups().add(parser.nextText());
+ }
+ else if (eventType == XmlPullParser.END_TAG) {
+ if (parser.getName().equals("sharedgroup")) {
+ done = true;
+ }
+ }
+ }
+ return groupsInfo;
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smackx/packet/StreamInitiation.java b/src/org/jivesoftware/smackx/packet/StreamInitiation.java new file mode 100644 index 0000000..511a02c --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/StreamInitiation.java @@ -0,0 +1,419 @@ +/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * Copyright 2003-2006 Jive Software.
+ *
+ * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.packet;
+
+import java.util.Date;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.util.StringUtils;
+
+/**
+ * The process by which two entities initiate a stream.
+ *
+ * @author Alexander Wenckus
+ */
+public class StreamInitiation extends IQ {
+
+ private String id;
+
+ private String mimeType;
+
+ private File file;
+
+ private Feature featureNegotiation;
+
+ /**
+ * The "id" attribute is an opaque identifier. This attribute MUST be
+ * present on type='set', and MUST be a valid string. This SHOULD NOT be
+ * sent back on type='result', since the <iq/> "id" attribute provides the
+ * only context needed. This value is generated by the Sender, and the same
+ * value MUST be used throughout a session when talking to the Receiver.
+ *
+ * @param id The "id" attribute.
+ */
+ public void setSesssionID(final String id) {
+ this.id = id;
+ }
+
+ /**
+ * Uniquely identifies a stream initiation to the recipient.
+ *
+ * @return The "id" attribute.
+ * @see #setSesssionID(String)
+ */
+ public String getSessionID() {
+ return id;
+ }
+
+ /**
+ * The "mime-type" attribute identifies the MIME-type for the data across
+ * the stream. This attribute MUST be a valid MIME-type as registered with
+ * the Internet Assigned Numbers Authority (IANA) [3] (specifically, as
+ * listed at <http://www.iana.org/assignments/media-types>). During
+ * negotiation, this attribute SHOULD be present, and is otherwise not
+ * required. If not included during negotiation, its value is assumed to be
+ * "binary/octect-stream".
+ *
+ * @param mimeType The valid mime-type.
+ */
+ public void setMimeType(final String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ /**
+ * Identifies the type of file that is desired to be transfered.
+ *
+ * @return The mime-type.
+ * @see #setMimeType(String)
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * Sets the file which contains the information pertaining to the file to be
+ * transfered.
+ *
+ * @param file The file identified by the stream initiator to be sent.
+ */
+ public void setFile(final File file) {
+ this.file = file;
+ }
+
+ /**
+ * Returns the file containing the information about the request.
+ *
+ * @return Returns the file containing the information about the request.
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * Sets the data form which contains the valid methods of stream neotiation
+ * and transfer.
+ *
+ * @param form The dataform containing the methods.
+ */
+ public void setFeatureNegotiationForm(final DataForm form) {
+ this.featureNegotiation = new Feature(form);
+ }
+
+ /**
+ * Returns the data form which contains the valid methods of stream
+ * neotiation and transfer.
+ *
+ * @return Returns the data form which contains the valid methods of stream
+ * neotiation and transfer.
+ */
+ public DataForm getFeatureNegotiationForm() {
+ return featureNegotiation.getData();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.jivesoftware.smack.packet.IQ#getChildElementXML()
+ */
+ public String getChildElementXML() {
+ StringBuilder buf = new StringBuilder();
+ if (this.getType().equals(IQ.Type.SET)) {
+ buf.append("<si xmlns=\"http://jabber.org/protocol/si\" ");
+ if (getSessionID() != null) {
+ buf.append("id=\"").append(getSessionID()).append("\" ");
+ }
+ if (getMimeType() != null) {
+ buf.append("mime-type=\"").append(getMimeType()).append("\" ");
+ }
+ buf
+ .append("profile=\"http://jabber.org/protocol/si/profile/file-transfer\">");
+
+ // Add the file section if there is one.
+ String fileXML = file.toXML();
+ if (fileXML != null) {
+ buf.append(fileXML);
+ }
+ }
+ else if (this.getType().equals(IQ.Type.RESULT)) {
+ buf.append("<si xmlns=\"http://jabber.org/protocol/si\">");
+ }
+ else {
+ throw new IllegalArgumentException("IQ Type not understood");
+ }
+ if (featureNegotiation != null) {
+ buf.append(featureNegotiation.toXML());
+ }
+ buf.append("</si>");
+ return buf.toString();
+ }
+
+ /**
+ * <ul>
+ * <li>size: The size, in bytes, of the data to be sent.</li>
+ * <li>name: The name of the file that the Sender wishes to send.</li>
+ * <li>date: The last modification time of the file. This is specified
+ * using the DateTime profile as described in Jabber Date and Time Profiles.</li>
+ * <li>hash: The MD5 sum of the file contents.</li>
+ * </ul>
+ * <p/>
+ * <p/>
+ * <desc> is used to provide a sender-generated description of the
+ * file so the receiver can better understand what is being sent. It MUST
+ * NOT be sent in the result.
+ * <p/>
+ * <p/>
+ * When <range> is sent in the offer, it should have no attributes.
+ * This signifies that the sender can do ranged transfers. When a Stream
+ * Initiation result is sent with the <range> element, it uses these
+ * attributes:
+ * <p/>
+ * <ul>
+ * <li>offset: Specifies the position, in bytes, to start transferring the
+ * file data from. This defaults to zero (0) if not specified.</li>
+ * <li>length - Specifies the number of bytes to retrieve starting at
+ * offset. This defaults to the length of the file from offset to the end.</li>
+ * </ul>
+ * <p/>
+ * <p/>
+ * Both attributes are OPTIONAL on the <range> element. Sending no
+ * attributes is synonymous with not sending the <range> element. When
+ * no <range> element is sent in the Stream Initiation result, the
+ * Sender MUST send the complete file starting at offset 0. More generally,
+ * data is sent over the stream byte for byte starting at the offset
+ * position for the length specified.
+ *
+ * @author Alexander Wenckus
+ */
+ public static class File implements PacketExtension {
+
+ private final String name;
+
+ private final long size;
+
+ private String hash;
+
+ private Date date;
+
+ private String desc;
+
+ private boolean isRanged;
+
+ /**
+ * Constructor providing the name of the file and its size.
+ *
+ * @param name The name of the file.
+ * @param size The size of the file in bytes.
+ */
+ public File(final String name, final long size) {
+ if (name == null) {
+ throw new NullPointerException("name cannot be null");
+ }
+
+ this.name = name;
+ this.size = size;
+ }
+
+ /**
+ * Returns the file's name.
+ *
+ * @return Returns the file's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the file's size.
+ *
+ * @return Returns the file's size.
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Sets the MD5 sum of the file's contents
+ *
+ * @param hash The MD5 sum of the file's contents.
+ */
+ public void setHash(final String hash) {
+ this.hash = hash;
+ }
+
+ /**
+ * Returns the MD5 sum of the file's contents
+ *
+ * @return Returns the MD5 sum of the file's contents
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * Sets the date that the file was last modified.
+ *
+ * @param date The date that the file was last modified.
+ */
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ /**
+ * Returns the date that the file was last modified.
+ *
+ * @return Returns the date that the file was last modified.
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ /**
+ * Sets the description of the file.
+ *
+ * @param desc The description of the file so that the file reciever can
+ * know what file it is.
+ */
+ public void setDesc(final String desc) {
+ this.desc = desc;
+ }
+
+ /**
+ * Returns the description of the file.
+ *
+ * @return Returns the description of the file.
+ */
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * True if a range can be provided and false if it cannot.
+ *
+ * @param isRanged True if a range can be provided and false if it cannot.
+ */
+ public void setRanged(final boolean isRanged) {
+ this.isRanged = isRanged;
+ }
+
+ /**
+ * Returns whether or not the initiator can support a range for the file
+ * tranfer.
+ *
+ * @return Returns whether or not the initiator can support a range for
+ * the file tranfer.
+ */
+ public boolean isRanged() {
+ return isRanged;
+ }
+
+ public String getElementName() {
+ return "file";
+ }
+
+ public String getNamespace() {
+ return "http://jabber.org/protocol/si/profile/file-transfer";
+ }
+
+ public String toXML() {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append("<").append(getElementName()).append(" xmlns=\"")
+ .append(getNamespace()).append("\" ");
+
+ if (getName() != null) {
+ buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
+ }
+
+ if (getSize() > 0) {
+ buffer.append("size=\"").append(getSize()).append("\" ");
+ }
+
+ if (getDate() != null) {
+ buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
+ } + + if (getHash() != null) { + buffer.append("hash=\"").append(getHash()).append("\" "); + } + + if ((desc != null && desc.length() > 0) || isRanged) { + buffer.append(">"); + if (getDesc() != null && desc.length() > 0) { + buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>"); + } + if (isRanged()) { + buffer.append("<range/>"); + } + buffer.append("</").append(getElementName()).append(">"); + } + else { + buffer.append("/>"); + } + return buffer.toString(); + } + } + + /** + * The feature negotiation portion of the StreamInitiation packet. + * + * @author Alexander Wenckus + * + */ + public class Feature implements PacketExtension { + + private final DataForm data; + + /** + * The dataform can be provided as part of the constructor. + * + * @param data The dataform. + */ + public Feature(final DataForm data) { + this.data = data; + } + + /** + * Returns the dataform associated with the feature negotiation. + * + * @return Returns the dataform associated with the feature negotiation. + */ + public DataForm getData() { + return data; + } + + public String getNamespace() { + return "http://jabber.org/protocol/feature-neg"; + } + + public String getElementName() { + return "feature"; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf + .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">"); + buf.append(data.toXML()); + buf.append("</feature>"); + return buf.toString(); + } + } +} diff --git a/src/org/jivesoftware/smackx/packet/Time.java b/src/org/jivesoftware/smackx/packet/Time.java new file mode 100644 index 0000000..5294e77 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/Time.java @@ -0,0 +1,198 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * A Time IQ packet, which is used by XMPP clients to exchange their respective local + * times. Clients that wish to fully support the entitity time protocol should register + * a PacketListener for incoming time requests that then respond with the local time. + * This class can be used to request the time from other clients, such as in the + * following code snippet: + * + * <pre> + * // Request the time from a remote user. + * Time timeRequest = new Time(); + * timeRequest.setType(IQ.Type.GET); + * timeRequest.setTo(someUser@example.com/resource); + * + * // Create a packet collector to listen for a response. + * PacketCollector collector = con.createPacketCollector( + * new PacketIDFilter(timeRequest.getPacketID())); + * + * con.sendPacket(timeRequest); + * + * // Wait up to 5 seconds for a result. + * IQ result = (IQ)collector.nextResult(5000); + * if (result != null && result.getType() == IQ.Type.RESULT) { + * Time timeResult = (Time)result; + * // Do something with result... + * }</pre><p> + * + * Warning: this is an non-standard protocol documented by + * <a href="http://www.xmpp.org/extensions/xep-0090.html">XEP-0090</a>. Because this is a + * non-standard protocol, it is subject to change. + * + * @author Matt Tucker + */ +public class Time extends IQ { + + private static SimpleDateFormat utcFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss"); + private static DateFormat displayFormat = DateFormat.getDateTimeInstance(); + + private String utc = null; + private String tz = null; + private String display = null; + + /** + * Creates a new Time instance with empty values for all fields. + */ + public Time() { + + } + + /** + * Creates a new Time instance using the specified calendar instance as + * the time value to send. + * + * @param cal the time value. + */ + public Time(Calendar cal) { + TimeZone timeZone = cal.getTimeZone(); + tz = cal.getTimeZone().getID(); + display = displayFormat.format(cal.getTime()); + // Convert local time to the UTC time. + utc = utcFormat.format(new Date( + cal.getTimeInMillis() - timeZone.getOffset(cal.getTimeInMillis()))); + } + + /** + * Returns the local time or <tt>null</tt> if the time hasn't been set. + * + * @return the lcocal time. + */ + public Date getTime() { + if (utc == null) { + return null; + } + Date date = null; + try { + Calendar cal = Calendar.getInstance(); + // Convert the UTC time to local time. + cal.setTime(new Date(utcFormat.parse(utc).getTime() + + cal.getTimeZone().getOffset(cal.getTimeInMillis()))); + date = cal.getTime(); + } + catch (Exception e) { + e.printStackTrace(); + } + return date; + } + + /** + * Sets the time using the local time. + * + * @param time the current local time. + */ + public void setTime(Date time) { + // Convert local time to UTC time. + utc = utcFormat.format(new Date( + time.getTime() - TimeZone.getDefault().getOffset(time.getTime()))); + } + + /** + * Returns the time as a UTC formatted String using the format CCYYMMDDThh:mm:ss. + * + * @return the time as a UTC formatted String. + */ + public String getUtc() { + return utc; + } + + /** + * Sets the time using UTC formatted String in the format CCYYMMDDThh:mm:ss. + * + * @param utc the time using a formatted String. + */ + public void setUtc(String utc) { + this.utc = utc; + + } + + /** + * Returns the time zone. + * + * @return the time zone. + */ + public String getTz() { + return tz; + } + + /** + * Sets the time zone. + * + * @param tz the time zone. + */ + public void setTz(String tz) { + this.tz = tz; + } + + /** + * Returns the local (non-utc) time in human-friendly format. + * + * @return the local time in human-friendly format. + */ + public String getDisplay() { + return display; + } + + /** + * Sets the local time in human-friendly format. + * + * @param display the local time in human-friendly format. + */ + public void setDisplay(String display) { + this.display = display; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"jabber:iq:time\">"); + if (utc != null) { + buf.append("<utc>").append(utc).append("</utc>"); + } + if (tz != null) { + buf.append("<tz>").append(tz).append("</tz>"); + } + if (display != null) { + buf.append("<display>").append(display).append("</display>"); + } + buf.append("</query>"); + return buf.toString(); + } +}
\ No newline at end of file diff --git a/src/org/jivesoftware/smackx/packet/VCard.java b/src/org/jivesoftware/smackx/packet/VCard.java new file mode 100644 index 0000000..9766db8 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/VCard.java @@ -0,0 +1,883 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.PacketCollector; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smack.util.StringUtils; + +/** + * A VCard class for use with the + * <a href="http://www.jivesoftware.org/smack/" target="_blank">SMACK jabber library</a>.<p> + * <p/> + * You should refer to the + * <a href="http://www.jabber.org/jeps/jep-0054.html" target="_blank">JEP-54 documentation</a>.<p> + * <p/> + * Please note that this class is incomplete but it does provide the most commonly found + * information in vCards. Also remember that VCard transfer is not a standard, and the protocol + * may change or be replaced.<p> + * <p/> + * <b>Usage:</b> + * <pre> + * <p/> + * // To save VCard: + * <p/> + * VCard vCard = new VCard(); + * vCard.setFirstName("kir"); + * vCard.setLastName("max"); + * vCard.setEmailHome("foo@fee.bar"); + * vCard.setJabberId("jabber@id.org"); + * vCard.setOrganization("Jetbrains, s.r.o"); + * vCard.setNickName("KIR"); + * <p/> + * vCard.setField("TITLE", "Mr"); + * vCard.setAddressFieldHome("STREET", "Some street"); + * vCard.setAddressFieldWork("CTRY", "US"); + * vCard.setPhoneWork("FAX", "3443233"); + * <p/> + * vCard.save(connection); + * <p/> + * // To load VCard: + * <p/> + * VCard vCard = new VCard(); + * vCard.load(conn); // load own VCard + * vCard.load(conn, "joe@foo.bar"); // load someone's VCard + * </pre> + * + * @author Kirill Maximov (kir@maxkir.com) + */ +public class VCard extends IQ { + + /** + * Phone types: + * VOICE?, FAX?, PAGER?, MSG?, CELL?, VIDEO?, BBS?, MODEM?, ISDN?, PCS?, PREF? + */ + private Map<String, String> homePhones = new HashMap<String, String>(); + private Map<String, String> workPhones = new HashMap<String, String>(); + + + /** + * Address types: + * POSTAL?, PARCEL?, (DOM | INTL)?, PREF?, POBOX?, EXTADR?, STREET?, LOCALITY?, + * REGION?, PCODE?, CTRY? + */ + private Map<String, String> homeAddr = new HashMap<String, String>(); + private Map<String, String> workAddr = new HashMap<String, String>(); + + private String firstName; + private String lastName; + private String middleName; + + private String emailHome; + private String emailWork; + + private String organization; + private String organizationUnit; + + private String photoMimeType; + private String photoBinval; + + /** + * Such as DESC ROLE GEO etc.. see JEP-0054 + */ + private Map<String, String> otherSimpleFields = new HashMap<String, String>(); + + // fields that, as they are should not be escaped before forwarding to the server + private Map<String, String> otherUnescapableFields = new HashMap<String, String>(); + + public VCard() { + } + + /** + * Set generic VCard field. + * + * @param field value of field. Possible values: NICKNAME, PHOTO, BDAY, JABBERID, MAILER, TZ, + * GEO, TITLE, ROLE, LOGO, NOTE, PRODID, REV, SORT-STRING, SOUND, UID, URL, DESC. + */ + public String getField(String field) { + return otherSimpleFields.get(field); + } + + /** + * Set generic VCard field. + * + * @param value value of field + * @param field field to set. See {@link #getField(String)} + * @see #getField(String) + */ + public void setField(String field, String value) { + setField(field, value, false); + } + + /** + * Set generic, unescapable VCard field. If unescabale is set to true, XML maybe a part of the + * value. + * + * @param value value of field + * @param field field to set. See {@link #getField(String)} + * @param isUnescapable True if the value should not be escaped, and false if it should. + */ + public void setField(String field, String value, boolean isUnescapable) { + if (!isUnescapable) { + otherSimpleFields.put(field, value); + } + else { + otherUnescapableFields.put(field, value); + } + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + // Update FN field + updateFN(); + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + // Update FN field + updateFN(); + } + + public String getMiddleName() { + return middleName; + } + + public void setMiddleName(String middleName) { + this.middleName = middleName; + // Update FN field + updateFN(); + } + + public String getNickName() { + return otherSimpleFields.get("NICKNAME"); + } + + public void setNickName(String nickName) { + otherSimpleFields.put("NICKNAME", nickName); + } + + public String getEmailHome() { + return emailHome; + } + + public void setEmailHome(String email) { + this.emailHome = email; + } + + public String getEmailWork() { + return emailWork; + } + + public void setEmailWork(String emailWork) { + this.emailWork = emailWork; + } + + public String getJabberId() { + return otherSimpleFields.get("JABBERID"); + } + + public void setJabberId(String jabberId) { + otherSimpleFields.put("JABBERID", jabberId); + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public String getOrganizationUnit() { + return organizationUnit; + } + + public void setOrganizationUnit(String organizationUnit) { + this.organizationUnit = organizationUnit; + } + + /** + * Get home address field + * + * @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, + * LOCALITY, REGION, PCODE, CTRY + */ + public String getAddressFieldHome(String addrField) { + return homeAddr.get(addrField); + } + + /** + * Set home address field + * + * @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, + * LOCALITY, REGION, PCODE, CTRY + */ + public void setAddressFieldHome(String addrField, String value) { + homeAddr.put(addrField, value); + } + + /** + * Get work address field + * + * @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, + * LOCALITY, REGION, PCODE, CTRY + */ + public String getAddressFieldWork(String addrField) { + return workAddr.get(addrField); + } + + /** + * Set work address field + * + * @param addrField one of POSTAL, PARCEL, (DOM | INTL), PREF, POBOX, EXTADR, STREET, + * LOCALITY, REGION, PCODE, CTRY + */ + public void setAddressFieldWork(String addrField, String value) { + workAddr.put(addrField, value); + } + + + /** + * Set home phone number + * + * @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF + * @param phoneNum phone number + */ + public void setPhoneHome(String phoneType, String phoneNum) { + homePhones.put(phoneType, phoneNum); + } + + /** + * Get home phone number + * + * @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF + */ + public String getPhoneHome(String phoneType) { + return homePhones.get(phoneType); + } + + /** + * Set work phone number + * + * @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF + * @param phoneNum phone number + */ + public void setPhoneWork(String phoneType, String phoneNum) { + workPhones.put(phoneType, phoneNum); + } + + /** + * Get work phone number + * + * @param phoneType one of VOICE, FAX, PAGER, MSG, CELL, VIDEO, BBS, MODEM, ISDN, PCS, PREF + */ + public String getPhoneWork(String phoneType) { + return workPhones.get(phoneType); + } + + /** + * Set the avatar for the VCard by specifying the url to the image. + * + * @param avatarURL the url to the image(png,jpeg,gif,bmp) + */ + public void setAvatar(URL avatarURL) { + byte[] bytes = new byte[0]; + try { + bytes = getBytes(avatarURL); + } + catch (IOException e) { + e.printStackTrace(); + } + + setAvatar(bytes); + } + + /** + * Removes the avatar from the vCard + * + * This is done by setting the PHOTO value to the empty string as defined in XEP-0153 + */ + public void removeAvatar() { + // Remove avatar (if any) + photoBinval = null; + photoMimeType = null; + } + + /** + * Specify the bytes of the JPEG for the avatar to use. + * If bytes is null, then the avatar will be removed. + * 'image/jpeg' will be used as MIME type. + * + * @param bytes the bytes of the avatar, or null to remove the avatar data + */ + public void setAvatar(byte[] bytes) { + setAvatar(bytes, "image/jpeg"); + } + + /** + * Specify the bytes for the avatar to use as well as the mime type. + * + * @param bytes the bytes of the avatar. + * @param mimeType the mime type of the avatar. + */ + public void setAvatar(byte[] bytes, String mimeType) { + // If bytes is null, remove the avatar + if (bytes == null) { + removeAvatar(); + return; + } + + // Otherwise, add to mappings. + String encodedImage = StringUtils.encodeBase64(bytes); + + setAvatar(encodedImage, mimeType); + } + + /** + * Specify the Avatar used for this vCard. + * + * @param encodedImage the Base64 encoded image as String + * @param mimeType the MIME type of the image + */ + public void setAvatar(String encodedImage, String mimeType) { + photoBinval = encodedImage; + photoMimeType = mimeType; + } + + /** + * Return the byte representation of the avatar(if one exists), otherwise returns null if + * no avatar could be found. + * <b>Example 1</b> + * <pre> + * // Load Avatar from VCard + * byte[] avatarBytes = vCard.getAvatar(); + * <p/> + * // To create an ImageIcon for Swing applications + * ImageIcon icon = new ImageIcon(avatar); + * <p/> + * // To create just an image object from the bytes + * ByteArrayInputStream bais = new ByteArrayInputStream(avatar); + * try { + * Image image = ImageIO.read(bais); + * } + * catch (IOException e) { + * e.printStackTrace(); + * } + * </pre> + * + * @return byte representation of avatar. + */ + public byte[] getAvatar() { + if (photoBinval == null) { + return null; + } + return StringUtils.decodeBase64(photoBinval); + } + + /** + * Returns the MIME Type of the avatar or null if none is set + * + * @return the MIME Type of the avatar or null + */ + public String getAvatarMimeType() { + return photoMimeType; + } + + /** + * Common code for getting the bytes of a url. + * + * @param url the url to read. + */ + public static byte[] getBytes(URL url) throws IOException { + final String path = url.getPath(); + final File file = new File(path); + if (file.exists()) { + return getFileBytes(file); + } + + return null; + } + + private static byte[] getFileBytes(File file) throws IOException { + BufferedInputStream bis = null; + try { + bis = new BufferedInputStream(new FileInputStream(file)); + int bytes = (int) file.length(); + byte[] buffer = new byte[bytes]; + int readBytes = bis.read(buffer); + if (readBytes != buffer.length) { + throw new IOException("Entire file not read"); + } + return buffer; + } + finally { + if (bis != null) { + bis.close(); + } + } + } + + /** + * Returns the SHA-1 Hash of the Avatar image. + * + * @return the SHA-1 Hash of the Avatar image. + */ + public String getAvatarHash() { + byte[] bytes = getAvatar(); + if (bytes == null) { + return null; + } + + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } + catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + + digest.update(bytes); + return StringUtils.encodeHex(digest.digest()); + } + + private void updateFN() { + StringBuilder sb = new StringBuilder(); + if (firstName != null) { + sb.append(StringUtils.escapeForXML(firstName)).append(' '); + } + if (middleName != null) { + sb.append(StringUtils.escapeForXML(middleName)).append(' '); + } + if (lastName != null) { + sb.append(StringUtils.escapeForXML(lastName)); + } + setField("FN", sb.toString()); + } + + /** + * Save this vCard for the user connected by 'connection'. Connection should be authenticated + * and not anonymous.<p> + * <p/> + * NOTE: the method is asynchronous and does not wait for the returned value. + * + * @param connection the Connection to use. + * @throws XMPPException thrown if there was an issue setting the VCard in the server. + */ + public void save(Connection connection) throws XMPPException { + checkAuthenticated(connection, true); + + setType(IQ.Type.SET); + setFrom(connection.getUser()); + PacketCollector collector = connection.createPacketCollector(new PacketIDFilter(getPacketID())); + connection.sendPacket(this); + + Packet response = collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + + // Cancel the collector. + collector.cancel(); + if (response == null) { + throw new XMPPException("No response from server on status set."); + } + if (response.getError() != null) { + throw new XMPPException(response.getError()); + } + } + + /** + * Load VCard information for a connected user. Connection should be authenticated + * and not anonymous. + */ + public void load(Connection connection) throws XMPPException { + checkAuthenticated(connection, true); + + setFrom(connection.getUser()); + doLoad(connection, connection.getUser()); + } + + /** + * Load VCard information for a given user. Connection should be authenticated and not anonymous. + */ + public void load(Connection connection, String user) throws XMPPException { + checkAuthenticated(connection, false); + + setTo(user); + doLoad(connection, user); + } + + private void doLoad(Connection connection, String user) throws XMPPException { + setType(Type.GET); + PacketCollector collector = connection.createPacketCollector( + new PacketIDFilter(getPacketID())); + connection.sendPacket(this); + + VCard result = null; + try { + result = (VCard) collector.nextResult(SmackConfiguration.getPacketReplyTimeout()); + + if (result == null) { + String errorMessage = "Timeout getting VCard information"; + throw new XMPPException(errorMessage, new XMPPError( + XMPPError.Condition.request_timeout, errorMessage)); + } + if (result.getError() != null) { + throw new XMPPException(result.getError()); + } + } + catch (ClassCastException e) { + System.out.println("No VCard for " + user); + } + + copyFieldsFrom(result); + } + + public String getChildElementXML() { + StringBuilder sb = new StringBuilder(); + new VCardWriter(sb).write(); + return sb.toString(); + } + + private void copyFieldsFrom(VCard from) { + Field[] fields = VCard.class.getDeclaredFields(); + for (Field field : fields) { + if (field.getDeclaringClass() == VCard.class && + !Modifier.isFinal(field.getModifiers())) { + try { + field.setAccessible(true); + field.set(this, field.get(from)); + } + catch (IllegalAccessException e) { + throw new RuntimeException("This cannot happen:" + field, e); + } + } + } + } + + private void checkAuthenticated(Connection connection, boolean checkForAnonymous) { + if (connection == null) { + throw new IllegalArgumentException("No connection was provided"); + } + if (!connection.isAuthenticated()) { + throw new IllegalArgumentException("Connection is not authenticated"); + } + if (checkForAnonymous && connection.isAnonymous()) { + throw new IllegalArgumentException("Connection cannot be anonymous"); + } + } + + private boolean hasContent() { + //noinspection OverlyComplexBooleanExpression + return hasNameField() + || hasOrganizationFields() + || emailHome != null + || emailWork != null + || otherSimpleFields.size() > 0 + || otherUnescapableFields.size() > 0 + || homeAddr.size() > 0 + || homePhones.size() > 0 + || workAddr.size() > 0 + || workPhones.size() > 0 + || photoBinval != null + ; + } + + private boolean hasNameField() { + return firstName != null || lastName != null || middleName != null; + } + + private boolean hasOrganizationFields() { + return organization != null || organizationUnit != null; + } + + // Used in tests: + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final VCard vCard = (VCard) o; + + if (emailHome != null ? !emailHome.equals(vCard.emailHome) : vCard.emailHome != null) { + return false; + } + if (emailWork != null ? !emailWork.equals(vCard.emailWork) : vCard.emailWork != null) { + return false; + } + if (firstName != null ? !firstName.equals(vCard.firstName) : vCard.firstName != null) { + return false; + } + if (!homeAddr.equals(vCard.homeAddr)) { + return false; + } + if (!homePhones.equals(vCard.homePhones)) { + return false; + } + if (lastName != null ? !lastName.equals(vCard.lastName) : vCard.lastName != null) { + return false; + } + if (middleName != null ? !middleName.equals(vCard.middleName) : vCard.middleName != null) { + return false; + } + if (organization != null ? + !organization.equals(vCard.organization) : vCard.organization != null) { + return false; + } + if (organizationUnit != null ? + !organizationUnit.equals(vCard.organizationUnit) : vCard.organizationUnit != null) { + return false; + } + if (!otherSimpleFields.equals(vCard.otherSimpleFields)) { + return false; + } + if (!workAddr.equals(vCard.workAddr)) { + return false; + } + if (photoBinval != null ? !photoBinval.equals(vCard.photoBinval) : vCard.photoBinval != null) { + return false; + } + + return workPhones.equals(vCard.workPhones); + } + + public int hashCode() { + int result; + result = homePhones.hashCode(); + result = 29 * result + workPhones.hashCode(); + result = 29 * result + homeAddr.hashCode(); + result = 29 * result + workAddr.hashCode(); + result = 29 * result + (firstName != null ? firstName.hashCode() : 0); + result = 29 * result + (lastName != null ? lastName.hashCode() : 0); + result = 29 * result + (middleName != null ? middleName.hashCode() : 0); + result = 29 * result + (emailHome != null ? emailHome.hashCode() : 0); + result = 29 * result + (emailWork != null ? emailWork.hashCode() : 0); + result = 29 * result + (organization != null ? organization.hashCode() : 0); + result = 29 * result + (organizationUnit != null ? organizationUnit.hashCode() : 0); + result = 29 * result + otherSimpleFields.hashCode(); + result = 29 * result + (photoBinval != null ? photoBinval.hashCode() : 0); + return result; + } + + public String toString() { + return getChildElementXML(); + } + + //============================================================== + + private class VCardWriter { + + private final StringBuilder sb; + + VCardWriter(StringBuilder sb) { + this.sb = sb; + } + + public void write() { + appendTag("vCard", "xmlns", "vcard-temp", hasContent(), new ContentBuilder() { + public void addTagContent() { + buildActualContent(); + } + }); + } + + private void buildActualContent() { + if (hasNameField()) { + appendN(); + } + + appendOrganization(); + appendGenericFields(); + appendPhoto(); + + appendEmail(emailWork, "WORK"); + appendEmail(emailHome, "HOME"); + + appendPhones(workPhones, "WORK"); + appendPhones(homePhones, "HOME"); + + appendAddress(workAddr, "WORK"); + appendAddress(homeAddr, "HOME"); + } + + private void appendPhoto() { + if (photoBinval == null) + return; + + appendTag("PHOTO", true, new ContentBuilder() { + public void addTagContent() { + appendTag("BINVAL", photoBinval); // No need to escape photoBinval, as it's already Base64 encoded + appendTag("TYPE", StringUtils.escapeForXML(photoMimeType)); + } + }); + } + private void appendEmail(final String email, final String type) { + if (email != null) { + appendTag("EMAIL", true, new ContentBuilder() { + public void addTagContent() { + appendEmptyTag(type); + appendEmptyTag("INTERNET"); + appendEmptyTag("PREF"); + appendTag("USERID", StringUtils.escapeForXML(email)); + } + }); + } + } + + private void appendPhones(Map<String, String> phones, final String code) { + Iterator<Map.Entry<String, String>> it = phones.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry<String,String> entry = it.next(); + appendTag("TEL", true, new ContentBuilder() { + public void addTagContent() { + appendEmptyTag(entry.getKey()); + appendEmptyTag(code); + appendTag("NUMBER", StringUtils.escapeForXML(entry.getValue())); + } + }); + } + } + + private void appendAddress(final Map<String, String> addr, final String code) { + if (addr.size() > 0) { + appendTag("ADR", true, new ContentBuilder() { + public void addTagContent() { + appendEmptyTag(code); + + Iterator<Map.Entry<String, String>> it = addr.entrySet().iterator(); + while (it.hasNext()) { + final Entry<String, String> entry = it.next(); + appendTag(entry.getKey(), StringUtils.escapeForXML(entry.getValue())); + } + } + }); + } + } + + private void appendEmptyTag(Object tag) { + sb.append('<').append(tag).append("/>"); + } + + private void appendGenericFields() { + Iterator<Map.Entry<String, String>> it = otherSimpleFields.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<String, String> entry = it.next(); + appendTag(entry.getKey().toString(), + StringUtils.escapeForXML(entry.getValue())); + } + + it = otherUnescapableFields.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<String, String> entry = it.next(); + appendTag(entry.getKey().toString(),entry.getValue()); + } + } + + private void appendOrganization() { + if (hasOrganizationFields()) { + appendTag("ORG", true, new ContentBuilder() { + public void addTagContent() { + appendTag("ORGNAME", StringUtils.escapeForXML(organization)); + appendTag("ORGUNIT", StringUtils.escapeForXML(organizationUnit)); + } + }); + } + } + + private void appendN() { + appendTag("N", true, new ContentBuilder() { + public void addTagContent() { + appendTag("FAMILY", StringUtils.escapeForXML(lastName)); + appendTag("GIVEN", StringUtils.escapeForXML(firstName)); + appendTag("MIDDLE", StringUtils.escapeForXML(middleName)); + } + }); + } + + private void appendTag(String tag, String attr, String attrValue, boolean hasContent, + ContentBuilder builder) { + sb.append('<').append(tag); + if (attr != null) { + sb.append(' ').append(attr).append('=').append('\'').append(attrValue).append('\''); + } + + if (hasContent) { + sb.append('>'); + builder.addTagContent(); + sb.append("</").append(tag).append(">\n"); + } + else { + sb.append("/>\n"); + } + } + + private void appendTag(String tag, boolean hasContent, ContentBuilder builder) { + appendTag(tag, null, null, hasContent, builder); + } + + private void appendTag(String tag, final String tagText) { + if (tagText == null) return; + final ContentBuilder contentBuilder = new ContentBuilder() { + public void addTagContent() { + sb.append(tagText.trim()); + } + }; + appendTag(tag, true, contentBuilder); + } + + } + + //============================================================== + + private interface ContentBuilder { + + void addTagContent(); + } + + //============================================================== +} + diff --git a/src/org/jivesoftware/smackx/packet/Version.java b/src/org/jivesoftware/smackx/packet/Version.java new file mode 100644 index 0000000..41ee419 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/Version.java @@ -0,0 +1,132 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; + +/** + * A Version IQ packet, which is used by XMPP clients to discover version information + * about the software running at another entity's JID.<p> + * + * An example to discover the version of the server: + * <pre> + * // Request the version from the server. + * Version versionRequest = new Version(); + * timeRequest.setType(IQ.Type.GET); + * timeRequest.setTo("example.com"); + * + * // Create a packet collector to listen for a response. + * PacketCollector collector = con.createPacketCollector( + * new PacketIDFilter(versionRequest.getPacketID())); + * + * con.sendPacket(versionRequest); + * + * // Wait up to 5 seconds for a result. + * IQ result = (IQ)collector.nextResult(5000); + * if (result != null && result.getType() == IQ.Type.RESULT) { + * Version versionResult = (Version)result; + * // Do something with result... + * }</pre><p> + * + * @author Gaston Dombiak + */ +public class Version extends IQ { + + private String name; + private String version; + private String os; + + /** + * Returns the natural-language name of the software. This property will always be + * present in a result. + * + * @return the natural-language name of the software. + */ + public String getName() { + return name; + } + + /** + * Sets the natural-language name of the software. This message should only be + * invoked when parsing the XML and setting the property to a Version instance. + * + * @param name the natural-language name of the software. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the specific version of the software. This property will always be + * present in a result. + * + * @return the specific version of the software. + */ + public String getVersion() { + return version; + } + + /** + * Sets the specific version of the software. This message should only be + * invoked when parsing the XML and setting the property to a Version instance. + * + * @param version the specific version of the software. + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * Returns the operating system of the queried entity. This property will always be + * present in a result. + * + * @return the operating system of the queried entity. + */ + public String getOs() { + return os; + } + + /** + * Sets the operating system of the queried entity. This message should only be + * invoked when parsing the XML and setting the property to a Version instance. + * + * @param os operating system of the queried entity. + */ + public void setOs(String os) { + this.os = os; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"jabber:iq:version\">"); + if (name != null) { + buf.append("<name>").append(name).append("</name>"); + } + if (version != null) { + buf.append("<version>").append(version).append("</version>"); + } + if (os != null) { + buf.append("<os>").append(os).append("</os>"); + } + buf.append("</query>"); + return buf.toString(); + } +} diff --git a/src/org/jivesoftware/smackx/packet/XHTMLExtension.java b/src/org/jivesoftware/smackx/packet/XHTMLExtension.java new file mode 100644 index 0000000..ba5e676 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/XHTMLExtension.java @@ -0,0 +1,126 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * An XHTML sub-packet, which is used by XMPP clients to exchange formatted text. The XHTML + * extension is only a subset of XHTML 1.0.<p> + * + * The following link summarizes the requirements of XHTML IM: + * <a href="http://www.jabber.org/jeps/jep-0071.html#sect-id2598018">Valid tags</a>.<p> + * + * Warning: this is an non-standard protocol documented by + * <a href="http://www.jabber.org/jeps/jep-0071.html">JEP-71</a>. Because this is a + * non-standard protocol, it is subject to change. + * + * @author Gaston Dombiak + */ +public class XHTMLExtension implements PacketExtension { + + private List<String> bodies = new ArrayList<String>(); + + /** + * Returns the XML element name of the extension sub-packet root element. + * Always returns "html" + * + * @return the XML element name of the packet extension. + */ + public String getElementName() { + return "html"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * According the specification the namespace is always "http://jabber.org/protocol/xhtml-im" + * + * @return the XML namespace of the packet extension. + */ + public String getNamespace() { + return "http://jabber.org/protocol/xhtml-im"; + } + + /** + * Returns the XML representation of a XHTML extension according the specification. + * + * Usually the XML representation will be inside of a Message XML representation like + * in the following example: + * <pre> + * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"> + * <subject>Any subject you want</subject> + * <body>This message contains something interesting.</body> + * <html xmlns="http://jabber.org/protocol/xhtml-im"> + * <body><p style='font-size:large'>This message contains something <em>interesting</em>.</p></body> + * </html> + * </message> + * </pre> + * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append( + "\">"); + // Loop through all the bodies and append them to the string buffer + for (Iterator<String> i = getBodies(); i.hasNext();) { + buf.append(i.next()); + } + buf.append("</").append(getElementName()).append(">"); + return buf.toString(); + } + + /** + * Returns an Iterator for the bodies in the packet. + * + * @return an Iterator for the bodies in the packet. + */ + public Iterator<String> getBodies() { + synchronized (bodies) { + return Collections.unmodifiableList(new ArrayList<String>(bodies)).iterator(); + } + } + + /** + * Adds a body to the packet. + * + * @param body the body to add. + */ + public void addBody(String body) { + synchronized (bodies) { + bodies.add(body); + } + } + + /** + * Returns a count of the bodies in the XHTML packet. + * + * @return the number of bodies in the XHTML packet. + */ + public int getBodiesCount() { + return bodies.size(); + } + +} diff --git a/src/org/jivesoftware/smackx/packet/package.html b/src/org/jivesoftware/smackx/packet/package.html new file mode 100644 index 0000000..490d1d7 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/package.html @@ -0,0 +1 @@ +<body>XML packets that are part of the XMPP extension protocols.</body>
\ No newline at end of file |