⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 irobex.java

📁 JRed is a 100% Java implementation of the IrDA infrared communications protocols. JRed lets you beam
💻 JAVA
字号:
/*
**************************************************************************
** $Header: /cvsroot/jred/jred/src/com/synchrona/jred/IrOBEX.java,v 1.2 2000/07/29 20:29:29 mpatters Exp $
**
** Copyright (C) 2000 Synchrona, Inc. All rights reserved.
**
** This file is part of JRed, a 100% Java implementation of the IrDA
** infrared communications protocols.
**
** This file may be distributed under the terms of the Synchrona Public
** License as defined by Synchrona, Inc. and appearing in the file
** LICENSE included in the packaging of this file. The Synchrona Public
** License is based on the Q Public License as defined by Troll Tech AS
** of Norway; it differs only in its use of the courts of Florida, USA
** rather than those of Oslo, Norway.
**************************************************************************
*/
package com.synchrona.jred;

import com.synchrona.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

/**
 * Implements the Infrared Data Association (IrDA) Object Exchange (OBEX)
 * protocol.
 */
public class IrOBEX {
	public static final byte IROBEX_FLAGS              = (byte) 0x00;
	public static final byte IROBEX_VERSION            = (byte) 0x10;
	public static final byte OPCODE_CONNECT_REQUEST    = (byte) 0x00;
	public static final byte OPCODE_DISCONNECT_REQUEST = (byte) 0x01;
	public static final byte OPCODE_PUT_REQUEST        = (byte) 0x02;
	public static final int  STATE_READY               = 37;
	public static final int  STATE_RECEIVING           = 51;

	private byte []               m_ayPacket      = null;
	private boolean               m_bDone         = false;
	private File                  m_inbox         = null;
	private Log                   m_log           = null;
	private int                   m_nPacketLength = 0;
	private int                   m_nState        = STATE_READY;
	private ByteArrayOutputStream m_outStream     = null;
	private Hashtable             m_params        = new Hashtable();

	public IrOBEX(Log log) {
		m_log = log;

		// We hope the user defined an inbox directory. If not, we'll
		// try to use the current directory. If that doesn't work, we
		// won't be able to save anything.
		String strInbox = System.getProperty("IrOBEX.InboxDirectory");
		if ( null == strInbox ) {
			m_log.warn("IrOBEX", "IrOBEX.InboxDirectory was not defined, using ./inbox instead.");
			strInbox = "." + File.separator + "inbox";
		}

		m_inbox = new File(strInbox);

		boolean bUseCurrent = false;
		if ( m_inbox.exists() ) {
			if ( !m_inbox.isDirectory() ) {
				m_log.error("IrOBEX", "IrOBEX.InboxDirectory (" + strInbox + ") exists but is not a directory...using current instead.");
				bUseCurrent = true;
			}
		} else {
			// If mkdir() fails (most likely because of permissions), we'll
			// have to use the current directory.
			m_log.warn("IrOBEX", "Creating a new inbox directory: \"" + strInbox + "\"");
			bUseCurrent = !m_inbox.mkdir();
			if ( bUseCurrent ) {
				m_log.error("IrOBEX", "Could not create IrOBEX.InboxDirectory (" + strInbox + ")...using current instead.");
			} else if ( !m_inbox.canWrite() ) {
				m_log.error("IrOBEX", "Cannot write to  IrOBEX.InboxDirectory (" + strInbox + ")...using current instead.");
			}
		}

		if ( bUseCurrent ) {
			m_inbox = new File(".");
			if ( m_inbox.canWrite() ) {
				// If we weren't able to write to the current directory,
				// we just can't save anymore.
				m_log.error("IrOBEX", "Cannot write to current directory. IrOBEX will not be able to save any beamed objects.");
			}
		}
	}

