Implemented interprocess DDE message posting.

diff --git a/dlls/user/message.c b/dlls/user/message.c
index c22285e..98b425c 100644
--- a/dlls/user/message.c
+++ b/dlls/user/message.c
@@ -512,7 +512,6 @@
     case WM_MDIGETACTIVE:
         if (lparam) return sizeof(BOOL);
         return 0;
-
     case WM_WINE_SETWINDOWPOS:
         push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
         return 0;
@@ -537,13 +536,6 @@
     /* these contain an HGLOBAL */
     case WM_PAINTCLIPBOARD:
     case WM_SIZECLIPBOARD:
-    case WM_DDE_INITIATE:
-    case WM_DDE_ADVISE:
-    case WM_DDE_UNADVISE:
-    case WM_DDE_DATA:
-    case WM_DDE_REQUEST:
-    case WM_DDE_POKE:
-    case WM_DDE_EXECUTE:
     /* these contain pointers */
     case WM_DROPOBJECT:
     case WM_QUERYDROPOBJECT:
@@ -763,7 +755,6 @@
         if (!*lparam) return TRUE;
         if (!get_buffer_space( buffer, sizeof(BOOL) )) return FALSE;
         break;
-
     /* these contain an HFONT */
     case WM_SETFONT:
     case WM_GETFONT:
@@ -784,13 +775,6 @@
     /* these contain an HGLOBAL */
     case WM_PAINTCLIPBOARD:
     case WM_SIZECLIPBOARD:
-    case WM_DDE_INITIATE:
-    case WM_DDE_ADVISE:
-    case WM_DDE_UNADVISE:
-    case WM_DDE_DATA:
-    case WM_DDE_REQUEST:
-    case WM_DDE_POKE:
-    case WM_DDE_EXECUTE:
     /* these contain pointers */
     case WM_DROPOBJECT:
     case WM_QUERYDROPOBJECT:
@@ -944,7 +928,7 @@
         memcpy( (RECT *)lparam, buffer, min( sizeof(RECT), size ));
         break;
     case EM_GETLINE:
-        size = min( size, *(WORD *)lparam );
+        size = min( size, (size_t)*(WORD *)lparam );
         memcpy( (WCHAR *)lparam, buffer, size );
         break;
     case LB_GETSELITEMS:
@@ -1065,6 +1049,271 @@
     }
 }
 
