package org.mobicents.slee.sippresence.server.subscription; import java.io.StringReader; import java.util.HashMap; import java.util.Map; import javax.sip.ServerTransaction; import javax.sip.message.Response; import javax.xml.bind.JAXBElement; import org.apache.log4j.Logger; import org.mobicents.slee.sipevent.server.publication.pojo.ComposedPublication; import org.mobicents.slee.sipevent.server.subscription.NotifyContent; import org.mobicents.slee.sipevent.server.subscription.pojo.Subscription; import org.mobicents.slee.sipevent.server.subscription.pojo.SubscriptionKey; import org.mobicents.slee.sippresence.pojo.datamodel.Person; import org.mobicents.slee.sippresence.pojo.pidf.Presence; import org.mobicents.slee.sippresence.pojo.rpid.Sphere; import org.mobicents.slee.sippresence.server.subscription.rules.OMAPresRule; import org.mobicents.slee.sippresence.server.subscription.rules.PresRuleCMPKey; import org.mobicents.slee.sippresence.server.subscription.rules.RulesetProcessor; import org.mobicents.slee.sippresence.server.subscription.rules.SubHandlingAction; import org.openxdm.xcap.client.appusage.presrules.jaxb.commonpolicy.Ruleset; import org.openxdm.xcap.common.key.AttributeUriKey; import org.openxdm.xcap.common.key.DocumentUriKey; import org.openxdm.xcap.common.key.ElementUriKey; import org.openxdm.xcap.common.key.XcapUriKey; import org.openxdm.xcap.common.uri.DocumentSelector; import org.openxdm.xcap.common.uri.ParseException; import org.openxdm.xcap.common.uri.Parser; /** * Logic for the sip presence subscription control, which complements the SIP * Event generic logic. * * @author martins * */ public class PresenceSubscriptionControl { private static Logger logger = Logger .getLogger(PresenceSubscriptionControl.class); private static final String[] eventPackages = { "presence" }; private PresenceSubscriptionControlSbbLocalObject sbb; public PresenceSubscriptionControl( PresenceSubscriptionControlSbbLocalObject sbb) { this.sbb = sbb; } public static String[] getEventPackages() { return eventPackages; } public void isSubscriberAuthorized(String subscriber, String subscriberDisplayName, String notifier, SubscriptionKey key, int expires, String content, String contentType, String contentSubtype, boolean eventList, String presRulesAUID, String presRulesDocumentName, ServerTransaction serverTransaction) { // get current combined rule from cmp Map combinedRules = sbb.getCombinedRules(); if (combinedRules == null) { combinedRules = new HashMap(); } PresRuleCMPKey cmpKey = new PresRuleCMPKey(subscriber, notifier, key .getEventPackage(), key.getRealEventId()); OMAPresRule combinedRule = (OMAPresRule) combinedRules.get(cmpKey); if (combinedRule == null) { // rule not found, lookup for another key with same subscriber and // notifier for (Object keyObject : combinedRules.keySet()) { PresRuleCMPKey k = (PresRuleCMPKey) keyObject; if (k.getSubscriber().equals(subscriber) && k.getNotifierWithoutParams().equals(cmpKey.getNotifierWithoutParams())) { // found compatible key, get rule and assign to this key // also combinedRule = (OMAPresRule) combinedRules.get(k); combinedRules.put(cmpKey, combinedRule); sbb.setCombinedRules(combinedRules); break; } } } if (combinedRule == null) { // get pres-rules doc on xdm DocumentSelector documentSelector = getDocumentSelector(cmpKey.getNotifierWithoutParams(), presRulesAUID, presRulesDocumentName); if (documentSelector == null) { sbb.getParentSbbCMP().newSubscriptionAuthorization(subscriber, subscriberDisplayName, notifier, key, expires, Response.FORBIDDEN,eventList,serverTransaction); } else { // we need to go to the xdm, reply accepted for subscription // state be pending combinedRules.put(cmpKey, new OMAPresRule()); sbb.setCombinedRules(combinedRules); sbb.getParentSbbCMP().newSubscriptionAuthorization(subscriber, subscriberDisplayName, notifier, key, expires, Response.ACCEPTED,eventList,serverTransaction); // ask for pres-rules doc sbb.getXDMClientControlSbb().get( new DocumentUriKey(documentSelector),null); } } else { // great, we already had the doc, send response code to the abstract // subscription control sbb.getParentSbbCMP().newSubscriptionAuthorization(subscriber, subscriberDisplayName, notifier, key, expires, combinedRule.getSubHandling().getResponseCode(),eventList, serverTransaction); } } public void removingSubscription(Subscription subscription, String presRulesAUID, String presRulesDocumentName) { if (logger.isDebugEnabled()) { logger.debug("removingSubscription(" + subscription + ")"); } // get combined rules cmp Map combinedRules = sbb.getCombinedRules(); if (combinedRules != null) { // remove subscription from map if (logger.isDebugEnabled()) { logger.debug("combined rules: " + combinedRules.keySet()); } PresRuleCMPKey cmpKey = new PresRuleCMPKey(subscription .getSubscriber(), subscription.getNotifier(), subscription .getKey().getEventPackage(), subscription.getKey() .getRealEventId()); if (combinedRules.remove(cmpKey) != null) { if (logger.isDebugEnabled()) { logger.debug("removed rule from combined rules map (" + subscription + ")"); } // check if subscription to pres-rules still needed boolean subscriptionNeeded = false; for (Object k : combinedRules.keySet()) { PresRuleCMPKey presRuleCMPKey = (PresRuleCMPKey) k; if (presRuleCMPKey.getNotifierWithoutParams().equals( cmpKey.getNotifierWithoutParams()) && presRuleCMPKey.getEventPackage().equals( subscription.getKey().getEventPackage())) { if (logger.isDebugEnabled()) { logger.debug("subscription needed due to rule " + presRuleCMPKey); } subscriptionNeeded = true; break; } } if (!subscriptionNeeded) { if (logger.isDebugEnabled()) { logger.debug("subscription not needed"); } DocumentSelector documentSelector = getDocumentSelector( cmpKey.getNotifierWithoutParams(), presRulesAUID, presRulesDocumentName); sbb.getXDMClientControlSbb().unsubscribeDocument( documentSelector); } } } } /** * async get response from xdm client */ public void getResponse(XcapUriKey key, int responseCode, String mimetype, String content) { DocumentSelector documentSelector = null; if (key instanceof DocumentUriKey) { documentSelector = ((DocumentUriKey) key).getDocumentSelector(); } else if (key instanceof AttributeUriKey) { documentSelector = ((AttributeUriKey) key).getDocumentSelector(); } else if (key instanceof ElementUriKey) { documentSelector = ((ElementUriKey) key).getDocumentSelector(); } else { try { documentSelector = Parser.parseDocumentSelector(key .getResourceSelector().getDocumentSelector()); } catch (ParseException e) { // won't happen logger .error( "bug, a xcap uri key document selector string could not be parsed", e); } } if (responseCode == 200) { // just simulate a document update documentUpdated(documentSelector, null, null, content); } else { // let's be friendly with clients without xcap, allow subscription String notifierWithoutParams = getUser(documentSelector); if (logger.isInfoEnabled()) { logger.info(notifierWithoutParams+ " pres-rules not found, allowing subscription"); } Map combinedRules = sbb.getCombinedRules(); if (combinedRules != null) { for (Object object : combinedRules.keySet()) { PresRuleCMPKey cmpKey = (PresRuleCMPKey) object; if (cmpKey.getNotifierWithoutParams().equals(notifierWithoutParams)) { OMAPresRule combinedRule = (OMAPresRule) combinedRules .get(cmpKey); combinedRule.setProvideAllDevices(true); combinedRule.setProvideAllAttributes(true); combinedRule.setProvideAllPersons(true); combinedRule.setProvideAllServices(true); combinedRule.setSubHandling(SubHandlingAction.allow); // notify auth changed sbb.getParentSbbCMP() .authorizationChanged( cmpKey.getSubscriber(), cmpKey.getNotifier(), "presence", cmpKey.getEventId(), combinedRule.getSubHandling() .getResponseCode()); } } sbb.setCombinedRules(combinedRules); } } // subscribe to changes in this pres-rules doc sbb.getXDMClientControlSbb().subscribeDocument(documentSelector); } public void documentUpdated(DocumentSelector documentSelector, String oldETag, String newETag, String documentAsString) { if (!documentSelector.getAUID().equals( "org.openmobilealliance.pres-rules") && !documentSelector.getAUID().equals("pres-rules")) { // not a pres-rules doc, maybe it's coming here because of // integrated servers using xdm client return; } String notifierWithoutParams = getUser(documentSelector); // unmarshall doc Ruleset ruleset = unmarshallRuleset(documentAsString); if (ruleset == null) { logger .error("rcvd ruleset update from xdm client but unmarshalling of ruleset failed, ignoring update"); return; } // get current combined rules from cmp Map combinedRules = sbb.getCombinedRules(); if (combinedRules == null) { combinedRules = new HashMap(); } // for each combined rules that has the user that updated the doc as // notifier reprocess the rules for (Object key : combinedRules.keySet()) { PresRuleCMPKey cmpKey = (PresRuleCMPKey) key; if (cmpKey.getNotifierWithoutParams().equals(notifierWithoutParams)) { OMAPresRule oldCombinedRule = (OMAPresRule) combinedRules .get(cmpKey); RulesetProcessor rulesetProcessor = new RulesetProcessor(cmpKey .getSubscriber(), notifierWithoutParams, ruleset, sbb); OMAPresRule newCombinedRule = rulesetProcessor .getCombinedRule(); combinedRules.put(cmpKey, newCombinedRule); // check permission changed if (oldCombinedRule.getSubHandling().getResponseCode() != newCombinedRule .getSubHandling().getResponseCode()) { sbb.getParentSbbCMP().authorizationChanged( cmpKey.getSubscriber(), cmpKey.getNotifier(), "presence", cmpKey.getEventId(), newCombinedRule.getSubHandling().getResponseCode()); } } } sbb.setCombinedRules(combinedRules); } /** * interface used by rules processor to get sphere for a notifier */ public String getSphere(String notifier) { // get ridden of notifier uri params, if any String notifierWithoutParams = notifier.split(";")[0]; ComposedPublication composedPublication = sbb.getPublicationChildSbb() .getComposedPublication(notifierWithoutParams, "presence"); if (composedPublication != null && composedPublication.getUnmarshalledContent().getValue() instanceof Presence) { Presence presence = (Presence) composedPublication .getUnmarshalledContent().getValue(); for (Object anyObject : presence.getAny()) { JAXBElement anyElement = (JAXBElement) anyObject; if (anyElement.getValue() instanceof Person) { Person person = (Person) anyElement.getValue(); for (Object anotherAnyObject : person.getAny()) { JAXBElement anotherAnyElement = (JAXBElement) anotherAnyObject; if (anotherAnyElement.getValue() instanceof Sphere) { Sphere sphere = ((Sphere) anotherAnyElement .getValue()); String result = null; for (Object contentObject : sphere.getContent()) { if (contentObject instanceof String) { if (result == null) { result = (String) contentObject; } else { result += " " + (String) contentObject; } } else if (contentObject instanceof JAXBElement) { JAXBElement contentElement = (JAXBElement) contentObject; if (result == null) { result = contentElement.getName() .getLocalPart(); } else { result += " " + contentElement.getName() .getLocalPart(); } } } return result; } } } } } return null; } public NotifyContent getNotifyContent(Subscription subscription) { try { ComposedPublication composedPublication = sbb .getPublicationChildSbb().getComposedPublication( subscription.getNotifier(), subscription.getKey().getEventPackage()); if (composedPublication != null) { return new NotifyContent(composedPublication .getUnmarshalledContent(), sbb.getHeaderFactory() .createContentTypeHeader( composedPublication.getContentType(), composedPublication.getContentSubType())); } } catch (Exception e) { logger.error("failed to get notify content", e); } return null; } public Object filterContentPerSubscriber(String subscriber, String notifier, String eventPackage, Object unmarshalledContent) { // get ridden of notifier uri params, if any //String notifier = subscription.getNotifier().split(";")[0]; // TODO apply transformations, including polite-block (see pres-rules // specs) return unmarshalledContent; } private Ruleset unmarshallRuleset(String documentAsString) { // unmarshall doc StringReader stringReader = new StringReader(documentAsString); try { return (Ruleset) sbb.getUnmarshaller().unmarshal(stringReader); } catch (Exception e) { logger.error("unmarshalling of ruleset failed", e); return null; } finally { stringReader.close(); } } // --------- AUX /** * from a user return the document selector pointing to it's pres-rules * * @param user * @return */ private DocumentSelector getDocumentSelector(String user, String presRulesAUID, String presRulesDocumentName) { return new DocumentSelector(presRulesAUID, "users/" + user, presRulesDocumentName); } /** * from a document selector point to a pres-rules doc return the user * @param documentSelector * @return */ private String getUser(DocumentSelector documentSelector) { return documentSelector.getDocumentParent() .substring("users/".length()); } }