/* * IMTemplate.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 2008-2009 Sun Microsystems, Inc. * * Sun Public License Notice * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is included as the file "license.terms", * and also available at http://www.sun.com/ * * The Original Code is from: * Brazil project web application toolkit release 2.3. * The Initial Developer of the Original Code is: suhler. * Portions created by suhler are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): suhler. * * Version: 1.2 * Created by suhler on 08/08/18 * Last modified by suhler on 09/07/07 13:57:22 * * Version Histories: * * 1.2 09/07/07-13:57:22 (suhler) * added vcard, better documentation, debug flags * * 1.2 70/01/01-00:00:02 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 1 0 xmpp/IMTemplate.java * * 1.1 08/08/18-14:55:58 (suhler) * date and time created 08/08/18 14:55:58 by suhler * */ package sunlabs.brazil.xmpp; import sunlabs.brazil.server.FileHandler; import sunlabs.brazil.server.Server; import sunlabs.brazil.template.RewriteContext; import sunlabs.brazil.template.Template; import sunlabs.brazil.util.regexp.Regexp; import sunlabs.brazil.util.StringMap; import sunlabs.brazil.template.QueueTemplate.Queue; import sunlabs.brazil.template.QueueTemplate; import java.io.IOException; import java.util.Properties; import java.util.Hashtable; import java.util.Dictionary; import java.util.Iterator; import java.util.StringTokenizer; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.Roster; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.MessageTypeFilter; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.RosterGroup; import org.jivesoftware.smack.packet.RosterPacket; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.packet.VCard; /** * XMPP client template. (Incomplete - still under construction.) * This uses the SMACK library at: * {@link http://www.igniterealtime.org/projects/smack/} * It has been tested with version 3.0.4. */ public class IMTemplate extends Template { boolean debug=false; static Hashtable connections = new Hashtable(); static public final String[] vcardFields = new String[] { "FN", "NICKNAME", "PHOTO", "BDAY", "JABBERID", "MAILER", "TZ", "GEO", "TITLE", "ROLE", "LOGO", "NOTE", "PRODID", "REV", "SORT-STRING", "SOUND", "UID", "URL", "DESC" }; /** *
*
<im command=login id=xx server=xx * user=xx pass=xx * [host=xx port=nn prepend=xxx] * [subscribe=reject|decide] * [qid=xx] * > *
Log into an XMPP server. "host" and "port" are only required * when the values implied by the "server" attribute are not correct. * "id" is an arbirary identifier used to identify this session. It * must be used with all other "im" commands, and is also the name of * the Queue used to receive messages from. * If "subscribe" is specified, it indicates the buddy subscription * mode, which defaults to automatic. * If "decide" is used, then it is up to the client to accept or * reject subscriptions. (The default is to accept all subscriptions). *

* The value of "qid" can be used to override the Q name, allowing * multiple accounts (id) to be used with the same Q (qid). *

<im command=logout id=xx> *
Log out of this session. Any entries in the Queue remain. *
<im command=message id=xx to=xxx body=xxx * [thread=xxx]> *
Send a message to another user (e.g. jow.blow@gmail.com). *
<im command=presence id=xx * [mode=available|away|chat|dnd|xa * type=available|unavailable|subscribe|subscribed|unsubscribe|unsubscribed * body=xxx to=xxx]> *
Send presence information. *
*
mode
If type is "available", meaning the client is accepting * messages, the mode specifies the client's presence status. * (defaults to "available") *
type
This sets the message type. "available", and "unavailable" * are used to indicate the client state, which is "available" if the client * is connected (even if the "mode" is unavailable), and "unavailable" if * they are disconnected. "available" is the default. *

* The other types are for managing subscriptions:
* - subscribe: request a subscription ("to" is required)
* - subscribed: grant a subscription request
* - unsubscribe: request subscription removal ("to" is required)
* - unsubscribed: grant a subscription removal request, or reject a subscription * request. *

body
The presence message (for type=available) *
*
<im command=getroster id=xx [prepend=xx]> *
Retrieve your buddy list (roster in smack-speak). *
<im command=addroster id=xx user=xx * [name=xx groups="g1 g2..."]> *
Add a buddy to the buddy list, in the specified groups. Groups are * created as-needed. *
<im command=deluser id=xx user=xx> *
Remove a buddy from the buddy list. *
<im command=vcard id=xx [user=xx prepend=xx]> *
Retrieve VCARD information. If "user" isn' specified, then the * currently logged-in user's vCard is returned. *
<dequeue name=[qid] .....> *
This is used to retrieve messages from the XMPP system, using the * {@link sunlabs.brazil.template.QueueTemplate}. * "name" is should be the value of "qid", if specified, or "id" otherwise. * Each dequeued item is a dictionary of name/value pairs describing * the event. *

* The following entries are always present: *

* Other entries may be present, depending on the "category", * including: "type", "body", "thread", and "subject" for Message * packets, lists of "type", "status", "name", and "user" entries * for roster packets, and lists of "body", "priority", "type", and * "state" entries for presence packets. Inspect the set of dequeued * properties for a complete list. *
* * Notes: * */ public void tag_im(RewriteContext hr) { hr.killToken(); debug=hr.isTrue("debug"); debug(hr); if (debug) { System.out.println(hr.getToken()); } String command = hr.get("command"); String id = hr.get("id"); hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix, " id=" + id + " cmd=" + command); if (id==null || command == null) { debug(hr, "id or command attributes missing"); return; } if (command.equals("login")) { XMPPConnection con = doLogin(hr, id); if (con != null) { // set the subscription state here? Roster roster = con.getRoster(); String sub = hr.get("subscribe"); if (sub != null) { if (sub.equals("reject")) { roster.setSubscriptionMode( Roster.SubscriptionMode.reject_all); } else if (sub.equals("decide")) { roster.setSubscriptionMode( Roster.SubscriptionMode.manual); } } mapRoster(hr, roster); con.addPacketListener(new MessageRelay(con, id, hr.get("qid")), new MyFilter()); } return; } /** * Every other command needs to be logged in */ XMPPConnection con = (XMPPConnection) connections.get(id); if (con == null) { logStatus(hr, "error: id (" + id + ") not logged in for: " + command); return; } if (command.equals("logout")) { con.sendPacket(new Presence(Presence.Type.unavailable)); con.disconnect(); connections.remove(id); logStatus(hr, "logout succeeded"); } else if (command.equals("message")) { String to = hr.get("to"); if (to == null) { logStatus(hr, "error: 'to' required"); } Message msg = new Message(to); msg.setBody(hr.get("body", "hello")); String thread = hr.get("thread"); if (thread != null) { msg.setThread(thread); } con.sendPacket(msg); logStatus(hr, "message sent"); } else if (command.equals("presence")) { String mode = hr.get("mode", "available"); String type = hr.get("type"); String body = hr.get("body"); String to = hr.get("to"); Presence.Type pt = Presence.Type.available; try { pt = Presence.Type.valueOf(hr.get("type","available")); } catch (IllegalArgumentException e) {} Presence pr = new Presence(pt); if (body != null) { pr.setStatus(body); } try { pr.setMode(Presence.Mode.valueOf(mode)); } catch (IllegalArgumentException e) {} if (to != null) { pr.setTo(to); } con.sendPacket(pr); logStatus(hr, "presence sent"); } else if (command.equals("getroster")) { // get the current buddy list mapRoster(hr, con.getRoster()); } else if (command.equals("addroster")) { // add a "buddy" String user = hr.get("user"); String name = hr.get("name", user); String groups = hr.get("groups"); if (user == null) { logStatus(hr, "error: user required"); return; } Roster roster = con.getRoster(); String[] groupA = toArray(groups); for(int i=0;groupA!=null && i" + qid + ": " + map); } QueueTemplate.enqueue(qid, from, map, false, false); } } String getPrepend(RewriteContext hr) { String prepend = hr.get("prepend", hr.prefix); if (!prepend.endsWith(".")) { prepend += "."; } return prepend; } // for testing static class MyFilter implements PacketFilter { public boolean accept(Packet pkt) { // System.out.println("Accepting (" + pkt.toXML() + ")"); // I haven't figured out what packets I need yet. return (pkt instanceof Presence) || (pkt instanceof Message) || (pkt instanceof RosterPacket); } } /** * command line interface?. * user pass server [host port] */ public static void main(String[] args) throws Exception { } }