1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_shell.hxx"
30 
31 #include "internal/global.hxx"
32 
33 #ifndef INFOTIPS_HXX_INCLUDED
34 #include "internal/thumbviewer.hxx"
35 #endif
36 #include "internal/shlxthdl.hxx"
37 #include "internal/registry.hxx"
38 #include "internal/fileextensions.hxx"
39 #include "internal/config.hxx"
40 #include "internal/zipfile.hxx"
41 #include "internal/utilities.hxx"
42 
43 #include "internal/resource.h"
44 
45 #include <stdio.h>
46 #include <utility>
47 #include <stdlib.h>
48 
49 #if defined _MSC_VER
50 #pragma warning(push, 1)
51 #endif
52 #include <shellapi.h>
53 #if defined _MSC_VER
54 #pragma warning(pop)
55 #endif
56 #include <memory>
57 
58 extern HINSTANCE g_hModule;
59 
60 namespace internal
61 {
62     /* The signet.png used for thumbnails of signed documents
63        is contained as resource in this module, the resource
64        id is 2000 */
65     void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t& buffer)
66     {
67         HRSRC hrc = FindResource(g_hModule, TEXT("#2000"), RT_RCDATA);
68         DWORD size = SizeofResource(g_hModule, hrc);
69         HGLOBAL hglob = LoadResource(g_hModule, hrc);
70         char* data = reinterpret_cast<char*>(LockResource(hglob));
71         buffer = ZipFile::ZipContentBuffer_t(data, data + size);
72     }
73 
74     bool IsSignedDocument(const ZipFile* zipfile)
75     {
76         return zipfile->HasContent("META-INF/documentsignatures.xml");
77     }
78 
79     bool IsWindowsXP()
80     {
81         OSVERSIONINFO osvi;
82         ZeroMemory(&osvi, sizeof(osvi));
83         osvi.dwOSVersionInfoSize = sizeof(osvi);
84         GetVersionEx(&osvi);
85 
86         return ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
87                 ((osvi.dwMajorVersion >= 5) && (osvi.dwMinorVersion >= 1)));
88     }
89 
90     /* Calculate where to position the signet image.
91        On Windows ME we need to shift the signet a
92        little bit to the left because Windows ME
93        puts an overlay icon to the lower right
94        corner of a thumbnail image so that our signet
95        we be hidden. */
96     Gdiplus::Point CalcSignetPosition(
97         const Gdiplus::Rect& canvas, const Gdiplus::Rect& thumbnail_border, const Gdiplus::Rect& signet)
98     {
99         int x = 0;
100         int y = 0;
101         int hoffset = canvas.GetRight() - thumbnail_border.GetRight();
102         int voffset = canvas.GetBottom() - thumbnail_border.GetBottom();
103 
104         if (hoffset > voffset)
105         {
106             x = thumbnail_border.GetRight() - signet.GetRight() + min(signet.GetRight() / 2, hoffset);
107             y = thumbnail_border.GetBottom() - signet.GetBottom();
108         }
109         else
110         {
111             x = thumbnail_border.GetRight() - signet.GetRight();
112             y = thumbnail_border.GetBottom() - signet.GetBottom() + min(signet.GetBottom() / 2, voffset);
113         }
114 
115         if (!IsWindowsXP())
116             x -= 15;
117 
118         return Gdiplus::Point(x,y);
119     }
120 }
121 
122 class StreamOnZipBuffer : public IStream
123 {
124 public:
125     StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer);
126 
127     // IUnknown
128     virtual ULONG STDMETHODCALLTYPE AddRef();
129     virtual ULONG STDMETHODCALLTYPE Release( void);
130     virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject);
131 
132     // IStream
133     virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead);
134     virtual HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten);
135     virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
136     virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize);
137     virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
138     virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags);
139     virtual HRESULT STDMETHODCALLTYPE Revert(void);
140     virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
141     virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
142     virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag);
143     virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm);
144 
145 private:
146     LONG ref_count_;
147     const ZipFile::ZipContentBuffer_t& ref_zip_buffer_;
148     size_t pos_;
149 };
150 
151 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer) :
152     ref_count_(1),
153     ref_zip_buffer_(zip_buffer),
154     pos_(0)
155 {
156 }
157 
158 // IUnknown methods
159 
160 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::AddRef(void)
161 {
162     return InterlockedIncrement(&ref_count_);
163 }
164 
165 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::Release( void)
166 {
167     long refcnt = InterlockedDecrement(&ref_count_);
168 
169     if (0 == ref_count_)
170         delete this;
171 
172     return refcnt;
173 }
174 
175 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
176 {
177     *ppvObject = 0;
178     IUnknown* pUnk = 0;
179 
180     if ((IID_IUnknown == riid) || (IID_IStream == riid))
181     {
182         pUnk = static_cast<IStream*>(this);
183         pUnk->AddRef();
184         *ppvObject = pUnk;
185         return S_OK;
186     }
187     return E_NOINTERFACE;
188 }
189 
190 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Read(void *pv, ULONG cb, ULONG *pcbRead)
191 {
192     if (pv == NULL)
193         return STG_E_INVALIDPOINTER;
194 
195     size_t size = ref_zip_buffer_.size();
196 
197     if (pos_ > size)
198         return S_FALSE;
199 
200     char* p = reinterpret_cast<char*>(pv);
201     ULONG read = 0;
202 
203     for ( ;(pos_ < size) && (cb > 0); pos_++, cb--, read++)
204         *p++ = ref_zip_buffer_[pos_];
205 
206     if (pcbRead)
207         *pcbRead = read;
208 
209     return S_OK;
210 }
211 
212 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *)
213 {
214     __int64 size = (__int64) ref_zip_buffer_.size();
215     __int64 p = 0;
216 
217     switch (dwOrigin)
218     {
219         case STREAM_SEEK_SET:
220             break;
221         case STREAM_SEEK_CUR:
222             p = (__int64) pos_;
223             break;
224         case STREAM_SEEK_END:
225             p = size - 1;
226             break;
227    }
228 
229    HRESULT hr = STG_E_INVALIDFUNCTION;
230 
231    p += dlibMove.QuadPart;
232 
233    if ( ( p >= 0 ) && (p < size) )
234    {
235         pos_ = (size_t) p;
236         hr = S_OK;
237    }
238    return hr;
239 }
240 
241 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
242 {
243     if (pstatstg == NULL)
244         return STG_E_INVALIDPOINTER;
245 
246     ZeroMemory(pstatstg, sizeof(STATSTG));
247 
248     if (grfStatFlag == STATFLAG_DEFAULT)
249     {
250         size_t sz = 4 * sizeof(wchar_t);
251         wchar_t* name = reinterpret_cast<wchar_t*>(CoTaskMemAlloc(sz));
252         ZeroMemory(name, sz);
253         memcpy(name, L"png", 3 * sizeof(wchar_t));
254         pstatstg->pwcsName = name;
255     }
256 
257     pstatstg->type = STGTY_LOCKBYTES;
258 
259     ULARGE_INTEGER uli;
260     uli.LowPart = ref_zip_buffer_.size();
261     uli.HighPart = 0;
262 
263     pstatstg->cbSize = uli;
264 
265     return S_OK;
266 }
267 
268 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Write(void const *, ULONG, ULONG *)
269 { return E_NOTIMPL; }
270 
271 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::SetSize(ULARGE_INTEGER)
272 { return E_NOTIMPL; }
273 
274 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *)
275 { return E_NOTIMPL; }
276 
277 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Commit(DWORD)
278 { return E_NOTIMPL; }
279 
280 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Revert(void)
281 { return E_NOTIMPL; }
282 
283 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
284 { return E_NOTIMPL; }
285 
286 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
287 { return E_NOTIMPL; }
288 
289 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Clone(IStream **)
290 {  return E_NOTIMPL; }
291 
292 
293 //#########################################
294 
295 
296 CThumbviewer::CThumbviewer(long RefCnt) :
297     ref_count_(RefCnt)
298 {
299     InterlockedIncrement(&g_DllRefCnt);
300 
301     thumbnail_size_.cx = 0;
302     thumbnail_size_.cy = 0;
303 
304     Gdiplus::GdiplusStartupInput gdiplusStartupInput;
305     Gdiplus::GdiplusStartup(&gdiplus_token_, &gdiplusStartupInput, NULL);
306 
307     ZipFile::ZipContentBuffer_t img_data;
308     internal::LoadSignetImageFromResource(img_data);
309     IStream* stream = new StreamOnZipBuffer(img_data);
310     signet_ = new Gdiplus::Bitmap(stream, TRUE);
311     stream->Release();
312 }
313 
314 CThumbviewer::~CThumbviewer()
315 {
316     delete signet_;
317     Gdiplus::GdiplusShutdown(gdiplus_token_);
318     InterlockedDecrement(&g_DllRefCnt);
319 }
320 
321 // IUnknown methods
322 
323 HRESULT STDMETHODCALLTYPE CThumbviewer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
324 {
325     *ppvObject = 0;
326     IUnknown* pUnk = 0;
327 
328     if ((IID_IUnknown == riid) || (IID_IPersistFile == riid))
329     {
330         pUnk = static_cast<IPersistFile*>(this);
331         pUnk->AddRef();
332         *ppvObject = pUnk;
333         return S_OK;
334     }
335     else if (IID_IExtractImage == riid)
336     {
337         pUnk = static_cast<IExtractImage*>(this);
338         pUnk->AddRef();
339         *ppvObject = pUnk;
340         return S_OK;
341     }
342     return E_NOINTERFACE;
343 }
344 
345 ULONG STDMETHODCALLTYPE CThumbviewer::AddRef(void)
346 {
347     return InterlockedIncrement(&ref_count_);
348 }
349 
350 ULONG STDMETHODCALLTYPE CThumbviewer::Release( void)
351 {
352     long refcnt = InterlockedDecrement(&ref_count_);
353 
354     if (0 == ref_count_)
355         delete this;
356 
357     return refcnt;
358 }
359 
360 // IExtractImage2 methods
361 
362 const std::string THUMBNAIL_CONTENT = "Thumbnails/thumbnail.png";
363 
364 HRESULT STDMETHODCALLTYPE CThumbviewer::Extract(HBITMAP *phBmpImage)
365 {
366     HRESULT hr = E_FAIL;
367 
368     try
369     {
370         std::wstring fname = getShortPathName( filename_ );
371         std::auto_ptr<ZipFile> zipfile( new ZipFile( WStringToString( fname ) ) );
372 
373         if (zipfile->HasContent(THUMBNAIL_CONTENT))
374         {
375             ZipFile::ZipContentBuffer_t thumbnail;
376             zipfile->GetUncompressedContent(THUMBNAIL_CONTENT, thumbnail);
377             IStream* stream = new StreamOnZipBuffer(thumbnail);
378 
379             Gdiplus::Bitmap thumbnail_png(stream, TRUE);
380 
381             if ((thumbnail_png.GetHeight() == 0) || (thumbnail_png.GetWidth() == 0))
382             {
383                 stream->Release();
384                 return E_FAIL;
385             }
386 
387     		HWND hwnd = GetDesktopWindow();
388     		HDC hdc = GetDC(hwnd);
389             HDC memDC = CreateCompatibleDC(hdc);
390 
391             if (memDC)
392             {
393                 UINT offset = 3; // reserve a little border space
394 
395                 Gdiplus::Rect canvas(0, 0, thumbnail_size_.cx, thumbnail_size_.cy);
396                 Gdiplus::Rect canvas_thumbnail(offset, offset, thumbnail_size_.cx - 2 * offset, thumbnail_size_.cy - 2 * offset);
397 
398                 Gdiplus::Rect scaledRect = CalcScaledAspectRatio(
399                     Gdiplus::Rect(0, 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight()), canvas_thumbnail);
400 
401                 struct {
402                     BITMAPINFOHEADER bi;
403                     DWORD ct[256];
404                 } dib;
405 
406                 ZeroMemory(&dib, sizeof(dib));
407 
408                 dib.bi.biSize = sizeof(BITMAPINFOHEADER);
409                 dib.bi.biWidth = thumbnail_size_.cx;
410                 dib.bi.biHeight = thumbnail_size_.cy;
411                 dib.bi.biPlanes = 1;
412                 dib.bi.biBitCount = static_cast<WORD>(color_depth_);
413                 dib.bi.biCompression = BI_RGB;
414 
415                 LPVOID lpBits;
416                 HBITMAP hMemBmp = CreateDIBSection(memDC, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0);
417                 HGDIOBJ hOldObj = SelectObject(memDC, hMemBmp);
418 
419                 Gdiplus::Graphics graphics(memDC);
420                 Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0, 0), 1);
421 
422                 Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255));
423                 graphics.FillRectangle(&whiteBrush, canvas);
424 
425                 scaledRect.X = (canvas.Width - scaledRect.Width) / 2;
426                 scaledRect.Y = (canvas.Height - scaledRect.Height) / 2;
427 
428                 Gdiplus::Rect border_rect(scaledRect.X, scaledRect.Y, scaledRect.Width, scaledRect.Height);
429                 graphics.DrawRectangle(&blackPen, border_rect);
430 
431                 scaledRect.X += 1;
432                 scaledRect.Y += 1;
433                 scaledRect.Width -= 1;
434                 scaledRect.Height -= 1;
435 
436                 graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
437                 Gdiplus::Status stat = graphics.DrawImage(
438                     &thumbnail_png, scaledRect, 0 , 0,
439                     thumbnail_png.GetWidth(), thumbnail_png.GetHeight(), Gdiplus::UnitPixel);
440 
441                 /* Add a signet sign to the thumbnail of signed documents */
442                 if (internal::IsSignedDocument(zipfile.get()))
443                 {
444                     double SCALING_FACTOR = 0.6;
445                     Gdiplus::Rect signet_scaled(
446                         0, 0, static_cast<INT>(signet_->GetWidth() * SCALING_FACTOR), static_cast<INT>(signet_->GetHeight() * SCALING_FACTOR));
447                     Gdiplus::Point pos_signet = internal::CalcSignetPosition(canvas_thumbnail, border_rect, signet_scaled);
448                     Gdiplus::Rect dest(pos_signet.X, pos_signet.Y, signet_scaled.GetRight(), signet_scaled.GetBottom());
449 
450                     stat = graphics.DrawImage(
451                         signet_, dest,
452                         0, 0, signet_->GetWidth(), signet_->GetHeight(),
453                         Gdiplus::UnitPixel);
454                 }
455 
456                 if (stat == Gdiplus::Ok)
457                 {
458                     *phBmpImage = hMemBmp;
459                     hr = NOERROR;
460                 }
461 
462                 SelectObject(memDC, hOldObj);
463                 DeleteDC(memDC);
464             }
465 
466             ReleaseDC(hwnd, hdc);
467             stream->Release();
468         }
469     }
470     catch(std::exception&)
471     {
472         OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" );
473         hr = E_FAIL;
474     }
475     return hr;
476 }
477 
478 HRESULT STDMETHODCALLTYPE CThumbviewer::GetLocation(
479     LPWSTR pszPathBuffer, DWORD cchMax, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags)
480 {
481     if ((prgSize == NULL) || (pdwFlags == NULL) || ((*pdwFlags & IEIFLAG_ASYNC) && (pdwPriority == NULL)))
482         return E_INVALIDARG;
483 
484     thumbnail_size_ = *prgSize;
485     color_depth_ = dwRecClrDepth;
486 
487     *pdwFlags = IEIFLAG_CACHE; // we don't cache the image
488 
489     wcsncpy(pszPathBuffer, filename_.c_str(), cchMax);
490 
491     return NOERROR;
492 }
493 
494 // IPersist methods
495 
496 HRESULT STDMETHODCALLTYPE CThumbviewer::GetClassID(CLSID* pClassID)
497 {
498     pClassID = const_cast<CLSID*>(&CLSID_THUMBVIEWER_HANDLER);
499     return S_OK;
500 }
501 
502 // IPersistFile methods
503 
504 HRESULT STDMETHODCALLTYPE CThumbviewer::Load(LPCOLESTR pszFileName, DWORD)
505 {
506     filename_ = pszFileName;
507     return S_OK;
508 }
509 
510 HRESULT STDMETHODCALLTYPE CThumbviewer::IsDirty()
511 { return E_NOTIMPL; }
512 
513 HRESULT STDMETHODCALLTYPE CThumbviewer::Save(LPCOLESTR, BOOL)
514 { return E_NOTIMPL; }
515 
516 HRESULT STDMETHODCALLTYPE CThumbviewer::SaveCompleted(LPCOLESTR)
517 { return E_NOTIMPL; }
518 
519 HRESULT STDMETHODCALLTYPE CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR*)
520 { return E_NOTIMPL; }
521 
522 
523 Gdiplus::Rect CThumbviewer::CalcScaledAspectRatio(Gdiplus::Rect src, Gdiplus::Rect dest)
524 {
525     Gdiplus::Rect result;
526     if (src.Width >= src.Height)
527         result = Gdiplus::Rect(0, 0, dest.Width, src.Height * dest.Width / src.Width);
528     else
529         result = Gdiplus::Rect(0, 0, src.Width * dest.Height / src.Height, dest.Height);
530 
531     return result;
532 }
533 
534