	public void serviceRequest(TinyTP tinyTP, IrLMPConnection conn, byte [] ayData, int nOffset, int nLength) throws Exception {
		if ( STATE_READY == m_nState ) {
			m_outStream     = new ByteArrayOutputStream();
			m_nPacketLength = (0x0000FF00 & (ayData[nOffset + 1] << 8)) | (0x000000FF & ayData[nOffset + 2]);
			m_log.debug("IrOBEX", "(serviceRequest) starting new packet: opcode=" + ayData[nOffset] + " length=" + m_nPacketLength);
		} else {
			m_log.debug("IrOBEX",
				"(serviceRequest) appending to current packet / length: " + nLength);
		}

		m_outStream.write(ayData, nOffset, nLength);
		m_bDone = (m_nPacketLength == m_outStream.size()); 
		m_log.debug("IrOBEX", "m_bDone: " + m_bDone);

		if ( m_bDone ) {
			m_log.debug("IrOBEX", "(serviceRequest) packet complete " + m_outStream.size());
			m_nState   = STATE_READY;
			m_ayPacket = m_outStream.toByteArray();
			processPacket(tinyTP);
		} else {
			m_nState = STATE_RECEIVING;
		}
	}

	/**
	 * Save a beamed object as a file in the inbox directory.
	 */
	private void doPut() {
		m_log.debug("IrOBEX", "(doPut)");

		String strName = (String) m_params.get("Name");
		m_log.debug("IrOBEX", "\tName: " + strName);

		String strDescription = (String) m_params.get("Description");
		m_log.debug("IrOBEX", "\tDescription: " + strDescription);

		ByteArrayOutputStream body = (ByteArrayOutputStream) m_params.get("Body");
		m_log.debug("IrOBEX", "\tBody: " + body.toString());

		//
		// Object names can contain characters with special meanings to the 
		// receiver. For instance, Palm application names can have '/' characters
		// in them, which Unix platforms use to denote directories. We need to
		// replace path separators with something, and here we use '_' characters.
		//
		String newName      = strName.replace(File.separatorChar, '_');
		File   beamedObject = new File(m_inbox.getName() + File.separator + newName);
		
		m_log.debug("IrOBEX", "\tsaving to " + beamedObject.getName());

		try {
			FileOutputStream out = new FileOutputStream(beamedObject);
			body.writeTo(out);
			out.close();
		} catch ( IOException ioException ) {
			m_log.error("IrOBEX", ioException.getMessage());
		}
	}

	private void processPacket(TinyTP tinyTP) {
		m_log.debug("IrOBEX", "(processPacket)");

		boolean bFinal  = (0 != (m_ayPacket[0] & 0x80));
		byte    yOpcode = (byte) (m_ayPacket[0] & 0x7F);

		switch ( yOpcode ) {
			case OPCODE_CONNECT_REQUEST:
				handleConnectRequest(tinyTP, bFinal);
			break;

			case OPCODE_DISCONNECT_REQUEST:
				handleDisconnectRequest(tinyTP, bFinal);
			break;

			case OPCODE_PUT_REQUEST:
				handlePutRequest(tinyTP, bFinal);
			break;

			default:
				m_log.warn("IrOBEX", "Unimplemented opcode: " + yOpcode);
			break;
		}
	}

	private void handleConnectRequest(TinyTP tinyTP, boolean bFinal) {
		m_log.debug("IrOBEX", "Connect-Request");

		int  nPacketLength    = (m_ayPacket[1] << 8) | m_ayPacket[2];
		byte yVersion         = m_ayPacket[3];
		byte yFlags           = m_ayPacket[4];
		int  nMaxPacketLength = (m_ayPacket[5] << 8) | m_ayPacket[6];

		byte [] ayResult = new byte[255];
		int     nLength  = 0;

		ayResult[nLength++] = (byte) 0xA0;           // 0: success
		nLength += 2;                                // 1 & 2: leave space for packet length
		ayResult[nLength++] = (byte) IROBEX_VERSION; // 3: IrOBEX version
		ayResult[nLength++] = (byte) IROBEX_FLAGS;   // 4: flags

		// bytes 5 & 6: maximum packet length
		ayResult[nLength++] = (byte) ((nMaxPacketLength & 0x0000FF00) >> 8);
		ayResult[nLength++] = (byte)  (nMaxPacketLength & 0x000000FF); 

		ayResult[1]         = (byte) ((nLength & 0x0000FF00) >> 8);
		ayResult[2]         = (byte) (nLength & 0x0000000FF);

		tinyTP.dataRequest(ayResult, 0, nLength);
	}

