net.sf.jml.message.p2p.MsnP2PMessage.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jml.message.p2p.MsnP2PMessage.java

Source

/*
 * Copyright 2004-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sf.jml.message.p2p;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import net.sf.cindy.util.ByteBufferUtils;
import net.sf.jml.MsnContact;
import net.sf.jml.MsnProtocol;
import net.sf.jml.impl.AbstractMessenger;
import net.sf.jml.message.MessageConstants;
import net.sf.jml.message.MsnMimeMessage;
import net.sf.jml.protocol.MsnSession;
import net.sf.jml.protocol.outgoing.OutgoingMSG;
import net.sf.jml.util.Charset;
import net.sf.jml.util.JmlConstants;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Msn P2P message. have a binary header. See:
 * <a href="http://zoronax.bot2k3.net/msn6/msnp9/msnslp_p2p.html">http://zoronax.bot2k3.net/msn6/msnp9/msnslp_p2p.html</a> and
 * <a href="http://siebe.bot2k3.net/docs/?url=binheaders.html">http://siebe.bot2k3.net/docs/?url=binheaders.html</a>.
 * 
 * @author Roger Chen
 * @author Angel Barragn Chacn
 */
public abstract class MsnP2PMessage extends MsnMimeMessage {

    /**
     * Loger for the class.
     */
    private static final Log log = LogFactory.getLog(MsnSession.class);

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Creates a new P2P message.
     */
    public MsnP2PMessage() {
        setContentType(MessageConstants.CT_P2P);
    }

    ////////////////////////////////////////////////////////////////////////////

    @Override
    protected void parseMessage(byte[] message) {
        ByteBuffer split = Charset.encode(JmlConstants.LINE_SEPARATOR + JmlConstants.LINE_SEPARATOR);
        int pos = ByteBufferUtils.indexOf(ByteBuffer.wrap(message), split);

        // header
        String header = pos == -1 ? Charset.decode(message) : Charset.decode(message, 0, pos);
        headers.parseString(header);

        // binaryHeader
        pos += split.remaining();
        binaryHeader.put(message, pos, BINARY_HEADER_LEN);
        binaryHeader.flip();

        // body
        pos += BINARY_HEADER_LEN;
        parseP2PBody(ByteBuffer.wrap(message, pos, message.length - pos - BINARY_FOOTER_LEN));

        // binaryFoot
        binaryFooter.put(message, message.length - BINARY_FOOTER_LEN, BINARY_FOOTER_LEN);
        binaryFooter.flip();
    }

    /**
     * @see MsnMimeMessage#toOutgoingMsg(MsnProtocol)
     */
    @Override
    public OutgoingMSG[] toOutgoingMsg(MsnProtocol protocol) {
        OutgoingMSG message = new OutgoingMSG(protocol);
        message.setMsgType(OutgoingMSG.TYPE_MSNC1);

        byte[] mimeMessageHeader = Charset.encodeAsByteArray(toString());

        byte[] body = bodyToMessage();
        if (body == null) {
            body = new byte[0];
        }

        ByteBuffer msg = ByteBuffer
                .allocate(mimeMessageHeader.length + BINARY_HEADER_LEN + body.length + BINARY_FOOTER_LEN);

        msg.put(mimeMessageHeader);
        msg.put(binaryHeader);
        msg.put(body);
        msg.put(binaryFooter);
        message.setMsg(msg.array());
        return new OutgoingMSG[] { message };
    }

    @Override
    protected void messageReceived(MsnSession session, MsnContact contact) {

        // Log the message
        //      log.info("Received P2P message\n" + toDebugString());

        // Check for current transmision from this client.
        DisplayPictureDuel duel = session.getMessenger().getDisplayPictureDuelManager().get(this.getField7());
        if (duel != null) {
            duel.process(this, contact);
        }

        // It is not a transmision from this client so may be a retrieval
        else {
            ((AbstractMessenger) session.getMessenger()).fireP2PMessageReceived(session.getSwitchboard(), this,
                    contact);
        }
    }

