1/* Copyright (c) 2015 The Android Open Source Project
2 * Copyright (C) 2015 Samsung LSI
3 * Copyright (c) 2008-2009, Motorola, Inc.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * - Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * - Neither the name of the Motorola, Inc. nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34package javax.obex;
35
36import java.io.IOException;
37import java.io.InputStream;
38import java.io.DataInputStream;
39import java.io.OutputStream;
40import java.io.DataOutputStream;
41import java.io.ByteArrayOutputStream;
42
43import android.util.Log;
44
45/**
46 * This class implements the Operation interface for server side connections.
47 * <P>
48 * <STRONG>Request Codes</STRONG> There are four different request codes that
49 * are in this class. 0x02 is a PUT request that signals that the request is not
50 * complete and requires an additional OBEX packet. 0x82 is a PUT request that
51 * says that request is complete. In this case, the server can begin sending the
52 * response. The 0x03 is a GET request that signals that the request is not
53 * finished. When the server receives a 0x83, the client is signaling the server
54 * that it is done with its request. TODO: Extend the ClientOperation and reuse
55 * the methods defined TODO: in that class.
56 * @hide
57 */
58public final class ServerOperation implements Operation, BaseStream {
59
60    private static final String TAG = "ServerOperation";
61
62    private static final boolean V = ObexHelper.VDBG; // Verbose debugging
63
64    public boolean isAborted;
65
66    public HeaderSet requestHeader;
67
68    public HeaderSet replyHeader;
69
70    public boolean finalBitSet;
71
72    private InputStream mInput;
73
74    private ServerSession mParent;
75
76    private int mMaxPacketLength;
77
78    private int mResponseSize;
79
80    private boolean mClosed;
81
82    private boolean mGetOperation;
83
84    private PrivateInputStream mPrivateInput;
85
86    private PrivateOutputStream mPrivateOutput;
87
88    private ObexTransport mTransport;
89
90    private boolean mPrivateOutputOpen;
91
92    private String mExceptionString;
93
94    private ServerRequestHandler mListener;
95
96    private boolean mRequestFinished;
97
98    private boolean mHasBody;
99
100    private boolean mSendBodyHeader = true;
101    // Assume SRM disabled - needs to be explicit
102    // enabled by client
103    private boolean mSrmEnabled = false;
104    // A latch - when triggered, there is not way back ;-)
105    private boolean mSrmActive = false;
106    // Set to true when a SRM enable response have been send
107    private boolean mSrmResponseSent = false;
108    // keep waiting until final-bit is received in request
109    // to handle the case where the SRM enable header is in
110    // a different OBEX packet than the SRMP header.
111    private boolean mSrmWaitingForRemote = true;
112    // Why should we wait? - currently not exposed to apps.
113    private boolean mSrmLocalWait = false;
114
115    /**
116     * Creates new ServerOperation
117     * @param p the parent that created this object
118     * @param in the input stream to read from
119     * @param out the output stream to write to
120     * @param request the initial request that was received from the client
121     * @param maxSize the max packet size that the client will accept
122     * @param listen the listener that is responding to the request
123     * @throws IOException if an IO error occurs
124     */
125    public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
126            ServerRequestHandler listen) throws IOException {
127
128        isAborted = false;
129        mParent = p;
130        mInput = in;
131        mMaxPacketLength = maxSize;
132        mClosed = false;
133        requestHeader = new HeaderSet();
134        replyHeader = new HeaderSet();
135        mPrivateInput = new PrivateInputStream(this);
136        mResponseSize = 3;
137        mListener = listen;
138        mRequestFinished = false;
139        mPrivateOutputOpen = false;
140        mHasBody = false;
141        ObexPacket packet;
142        mTransport = p.getTransport();
143
144        /*
145         * Determine if this is a PUT request
146         */
147        if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
148                (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
149            /*
150             * It is a PUT request.
151             */
152            mGetOperation = false;
153
154            /*
155             * Determine if the final bit is set
156             */
157            if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
158                finalBitSet = false;
159            } else {
160                finalBitSet = true;
161                mRequestFinished = true;
162            }
163        } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
164                (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
165            /*
166             * It is a GET request.
167             */
168            mGetOperation = true;
169
170            // For Get request, final bit set is decided by server side logic
171            finalBitSet = false;
172
173            if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
174                mRequestFinished = true;
175            }
176        } else {
177            throw new IOException("ServerOperation can not handle such request");
178        }
179
180        packet = ObexPacket.read(request, mInput);
181
182        /*
183         * Determine if the packet length is larger than this device can receive
184         */
185        if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
186            mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
187            throw new IOException("Packet received was too large. Length: "
188                    + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
189        }
190
191        /*
192         * Determine if any headers were sent in the initial request
193         */
194        if (packet.mLength > 3) {
195            if(!handleObexPacket(packet)) {
196                return;
197            }
198            if (!mHasBody) {
199                while ((!mGetOperation) && (!finalBitSet)) {
200                    sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
201                    if (mPrivateInput.available() > 0) {
202                        break;
203                    }
204                }
205            }
206        }
207
208        while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
209            sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
210            if (mPrivateInput.available() > 0) {
211                break;
212            }
213        }
214
215        // wait for get request finished !!!!
216        while (mGetOperation && !mRequestFinished) {
217            sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
218        }
219    }
220
221    /**
222     * Parse headers and update member variables
223     * @param packet the received obex packet
224     * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
225     * response have been send. Else true.
226     * @throws IOException
227     */
228    private boolean handleObexPacket(ObexPacket packet) throws IOException {
229        byte[] body = updateRequestHeaders(packet);
230
231        if (body != null) {
232            mHasBody = true;
233        }
234        if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
235            mListener.setConnectionId(ObexHelper
236                    .convertToLong(requestHeader.mConnectionID));
237        } else {
238            mListener.setConnectionId(1);
239        }
240
241        if (requestHeader.mAuthResp != null) {
242            if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
243                mExceptionString = "Authentication Failed";
244                mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
245                mClosed = true;
246                requestHeader.mAuthResp = null;
247                return false;
248            }
249            requestHeader.mAuthResp = null;
250        }
251
252        if (requestHeader.mAuthChall != null) {
253            mParent.handleAuthChall(requestHeader);
254            // send the auhtResp to the client
255            replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
256            System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
257                    replyHeader.mAuthResp.length);
258            requestHeader.mAuthResp = null;
259            requestHeader.mAuthChall = null;
260        }
261
262        if (body != null) {
263            mPrivateInput.writeBytes(body, 1);
264        }
265        return true;
266    }
267
268    /**
269     * Update the request header set, and sniff on SRM headers to update local state.
270     * @param data the OBEX packet data
271     * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
272     * @throws IOException
273     */
274    private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
275        byte[] body = null;
276        if (packet.mPayload != null) {
277            body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
278        }
279        Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
280        if(mTransport.isSrmSupported() && srmMode != null
281                && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
282            mSrmEnabled = true;
283            if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
284        }
285        checkForSrmWait(packet.mHeaderId);
286        if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
287            if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
288            mSrmActive = true;
289        }
290        return body;
291    }
292
293    /**
294     * Call this only when a complete request have been received.
295     * (This is not optimal, but the current design is not really suited to
296     * the way SRM is specified.)
297     */
298    private void checkForSrmWait(int headerId){
299        if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
300                || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
301                || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
302            try {
303                mSrmWaitingForRemote = false;
304                Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
305                if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
306                    mSrmWaitingForRemote = true;
307                    // Clear the wait header, as the absents of the header when the final bit is set
308                    // indicates don't wait.
309                    requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
310                }
311            } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
312        }
313    }
314
315    public boolean isValidBody() {
316        return mHasBody;
317    }
318
319    /**
320     * Determines if the operation should continue or should wait. If it should
321     * continue, this method will continue the operation.
322     * @param sendEmpty if <code>true</code> then this will continue the
323     *        operation even if no headers will be sent; if <code>false</code>
324     *        then this method will only continue the operation if there are
325     *        headers to send
326     * @param inStream if<code>true</code> the stream is input stream, otherwise
327     *        output stream
328     * @return <code>true</code> if the operation was completed;
329     *         <code>false</code> if no operation took place
330     */
331    public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
332            throws IOException {
333        if (!mGetOperation) {
334            if (!finalBitSet) {
335                if (sendEmpty) {
336                    sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
337                    return true;
338                } else {
339                    if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
340                        sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
341                        return true;
342                    } else {
343                        return false;
344                    }
345                }
346            } else {
347                return false;
348            }
349        } else {
350            sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
351            return true;
352        }
353    }
354
355    /**
356     * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
357     * will wait for a response from the client before ending unless SRM is active.
358     * @param type the response code to send back to the client
359     * @return <code>true</code> if the final bit was not set on the reply;
360     *         <code>false</code> if no reply was received because the operation
361     *         ended, an abort was received, the final bit was set in the
362     *         reply or SRM is active.
363     * @throws IOException if an IO error occurs
364     */
365    public synchronized boolean sendReply(int type) throws IOException {
366        ByteArrayOutputStream out = new ByteArrayOutputStream();
367        boolean skipSend = false;
368        boolean skipReceive = false;
369        boolean srmRespSendPending = false;
370
371        long id = mListener.getConnectionId();
372        if (id == -1) {
373            replyHeader.mConnectionID = null;
374        } else {
375            replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
376        }
377
378        if(mSrmEnabled && !mSrmResponseSent) {
379            // As we are not ensured that the SRM enable is in the first OBEX packet
380            // We must check for each reply.
381            if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
382            replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
383            srmRespSendPending = true;
384        }
385
386        if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
387            replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
388        }
389
390        byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
391        int bodyLength = -1;
392        int orginalBodyLength = -1;
393
394        if (mPrivateOutput != null) {
395            bodyLength = mPrivateOutput.size();
396            orginalBodyLength = bodyLength;
397        }
398
399        if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
400
401            int end = 0;
402            int start = 0;
403
404            while (end != headerArray.length) {
405                end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
406                        - ObexHelper.BASE_PACKET_LENGTH);
407                if (end == -1) {
408
409                    mClosed = true;
410
411                    if (mPrivateInput != null) {
412                        mPrivateInput.close();
413                    }
414
415                    if (mPrivateOutput != null) {
416                        mPrivateOutput.close();
417                    }
418                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
419                    throw new IOException("OBEX Packet exceeds max packet size");
420                }
421                byte[] sendHeader = new byte[end - start];
422                System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
423
424                mParent.sendResponse(type, sendHeader);
425                start = end;
426            }
427
428            if (bodyLength > 0) {
429                return true;
430            } else {
431                return false;
432            }
433
434        } else {
435            out.write(headerArray);
436        }
437
438        // For Get operation: if response code is OBEX_HTTP_OK, then this is the
439        // last packet; so set finalBitSet to true.
440        if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
441            finalBitSet = true;
442        }
443
444        if(mSrmActive) {
445            if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
446                    mSrmResponseSent == true) {
447                // we are in the middle of a SRM PUT operation, don't send a continue.
448                skipSend = true;
449            } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
450                // We are still receiving the get request, receive, but don't send continue.
451                skipSend = true;
452            } else if(mGetOperation && mRequestFinished == true) {
453                // All done receiving the GET request, send data to the client, without
454                // expecting a continue.
455                skipReceive = true;
456            }
457            if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
458                    + " skipReceive==" + skipReceive);
459        }
460        if(srmRespSendPending) {
461            if(V)Log.v(TAG,
462                    "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
463            mSrmResponseSent = true;
464        }
465
466        if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
467            if (bodyLength > 0) {
468                /*
469                 * Determine if I can send the whole body or just part of
470                 * the body.  Remember that there is the 3 bytes for the
471                 * response message and 3 bytes for the header ID and length
472                 */
473                if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
474                    bodyLength = mMaxPacketLength - headerArray.length - 6;
475                }
476
477                byte[] body = mPrivateOutput.readBytes(bodyLength);
478
479                /*
480                 * Since this is a put request if the final bit is set or
481                 * the output stream is closed we need to send the 0x49
482                 * (End of Body) otherwise, we need to send 0x48 (Body)
483                 */
484                if ((finalBitSet) || (mPrivateOutput.isClosed())) {
485                    if(mSendBodyHeader == true) {
486                        out.write(0x49);
487                        bodyLength += 3;
488                        out.write((byte)(bodyLength >> 8));
489                        out.write((byte)bodyLength);
490                        out.write(body);
491                    }
492                } else {
493                    if(mSendBodyHeader == true) {
494                    out.write(0x48);
495                    bodyLength += 3;
496                    out.write((byte)(bodyLength >> 8));
497                    out.write((byte)bodyLength);
498                    out.write(body);
499                    }
500                }
501
502            }
503        }
504
505        if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
506            if(mSendBodyHeader) {
507                out.write(0x49);
508                orginalBodyLength = 3;
509                out.write((byte)(orginalBodyLength >> 8));
510                out.write((byte)orginalBodyLength);
511            }
512        }
513
514        if(skipSend == false) {
515            mResponseSize = 3;
516            mParent.sendResponse(type, out.toByteArray());
517        }
518
519        if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
520
521            if(mGetOperation && skipReceive) {
522                // Here we need to check for and handle abort (throw an exception).
523                // Any other signal received should be discarded silently (only on server side)
524                checkSrmRemoteAbort();
525            } else {
526                // Receive and handle data (only send reply if !skipSend)
527                // Read a complete OBEX Packet
528                ObexPacket packet = ObexPacket.read(mInput);
529
530                int headerId = packet.mHeaderId;
531                if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
532                        && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
533                        && (headerId != ObexHelper.OBEX_OPCODE_GET)
534                        && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
535
536                    /*
537                     * Determine if an ABORT was sent as the reply
538                     */
539                    if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
540                        handleRemoteAbort();
541                    } else {
542                        // TODO:shall we send this if it occurs during SRM? Errata on the subject
543                        mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
544                        mClosed = true;
545                        mExceptionString = "Bad Request Received";
546                        throw new IOException("Bad Request Received");
547                    }
548                } else {
549
550                    if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
551                        finalBitSet = true;
552                    } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
553                        mRequestFinished = true;
554                    }
555
556                    /*
557                     * Determine if the packet length is larger than the negotiated packet size
558                     */
559                    if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
560                        mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
561                        throw new IOException("Packet received was too large");
562                    }
563
564                    /*
565                     * Determine if any headers were sent in the initial request
566                     */
567                    if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
568                        if(handleObexPacket(packet) == false) {
569                            return false;
570                        }
571                    }
572                }
573
574            }
575            return true;
576        } else {
577            return false;
578        }
579    }
580
581    /**
582     * This method will look for an abort from the peer during a SRM transfer.
583     * The function will not block if no data has been received from the remote device.
584     * If data have been received, the function will block while reading the incoming
585     * OBEX package.
586     * An Abort request will be handled, and cause an IOException("Abort Received").
587     * Other messages will be discarded silently as per GOEP specification.
588     * @throws IOException if an abort request have been received.
589     * TODO: I think this is an error in the specification. If we discard other messages,
590     *       the peer device will most likely stall, as it will not receive the expected
591     *       response for the message...
592     *       I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
593     *       header values shall be ignored by the receiving device."
594     *       If any signal is received during an active SRM transfer it is unexpected regardless
595     *       whether or not it contains SRM/SRMP headers...
596     */
597    private void checkSrmRemoteAbort() throws IOException {
598        if(mInput.available() > 0) {
599            ObexPacket packet = ObexPacket.read(mInput);
600            /*
601             * Determine if an ABORT was sent as the reply
602             */
603            if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
604                handleRemoteAbort();
605            } else {
606                // TODO: should we throw an exception here anyway? - don't see how to
607                //       ignore SRM/SRMP headers without ignoring the complete signal
608                //       (in this particular case).
609                Log.w(TAG, "Received unexpected request from client - discarding...\n"
610                        + "   headerId: " + packet.mHeaderId + " length: " + packet.mLength);
611            }
612        }
613    }
614
615    private void handleRemoteAbort() throws IOException {
616        /* TODO: To increase the speed of the abort operation in SRM, we need
617         *       to be able to flush the L2CAP queue for the PSM in use.
618         *       This could be implemented by introducing a control
619         *       message to be send over the socket, that in the abort case
620         *       could carry a flush command. */
621        mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
622        mClosed = true;
623        isAborted = true;
624        mExceptionString = "Abort Received";
625        throw new IOException("Abort Received");
626    }
627
628    /**
629     * Sends an ABORT message to the server. By calling this method, the
630     * corresponding input and output streams will be closed along with this
631     * object.
632     * @throws IOException if the transaction has already ended or if an OBEX
633     *         server called this method
634     */
635    public void abort() throws IOException {
636        throw new IOException("Called from a server");
637    }
638
639    /**
640     * Returns the headers that have been received during the operation.
641     * Modifying the object returned has no effect on the headers that are sent
642     * or retrieved.
643     * @return the headers received during this <code>Operation</code>
644     * @throws IOException if this <code>Operation</code> has been closed
645     */
646    public HeaderSet getReceivedHeader() throws IOException {
647        ensureOpen();
648        return requestHeader;
649    }
650
651    /**
652     * Specifies the headers that should be sent in the next OBEX message that
653     * is sent.
654     * @param headers the headers to send in the next message
655     * @throws IOException if this <code>Operation</code> has been closed or the
656     *         transaction has ended and no further messages will be exchanged
657     * @throws IllegalArgumentException if <code>headers</code> was not created
658     *         by a call to <code>ServerRequestHandler.createHeaderSet()</code>
659     */
660    public void sendHeaders(HeaderSet headers) throws IOException {
661        ensureOpen();
662
663        if (headers == null) {
664            throw new IOException("Headers may not be null");
665        }
666
667        int[] headerList = headers.getHeaderList();
668        if (headerList != null) {
669            for (int i = 0; i < headerList.length; i++) {
670                replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
671            }
672
673        }
674    }
675
676    /**
677     * Retrieves the response code retrieved from the server. Response codes are
678     * defined in the <code>ResponseCodes</code> interface.
679     * @return the response code retrieved from the server
680     * @throws IOException if an error occurred in the transport layer during
681     *         the transaction; if this method is called on a
682     *         <code>HeaderSet</code> object created by calling
683     *         <code>createHeaderSet</code> in a <code>ClientSession</code>
684     *         object; if this is called from a server
685     */
686    public int getResponseCode() throws IOException {
687        throw new IOException("Called from a server");
688    }
689
690    /**
691     * Always returns <code>null</code>
692     * @return <code>null</code>
693     */
694    public String getEncoding() {
695        return null;
696    }
697
698    /**
699     * Returns the type of content that the resource connected to is providing.
700     * E.g. if the connection is via HTTP, then the value of the content-type
701     * header field is returned.
702     * @return the content type of the resource that the URL references, or
703     *         <code>null</code> if not known
704     */
705    public String getType() {
706        try {
707            return (String)requestHeader.getHeader(HeaderSet.TYPE);
708        } catch (IOException e) {
709            return null;
710        }
711    }
712
713    /**
714     * Returns the length of the content which is being provided. E.g. if the
715     * connection is via HTTP, then the value of the content-length header field
716     * is returned.
717     * @return the content length of the resource that this connection's URL
718     *         references, or -1 if the content length is not known
719     */
720    public long getLength() {
721        try {
722            Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
723
724            if (temp == null) {
725                return -1;
726            } else {
727                return temp.longValue();
728            }
729        } catch (IOException e) {
730            return -1;
731        }
732    }
733
734    public int getMaxPacketSize() {
735        return mMaxPacketLength - 6 - getHeaderLength();
736    }
737
738    public int getHeaderLength() {
739        long id = mListener.getConnectionId();
740        if (id == -1) {
741            replyHeader.mConnectionID = null;
742        } else {
743            replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
744        }
745
746        byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
747
748        return headerArray.length;
749    }
750
751    /**
752     * Open and return an input stream for a connection.
753     * @return an input stream
754     * @throws IOException if an I/O error occurs
755     */
756    public InputStream openInputStream() throws IOException {
757        ensureOpen();
758        return mPrivateInput;
759    }
760
761    /**
762     * Open and return a data input stream for a connection.
763     * @return an input stream
764     * @throws IOException if an I/O error occurs
765     */
766    public DataInputStream openDataInputStream() throws IOException {
767        return new DataInputStream(openInputStream());
768    }
769
770    /**
771     * Open and return an output stream for a connection.
772     * @return an output stream
773     * @throws IOException if an I/O error occurs
774     */
775    public OutputStream openOutputStream() throws IOException {
776        ensureOpen();
777
778        if (mPrivateOutputOpen) {
779            throw new IOException("no more input streams available, stream already opened");
780        }
781
782        if (!mRequestFinished) {
783            throw new IOException("no  output streams available ,request not finished");
784        }
785
786        if (mPrivateOutput == null) {
787            mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
788        }
789        mPrivateOutputOpen = true;
790        return mPrivateOutput;
791    }
792
793    /**
794     * Open and return a data output stream for a connection.
795     * @return an output stream
796     * @throws IOException if an I/O error occurs
797     */
798    public DataOutputStream openDataOutputStream() throws IOException {
799        return new DataOutputStream(openOutputStream());
800    }
801
802    /**
803     * Closes the connection and ends the transaction
804     * @throws IOException if the operation has already ended or is closed
805     */
806    public void close() throws IOException {
807        ensureOpen();
808        mClosed = true;
809    }
810
811    /**
812     * Verifies that the connection is open and no exceptions should be thrown.
813     * @throws IOException if an exception needs to be thrown
814     */
815    public void ensureOpen() throws IOException {
816        if (mExceptionString != null) {
817            throw new IOException(mExceptionString);
818        }
819        if (mClosed) {
820            throw new IOException("Operation has already ended");
821        }
822    }
823
824    /**
825     * Verifies that additional information may be sent. In other words, the
826     * operation is not done.
827     * <P>
828     * Included to implement the BaseStream interface only. It does not do
829     * anything on the server side since the operation of the Operation object
830     * is not done until after the handler returns from its method.
831     * @throws IOException if the operation is completed
832     */
833    public void ensureNotDone() throws IOException {
834    }
835
836    /**
837     * Called when the output or input stream is closed. It does not do anything
838     * on the server side since the operation of the Operation object is not
839     * done until after the handler returns from its method.
840     * @param inStream <code>true</code> if the input stream is closed;
841     *        <code>false</code> if the output stream is closed
842     * @throws IOException if an IO error occurs
843     */
844    public void streamClosed(boolean inStream) throws IOException {
845
846    }
847
848    public void noBodyHeader(){
849        mSendBodyHeader = false;
850    }
851}
852