	private void handleDisconnectRequest(TinyTP tinyTP, boolean bFinal) {
		m_log.debug("IrOBEX", "Disconnect-Request");

		byte [] ayResponse      = new byte[3];
		int     nResponseLength = 0;

		ayResponse[nResponseLength++] = (byte) 0xA0;
		ayResponse[nResponseLength++] = (byte) 0x00;
		ayResponse[nResponseLength++] = (byte) 0x03;
	
		tinyTP.dataRequest(ayResponse, 0, nResponseLength);
	}

	private void handlePutRequest(TinyTP tinyTP, boolean bFinal) {
		m_log.debug("IrOBEX", "Put-Request packet-length=" + m_nPacketLength);

		try {
			parseHeaders();
			if ( bFinal ) {
				doPut();
				m_params.clear();
			}
		} catch ( Exception e ) {
			m_log.error("IrOBEX", e.toString());
		}

		byte [] ayResponse      = new byte[3];
		int     nResponseLength = 0;

		ayResponse[nResponseLength++] = (byte) (bFinal ? 0xA0 : 0x90);
		ayResponse[nResponseLength++] = (byte) 0x00;
		ayResponse[nResponseLength++] = (byte) 0x03;
	
		tinyTP.dataRequest(ayResponse, 0, nResponseLength);
	}

