msi: Don't crash if record has no fields.
[wine] / dlls / shell32 / changenotify.c
1 /*
2  *      shell change notification
3  *
4  * Copyright 2000 Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <string.h>
23
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wine/debug.h"
29 #include "shell32_main.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(shell);
32
33 static CRITICAL_SECTION SHELL32_ChangenotifyCS;
34 static CRITICAL_SECTION_DEBUG critsect_debug =
35 {
36     0, 0, &SHELL32_ChangenotifyCS,
37     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
38       0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ChangenotifyCS") }
39 };
40 static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
41
42 typedef SHChangeNotifyEntry *LPNOTIFYREGISTER;
43
44 /* internal list of notification clients (internal) */
45 typedef struct _NOTIFICATIONLIST
46 {
47         struct _NOTIFICATIONLIST *next;
48         struct _NOTIFICATIONLIST *prev;
49         HWND hwnd;              /* window to notify */
50         DWORD uMsg;             /* message to send */
51         LPNOTIFYREGISTER apidl; /* array of entries to watch*/
52         UINT cidl;              /* number of pidls in array */
53         LONG wEventMask;        /* subscribed events */
54         LONG wSignalledEvent;   /* event that occurred */
55         DWORD dwFlags;          /* client flags */
56         LPCITEMIDLIST pidlSignaled; /*pidl of the path that caused the signal*/
57     
58 } NOTIFICATIONLIST, *LPNOTIFICATIONLIST;
59
60 static NOTIFICATIONLIST *head, *tail;
61
62 #define SHCNE_NOITEMEVENTS ( \
63    SHCNE_ASSOCCHANGED )
64
65 #define SHCNE_ONEITEMEVENTS ( \
66    SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
67    SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
68    SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
69    SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
70    SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE )
71
72 #define SHCNE_TWOITEMEVENTS ( \
73    SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM )
74
75 /* for dumping events */
76 static const char * DumpEvent( LONG event )
77 {
78     if( event == SHCNE_ALLEVENTS )
79         return "SHCNE_ALLEVENTS";
80 #define DUMPEV(x)  ,( event & SHCNE_##x )? #x " " : ""
81     return wine_dbg_sprintf( "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
82     DUMPEV(RENAMEITEM)
83     DUMPEV(CREATE)
84     DUMPEV(DELETE)
85     DUMPEV(MKDIR)
86     DUMPEV(RMDIR)
87     DUMPEV(MEDIAINSERTED)
88     DUMPEV(MEDIAREMOVED)
89     DUMPEV(DRIVEREMOVED)
90     DUMPEV(DRIVEADD)
91     DUMPEV(NETSHARE)
92     DUMPEV(NETUNSHARE)
93     DUMPEV(ATTRIBUTES)
94     DUMPEV(UPDATEDIR)
95     DUMPEV(UPDATEITEM)
96     DUMPEV(SERVERDISCONNECT)
97     DUMPEV(UPDATEIMAGE)
98     DUMPEV(DRIVEADDGUI)
99     DUMPEV(RENAMEFOLDER)
100     DUMPEV(FREESPACE)
101     DUMPEV(EXTENDED_EVENT)
102     DUMPEV(ASSOCCHANGED)
103     DUMPEV(INTERRUPT)
104     );
105 #undef DUMPEV
106 }
107
108 static const char * NodeName(const NOTIFICATIONLIST *item)
109 {
110     const char *str;
111     WCHAR path[MAX_PATH];
112
113     if(SHGetPathFromIDListW(item->apidl[0].pidl, path ))
114         str = wine_dbg_sprintf("%s", debugstr_w(path));
115     else
116         str = wine_dbg_sprintf("<not a disk file>" );
117     return str;
118 }
119
120 static void AddNode(LPNOTIFICATIONLIST item)
121 {
122     TRACE("item %p\n", item );
123
124     /* link items */
125     item->prev = tail;
126     item->next = NULL;
127     if( tail )
128         tail->next = item;
129     else
130         head = item;
131     tail = item;
132 }
133
134 static LPNOTIFICATIONLIST FindNode( HANDLE hitem )
135 {
136     LPNOTIFICATIONLIST ptr;
137     for( ptr = head; ptr; ptr = ptr->next )
138         if( ptr == (LPNOTIFICATIONLIST) hitem )
139             return ptr;
140     return NULL;
141 }
142
143 static void DeleteNode(LPNOTIFICATIONLIST item)
144 {
145     UINT i;
146
147     TRACE("item=%p prev=%p next=%p\n", item, item->prev, item->next);
148
149     /* remove item from list */
150     if( item->prev )
151         item->prev->next = item->next;
152     else
153         head = item->next;
154     if( item->next )
155         item->next->prev = item->prev;
156     else
157         tail = item->prev;
158
159     /* free the item */
160     for (i=0; i<item->cidl; i++)
161         SHFree((LPITEMIDLIST)item->apidl[i].pidl);
162     SHFree(item->apidl);
163     SHFree(item);
164 }
165
166 void InitChangeNotifications(void)
167 {
168 }
169
170 void FreeChangeNotifications(void)
171 {
172     TRACE("\n");
173
174     EnterCriticalSection(&SHELL32_ChangenotifyCS);
175
176     while( head )
177         DeleteNode( head );
178
179     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
180
181     DeleteCriticalSection(&SHELL32_ChangenotifyCS);
182 }
183
184 /*************************************************************************
185  * SHChangeNotifyRegister                       [SHELL32.2]
186  *
187  */
188 ULONG WINAPI
189 SHChangeNotifyRegister(
190     HWND hwnd,
191     int fSources,
192     LONG wEventMask,
193     UINT uMsg,
194     int cItems,
195     SHChangeNotifyEntry *lpItems)
196 {
197     LPNOTIFICATIONLIST item;
198     int i;
199
200     item = SHAlloc(sizeof(NOTIFICATIONLIST));
201
202     TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
203         hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item);
204
205     item->next = NULL;
206     item->prev = NULL;
207     item->cidl = cItems;
208     item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems);
209     for(i=0;i<cItems;i++)
210     {
211         item->apidl[i].pidl = ILClone(lpItems[i].pidl);
212         item->apidl[i].fRecursive = lpItems[i].fRecursive;
213     }
214     item->hwnd = hwnd;
215     item->uMsg = uMsg;
216     item->wEventMask = wEventMask;
217     item->wSignalledEvent = 0;
218     item->dwFlags = fSources;
219
220     TRACE("new node: %s\n", NodeName( item ));
221
222     EnterCriticalSection(&SHELL32_ChangenotifyCS);
223
224     AddNode(item);
225
226     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
227
228     return (ULONG)item;
229 }
230
231 /*************************************************************************
232  * SHChangeNotifyDeregister                     [SHELL32.4]
233  */
234 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
235 {
236     LPNOTIFICATIONLIST node;
237
238     TRACE("(0x%08x)\n", hNotify);
239
240     EnterCriticalSection(&SHELL32_ChangenotifyCS);
241
242     node = FindNode((HANDLE)hNotify);
243     if( node )
244         DeleteNode(node);
245
246     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
247
248     return node?TRUE:FALSE;
249 }
250
251 /*************************************************************************
252  * SHChangeNotifyUpdateEntryList                [SHELL32.5]
253  */
254 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
255                               DWORD unknown3, DWORD unknown4)
256 {
257     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
258           unknown1, unknown2, unknown3, unknown4);
259
260     return -1;
261 }
262
263 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
264 {
265     TRACE("%p %p %d\n", changed, watched, sub );
266     if ( !watched )
267         return FALSE;
268     if (ILIsEqual( watched, changed ) )
269         return TRUE;
270     if( sub && ILIsParent( watched, changed, FALSE ) )
271         return TRUE;
272     return FALSE;
273 }
274
275 /*************************************************************************
276  * SHChangeNotify                               [SHELL32.@]
277  */
278 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
279 {
280     LPCITEMIDLIST Pidls[2];
281     LPNOTIFICATIONLIST ptr;
282     UINT typeFlag = uFlags & SHCNF_TYPE;
283
284     Pidls[0] = NULL;
285     Pidls[1] = NULL;
286
287     TRACE("(0x%08x,0x%08x,%p,%p):stub.\n", wEventId, uFlags, dwItem1, dwItem2);
288
289     if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
290     {
291         TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
292         dwItem1 = 0;
293         dwItem2 = 0;
294         return;
295     }
296     else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
297     {
298         TRACE("dwItem2 is not zero, but should be\n");
299         dwItem2 = 0;
300         return;
301     }
302
303     if( ( ( wEventId & SHCNE_NOITEMEVENTS ) && 
304           ( wEventId & ~SHCNE_NOITEMEVENTS ) ) ||
305         ( ( wEventId & SHCNE_ONEITEMEVENTS ) && 
306           ( wEventId & ~SHCNE_ONEITEMEVENTS ) ) ||
307         ( ( wEventId & SHCNE_TWOITEMEVENTS ) && 
308           ( wEventId & ~SHCNE_TWOITEMEVENTS ) ) )
309     {
310         WARN("mutually incompatible events listed\n");
311         return;
312     }
313
314     /* convert paths in IDLists*/
315     switch (typeFlag)
316     {
317     case SHCNF_PATHA:
318         if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA((LPCSTR)dwItem1);
319         if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA((LPCSTR)dwItem2);
320         break;
321     case SHCNF_PATHW:
322         if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW((LPCWSTR)dwItem1);
323         if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW((LPCWSTR)dwItem2);
324         break;
325     case SHCNF_IDLIST:
326         Pidls[0] = (LPCITEMIDLIST)dwItem1;
327         Pidls[1] = (LPCITEMIDLIST)dwItem2;
328         break;
329     case SHCNF_PRINTERA:
330     case SHCNF_PRINTERW:
331         FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
332         return;
333     case SHCNF_DWORD:
334     default:
335         FIXME("unknown type %08x\n",typeFlag);
336         return;
337     }
338
339     {
340         WCHAR path[MAX_PATH];
341
342         if( Pidls[0] && SHGetPathFromIDListW(Pidls[0], path ))
343             TRACE("notify %08x on item1 = %s\n", wEventId, debugstr_w(path));
344     
345         if( Pidls[1] && SHGetPathFromIDListW(Pidls[1], path ))
346             TRACE("notify %08x on item2 = %s\n", wEventId, debugstr_w(path));
347     }
348
349     EnterCriticalSection(&SHELL32_ChangenotifyCS);
350
351     /* loop through the list */
352     for( ptr = head; ptr; ptr = ptr->next )
353     {
354         BOOL notify;
355         DWORD i;
356
357         notify = FALSE;
358
359         TRACE("trying %p\n", ptr);
360
361         for( i=0; (i<ptr->cidl) && !notify ; i++ )
362         {
363             LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
364             BOOL subtree = ptr->apidl[i].fRecursive;
365
366             if (wEventId & ptr->wEventMask)
367             {
368                 if( !pidl )          /* all ? */
369                     notify = TRUE;
370                 else if( wEventId & SHCNE_NOITEMEVENTS )
371                     notify = TRUE;
372                 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
373                     notify = should_notify( Pidls[0], pidl, subtree );
374                 else if( wEventId & SHCNE_TWOITEMEVENTS )
375                     notify = should_notify( Pidls[1], pidl, subtree );
376             }
377         }
378
379         if( !notify )
380             continue;
381
382         ptr->pidlSignaled = ILClone(Pidls[0]);
383
384         TRACE("notifying %s, event %s(%x) before\n", NodeName( ptr ), DumpEvent(
385                wEventId ),wEventId );
386
387         ptr->wSignalledEvent |= wEventId;
388
389         if (ptr->dwFlags  & SHCNRF_NewDelivery)
390             SendMessageA(ptr->hwnd, ptr->uMsg, (WPARAM) ptr, (LPARAM) GetCurrentProcessId());
391         else
392             SendMessageA(ptr->hwnd, ptr->uMsg, (WPARAM)Pidls, wEventId);
393
394         TRACE("notifying %s, event %s(%x) after\n", NodeName( ptr ), DumpEvent(
395                 wEventId ),wEventId );
396
397     }
398     TRACE("notify Done\n");
399     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
400     
401     /* if we allocated it, free it. The ANSI flag is also set in its Unicode sibling. */
402     if ((typeFlag & SHCNF_PATHA) || (typeFlag & SHCNF_PRINTERA))
403     {
404         SHFree((LPITEMIDLIST)Pidls[0]);
405         SHFree((LPITEMIDLIST)Pidls[1]);
406     }
407 }
408
409 /*************************************************************************
410  * NTSHChangeNotifyRegister                     [SHELL32.640]
411  * NOTES
412  *   Idlist is an array of structures and Count specifies how many items in the array
413  *   (usually just one I think).
414  */
415 DWORD WINAPI NTSHChangeNotifyRegister(
416     HWND hwnd,
417     LONG events1,
418     LONG events2,
419     DWORD msg,
420     int count,
421     SHChangeNotifyEntry *idlist)
422 {
423     FIXME("(%p,0x%08x,0x%08x,0x%08x,0x%08x,%p):semi stub.\n",
424                 hwnd,events1,events2,msg,count,idlist);
425
426     return (DWORD) SHChangeNotifyRegister(hwnd, events1, events2, msg, count, idlist);
427 }
428
429 /*************************************************************************
430  * SHChangeNotification_Lock                    [SHELL32.644]
431  */
432 HANDLE WINAPI SHChangeNotification_Lock(
433         HANDLE hChange,
434         DWORD dwProcessId,
435         LPITEMIDLIST **lppidls,
436         LPLONG lpwEventId)
437 {
438     DWORD i;
439     LPNOTIFICATIONLIST node;
440     LPCITEMIDLIST *idlist;
441
442     TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
443
444     /* EnterCriticalSection(&SHELL32_ChangenotifyCS); */
445
446     node = FindNode( hChange );
447     if( node )
448     {
449         idlist = SHAlloc( sizeof(LPCITEMIDLIST *) * node->cidl );
450         for(i=0; i<node->cidl; i++)
451             idlist[i] = (LPCITEMIDLIST)node->pidlSignaled;
452         *lpwEventId = node->wSignalledEvent;
453         *lppidls = (LPITEMIDLIST*)idlist;
454         node->wSignalledEvent = 0;
455     }
456     else
457         ERR("Couldn't find %p\n", hChange );
458
459     /* LeaveCriticalSection(&SHELL32_ChangenotifyCS); */
460
461     return (HANDLE) node;
462 }
463
464 /*************************************************************************
465  * SHChangeNotification_Unlock                  [SHELL32.645]
466  */
467 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
468 {
469     TRACE("\n");
470     return 1;
471 }
472
473 /*************************************************************************
474  * NTSHChangeNotifyDeregister                   [SHELL32.641]
475  */
476 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
477 {
478     FIXME("(0x%08x):semi stub.\n",x1);
479
480     return SHChangeNotifyDeregister( x1 );
481 }