package org.jivesoftware.openfire.plugin.rest.service; import java.io.*; import java.util.*; import java.text.*; import javax.servlet.http.HttpServletRequest; import javax.annotation.PostConstruct; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.DefaultValue; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Context; import javax.xml.bind.*; import org.codehaus.jackson.map.*; import org.codehaus.jackson.xc.*; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.util.*; import org.jivesoftware.openfire.plugin.rest.controller.UserServiceController; import org.jivesoftware.openfire.plugin.rest.controller.MUCRoomController; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType; import org.jivesoftware.openfire.plugin.rest.entity.RosterEntities; import org.jivesoftware.openfire.plugin.rest.entity.RosterItemEntity; import org.jivesoftware.openfire.plugin.rest.entity.UserEntities; import org.jivesoftware.openfire.plugin.rest.entity.MUCChannelType; import org.jivesoftware.openfire.plugin.rest.entity.MUCRoomEntities; import org.jivesoftware.openfire.plugin.rest.entity.MUCRoomEntity; import org.jivesoftware.openfire.plugin.rest.entity.OccupantEntities; import org.jivesoftware.openfire.plugin.rest.entity.ParticipantEntities; import org.jivesoftware.openfire.plugin.rest.RestEventSourceServlet; import org.jivesoftware.openfire.user.*; import org.jivesoftware.openfire.SharedGroupException; import org.jivesoftware.openfire.user.UserNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.packet.*; import org.jivesoftware.openfire.archive.*; @Path("restapi/v1/chat") public class ChatService { private static final Logger Log = LoggerFactory.getLogger(ChatService.class); private XMPPServer server; private UserServiceController userService; @Context private HttpServletRequest httpRequest; @PostConstruct public void init() { server = XMPPServer.getInstance(); userService = UserServiceController.getInstance(); } //------------------------------------------------------- // // POST xmpp messags // //------------------------------------------------------- @POST @Path("/xmpp") public Response postXmppMessage(String xmpp) throws ServiceException { Log.info("postXmppMessage \n" + xmpp); try { String endUser = getEndUser(); if (!RestEventSourceServlet.sendXmppMessage(endUser, xmpp)) { throw new ServiceException("Exception", "send xmpp failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } /** * Chat Presence */ //------------------------------------------------------- // // POST self presence // //------------------------------------------------------- @POST @Path("/presence") public Response postPresence(@QueryParam("show") String show, @QueryParam("status") String status) throws ServiceException { Log.info("postPresence " + show + " " + status); try { String endUser = getEndUser(); if (!RestEventSourceServlet.postPresence(endUser, show, status)) { throw new ServiceException("Exception", "send chat failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } //------------------------------------------------------- // // POST chat message and GET chat history // //------------------------------------------------------- @POST @Path("/messages/{to}") public Response postMessage(@PathParam("to") String to, String body) throws ServiceException { Log.info("postMessage " + to + "\n" + body); try { String endUser = getEndUser(); // sent to destination if (!RestEventSourceServlet.sendChatMessage(endUser, body, to)) { throw new ServiceException("Exception", "send chat failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } // echo back to sender RestEventSourceServlet.emitData(endUser, "{\"type\": \"chat\", \"to\":\"" + to + "\", \"from\":\"" + makeJid(endUser) + "\", \"body\": \"" + body + "\"}"); } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } @GET @Path("/messages") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Conversations getConversations(@QueryParam("keywords") String keywords, @QueryParam("to") String to, @QueryParam("start") String start, @QueryParam("end") String end, @QueryParam("room") String room, @QueryParam("service") String service) throws ServiceException { Log.info("getConversations " + keywords + " " + " " + to + " " + start + " " + end + " " + room + " " + service); try { String endUser = getEndUser(); ArchiveSearch search = new ArchiveSearch(); JID participant1JID = makeJid(endUser); JID participant2JID = null; if (to != null) participant2JID = makeJid(to); if (participant2JID != null) { search.setParticipants(participant1JID, participant2JID); } else { search.setParticipants(participant1JID); } if (start != null) { DateFormat formatter = new SimpleDateFormat("MM/dd/yy"); try { Date date = formatter.parse(start); search.setDateRangeMin(date); } catch (Exception e) { Log.error("getConversations", e); throw new ServiceException("Exception", "Bad start date", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } if (end != null) { DateFormat formatter = new SimpleDateFormat("MM/dd/yy"); try { Date date = formatter.parse(end); // The user has chosen an end date and expects that any conversation // that falls on that day will be included in the search results. For // example, say the user choose 6/17/2006 as an end date. If a conversation // occurs at 5:33 PM that day, it should be included in the results. In // order to make this possible, we need to make the end date one millisecond // before the next day starts. date = new Date(date.getTime() + JiveConstants.DAY - 1); search.setDateRangeMax(date); } catch (Exception e) { Log.error("getConversations", e); throw new ServiceException("Exception", "Bad end date", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } if (keywords != null) search.setQueryString(keywords); if (service == null) service = "conference"; if (room != null) { search.setRoom(new JID(room + "@" + service + "." + server.getServerInfo().getXMPPDomain())); } search.setSortOrder(ArchiveSearch.SortOrder.ascending); Collection<Conversation> conversations = new ArchiveSearcher().search(search); Collection<Conversation> list = new ArrayList<Conversation>(); for (Conversation conversation : conversations) { list.add(conversation); } return new Conversations(list); } catch (Exception e) { Log.error("getConversations", e); throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } //------------------------------------------------------- // // Search for users. CRUD user profile (properties) // //------------------------------------------------------- @GET @Path("/users") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public UserEntities getUser(@QueryParam("search") String search) throws ServiceException { return userService.getUsersBySearch(search); } @POST @Path("/users/{propertyName}") public Response setUserProperty(@PathParam("propertyName") String propertyName, String propertyValue) throws ServiceException { try { String endUser = getEndUser(); User user = server.getUserManager().getUser(getEndUser()); user.getProperties().put(propertyName, propertyValue); } catch (Exception e) { Log.error("setUserProperty", e); throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } @DELETE @Path("/users/{propertyName}") public Response deleteUserProperty(@PathParam("propertyName") String propertyName) throws ServiceException { try { String endUser = getEndUser(); User user = server.getUserManager().getUser(getEndUser()); user.getProperties().remove(propertyName); } catch (Exception e) { Log.error("deleteUserProperty", e); throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } //------------------------------------------------------- // // CRUD contacts // //------------------------------------------------------- @GET @Path("/contacts") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public RosterEntities getUserRoster() throws ServiceException { RosterEntities roster = RestEventSourceServlet.getRoster(getEndUser()); //RosterEntities roster = userService.getRosterEntities(getEndUser()); if (roster == null) { throw new ServiceException("Exception", "get roster failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return roster; } @POST @Path("/contacts") public Response createRoster(RosterItemEntity rosterItemEntity) throws ServiceException { Log.info("createRoster"); try { String endUser = getEndUser(); userService.addRosterItem(endUser, rosterItemEntity); } catch (Exception e) { Log.error("getConversations", e); throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.CREATED).build(); } @PUT @Path("/contacts/{rosterJid}") public Response updateRoster(@PathParam("rosterJid") String rosterJid, RosterItemEntity rosterItemEntity) throws ServiceException { Log.info("updateRoster " + rosterJid); try { String endUser = getEndUser(); userService.updateRosterItem(endUser, rosterJid, rosterItemEntity); } catch (Exception e) { Log.error("getConversations", e); throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.CREATED).build(); } @DELETE @Path("/contacts/{rosterJid}") public Response deleteRoster(@PathParam("rosterJid") String rosterJid) throws ServiceException { Log.info("deleteRoster " + rosterJid); try { String endUser = getEndUser(); userService.deleteRosterItem(endUser, rosterJid); } catch (Exception e) { Log.error("getConversations", e); throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.CREATED).build(); } //------------------------------------------------------- // // get, join, leave, post message chat rooms // //------------------------------------------------------- @GET @Path("/rooms") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public MUCRoomEntities getMUCRooms(@DefaultValue("conference") @QueryParam("servicename") String serviceName, @DefaultValue(MUCChannelType.PUBLIC) @QueryParam("type") String channelType, @QueryParam("search") String roomSearch, @DefaultValue("false") @QueryParam("expandGroups") Boolean expand) { return MUCRoomController.getInstance().getChatRooms(serviceName, channelType, roomSearch, expand); } @GET @Path("/rooms/{roomName}") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public MUCRoomEntity getMUCRoomJSON2(@PathParam("roomName") String roomName, @DefaultValue("conference") @QueryParam("servicename") String serviceName, @DefaultValue("false") @QueryParam("expandGroups") Boolean expand) throws ServiceException { return MUCRoomController.getInstance().getChatRoom(roomName, serviceName, expand); } @GET @Path("/rooms/{roomName}/participants") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public ParticipantEntities getMUCRoomParticipants(@PathParam("roomName") String roomName, @DefaultValue("conference") @QueryParam("servicename") String serviceName) { return MUCRoomController.getInstance().getRoomParticipants(roomName, serviceName); } @GET @Path("/rooms/{roomName}/occupants") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public OccupantEntities getMUCRoomOccupants(@PathParam("roomName") String roomName, @DefaultValue("conference") @QueryParam("servicename") String serviceName) { return MUCRoomController.getInstance().getRoomOccupants(roomName, serviceName); } @PUT @Path("/rooms/{roomName}") public Response joinRoom(@DefaultValue("conference") @QueryParam("service") String service, @PathParam("roomName") String roomName) throws ServiceException { Log.info("joinRoom " + service + " " + roomName); try { String endUser = getEndUser(); if (!RestEventSourceServlet.joinRoom(endUser, roomName + "@" + service + "." + server.getServerInfo().getXMPPDomain(), endUser)) { throw new ServiceException("Exception", "join room failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } @DELETE @Path("/rooms/{roomName}") public Response leaveRoom(@DefaultValue("conference") @QueryParam("service") String service, @PathParam("roomName") String roomName) throws ServiceException { Log.info("leaveRoom " + service + " " + roomName); try { String endUser = getEndUser(); if (!RestEventSourceServlet.leaveRoom(endUser, roomName + "@" + service + "." + server.getServerInfo().getXMPPDomain())) { throw new ServiceException("Exception", "leave room failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } @POST @Path("/rooms/{roomName}") public Response postToRoom(@DefaultValue("conference") @QueryParam("service") String service, @PathParam("roomName") String roomName, String body) throws ServiceException { try { String endUser = getEndUser(); if (!RestEventSourceServlet.sendRoomMessage(endUser, roomName + "@" + service + "." + server.getServerInfo().getXMPPDomain(), body)) { throw new ServiceException("Exception", "send message to room failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } @POST @Path("/rooms/{roomName}/{invitedJid}") public Response inviteToRoom(@DefaultValue("conference") @QueryParam("service") String service, @PathParam("roomName") String roomName, @PathParam("invitedJid") String invitedJid, String reason) throws ServiceException { try { String endUser = getEndUser(); if (!RestEventSourceServlet.inviteToRoom(endUser, roomName + "@" + service + "." + server.getServerInfo().getXMPPDomain(), invitedJid, reason)) { throw new ServiceException("Exception", "invite to room failed", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } } catch (Exception e) { throw new ServiceException("Exception", e.getMessage(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return Response.status(Response.Status.OK).build(); } //------------------------------------------------------- // // Utitlities // //------------------------------------------------------- private String getEndUser() throws ServiceException { String endUser = httpRequest.getUserPrincipal().getName(); Log.info("getEndUser " + endUser); if (endUser == null) { throw new ServiceException("Exception", "Access denied", ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST); } return endUser; } private JID makeJid(String participant1) { JID participant1JID = null; try { int position = participant1.lastIndexOf("@"); if (position > -1) { String node = participant1.substring(0, position); participant1JID = new JID(JID.escapeNode(node) + participant1.substring(position)); } else { participant1JID = new JID(JID.escapeNode(participant1), server.getServerInfo().getXMPPDomain(), null); } } catch (Exception e) { Log.error("makeJid", e); } return participant1JID; } private Object jsonToObject(String json, Class objectClass) { Object object = null; try { ObjectMapper mapper = new ObjectMapper(); AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(); mapper.setAnnotationIntrospector(introspector); object = mapper.readValue(json, objectClass); } catch (Exception e) { Log.error("jsonToObject", e); } Log.info("jsonToObject\n" + json + "\nObject= " + object); return object; } private String objectToJson(Object object) { String json = null; try { ObjectMapper mapper = new ObjectMapper(); AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(); mapper.setAnnotationIntrospector(introspector); json = mapper.writeValueAsString(object); } catch (Exception e) { Log.error("objectToJson", e); } Log.info("objectToJson\n" + json + "\nObject= " + object); return json; } }