/*******************************************************************************
Jimm - Mobile Messaging - J2ME ICQ clone
Copyright (C) 2003-05 Jimm Project
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 2
of the License, or (at your option) any later version.
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************
File: src/jimm/comm/UpdateContactListAction.java
Version: ###VERSION### Date: ###DATE###
Author(s): Manuel Linsmayer, Andreas Rossbacher
*******************************************************************************/
// #sijapp cond.if protocols_ICQ is "true" #
package protocol.icq.action;
import java.util.Vector;
import jimm.*;
import jimm.comm.*;
import protocol.*;
import protocol.icq.*;
import protocol.icq.packet.*;
import jimm.modules.*;
public class UpdateContactListAction extends IcqAction {
/* Action states */
private static final int STATE_ERROR = -1;
private static final int STATE_RENAME = 1;
private static final int STATE_COMPLETED = 3;
private static final int STATE_MOVE1 = 4;
private static final int STATE_MOVE2 = 5;
private static final int STATE_MOVE3 = 6;
private static final int STATE_ADD = 18;
private static final int STATE_DELETE_CONTACT = 7;
private static final int STATE_DELETE_GROUP = 9;
private static final int STATE_ADD_GROUP = 11;
private static final int STATE_COMMIT = 13;
/* Action types */
public static final int ACTION_ADD = 1;
public static final int ACTION_DEL = 2;
public static final int ACTION_RENAME = 3;
public static final int ACTION_MOVE = 4;
private static final int ACTION_ADD_REQ_AUTH = 5;
public static final int ACTION_MOVE_REQ_AUTH = 6;
private static final int ACTION_MOVE_FROM_NIL = 7;
/* Timeout */
public static final int TIMEOUT = 10; // seconds
/** ************************************************************************* */
/* Contact item */
private Contact contact;
private int contactFromId;
private int contactToId;
/* Group item */
private Group gItem;
private Group newGItem;
private int action;
private int state;
private int errorCode;
/**
* Removes or adds given contact
*/
public UpdateContactListAction(Icq icq, Contact cItem, int _action) {
this.action = _action;
this.contact = cItem;
this.contactFromId = ((IcqContact)cItem).getContactId();
this.gItem = icq.getGroup(cItem);
}
/**
* Removes or adds given group
*/
public UpdateContactListAction(Group cItem, int _action) {
this.action = _action;
this.contact = null;
this.gItem = cItem;
}
/**
* Move contact
*/
public UpdateContactListAction(Contact cItem, Group oldGroup, Group newGroup) {
this.contact = cItem;
this.contactFromId = ((IcqContact)cItem).getContactId();
this.gItem = oldGroup;
this.newGItem = newGroup;
action = ACTION_MOVE;
if ((null != gItem) && ("Not In List".equals(gItem.getName()))) {
action = ACTION_MOVE_FROM_NIL;
}
}
private void addItem() throws JimmException {
byte[] buf;
if (null == contact) {
buf = packGroup(gItem);
state = STATE_ADD_GROUP;
} else {
gItem = getIcq().getGroup(contact);
contactToId = getIcq().createRandomId();
buf = packContact(contact, contactToId, gItem.getId(), action == ACTION_ADD_REQ_AUTH);
state = STATE_ADD;
}
sendSsiPacket(SnacPacket.CLI_ROSTERADD_COMMAND, buf);
}
private void sendSsiPacket(int cmd, byte[] buf) throws JimmException {
sendPacket(new SnacPacket(SnacPacket.SSI_FAMILY,
cmd, getConnection().getNextCounter(), buf));
}
public void init() throws JimmException {
byte[] buf;
if (ACTION_RENAME != action) {
/* Send a CLI_ADDSTART packet */
transactionStart();
}
switch (action) {
/* Send a CLI_ROSTERUPDATE packet */
case ACTION_RENAME:
buf = (null != contact) ? packContact(contact, contactFromId, contact.getGroupId(), false) : packGroup(gItem);
sendSsiPacket(SnacPacket.CLI_ROSTERUPDATE_COMMAND, buf);
state = STATE_RENAME;
break;
/* Send CLI_ROSTERADD packet */
case ACTION_ADD:
case ACTION_ADD_REQ_AUTH:
addItem();
break;
/* Send a CLI_ROSTERDELETE packet */
case ACTION_MOVE_FROM_NIL:
case ACTION_DEL:
if (null != contact) {
buf = packContact(contact, contactFromId, gItem.getId(), false);
this.state = STATE_DELETE_CONTACT;
} else {
buf = packGroup(gItem);
this.state = STATE_DELETE_GROUP;
}
sendSsiPacket(SnacPacket.CLI_ROSTERDELETE_COMMAND, buf);
break;
/* Move contact between groups (like Miranda does) */
case ACTION_MOVE:
sendSsiPacket(SnacPacket.CLI_ROSTERUPDATE_COMMAND,
packContact(contact, contactFromId, gItem.getId(), false));
this.state = STATE_MOVE1;
break;
}
active();
}
private boolean processPaket(Packet packet) throws JimmException {
/* Watch out for SRV_UPDATEACK packet type */
SnacPacket snacPacket;
if (packet instanceof SnacPacket) {
snacPacket = (SnacPacket) packet;
} else {
return false;
}
if (SnacPacket.SSI_FAMILY != snacPacket.getFamily()) {
return false;
}
if (SnacPacket.CLI_ROSTERDELETE_COMMAND == snacPacket.getCommand()) {
ArrayReader buf = snacPacket.getReader();
int length = buf.getWordBE();
String uin = StringUtils.byteArrayToAsciiString(
buf.getArray(length), 0, length);
return (null != contact) && uin.equals(contact.getUserId());
}
if (SnacPacket.SRV_UPDATEACK_COMMAND != snacPacket.getCommand()) {
return false;
}
// Check error code, see ICQv8 specification
int retCode = snacPacket.getReader().getWordBE();
switch (retCode) {
case 0x002: errorCode = 154; break;
case 0x003: errorCode = 155; break;
case 0x00A: errorCode = 156; break;
case 0x00C: errorCode = 157; break;
case 0x00D: errorCode = 158; break;
}
if ((0x00A == retCode) && (ACTION_MOVE_FROM_NIL == action)) {
errorCode = 0;
state = STATE_COMPLETED;
contact.setGroup(newGItem);
contact.setTempFlag(false);
getIcq().addContact(contact);
return true;
}
if ((0x00A == retCode) && (ACTION_DEL == action)) {
errorCode = 0;
state = STATE_COMPLETED;
return true;
}
if (0 != errorCode) {
// #sijapp cond.if modules_DEBUGLOG is "true" #
DebugLog.println("update action = " + action
+ " state = " + state
+ " ret code = " + retCode);
// #sijapp cond.end #
// non critical
getIcq().showException(new JimmException(errorCode, 0));
state = STATE_ERROR;
return true;
}
switch (state) {
case STATE_ADD_GROUP:
if (0 < getIcq().getGroupItems().size()) {
sendGroupsList();
this.state = STATE_COMMIT;
} else {
transactionCommit();
this.state = STATE_COMPLETED;
}
break;
case STATE_ADD:
if (0 == retCode) {
sendGroup(gItem);
((IcqContact)contact).setContactId(contactToId);
contact.setBooleanValue(IcqContact.CONTACT_NO_AUTH, action == ACTION_ADD_REQ_AUTH);
this.state = STATE_COMPLETED;
}
transactionCommit();
if ((0 != retCode) && (action == ACTION_ADD)) {
action = ACTION_ADD_REQ_AUTH;
transactionStart();
addItem();
}
break;
case STATE_RENAME:
this.state = STATE_COMPLETED;
break;
/* STATE_MOVE */
case STATE_MOVE1:
sendSsiPacket(SnacPacket.CLI_ROSTERDELETE_COMMAND,
packContact(contact, contactFromId, gItem.getId(), false));
this.state = STATE_MOVE2;
break;
case STATE_MOVE2:
contactToId = getIcq().createRandomId();
sendSsiPacket(SnacPacket.CLI_ROSTERADD_COMMAND,
packContact(contact, contactToId, newGItem.getId(), action == ACTION_MOVE_REQ_AUTH));
this.state = STATE_MOVE3;
break;
case STATE_MOVE3:
((IcqContact)contact).setContactId(contactToId);
sendGroup(newGItem);
this.state = STATE_COMMIT;
break;
case STATE_DELETE_CONTACT:
sendGroup(gItem);
this.state = STATE_COMMIT;
break;
case STATE_DELETE_GROUP:
sendGroupsList();
this.state = STATE_COMMIT;
break;
case STATE_COMMIT:
transactionCommit();
this.state = STATE_COMPLETED;
break;
}
active();
return true;
}
private void transactionStart() throws JimmException {
sendSsiPacket(SnacPacket.CLI_ADDSTART_COMMAND, new byte[0]);
}
private void transactionCommit() throws JimmException {
sendSsiPacket(SnacPacket.CLI_ADDEND_COMMAND, new byte[0]);
}
private void sendGroupsList() throws JimmException {
sendSsiPacket(SnacPacket.CLI_ROSTERUPDATE_COMMAND, packGroups());
}
private void sendGroup(Group group) throws JimmException {
sendSsiPacket(SnacPacket.CLI_ROSTERUPDATE_COMMAND, packGroup(group));
}
/* Forwards received packet, returns true if packet was consumed */
public boolean forward(Packet packet) throws JimmException {
boolean result = processPaket(packet);
if (result && (0 != errorCode)) {
if ((ACTION_MOVE != action) && (ACTION_RENAME != action)) {
transactionCommit();
}
active();
}
return result;
}
public boolean isCompleted() {
return (this.state == UpdateContactListAction.STATE_COMPLETED);
}
public boolean isError() {
if (this.state == ConnectAction.STATE_ERROR) return true;
if (isNotActive(TIMEOUT) || (errorCode != 0)) {
this.state = ConnectAction.STATE_ERROR;
}
return (this.state == ConnectAction.STATE_ERROR);
}
private byte[] packContact(Contact cItem, int contactId, int groupId, boolean auth) {
OutStream stream = new OutStream();
stream.writeLenAndUtf8String(cItem.getUserId());
stream.writeWordBE(groupId);
stream.writeWordBE(contactId);//getContactId(cItem));
stream.writeWordBE(0); // Type (Buddy record)
/* Additional data */
OutStream addData = new OutStream();
/* TLV(0x0131) - name */
if ((ACTION_DEL != action) && (ACTION_MOVE_FROM_NIL != action)) {
addData.writeWordBE(0x0131);
addData.writeLenAndUtf8String(cItem.getName());
}
// /* Server-side additional data */
//if (contact.ssData != null) {
// Util.writeByteArray(addData, contact.ssData);
//}
/* TLV(0x0066) - you are awaiting authorization for this buddy */
if (auth) {
addData.writeTLV(0x0066, null);
}
/* Append additional data to stream */
stream.writeWordBE(addData.size());
stream.writeByteArray(addData.toByteArray());
// Util.showBytes(stream.toByteArray());
return stream.toByteArray();
}
private byte[] packGroup(Group gItem) {
OutStream stream = new OutStream();
stream.writeLenAndUtf8String(gItem.getName());
stream.writeWordBE(gItem.getId()); // Group Id
stream.writeWordBE(0); // Id
stream.writeWordBE(1); // Type (Group)
/* Contact items */
Vector items = getIcq().getContacts(gItem);
int size = items.size();
if (size != 0) {
/* Length of the additional data */
stream.writeWordBE(size * 2 + 4);
/* TLV(0x00C8) */
stream.writeWordBE(0x00c8);
stream.writeWordBE(size * 2);
for (int i = 0; i < size; ++i) {
IcqContact item = (IcqContact)items.elementAt(i);
stream.writeWordBE(item.getContactId());
}
} else {
stream.writeWordBE(0);
}
return stream.toByteArray();
}
private byte[] packGroups() {
OutStream stream = new OutStream();
Vector gItems = getIcq().getGroupItems();
int size = gItems.size();
stream.writeLenAndUtf8String("");// Master Group Name
stream.writeWordBE(0); // Group Id
stream.writeWordBE(0); // Id
stream.writeWordBE(1);
stream.writeWordBE(size * 2 + 4);
stream.writeWordBE(0xc8);
stream.writeWordBE(size * 2);
for (int i = 0; i < size; ++i) {
stream.writeWordBE(((Group)gItems.elementAt(i)).getId());
}
return stream.toByteArray();
}
}
// #sijapp cond.end #