Move OpenQuery into msiquery.c and make it non-static.
[wine] / dlls / shlwapi / istream.c
1 /*
2  * SHLWAPI IStream functions
3  *
4  * Copyright 2002 Jon Griffiths
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdarg.h>
21 #include <string.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "winnls.h"
31 #define NO_SHLWAPI_REG
32 #define NO_SHLWAPI_PATH
33 #include "shlwapi.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
37
38 /* Layout of ISHFileStream object */
39 typedef struct
40 {
41   IStreamVtbl *lpVtbl;
42   ULONG    ref;
43   HANDLE   hFile;
44   DWORD    dwMode;
45   LPOLESTR lpszPath;
46   DWORD    type;
47   DWORD    grfStateBits;
48 } ISHFileStream;
49
50 static HRESULT WINAPI IStream_fnCommit(IStream*,DWORD);
51
52
53 /**************************************************************************
54 *  IStream_fnQueryInterface
55 */
56 static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj)
57 {
58   ISHFileStream *This = (ISHFileStream *)iface;
59
60   TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj);
61
62   *ppvObj = NULL;
63
64   if(IsEqualIID(riid, &IID_IUnknown) ||
65      IsEqualIID(riid, &IID_IStream))
66   {
67     *ppvObj = This;
68
69     IStream_AddRef((IStream*)*ppvObj);
70     return S_OK;
71   }
72   return E_NOINTERFACE;
73 }
74
75 /**************************************************************************
76 *  IStream_fnAddRef
77 */
78 static ULONG WINAPI IStream_fnAddRef(IStream *iface)
79 {
80   ISHFileStream *This = (ISHFileStream *)iface;
81   ULONG refCount = InterlockedIncrement(&This->ref);
82   
83   TRACE("(%p)->(ref before=%lu)\n",This, refCount - 1);
84
85   return refCount;
86 }
87
88 /**************************************************************************
89 *  IStream_fnRelease
90 */
91 static ULONG WINAPI IStream_fnRelease(IStream *iface)
92 {
93   ISHFileStream *This = (ISHFileStream *)iface;
94   ULONG refCount = InterlockedDecrement(&This->ref); 
95
96   TRACE("(%p)->(ref before=%lu)\n",This, refCount + 1);
97   
98   if (!refCount)
99   {
100     IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
101     LocalFree((HLOCAL)This->lpszPath);
102     CloseHandle(This->hFile);
103     HeapFree(GetProcessHeap(), 0, This);
104   }
105   
106   return refCount;
107 }
108
109 /**************************************************************************
110  * IStream_fnRead
111  */
112 static HRESULT WINAPI IStream_fnRead(IStream *iface, void* pv, ULONG cb, ULONG* pcbRead)
113 {
114   ISHFileStream *This = (ISHFileStream *)iface;
115   HRESULT hRet = S_OK;
116   DWORD dwRead = 0;
117
118   TRACE("(%p,%p,0x%08lx,%p)\n", This, pv, cb, pcbRead);
119
120   if (!pv)
121     hRet = STG_E_INVALIDPOINTER;
122   else if (!ReadFile(This->hFile, pv, cb, &dwRead, NULL))
123   {
124     hRet = (HRESULT)GetLastError();
125     if(hRet > 0)
126       hRet = HRESULT_FROM_WIN32(hRet);
127   }
128   if (pcbRead)
129     *pcbRead = dwRead;
130   return hRet;
131 }
132
133 /**************************************************************************
134  * IStream_fnWrite
135  */
136 static HRESULT WINAPI IStream_fnWrite(IStream *iface, const void* pv, ULONG cb, ULONG* pcbWritten)
137 {
138   ISHFileStream *This = (ISHFileStream *)iface;
139   HRESULT hRet = S_OK;
140   DWORD dwWritten = 0;
141
142   TRACE("(%p,%p,0x%08lx,%p)\n", This, pv, cb, pcbWritten);
143
144   if (!pv)
145     hRet = STG_E_INVALIDPOINTER;
146   else if (!(This->dwMode & STGM_WRITE))
147     hRet = E_FAIL;
148   else if (!WriteFile(This->hFile, pv, cb, &dwWritten, NULL))
149   {
150     hRet = (HRESULT)GetLastError();
151     if(hRet > 0)
152       hRet = HRESULT_FROM_WIN32(hRet);
153   }
154   if (pcbWritten)
155     *pcbWritten = dwWritten;
156   return hRet;
157 }
158
159 /**************************************************************************
160  *  IStream_fnSeek
161  */
162 static HRESULT WINAPI IStream_fnSeek(IStream *iface, LARGE_INTEGER dlibMove,
163                                      DWORD dwOrigin, ULARGE_INTEGER* pNewPos)
164 {
165   ISHFileStream *This = (ISHFileStream *)iface;
166   DWORD dwPos;
167
168   TRACE("(%p,%ld,%ld,%p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos);
169
170   IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
171   dwPos = SetFilePointer(This->hFile, dlibMove.u.LowPart, NULL, dwOrigin);
172
173   if (pNewPos)
174   {
175     pNewPos->u.HighPart = 0;
176     pNewPos->u.LowPart = dwPos;
177   }
178   return S_OK;
179 }
180
181 /**************************************************************************
182  * IStream_fnSetSize
183  */
184 static HRESULT WINAPI IStream_fnSetSize(IStream *iface, ULARGE_INTEGER libNewSize)
185 {
186   ISHFileStream *This = (ISHFileStream *)iface;
187
188   TRACE("(%p,%ld)\n", This, libNewSize.u.LowPart);
189   IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
190   return E_NOTIMPL;
191 }
192
193 /**************************************************************************
194  * IStream_fnCopyTo
195  */
196 static HRESULT WINAPI IStream_fnCopyTo(IStream *iface, IStream* pstm, ULARGE_INTEGER cb,
197                                        ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten)
198 {
199   ISHFileStream *This = (ISHFileStream *)iface;
200   char copyBuff[1024];
201   ULONGLONG ulSize;
202   HRESULT hRet = S_OK;
203
204   TRACE("(%p,%p,%ld,%p,%p)\n", This, pstm, cb.u.LowPart, pcbRead, pcbWritten);
205
206   if (pcbRead)
207     pcbRead->QuadPart = 0;
208   if (pcbWritten)
209     pcbWritten->QuadPart = 0;
210
211   if (!pstm)
212     return STG_E_INVALIDPOINTER;
213
214   IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */
215
216   /* Copy data */
217   ulSize = cb.QuadPart;
218   while (ulSize)
219   {
220     ULONG ulLeft, ulAmt;
221
222     ulLeft = ulSize > sizeof(copyBuff) ? sizeof(copyBuff) : ulSize;
223
224     /* Read */
225     hRet = IStream_fnRead(iface, copyBuff, ulLeft, &ulAmt);
226     if (pcbRead)
227       pcbRead->QuadPart += ulAmt;
228     if (FAILED(hRet) || ulAmt != ulLeft)
229       break;
230
231     /* Write */
232     hRet = IStream_fnWrite(pstm, copyBuff, ulLeft, &ulAmt);
233     if (pcbWritten)
234       pcbWritten->QuadPart += ulAmt;
235     if (FAILED(hRet) || ulAmt != ulLeft)
236       break;
237
238     ulSize -= ulLeft;
239   }
240   return hRet;
241 }
242
243 /**************************************************************************
244  * IStream_fnCommit
245  */
246 static HRESULT WINAPI IStream_fnCommit(IStream *iface, DWORD grfCommitFlags)
247 {
248   ISHFileStream *This = (ISHFileStream *)iface;
249
250   TRACE("(%p,%ld)\n", This, grfCommitFlags);
251   /* Currently unbuffered: This function is not needed */
252   return S_OK;
253 }
254
255 /**************************************************************************
256  * IStream_fnRevert
257  */
258 static HRESULT WINAPI IStream_fnRevert(IStream *iface)
259 {
260   ISHFileStream *This = (ISHFileStream *)iface;
261
262   TRACE("(%p)\n", This);
263   return E_NOTIMPL;
264 }
265
266 /**************************************************************************
267  * IStream_fnLockUnlockRegion
268  */
269 static HRESULT WINAPI IStream_fnLockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
270                                                  ULARGE_INTEGER cb, DWORD dwLockType)
271 {
272   ISHFileStream *This = (ISHFileStream *)iface;
273   TRACE("(%p,%ld,%ld,%ld)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
274   return E_NOTIMPL;
275 }
276
277 /*************************************************************************
278  * IStream_fnStat
279  */
280 static HRESULT WINAPI IStream_fnStat(IStream *iface, STATSTG* lpStat,
281                                      DWORD grfStatFlag)
282 {
283   ISHFileStream *This = (ISHFileStream *)iface;
284   BY_HANDLE_FILE_INFORMATION fi;
285   HRESULT hRet = S_OK;
286
287   TRACE("(%p,%p,%ld)\n", This, lpStat, grfStatFlag);
288
289   if (!grfStatFlag)
290     hRet = STG_E_INVALIDPOINTER;
291   else
292   {
293     memset(&fi, 0, sizeof(fi));
294     GetFileInformationByHandle(This->hFile, &fi);
295
296     if (grfStatFlag & STATFLAG_NONAME)
297       lpStat->pwcsName = NULL;
298     else
299       lpStat->pwcsName = StrDupW(This->lpszPath);
300     lpStat->type = This->type;
301     lpStat->cbSize.u.LowPart = fi.nFileSizeLow;
302     lpStat->cbSize.u.HighPart = fi.nFileSizeHigh;
303     lpStat->mtime = fi.ftLastWriteTime;
304     lpStat->ctime = fi.ftCreationTime;
305     lpStat->atime = fi.ftLastAccessTime;
306     lpStat->grfMode = This->dwMode;
307     lpStat->grfLocksSupported = 0;
308     memcpy(&lpStat->clsid, &IID_IStream, sizeof(CLSID));
309     lpStat->grfStateBits = This->grfStateBits;
310     lpStat->reserved = 0;
311   }
312   return hRet;
313 }
314
315 /*************************************************************************
316  * IStream_fnClone
317  */
318 static HRESULT WINAPI IStream_fnClone(IStream *iface, IStream** ppstm)
319 {
320   ISHFileStream *This = (ISHFileStream *)iface;
321
322   TRACE("(%p)\n",This);
323   if (ppstm)
324     *ppstm = NULL;
325   return E_NOTIMPL;
326 }
327
328 static struct IStreamVtbl SHLWAPI_fsVTable =
329 {
330   IStream_fnQueryInterface,
331   IStream_fnAddRef,
332   IStream_fnRelease,
333   IStream_fnRead,
334   IStream_fnWrite,
335   IStream_fnSeek,
336   IStream_fnSetSize,
337   IStream_fnCopyTo,
338   IStream_fnCommit,
339   IStream_fnRevert,
340   IStream_fnLockUnlockRegion,
341   IStream_fnLockUnlockRegion,
342   IStream_fnStat,
343   IStream_fnClone
344 };
345
346 /**************************************************************************
347  * IStream_Create
348  *
349  * Internal helper: Create and initialise a new file stream object.
350  */
351 static IStream *IStream_Create(LPCWSTR lpszPath, HANDLE hFile, DWORD dwMode)
352 {
353  ISHFileStream* fileStream;
354
355  fileStream = (ISHFileStream*)HeapAlloc(GetProcessHeap(), 0, sizeof(ISHFileStream));
356
357  if (fileStream)
358  {
359    fileStream->lpVtbl = &SHLWAPI_fsVTable;
360    fileStream->ref = 1;
361    fileStream->hFile = hFile;
362    fileStream->dwMode = dwMode;
363    fileStream->lpszPath = StrDupW(lpszPath);
364    fileStream->type = 0; /* FIXME */
365    fileStream->grfStateBits = 0; /* FIXME */
366  }
367  TRACE ("Returning %p\n", fileStream);
368  return (IStream *)fileStream;
369 }
370
371 /*************************************************************************
372  * SHCreateStreamOnFileEx   [SHLWAPI.@]
373  *
374  * Create a stream on a file.
375  *
376  * PARAMS
377  *  lpszPath     [I] Path of file to create stream on
378  *  dwMode       [I] Mode to create stream in
379  *  dwAttributes [I] Attributes of the file
380  *  bCreate      [I] Whether to create the file if it doesn't exist
381  *  lpTemplate   [I] Reserved, must be NULL
382  *  lppStream    [O] Destination for created stream
383  *
384  * RETURNS
385  * Success: S_OK. lppStream contains the new stream object
386  * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
387  *
388  * NOTES
389  *  This function is available in Unicode only.
390  */
391 HRESULT WINAPI SHCreateStreamOnFileEx(LPCWSTR lpszPath, DWORD dwMode,
392                                       DWORD dwAttributes, BOOL bCreate,
393                                       IStream *lpTemplate, IStream **lppStream)
394 {
395   DWORD dwAccess, dwShare, dwCreate;
396   HANDLE hFile;
397
398   TRACE("(%s,%ld,0x%08lX,%d,%p,%p)\n", debugstr_w(lpszPath), dwMode,
399         dwAttributes, bCreate, lpTemplate, lppStream);
400
401   if (!lpszPath || !lppStream || lpTemplate)
402     return E_INVALIDARG;
403
404   *lppStream = NULL;
405
406   if (dwMode & ~(STGM_WRITE|STGM_READWRITE|STGM_SHARE_DENY_NONE|STGM_SHARE_DENY_READ|STGM_CREATE))
407   {
408     WARN("Invalid mode 0x%08lX\n", dwMode);
409     return E_INVALIDARG;
410   }
411
412   /* Access */
413   switch (dwMode & (STGM_WRITE|STGM_READWRITE))
414   {
415   case STGM_READWRITE|STGM_WRITE:
416   case STGM_READWRITE:
417     dwAccess = GENERIC_READ|GENERIC_WRITE;
418     break;
419   case STGM_WRITE:
420     dwAccess = GENERIC_WRITE;
421     break;
422   default:
423     dwAccess = GENERIC_READ;
424     break;
425   }
426
427   /* Sharing */
428   switch (dwMode & STGM_SHARE_DENY_READ)
429   {
430   case STGM_SHARE_DENY_READ:
431     dwShare = FILE_SHARE_WRITE;
432     break;
433   case STGM_SHARE_DENY_WRITE:
434     dwShare = FILE_SHARE_READ;
435     break;
436   case STGM_SHARE_EXCLUSIVE:
437     dwShare = 0;
438     break;
439   default:
440     dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE;
441   }
442
443   /* FIXME: Creation Flags, MSDN is fuzzy on the mapping... */
444   if (dwMode == STGM_FAILIFTHERE)
445     dwCreate = bCreate ? CREATE_NEW : OPEN_EXISTING;
446   else if (dwMode & STGM_CREATE)
447     dwCreate = CREATE_ALWAYS;
448   else
449     dwCreate = OPEN_ALWAYS;
450
451   /* Open HANDLE to file */
452   hFile = CreateFileW(lpszPath, dwAccess, dwShare, NULL, dwCreate,
453                       dwAttributes, 0);
454
455   if(hFile == INVALID_HANDLE_VALUE)
456   {
457     HRESULT hRet = (HRESULT)GetLastError();
458     if(hRet > 0)
459       hRet = HRESULT_FROM_WIN32(hRet);
460     return hRet;
461   }
462
463   *lppStream = IStream_Create(lpszPath, hFile, dwMode);
464
465   if(!*lppStream)
466   {
467     CloseHandle(hFile);
468     return E_OUTOFMEMORY;
469   }
470   return S_OK;
471 }
472
473 /*************************************************************************
474  * SHCreateStreamOnFileW   [SHLWAPI.@]
475  *
476  * See SHCreateStreamOnFileA.
477  */
478 HRESULT WINAPI SHCreateStreamOnFileW(LPCWSTR lpszPath, DWORD dwMode,
479                                    IStream **lppStream)
480 {
481   DWORD dwAttr;
482
483   TRACE("(%s,%ld,%p)\n", debugstr_w(lpszPath), dwMode, lppStream);
484
485   if (!lpszPath || !lppStream)
486     return E_INVALIDARG;
487
488   dwAttr = GetFileAttributesW(lpszPath);
489   if (dwAttr == INVALID_FILE_ATTRIBUTES)
490     dwAttr = 0;
491
492   return SHCreateStreamOnFileEx(lpszPath, dwMode|STGM_WRITE, dwAttr,
493                                 TRUE, NULL, lppStream);
494 }
495
496 /*************************************************************************
497  * SHCreateStreamOnFileA   [SHLWAPI.@]
498  *
499  * Create a stream on a file.
500  *
501  * PARAMS
502  *  lpszPath  [I] Path of file to create stream on
503  *  dwMode    [I] Mode to create stream in
504  *  lppStream [O] Destination for created IStream object
505  *
506  * RETURNS
507  * Success: S_OK. lppStream contains the new IStream object
508  * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
509  */
510 HRESULT WINAPI SHCreateStreamOnFileA(LPCSTR lpszPath, DWORD dwMode,
511                                      IStream **lppStream)
512 {
513   WCHAR szPath[MAX_PATH];
514
515   TRACE("(%s,%ld,%p)\n", debugstr_a(lpszPath), dwMode, lppStream);
516
517   if (!lpszPath)
518     return E_INVALIDARG;
519   MultiByteToWideChar(0, 0, lpszPath, -1, szPath, MAX_PATH);
520   return SHCreateStreamOnFileW(szPath, dwMode, lppStream);
521 }
522
523 /*************************************************************************
524  * @       [SHLWAPI.184]
525  *
526  * Call IStream_Read() on a stream.
527  *
528  * PARAMS
529  *  lpStream [I] IStream object
530  *  lpvDest  [O] Destination for data read
531  *  ulSize   [I] Size of data to read
532  *
533  * RETURNS
534  *  Success: S_OK. ulSize bytes have been read from the stream into lpvDest.
535  *  Failure: An HRESULT error code, or E_FAIL if the read succeeded but the
536  *           number of bytes read does not match.
537  */
538 HRESULT WINAPI SHLWAPI_184(IStream *lpStream, LPVOID lpvDest, ULONG ulSize)
539 {
540   ULONG ulRead;
541   HRESULT hRet;
542
543   TRACE("(%p,%p,%ld)\n", lpStream, lpvDest, ulSize);
544
545   hRet = IStream_Read(lpStream, lpvDest, ulSize, &ulRead);
546
547   if (SUCCEEDED(hRet) && ulRead != ulSize)
548     hRet = E_FAIL;
549   return hRet;
550 }
551
552 /*************************************************************************
553  * @       [SHLWAPI.166]
554  *
555  * Determine if a stream has 0 length.
556  *
557  * PARAMS
558  *  lpStream [I] IStream object
559  *
560  * RETURNS
561  *  TRUE:  If the stream has 0 length
562  *  FALSE: Otherwise.
563  */
564 BOOL WINAPI SHIsEmptyStream(IStream *lpStream)
565 {
566   STATSTG statstg;
567   BOOL bRet = TRUE;
568
569   TRACE("(%p)\n", lpStream);
570
571   memset(&statstg, 0, sizeof(statstg));
572
573   if(SUCCEEDED(IStream_Stat(lpStream, &statstg, 1)))
574   {
575     if(statstg.cbSize.QuadPart)
576       bRet = FALSE; /* Non-Zero */
577   }
578   else
579   {
580     DWORD dwDummy;
581
582     /* Try to read from the stream */
583     if(SUCCEEDED(SHLWAPI_184(lpStream, &dwDummy, sizeof(dwDummy))))
584     {
585       LARGE_INTEGER zero;
586       zero.QuadPart = 0;
587
588       IStream_Seek(lpStream, zero, 0, NULL);
589       bRet = FALSE; /* Non-Zero */
590     }
591   }
592   return bRet;
593 }
594
595 /*************************************************************************
596  * @       [SHLWAPI.212]
597  *
598  * Call IStream_Write() on a stream.
599  *
600  * PARAMS
601  *  lpStream [I] IStream object
602  *  lpvSrc   [I] Source for data to write
603  *  ulSize   [I] Size of data
604  *
605  * RETURNS
606  *  Success: S_OK. ulSize bytes have been written to the stream from lpvSrc.
607  *  Failure: An HRESULT error code, or E_FAIL if the write succeeded but the
608  *           number of bytes written does not match.
609  */
610 HRESULT WINAPI SHLWAPI_212(IStream *lpStream, LPCVOID lpvSrc, ULONG ulSize)
611 {
612   ULONG ulWritten;
613   HRESULT hRet;
614
615   TRACE("(%p,%p,%ld)\n", lpStream, lpvSrc, ulSize);
616
617   hRet = IStream_Write(lpStream, lpvSrc, ulSize, &ulWritten);
618
619   if (SUCCEEDED(hRet) && ulWritten != ulSize)
620     hRet = E_FAIL;
621
622   return hRet;
623 }
624
625 /*************************************************************************
626  * @       [SHLWAPI.213]
627  *
628  * Seek to the start of a stream.
629  *
630  * PARAMS
631  *  lpStream [I] IStream object
632  *
633  * RETURNS
634  *  Success: S_OK. The current position within the stream is updated
635  *  Failure: An HRESULT error code.
636  */
637 HRESULT WINAPI IStream_Reset(IStream *lpStream)
638 {
639   LARGE_INTEGER zero;
640   TRACE("(%p)\n", lpStream);
641   zero.QuadPart = 0;
642   return IStream_Seek(lpStream, zero, 0, NULL);
643 }
644
645 /*************************************************************************
646  * @       [SHLWAPI.214]
647  *
648  * Get the size of a stream.
649  *
650  * PARAMS
651  *  lpStream [I] IStream object
652  *  lpulSize [O] Destination for size
653  *
654  * RETURNS
655  *  Success: S_OK. lpulSize contains the size of the stream.
656  *  Failure: An HRESULT error code.
657  */
658 HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize)
659 {
660   STATSTG statstg;
661   HRESULT hRet;
662
663   TRACE("(%p,%p)\n", lpStream, lpulSize);
664
665   memset(&statstg, 0, sizeof(statstg));
666
667   hRet = IStream_Stat(lpStream, &statstg, 1);
668
669   if (SUCCEEDED(hRet) && lpulSize)
670     *lpulSize = statstg.cbSize;
671   return hRet;
672 }