shell32: Fixed SHCNRF_NewDelivery flag support in SHChangeNotify.
[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/list.h"
29 #include "wine/debug.h"
30 #include "shell32_main.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(shell);
33
34 static CRITICAL_SECTION SHELL32_ChangenotifyCS;
35 static CRITICAL_SECTION_DEBUG critsect_debug =
36 {
37     0, 0, &SHELL32_ChangenotifyCS,
38     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
39       0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ChangenotifyCS") }
40 };
41 static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
42
43 typedef SHChangeNotifyEntry *LPNOTIFYREGISTER;
44
45 /* internal list of notification clients (internal) */
46 typedef struct _NOTIFICATIONLIST
47 {
48         struct list entry;
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         DWORD dwFlags;          /* client flags */
55         ULONG id;
56 } NOTIFICATIONLIST, *LPNOTIFICATIONLIST;
57
58 static struct list notifications = LIST_INIT( notifications );
59 static LONG next_id;
60
61 #define SHCNE_NOITEMEVENTS ( \
62    SHCNE_ASSOCCHANGED )
63
64 #define SHCNE_ONEITEMEVENTS ( \
65    SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
66    SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
67    SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
68    SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
69    SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE )
70
71 #define SHCNE_TWOITEMEVENTS ( \
72    SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM )
73
74 /* for dumping events */
75 static const char * DumpEvent( LONG event )
76 {
77     if( event == SHCNE_ALLEVENTS )
78         return "SHCNE_ALLEVENTS";
79 #define DUMPEV(x)  ,( event & SHCNE_##x )? #x " " : ""
80     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"
81     DUMPEV(RENAMEITEM)
82     DUMPEV(CREATE)
83     DUMPEV(DELETE)
84     DUMPEV(MKDIR)
85     DUMPEV(RMDIR)
86     DUMPEV(MEDIAINSERTED)
87     DUMPEV(MEDIAREMOVED)
88     DUMPEV(DRIVEREMOVED)
89     DUMPEV(DRIVEADD)
90     DUMPEV(NETSHARE)
91     DUMPEV(NETUNSHARE)
92     DUMPEV(ATTRIBUTES)
93     DUMPEV(UPDATEDIR)
94     DUMPEV(UPDATEITEM)
95     DUMPEV(SERVERDISCONNECT)
96     DUMPEV(UPDATEIMAGE)
97     DUMPEV(DRIVEADDGUI)
98     DUMPEV(RENAMEFOLDER)
99     DUMPEV(FREESPACE)
100     DUMPEV(EXTENDED_EVENT)
101     DUMPEV(ASSOCCHANGED)
102     DUMPEV(INTERRUPT)
103     );
104 #undef DUMPEV
105 }
106
107 static const char * NodeName(const NOTIFICATIONLIST *item)
108 {
109     const char *str;
110     WCHAR path[MAX_PATH];
111
112     if(SHGetPathFromIDListW(item->apidl[0].pidl, path ))
113         str = wine_dbg_sprintf("%s", debugstr_w(path));
114     else
115         str = wine_dbg_sprintf("<not a disk file>" );
116     return str;
117 }
118
119 static void DeleteNode(LPNOTIFICATIONLIST item)
120 {
121     UINT i;
122
123     TRACE("item=%p\n", item);
124
125     /* remove item from list */
126     list_remove( &item->entry );
127
128     /* free the item */
129     for (i=0; i<item->cidl; i++)
130         SHFree((LPITEMIDLIST)item->apidl[i].pidl);
131     SHFree(item->apidl);
132     SHFree(item);
133 }
134
135 void InitChangeNotifications(void)
136 {
137 }
138
139 void FreeChangeNotifications(void)
140 {
141     LPNOTIFICATIONLIST ptr, next;
142
143     TRACE("\n");
144
145     EnterCriticalSection(&SHELL32_ChangenotifyCS);
146
147     LIST_FOR_EACH_ENTRY_SAFE( ptr, next, &notifications, NOTIFICATIONLIST, entry )
148         DeleteNode( ptr );
149
150     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
151
152     DeleteCriticalSection(&SHELL32_ChangenotifyCS);
153 }
154
155 /*************************************************************************
156  * SHChangeNotifyRegister                       [SHELL32.2]
157  *
158  */
159 ULONG WINAPI
160 SHChangeNotifyRegister(
161     HWND hwnd,
162     int fSources,
163     LONG wEventMask,
164     UINT uMsg,
165     int cItems,
166     SHChangeNotifyEntry *lpItems)
167 {
168     LPNOTIFICATIONLIST item;
169     int i;
170
171     item = SHAlloc(sizeof(NOTIFICATIONLIST));
172
173     TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
174         hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item);
175
176     item->cidl = cItems;
177     item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems);
178     for(i=0;i<cItems;i++)
179     {
180         item->apidl[i].pidl = ILClone(lpItems[i].pidl);
181         item->apidl[i].fRecursive = lpItems[i].fRecursive;
182     }
183     item->hwnd = hwnd;
184     item->uMsg = uMsg;
185     item->wEventMask = wEventMask;
186     item->dwFlags = fSources;
187     item->id = InterlockedIncrement( &next_id );
188
189     TRACE("new node: %s\n", NodeName( item ));
190
191     EnterCriticalSection(&SHELL32_ChangenotifyCS);
192
193     list_add_tail( &notifications, &item->entry );
194
195     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
196
197     return item->id;
198 }
199
200 /*************************************************************************
201  * SHChangeNotifyDeregister                     [SHELL32.4]
202  */
203 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
204 {
205     LPNOTIFICATIONLIST node;
206
207     TRACE("(0x%08x)\n", hNotify);
208
209     EnterCriticalSection(&SHELL32_ChangenotifyCS);
210
211     LIST_FOR_EACH_ENTRY( node, &notifications, NOTIFICATIONLIST, entry )
212     {
213         if (node->id == hNotify)
214         {
215             DeleteNode( node );
216             LeaveCriticalSection(&SHELL32_ChangenotifyCS);
217             return TRUE;
218         }
219     }
220     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
221     return FALSE;
222 }
223
224 /*************************************************************************
225  * SHChangeNotifyUpdateEntryList                [SHELL32.5]
226  */
227 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
228                               DWORD unknown3, DWORD unknown4)
229 {
230     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
231           unknown1, unknown2, unknown3, unknown4);
232
233     return -1;
234 }
235
236 struct new_delivery_notification
237 {
238     LONG event;
239     DWORD pidl1_size;
240     DWORD pidl2_size;
241     LPITEMIDLIST pidls[2];
242     BYTE data[1];
243 };
244
245 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
246 {
247     TRACE("%p %p %d\n", changed, watched, sub );
248     if ( !watched )
249         return FALSE;
250     if (ILIsEqual( watched, changed ) )
251         return TRUE;
252     if( sub && ILIsParent( watched, changed, FALSE ) )
253         return TRUE;
254     return FALSE;
255 }
256
257 /*************************************************************************
258  * SHChangeNotify                               [SHELL32.@]
259  */
260 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
261 {
262     struct notification_recipients {
263         struct list entry;
264         HWND hwnd;
265         DWORD msg;
266         DWORD flags;
267     } *cur, *next;
268
269     HANDLE shared_data = NULL;
270     LPITEMIDLIST Pidls[2];
271     LPNOTIFICATIONLIST ptr;
272     struct list recipients;
273
274     Pidls[0] = NULL;
275     Pidls[1] = NULL;
276
277     TRACE("(0x%08x,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2);
278
279     if(uFlags & ~(SHCNF_TYPE|SHCNF_FLUSH))
280         FIXME("ignoring unsupported flags: %x\n", uFlags);
281
282     if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
283     {
284         TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
285         dwItem1 = 0;
286         dwItem2 = 0;
287         return;
288     }
289     else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
290     {
291         TRACE("dwItem2 is not zero, but should be\n");
292         dwItem2 = 0;
293         return;
294     }
295
296     if( ( ( wEventId & SHCNE_NOITEMEVENTS ) && 
297           ( wEventId & ~SHCNE_NOITEMEVENTS ) ) ||
298         ( ( wEventId & SHCNE_ONEITEMEVENTS ) && 
299           ( wEventId & ~SHCNE_ONEITEMEVENTS ) ) ||
300         ( ( wEventId & SHCNE_TWOITEMEVENTS ) && 
301           ( wEventId & ~SHCNE_TWOITEMEVENTS ) ) )
302     {
303         WARN("mutually incompatible events listed\n");
304         return;
305     }
306
307     /* convert paths in IDLists*/
308     switch (uFlags & SHCNF_TYPE)
309     {
310     case SHCNF_PATHA:
311         if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA(dwItem1);
312         if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA(dwItem2);
313         break;
314     case SHCNF_PATHW:
315         if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1);
316         if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2);
317         break;
318     case SHCNF_IDLIST:
319         Pidls[0] = ILClone(dwItem1);
320         Pidls[1] = ILClone(dwItem2);
321         break;
322     case SHCNF_PRINTERA:
323     case SHCNF_PRINTERW:
324         FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
325         return;
326     case SHCNF_DWORD:
327     default:
328         FIXME("unknown type %08x\n", uFlags & SHCNF_TYPE);
329         return;
330     }
331
332     list_init(&recipients);
333     EnterCriticalSection(&SHELL32_ChangenotifyCS);
334     LIST_FOR_EACH_ENTRY( ptr, &notifications, NOTIFICATIONLIST, entry )
335     {
336         struct notification_recipients *item;
337         BOOL notify = FALSE;
338         DWORD i;
339
340         for( i=0; (i<ptr->cidl) && !notify ; i++ )
341         {
342             LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
343             BOOL subtree = ptr->apidl[i].fRecursive;
344
345             if (wEventId & ptr->wEventMask)
346             {
347                 if( !pidl )          /* all ? */
348                     notify = TRUE;
349                 else if( wEventId & SHCNE_NOITEMEVENTS )
350                     notify = TRUE;
351                 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
352                     notify = should_notify( Pidls[0], pidl, subtree );
353                 else if( wEventId & SHCNE_TWOITEMEVENTS )
354                     notify = should_notify( Pidls[1], pidl, subtree );
355             }
356         }
357
358         if( !notify )
359             continue;
360
361         item = SHAlloc(sizeof(struct notification_recipients));
362         if(!item) {
363             ERR("out of memory\n");
364             continue;
365         }
366
367         item->hwnd = ptr->hwnd;
368         item->msg = ptr->uMsg;
369         item->flags = ptr->dwFlags;
370         list_add_tail(&recipients, &item->entry);
371     }
372     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
373
374     LIST_FOR_EACH_ENTRY_SAFE(cur, next, &recipients, struct notification_recipients, entry)
375     {
376         TRACE("notifying %p, event %s(%x)\n", cur->hwnd, DumpEvent(wEventId), wEventId);
377
378         if (cur->flags  & SHCNRF_NewDelivery) {
379             if(!shared_data) {
380                 struct new_delivery_notification *notification;
381                 UINT size1 = ILGetSize(Pidls[0]), size2 = ILGetSize(Pidls[1]);
382                 UINT offset = (size1+sizeof(int)-1)/sizeof(int)*sizeof(int);
383
384                 notification = SHAlloc(sizeof(struct new_delivery_notification)+offset+size2);
385                 if(!notification) {
386                     ERR("out of memory\n");
387                 } else {
388                     notification->event = wEventId;
389                     notification->pidl1_size = size1;
390                     notification->pidl2_size = size2;
391                     if(size1)
392                         memcpy(notification->data, Pidls[0], size1);
393                     if(size2)
394                         memcpy(notification->data+offset, Pidls[1], size2);
395
396                     shared_data = SHAllocShared(notification,
397                         sizeof(struct new_delivery_notification)+size1+size2,
398                         GetCurrentProcessId());
399                     SHFree(notification);
400                 }
401             }
402
403             if(shared_data)
404                 SendMessageA(cur->hwnd, cur->msg, (WPARAM)shared_data, GetCurrentProcessId());
405             else
406                 ERR("out of memory\n");
407         } else {
408             SendMessageA(cur->hwnd, cur->msg, (WPARAM)Pidls, wEventId);
409         }
410
411         list_remove(&cur->entry);
412         SHFree(cur);
413     }
414     SHFreeShared(shared_data, GetCurrentProcessId());
415     SHFree(Pidls[0]);
416     SHFree(Pidls[1]);
417
418     if (wEventId & SHCNE_ASSOCCHANGED)
419     {
420         static const WCHAR args[] = {' ','-','a',0 };
421         TRACE("refreshing file type associations\n");
422         run_winemenubuilder( args );
423     }
424 }
425
426 /*************************************************************************
427  * NTSHChangeNotifyRegister                     [SHELL32.640]
428  * NOTES
429  *   Idlist is an array of structures and Count specifies how many items in the array
430  *   (usually just one I think).
431  */
432 DWORD WINAPI NTSHChangeNotifyRegister(
433     HWND hwnd,
434     LONG events1,
435     LONG events2,
436     DWORD msg,
437     int count,
438     SHChangeNotifyEntry *idlist)
439 {
440     FIXME("(%p,0x%08x,0x%08x,0x%08x,0x%08x,%p):semi stub.\n",
441                 hwnd,events1,events2,msg,count,idlist);
442
443     return SHChangeNotifyRegister(hwnd, events1, events2, msg, count, idlist);
444 }
445
446 /*************************************************************************
447  * SHChangeNotification_Lock                    [SHELL32.644]
448  */
449 HANDLE WINAPI SHChangeNotification_Lock(
450         HANDLE hChange,
451         DWORD dwProcessId,
452         LPITEMIDLIST **lppidls,
453         LPLONG lpwEventId)
454 {
455     struct new_delivery_notification *ndn;
456     UINT offset;
457
458     TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
459
460     ndn = SHLockShared(hChange, dwProcessId);
461     if(!ndn) {
462         WARN("SHLockShared failed\n");
463         return NULL;
464     }
465
466     if(lppidls) {
467         offset = (ndn->pidl1_size+sizeof(int)-1)/sizeof(int)*sizeof(int);
468         ndn->pidls[0] = ndn->pidl1_size ? (LPITEMIDLIST)ndn->data : NULL;
469         ndn->pidls[1] = ndn->pidl2_size ? (LPITEMIDLIST)(ndn->data+offset) : NULL;
470         *lppidls = ndn->pidls;
471     }
472
473     if(lpwEventId)
474         *lpwEventId = ndn->event;
475
476     return ndn;
477 }
478
479 /*************************************************************************
480  * SHChangeNotification_Unlock                  [SHELL32.645]
481  */
482 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
483 {
484     TRACE("%p\n", hLock);
485     return SHUnlockShared(hLock);
486 }
487
488 /*************************************************************************
489  * NTSHChangeNotifyDeregister                   [SHELL32.641]
490  */
491 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
492 {
493     FIXME("(0x%08x):semi stub.\n",x1);
494
495     return SHChangeNotifyDeregister( x1 );
496 }