Add debug info for IExtractIconW.
[wine] / dlls / shell32 / shlfileop.c
1 /*
2  * SHFileOperation
3  *
4  * Copyright 2000 Juergen Schmied
5  * Copyright 2002 Andriy Palamarchuk
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 #include <string.h>
22
23 #include "winreg.h"
24 #include "shellapi.h"
25 #include "shlobj.h"
26 #include "shresdef.h"
27 #include "shell32_main.h"
28 #include "undocshell.h"
29 #include "shlwapi.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(shell);
33
34 BOOL SHELL_WarnItemDelete (int nKindOfDialog, LPCSTR szDir)
35 {
36         char szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
37
38         if(nKindOfDialog == ASK_DELETE_FILE)
39         {
40           LoadStringA(shell32_hInstance, IDS_DELETEITEM_TEXT, szText,
41                 sizeof(szText));
42           LoadStringA(shell32_hInstance, IDS_DELETEITEM_CAPTION,
43                 szCaption, sizeof(szCaption));
44         }
45         else if(nKindOfDialog == ASK_DELETE_FOLDER)
46         {
47           LoadStringA(shell32_hInstance, IDS_DELETEITEM_TEXT, szText,
48                 sizeof(szText));
49           LoadStringA(shell32_hInstance, IDS_DELETEFOLDER_CAPTION,
50                 szCaption, sizeof(szCaption));
51         }
52         else if(nKindOfDialog == ASK_DELETE_MULTIPLE_ITEM)
53         {
54           LoadStringA(shell32_hInstance, IDS_DELETEMULTIPLE_TEXT, szText,
55                 sizeof(szText));
56           LoadStringA(shell32_hInstance, IDS_DELETEITEM_CAPTION,
57                 szCaption, sizeof(szCaption));
58         }
59         else {
60           FIXME("Called without a valid nKindOfDialog specified!\n");
61           LoadStringA(shell32_hInstance, IDS_DELETEITEM_TEXT, szText,
62                 sizeof(szText));
63           LoadStringA(shell32_hInstance, IDS_DELETEITEM_CAPTION,
64                 szCaption, sizeof(szCaption));
65         }
66
67         FormatMessageA(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
68             szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)&szDir);
69
70         return (IDOK == MessageBoxA(GetActiveWindow(), szBuffer, szCaption, MB_OKCANCEL | MB_ICONEXCLAMATION));
71 }
72
73 /**************************************************************************
74  *      SHELL_DeleteDirectoryA()
75  *
76  * like rm -r
77  */
78
79 BOOL SHELL_DeleteDirectoryA(LPCSTR pszDir, BOOL bShowUI)
80 {
81         BOOL            ret = FALSE;
82         HANDLE          hFind;
83         WIN32_FIND_DATAA wfd;
84         char            szTemp[MAX_PATH];
85
86         strcpy(szTemp, pszDir);
87         PathAddBackslashA(szTemp);
88         strcat(szTemp, "*.*");
89
90         if (bShowUI && !SHELL_WarnItemDelete(ASK_DELETE_FOLDER, pszDir))
91           return FALSE;
92
93         if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(szTemp, &wfd)))
94         {
95           do
96           {
97             if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, ".."))
98             {
99               strcpy(szTemp, pszDir);
100               PathAddBackslashA(szTemp);
101               strcat(szTemp, wfd.cFileName);
102
103               if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
104                 SHELL_DeleteDirectoryA(szTemp, FALSE);
105               else
106                 DeleteFileA(szTemp);
107             }
108           } while(FindNextFileA(hFind, &wfd));
109
110           FindClose(hFind);
111           ret = RemoveDirectoryA(pszDir);
112         }
113
114         return ret;
115 }
116
117 /**************************************************************************
118  *      SHELL_DeleteFileA()
119  */
120
121 BOOL SHELL_DeleteFileA(LPCSTR pszFile, BOOL bShowUI)
122 {
123         if (bShowUI && !SHELL_WarnItemDelete(ASK_DELETE_FILE, pszFile))
124                 return FALSE;
125
126         return DeleteFileA(pszFile);
127 }
128
129 /*************************************************************************
130  * SHCreateDirectory                            [SHELL32.165]
131  *
132  * NOTES
133  *  exported by ordinal
134  *  not sure about LPSECURITY_ATTRIBUTES
135  */
136 DWORD WINAPI SHCreateDirectory(LPSECURITY_ATTRIBUTES sec,LPCSTR path)
137 {
138         DWORD ret;
139         TRACE("(%p,%s)\n",sec,path);
140         if ((ret = CreateDirectoryA(path,sec)))
141         {
142           SHChangeNotifyA(SHCNE_MKDIR, SHCNF_PATHA, path, NULL);
143         }
144         return ret;
145 }
146
147 /************************************************************************
148  *      Win32DeleteFile                         [SHELL32.164]
149  *
150  * Deletes a file.  Also triggers a change notify if one exists.
151  *
152  * FIXME:
153  * Verified on Win98 / IE 5 (SHELL32 4.72, March 1999 build) to be
154  * ANSI.  Is this Unicode on NT?
155  *
156  */
157
158 BOOL WINAPI Win32DeleteFile(LPSTR fName)
159 {
160         TRACE("%p(%s)\n", fName, fName);
161
162         DeleteFileA(fName);
163         SHChangeNotifyA(SHCNE_DELETE, SHCNF_PATHA, fName, NULL);
164         return TRUE;
165 }
166
167 /**************************************************************************
168  *      SHELL_FileNamesMatch()
169  *
170  * Accepts two \0 delimited lists of the file names. Checks whether number of
171  * files in the both lists is the same.
172  */
173 BOOL SHELL_FileNamesMatch(LPCSTR pszFiles1, LPCSTR pszFiles2)
174 {
175     while ((pszFiles1[strlen(pszFiles1) + 1] != '\0') &&
176            (pszFiles2[strlen(pszFiles2) + 1] != '\0'))
177     {
178         pszFiles1 += strlen(pszFiles1) + 1;
179         pszFiles2 += strlen(pszFiles2) + 1;
180     }
181
182     return
183         ((pszFiles1[strlen(pszFiles1) + 1] == '\0') &&
184          (pszFiles2[strlen(pszFiles2) + 1] == '\0')) ||
185         ((pszFiles1[strlen(pszFiles1) + 1] != '\0') &&
186          (pszFiles2[strlen(pszFiles2) + 1] != '\0'));
187 }
188
189 /*************************************************************************
190  * SHFileOperationA                             [SHELL32.@]
191  *
192  * NOTES
193  *     exported by name
194  */
195 DWORD WINAPI SHFileOperationA (LPSHFILEOPSTRUCTA lpFileOp)
196 {
197         LPSTR pFrom = (LPSTR)lpFileOp->pFrom;
198         LPSTR pTo = (LPSTR)lpFileOp->pTo;
199         LPSTR pTempTo;
200         TRACE("flags (0x%04x) : %s%s%s%s%s%s%s%s%s%s%s%s \n", lpFileOp->fFlags,
201                 lpFileOp->fFlags & FOF_MULTIDESTFILES ? "FOF_MULTIDESTFILES " : "",
202                 lpFileOp->fFlags & FOF_CONFIRMMOUSE ? "FOF_CONFIRMMOUSE " : "",
203                 lpFileOp->fFlags & FOF_SILENT ? "FOF_SILENT " : "",
204                 lpFileOp->fFlags & FOF_RENAMEONCOLLISION ? "FOF_RENAMEONCOLLISION " : "",
205                 lpFileOp->fFlags & FOF_NOCONFIRMATION ? "FOF_NOCONFIRMATION " : "",
206                 lpFileOp->fFlags & FOF_WANTMAPPINGHANDLE ? "FOF_WANTMAPPINGHANDLE " : "",
207                 lpFileOp->fFlags & FOF_ALLOWUNDO ? "FOF_ALLOWUNDO " : "",
208                 lpFileOp->fFlags & FOF_FILESONLY ? "FOF_FILESONLY " : "",
209                 lpFileOp->fFlags & FOF_SIMPLEPROGRESS ? "FOF_SIMPLEPROGRESS " : "",
210                 lpFileOp->fFlags & FOF_NOCONFIRMMKDIR ? "FOF_NOCONFIRMMKDIR " : "",
211                 lpFileOp->fFlags & FOF_NOERRORUI ? "FOF_NOERRORUI " : "",
212                 lpFileOp->fFlags & 0xf800 ? "MORE-UNKNOWN-Flags" : "");
213         switch(lpFileOp->wFunc) {
214         case FO_COPY:
215         case FO_MOVE:
216         {
217                 /* establish when pTo is interpreted as the name of the destination file
218                  * or the directory where the Fromfile should be copied to.
219                  * This depends on:
220                  * (1) pTo points to the name of an existing directory;
221                  * (2) the flag FOF_MULTIDESTFILES is present;
222                  * (3) whether pFrom point to multiple filenames.
223                  *
224                  * Some experiments:
225                  *
226                  * destisdir               1 1 1 1 0 0 0 0
227                  * FOF_MULTIDESTFILES      1 1 0 0 1 1 0 0
228                  * multiple from filenames 1 0 1 0 1 0 1 0
229                  *                         ---------------
230                  * copy files to dir       1 0 1 1 0 0 1 0
231                  * create dir              0 0 0 0 0 0 1 0
232                  */
233                 int multifrom = pFrom[strlen(pFrom) + 1] != '\0';
234                 int destisdir = PathIsDirectoryA( pTo );
235                 int todir = 0;
236
237                 if (lpFileOp->wFunc == FO_COPY)
238                     TRACE("File Copy:\n");
239                 else
240                     TRACE("File Move:\n");
241
242                 if( destisdir ) {
243                     if ( !((lpFileOp->fFlags & FOF_MULTIDESTFILES) && !multifrom))
244                         todir = 1;
245                 } else {
246                     if ( !(lpFileOp->fFlags & FOF_MULTIDESTFILES) && multifrom)
247                         todir = 1;
248                 }
249
250                 if ((pTo[strlen(pTo) + 1] != '\0') &&
251                     !(lpFileOp->fFlags & FOF_MULTIDESTFILES))
252                 {
253                     WARN("Attempt to use multiple file names as a destination "
254                          "without specifying FOF_MULTIDESTFILES\n");
255                     return 1;
256                 }
257
258                 if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
259                     !SHELL_FileNamesMatch(pTo, pFrom))
260                 {
261                     WARN("Attempt to use multiple file names as a destination "
262                          "with mismatching number of files in the source and "
263                          "destination lists\n");
264                     return 1;
265                 }
266
267                 if ( todir ) {
268                     char szTempFrom[MAX_PATH];
269                     char *fromfile;
270                     int lenPTo;
271                     if ( ! destisdir) {
272                         TRACE("   creating directory %s\n",pTo);
273                         SHCreateDirectory(NULL,pTo);
274                     }
275                     lenPTo = strlen(pTo);
276                     while(1) {
277                         HANDLE hFind;
278                         WIN32_FIND_DATAA wfd;
279
280                         if(!pFrom[0]) break;
281                         TRACE("   From Pattern='%s'\n", pFrom);
282                         if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(pFrom, &wfd)))
283                         {
284                           do
285                           {
286                             if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, ".."))
287                             {
288                               strcpy(szTempFrom, pFrom);
289
290                               pTempTo = HeapAlloc(GetProcessHeap(), 0,
291                                                   lenPTo + strlen(wfd.cFileName) + 5);
292                               if (pTempTo) {
293                                   strcpy(pTempTo,pTo);
294                                   PathAddBackslashA(pTempTo);
295                                   strcat(pTempTo,wfd.cFileName);
296
297                                   fromfile = PathFindFileNameA(szTempFrom);
298                                   fromfile[0] = '\0';
299                                   PathAddBackslashA(szTempFrom);
300                                   strcat(szTempFrom, wfd.cFileName);
301                                   TRACE("   From='%s' To='%s'\n", szTempFrom, pTempTo);
302                                   if(lpFileOp->wFunc == FO_COPY)
303                                   {
304                                       if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
305                                       {
306                                           /* copy recursively */
307                                           if(!(lpFileOp->fFlags & FOF_FILESONLY))
308                                           {
309                                               SHFILEOPSTRUCTA shfo;
310
311                                               SHCreateDirectory(NULL,pTempTo);
312                                               PathAddBackslashA(szTempFrom);
313                                               strcat(szTempFrom, "*.*");
314                                               szTempFrom[strlen(szTempFrom) + 1] = '\0';
315                                               pTempTo[strlen(pTempTo) + 1] = '\0';
316                                               memcpy(&shfo, lpFileOp, sizeof(shfo));
317                                               shfo.pFrom = szTempFrom;
318                                               shfo.pTo = pTempTo;
319                                               SHFileOperationA(&shfo);
320
321                                               szTempFrom[strlen(szTempFrom) - 4] = '\0';
322                                           }
323                                       }
324                                       else
325                                           CopyFileA(szTempFrom, pTempTo, FALSE);
326                                   }
327                                   else
328                                   {
329                                       /* move file/directory */
330                                       MoveFileA(szTempFrom, pTempTo);
331                                   }
332                                   HeapFree(GetProcessHeap(), 0, pTempTo);
333                               }
334                             }
335                           } while(FindNextFileA(hFind, &wfd));
336                           FindClose(hFind);
337                         }
338                         else
339                         {
340                             /* can't find file with specified name */
341                             break;
342                         }
343                         pFrom += strlen(pFrom) + 1;
344                     }
345                 } else {
346                     while(1) {
347                             if(!pFrom[0]) break;
348                             if(!pTo[0]) break;
349                             TRACE("   From='%s' To='%s'\n", pFrom, pTo);
350
351                             pTempTo = HeapAlloc(GetProcessHeap(), 0, strlen(pTo)+1);
352                             if (pTempTo)
353                             {
354                                 strcpy( pTempTo, pTo );
355                                 PathRemoveFileSpecA(pTempTo);
356                                 TRACE("   Creating Directory '%s'\n", pTempTo);
357                                 SHCreateDirectory(NULL,pTempTo);
358                                 HeapFree(GetProcessHeap(), 0, pTempTo);
359                             }
360                             if (lpFileOp->wFunc == FO_COPY)
361                                 CopyFileA(pFrom, pTo, FALSE);
362                             else
363                                 MoveFileA(pFrom, pTo);
364
365                             pFrom += strlen(pFrom) + 1;
366                             pTo += strlen(pTo) + 1;
367                     }
368                 }
369                 TRACE("Setting AnyOpsAborted=FALSE\n");
370                 lpFileOp->fAnyOperationsAborted=FALSE;
371                 return 0;
372         }
373
374         case FO_DELETE:
375         {
376                 HANDLE          hFind;
377                 WIN32_FIND_DATAA wfd;
378                 char            szTemp[MAX_PATH];
379                 char            *file_name;
380
381                 TRACE("File Delete:\n");
382                 while(1) {
383                         if(!pFrom[0]) break;
384                         TRACE("   Pattern='%s'\n", pFrom);
385                         if(INVALID_HANDLE_VALUE != (hFind = FindFirstFileA(pFrom, &wfd)))
386                         {
387                           do
388                           {
389                             if(strcasecmp(wfd.cFileName, ".") && strcasecmp(wfd.cFileName, ".."))
390                             {
391                               strcpy(szTemp, pFrom);
392                               file_name = PathFindFileNameA(szTemp);
393                               file_name[0] = '\0';
394                               PathAddBackslashA(szTemp);
395                               strcat(szTemp, wfd.cFileName);
396
397                               TRACE("   File='%s'\n", szTemp);
398                               if(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
399                               {
400                                 if(!(lpFileOp->fFlags & FOF_FILESONLY))
401                                     SHELL_DeleteDirectoryA(szTemp, FALSE);
402                               }
403                               else
404                                 DeleteFileA(szTemp);
405                             }
406                           } while(FindNextFileA(hFind, &wfd));
407
408                           FindClose(hFind);
409                         }
410                         pFrom += strlen(pFrom) + 1;
411                 }
412                 TRACE("Setting AnyOpsAborted=FALSE\n");
413                 lpFileOp->fAnyOperationsAborted=FALSE;
414                 return 0;
415         }
416
417         case FO_RENAME:
418             TRACE("File Rename:\n");
419             if (pFrom[strlen(pFrom) + 1] != '\0')
420             {
421                 WARN("Attempt to rename more than one file\n");
422                 return 1;
423             }
424             lpFileOp->fAnyOperationsAborted = FALSE;
425             TRACE("From %s, To %s\n", pFrom, pTo);
426             return !MoveFileA(pFrom, pTo);
427
428         default:
429                 FIXME("Unhandled shell file operation %d\n", lpFileOp->wFunc);
430         }
431
432         return 1;
433 }
434
435 /*************************************************************************
436  * SHFileOperationW                             [SHELL32.@]
437  *
438  * NOTES
439  *     exported by name
440  */
441 DWORD WINAPI SHFileOperationW (LPSHFILEOPSTRUCTW lpFileOp)
442 {
443         FIXME("(%p):stub.\n", lpFileOp);
444         return 1;
445 }
446
447 /*************************************************************************
448  * SHFileOperation                              [SHELL32.@]
449  *
450  */
451 DWORD WINAPI SHFileOperationAW(LPVOID lpFileOp)
452 {
453         if (SHELL_OsIsUnicode())
454           return SHFileOperationW(lpFileOp);
455         return SHFileOperationA(lpFileOp);
456 }
457
458 /*************************************************************************
459  * SheGetDirW [SHELL32.281]
460  *
461  */
462 HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v)
463 {       FIXME("%p %p stub\n",u,v);
464         return 0;
465 }
466
467 /*************************************************************************
468  * SheChangeDirW [SHELL32.274]
469  *
470  */
471 HRESULT WINAPI SheChangeDirW(LPWSTR u)
472 {       FIXME("(%s),stub\n",debugstr_w(u));
473         return 0;
474 }
475
476 /*************************************************************************
477  * IsNetDrive                   [SHELL32.66]
478  */
479 BOOL WINAPI IsNetDrive(DWORD drive)
480 {
481         char root[4];
482         strcpy(root, "A:\\");
483         root[0] += drive;
484         return (GetDriveTypeA(root) == DRIVE_REMOTE);
485 }