Package tigase.server.xmppcomponent

Source Code of tigase.server.xmppcomponent.ComponentConnectionManager

/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2007 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev: 1315 $
* Last modified by $Author: kobit $
* $Date: 2008-12-15 18:12:22 +0000 (Mon, 15 Dec 2008) $
*/

package tigase.server.xmppcomponent;

import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
import java.util.List;
import java.util.Queue;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import tigase.net.ConnectionType;
import tigase.net.SocketType;
import tigase.server.ConnectionManager;
import tigase.server.Packet;
import tigase.disco.XMPPService;
import tigase.disco.ServiceEntity;
import tigase.disco.ServiceIdentity;
import tigase.util.Algorithms;
import tigase.util.JIDUtils;
import tigase.xml.Element;
import tigase.xmpp.Authorization;
import tigase.xmpp.XMPPIOService;
import tigase.xmpp.PacketErrorTypeException;

/**
* Class ComponentConnectionManager
*
* Created: Tue Nov 22 07:07:11 2005
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev: 1315 $
*/
public class ComponentConnectionManager extends ConnectionManager<XMPPIOService>
  implements XMPPService {

  public int[] PORTS = {5555};
  public ConnectionType PORT_TYPE_PROP_VAL = ConnectionType.accept;
  public SocketType PORT_SOCKET_PROP_VAL = SocketType.plain;
  public static final String SECRET_PROP_KEY = "secret";
  public String SECRET_PROP_VAL =  "someSecret";
  public static final String PORT_LOCAL_HOST_PROP_KEY = "local-host";
  public String PORT_LOCAL_HOST_PROP_VAL = "localhost";
  public String PORT_REMOTE_HOST_PROP_VAL = "comp-1.localhost";
  public static final String PORT_ROUTING_TABLE_PROP_KEY = "routing-table";
  public String[] PORT_ROUTING_TABLE_PROP_VAL =
  { PORT_REMOTE_HOST_PROP_VAL, ".*@" + PORT_REMOTE_HOST_PROP_VAL,
    ".*\\." + PORT_REMOTE_HOST_PROP_VAL };
  public String[] PORT_IFC_PROP_VAL = {"*"};
  public static final String PACK_ROUTED_KEY = "pack-routed";
  public boolean PACK_ROUTED_VAL = false;
  public static final String RETURN_SERVICE_DISCO_KEY = "service-disco";
  public static final boolean RETURN_SERVICE_DISCO_VAL = true;
  public static final String IDENTITY_TYPE_KEY = "identity-type";
  public static final String IDENTITY_TYPE_VAL = "generic";

  private ServiceEntity serviceEntity = null;
  private boolean pack_routed = PACK_ROUTED_VAL;
  //private boolean service_disco = RETURN_SERVICE_DISCO_VAL;
  private String identity_type = IDENTITY_TYPE_VAL;
  private String service_id = "it doesn't matter";

  /**
   * Variable <code>log</code> is a class logger.
   */
  private static final Logger log =
    Logger.getLogger("tigase.server.xmppcomponent.ComponentConnectionManager");

  @Override
  public void processPacket(Packet packet) {
    log.finer("Processing packet: " + packet.getElemName()
      + ", type: " + packet.getType());
    log.finest("Processing packet: " + packet.getStringData());
    if (packet.getElemTo() != null
      && packet.getElemTo().equals(getComponentId())) {
      try {
        addOutPacket(
          Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,
            "Not implemented", true));
      } catch (PacketErrorTypeException e) {
        log.warning("Packet processing exception: " + e);
      }
      return;
    }
    if (pack_routed) {
      writePacketToSocket(packet.packRouted());
    } else {
      writePacketToSocket(packet);
    }
  }

  public Queue<Packet> processSocketData(XMPPIOService serv) {
    Packet p = null;
    while ((p = serv.getReceivedPackets().poll()) != null) {
      log.finer("Processing packet: " + p.getElemName()
        + ", type: " + p.getType());
      log.finest("Processing socket data: " + p.getStringData());
      if (p.getElemName().equals("handshake")) {
        processHandshake(p, serv);
      } else {
        if (p.isRouted()) {
          p = p.unpackRouted();
        } // end of if (p.isRouted())
        addOutPacket(p);
      }
    } // end of while ()
    return null;
  }

  private void processHandshake(Packet p, XMPPIOService serv) {
    switch (serv.connectionType()) {
    case connect: {
      String data = p.getElemCData();
      if (data == null) {
        String[] routings =
          (String[])serv.getSessionData().get(PORT_ROUTING_TABLE_PROP_KEY);
        updateRoutings(routings, true);
        String addr =
          (String)serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);
        log.fine("Connected to: " + addr);
        updateServiceDiscovery(addr, "XEP-0114 connected");
      } else {
        log.warning("Incorrect packet received: " + p.getStringData());
      }
      break;
    }
    case accept: {
      String digest = p.getElemCData();
      String id =
        (String)serv.getSessionData().get(XMPPIOService.SESSION_ID_KEY);
      String secret =
        (String)serv.getSessionData().get(SECRET_PROP_KEY);
      try {
        String loc_digest = Algorithms.hexDigest(id, secret, "SHA");
        log.finest("Calculating digest: id="+id+", secret="+secret
          +", digest="+loc_digest);
        if (digest != null && digest.equals(loc_digest)) {
          Packet resp = new Packet(new Element("handshake"));
          writePacketToSocket(serv, resp);
          String[] routings =
            (String[])serv.getSessionData().get(PORT_ROUTING_TABLE_PROP_KEY);
          updateRoutings(routings, true);
          String addr =
                  (String)serv.getSessionData().get(XMPPIOService.HOSTNAME_KEY);
          log.fine("Connected to: " + addr);
          updateServiceDiscovery(addr, "XEP-0114 connected");
        } else {
          log.info("Handshaking passwords don't match, disconnecting...");
          serv.stop();
        }
      } catch (Exception e) {
        log.log(Level.SEVERE, "Handshaking error.", e);
      }
      break;
    }
    default:
      // Do nothing, more data should come soon...
      break;
    } // end of switch (service.connectionType())
  }

  @Override
  public Map<String, Object> getDefaults(Map<String, Object> params) {
    String config_type = (String)params.get("config-type");
    if (config_type.equals(GEN_CONFIG_SM)) {
      PACK_ROUTED_VAL = true;
      PORT_TYPE_PROP_VAL = ConnectionType.accept;
    }
    if (config_type.equals(GEN_CONFIG_CS)) {
      PACK_ROUTED_VAL = true;
      PORT_TYPE_PROP_VAL = ConnectionType.connect;
      PORT_IFC_PROP_VAL = new String[] {"localhost"};
    }
    boolean def_found = false;
    for (String key: params.keySet()) {
      String gen_ext_comp = GEN_EXT_COMP;
      if (key.startsWith(GEN_EXT_COMP)) {
        String end = key.substring(GEN_EXT_COMP.length());
        if (getName().endsWith(end)) {
          gen_ext_comp = key;
        } // end of if (getName().endsWith(end))
        if (params.get(gen_ext_comp) != null) {
          def_found = true;
          log.config("Found default configuration: " +
            (String)params.get(gen_ext_comp));
          String[] comp_params = ((String)params.get(gen_ext_comp)).split(",");
          int idx = 0;
          if (comp_params.length >= idx + 1) {
            PORT_LOCAL_HOST_PROP_VAL = comp_params[idx++];
            log.config("Setting PORT_LOCAL_HOST_PROP_VAL to "
              + PORT_LOCAL_HOST_PROP_VAL);
          }
          if (comp_params.length >= idx + 1) {
            PORT_REMOTE_HOST_PROP_VAL = comp_params[idx++];
            log.config("Setting PORT_REMOTE_HOST_PROP_VAL to "
              + PORT_REMOTE_HOST_PROP_VAL);
            PORT_ROUTING_TABLE_PROP_VAL = new String[] { PORT_REMOTE_HOST_PROP_VAL };
            if (config_type.equals(GEN_CONFIG_CS)) {
              PORT_IFC_PROP_VAL = new String[] {PORT_REMOTE_HOST_PROP_VAL};
            }
          }
          if (comp_params.length >= idx + 1) {
            try {
              PORTS[0] = Integer.decode(comp_params[idx++]);
            } catch (NumberFormatException e) {
              log.warning("Incorrect component port number: " + comp_params[idx-1]);
              PORTS[0] = 5555;
            }
            log.config("Setting PORTS[0] to " + PORTS[0]);
          }
          if (comp_params.length >= idx + 1) {
            SECRET_PROP_VAL = comp_params[idx++];
            log.config("Setting SECRET_PROP_VAL to " + SECRET_PROP_VAL);
          }
          if (comp_params.length >= idx + 1) {
            if (comp_params[idx++].equals("ssl")) {
              PORT_SOCKET_PROP_VAL = SocketType.plain;
            }
            log.config("Setting PORT_SOCKET_PROP_VAL to " + PORT_SOCKET_PROP_VAL);
          }
          if (comp_params.length >= idx + 1) {
            if (comp_params[idx++].equals("accept")) {
              PORT_TYPE_PROP_VAL = ConnectionType.accept;
            } else {
              PORT_TYPE_PROP_VAL = ConnectionType.connect;
            }
            log.config("Setting PORT_TYPE_PROP_VAL to " + PORT_TYPE_PROP_VAL);
          }
          if (comp_params.length >= idx + 1) {
            PORT_ROUTING_TABLE_PROP_VAL = new String[] { comp_params[idx++] };
          } else {
            if (config_type.equals(GEN_CONFIG_COMP)) {
              // This is specialized configuration for a single
              // external component so all traffic should go through
              // the external component (it acts as like s2s component)
              PORT_ROUTING_TABLE_PROP_VAL =  new String[] { "*" };
            } else {
              String regex_host = PORT_REMOTE_HOST_PROP_VAL.replace(".", "\\.");
              PORT_ROUTING_TABLE_PROP_VAL =
                new String[] { regex_host, ".*@" + regex_host, ".*\\." + regex_host  };
            }
          }
          break;
        }
      }
    }
    if (!def_found) {
      PORT_LOCAL_HOST_PROP_VAL = "localhost";
      PORT_REMOTE_HOST_PROP_VAL = getName() + ".localhost";
      String regex_host = PORT_REMOTE_HOST_PROP_VAL.replace(".", "\\.");
      PORT_ROUTING_TABLE_PROP_VAL =
        new String[] { regex_host, ".*@" + regex_host, ".*\\." + regex_host  };
    } // end of if (!def_found)
    Map<String, Object> props = super.getDefaults(params);
    props.put(PACK_ROUTED_KEY, PACK_ROUTED_VAL);
    props.put(RETURN_SERVICE_DISCO_KEY, RETURN_SERVICE_DISCO_VAL);
    props.put(IDENTITY_TYPE_KEY, IDENTITY_TYPE_VAL);
    return props;
  }

  @Override
  public void setProperties(Map<String, Object> props) {
    super.setProperties(props);
    pack_routed = (Boolean)props.get(PACK_ROUTED_KEY);
    //service_disco = (Boolean)props.get(RETURN_SERVICE_DISCO_KEY);
    identity_type = (String)props.get(IDENTITY_TYPE_KEY);

    //serviceEntity = new ServiceEntity(getName(), "external", "XEP-0114");
    serviceEntity = new ServiceEntity("XEP-0114 " + getName(), null, "XEP-0114");
    serviceEntity.addIdentities(
      new ServiceIdentity("component", identity_type, "XEP-0114 " + getName()));
  }

  @Override
  protected int[] getDefPlainPorts() {
    return PORTS;
  }

  @Override
  protected Map<String, Object> getParamsForPort(int port) {
    Map<String, Object> defs = new TreeMap<String, Object>();
    defs.put(SECRET_PROP_KEY, SECRET_PROP_VAL);
    defs.put(PORT_LOCAL_HOST_PROP_KEY, PORT_LOCAL_HOST_PROP_VAL);
    defs.put(PORT_ROUTING_TABLE_PROP_KEY, PORT_ROUTING_TABLE_PROP_VAL);
    defs.put(PORT_TYPE_PROP_KEY, PORT_TYPE_PROP_VAL);
    defs.put(PORT_SOCKET_PROP_KEY, PORT_SOCKET_PROP_VAL);
    defs.put(PORT_REMOTE_HOST_PROP_KEY, PORT_REMOTE_HOST_PROP_VAL);
    defs.put(PORT_IFC_PROP_KEY, PORT_IFC_PROP_VAL);
    defs.put(MAX_RECONNECTS_PROP_KEY, (int)(30*MINUTE));
    return defs;
  }

  @Override
  protected String getUniqueId(XMPPIOService serv) {
    //    return (String)serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);
    return service_id;
  }

  @Override
  public void serviceStopped(XMPPIOService service) {
    super.serviceStopped(service);
    Map<String, Object> sessionData = service.getSessionData();
    String[] routings = (String[])sessionData.get(PORT_ROUTING_TABLE_PROP_KEY);
    updateRoutings(routings, false);
    ConnectionType type = service.connectionType();
    if (type == ConnectionType.connect) {
      addWaitingTask(sessionData);
      //reconnectService(sessionData, connectionDelay);
    } // end of if (type == ConnectionType.connect)
    //    removeRouting(serv.getRemoteHost());
    String addr = (String)sessionData.get(PORT_REMOTE_HOST_PROP_KEY);
    log.fine("Disonnected from: " + addr);
    updateServiceDiscovery(addr, "XEP-0114 disconnected");
  }

  @Override
  protected String getServiceId(Packet packet) {
    return service_id;
  }

  @Override
  public void serviceStarted(XMPPIOService serv) {
    super.serviceStarted(serv);
    log.finest("c2c connection opened: " + serv.getRemoteAddress()
      + ", type: " + serv.connectionType().toString()
      + ", id=" + serv.getUniqueId());
//     String addr =
//       (String)service.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);
//     addRouting(addr);
    //    addRouting(serv.getRemoteHost());
    switch (serv.connectionType()) {
    case connect:
      // Send init xmpp stream here
      //XMPPIOService serv = (XMPPIOService)service;
      String compName =
        (String)serv.getSessionData().get(PORT_LOCAL_HOST_PROP_KEY);
      String data =
        "<stream:stream"
        + " xmlns='jabber:component:accept'"
        + " xmlns:stream='http://etherx.jabber.org/streams'"
        + " to='" + compName + "'"
        + ">";
      log.finest("cid: " + (String)serv.getSessionData().get("cid")
        + ", sending: " + data);
      serv.xmppStreamOpen(data);
      break;
    default:
      // Do nothing, more data should come soon...
      break;
    } // end of switch (service.connectionType())
  }

  public String xmppStreamOpened(XMPPIOService service,
    Map<String, String> attribs) {

    log.finer("Stream opened: " + attribs.toString());

    switch (service.connectionType()) {
    case connect: {
      String id = attribs.get("id");
      service.getSessionData().put(XMPPIOService.SESSION_ID_KEY, id);
      String secret =
        (String)service.getSessionData().get(SECRET_PROP_KEY);
      try {
        String digest = Algorithms.hexDigest(id, secret, "SHA");
        log.finest("Calculating digest: id="+id+", secret="+secret
          +", digest="+digest);
        return "<handshake>" + digest + "</handshake>";
      } catch (NoSuchAlgorithmException e) {
        log.log(Level.SEVERE, "Can not generate digest for pass phrase.", e);
        return null;
      }
    }
    case accept: {
      String hostname = attribs.get("to");
      service.getSessionData().put(XMPPIOService.HOSTNAME_KEY, hostname);
      String id = UUID.randomUUID().toString();
      service.getSessionData().put(XMPPIOService.SESSION_ID_KEY, id);
      return "<stream:stream"
        + " xmlns='jabber:component:accept'"
        + " xmlns:stream='http://etherx.jabber.org/streams'"
        + " from='" + hostname + "'"
        + " id='" + id + "'"
        + ">";
    }
    default:
      // Do nothing, more data should come soon...
      break;
    } // end of switch (service.connectionType())
    return null;
  }

  public void xmppStreamClosed(XMPPIOService serv) {
    log.finer("Stream closed.");
  }

  /**
   * Method <code>getMaxInactiveTime</code> returns max keep-alive time
   * for inactive connection. we shoulnd not really close external component
   * connection at all, so let's say something like: 1000 days...
   *
   * @return a <code>long</code> value
   */
  protected long getMaxInactiveTime() {
    return 1000*24*HOUR;
  }

  private void updateRoutings(String[] routings, boolean add) {
    if (add) {
      for (String route: routings) {
        try {
          addRegexRouting(route);
        } catch (Exception e) {
          log.warning("Can not add regex routing '" + route + "' : " + e);
        }
      }
    } else {
      for (String route: routings) {
        try {
          removeRegexRouting(route);
        } catch (Exception e) {
          log.warning("Can not remove regex routing '" + route + "' : " + e);
        }
      }
    }
  }

  private void updateServiceDiscovery(String jid, String name) {
    ServiceEntity item = new ServiceEntity(jid, null, name);
    //item.addIdentities(new ServiceIdentity("component", identity_type, name));
    log.finest("Modifing service-discovery info: " + item.toString());
    serviceEntity.addItems(item);
  }

  public Element getDiscoInfo(String node, String jid) {
    if (jid != null && getName().equals(JIDUtils.getNodeNick(jid))) {
      return serviceEntity.getDiscoInfo(node);
    }
    return null;
  }

  public   List<Element> getDiscoFeatures() { return null; }

  public List<Element> getDiscoItems(String node, String jid) {
    if (getName().equals(JIDUtils.getNodeNick(jid))) {
      return serviceEntity.getDiscoItems(node, null);
    } else {
       return Arrays.asList(serviceEntity.getDiscoItem(null,
          JIDUtils.getNodeID(getName(), jid)));
    }
  }

  protected XMPPIOService getXMPPIOServiceInstance() {
    return new XMPPIOService();
  }

}
TOP

Related Classes of tigase.server.xmppcomponent.ComponentConnectionManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.