+/* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
+ * to the memory handle, we keep track (in the server side) of all pairs of handle
+ * used (the client passes its value and the content of the memory handle), and
+ * the server stored both values (the client, and the local one, created after the
+ * content). When a ACK message is generated, the list of pair is searched for a 
+ * matching pair, so that the client memory handle can be returned.
+ */
+struct DDE_pair {
+    HGLOBAL     client_hMem;
+    HGLOBAL     server_hMem;
+};
+
+static      struct DDE_pair*    dde_pairs;
+static      int                 dde_num_alloc;
+static      int                 dde_num_used;
+static      CRITICAL_SECTION    dde_crst = CRITICAL_SECTION_INIT("Raw_DDE_CritSect");
+
+static BOOL dde_add_pair(HGLOBAL chm, HGLOBAL shm)
+{
+    int  i;
+#define GROWBY  4
+
+    EnterCriticalSection(&dde_crst);
+
+    /* now remember the pair of hMem on both sides */
+    if (dde_num_used == dde_num_alloc)
+    {
+        struct DDE_pair* tmp = HeapReAlloc( GetProcessHeap(), 0, dde_pairs,
+                                            (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
+        if (!tmp)
+        {
+            LeaveCriticalSection(&dde_crst);
+            return FALSE;
+        }
+        dde_pairs = tmp;
+        /* zero out newly allocated part */
+        memset(&dde_pairs[dde_num_alloc], 0, GROWBY * sizeof(struct DDE_pair));
+        dde_num_alloc += GROWBY;
+    }
+#undef GROWBY
+    for (i = 0; i < dde_num_alloc; i++)
+    {
+        if (dde_pairs[i].server_hMem == 0)
+        {
+            dde_pairs[i].client_hMem = chm;
+            dde_pairs[i].server_hMem = shm;
+            dde_num_used++;
+            break;
+        }
+    }
+    LeaveCriticalSection(&dde_crst);
+    return TRUE;
+}
+
+static HGLOBAL dde_get_pair(HGLOBAL shm)
+{
+    int  i;
+    HGLOBAL     ret = 0;
+
+    EnterCriticalSection(&dde_crst);
+    for (i = 0; i < dde_num_alloc; i++)
+    {
+        if (dde_pairs[i].server_hMem == shm)
+        {
+            /* free this pair */
+            dde_pairs[i].server_hMem = 0;
+            dde_num_used--;
+            ret = dde_pairs[i].client_hMem;
+            break;
+        }
+    }
+    LeaveCriticalSection(&dde_crst);
+    return ret;
+}
+
+/***********************************************************************
+ *		post_dde_message
+ *
+ * Post a DDE messag
+ */
+static BOOL post_dde_message( DWORD dest_tid, struct packed_message *data, const struct send_message_info *info )
+{
+    void*       ptr = NULL;
+    int         size = 0;
+    UINT        uiLo, uiHi;
+    LPARAM      lp = 0;
+    HGLOBAL     hunlock = 0;
+    int         i;
+    DWORD       res;
+
+    if (!UnpackDDElParam( info->msg, info->lparam, &uiLo, &uiHi ))
+        return FALSE;
+
+    lp = info->lparam;
+    switch (info->msg)
+    {
+        /* DDE messages which don't require packing are:
+         * WM_DDE_INITIATE
+         * WM_DDE_TERMINATE
+         * WM_DDE_REQUEST
+         * WM_DDE_UNADVISE
+         */
+    case WM_DDE_ACK:
+        if (HIWORD(uiHi))
+        {
+            /* uiHi should contain a hMem from WM_DDE_EXECUTE */
+            HGLOBAL h = dde_get_pair( uiHi );
+            if (h)
+            {
+                /* send back the value of h on the other side */
+                push_data( data, &h, sizeof(HGLOBAL) );
+                lp = uiLo;
+                TRACE( "send dde-ack %x %08x => %08lx\n", uiLo, uiHi, (DWORD)h );
+            }
+        }
+        else
+        {
+            /* uiHi should contain either an atom or 0 */
+            TRACE( "send dde-ack %x atom=%x\n", uiLo, uiHi );
+            lp = MAKELONG( uiLo, uiHi );
+        }
+        break;
+    case WM_DDE_ADVISE:
+    case WM_DDE_DATA:
+    case WM_DDE_POKE:
+        size = 0;
+        if (uiLo)
+        {
+            size = GlobalSize( (HGLOBAL)uiLo ) ;
+            if ((info->msg == WM_DDE_ADVISE && size < sizeof(DDEADVISE)) ||
+                (info->msg == WM_DDE_DATA   && size < sizeof(DDEDATA))   ||
+                (info->msg == WM_DDE_POKE   && size < sizeof(DDEPOKE))
+                )
+            return FALSE;
+        }
+        else if (info->msg != WM_DDE_DATA) return FALSE;
+        
+        lp = uiHi;
+        if (uiLo)
+        {
+            if ((ptr = GlobalLock( (HGLOBAL)uiLo) ))
+            {
+                push_data( data, ptr, size );
+                hunlock = (HGLOBAL)uiLo;
+            }
+        }
+        TRACE( "send ddepack %u %x\n", size, uiHi );
+        break;
+    case WM_DDE_EXECUTE:
+        if (info->lparam)
+        {
+            if ((ptr = GlobalLock( (HGLOBAL)info->lparam) ))
+            {
+                push_data(data, ptr, GlobalSize( (HGLOBAL)info->lparam ));
+                /* so that the other side can send it back on ACK */
+                lp = info->lparam;
+                hunlock = (HGLOBAL)info->lparam;
+            }
+        }
+        break;
+    }
+    SERVER_START_REQ( send_message )
+    {
+        req->id      = (void *)dest_tid;
+        req->type    = info->type;
+        req->win     = info->hwnd;
+        req->msg     = info->msg;
+        req->wparam  = info->wparam;
+        req->lparam  = lp;
+        req->time    = GetCurrentTime();
+        req->timeout = -1;
+        for (i = 0; i < data->count; i++) 
+            wine_server_add_data( req, data->data[i], data->size[i] );
+        if ((res = wine_server_call( req )))
+        {
+            if (res == STATUS_INVALID_PARAMETER)
+                /* FIXME: find a STATUS_ value for this one */
+                SetLastError( ERROR_INVALID_THREAD_ID );
+            else
+                SetLastError( RtlNtStatusToDosError(res) );
+        }   
+        else 
+            FreeDDElParam(info->msg, info->lparam);
+    }
+    SERVER_END_REQ;
+    if (hunlock) GlobalUnlock(hunlock);
+
+    return !res;
+}
+
+/***********************************************************************
+ *		unpack_dde_message
+ *
+ * Unpack a posted DDE message received from another process.
+ */
+static BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
+                                void **buffer, size_t size )
+{
+    UINT	uiLo, uiHi;
+    HGLOBAL	hMem = 0;
+    void*	ptr;
+
+    switch (message)
+    {
+    case WM_DDE_ACK:
+        if (size)
+        {
+            /* hMem is being passed */
+            if (size != sizeof(HGLOBAL)) return FALSE;
+            if (!buffer || !*buffer) return FALSE;
+            uiLo = *lparam;
+            memcpy( &hMem, *buffer, size );
+            uiHi = hMem;
+            TRACE("recv dde-ack %u mem=%x[%lx]\n", uiLo, uiHi, GlobalSize( uiHi ));
+        }
+        else
+        {
+            uiLo = LOWORD( *lparam );
+            uiHi = HIWORD( *lparam );
+            TRACE("recv dde-ack %x atom=%x\n", uiLo, uiHi);
+        }
+	*lparam = PackDDElParam( WM_DDE_ACK, uiLo, uiHi );
+	break;
+    case WM_DDE_ADVISE:
+    case WM_DDE_DATA:
+    case WM_DDE_POKE:
+	if ((!buffer || !*buffer) && message != WM_DDE_DATA) return FALSE;
+	uiHi = *lparam;
+	TRACE( "recv ddepack %u %x\n", size, uiHi );
+        if (size)
+        {
+            hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size );
+            if (hMem && (ptr = GlobalLock( hMem )))
+            {
+                memcpy( ptr, *buffer, size );
+                GlobalUnlock( hMem );
+            }
+            else return FALSE;
+        }
+	uiLo = hMem;
+
+	*lparam = PackDDElParam( message, uiLo, uiHi );
+	break;
+    case WM_DDE_EXECUTE:
+	if (size)
+	{
+	    if (!buffer || !*buffer) return FALSE;
+	    hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size );
+	    if (hMem && (ptr = GlobalLock( hMem )))
+	    {
+		memcpy( ptr, *buffer, size );
+		GlobalUnlock( hMem );
+                TRACE( "exec: pairing c=%08lx s=%08lx\n", *lparam, (DWORD)hMem );
+                if (!dde_add_pair( *lparam, hMem )) 
+                {
+                    GlobalFree( hMem );
+                    return FALSE;
+                }
+	    }
+	} else return FALSE;
+	*lparam = hMem;
+        break;
+    }
+    return TRUE;
+}
 
 /***********************************************************************
  *           call_window_proc
@@ -1222,6 +1471,18 @@
             /* fall through */
         case MSG_POSTED:
             queue->GetMessageExtraInfoVal = extra_info;
