001: /**
002: * $Revision$
003: * $Date$
004: *
005: * Copyright (C) 2006-2007 Jive Software. All rights reserved.
006: *
007: * This software is published under the terms of the GNU Public License (GPL),
008: * a copy of which is included in this distribution.
009: *
010: * Heavily inspired by joscardemo of the Joust Project: http://joust.kano.net/
011: */package org.jivesoftware.openfire.gateway.protocols.oscar;
012:
013: import net.kano.joscar.BinaryTools;
014: import net.kano.joscar.ByteBlock;
015: import net.kano.joscar.OscarTools;
016: import net.kano.joscar.flap.FlapCommand;
017: import net.kano.joscar.flap.FlapPacketEvent;
018: import net.kano.joscar.flapcmd.LoginFlapCmd;
019: import net.kano.joscar.flapcmd.SnacCommand;
020: import net.kano.joscar.net.ConnDescriptor;
021: import net.kano.joscar.ratelim.RateLimitingQueueMgr;
022: import net.kano.joscar.snac.*;
023: import net.kano.joscar.snaccmd.*;
024: import net.kano.joscar.snaccmd.buddy.BuddyOfflineCmd;
025: import net.kano.joscar.snaccmd.buddy.BuddyStatusCmd;
026: import net.kano.joscar.snaccmd.conn.*;
027: import net.kano.joscar.snaccmd.error.SnacError;
028: import net.kano.joscar.snaccmd.icbm.InstantMessage;
029: import net.kano.joscar.snaccmd.icbm.OldIcbm;
030: import net.kano.joscar.snaccmd.icbm.RecvImIcbm;
031: import net.kano.joscar.snaccmd.icbm.TypingCmd;
032: import net.kano.joscar.snaccmd.icon.IconDataCmd;
033: import net.kano.joscar.snaccmd.icon.IconRequest;
034: import net.kano.joscar.snaccmd.icon.UploadIconAck;
035: import net.kano.joscar.snaccmd.icon.UploadIconCmd;
036: import org.apache.log4j.Logger;
037: import org.jivesoftware.openfire.gateway.avatars.Avatar;
038: import org.jivesoftware.openfire.gateway.type.PresenceType;
039: import org.jivesoftware.openfire.gateway.type.TransportType;
040: import org.jivesoftware.util.JiveGlobals;
041: import org.jivesoftware.util.LocaleUtils;
042: import org.jivesoftware.util.NotFoundException;
043: import org.jivesoftware.util.StringUtils;
044: import org.xmpp.packet.Message;
045:
046: import java.io.UnsupportedEncodingException;
047: import java.security.MessageDigest;
048: import java.security.NoSuchAlgorithmException;
049: import java.text.DateFormat;
050: import java.util.Arrays;
051: import java.util.Collection;
052: import java.util.List;
053:
054: /**
055: * Handles incoming FLAP packets.
056: *
057: * @author Daniel Henninger
058: * Heavily inspired by joscardemo from the joscar project.
059: */
060: public abstract class BasicFlapConnection extends
061: AbstractFlapConnection {
062:
063: static Logger Log = Logger.getLogger(BasicFlapConnection.class);
064:
065: protected final ByteBlock cookie;
066: protected boolean sentClientReady = false;
067:
068: protected int[] snacFamilies = null;
069: protected Collection<SnacFamilyInfo> snacFamilyInfos;
070: protected RateLimitingQueueMgr rateMgr = new RateLimitingQueueMgr();
071:
072: public BasicFlapConnection(ConnDescriptor cd,
073: OSCARSession mainSession, ByteBlock cookie) {
074: super (cd, mainSession);
075: this .cookie = cookie;
076: initBasicFlapConnection();
077: }
078:
079: private void initBasicFlapConnection() {
080: sp.setSnacQueueManager(rateMgr);
081: }
082:
083: protected DateFormat dateFormat = DateFormat.getDateTimeInstance(
084: DateFormat.SHORT, DateFormat.SHORT);
085:
086: protected void handleFlapPacket(FlapPacketEvent e) {
087: Log.debug("OSCAR flap packet received: " + e);
088: FlapCommand cmd = e.getFlapCommand();
089:
090: if (cmd instanceof LoginFlapCmd) {
091: getFlapProcessor().sendFlap(new LoginFlapCmd(cookie));
092: }
093: }
094:
095: protected void handleSnacPacket(SnacPacketEvent e) {
096: Log.debug("OSCAR snac packet received: " + e);
097: SnacCommand cmd = e.getSnacCommand();
098: if (cmd instanceof ServerReadyCmd) {
099: ServerReadyCmd src = (ServerReadyCmd) cmd;
100: setSnacFamilies(src.getSnacFamilies());
101:
102: Collection<SnacFamilyInfo> familyInfos = SnacFamilyInfoFactory
103: .getDefaultFamilyInfos(src.getSnacFamilies());
104: setSnacFamilyInfos(familyInfos);
105:
106: getMainSession().registerSnacFamilies(this );
107:
108: request(new ClientVersionsCmd(familyInfos));
109: request(new RateInfoRequest());
110: } else if (cmd instanceof RecvImIcbm) {
111: RecvImIcbm icbm = (RecvImIcbm) cmd;
112:
113: String sn = icbm.getSenderInfo().getScreenname();
114: InstantMessage message = icbm.getMessage();
115: String msg = StringUtils.unescapeFromXML(OscarTools
116: .stripHtml(message.getMessage()));
117:
118: getMainSession().getTransport().sendMessage(
119: getMainSession().getJIDWithHighestPriority(),
120: getMainSession().getTransport().convertIDToJID(sn),
121: msg);
122: } else if (cmd instanceof OldIcbm) {
123: OldIcbm oicbm = (OldIcbm) cmd;
124: if (oicbm.getMessageType() == OldIcbm.MTYPE_PLAIN) {
125: String uin = String.valueOf(oicbm.getSender());
126: String msg = StringUtils.unescapeFromXML(OscarTools
127: .stripHtml(oicbm.getReason()));
128: Log.debug("Got ICBM message " + uin + " with " + msg
129: + "\n" + oicbm);
130: // InstantMessage message = oicbm.getMessage();
131: // Log.debug("Got ICBM message "+uin+" with "+message+"\n"+oicbm);
132: // String msg = StringUtils.unescapeFromXML(OscarTools.stripHtml(message.getMessage()));
133:
134: getMainSession().getTransport().sendMessage(
135: getMainSession().getJIDWithHighestPriority(),
136: getMainSession().getTransport().convertIDToJID(
137: uin), msg);
138: }
139: } else if (cmd instanceof WarningNotification) {
140: WarningNotification wn = (WarningNotification) cmd;
141: MiniUserInfo warner = wn.getWarner();
142: if (warner == null) {
143: getMainSession().getTransport().sendMessage(
144: getMainSession().getJIDWithHighestPriority(),
145: getMainSession().getTransport().getJID(),
146: LocaleUtils.getLocalizedString(
147: "gateway.aim.warninganon", "gateway",
148: Arrays.asList(wn.getNewLevel()
149: .toString())),
150: Message.Type.headline);
151: } else {
152: Log
153: .debug("*** " + warner.getScreenname()
154: + " warned you up to "
155: + wn.getNewLevel() + "%");
156: getMainSession().getTransport().sendMessage(
157: getMainSession().getJIDWithHighestPriority(),
158: getMainSession().getTransport().getJID(),
159: LocaleUtils.getLocalizedString(
160: "gateway.aim.warningdirect", "gateway",
161: Arrays.asList(warner.getScreenname(),
162: wn.getNewLevel().toString())),
163: Message.Type.headline);
164: }
165: } else if (cmd instanceof ExtraInfoAck) {
166: ExtraInfoAck eia = (ExtraInfoAck) cmd;
167: List<ExtraInfoBlock> extraInfo = eia.getExtraInfos();
168: if (extraInfo != null) {
169: for (ExtraInfoBlock i : extraInfo) {
170: ExtraInfoData data = i.getExtraData();
171:
172: if (JiveGlobals.getBooleanProperty(
173: "plugin.gateway."
174: + getMainSession().getTransport()
175: .getType() + ".avatars",
176: true)
177: && (data.getFlags() & ExtraInfoData.FLAG_UPLOAD_ICON) != 0
178: && getMainSession().pendingAvatar != null) {
179: Log
180: .debug("OSCAR: Server has indicated that it wants our icon.");
181: request(new UploadIconCmd(ByteBlock
182: .wrap(getMainSession().pendingAvatar)),
183: new SnacRequestAdapter() {
184: public void handleResponse(
185: SnacResponseEvent e) {
186: SnacCommand cmd = e
187: .getSnacCommand();
188: if (cmd instanceof UploadIconAck
189: && getMainSession().pendingAvatar != null) {
190: UploadIconAck iconAck = (UploadIconAck) cmd;
191: if (iconAck.getCode() == UploadIconAck.CODE_DEFAULT
192: || iconAck
193: .getCode() == UploadIconAck.CODE_SUCCESS) {
194: ExtraInfoBlock iconInfo = iconAck
195: .getIconInfo();
196: if (iconInfo == null) {
197: Log
198: .debug("OSCAR: Got icon ack with no iconInfo: "
199: + iconAck);
200: }
201: Log
202: .debug("OSCAR: Successfully set icon.");
203: try {
204: MessageDigest md = MessageDigest
205: .getInstance("MD5");
206: md
207: .update(getMainSession().pendingAvatar);
208: getMainSession()
209: .getAvatar()
210: .setLegacyIdentifier(
211: StringUtils
212: .encodeHex(md
213: .digest()));
214: } catch (NoSuchAlgorithmException ee) {
215: Log
216: .error(
217: "No algorithm found for MD5!",
218: ee);
219: }
220: } else if (iconAck
221: .getCode() == UploadIconAck.CODE_BAD_FORMAT) {
222: Log
223: .debug("OSCAR: Uploaded icon was not in an unaccepted format.");
224: } else if (iconAck
225: .getCode() == UploadIconAck.CODE_TOO_LARGE) {
226: Log
227: .debug("OSCAR: Uploaded icon was too large to be accepted.");
228: } else {
229: Log
230: .debug("OSCAR: Got unknown code from UploadIconAck: "
231: + iconAck
232: .getCode());
233: }
234: getMainSession().pendingAvatar = null;
235: } else if (cmd instanceof SnacError) {
236: Log
237: .debug("Got SnacError while setting icon: "
238: + cmd);
239: }
240: }
241: });
242: }
243: }
244: }
245: } else if (cmd instanceof BuddyStatusCmd) {
246: BuddyStatusCmd bsc = (BuddyStatusCmd) cmd;
247: FullUserInfo info = bsc.getUserInfo();
248: PresenceType pType = PresenceType.available;
249: String vStatus = "";
250: if (info.getAwayStatus()) {
251: pType = PresenceType.away;
252: }
253:
254: if (getMainSession().getTransport().getType().equals(
255: TransportType.icq)
256: && info.getScreenname().matches("/^\\d+$/")) {
257: pType = ((OSCARTransport) getMainSession()
258: .getTransport()).convertICQStatusToXMPP(info
259: .getIcqStatus());
260: }
261:
262: List<ExtraInfoBlock> extraInfo = info.getExtraInfoBlocks();
263: if (extraInfo != null) {
264: for (ExtraInfoBlock i : extraInfo) {
265: ExtraInfoData data = i.getExtraData();
266:
267: if (i.getType() == ExtraInfoBlock.TYPE_AVAILMSG) {
268: ByteBlock msgBlock = data.getData();
269: int len = BinaryTools.getUShort(msgBlock, 0);
270: if (len >= 0) {
271: byte[] msgBytes = msgBlock.subBlock(2, len)
272: .toByteArray();
273: String msg;
274: try {
275: msg = new String(msgBytes, "UTF-8");
276: } catch (UnsupportedEncodingException e1) {
277: continue;
278: }
279: if (msg.length() > 0) {
280: vStatus = msg;
281: }
282: }
283: } else if (i.getType() == ExtraInfoBlock.TYPE_ICONHASH
284: && JiveGlobals.getBooleanProperty(
285: "plugin.gateway."
286: + getMainSession()
287: .getTransport()
288: .getType()
289: + ".avatars", true)) {
290: try {
291: OSCARBuddy oscarBuddy = (OSCARBuddy) getMainSession()
292: .getBuddyManager()
293: .getBuddy(
294: getMainSession()
295: .getTransport()
296: .convertIDToJID(
297: info
298: .getScreenname()));
299: Avatar curAvatar = oscarBuddy.getAvatar();
300: if (curAvatar == null
301: || !curAvatar
302: .getLegacyIdentifier()
303: .equals(
304: StringUtils
305: .encodeHex(i
306: .getExtraData()
307: .getData()
308: .toByteArray()))) {
309: IconRequest req = new IconRequest(info
310: .getScreenname(), i
311: .getExtraData());
312: request(req, new SnacRequestAdapter() {
313: public void handleResponse(
314: SnacResponseEvent e) {
315: SnacCommand cmd = e
316: .getSnacCommand();
317: if (cmd instanceof IconDataCmd) {
318: IconDataCmd idc = (IconDataCmd) cmd;
319: if (idc.getIconData()
320: .getLength() > 0
321: && idc
322: .getIconData()
323: .getLength() != 90) {
324: Log
325: .debug("Got icon data: "
326: + idc);
327: if (getMainSession()
328: .getBuddyManager()
329: .isActivated()) {
330: try {
331: OSCARBuddy oscarBuddy = (OSCARBuddy) getMainSession()
332: .getBuddyManager()
333: .getBuddy(
334: getMainSession()
335: .getTransport()
336: .convertIDToJID(
337: idc
338: .getScreenname()));
339: oscarBuddy
340: .setAvatar(new Avatar(
341: getMainSession()
342: .getTransport()
343: .convertIDToJID(
344: idc
345: .getScreenname()),
346: StringUtils
347: .encodeHex(idc
348: .getIconInfo()
349: .getExtraData()
350: .getData()
351: .toByteArray()),
352: idc
353: .getIconData()
354: .toByteArray()));
355: } catch (NotFoundException ee) {
356: // Apparently we don't care about this contact.
357: } catch (IllegalArgumentException ee) {
358: Log
359: .debug("OSCAR: Got null avatar, ignoring.");
360: }
361: }
362: }
363: }
364: }
365:
366: public void handleTimeout(
367: SnacRequestTimeoutEvent e) {
368: Log
369: .debug("Time out while waiting for icon data.");
370: }
371: });
372: }
373: } catch (NotFoundException ee) {
374: // Apparently we don't care about this contact.
375: }
376: }
377: }
378: }
379: if (getMainSession().getBuddyManager().isActivated()) {
380: try {
381: OSCARBuddy oscarBuddy = (OSCARBuddy) getMainSession()
382: .getBuddyManager()
383: .getBuddy(
384: getMainSession()
385: .getTransport()
386: .convertIDToJID(
387: info
388: .getScreenname()));
389: oscarBuddy.setPresenceAndStatus(pType, vStatus);
390: } catch (NotFoundException ee) {
391: // Apparently we don't care about this contact.
392: Log
393: .debug("OSCAR: Received presense notification for contact we don't care about: "
394: + info.getScreenname());
395: }
396: } else {
397: getMainSession().getBuddyManager().storePendingStatus(
398: getMainSession().getTransport().convertIDToJID(
399: info.getScreenname()), pType, vStatus);
400: }
401: } else if (cmd instanceof BuddyOfflineCmd) {
402: BuddyOfflineCmd boc = (BuddyOfflineCmd) cmd;
403: if (getMainSession().getBuddyManager().isActivated()) {
404: try {
405: OSCARBuddy oscarBuddy = (OSCARBuddy) getMainSession()
406: .getBuddyManager()
407: .getBuddy(
408: getMainSession()
409: .getTransport()
410: .convertIDToJID(
411: boc.getScreenname()));
412: oscarBuddy.setPresence(PresenceType.unavailable);
413: } catch (NotFoundException ee) {
414: // Apparently we don't care about this contact.
415: }
416: } else {
417: getMainSession().getBuddyManager().storePendingStatus(
418: getMainSession().getTransport().convertIDToJID(
419: boc.getScreenname()),
420: PresenceType.unavailable, null);
421: }
422: } else if (cmd instanceof TypingCmd) {
423: TypingCmd tc = (TypingCmd) cmd;
424: String sn = tc.getScreenname();
425:
426: if (tc.getTypingState() == TypingCmd.STATE_TYPING) {
427: getMainSession().getTransport()
428: .sendComposingNotification(
429: getMainSession()
430: .getJIDWithHighestPriority(),
431: getMainSession().getTransport()
432: .convertIDToJID(sn));
433: } else if (tc.getTypingState() == TypingCmd.STATE_PAUSED) {
434: getMainSession().getTransport()
435: .sendComposingPausedNotification(
436: getMainSession()
437: .getJIDWithHighestPriority(),
438: getMainSession().getTransport()
439: .convertIDToJID(sn));
440: } else if (tc.getTypingState() == TypingCmd.STATE_NO_TEXT) {
441: getMainSession().getTransport()
442: .sendChatInactiveNotification(
443: getMainSession()
444: .getJIDWithHighestPriority(),
445: getMainSession().getTransport()
446: .convertIDToJID(sn));
447: }
448: }
449: }
450:
451: protected void handleSnacResponse(SnacResponseEvent e) {
452: Log.debug("OSCAR snac packet response: " + e);
453: SnacCommand cmd = e.getSnacCommand();
454:
455: if (cmd instanceof RateInfoCmd) {
456: RateInfoCmd ric = (RateInfoCmd) cmd;
457: List<RateClassInfo> rateClasses = ric.getRateClassInfos();
458:
459: int[] classes = new int[rateClasses.size()];
460: for (int i = 0; i < rateClasses.size(); i++) {
461: classes[i] = rateClasses.get(i).getRateClass();
462: }
463:
464: request(new RateAck(classes));
465: }
466: }
467:
468: public int[] getSnacFamilies() {
469: return snacFamilies;
470: }
471:
472: protected void setSnacFamilies(int[] families) {
473: this .snacFamilies = families.clone();
474: Arrays.sort(snacFamilies);
475: }
476:
477: protected void setSnacFamilyInfos(Collection<SnacFamilyInfo> infos) {
478: snacFamilyInfos = infos;
479: }
480:
481: protected boolean supportsFamily(int family) {
482: return Arrays.binarySearch(snacFamilies, family) >= 0;
483: }
484:
485: protected void clientReady() {
486: if (!sentClientReady) {
487: sentClientReady = true;
488: request(new ClientReadyCmd(snacFamilyInfos));
489: }
490: }
491:
492: protected SnacRequest dispatchRequest(SnacCommand cmd) {
493: return dispatchRequest(cmd, null);
494: }
495:
496: protected SnacRequest dispatchRequest(SnacCommand cmd,
497: SnacRequestListener listener) {
498: SnacRequest req = new SnacRequest(cmd, listener);
499: dispatchRequest(req);
500: return req;
501: }
502:
503: protected void dispatchRequest(SnacRequest req) {
504: getMainSession().handleRequest(req);
505: }
506:
507: protected SnacRequest request(SnacCommand cmd,
508: SnacRequestListener listener) {
509: SnacRequest req = new SnacRequest(cmd, listener);
510:
511: handleReq(req);
512:
513: return req;
514: }
515:
516: private void handleReq(SnacRequest request) {
517: int family = request.getCommand().getFamily();
518: if (snacFamilies == null || supportsFamily(family)) {
519: // this connection supports this snac, so we'll send it here
520: sendRequest(request);
521: } else {
522: getMainSession().handleRequest(request);
523: }
524: }
525:
526: }
|