1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // XMergeFilter.cpp: implementation of the CXMergeFilter class.
23 //
24 //////////////////////////////////////////////////////////////////////
25 
26 
27 #include "stdafx.h"
28 
29 #include "XMergeFilter.h"
30 
31 #include <string>
32 
33 
34 #define ERR_NOJAVA       1
35 #define ERR_BADCLASSPATH 2
36 #define ERR_INITJAVA     3
37 
38 
39 const LPTSTR CXMergeFilter::m_pszPSWExportCLSID		= _T("{BDD611C3-7BAB-460F-8711-5B9AC9EF6020}");
40 const LPTSTR CXMergeFilter::m_pszPSWExportExt		= _T("sxw");
41 const LPTSTR CXMergeFilter::m_pszPSWExportDesc		= _T("OpenOffice.org XML Writer Document");
42 const LPTSTR CXMergeFilter::m_pszPSWExportShortDesc	= _T("OpenOffice.org XML Writer");
43 
44 const LPTSTR CXMergeFilter::m_pszPSWImportCLSID		= _T("{CB43F086-838D-4FA4-B5F6-3406B9A57439}");
45 const LPTSTR CXMergeFilter::m_pszPSWImportExt		= _T("psw");
46 const LPTSTR CXMergeFilter::m_pszPSWImportDesc		= _T("Pocket Word Document - Pocket PC");
47 const LPTSTR CXMergeFilter::m_pszPSWImportShortDesc	= _T("Pocket Word");
48 
49 const LPTSTR CXMergeFilter::m_pszPXLExportCLSID		= _T("{C6AB3E74-9F4F-4370-8120-A8A6FABB7A7C}");
50 const LPTSTR CXMergeFilter::m_pszPXLExportExt		= _T("sxc");
51 const LPTSTR CXMergeFilter::m_pszPXLExportDesc		= _T("OpenOffice.org XML Calc Document");
52 const LPTSTR CXMergeFilter::m_pszPXLExportShortDesc	= _T("OpenOffice.org XML Calc");
53 
54 const LPTSTR CXMergeFilter::m_pszPXLImportCLSID		= _T("{43887C67-4D5D-4127-BAAC-87A288494C7C}");
55 const LPTSTR CXMergeFilter::m_pszPXLImportExt		= _T("pxl");
56 const LPTSTR CXMergeFilter::m_pszPXLImportDesc		= _T("Pocket Excel Document - Pocket PC");
57 const LPTSTR CXMergeFilter::m_pszPXLImportShortDesc	= _T("Pocket Excel");
58 
59 
60 //////////////////////////////////////////////////////////////////////
61 // Construction/Destruction
62 //////////////////////////////////////////////////////////////////////
63 
CXMergeFilter()64 CXMergeFilter::CXMergeFilter() : m_cRef(1)
65 {
66 	m_bHaveExcel = FALSE;
67 	m_bHaveWord  = FALSE;
68 
69 	m_szClasspath   = NULL;
70 	m_szJavaBaseDir = NULL;
71 }
72 
~CXMergeFilter()73 CXMergeFilter::~CXMergeFilter()
74 {
75 	if (m_szClasspath != NULL)
76 	{
77 		delete m_szClasspath;
78 	}
79 
80 	if (m_szJavaBaseDir != NULL)
81 	{
82 		delete m_szJavaBaseDir;
83 	}
84 
85 }
86 
87 
88 //////////////////////////////////////////////////////////////////////
89 // IUnknown Methods
90 //////////////////////////////////////////////////////////////////////
91 
QueryInterface(REFIID riid,void ** ppvObject)92 STDMETHODIMP CXMergeFilter::QueryInterface(REFIID riid, void **ppvObject)
93 {
94 	if(ppvObject == NULL)
95 		return E_INVALIDARG;
96 
97     if (::IsEqualIID(riid, IID_IUnknown))
98 	{
99         *ppvObject = static_cast<IUnknown *>(this);
100 	}
101     else if (::IsEqualIID(riid, IID_ICeFileFilter))
102 	{
103         *ppvObject = static_cast<ICeFileFilter *>(this);
104 	}
105 	else
106 	{
107 		*ppvObject = NULL;
108 		return E_NOINTERFACE;
109 	}
110 
111 	reinterpret_cast<IUnknown *>(*ppvObject)->AddRef();
112 	return S_OK;
113 }
114 
115 
STDMETHODIMP_(ULONG)116 STDMETHODIMP_(ULONG) CXMergeFilter::AddRef()
117 {
118 	return ::InterlockedIncrement(&m_cRef);
119 }
120 
121 
STDMETHODIMP_(ULONG)122 STDMETHODIMP_(ULONG) CXMergeFilter::Release()
123 {
124 	if(::InterlockedDecrement(&m_cRef) == 0)
125 	{
126 		delete this;
127 		return 0;
128 	}
129 	return m_cRef;
130 }
131 
132 
133 //////////////////////////////////////////////////////////////////////
134 // ICeFileFilter
135 //////////////////////////////////////////////////////////////////////
136 
FilterOptions(HWND hwndParent)137 STDMETHODIMP CXMergeFilter::FilterOptions(HWND hwndParent)
138 {
139 	// We don't currently allow any options
140 	return HRESULT_FROM_WIN32(NOERROR);
141 }
142 
FormatMessage(DWORD dwFlags,DWORD dwMessageId,DWORD dwLanguageId,LPTSTR lpBuffer,DWORD nSize,va_list * Arguments,DWORD * pcb)143 STDMETHODIMP CXMergeFilter::FormatMessage(DWORD dwFlags, DWORD dwMessageId,
144 						DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize,
145 						va_list *Arguments, DWORD *pcb)
146 {
147 	TCHAR errMsg[1024];
148 
149 	HKEY  hKey   = NULL;
150 	DWORD dwSize = 1024;
151 
152 
153 	long lRet = 0;
154 
155 	// Attempt to find the messages in the registry
156 	lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Sun Microsystems\\StarOffice\\XMergeSync\\Messages\\Error"),
157 							0, KEY_READ, &hKey);
158 	if (lRet != ERROR_SUCCESS)
159 	{
160 		// Try the user's portion of the registry
161 		lRet = ::RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Sun Microsystems\\StarOffice\\XMergeSync\\Messages\\Error"),
162 							0, KEY_READ, &hKey);
163 		if (lRet != ERROR_SUCCESS)
164 		{
165 			hKey = NULL;
166 		}
167 	}
168 
169 
170 	switch(dwMessageId)
171 	{
172 	case ERR_NOJAVA:
173 		lRet = ::RegQueryValueEx(hKey, _T("Java"), 0, NULL, (LPBYTE)errMsg, &dwSize);
174 		if (lRet != ERROR_SUCCESS)
175 		{
176 			lstrcpy(errMsg, "Unable to locate Java 1.4/1.5 installation.");
177 		}
178 		break;
179 
180 	case ERR_BADCLASSPATH:
181 		lRet = ::RegQueryValueEx(hKey, _T("Classpath"), 0, NULL, (LPBYTE)errMsg, &dwSize);
182 		if (lRet != ERROR_SUCCESS)
183 		{
184 			lstrcpy(errMsg, "Unable to locate XMerge Jar files.");
185 		}
186 		break;
187 
188 	case ERR_INITJAVA:
189 		lRet = ::RegQueryValueEx(hKey, _T("JavaInit"), 0, NULL, (LPBYTE)errMsg, &dwSize);
190 		if (lRet != ERROR_SUCCESS)
191 		{
192 			lstrcpy(errMsg, "Error initialising the Java Runtime Environment.");
193 		}
194 		break;
195 	}
196 
197 	char* buf = (char*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, (lstrlen(errMsg) + 1) * sizeof(TCHAR));
198 	lstrcpyn(buf, errMsg, lstrlen(errMsg));
199 
200 	*(char**)lpBuffer = buf;
201 	*pcb = strlen(errMsg);
202 
203 	return HRESULT_FROM_WIN32(NOERROR);
204 }
205 
206 
NextConvertFile(int nConversion,CFF_CONVERTINFO * pci,CFF_SOURCEFILE * psf,CFF_DESTINATIONFILE * pdf,volatile BOOL * pbCancel,CF_ERROR * perr)207 STDMETHODIMP CXMergeFilter::NextConvertFile(int nConversion, CFF_CONVERTINFO *pci,
208 							 CFF_SOURCEFILE *psf, CFF_DESTINATIONFILE *pdf,
209 							 volatile BOOL *pbCancel, CF_ERROR *perr)
210 {
211 	std::string appArgs;
212 	std::string appName;
213 
214 	STARTUPINFO si;
215     PROCESS_INFORMATION pi;
216 
217     ZeroMemory( &si, sizeof(si) );
218 	ZeroMemory( &pi, sizeof(pi) );
219 
220 	si.cb = sizeof(si);
221 
222 
223 	/*
224 	 * First step: Locate Java and establish the classpath.  If these can't
225 	 *             be done successfully, then avoid all further processing.
226 	 */
227 
228 	// Locate Java Home if it hasn't already been done.
229 	if (m_szJavaBaseDir == NULL)
230 	{
231 		m_szJavaBaseDir = GetJavaBaseDir();
232 
233 		if (m_szJavaBaseDir == NULL)
234 		{
235 			*perr = ERR_NOJAVA;
236 			return HRESULT_FROM_WIN32(E_FAIL);
237 		}
238 	}
239 
240 	// Get the Apache OpenOffice class directory
241 	if (m_szClasspath == NULL)
242 	{
243 		m_szClasspath = GetXMergeClassPath();
244 
245 		if (m_szClasspath == NULL)
246 		{
247 			*perr = ERR_BADCLASSPATH;
248 			return HRESULT_FROM_WIN32(E_FAIL);
249 		}
250 	}
251 
252 
253 	/*
254 	 * Second step:  Check the files we're going to process.  If we don't have
255 	 *				 an XMerge plugin for the file then we can't convert.
256 	 */
257 	if ((!lstrcmp(psf->szExtension, "sxw")  || !lstrcmp(psf->szExtension, "psw"))
258 		    && !m_bHaveWord)
259 	{
260 		*perr = ERR_BADCLASSPATH;
261 		return HRESULT_FROM_WIN32(E_FAIL);
262 	}
263 	else if ((!lstrcmp(psf->szExtension, "sxc")  || !lstrcmp(psf->szExtension, "pxl"))
264 				 && !m_bHaveExcel)
265 	{
266 		*perr = ERR_BADCLASSPATH;
267 		return HRESULT_FROM_WIN32(E_FAIL);
268 	}
269 
270 
271 	/*
272 	 * Third step:  Locate the Java executable and build and execute the command
273 	 *				line to carry out the conversion.
274 	 */
275 
276 	// Find the Java executable and make sure it exists
277 	appName += m_szJavaBaseDir;
278 	appName += "\\bin\\javaw.exe";
279 
280 	if (GetFileAttributes(appName.c_str()) == INVALID_FILE_SIZE)
281 	{
282 		*perr = ERR_NOJAVA;
283 		return HRESULT_FROM_WIN32(E_FAIL);
284 	}
285 
286 	// Wrap the executable path in quotes in case of spaces
287 	appName.insert(0, "\"");
288 	appName.append("\"");
289 
290 
291 
292 	// Need to build the entire command line for calling out to Java
293 	appArgs = appName;
294         if (*m_szClasspath) {
295             appArgs += " -Djava.class.path=";
296             appArgs += m_szClasspath;
297         } // else avoid empty class path
298 	appArgs += " org.openoffice.xmerge.util.ActiveSyncDriver ";
299 
300 	if (!lstrcmp(psf->szExtension, "sxw"))
301 	{
302 		appArgs += "staroffice/sxw ";
303 		appArgs += "application/x-pocket-word ";
304 	}
305 	else if(!lstrcmp(psf->szExtension, "psw"))
306 	{
307 		appArgs += "application/x-pocket-word ";
308 		appArgs += "staroffice/sxw ";
309 	}
310 	else if(!lstrcmp(psf->szExtension, "sxc"))
311 	{
312 		appArgs += "staroffice/sxc ";
313 		appArgs += "application/x-pocket-excel ";
314 	}
315 	else if(!lstrcmp(psf->szExtension, "pxl"))
316 	{
317 		appArgs += "application/x-pocket-excel ";
318 		appArgs += "staroffice/sxc ";
319 	}
320 
321 
322 	// ActiveSync sometimes gives out long file names, especially when automatically syncing
323 	appArgs += "\"";
324 	appArgs += psf->szFullpath;
325 	appArgs += "\" \"";
326 	appArgs += pdf->szFullpath;
327 	appArgs += "\"";
328 
329 	if(!CreateProcess(NULL,
330 				  (char*)appArgs.c_str(),
331 				  NULL,				// No Process Attributes
332 				  NULL,				// No Thread Attributes
333 				  FALSE,			// Don't want this process getting handles
334 				  CREATE_NO_WINDOW,	// No console
335 				  NULL,				// No special environment
336 				  NULL,				// Current Working Directory is okay
337 				  &si,
338 				  &pi))
339 	{
340 		*perr = ERR_INITJAVA;
341 		return HRESULT_FROM_WIN32(E_FAIL);
342 	}
343 
344 	// Wait for the new process to work
345 	WaitForSingleObject(pi.hProcess, INFINITE);
346 
347 	CloseHandle(pi.hProcess);
348 	CloseHandle(pi.hThread);
349 
350 	return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
351 }
352 
353 
354 typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD, LPTSTR );
355 
356 
GetJavaBaseDir()357 TCHAR* CXMergeFilter::GetJavaBaseDir()
358 {
359 	HRESULT lRet;
360 
361 	HKEY hKey = NULL;
362 	HKEY hDataKey = NULL;
363 
364 	TCHAR szClassName[_MAX_PATH] = "\0";
365 	TCHAR szKeyName[_MAX_PATH]   = "\0";
366     TCHAR szCurrentJava[_MAX_PATH] = "\0";
367 	DWORD dwClassName            = _MAX_PATH;
368 	DWORD dwKeyName              = _MAX_PATH;
369 
370 	/*
371 	 * Java leaves registry keys at HKLM\SOFTWARE\JavaSoft.
372 	 *
373 	 * Check for a JRE installation first
374 	 */
375 	lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\JavaSoft\\Java Runtime Environment"), 0, KEY_READ, &hKey);
376 	if (lRet != ERROR_SUCCESS)
377 		return NULL;
378 
379 	// Locations shouldn't be greater than _MAX_PATH
380 	TCHAR*  szJavaHome = new TCHAR[_MAX_PATH + 1];
381 	DWORD dwSize = _MAX_PATH + 1;
382 
383     /* use current version */
384     lRet = ::RegQueryValueEx(hKey, _T("CurrentVersion"), 0, NULL, (LPBYTE)szCurrentJava, &dwSize);
385 
386     /*
387 	for (DWORD i = 0; lRet != ERROR_NO_MORE_ITEMS; i++)
388 	{
389 		lRet = ::RegEnumKeyEx(hKey, i, szKeyName, &dwKeyName, 0, szClassName, &dwClassName, NULL);
390 		if(!strncmp(szKeyName, "1.4", 3))
391 			break;
392 		dwKeyName = _MAX_PATH;
393 	}
394     // Found a Java 1.4 installation.  Can now read its home directory.
395     */
396 
397 
398 	lRet = ::RegOpenKeyEx(hKey, _T(szCurrentJava), 0, KEY_READ, &hDataKey);
399 	if (lRet != ERROR_SUCCESS)
400 	{
401 		RegCloseKey(hKey);
402 		delete [] szJavaHome;
403 		return NULL;
404 	}
405 
406 
407 	// Now read the JavaHome value
408     dwSize = _MAX_PATH + 1;
409 	lRet = ::RegQueryValueEx(hDataKey, _T("JavaHome"), 0, NULL, (LPBYTE)szJavaHome, &dwSize);
410 	if (lRet != ERROR_SUCCESS)
411 	{
412 		RegCloseKey(hDataKey);
413 		RegCloseKey(hKey);
414 		delete [] szJavaHome;
415 		return NULL;
416 	}
417 
418 	RegCloseKey(hDataKey);
419 	RegCloseKey(hKey);
420 
421 
422 	// Check that the directory exists before returning it
423 	DWORD dwAttrs = GetFileAttributes(szJavaHome);
424 
425 	if (((dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) || dwAttrs == INVALID_FILE_SIZE)
426 	{
427 		delete [] szJavaHome;
428 		return NULL;
429 	}
430 
431 	return szJavaHome;
432 }
433 
434 
435 
GetXMergeClassPath()436 TCHAR* CXMergeFilter::GetXMergeClassPath()
437 {
438 	/*
439 	 * The DLL will be installed by setup in the program directory of
440 	 * the installation.  The XMerge Jar files, if present, will be
441 	 * located in the classes directory below program.
442 	 */
443 
444 	TCHAR szJarPath[MAX_PATH];
445 	TCHAR szTmpPath[MAX_PATH];
446 
447 	ZeroMemory(szJarPath, MAX_PATH);
448 	ZeroMemory(szTmpPath, MAX_PATH);
449 
450 	WIN32_FILE_ATTRIBUTE_DATA fInfo;
451 
452 	std::string clsPath;
453 
454 
455 	// Get the location of the module.
456 	GetModuleFileName(_Module.m_hInst, szTmpPath, MAX_PATH);
457 
458 	// Strip off the xmergesync.dll component
459 	_strlwr(szTmpPath);
460 	char* modName = strstr(szTmpPath, "xmergesync.dll");
461 	strncpy(szJarPath, szTmpPath, modName - szTmpPath);
462 
463 	// Append the classes directory
464 	strncat(szJarPath, "classes\\", 8);
465 
466 
467 	// The core xmerge.jar must be present
468 	ZeroMemory(szTmpPath, MAX_PATH);
469 	_snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "xmerge.jar");
470 
471 	if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
472 	{
473 		return NULL;
474 	}
475 	else
476 	{
477 		clsPath += szTmpPath;
478 		clsPath += ";";
479 	}
480 
481 
482 	// Now check for Pocket Word
483 	ZeroMemory(szTmpPath, MAX_PATH);
484 	_snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "pocketword.jar");
485 
486 	if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
487 	{
488 		m_bHaveWord = FALSE;
489 	}
490 	else
491 	{
492 		m_bHaveWord = TRUE;
493 		clsPath += szTmpPath;
494 		clsPath += ";";
495 	}
496 
497 	// Now check for Pocket Excel
498 	ZeroMemory(szTmpPath, MAX_PATH);
499 	_snprintf(szTmpPath, MAX_PATH, "%s%s\0", szJarPath, "pexcel.jar");
500 
501 	if (!GetFileAttributesEx(szTmpPath, GetFileExInfoStandard, &fInfo))
502 	{
503 		m_bHaveExcel = FALSE;
504 	}
505 	else
506 	{
507 		m_bHaveExcel = TRUE;
508 		clsPath += szTmpPath;
509 		clsPath += ";";
510 	}
511 
512 	// Quotes may be need around the ClassPath
513 	clsPath.insert(0, "\"");
514 	clsPath += "\"";
515 
516 
517 	// Return the data
518 	return _strdup(clsPath.c_str());
519 }
520