ClientOperation.java revision 9439a7fe517b858bc5e5c654b459315e4722feb2
1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package javax.obex;
34
35import java.io.IOException;
36import java.io.InputStream;
37import java.io.OutputStream;
38import java.io.DataInputStream;
39import java.io.DataOutputStream;
40import java.io.ByteArrayOutputStream;
41
42/**
43 * This class implements the <code>Operation</code> interface.  It will read
44 * and write data via puts and gets.
45 *
46 * @version 0.3 November 28, 2008
47 */
48public class ClientOperation implements Operation, BaseStream {
49
50    /**
51     * Defines the basic packet length used by OBEX.  Event OBEX packet has the
52     * same basic format:<BR>
53     * Byte 0: Request or Response Code
54     * Byte 1&2: Length of the packet.
55     */
56    private static final int BASE_PACKET_LENGTH = 3;
57
58    private ClientSession parent;
59
60    private InputStream socketInput;
61
62    private PrivateInputStream privateInput;
63
64    private PrivateOutputStream privateOutput;
65
66    private boolean isClosed;
67
68    private String exceptionMessage;
69
70    private int maxPacketSize;
71
72    private boolean isDone;
73
74    private boolean isGet;
75
76    private HeaderSet requestHeaders;
77
78    private HeaderSet replyHeaders;
79
80    private boolean isEndOfBodySent;
81
82    private boolean inputStreamOpened;
83
84    private boolean outputStreamOpened;
85
86    private boolean isValidateConnected;
87
88    /**
89     * Creates new OperationImpl to read and write data to a server
90     *
91     * @param in the input stream to read from
92     *
93     * @param maxSize the maximum packet size
94     *
95     * @param p the parent to this object
96     *
97     * @param headers the headers to set in the initial request
98     *
99     * @param type <code>true</code> if this is a get request;
100     * <code>false</code. if this is a put request
101     *
102     * @exception IOExcpetion if the an IO error occured
103     */
104    public ClientOperation(InputStream in, int maxSize, ClientSession p, HeaderSet header,
105            boolean type) throws IOException {
106
107        parent = p;
108        isEndOfBodySent = false;
109        socketInput = in;
110        isClosed = false;
111        isDone = false;
112        maxPacketSize = maxSize;
113        isGet = type;
114
115        inputStreamOpened = false;
116        outputStreamOpened = false;
117        isValidateConnected = false;
118
119        privateInput = null;
120        privateOutput = null;
121
122        replyHeaders = new HeaderSet();
123
124        requestHeaders = new HeaderSet();
125
126        int[] headerList = header.getHeaderList();
127
128        if (headerList != null) {
129
130            for (int i = 0; i < headerList.length; i++) {
131                requestHeaders.setHeader(headerList[i], header.getHeader(headerList[i]));
132            }
133        }
134
135        if ((header).authChall != null) {
136            requestHeaders.authChall = new byte[(header).authChall.length];
137            System.arraycopy((header).authChall, 0, requestHeaders.authChall, 0,
138                    (header).authChall.length);
139        }
140
141        if ((header).authResp != null) {
142            requestHeaders.authResp = new byte[(header).authResp.length];
143            System.arraycopy((header).authResp, 0, requestHeaders.authResp, 0,
144                    (header).authResp.length);
145
146        }
147        //        requestHeaders = (HeaderSet)header;
148    }
149
150    /**
151     * Sends an ABORT message to the server.  By calling this method, the
152     * corresponding input and output streams will be closed along with this
153     * object.
154     *
155     * @exception IOException if the transaction has already ended or if an
156     * OBEX server called this method
157     */
158    public synchronized void abort() throws IOException {
159        ensureOpen();
160        // need check again .
161        //	if(isDone) {
162        //	     throw new IOException("Operation has already ended");
163        //	}
164
165        //no compatible with sun-ri
166        if ((isDone) && (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE)) {
167            throw new IOException("Operation has already ended");
168        }
169
170        exceptionMessage = "Operation aborted";
171        if ((!isDone) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
172            isDone = true;
173            /*
174             * Since we are not sending any headers or returning any headers then
175             * we just need to write and read the same bytes
176             */
177            parent.sendRequest(0xFF, null, replyHeaders, null);
178
179            if (replyHeaders.responseCode != ResponseCodes.OBEX_HTTP_OK) {
180                throw new IOException("Invalid response code from server");
181            }
182
183            exceptionMessage = null;
184        }
185
186        close();
187    }
188
189    /**
190     * Retrieves the response code retrieved from the server.  Response codes
191     * are defined in the <code>ResponseCodes</code> interface.
192     *
193     * @return the response code retrieved from the server
194     *
195     * @exception IOException if an error occurred in the transport layer during
196     * the transaction; if this method is called on a <code>HeaderSet</code>
197     * object created by calling <code>createHeaderSet</code> in a
198     * <code>ClientSession</code> object
199     */
200    public synchronized int getResponseCode() throws IOException {
201        //avoid dup validateConnection
202        if ((replyHeaders.responseCode == -1)
203                || (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
204            validateConnection();
205        }
206
207        return replyHeaders.responseCode;
208    }
209
210    /**
211     * This method will always return <code>null</code>
212     *
213     * @return <code>null</code>
214     */
215    public String getEncoding() {
216        return null;
217    }
218
219    /**
220     * Returns the type of content that the resource connected to is providing.
221     * E.g. if the connection is via HTTP, then the value of the content-type
222     * header field is returned.
223     *
224     * @return the content type of the resource that the URL references, or
225     * <code>null</code> if not known
226     */
227    public String getType() {
228        try {
229            return (String)replyHeaders.getHeader(HeaderSet.TYPE);
230        } catch (IOException e) {
231            return null;
232        }
233    }
234
235    /**
236     * Returns the length of the content which is being provided. E.g. if the
237     * connection is via HTTP, then the value of the content-length header
238     * field is returned.
239     *
240     * @return the content length of the resource that this connection's URL
241     * references, or -1 if the content length is not known
242     */
243    public long getLength() {
244        try {
245            Long temp = (Long)replyHeaders.getHeader(HeaderSet.LENGTH);
246
247            if (temp == null) {
248                return -1;
249            } else {
250                return temp.longValue();
251            }
252        } catch (IOException e) {
253            return -1;
254        }
255    }
256
257    /**
258     * Open and return an input stream for a connection.
259     *
260     * @return an input stream
261     *
262     * @exception IOException if an I/O error occurs
263     */
264    public InputStream openInputStream() throws IOException {
265        // TODO: this mode is not set yet.
266        // if ((parent.mode & Connector.READ) == 0)
267        // throw new IOException("write-only connection");
268
269        ensureOpen();
270
271        if (inputStreamOpened)
272            throw new IOException("no more input streams available");
273        if (isGet) {
274            // send the GET request here
275            validateConnection();
276            isValidateConnected = true;
277        } else {
278            if (privateInput == null) {
279                privateInput = new PrivateInputStream(this);
280            }
281        }
282
283        inputStreamOpened = true;
284
285        return privateInput;
286    }
287
288    /**8
289     * Open and return a data input stream for a connection.
290     *
291     * @return an input stream
292     *
293     * @exception IOException if an I/O error occurs
294     */
295    public DataInputStream openDataInputStream() throws IOException {
296        return new DataInputStream(openInputStream());
297    }
298
299    /**
300     * Open and return an output stream for a connection.
301     *
302     * @return an output stream
303     *
304     * @exception IOException if an I/O error occurs
305     */
306    public OutputStream openOutputStream() throws IOException {
307        // TODO: this mode is not set yet.
308        //    	if ((parent.mode & Connector.WRITE) == 0)
309        //    		throw new IOException("read-only connection");
310        ensureOpen();
311        ensureNotDone();
312
313        if (outputStreamOpened)
314            throw new IOException("no more output streams available");
315
316        if (privateOutput == null) {
317            // there are 3 bytes operation headers and 3 bytes body headers //
318            privateOutput = new PrivateOutputStream(this, maxPacketSize - 6);
319        }
320
321        outputStreamOpened = true;
322
323        return privateOutput;
324    }
325
326    public int getMaxPacketSize() {
327        return maxPacketSize - 6;
328    }
329
330    /**
331     * Open and return a data output stream for a connection.
332     *
333     * @return an output stream
334     *
335     * @exception IOException if an I/O error occurs
336     */
337    public DataOutputStream openDataOutputStream() throws IOException {
338        return new DataOutputStream(openOutputStream());
339    }
340
341    /**
342     * Closes the connection and ends the transaction
343     *
344     * @exception IOException if the operation has already ended or is closed
345     */
346    public void close() throws IOException {
347        isClosed = true;
348        inputStreamOpened = false;
349        outputStreamOpened = false;
350        parent.setInactive();
351    }
352
353    /**
354     * Returns the headers that have been received during the operation.
355     * Modifying the object returned has no effect on the headers that are
356     * sent or retrieved.
357     *
358     * @return the headers received during this <code>Operation</code>
359     *
360     * @exception IOException if this <code>Operation</code> has been closed
361     */
362    public HeaderSet getReceivedHeaders() throws IOException {
363        ensureOpen();
364
365        return replyHeaders;
366    }
367
368    /**
369     * Specifies the headers that should be sent in the next OBEX message that
370     * is sent.
371     *
372     * @param headers the headers to send in the next message
373     *
374     * @exception IOException  if this <code>Operation</code> has been closed
375     * or the transaction has ended and no further messages will be exchanged
376     *
377     * @exception IllegalArgumentException if <code>headers</code> was not created
378     * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
379     *
380     * @exception NullPointerException if <code>headers</code> is <code>null</code>
381     */
382    public void sendHeaders(HeaderSet headers) throws IOException {
383        ensureOpen();
384        if (isDone) {
385            throw new IOException("Operation has already exchanged all data");
386        }
387
388        if (headers == null) {
389            throw new NullPointerException("Headers may not be null");
390        }
391
392        int[] headerList = headers.getHeaderList();
393        if (headerList != null) {
394            for (int i = 0; i < headerList.length; i++) {
395                requestHeaders.setHeader(headerList[i], headers.getHeader(headerList[i]));
396            }
397        }
398    }
399
400    /**
401     * Reads a response from the server.  It will populate the appropriate body
402     * and headers.
403     *
404     * @return <code>true</code> if the transaction should end;
405     * <code>false</code> if the transaction should not end
406     *
407     * @exception IOException if an IO error occurred
408     */
409    private boolean readResponse() throws IOException {
410        replyHeaders.responseCode = socketInput.read();
411        int packetLength = socketInput.read();
412        packetLength = (packetLength << 8) + socketInput.read();
413
414        if (packetLength > OBEXConstants.MAX_PACKET_SIZE_INT) {
415            if (exceptionMessage != null) {
416                abort();
417            }
418            throw new IOException("Received a packet that was too big");
419        }
420
421        if (packetLength > BASE_PACKET_LENGTH) {
422            int dataLength = packetLength - BASE_PACKET_LENGTH;
423            byte[] data = new byte[dataLength];
424            int readLength = socketInput.read(data);
425            if (readLength != dataLength) {
426                throw new IOException("Received a packet without data as decalred length");
427            }
428            byte[] body = OBEXHelper.updateHeaderSet(replyHeaders, data);
429
430            if (body != null) {
431                privateInput.writeBytes(body, 1);
432
433                /*
434                 * Determine if a body (0x48) header or an end of body (0x49)
435                 * was received.  If we received an end of body and
436                 * a response code of OBEX_HTTP_OK, then the operation should
437                 * end.
438                 */
439                if ((body[0] == 0x49) && (replyHeaders.responseCode == ResponseCodes.OBEX_HTTP_OK)) {
440                    return false;
441                }
442            }
443        }
444
445        if (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
446            return true;
447        } else {
448            return false;
449        }
450    }
451
452    /**
453     * Verifies that additional information may be sent.  In other words, the
454     * operation is not done.
455     *
456     * @exception IOException if the operation is completed
457     */
458    public void ensureNotDone() throws IOException {
459        if (isDone) {
460            throw new IOException("Operation has completed");
461        }
462    }
463
464    /**
465     * Verifies that the connection is open and no exceptions should be thrown.
466     *
467     * @exception IOException if an exception needs to be thrown
468     */
469    public void ensureOpen() throws IOException {
470        parent.ensureOpen();
471
472        if (exceptionMessage != null) {
473            throw new IOException(exceptionMessage);
474        }
475        if (isClosed) {
476            throw new IOException("Operation has already ended");
477        }
478    }
479
480    /**
481     * Verifies that the connection is open and the proper data has been read.
482     *
483     * @exception IOException if an IO error occurs
484     */
485    private void validateConnection() throws IOException {
486        ensureOpen();
487
488        // to sure only one privateInput object exist.
489        if (privateInput == null) {
490            startProcessing();
491        }
492    }
493
494    /**
495     * Sends a request to the client of the specified type
496     *
497     * @param response the response code to send back to the client
498     *
499     * @return <code>true</code> if there is more data to send;
500     * <code>false</code> if there is no more data to send
501     *
502     * @exception IOException if an IO error occurs
503     */
504    protected boolean sendRequest(int type) throws IOException {
505        boolean returnValue = false;
506        ByteArrayOutputStream out = new ByteArrayOutputStream();
507        int bodyLength = -1;
508        byte[] headerArray = OBEXHelper.createHeader(requestHeaders, true);
509        if (privateOutput != null) {
510            bodyLength = privateOutput.size();
511        }
512
513        /*
514         * Determine if there is space to add a body request.  At present
515         * this method checks to see if there is room for at least a 17
516         * byte body header.  This number needs to be at least 6 so that
517         * there is room for the header ID and length and the reply ID and
518         * length, but it is a waste of resources if we can't send much of
519         * the body.
520         */
521        if ((BASE_PACKET_LENGTH + headerArray.length) > maxPacketSize) {
522            int end = 0;
523            int start = 0;
524            // split & send the headerArray in multiple packets.
525
526            while (end != headerArray.length) {
527                //split the headerArray
528                end = OBEXHelper.findHeaderEnd(headerArray, start, maxPacketSize
529                        - BASE_PACKET_LENGTH);
530                // can not split
531                if (end == -1) {
532                    isDone = true;
533                    abort();
534                    // isDone = true;
535                    exceptionMessage = "Header larger then can be sent in a packet";
536                    isClosed = true;
537
538                    if (privateInput != null) {
539                        privateInput.close();
540                    }
541
542                    if (privateOutput != null) {
543                        privateOutput.close();
544                    }
545                    throw new IOException("OBEX Packet exceeds max packet size");
546                }
547
548                byte[] sendHeader = new byte[end - start];
549                System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
550                if (!parent.sendRequest(type, sendHeader, replyHeaders, privateInput)) {
551                    return false;
552                }
553
554                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
555                    return false;
556                }
557
558                start = end;
559            }
560
561            if (bodyLength > 0) {
562                return true;
563            } else {
564                return false;
565            }
566        } else {
567            out.write(headerArray);
568        }
569
570        if (bodyLength > 0) {
571            /*
572             * Determine if I can send the whole body or just part of
573             * the body.  Remember that there is the 3 bytes for the
574             * response message and 3 bytes for the header ID and length
575             */
576            if (bodyLength > (maxPacketSize - headerArray.length - 6)) {
577                returnValue = true;
578
579                bodyLength = maxPacketSize - headerArray.length - 6;
580            }
581
582            byte[] body = privateOutput.readBytes(bodyLength);
583
584            /*
585             * Since this is a put request if the final bit is set or
586             * the output stream is closed we need to send the 0x49
587             * (End of Body) otherwise, we need to send 0x48 (Body)
588             */
589            if ((privateOutput.isClosed()) && (!returnValue) && (!isEndOfBodySent)
590                    && ((type & 0x80) != 0)) {
591                out.write(0x49);
592                isEndOfBodySent = true;
593            } else {
594                out.write(0x48);
595            }
596
597            bodyLength += 3;
598            out.write((byte)(bodyLength >> 8));
599            out.write((byte)bodyLength);
600
601            if (body != null) {
602                out.write(body);
603            }
604        }
605
606        if (outputStreamOpened && bodyLength <= 0 && !isEndOfBodySent) {
607            // only 0x82 or 0x83 can send 0x49
608            if ((type & 0x80) == 0) {
609                out.write(0x48);
610            } else {
611                out.write(0x49);
612                isEndOfBodySent = true;
613
614            }
615
616            bodyLength = 3;
617            out.write((byte)(bodyLength >> 8));
618            out.write((byte)bodyLength);
619        }
620
621        if (out.size() == 0) {
622            if (!parent.sendRequest(type, null, replyHeaders, privateInput)) {
623                return false;
624            }
625            return returnValue;
626        }
627        if ((out.size() > 0)
628                && (!parent.sendRequest(type, out.toByteArray(), replyHeaders, privateInput))) {
629            return false;
630        }
631
632        // send all of the output data in 0x48,
633        // send 0x49 with empty body
634        if ((privateOutput != null) && (privateOutput.size() > 0))
635            returnValue = true;
636
637        return returnValue;
638    }
639
640    /**
641     * This method starts the processing thread results.  It will send the
642     * initial request.  If the response takes more then one packet, a thread
643     * will be started to handle additional requests
644     *
645     * @exception IOException if an IO error occurs
646     */
647    private synchronized void startProcessing() throws IOException {
648
649        if (privateInput == null) {
650            privateInput = new PrivateInputStream(this);
651        }
652        boolean more = true;
653
654        if (isGet) {
655            if (!isDone) {
656                replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
657                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
658                    more = sendRequest(0x03);
659                }
660
661                if (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
662                    parent.sendRequest(0x83, null, replyHeaders, privateInput);
663                }
664                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
665                    isDone = true;
666                }
667            }
668        } else {
669
670            if (!isDone) {
671                replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
672                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
673                    more = sendRequest(0x02);
674
675                }
676            }
677
678            if (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
679                parent.sendRequest(0x82, null, replyHeaders, privateInput);
680            }
681
682            if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
683                isDone = true;
684            }
685        }
686    }
687
688    /**
689     * Continues the operation since there is no data to read.
690     *
691     * @param sendEmpty <code>true</code> if the operation should send an
692     * empty packet or not send anything if there is no data to send
693     * @param inStream  <code>true</code> if the stream is input stream or
694     * is output stream
695     * @exception IOException if an IO error occurs
696     */
697    public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
698            throws IOException {
699
700        if (isGet) {
701            if ((inStream) && (!isDone)) {
702                // to deal with inputstream in get operation
703                parent.sendRequest(0x83, null, replyHeaders, privateInput);
704                /*
705                  * Determine if that was not the last packet in the operation
706                  */
707                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
708                    isDone = true;
709                }
710
711                return true;
712
713            } else if ((!inStream) && (!isDone)) {
714                // to deal with outputstream in get operation
715
716                if (privateInput == null) {
717                    privateInput = new PrivateInputStream(this);
718                }
719                sendRequest(0x03);
720                return true;
721
722            } else if (isDone) {
723                return false;
724            }
725
726        } else {
727            if ((!inStream) && (!isDone)) {
728                // to deal with outputstream in put operation
729                if (replyHeaders.responseCode == -1) {
730                    replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
731                }
732                sendRequest(0x02);
733                return true;
734            } else if ((inStream) && (!isDone)) {
735                // How to deal with inputstream  in put operation ?
736                return false;
737
738            } else if (isDone) {
739                return false;
740            }
741
742        }
743        return false;
744    }
745
746    /**
747     * Called when the output or input stream is closed.
748     *
749     * @param inStream <code>true</code> if the input stream is closed;
750     * <code>false</code> if the output stream is closed
751     *
752     * @exception IOException if an IO error occurs
753     */
754    public void streamClosed(boolean inStream) throws IOException {
755        if (!isGet) {
756            if ((!inStream) && (!isDone)) {
757                // to deal with outputstream in put operation
758
759                boolean more = true;
760
761                if ((privateOutput != null) && (privateOutput.size() <= 0)) {
762                    byte[] headerArray = OBEXHelper.createHeader(requestHeaders, false);
763                    if (headerArray.length <= 0)
764                        more = false;
765                }
766                // If have not sent any data so send  all now
767                if (replyHeaders.responseCode == -1) {
768                    replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
769                }
770
771                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
772                    more = sendRequest(0x02);
773                }
774
775                /*
776                 * According to the IrOBEX specification, after the final put, you
777                 * only have a single reply to send.  so we don't need the while
778                 * loop.
779                 */
780                while (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
781
782                    sendRequest(0x82);
783                }
784                isDone = true;
785            } else if ((inStream) && (isDone)) {
786                // how to deal with input stream in put stream ?
787                isDone = true;
788            }
789        } else {
790            isValidateConnected = false;
791            if ((inStream) && (!isDone)) {
792
793                // to deal with inputstream in get operation
794                // Have not sent any data so send it all now
795
796                if (replyHeaders.responseCode == -1) {
797                    replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
798                }
799
800                while (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
801                    if (!sendRequest(0x83)) {
802                        break;
803                    }
804                }
805                while (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
806                    parent.sendRequest(0x83, null, replyHeaders, privateInput);
807                }
808                isDone = true;
809            } else if ((!inStream) && (!isDone)) {
810                // to deal with outputstream in get operation
811                // part of the data may have been sent in continueOperation.
812
813                boolean more = true;
814
815                if ((privateOutput != null) && (privateOutput.size() <= 0)) {
816                    byte[] headerArray = OBEXHelper.createHeader(requestHeaders, false);
817                    if (headerArray.length <= 0)
818                        more = false;
819                }
820
821                if (privateInput == null) {
822                    privateInput = new PrivateInputStream(this);
823                }
824                if ((privateOutput != null) && (privateOutput.size() <= 0))
825                    more = false;
826
827                replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
828                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
829                    more = sendRequest(0x03);
830                }
831                sendRequest(0x83);
832                //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
833                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
834                    isDone = true;
835                }
836
837            }
838        }
839    }
840}
841