/*
* Copyright (c) 2002-2003, The Joust Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* - Neither the name of the Joust Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* File created by keith @ Mar 3, 2003
*
*/
package net.kano.joscar.ssiitem;
import net.kano.joscar.BinaryTools;
import net.kano.joscar.ByteBlock;
import net.kano.joscar.DefensiveTools;
import net.kano.joscar.snaccmd.ssi.SsiItem;
import net.kano.joscar.tlv.MutableTlvChain;
import net.kano.joscar.tlv.Tlv;
import net.kano.joscar.tlv.TlvChain;
import net.kano.joscar.tlv.TlvTools;
/**
* An SSI item object representing a buddy on the user's buddy list. A buddy
* item contains a set of buddy alert flags (for the different types of alerts),
* an alert sound filename, a buddy comment, and (though WinAIM does not yet
* support it) an "alias" or "display name" for the buddy.
* <br>
* <br>
* Note that this class is only used to store data and that <b>changes to this
* object are not reflected on the server</b> without sending the changes to the
* server with a {@link net.kano.joscar.snaccmd.ssi.ModifyItemsCmd
* ModifyItemsCmd}.
*/
public class BuddyItem extends AbstractItemObj implements SsiItemObjectWithId {
/**
* An alert action flag indicating that a window should be popped up when
* the buddy alert is activated.
*/
public static final int MASK_ACTION_POPUP = 0x01;
/**
* An alert action flag indicating that a sound should be played when the
* buddy alert is activated. The sound file is specified in {@link
* #getAlertSound getAlertSound}.
*/
public static final int MASK_ACTION_PLAY_SOUND = 0x02;
/**
* An alert flag indicating that the buddy's alert should be activated when
* he or she signs on.
*/
public static final int MASK_WHEN_ONLINE = 0x01;
/**
* An alert flag indicating that the buddy's alert should be activated when
* he or she comes back from being idle.
*/
public static final int MASK_WHEN_UNIDLE = 0x02;
/**
* An alert flag indicating that the buddy's alert should be activated when
* he or she comes back from being away.
*/
public static final int MASK_WHEN_UNAWAY = 0x04;
/** A TLV type containing the user's "alias," or "display name." */
private static final int TYPE_ALIAS = 0x0131;
/** A TLV type containing the user's "buddy comment." */
private static final int TYPE_COMMENT = 0x013c;
/**
* A TLV type containing the filename of a sound to play when an alert for
* this buddy is activated.
*/
private static final int TYPE_ALERT_SOUND = 0x013e;
/** A TLV type containing a set of buddy alert flags. */
private static final int TYPE_ALERT_FLAGS = 0x13d;
private static final int TYPE_AWAITING_AUTH = 0x0066;
/** The buddy's screenname. */
private final String sn;
/** The ID of the parent group of this buddy. */
private int groupid;
/** The ID of this buddy in its parent group. */
private int id;
/** The buddy's "alias." */
private String alias;
/** The buddy's buddy comment. */
private String comment;
/** A bit mask for what to do when an alert is activated. */
private int alertActionMask;
/** A bit mask for when to activate a buddy alert for this buddy. */
private int alertWhenMask;
/** A sound to play when an alert is activated. */
private String alertSound;
private boolean awaitingAuth;
/**
* Creates a new buddy item object from the given SSI item.
*
* @param item a "buddy" (type {@link SsiItem#TYPE_BUDDY}) SSI item
*/
public BuddyItem(SsiItem item) {
DefensiveTools.checkNull(item, "item");
sn = item.getName();
groupid = item.getParentId();
id = item.getId();
TlvChain chain = TlvTools.readChain(item.getData());
alias = chain.getUtf8String(TYPE_ALIAS);
comment = chain.getUtf8String(TYPE_COMMENT);
alertSound = chain.getString(TYPE_ALERT_SOUND);
Tlv alertTlv = chain.getLastTlv(TYPE_ALERT_FLAGS);
if (alertTlv != null) {
ByteBlock alertMaskData = alertTlv.getData();
alertActionMask = BinaryTools.getUByte(alertMaskData, 0);
alertWhenMask = BinaryTools.getUByte(alertMaskData, 1);
if (alertActionMask == -1) alertActionMask = 0;
if (alertWhenMask == -1) alertWhenMask = 0;
} else {
alertActionMask = 0;
alertWhenMask = 0;
}
awaitingAuth = chain.hasTlv(TYPE_AWAITING_AUTH);
MutableTlvChain extraTlvs = TlvTools.getMutableCopy(chain);
//Why?
//extraTlvs.removeTlvs(TYPE_ALIAS, TYPE_COMMENT, TYPE_ALERT_SOUND,
// TYPE_ALERT_FLAGS, TYPE_AWAITING_AUTH);
addExtraTlvs(extraTlvs);
}
/**
* Creates a buddy item object with the same properties as the given object.
*
* @param other a buddy item object to copy
*/
public BuddyItem(BuddyItem other) {
this(other.sn, other.getGroupId(), other.getId(), other.getAlias(),
other.getBuddyComment(),
other.alertWhenMask, other.alertActionMask, other.alertSound,
other.awaitingAuth, other.copyExtraTlvs());
}
/**
* Creates a new buddy item with the given screenname, group ID, and buddy
* ID. All other fields are set to <code>null</code> or <code>0</code>
* depending on the field's type.
*
* @param sn the buddy's screenname
* @param groupid the ID of the group in which this buddy resides
* @param id the buddy's buddy ID
*/
public BuddyItem(String sn, int groupid, int id) {
this(sn, groupid, id, null, null, 0, 0, null);
}
/**
* Creates a new buddy item object with the given properties. All fields
* other than <code>sn</code>, <code>groupid</code>, and <code>id</code>
* can be either <code>0</code> or <code>null</code> (depending on type) to
* indicate that the given field should not be sent.
*
* @param sn the buddy's screenname
* @param groupid the ID of the group in which this buddy resides
* @param id the buddy's buddy ID
* @param alias an "alias" or "display name" for this buddy (only supported
* by joscar and gaim)
* @param comment a "buddy comment" for this buddy
* @param alertWhenMask a set of bit flags indicating when a buddy alert
* should be activated (see the {@link
* #MASK_WHEN_ONLINE MASK_WHEN_<i>*</i>} constants)
* @param alertActionMask a set of bit flags indicating what should happen
* when a buddy alert is activated (see the {@link
* #MASK_ACTION_POPUP MASK_ACTION_<i>*</i>} constants)
* @param alertSound the name of a sound file to play when an alert is
* activated (normally stored without a full path or an extension,
* like "moo")
*/
public BuddyItem(String sn, int groupid, int id, String alias,
String comment, int alertWhenMask, int alertActionMask,
String alertSound) {
this(sn, groupid, id, alias, comment, alertWhenMask, alertActionMask,
alertSound, false, null);
}
/**
* Creates a new buddy item object with the given properties. All fields
* other than <code>sn</code>, <code>groupid</code>, and <code>id</code>
* can be either <code>0</code> or <code>null</code> (depending on type) to
* indicate that the given field should not be sent.
*
* @param sn the buddy's screenname
* @param groupid the ID of the group in which this buddy resides
* @param id the buddy's buddy ID
* @param alias an "alias" or "display name" for this buddy (only supported
* by joscar and gaim)
* @param comment a "buddy comment" for this buddy
* @param alertWhenMask a set of bit flags indicating when a buddy alert
* should be activated (see the {@link
* #MASK_WHEN_ONLINE MASK_WHEN_<i>*</i>} constants)
* @param alertActionMask a set of bit flags indicating what should happen
* when a buddy alert is activated (see the {@link
* #MASK_ACTION_POPUP MASK_ACTION_<i>*</i>} constants)
* @param alertSound the name of a sound file to play when an alert is
* activated (normally stored without a full path or an extension,
* like "moo")
* @param extraTlvs a set of extra TLV's to store in this item
*/
public BuddyItem(String sn, int groupid, int id, String alias,
String comment, int alertWhenMask, int alertActionMask,
String alertSound, boolean awaitingAuth, TlvChain extraTlvs) {
super(extraTlvs);
DefensiveTools.checkNull(sn, "sn");
DefensiveTools.checkRange(groupid, "groupid", 0);
DefensiveTools.checkRange(id, "id", 0);
DefensiveTools.checkRange(alertWhenMask, "alertWhenMask", 0);
DefensiveTools.checkRange(alertActionMask, "alertActionMask", 0);
this.sn = sn;
this.groupid = groupid;
this.id = id;
this.alias = alias;
this.comment = comment;
this.alertActionMask = alertActionMask;
this.alertWhenMask = alertWhenMask;
this.alertSound = alertSound;
this.awaitingAuth = awaitingAuth;
}
/**
* Returns this buddy's screenname.
*
* @return this buddy's screenname
*/
public final String getScreenname() { return sn; }
/**
* Returns the ID of the group in which this buddy resides.
*
* @return the ID of this buddy's parent group
*/
public final synchronized int getGroupId() { return groupid; }
/**
* Returns the ID of this buddy in its parent group.
*
* @return this buddy's "buddy ID"
*/
public final synchronized int getId() { return id; }
/**
* Returns this buddy's "alias" or "display name." This is only supported by
* some clients.
*
* @return this buddy's "alias" or "display name," or <code>null</code> if
* this buddy has no alias
*/
public synchronized final String getAlias() { return alias; }
/**
* Returns this buddy's "buddy comment." A buddy comment is a string of text
* normally edited by the user to store some brief information about the
* buddy. WinAIM puts a limit of 84 characters on this value, but there is
* no hard (server-side) limit. Note that this value is stored as UTF-8
* text, although some clients may read it as ASCII.
*
* @return this buddy's "buddy comment," or <code>null</code> if this buddy
* has no buddy comment
*/
public synchronized final String getBuddyComment() { return comment; }
/**
* Returns a bit mask describing what should happen when a buddy alert is
* activated for this buddy. Normally a combination of any of {@link
* #MASK_ACTION_POPUP} and {@link #MASK_ACTION_PLAY_SOUND}. One can test
* for a given value using code resembling the following:
* <pre>
if ((buddyItem.getAlertActionMask() & BuddyItem.MASK_ACTION_POPUP) != 0) {
// popup alert box
}
* </pre>
*
* @return a set of bit flags describing what should happen when this
* buddy's buddy alert is activated
*/
public synchronized final int getAlertActionMask() {
return alertActionMask;
}
/**
* Returns a bit mask describing when a buddy alert for this user should be
* activated. Normally a combination of any of {@link #MASK_WHEN_ONLINE},
* {@link #MASK_WHEN_UNAWAY}, and {@link #MASK_WHEN_UNIDLE}. One can test
* for a given value using code resembling the following:
* <pre>
if ((buddyItem.getAlertWhenMask() & BuddyItem.MASK_WHEN_ONLINE) != 0) {
System.out.println("An alert should be triggered when "
+ buddyItem.getScreenname() + " signs on!");
}
* </pre>
*
* @return a set of bit flags describing when a buddy alert for this user
* should be activated
*/
public synchronized final int getAlertWhenMask() { return alertWhenMask; }
/**
* Returns the name of a sound file that should be played when this buddy's
* alert is activated. The sound should only be played if this buddy's
* {@linkplain #getAlertActionMask alert action mask} contains {@link
* #MASK_ACTION_PLAY_SOUND}. The filename is normally stored without a full
* path or file extension, like <code>"moo"</code> to represent
* <code>C:\Program Files\AIM95\Sounds\moo.wav</code>.
*
* @return the name of a sound file that should be played when this buddy's
* alert is activated, or <code>null</code> if none is stored for
* this buddy
*
* @see #getAlertActionMask
*/
public synchronized final String getAlertSound() { return alertSound; }
public synchronized boolean isAwaitingAuth() { return awaitingAuth; }
public synchronized void setGroupid(int groupid) {
this.groupid = groupid;
}
public synchronized void setId(int id) {
this.id = id;
}
/**
* Sets this buddy's "alias" or "display name."
*
* @param alias this buddy's new "alias" or "display name," or
* <code>null</code> to erase this buddy's alias
*/
public synchronized final void setAlias(String alias) {
this.alias = alias;
}
/**
* Sets this buddy's "buddy comment."
*
* @param comment this buddy's new "buddy comment," or <code>null</code> to
* erase this buddy's comment
*/
public synchronized final void setComment(String comment) {
this.comment = comment;
}
/**
* Sets this buddy's "alert action mask." Normally a combination of any of
* {@link #MASK_ACTION_POPUP} and {@link #MASK_ACTION_PLAY_SOUND}.
*
* @param alertActionMask a new "alert action mask" for this buddy
*/
public synchronized final void setAlertActionMask(int alertActionMask) {
this.alertActionMask = alertActionMask;
}
/**
* Sets this buddy's "alert criteria mask." Normally a combination of any
* of {@link #MASK_WHEN_ONLINE}, {@link #MASK_WHEN_UNAWAY}, and {@link
* #MASK_WHEN_UNIDLE}.
*
* @param alertWhenMask a new "alert criteria mask" for this buddy
*/
public synchronized final void setAlertWhenMask(int alertWhenMask) {
this.alertWhenMask = alertWhenMask;
}
/**
* Sets the "alert sound" filename for this buddy. This is normally stored
* without full path or file extension, like <code>"moo"</code> to represent
* <code>C:\Program Files\AIM95\Sounds\moo.wav</code>.
*
* @param alertSound the buddy's "alert sound filename," or
* <code>null</code> to erase any alert sound file currently stored
* in this item
*/
public synchronized final void setAlertSound(String alertSound) {
this.alertSound = alertSound;
}
public synchronized void setAwaitingAuth(boolean awaitingAuth) {
this.awaitingAuth = awaitingAuth;
}
public synchronized SsiItem toSsiItem() {
MutableTlvChain chain = (MutableTlvChain)copyExtraTlvs();
if (alias != null) {
chain.replaceTlv(Tlv.getUtf8Instance(TYPE_ALIAS, alias));
} else {
chain.removeTlvs(TYPE_ALIAS);
}
if (comment != null) {
chain.replaceTlv(Tlv.getUtf8Instance(TYPE_COMMENT, comment));
} else {
chain.removeTlvs(TYPE_COMMENT);
}
if (alertActionMask != 0 || alertWhenMask != 0) {
// this is the most elegant statement I've ever written.
ByteBlock block = ByteBlock.wrap(new byte[] {
BinaryTools.getUByte(alertActionMask)[0],
BinaryTools.getUByte(alertWhenMask)[0]
});
chain.replaceTlv(new Tlv(TYPE_ALERT_FLAGS, block));
} else {
chain.removeTlvs(TYPE_ALERT_FLAGS);
}
if (alertSound != null) {
chain.replaceTlv(Tlv.getStringInstance(TYPE_ALERT_SOUND, alertSound));
} else {
chain.removeTlvs(TYPE_ALERT_SOUND);
}
if (awaitingAuth) {
chain.replaceTlv(new Tlv(TYPE_AWAITING_AUTH));
} else {
chain.removeTlvs(TYPE_AWAITING_AUTH);
}
return new SsiItem(sn, groupid, id, SsiItem.TYPE_BUDDY,
ByteBlock.createByteBlock(chain));
}
public synchronized String toString() {
boolean popupAlert = (alertActionMask & MASK_ACTION_POPUP) != 0;
boolean playSound = (alertActionMask & MASK_ACTION_PLAY_SOUND) != 0;
boolean alertOnSignon = (alertWhenMask & MASK_WHEN_ONLINE) != 0;
boolean alertOnUnidle = (alertWhenMask & MASK_WHEN_UNIDLE) != 0;
boolean alertOnBack = (alertWhenMask & MASK_WHEN_UNAWAY) != 0;
return "BuddyItem for " + sn + " (buddy 0x" + Integer.toHexString(id)
+ " in group 0x" + Integer.toHexString(groupid) + "): "
+ (awaitingAuth ? "awaiting auth - " : "")
+ "alias=" + alias + ", comment=\"" + comment + "\", alerts: "
+ (popupAlert ? "[popup alert] " : "")
+ (playSound ? "[play " + alertSound + "] " : "")
+ (alertOnSignon ? "[on signon] " : "")
+ (alertOnUnidle ? "[on unidle] " : "")
+ (alertOnBack ? "[on unaway] " : "");
}
}
|