+	    if (info.msg.message >= WM_DDE_FIRST && info.msg.message <= WM_DDE_LAST)
+	    {
+		if (!unpack_dde_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
+                                         &info.msg.lParam, &buffer, size ))
+		{
+		    ERR( "invalid packed dde-message %x (%s) hwnd %x wp %x lp %lx size %d\n",
+			 info.msg.message, SPY_GetMsgName(info.msg.message, info.msg.hwnd), 
+			 info.msg.hwnd, info.msg.wParam, info.msg.lParam, size );
+		    /* ignore it */
+		    continue;
+		}
+	    }
             *msg = info.msg;
             if (buffer) HeapFree( GetProcessHeap(), 0, buffer );
             return TRUE;
@@ -1321,6 +1582,10 @@
             return FALSE;
         }
     }
+    else if (info->type == MSG_POSTED && info->msg >= WM_DDE_FIRST && info->msg <= WM_DDE_LAST)
+    {
+        return post_dde_message( dest_tid, &data, info );
+    }
 
     SERVER_START_REQ( send_message )
     {
diff --git a/server/queue.c b/server/queue.c
index a639ebe..150a213 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -749,7 +749,7 @@
 
         switch(msg->type)
         {
-       case MSG_OTHER_PROCESS:
+        case MSG_OTHER_PROCESS:
             msg->data_size = get_req_data_size();
             if (msg->data_size && !(msg->data = memdup( get_req_data(), msg->data_size )))
             {
@@ -771,6 +771,13 @@
             set_queue_bits( recv_queue, QS_SENDMESSAGE );
             break;
         case MSG_POSTED:
+            /* needed for posted DDE messages */
+            msg->data_size = get_req_data_size();
+            if (msg->data_size && !(msg->data = memdup( get_req_data(), msg->data_size )))
+            {
+                free( msg );
+                break;
+            }
             append_message( &recv_queue->msg_list[POST_MESSAGE], msg );
             set_queue_bits( recv_queue, QS_POSTMESSAGE );
             break;
@@ -803,8 +810,12 @@
                                    struct get_message_reply *reply,
                                    struct message *msg, enum message_kind kind )
 {
-    assert( !msg->data_size );  /* posted messages can't have data */
-
+    reply->total = msg->data_size;
+    if (msg->data_size > get_reply_max_size())
+    {
+        set_error( STATUS_BUFFER_OVERFLOW );
+        return;
+    }
     reply->type   = msg->type;
     reply->win    = msg->win;
     reply->msg    = msg->msg;
@@ -814,16 +825,22 @@
     reply->y      = msg->y;
     reply->time   = msg->time;
     reply->info   = msg->info;
-    reply->total  = 0;
 
     /* raw messages always get removed */
     if ((msg->type == MSG_HARDWARE_RAW) || (flags & GET_MSG_REMOVE))
     {
         queue->last_msg = NULL;
+        if (msg->data)
+        {
+            set_reply_data_ptr( msg->data, msg->data_size );
+            msg->data = NULL;
+            msg->data_size = 0;
+        }
         remove_queue_message( queue, msg, kind );
     }
     else  /* remember it as the last returned message */
     {
+        if (msg->data) set_reply_data( msg->data, msg->data_size );
         queue->last_msg = msg;
         queue->last_msg_kind = kind;
     }