    /**
     * Parse the body part of this P2P message.
     * 
     * @param buffer Buffer with the body to be parsed.
     */
    protected abstract void parseP2PBody(ByteBuffer buffer);

    /**
     * Retrieve the body part for this P2P message.
     * 
     * @return Binary content for the body part of this P2P message.
     */
    protected abstract byte[] bodyToMessage();

    /**
     * Creates a debug representation for this P2P message.
     * @return String representation for the message.
     */
    public String toDebugString() {

        // Create a buffer
        StringBuffer result = new StringBuffer();

        // Add the message and headers
        result.append(toString());

        // Add the binary headers
        result.append("===================\n");
        result.append("=  Binary Headers =\n");
        result.append("===================\n");
        result.append("SessionID: ").append(getSessionId()).append('\n');
        result.append("Identifier: ").append(getIdentifier()).append(" (").append(toHex(getIdentifier()))
                .append(")\n");
        result.append("Data Offset: ").append(getOffset()).append('\n');
        result.append("Data Total Size: ").append(getTotalLength()).append('\n');
        result.append("Message Length: ").append(getCurrentLength()).append('\n');
        result.append("Flag: ").append(toHex(getFlag())).append('\n');
        result.append("Ack Identifier: ").append(getField7()).append(" (").append(toHex(getField7())).append(")\n");
        result.append("Ack Unique  ID: ").append(getField8()).append(" (").append(toHex(getField8())).append(")\n");
        result.append("Ack Data  Size: ").append(getField9()).append('\n');

        // Add the body
        result.append("===================\n");
        result.append("=       Body      =\n");
        result.append("===================\n");
        result.append(toDebugBody());

        // Add the binary footer
        result.append("===================\n");
        result.append("=  Binary Footer  =\n");
        result.append("===================\n");
        result.append("AppID: ").append(getAppId()).append('\n');

        // Return the result
        return result.toString();
    }

    private String toHex(long value) {
        return "0x" + BigInteger.valueOf(value).toString(16);
    }

    /**
     * Retrieves a String representation of the body for this message.
     * 
     * @return String value.
     */
    protected String toDebugBody() {

        // Create the buffer
        StringBuffer buffer = new StringBuffer();

        // Add the body
        byte[] body = bodyToMessage();
        if (body == null) {
            return "";
        }
        buffer.append(Charset.decode(body));

        // Return the buffer
        return buffer.toString();

    }

    ////////////////////////////////////////////////////////////////////////////
    //                                                                        //
    //                     Headers for the MSG message                        //
    //                                                                        //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * P2P message destination header name.
     */
    protected static final String KEY_P2P_DEST = "P2P-Dest";

    /**
     * Retrieve the P2P destination header value.
     * 
     * @return Value for the destination of this P2P message.
     */
    public String getP2PDest() {
        return headers.getProperty(KEY_P2P_DEST);
    }

    /**
     * Sets the destination for this P2P message.
     * 
     * @param dest New destination for this P2P message.
     */
    public void setP2PDest(String dest) {
        headers.setProperty(KEY_P2P_DEST, dest);
    }

    ////////////////////////////////////////////////////////////////////////////
    //                                                                        //
    //                           Binary Header Fields                         //
    //                                                                        //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Length of the binary header for the P2P messages.
     */
    protected static final int BINARY_HEADER_LEN = 48;

    /**
     * Buffer for the binary header of this P2P message.
     */
    private final ByteBuffer binaryHeader = ByteBuffer.allocate(BINARY_HEADER_LEN).order(ByteOrder.LITTLE_ENDIAN);

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the session identifier for this P2P message.
     * 
     * @return Session identifier. 
     */
    public int getSessionId() {
        return binaryHeader.getInt(0);
    }