	private void parseHeaders() throws Exception {
		int             nLength = 0;
		DataInputStream data    = new DataInputStream(new ByteArrayInputStream(m_ayPacket));

		// Packets begin with 1-byte opcode, but we already know it's
		// a PUT request (although we should assert that here)
		data.readByte();

		// Next is a 16-bit packet length
		short nPacketLength = data.readShort();
		m_log.debug("IrOBEX", "(parseHeaders) nPacketLength: " + nPacketLength);

		while ( data.available() > 0 ) {
			// Figure out datatype
			String  strName = null;
			Object  value   = null;
			byte [] ayBody  = null;
			boolean bBody   = false; // reinitialize each time through

			// Each header begins with a 1-byte type.
			int yHeader = data.readByte();
			m_log.debug("IrOBEX", "yHeader=" + yHeader);

			switch ( (byte) (0x000000FF & yHeader) ) {
				case (byte) 0xC0: strName = "Count";           break;
				case (byte) 0x01: strName = "Name";            break;
				case (byte) 0x42: strName = "Type";            break;
				case (byte) 0xC3: strName = "Length";          break;
				case (byte) 0x44: strName = "Time";            break;
				case (byte) 0x05: strName = "Description";     break;
				case (byte) 0x46: strName = "Target";          break;
				case (byte) 0x47: strName = "HTTP";            break;

				// See below for special handling of Body & End-Of-Body
				// chunks.
				case (byte) 0x48: // fall through
				case (byte) 0x49:
					strName = "Body";
					bBody   = true;
				break;

				case (byte) 0x4A: strName = "Who";             break;
				case (byte) 0xCB: strName = "Connection-ID";   break;
				case (byte) 0x4C: strName = "App-Parameters";  break;
				case (byte) 0x4D: strName = "Challenge";       break;
				case (byte) 0x4E: strName = "Response";        break;
				case (byte) 0x4F: strName = "Object-Class";    break;

				// Not part of the IrOBEX specification, and outside
				// the range of ID's that are supposed to be recognized
				// by IrOBEX services. It's a 4-byte ASCII string disguised
				// as an int. Is this considered intellectual property
				// of Palm Computing? If so, we need to ignore it.
				case (byte) 0xCF: strName = "Palm-Creator-ID"; break;

				default:
					strName = "Unknown (" + yHeader + ")";
				break;
			}

			m_log.debug("IrOBEX", strName);

			switch ( 0x000000C0 & yHeader ) {
				case 0x00000000:
					// Unicode text, null terminated, prefixed by 2-byte length
					// Length includes header byte and 2-byte length, so we need
					// to subtract 3 before continuing
					nLength = data.readShort() - 3;
					m_log.debug("IrOBEX", "reading string length=" + nLength);
					// Java uses modified UTF-8, while OBEX specifies
					// UTF-16. We can convert by reading 16-bit characters
					// and constructing a string from them.
					char [] acText = new char[nLength / 2];
					for ( int i = 0; i < acText.length; i++ ) {
						acText[i] = data.readChar();
					}
					// Convert to string, minus the trailing null
					String strValue = new String(acText, 0, acText.length - 1);
					m_log.debug("IrOBEX", "strValue: #"  + strValue + "#");
					value = strValue;
				break;

				case 0x00000040:
					// Byte sequence prefaced by 2-byte length
					// Length includes header byte and 2-byte length, so we need
					// to subtract 3 before continuing
					nLength = data.readShort() - 3;
					m_log.debug("IrOBEX", "reading binary sequence length=" + nLength);
					ayBody = new byte[nLength];
					data.read(ayBody);
					// If this sequence is a Body or End-Of-Body chunk,
					// it will be contenated into the preceding body
					// segments below.
					String strBody = new String(ayBody);
					m_log.debug("IrOBEX", "body: #" + strBody + "#");
				break;

				case 0x00000080: // 1 byte
					m_log.debug("IrOBEX", "reading byte");
					value = new Byte(data.readByte());
				break;

				case 0x000000C0: // 4-byte integer
					m_log.debug("IrOBEX", "reading int");
					value = new Integer(data.readInt());
				break;

				default:
					throw new Exception("Unhandled datatype: " + yHeader);
			}

			m_log.debug("IrOBEX", "(parseHeaders) " + strName);

			// We need to handle Body and End-Of-Body chunks differently,
			// because they need to be concatenated into a single stream
			// of bytes.
			if ( bBody ) {
				m_log.debug("IrOBEX", "bBody was true");
				if ( (null != ayBody) && (ayBody.length > 0 ) ) {
					ByteArrayOutputStream body =
						(ByteArrayOutputStream) m_params.get("Body");

					if ( null == body ) {
						m_log.debug("IrOBEX", "allocating new ByteArrayOutputStream");
						body = new ByteArrayOutputStream();
					}

					body.write(ayBody, 0, ayBody.length);
					m_log.debug("IrOBEX", "putting body into hash table");
					m_params.put("Body", body);
				}
			} else {
				m_log.debug("IrOBEX", "putting key [" + strName + "] value [" + value + "]");
				m_params.put(strName, value);
			}
		}	
	}

	private void connectRequest(TinyTP tinyTP) {
		byte [] ayRequest      = new byte[255];
		int     nRequestLength = 0;

		ayRequest[nRequestLength++] = (byte) 0x80;           // 0: opcode
		nRequestLength += 2;                                 // 1 & 2: leave space for packet length
		ayRequest[nRequestLength++] = (byte) IROBEX_VERSION; // 3: IrOBEX version
		ayRequest[nRequestLength++] = (byte) IROBEX_FLAGS;   // 4: flags
	
		// bytes 5 & 6: maximum packet length
		ayRequest[nRequestLength++] = (byte) 0xFF;
		ayRequest[nRequestLength++] = (byte) 0xFF;
	
		ayRequest[1] = (byte) ((nRequestLength & 0x0000FF00) >> 8);
		ayRequest[2] = (byte) (nRequestLength & 0x0000000FF);

		tinyTP.dataRequest(ayRequest, 0, nRequestLength);
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -