Bug 799452 - patch 2: Parse PUT request header, r=qdot
authorEric Chou <echou@mozilla.com>
Wed, 17 Oct 2012 14:48:52 +0800
changeset 110646 97e38fd5d9e49b67a8356c532da523fb3a950200
parent 110645 a28241aa4a6c8fbb53ddf1854ae551317fc31ff4
child 110647 c1846c1b5c9947cf8da1363c620490bd9985d516
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersqdot
bugs799452
milestone19.0a1
Bug 799452 - patch 2: Parse PUT request header, r=qdot
dom/bluetooth/BluetoothOppManager.cpp
dom/bluetooth/BluetoothOppManager.h
dom/bluetooth/ObexBase.cpp
dom/bluetooth/ObexBase.h
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -22,17 +22,17 @@ using namespace mozilla::ipc;
 
 // Sending system message "bluetooth-opp-update-progress" every 50kb
 static const uint32_t kUpdateProgressBase = 50 * 1024;
 
 static mozilla::RefPtr<BluetoothOppManager> sInstance;
 static nsCOMPtr<nsIInputStream> stream = nullptr;
 static uint32_t sSentFileLength = 0;
 static nsString sFileName;
-static uint64_t sFileLength = 0;
+static uint32_t sFileLength = 0;
 static nsString sContentType;
 static int sUpdateProgressCounter = 0;
 
 class ReadFileTask : public nsRunnable
 {
 public:
   ReadFileTask(nsIDOMBlob* aBlob) : mBlob(aBlob)
   {
@@ -93,16 +93,18 @@ BluetoothOppManager::BluetoothOppManager
                                            , mLastCommand(0)
                                            , mBlob(nullptr)
                                            , mRemoteObexVersion(0)
                                            , mRemoteConnectionFlags(0)
                                            , mRemoteMaxPacketLength(0)
                                            , mAbortFlag(false)
                                            , mReadFileThread(nullptr)
                                            , mPacketLeftLength(0)
+                                           , mReceiving(false)
+                                           , mPutFinal(false)
 {
   // FIXME / Bug 800249:
   //   mConnectedDeviceAddress is Bluetooth address of connected device,
   //   we will be able to get this value after bug 800249 lands. For now,
   //   just assign a fake value to it.
   mConnectedDeviceAddress.AssignASCII("00:00:00:00:00:00");
 }
 
@@ -217,17 +219,17 @@ BluetoothOppManager::StopSendingFile(Blu
 void
 BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
 {
   uint8_t opCode;
   int packetLength;
   int receivedLength = aMessage->mSize;
 
   if (mPacketLeftLength > 0) {
-    opCode = ObexRequestCode::Put;
+    opCode = mPutFinal ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
     packetLength = mPacketLeftLength;
   } else {
     opCode = aMessage->mData[0];
     packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
   }
 
   if (mLastCommand == ObexRequestCode::Connect) {
     if (opCode == ObexResponseCode::Success) {
@@ -249,28 +251,42 @@ BluetoothOppManager::ReceiveSocketData(U
         if (file) {
           rv = file->GetName(sFileName);
         }
 
         if (!file || sFileName.IsEmpty()) {
           sFileName.AssignLiteral("Unknown");
         }
 
-        rv = mBlob->GetSize(&sFileLength);
+        rv = mBlob->GetType(sContentType);
+        if (NS_FAILED(rv)) {
+          NS_WARNING("Can't get content type");
+          return;
+        }
+
+        uint64_t fileLength;
+        rv = mBlob->GetSize(&fileLength);
         if (NS_FAILED(rv)) {
           NS_WARNING("Can't get file size");
           return;
         }
 
-        rv = mBlob->GetType(sContentType);
-        if (NS_FAILED(rv)) {
-          NS_WARNING("Can't get content type");
+        // Currently we keep the size of files which were sent/received via
+        // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
+        // is only 4-byte long. Although it is possible to transfer a file
+        // larger than UINT32_MAX, it needs to parse another OBEX Header
+        // and I would like to leave it as a feature.
+        if (fileLength <= UINT32_MAX) {
+          NS_WARNING("The file size is too large for now");
+          SendDisconnectRequest();
           return;
         }
 
+        sFileLength = fileLength;
+
         if (NS_FAILED(NS_NewThread(getter_AddRefs(mReadFileThread)))) {
           NS_WARNING("Can't create thread");
           SendDisconnectRequest();
           return;
         }
 
         sUpdateProgressCounter = 1;
         sSentFileLength = 0;
@@ -281,16 +297,17 @@ BluetoothOppManager::ReceiveSocketData(U
       }
     }
   } else if (mLastCommand == ObexRequestCode::Disconnect) {
     if (opCode != ObexResponseCode::Success) {
       // FIXME: Needs error handling here
       NS_WARNING("[OPP] Disconnect failed");
     } else {
       mConnected = false;
+      mReceiving = false;
       mLastCommand = 0;
       mBlob = nullptr;
       mReadFileThread = nullptr;
     }
   } else if (mLastCommand == ObexRequestCode::Put) {
     if (opCode != ObexResponseCode::Continue) {
       // FIXME: Needs error handling here
       NS_WARNING("[OPP] Put failed");
@@ -325,45 +342,63 @@ BluetoothOppManager::ReceiveSocketData(U
       NS_WARNING("[OPP] Abort failed");
     }
 
     FileTransferComplete(mConnectedDeviceAddress, false, false, sFileName,
                          sSentFileLength, sContentType);
     SendDisconnectRequest();
   } else {
     // Remote request or unknown mLastCommand
+    ObexHeaderSet pktHeaders(opCode);
+
     if (opCode == ObexRequestCode::Connect) {
+      ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders);
       ReplyToConnect();
     } else if (opCode == ObexRequestCode::Disconnect) {
+      ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders);
       ReplyToDisconnect();
     } else if (opCode == ObexRequestCode::Put ||
                opCode == ObexRequestCode::PutFinal) {
+      if (!mReceiving) {
+        MOZ_ASSERT(mPacketLeftLength == 0);
+        ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders);
+
+        pktHeaders.GetName(sFileName);
+        pktHeaders.GetContentType(sContentType);
+        pktHeaders.GetLength(&sFileLength);
+
+        ReceivingFileConfirmation(mConnectedDeviceAddress, sFileName, sFileLength, sContentType);
+        mReceiving = true;
+      }
+
       /*
        * A PUT request from remote devices may be divided into multiple parts.
        * In other words, one request may need to be received multiple times,
        * so here we keep a variable mPacketLeftLength to indicate if current
        * PUT request is done.
        */
-      bool final = (opCode == ObexRequestCode::PutFinal);
+      mPutFinal = (opCode == ObexRequestCode::PutFinal);
 
       if (mPacketLeftLength == 0) {
-        if (receivedLength < packetLength) {
-          mPacketLeftLength = packetLength - receivedLength;
-        } else {
-          ReplyToPut(final);
-        }
+        NS_ASSERTION(mPacketLeftLength >= receivedLength,
+                     "Invalid packet length");
+        mPacketLeftLength = packetLength - receivedLength;
       } else {
-        NS_ASSERTION(mPacketLeftLength < receivedLength,
+        NS_ASSERTION(mPacketLeftLength >= receivedLength,
                      "Invalid packet length");
+        mPacketLeftLength -= receivedLength;
+      }
 
-        if (mPacketLeftLength <= receivedLength) {
-          ReplyToPut(final);
-          mPacketLeftLength = 0;
-        } else {
-          mPacketLeftLength -= receivedLength;
+      if (mPacketLeftLength == 0) {
+        ReplyToPut(mPutFinal);
+
+        if (mPutFinal) {
+          mReceiving = false;
+          FileTransferComplete(mConnectedDeviceAddress, true, true, sFileName,
+                               sSentFileLength, sContentType);
         }
       }
     }
   }
 }
 
 void
 BluetoothOppManager::SendConnectRequest()
@@ -664,31 +699,16 @@ BluetoothOppManager::UpdateProgress(cons
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast [bluetooth-opp-update-progress]");
     return;
   }
 }
 
 void
-BluetoothOppManager::OnConnectSuccess()
-{
-}
-
-void
-BluetoothOppManager::OnConnectError()
-{
-}
-
-void
-BluetoothOppManager::OnDisconnect()
-{
-}
-
-void
 BluetoothOppManager::ReceivingFileConfirmation(const nsString& aAddress,
                                                const nsString& aFileName,
                                                uint32_t aFileLength,
                                                const nsString& aContentType)
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
@@ -710,8 +730,23 @@ BluetoothOppManager::ReceivingFileConfir
   v = aContentType;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast [bluetooth-opp-receiving-file-confirmation]");
     return;
   }
 }
+
+void
+BluetoothOppManager::OnConnectSuccess()
+{
+}
+
+void
+BluetoothOppManager::OnConnectError()
+{
+}
+
+void
+BluetoothOppManager::OnDisconnect()
+{
+}
--- a/dom/bluetooth/BluetoothOppManager.h
+++ b/dom/bluetooth/BluetoothOppManager.h
@@ -91,16 +91,18 @@ private:
   int mConnectionId;
   int mLastCommand;
   uint8_t mRemoteObexVersion;
   uint8_t mRemoteConnectionFlags;
   int mRemoteMaxPacketLength;
   bool mAbortFlag;
   int mPacketLeftLength;
   nsString mConnectedDeviceAddress;
+  bool mReceiving;
+  bool mPutFinal;
 
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsCOMPtr<nsIThread> mReadFileThread;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/ObexBase.cpp
+++ b/dom/bluetooth/ObexBase.cpp
@@ -70,44 +70,49 @@ SetObexPacketInfo(uint8_t* retBuf, uint8
 
 void
 ParseHeaders(uint8_t* buf, int totalLength, ObexHeaderSet* retHandlerSet)
 {
   uint8_t* ptr = buf;
 
   while (ptr - buf < totalLength) {
     ObexHeaderId headerId = (ObexHeaderId)*ptr++;
-    int headerLength = 0;
+    int contentLength = 0;
     uint8_t highByte, lowByte;
 
     // Defined in 2.1 OBEX Headers, IrOBEX 1.2
     switch (headerId >> 6)
     {
       case 0x00:
         // NULL terminated Unicode text, length prefixed with 2 byte unsigned integer.
       case 0x01:
         // byte sequence, length prefixed with 2 byte unsigned integer.
         highByte = *ptr++;
         lowByte = *ptr++;
-        headerLength = ((int)highByte << 8) | lowByte;
+        contentLength = (((int)highByte << 8) | lowByte) - 3;
         break;
 
       case 0x02:
         // 1 byte quantity
-        headerLength = 1;
+        contentLength = 1;
         break;
 
       case 0x03:
         // 4 byte quantity
-        headerLength = 4;
+        contentLength = 4;
         break;
     }
 
-    // Content
-    uint8_t* headerContent = new uint8_t[headerLength];
-    memcpy(headerContent, ptr, headerLength);
-    retHandlerSet->AddHeader(new ObexHeader(headerId, headerLength, headerContent));
+    // FIXME: This case should be happened when we are receiving header 'Body'
+    // (file body). I will handle this in another bug.
+    if (contentLength + (ptr - buf) > totalLength) {
+      break;
+    }
 
-    ptr += headerLength;
+    uint8_t* content = new uint8_t[contentLength];
+    memcpy(content, ptr, contentLength);
+    retHandlerSet->AddHeader(new ObexHeader(headerId, contentLength, content));
+
+    ptr += contentLength;
   }
 }
 
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/ObexBase.h
+++ b/dom/bluetooth/ObexBase.h
@@ -134,16 +134,66 @@ public:
   ~ObexHeaderSet()
   {
   }
 
   void AddHeader(ObexHeader* aHeader)
   {
     mHeaders.AppendElement(aHeader);
   }
+
+  void GetName(nsString& aRetName)
+  {
+    int length = mHeaders.Length();
+
+    for (int i = 0; i < length; ++i) {
+      if (mHeaders[i]->mId == ObexHeaderId::Name) {
+        uint8_t* ptr = mHeaders[i]->mData.get();
+        int nameLength = mHeaders[i]->mDataLength / 2;
+
+        for (int j = 0; j < nameLength; ++j) {
+          PRUnichar c = ((((uint32_t)ptr[j * 2]) << 8) | ptr[j * 2 + 1]);
+          aRetName += c;
+        }
+
+        break;
+      }
+    }
+  }
+
+  void GetContentType(nsString& aRetContentType)
+  {
+    int length = mHeaders.Length();
+
+    for (int i = 0; i < length; ++i) {
+      if (mHeaders[i]->mId == ObexHeaderId::Type) {
+        uint8_t* ptr = mHeaders[i]->mData.get();
+        aRetContentType.AssignASCII((const char*)ptr);
+        break;
+      }
+    }
+  }
+
+  // @return file length, 0 means file length is unknown.
+  void GetLength(uint32_t* aRetLength)
+  {
+    int length = mHeaders.Length();
+    *aRetLength = 0;
+
+    for (int i = 0; i < length; ++i) {
+      if (mHeaders[i]->mId == ObexHeaderId::Length) {
+        uint8_t* ptr = mHeaders[i]->mData.get();
+        *aRetLength = ((uint32_t)ptr[0] << 24) |
+                      ((uint32_t)ptr[1] << 16) |
+                      ((uint32_t)ptr[2] << 8) |
+                      ((uint32_t)ptr[3]);
+        break;
+      }
+    }
+  }
 };
 
 int AppendHeaderName(uint8_t* retBuf, const char* name, int length);
 int AppendHeaderBody(uint8_t* retBuf, uint8_t* data, int length);
 int AppendHeaderLength(uint8_t* retBuf, int objectLength);
 int AppendHeaderConnectionId(uint8_t* retBuf, int connectionId);
 void SetObexPacketInfo(uint8_t* retBuf, uint8_t opcode, int packetLength);
 void ParseHeaders(uint8_t* buf, int totalLength, ObexHeaderSet* retHanderSet);