001: /**
002: * $RCSfile: BroadcastPlugin.java,v $
003: * $Revision: 3117 $
004: * $Date: 2005-11-25 22:57:29 -0300 (Fri, 25 Nov 2005) $
005: *
006: * Copyright (C) 2004 Jive Software. All rights reserved.
007: *
008: * This software is published under the terms of the GNU Public License (GPL),
009: * a copy of which is included in this distribution.
010: */package org.jivesoftware.openfire.plugin;
011:
012: import org.dom4j.Element;
013: import org.jivesoftware.openfire.SessionManager;
014: import org.jivesoftware.openfire.XMPPServer;
015: import org.jivesoftware.openfire.container.Plugin;
016: import org.jivesoftware.openfire.container.PluginManager;
017: import org.jivesoftware.openfire.group.Group;
018: import org.jivesoftware.openfire.group.GroupManager;
019: import org.jivesoftware.openfire.group.GroupNotFoundException;
020: import org.jivesoftware.util.JiveGlobals;
021: import org.jivesoftware.util.PropertyEventDispatcher;
022: import org.jivesoftware.util.PropertyEventListener;
023: import org.xmpp.component.Component;
024: import org.xmpp.component.ComponentException;
025: import org.xmpp.component.ComponentManager;
026: import org.xmpp.component.ComponentManagerFactory;
027: import org.xmpp.packet.*;
028:
029: import java.io.File;
030: import java.util.*;
031:
032: /**
033: * Broadcast service plugin. It accepts messages and broadcasts them out to
034: * groups of connected users. The address <tt>all@[serviceName].[server]</tt> is
035: * reserved for sending a broadcast message to all connected users. Otherwise,
036: * broadcast messages can be sent to groups of users by using addresses
037: * in the form <tt>[group]@[serviceName].[server]</tt>.
038: *
039: * @author Matt Tucker
040: */
041: public class BroadcastPlugin implements Plugin, Component,
042: PropertyEventListener {
043:
044: private String serviceName;
045: private SessionManager sessionManager;
046: private GroupManager groupManager;
047: private List<JID> allowedUsers;
048: private boolean groupMembersAllowed;
049: private boolean disableGroupPermissions;
050: private ComponentManager componentManager;
051: private PluginManager pluginManager;
052:
053: /**
054: * Constructs a new broadcast plugin.
055: */
056: public BroadcastPlugin() {
057: serviceName = JiveGlobals.getProperty(
058: "plugin.broadcast.serviceName", "broadcast");
059: disableGroupPermissions = JiveGlobals
060: .getBooleanProperty("plugin.broadcast.disableGroupPermissions");
061: groupMembersAllowed = JiveGlobals.getBooleanProperty(
062: "plugin.broadcast.groupMembersAllowed", true);
063: allowedUsers = stringToList(JiveGlobals.getProperty(
064: "plugin.broadcast.allowedUsers", ""));
065: }
066:
067: // Plugin Interface
068:
069: public void initializePlugin(PluginManager manager,
070: File pluginDirectory) {
071: pluginManager = manager;
072: sessionManager = SessionManager.getInstance();
073: groupManager = GroupManager.getInstance();
074:
075: // Register as a component.
076: componentManager = ComponentManagerFactory
077: .getComponentManager();
078: try {
079: componentManager.addComponent(serviceName, this );
080: } catch (Exception e) {
081: componentManager.getLog().error(e);
082: }
083: PropertyEventDispatcher.addListener(this );
084: }
085:
086: public void destroyPlugin() {
087: PropertyEventDispatcher.removeListener(this );
088: // Unregister component.
089: if (componentManager != null) {
090: try {
091: componentManager.removeComponent(serviceName);
092: } catch (Exception e) {
093: componentManager.getLog().error(e);
094: }
095: }
096: componentManager = null;
097: pluginManager = null;
098: sessionManager = null;
099: groupManager = null;
100: allowedUsers.clear();
101: }
102:
103: public void initialize(JID jid, ComponentManager componentManager) {
104: }
105:
106: public void start() {
107: }
108:
109: public void shutdown() {
110: }
111:
112: // Component Interface
113:
114: public String getName() {
115: // Get the name from the plugin.xml file.
116: return pluginManager.getName(this );
117: }
118:
119: public String getDescription() {
120: // Get the description from the plugin.xml file.
121: return pluginManager.getDescription(this );
122: }
123:
124: public void processPacket(Packet packet) {
125: boolean canProceed = false;
126: Group group = null;
127: String toNode = packet.getTo().getNode();
128: // Check if user is allowed to send packet to this service[+group]
129: boolean targetAll = "all".equals(toNode);
130: if (targetAll) {
131: // See if the user is allowed to send the packet.
132: JID address = new JID(packet.getFrom().toBareJID());
133: if (allowedUsers.isEmpty()
134: || allowedUsers.contains(address)) {
135: canProceed = true;
136: }
137: } else {
138: try {
139: if (toNode != null) {
140: group = groupManager.getGroup(toNode);
141: boolean isGroupUser = group
142: .isUser(packet.getFrom())
143: || group.isUser(new JID(packet.getFrom()
144: .toBareJID()));
145: if (disableGroupPermissions
146: || (groupMembersAllowed && isGroupUser)
147: || allowedUsers.contains(new JID(packet
148: .getFrom().toBareJID()))) {
149: canProceed = true;
150: }
151: }
152: } catch (GroupNotFoundException e) {
153: // Ignore.
154: }
155: }
156: if (packet instanceof Message) {
157: // Respond to incoming messages
158: Message message = (Message) packet;
159: processMessage(message, targetAll, group, canProceed);
160: } else if (packet instanceof Presence) {
161: // Respond to presence subscription request or presence probe
162: Presence presence = (Presence) packet;
163: processPresence(canProceed, presence);
164: } else if (packet instanceof IQ) {
165: // Handle disco packets
166: IQ iq = (IQ) packet;
167: // Ignore IQs of type ERROR or RESULT
168: if (IQ.Type.error == iq.getType()
169: || IQ.Type.result == iq.getType()) {
170: return;
171: }
172: processIQ(iq, targetAll, group, canProceed);
173: }
174: }
175:
176: private void processMessage(Message message, boolean targetAll,
177: Group group, boolean canProceed) {
178: // Check to see if trying to broadcast to all connected users.
179: if (targetAll) {
180: if (!canProceed) {
181: Message error = new Message();
182: if (message.getID() != null) {
183: error.setID(message.getID());
184: }
185: error.setError(PacketError.Condition.not_allowed);
186: error.setTo(message.getFrom());
187: error.setFrom(message.getTo());
188: error.setSubject("Error sending broadcast message");
189: error
190: .setBody("Not allowed to send a broadcast message to "
191: + message.getTo());
192: try {
193: componentManager.sendPacket(this , error);
194: } catch (Exception e) {
195: componentManager.getLog().error(e);
196: }
197: return;
198: }
199: sessionManager.broadcast(message);
200: }
201: // See if the name is a group.
202: else {
203: if (group == null) {
204: // The address is not recognized so send an error message back.
205: Message error = new Message();
206: if (message.getID() != null) {
207: error.setID(message.getID());
208: }
209: error.setTo(message.getFrom());
210: error.setFrom(message.getTo());
211: error.setError(PacketError.Condition.not_allowed);
212: error.setSubject("Error sending broadcast message");
213: error.setBody("Address not valid: " + message.getTo());
214: try {
215: componentManager.sendPacket(this , error);
216: } catch (Exception e) {
217: componentManager.getLog().error(e);
218: }
219: } else if (canProceed) {
220: // Broadcast message to group users. Users that are offline will get
221: // the message when they come back online
222: for (JID userJID : group.getMembers()) {
223: Message newMessage = message.createCopy();
224: newMessage.setTo(userJID);
225: try {
226: componentManager.sendPacket(this , newMessage);
227: } catch (Exception e) {
228: componentManager.getLog().error(e);
229: }
230: }
231: } else {
232: // Otherwise, the address is recognized so send an error message back.
233: Message error = new Message();
234: if (message.getID() != null) {
235: error.setID(message.getID());
236: }
237: error.setTo(message.getFrom());
238: error.setFrom(message.getTo());
239: error.setError(PacketError.Condition.not_allowed);
240: error.setSubject("Error sending broadcast message");
241: error
242: .setBody("Not allowed to send a broadcast message to "
243: + message.getTo());
244: try {
245: componentManager.sendPacket(this , error);
246: } catch (Exception e) {
247: componentManager.getLog().error(e);
248: }
249: }
250: }
251: }
252:
253: private void processPresence(boolean canProceed, Presence presence) {
254: try {
255: if (Presence.Type.subscribe == presence.getType()) {
256: // Accept all presence requests if user has permissions
257: // Reply that the subscription request was approved or rejected
258: Presence reply = new Presence();
259: reply.setTo(presence.getFrom());
260: reply.setFrom(presence.getTo());
261: reply.setType(canProceed ? Presence.Type.subscribed
262: : Presence.Type.unsubscribed);
263: componentManager.sendPacket(this , reply);
264: } else if (Presence.Type.unsubscribe == presence.getType()) {
265: // Send confirmation of unsubscription
266: Presence reply = new Presence();
267: reply.setTo(presence.getFrom());
268: reply.setFrom(presence.getTo());
269: reply.setType(Presence.Type.unsubscribed);
270: componentManager.sendPacket(this , reply);
271: if (!canProceed) {
272: // Send unavailable presence of the service
273: reply = new Presence();
274: reply.setTo(presence.getFrom());
275: reply.setFrom(presence.getTo());
276: reply.setType(Presence.Type.unavailable);
277: componentManager.sendPacket(this , reply);
278: }
279: } else if (Presence.Type.probe == presence.getType()) {
280: // Send that the service is available
281: Presence reply = new Presence();
282: reply.setTo(presence.getFrom());
283: reply.setFrom(presence.getTo());
284: if (!canProceed) {
285: // Send forbidden error since user is not allowed
286: reply.setError(PacketError.Condition.forbidden);
287: }
288: componentManager.sendPacket(this , reply);
289: }
290: } catch (ComponentException e) {
291: componentManager.getLog().error(e);
292: }
293: }
294:
295: private void processIQ(IQ iq, boolean targetAll, Group group,
296: boolean canProceed) {
297: IQ reply = IQ.createResultIQ(iq);
298: Element childElement = iq.getChildElement();
299: String namespace = childElement.getNamespaceURI();
300: Element childElementCopy = iq.getChildElement().createCopy();
301: reply.setChildElement(childElementCopy);
302: if ("http://jabber.org/protocol/disco#info".equals(namespace)) {
303: if (iq.getTo().getNode() == null) {
304: // Return service identity and features
305: Element identity = childElementCopy
306: .addElement("identity");
307: identity.addAttribute("category", "component");
308: identity.addAttribute("type", "generic");
309: identity.addAttribute("name", "Broadcast service");
310: childElementCopy.addElement("feature").addAttribute(
311: "var", "http://jabber.org/protocol/disco#info");
312: childElementCopy
313: .addElement("feature")
314: .addAttribute("var",
315: "http://jabber.org/protocol/disco#items");
316: } else {
317: if (targetAll) {
318: // Return identity and features of the "all" group
319: Element identity = childElementCopy
320: .addElement("identity");
321: identity.addAttribute("category", "component");
322: identity.addAttribute("type", "generic");
323: identity.addAttribute("name",
324: "Broadcast all connected users");
325: childElementCopy
326: .addElement("feature")
327: .addAttribute("var",
328: "http://jabber.org/protocol/disco#info");
329: } else if (group != null && canProceed) {
330: // Return identity and features of the "all" group
331: Element identity = childElementCopy
332: .addElement("identity");
333: identity.addAttribute("category", "component");
334: identity.addAttribute("type", "generic");
335: identity.addAttribute("name", "Broadcast "
336: + group.getName());
337: childElementCopy
338: .addElement("feature")
339: .addAttribute("var",
340: "http://jabber.org/protocol/disco#info");
341: } else {
342: // Group not found or not allowed to use that group so
343: // answer item_not_found error
344: reply
345: .setError(PacketError.Condition.item_not_found);
346: }
347: }
348: } else if ("http://jabber.org/protocol/disco#items"
349: .equals(namespace)) {
350: if (iq.getTo().getNode() == null) {
351: // Return the list of groups hosted by the service that can be used by the user
352: Collection<Group> groups;
353: JID address = new JID(iq.getFrom().toBareJID());
354: if (allowedUsers.contains(address)) {
355: groups = groupManager.getGroups();
356: } else {
357: groups = groupManager.getGroups(iq.getFrom());
358: }
359: for (Group userGroup : groups) {
360: try {
361: JID groupJID = new JID(userGroup.getName()
362: + "@" + serviceName + "."
363: + componentManager.getServerName());
364: childElementCopy.addElement("item")
365: .addAttribute("jid",
366: groupJID.toString());
367: } catch (Exception e) {
368: // Group name is not valid to be used as a JID
369: }
370: }
371: if (allowedUsers.isEmpty()
372: || allowedUsers.contains(address)) {
373: // Add the "all" group to the list
374: childElementCopy.addElement("item").addAttribute(
375: "jid",
376: "all@" + serviceName + "."
377: + componentManager.getServerName());
378: }
379: }
380: } else {
381: // Answer an error since the server can't handle the requested namespace
382: reply.setError(PacketError.Condition.service_unavailable);
383: }
384: try {
385: componentManager.sendPacket(this , reply);
386: } catch (Exception e) {
387: componentManager.getLog().error(e);
388: }
389: }
390:
391: // Other Methods
392:
393: /**
394: * Returns the service name of this component, which is "broadcast" by default.
395: *
396: * @return the service name of this component.
397: */
398: public String getServiceName() {
399: return serviceName;
400: }
401:
402: /**
403: * Sets the service name of this component, which is "broadcast" by default.
404: *
405: * @param serviceName the service name of this component.
406: */
407: public void setServiceName(String serviceName) {
408: JiveGlobals.setProperty("plugin.broadcast.serviceName",
409: serviceName);
410: }
411:
412: /**
413: * Returns a collection of the addresses of users allowed to send broadcast
414: * messages. If no users are defined, anyone can send broadcast messages to
415: * all users. Additional users may also be allowed to send broadcast messages
416: * to specific groups depending on the group settings.
417: *
418: * @return the users allowed to send broadcast messages.
419: */
420: public Collection<JID> getGlobalAllowedUsers() {
421: return allowedUsers;
422: }
423:
424: /**
425: * Sets the collection of addresses of users allowed to send broadcast
426: * messages. If the collection is empty, anyone can send broadcast messages.
427: * Additional users may also be allowed to send broadcast messages to
428: * specific groups depending on the group settings.
429: *
430: * @param allowedUsers collection of users allowed to send broadcast messages
431: * to all users.
432: */
433: public void setGlobalAllowedUsers(Collection<String> allowedUsers) {
434: StringBuilder buf = new StringBuilder();
435: for (String jid : allowedUsers) {
436: buf.append(jid).append(",");
437: }
438: JiveGlobals.setProperty("plugin.broadcast.allowedUsers", buf
439: .toString());
440: }
441:
442: /**
443: * Returns true if all permission checking on sending messages to groups is disabled
444: * (enabled by default). When disabled, any user in the system can send a message to
445: * a group.
446: *
447: * @return true if group permission checking is disabled.
448: */
449: public boolean isGroupPermissionsDisabled() {
450: return disableGroupPermissions;
451: }
452:
453: /**
454: * Enables or disables permission checking when sending messages to a group. When
455: * disabled, any user in the system can send a message to a group.
456: *
457: * @param disableGroupPermissions true if group permission checking should be disabled.
458: */
459: public void setGroupPermissionsDisabled(
460: boolean disableGroupPermissions) {
461: this .disableGroupPermissions = disableGroupPermissions;
462: JiveGlobals.setProperty(
463: "plugin.broadcast.disableGroupPermissions", Boolean
464: .toString(disableGroupPermissions));
465: }
466:
467: /**
468: * Returns true if normal group members are allowed to send broadcast messages
469: * to groups they belong to. Otherwise, only group administrators can send
470: * broadcast messages to groups. Global allowed users can also send messages to
471: * groups.
472: *
473: * @return true if group members are allowed to broadcast messages; otherwise only
474: * group admins are allowed.
475: */
476: public boolean isGroupMembersAllowed() {
477: return groupMembersAllowed;
478: }
479:
480: /**
481: * Sets whether normal group members are allowed to send broadcast messages
482: * to groups they belong to. Otherwise, only group administrators can send
483: * broadcast messages to groups. Global allowed users can also send messages to
484: * groups.
485: *
486: * @param allowed true if group members are allowed to broadcast messages; otherwise only
487: * group admins are allowed.
488: */
489: public void setGroupMembersAllowed(boolean allowed) {
490: this .groupMembersAllowed = allowed;
491: JiveGlobals.setProperty("plugin.broadcast.groupMembersAllowed",
492: Boolean.toString(allowed));
493: }
494:
495: // PropertyEventListener Methods
496:
497: public void propertySet(String property, Map params) {
498: if (property.equals("plugin.broadcast.groupMembersAllowed")) {
499: this .groupMembersAllowed = Boolean
500: .parseBoolean((String) params.get("value"));
501: } else if (property
502: .equals("plugin.broadcast.disableGroupPermissions")) {
503: this .disableGroupPermissions = Boolean
504: .parseBoolean((String) params.get("value"));
505: } else if (property.equals("plugin.broadcast.allowedUsers")) {
506: this .allowedUsers = stringToList((String) params
507: .get("value"));
508: } else if (property.equals("plugin.broadcast.serviceName")) {
509: changeServiceName((String) params.get("value"));
510: }
511: }
512:
513: public void propertyDeleted(String property, Map params) {
514: if (property.equals("plugin.broadcast.groupMembersAllowed")) {
515: this .groupMembersAllowed = true;
516: } else if (property
517: .equals("plugin.broadcast.disableGroupPermissions")) {
518: this .disableGroupPermissions = false;
519: } else if (property.equals("plugin.broadcast.allowedUsers")) {
520: this .allowedUsers = Collections.emptyList();
521: } else if (property.equals("plugin.broadcast.serviceName")) {
522: changeServiceName("broadcast");
523: }
524: }
525:
526: public void xmlPropertySet(String property, Map params) {
527: // Ignore.
528: }
529:
530: public void xmlPropertyDeleted(String property, Map params) {
531: // Ignore.
532: }
533:
534: /**
535: * Changes the service name to a new value.
536: *
537: * @param serviceName the service name.
538: */
539: private void changeServiceName(String serviceName) {
540: if (serviceName == null) {
541: throw new NullPointerException(
542: "Service name cannot be null");
543: }
544: if (this .serviceName.equals(serviceName)) {
545: return;
546: }
547:
548: // Re-register the service.
549: try {
550: componentManager.removeComponent(this .serviceName);
551: } catch (Exception e) {
552: componentManager.getLog().error(e);
553: }
554: try {
555: componentManager.addComponent(serviceName, this );
556: } catch (Exception e) {
557: componentManager.getLog().error(e);
558: }
559: this .serviceName = serviceName;
560: }
561:
562: /**
563: * Returns a comma-delimitted list of strings into a Collection of Strings.
564: *
565: * @param str the String.
566: * @return a list.
567: */
568: private List<JID> stringToList(String str) {
569: List<JID> values = new ArrayList<JID>();
570: StringTokenizer tokens = new StringTokenizer(str, ",");
571: while (tokens.hasMoreTokens()) {
572: String value = tokens.nextToken().trim();
573: if (!value.equals("")) {
574: // See if this is a full JID or just a username.
575: if (value.contains("@")) {
576: values.add(new JID(value));
577: } else {
578: values.add(XMPPServer.getInstance().createJID(
579: value, null));
580: }
581: }
582: }
583: return values;
584: }
585: }
|