    /**
     * Sets the session identifier for this P2P message.
     * 
     * @param sessionId New session identifier.
     */
    public void setSessionId(int sessionId) {
        binaryHeader.putInt(0, sessionId);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the message identifier for this P2P message.
     * 
     * @return Message identifier.
     */
    public int getIdentifier() {
        return binaryHeader.getInt(4);
    }

    /**
     * Sets the new message identifier for this P2P message.
     * 
     * @param identifier New message identifier.
     */
    public void setIdentifier(int identifier) {
        binaryHeader.putInt(4, identifier);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the data offset in this P2P message.
     * 
     * @return data offset. If this is a data message, this fiel has the offset 
     * of the sending data with respect to the total data.
     */
    public long getOffset() {
        return binaryHeader.getLong(8);
    }

    /**
     * Sets the offset of the transmited data for this P2P message.
     * 
     * @param offset New offset for the message.
     */
    public void setOffset(long offset) {
        binaryHeader.putLong(8, offset);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the total length of the data (MsnObject) to be transmited.
     * 
     * @return Total amount of bytes to be transmited for the MsnObject.
     */
    public long getTotalLength() {
        return binaryHeader.getLong(16);
    }

    /**
     * Sets the total amount of data to be transmited.
     * 
     * @param totalLength New total amount of data to be transmited for the 
     * MsnObject.
     */
    public void setTotalLength(long totalLength) {
        binaryHeader.putLong(16, totalLength);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the current length of this message.
     * 
     * @return Current length for this message.
     */
    public int getCurrentLength() {
        return binaryHeader.getInt(24);
    }

    /**
     * Sets the current length for this message.
     * 
     * @param currentLength New current length for this message.
     */
    public void setCurrentLength(int currentLength) {
        binaryHeader.putInt(24, currentLength);
    }

    ////////////////////////////////////////////////////////////////////////////

    protected static final int FLAG_NONE = 0x00;
    protected static final int FLAG_ACK = 0x02;
    protected static final int FLAG_BYE_ACK = 0x40;
    protected static final int FLAG_DATA = 0x20;
    protected static final int FLAG_BYE = 0x80;

    /**
     * Retrieves the flag value for this P2P message.
     * 
     * @return Type of message.
     */
    public int getFlag() {
        return binaryHeader.getInt(28);
    }

    /**
     * Sets the new flag value for this P2P message.
     * 
     * @param flag Type of message.
     */
    public void setFlag(int flag) {
        binaryHeader.putInt(28, flag);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the Acknowledged identifier for this P2P message.
     * 
     * @return the identifier.
     */
    public int getField7() {
        return binaryHeader.getInt(32);
    }

    /**
     * Sets the Acknowledged identifier for this message.
     * 
     * @param field7 The identifier.
     */
    public void setField7(int field7) {
        binaryHeader.putInt(32, field7);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the Acknowledged unique ID for this message.
     * 
     * @return the identifier.
     */
    public int getField8() {
        return binaryHeader.getInt(36);
    }

    /**
     * Sets the Acknowledged unique ID for this message.
     * 
     * @param field8 The identifier.
     */
    public void setField8(int field8) {
        binaryHeader.putInt(36, field8);
    }

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the Acknowledged data size for this P2P message.
     * 
     * @return The data size.
     */
    public long getField9() {
        return binaryHeader.getLong(40);
    }

    /**
     * Sets the Acknowledged data size for this P2P message.
     * 
     * @param field9 The size.
     */
    public void setField9(long field9) {
        binaryHeader.putLong(40, field9);
    }

    ////////////////////////////////////////////////////////////////////////////
    //                                                                        //
    //                           Binary Footer Fields                         //
    //                                                                        //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Binary footer length
     */
    protected static final int BINARY_FOOTER_LEN = 4;

    /**
     * Buffer for the binary footer of this P2P message.
     */
    private final ByteBuffer binaryFooter = ByteBuffer.allocate(BINARY_FOOTER_LEN);

    ////////////////////////////////////////////////////////////////////////////

    /**
     * Retrieves the application identifier for this P2P message.
     * 
     * @return Identifier.
     */
    public int getAppId() {
        return binaryFooter.getInt(0);
    }

    /**
     * Sets the application identifier for this P2P message.
     * 
     * @param appId New application identifier.
     */
    public void setAppId(int appId) {
        binaryFooter.putInt(0, appId);
    }

}