Moved standard syslevel APIs declaration to winbase.h.
[wine] / dlls / dplayx / name_server.c
1 /* DPLAYX.DLL name server implementation
2  *
3  * Copyright 2000 - Peter Hunnisett
4  *
5  * <presently under construction - contact hunnise@nortelnetworks.com>
6  *
7  */
8  
9 /* NOTE: Methods with the NS_ prefix are name server methods */
10
11 #include "winbase.h"
12 #include "debugtools.h"
13 #include "heap.h"
14 #include "mmsystem.h"
15
16 #include "dplayx_global.h"
17 #include "name_server.h"
18 #include "dplaysp.h"
19 #include "dplayx_messages.h"
20 #include "dplayx_queue.h"
21
22 /* FIXME: Need to create a crit section, store and use it */
23
24 DEFAULT_DEBUG_CHANNEL(dplay);
25
26 /* NS specific structures */
27 struct NSCacheData
28 {
29   DPQ_ENTRY(NSCacheData) next;
30
31   DWORD dwTime; /* Time at which data was last known valid */
32   LPDPSESSIONDESC2 data;
33
34   LPVOID lpNSAddrHdr;
35
36 };
37 typedef struct NSCacheData NSCacheData, *lpNSCacheData;
38
39 struct NSCache
40 {
41   lpNSCacheData present; /* keep track of what is to be looked at when walking */
42
43   DPQ_HEAD(NSCacheData) first;
44 }; 
45 typedef struct NSCache NSCache, *lpNSCache;
46
47 /* Function prototypes */
48 DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData );
49
50 /* Name Server functions 
51  * --------------------- 
52  */
53 void NS_SetLocalComputerAsNameServer( LPCDPSESSIONDESC2 lpsd )
54 {
55 #if 0
56   /* FIXME: Remove this method? */
57   DPLAYX_SetLocalSession( lpsd );
58 #endif
59 }
60
61 DPQ_DECL_COMPARECB( cbUglyPig, GUID )
62 {
63   return IsEqualGUID( elem1, elem2 );
64 }
65
66 /* Store the given NS remote address for future reference */
67 void NS_SetRemoteComputerAsNameServer( LPVOID                    lpNSAddrHdr,
68                                        DWORD                     dwHdrSize,
69                                        LPDPMSG_ENUMSESSIONSREPLY lpMsg,
70                                        LPVOID                    lpNSInfo )
71 {
72   lpNSCache     lpCache = (lpNSCache)lpNSInfo;
73   lpNSCacheData lpCacheNode;
74
75   TRACE( "%p, %p, %p\n", lpNSAddrHdr, lpMsg, lpNSInfo );
76
77   /* FIXME: Should check to see if the reply is for an existing session. If
78    *        so we remove the old and add the new so oldest is at front.
79    */
80
81   /* See if we can find this session. If we can, remove it as it's a dup */
82   DPQ_REMOVE_ENTRY_CB( lpCache->first, next, data->guidInstance, cbUglyPig,
83                      lpMsg->sd.guidInstance, lpCacheNode );
84
85   if( lpCacheNode != NULL )
86   {
87     TRACE( "Duplicate session entry for %s removed - updated version kept\n",
88            debugstr_guid( &lpCacheNode->data->guidInstance ) );
89     cbDeleteNSNodeFromHeap( lpCacheNode );
90   }
91
92   /* Add this to the list */
93   lpCacheNode = (lpNSCacheData)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
94                                           sizeof( *lpCacheNode ) );
95
96   if( lpCacheNode == NULL )
97   {
98     ERR( "no memory for NS node\n" );
99     return;
100   }
101
102   lpCacheNode->lpNSAddrHdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
103                                         dwHdrSize );
104   CopyMemory( lpCacheNode->lpNSAddrHdr, lpNSAddrHdr, dwHdrSize );
105               
106
107   lpCacheNode->data = (LPDPSESSIONDESC2)HeapAlloc( GetProcessHeap(),
108                                                    HEAP_ZERO_MEMORY, 
109                                                    sizeof( *lpCacheNode->data ) );
110
111   if( lpCacheNode->data == NULL )
112   {
113     ERR( "no memory for SESSIONDESC2\n" );
114     return;
115   }
116
117   CopyMemory( lpCacheNode->data, &lpMsg->sd, sizeof( *lpCacheNode->data ) );
118   lpCacheNode->data->sess.lpszSessionNameA = HEAP_strdupWtoA( GetProcessHeap(),
119                                                               HEAP_ZERO_MEMORY, 
120                                                               (LPWSTR)(lpMsg+1) );
121
122   lpCacheNode->dwTime = timeGetTime();
123
124   DPQ_INSERT(lpCache->first, lpCacheNode, next );
125
126   lpCache->present = lpCacheNode;
127
128   /* Use this message as an oportunity to weed out any old sessions so 
129    * that we don't enum them again 
130    */
131   NS_PruneSessionCache( lpNSInfo );
132 }
133
134 LPVOID NS_GetNSAddr( LPVOID lpNSInfo )
135 {
136   lpNSCache lpCache = (lpNSCache)lpNSInfo;
137
138   FIXME( ":quick stub\n" );
139
140   /* Ok. Cheat and don't search for the correct stuff just take the first.
141    * FIXME: In the future how are we to know what is _THE_ enum we used?
142    *        This is going to have to go into dplay somehow. Perhaps it
143    *        comes back with app server id for the join command! Oh...that
144    *        must be it. That would make this method obsolete once that's
145    *        in place.
146    */
147
148   return lpCache->first.lpQHFirst->lpNSAddrHdr;
149 }
150
151 /* This function is responsible for sending a request for all other known
152    nameservers to send us what sessions they have registered locally
153  */
154 HRESULT NS_SendSessionRequestBroadcast( LPCGUID lpcGuid,
155                                         DWORD dwFlags,
156                                         LPSPINITDATA lpSpData )
157                                      
158 {
159   DPSP_ENUMSESSIONSDATA data;
160   LPDPMSG_ENUMSESSIONSREQUEST lpMsg;
161
162   TRACE( "enumerating for guid %s\n", debugstr_guid( lpcGuid ) );
163
164   /* Get the SP to deal with sending the EnumSessions request */
165   FIXME( ": not all data fields are correct\n" );
166
167   data.dwMessageSize = lpSpData->dwSPHeaderSize + sizeof( *lpMsg ); /*FIXME!*/
168   data.lpMessage = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
169                               data.dwMessageSize );
170   data.lpISP = lpSpData->lpISP;
171   data.bReturnStatus = (dwFlags & DPENUMSESSIONS_RETURNSTATUS) ? TRUE : FALSE;
172
173
174   lpMsg = (LPDPMSG_ENUMSESSIONSREQUEST)(((BYTE*)data.lpMessage)+lpSpData->dwSPHeaderSize); 
175
176   /* Setup EnumSession reqest message */
177   lpMsg->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG;
178   lpMsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREQUEST;
179   lpMsg->envelope.wVersion   = DPMSGVER_DP6;
180
181   lpMsg->dwPasswordSize = 0; /* FIXME: If enumerating passwords..? */
182   lpMsg->dwFlags        = dwFlags;
183
184   CopyMemory( &lpMsg->guidApplication, lpcGuid, sizeof( *lpcGuid ) );
185
186   return (lpSpData->lpCB->EnumSessions)( &data ); 
187 }
188
189 /* Delete a name server node which has been allocated on the heap */
190 DPQ_DECL_DELETECB( cbDeleteNSNodeFromHeap, lpNSCacheData )
191 {
192   /* NOTE: This proc doesn't deal with the walking pointer */
193
194   /* FIXME: Memory leak on data (contained ptrs) */
195   HeapFree( GetProcessHeap(), 0, elem->data );
196   HeapFree( GetProcessHeap(), 0, elem->lpNSAddrHdr );
197   HeapFree( GetProcessHeap(), 0, elem );
198 }
199
200 /* Render all data in a session cache invalid */
201 void NS_InvalidateSessionCache( LPVOID lpNSInfo )
202 {
203   lpNSCache lpCache = (lpNSCache)lpNSInfo;
204
205   if( lpCache == NULL )
206   {
207     ERR( ": invalidate non existant cache\n" );
208     return;
209   }
210
211   DPQ_DELETEQ( lpCache->first, next, lpNSCacheData, cbDeleteNSNodeFromHeap );
212
213   /* NULL out the walking pointer */
214   lpCache->present = NULL;
215 }
216
217 /* Create and initialize a session cache */
218 BOOL NS_InitializeSessionCache( LPVOID* lplpNSInfo )
219 {
220   lpNSCache lpCache = (lpNSCache)HeapAlloc( GetProcessHeap(),
221                                             HEAP_ZERO_MEMORY,
222                                             sizeof( *lpCache ) );
223
224   *lplpNSInfo = lpCache;
225
226   if( lpCache == NULL )
227   {
228     return FALSE;
229   }
230
231   DPQ_INIT(lpCache->first);
232   lpCache->present = NULL;
233
234   return TRUE;
235 }
236
237 /* Delete a session cache */
238 void NS_DeleteSessionCache( LPVOID lpNSInfo )
239 {
240   NS_InvalidateSessionCache( (lpNSCache)lpNSInfo );
241 }
242
243 /* Reinitialize the present pointer for this cache */
244 void NS_ResetSessionEnumeration( LPVOID lpNSInfo )
245 {
246   ((lpNSCache)lpNSInfo)->present = ((lpNSCache)lpNSInfo)->first.lpQHFirst;
247 }
248
249 LPDPSESSIONDESC2 NS_WalkSessions( LPVOID lpNSInfo )
250 {
251   LPDPSESSIONDESC2 lpSessionDesc;
252   lpNSCache lpCache = (lpNSCache)lpNSInfo;
253
254   /* FIXME: The pointers could disappear when walking if a prune happens */
255
256   /* Test for end of the list */ 
257   if( lpCache->present == NULL )
258   {
259     return NULL;
260   }
261
262   lpSessionDesc = lpCache->present->data;
263
264   /* Advance tracking pointer */
265   lpCache->present = lpCache->present->next.lpQNext;
266
267   return lpSessionDesc;
268 }
269
270 /* This method should check to see if there are any sessions which are
271  * older than the criteria. If so, just delete that information.
272  */
273 /* FIXME: This needs to be called by some periodic timer */
274 void NS_PruneSessionCache( LPVOID lpNSInfo )
275 {
276   lpNSCache     lpCache = lpNSInfo;
277
278   const DWORD dwPresentTime = timeGetTime();
279   const DWORD dwPrunePeriod = 60000; /* is 60 secs enough? */ 
280   const DWORD dwPruneTime   = dwPresentTime - dwPrunePeriod;
281
282   /* This silly little algorithm is based on the fact we keep entries in 
283    * the queue in a time based order. It also assumes that it is not possible
284    * to wrap around over yourself (which is not unreasonable).
285    * The if statements verify if the first entry in the queue is less 
286    * than dwPrunePeriod old depending on the "clock" roll over.
287    */
288   for( ;; )
289   {
290     lpNSCacheData lpFirstData;
291
292     if( DPQ_IS_EMPTY(lpCache->first) )
293     {
294       /* Nothing to prune */
295       break;
296     }
297
298     if( dwPruneTime > dwPresentTime ) /* 0 <= dwPresentTime <= dwPrunePeriod */
299     {
300       if( ( DPQ_FIRST(lpCache->first)->dwTime <= dwPresentTime ) ||
301           ( DPQ_FIRST(lpCache->first)->dwTime > dwPruneTime )
302         )
303       {
304         /* Less than dwPrunePeriod old - keep */
305         break; 
306       }
307     }
308     else /* dwPrunePeriod <= dwPresentTime <= max dword */
309     {
310       if( ( DPQ_FIRST(lpCache->first)->dwTime <= dwPresentTime ) &&
311           ( DPQ_FIRST(lpCache->first)->dwTime > dwPruneTime ) 
312         )
313       {
314         /* Less than dwPrunePeriod old - keep */
315         break;
316       }
317     }
318
319     lpFirstData = DPQ_FIRST(lpCache->first);
320     DPQ_REMOVE( lpCache->first, DPQ_FIRST(lpCache->first), next );
321     cbDeleteNSNodeFromHeap( lpFirstData );
322   }
323
324 }
325
326 /* NAME SERVER Message stuff */
327 void NS_ReplyToEnumSessionsRequest( LPVOID lpMsg, 
328                                     LPDPSP_REPLYDATA lpReplyData,
329                                     IDirectPlay2Impl* lpDP )
330 {
331   LPDPMSG_ENUMSESSIONSREPLY rmsg;
332   DWORD dwVariableSize;
333   DWORD dwVariableLen;
334   LPWSTR string;
335   /* LPDPMSG_ENUMSESSIONSREQUEST msg = (LPDPMSG_ENUMSESSIONSREQUEST)lpMsg; */
336   BOOL bAnsi = TRUE; /* FIXME: This needs to be in the DPLAY interface */
337
338   FIXME( ": few fixed + need to check request for response\n" );
339
340   dwVariableLen = bAnsi ? lstrlenA( lpDP->dp2->lpSessionDesc->sess.lpszSessionNameA ) + 1 
341                          : lstrlenW( lpDP->dp2->lpSessionDesc->sess.lpszSessionName ) + 1;
342
343   dwVariableSize = dwVariableLen * sizeof( WCHAR );
344
345   lpReplyData->dwMessageSize = lpDP->dp2->spData.dwSPHeaderSize +
346                                  sizeof( *rmsg ) + dwVariableSize;
347   lpReplyData->lpMessage     = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
348                                           lpReplyData->dwMessageSize );
349
350   rmsg = (LPDPMSG_ENUMSESSIONSREPLY)( (BYTE*)lpReplyData->lpMessage + 
351                                              lpDP->dp2->spData.dwSPHeaderSize);
352
353   rmsg->envelope.dwMagic    = DPMSGMAGIC_DPLAYMSG; 
354   rmsg->envelope.wCommandId = DPMSGCMD_ENUMSESSIONSREPLY;
355   rmsg->envelope.wVersion   = DPMSGVER_DP6;
356
357   CopyMemory( &rmsg->sd, lpDP->dp2->lpSessionDesc, 
358               sizeof( lpDP->dp2->lpSessionDesc->dwSize ) ); 
359   rmsg->dwUnknown = 0x0000005c;
360   if( bAnsi )
361   {
362     string = HEAP_strdupAtoW( GetProcessHeap(), 0, 
363                               lpDP->dp2->lpSessionDesc->sess.lpszSessionNameA );
364     /* FIXME: Memory leak */
365   }
366   else
367   {
368     string = lpDP->dp2->lpSessionDesc->sess.lpszSessionName;
369   }
370
371   lstrcpyW( (LPWSTR)(rmsg+1), string );
372  
373 }