1 /* 2 * MIME OLE International interface 3 * 4 * Copyright 2008 Huw Davies for CodeWeavers 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 #define COBJMACROS 22 #define NONAMELESSUNION 23 24 #include <stdarg.h> 25 #include <stdio.h> 26 27 #include "windef.h" 28 #include "winbase.h" 29 #include "winuser.h" 30 #include "winnls.h" 31 #include "objbase.h" 32 #include "ole2.h" 33 #include "mimeole.h" 34 #include "mlang.h" 35 36 #include "wine/list.h" 37 #include "wine/debug.h" 38 39 #include "inetcomm_private.h" 40 41 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm); 42 43 typedef struct 44 { 45 struct list entry; 46 INETCSETINFO cs_info; 47 } charset_entry; 48 49 typedef struct 50 { 51 IMimeInternational IMimeInternational_iface; 52 LONG refs; 53 CRITICAL_SECTION cs; 54 55 struct list charsets; 56 LONG next_charset_handle; 57 HCHARSET default_charset; 58 } internat_impl; 59 60 static inline internat_impl *impl_from_IMimeInternational(IMimeInternational *iface) 61 { 62 return CONTAINING_RECORD(iface, internat_impl, IMimeInternational_iface); 63 } 64 65 static inline HRESULT get_mlang(IMultiLanguage **ml) 66 { 67 return CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, 68 &IID_IMultiLanguage, (void **)ml); 69 } 70 71 static HRESULT WINAPI MimeInternat_QueryInterface( IMimeInternational *iface, REFIID riid, LPVOID *ppobj ) 72 { 73 if (IsEqualGUID(riid, &IID_IUnknown) || 74 IsEqualGUID(riid, &IID_IMimeInternational)) 75 { 76 IMimeInternational_AddRef( iface ); 77 *ppobj = iface; 78 return S_OK; 79 } 80 81 FIXME("interface %s not implemented\n", debugstr_guid(riid)); 82 return E_NOINTERFACE; 83 } 84 85 static ULONG WINAPI MimeInternat_AddRef( IMimeInternational *iface ) 86 { 87 internat_impl *This = impl_from_IMimeInternational( iface ); 88 return InterlockedIncrement(&This->refs); 89 } 90 91 static ULONG WINAPI MimeInternat_Release( IMimeInternational *iface ) 92 { 93 internat_impl *This = impl_from_IMimeInternational( iface ); 94 ULONG refs; 95 96 refs = InterlockedDecrement(&This->refs); 97 if (!refs) 98 { 99 charset_entry *charset, *cursor2; 100 101 LIST_FOR_EACH_ENTRY_SAFE(charset, cursor2, &This->charsets, charset_entry, entry) 102 { 103 list_remove(&charset->entry); 104 HeapFree(GetProcessHeap(), 0, charset); 105 } 106 This->cs.DebugInfo->Spare[0] = 0; 107 DeleteCriticalSection(&This->cs); 108 HeapFree(GetProcessHeap(), 0, This); 109 } 110 111 return refs; 112 } 113 114 static HRESULT WINAPI MimeInternat_SetDefaultCharset(IMimeInternational *iface, HCHARSET hCharset) 115 { 116 internat_impl *This = impl_from_IMimeInternational( iface ); 117 118 TRACE("(%p)->(%p)\n", iface, hCharset); 119 120 if(hCharset == NULL) return E_INVALIDARG; 121 /* FIXME check hCharset is valid */ 122 123 InterlockedExchangePointer(&This->default_charset, hCharset); 124 125 return S_OK; 126 } 127 128 static HRESULT WINAPI MimeInternat_GetDefaultCharset(IMimeInternational *iface, LPHCHARSET phCharset) 129 { 130 internat_impl *This = impl_from_IMimeInternational( iface ); 131 HRESULT hr = S_OK; 132 133 TRACE("(%p)->(%p)\n", iface, phCharset); 134 135 if(This->default_charset == NULL) 136 { 137 HCHARSET hcs; 138 hr = IMimeInternational_GetCodePageCharset(iface, GetACP(), CHARSET_BODY, &hcs); 139 if(SUCCEEDED(hr)) 140 InterlockedCompareExchangePointer(&This->default_charset, hcs, NULL); 141 } 142 *phCharset = This->default_charset; 143 144 return hr; 145 } 146 147 static HRESULT mlang_getcodepageinfo(UINT cp, MIMECPINFO *mlang_cp_info) 148 { 149 HRESULT hr; 150 IMultiLanguage *ml; 151 152 hr = get_mlang(&ml); 153 154 if(SUCCEEDED(hr)) 155 { 156 hr = IMultiLanguage_GetCodePageInfo(ml, cp, mlang_cp_info); 157 IMultiLanguage_Release(ml); 158 } 159 return hr; 160 } 161 162 static HRESULT WINAPI MimeInternat_GetCodePageCharset(IMimeInternational *iface, CODEPAGEID cpiCodePage, 163 CHARSETTYPE ctCsetType, 164 LPHCHARSET phCharset) 165 { 166 HRESULT hr; 167 MIMECPINFO mlang_cp_info; 168 169 TRACE("(%p)->(%d, %d, %p)\n", iface, cpiCodePage, ctCsetType, phCharset); 170 171 *phCharset = NULL; 172 173 hr = mlang_getcodepageinfo(cpiCodePage, &mlang_cp_info); 174 if(SUCCEEDED(hr)) 175 { 176 const WCHAR *charset_nameW = NULL; 177 char *charset_name; 178 DWORD len; 179 180 switch(ctCsetType) 181 { 182 case CHARSET_BODY: 183 charset_nameW = mlang_cp_info.wszBodyCharset; 184 break; 185 case CHARSET_HEADER: 186 charset_nameW = mlang_cp_info.wszHeaderCharset; 187 break; 188 case CHARSET_WEB: 189 charset_nameW = mlang_cp_info.wszWebCharset; 190 break; 191 default: 192 return MIME_E_INVALID_CHARSET_TYPE; 193 } 194 len = WideCharToMultiByte(CP_ACP, 0, charset_nameW, -1, NULL, 0, NULL, NULL); 195 charset_name = HeapAlloc(GetProcessHeap(), 0, len); 196 WideCharToMultiByte(CP_ACP, 0, charset_nameW, -1, charset_name, len, NULL, NULL); 197 hr = IMimeInternational_FindCharset(iface, charset_name, phCharset); 198 HeapFree(GetProcessHeap(), 0, charset_name); 199 } 200 return hr; 201 } 202 203 static HRESULT mlang_getcsetinfo(const char *charset, MIMECSETINFO *mlang_info) 204 { 205 DWORD len = MultiByteToWideChar(CP_ACP, 0, charset, -1, NULL, 0); 206 BSTR bstr = SysAllocStringLen(NULL, len - 1); 207 HRESULT hr; 208 IMultiLanguage *ml; 209 210 MultiByteToWideChar(CP_ACP, 0, charset, -1, bstr, len); 211 212 hr = get_mlang(&ml); 213 214 if(SUCCEEDED(hr)) 215 { 216 hr = IMultiLanguage_GetCharsetInfo(ml, bstr, mlang_info); 217 IMultiLanguage_Release(ml); 218 } 219 SysFreeString(bstr); 220 if(FAILED(hr)) hr = MIME_E_NOT_FOUND; 221 return hr; 222 } 223 224 static HCHARSET add_charset(struct list *list, MIMECSETINFO *mlang_info, HCHARSET handle) 225 { 226 charset_entry *charset = HeapAlloc(GetProcessHeap(), 0, sizeof(*charset)); 227 228 WideCharToMultiByte(CP_ACP, 0, mlang_info->wszCharset, -1, 229 charset->cs_info.szName, sizeof(charset->cs_info.szName), NULL, NULL); 230 charset->cs_info.cpiWindows = mlang_info->uiCodePage; 231 charset->cs_info.cpiInternet = mlang_info->uiInternetEncoding; 232 charset->cs_info.hCharset = handle; 233 charset->cs_info.dwReserved1 = 0; 234 list_add_head(list, &charset->entry); 235 236 return charset->cs_info.hCharset; 237 } 238 239 static HRESULT WINAPI MimeInternat_FindCharset(IMimeInternational *iface, LPCSTR pszCharset, 240 LPHCHARSET phCharset) 241 { 242 internat_impl *This = impl_from_IMimeInternational( iface ); 243 HRESULT hr = MIME_E_NOT_FOUND; 244 charset_entry *charset; 245 246 TRACE("(%p)->(%s, %p)\n", iface, debugstr_a(pszCharset), phCharset); 247 248 *phCharset = NULL; 249 250 EnterCriticalSection(&This->cs); 251 252 LIST_FOR_EACH_ENTRY(charset, &This->charsets, charset_entry, entry) 253 { 254 if(!lstrcmpiA(charset->cs_info.szName, pszCharset)) 255 { 256 *phCharset = charset->cs_info.hCharset; 257 hr = S_OK; 258 break; 259 } 260 } 261 262 if(hr == MIME_E_NOT_FOUND) 263 { 264 MIMECSETINFO mlang_info; 265 266 LeaveCriticalSection(&This->cs); 267 hr = mlang_getcsetinfo(pszCharset, &mlang_info); 268 EnterCriticalSection(&This->cs); 269 270 if(SUCCEEDED(hr)) 271 *phCharset = add_charset(&This->charsets, &mlang_info, 272 UlongToHandle(InterlockedIncrement(&This->next_charset_handle))); 273 } 274 275 LeaveCriticalSection(&This->cs); 276 return hr; 277 } 278 279 static HRESULT WINAPI MimeInternat_GetCharsetInfo(IMimeInternational *iface, HCHARSET hCharset, 280 LPINETCSETINFO pCsetInfo) 281 { 282 internat_impl *This = impl_from_IMimeInternational( iface ); 283 HRESULT hr = MIME_E_INVALID_HANDLE; 284 charset_entry *charset; 285 286 TRACE("(%p)->(%p, %p)\n", iface, hCharset, pCsetInfo); 287 288 EnterCriticalSection(&This->cs); 289 290 LIST_FOR_EACH_ENTRY(charset, &This->charsets, charset_entry, entry) 291 { 292 if(charset->cs_info.hCharset == hCharset) 293 { 294 *pCsetInfo = charset->cs_info; 295 hr = S_OK; 296 break; 297 } 298 } 299 300 LeaveCriticalSection(&This->cs); 301 302 return hr; 303 } 304 305 static HRESULT WINAPI MimeInternat_GetCodePageInfo(IMimeInternational *iface, CODEPAGEID cpiCodePage, 306 LPCODEPAGEINFO pCodePageInfo) 307 { 308 FIXME("stub\n"); 309 return E_NOTIMPL; 310 } 311 312 static HRESULT WINAPI MimeInternat_CanConvertCodePages(IMimeInternational *iface, CODEPAGEID cpiSource, 313 CODEPAGEID cpiDest) 314 { 315 HRESULT hr; 316 IMultiLanguage *ml; 317 318 TRACE("(%p)->(%d, %d)\n", iface, cpiSource, cpiDest); 319 320 /* Could call mlang.IsConvertINetStringAvailable() to avoid the COM overhead if need be. */ 321 322 hr = get_mlang(&ml); 323 if(SUCCEEDED(hr)) 324 { 325 hr = IMultiLanguage_IsConvertible(ml, cpiSource, cpiDest); 326 IMultiLanguage_Release(ml); 327 } 328 329 return hr; 330 } 331 332 static HRESULT WINAPI MimeInternat_DecodeHeader(IMimeInternational *iface, HCHARSET hCharset, 333 LPCSTR pszData, 334 LPPROPVARIANT pDecoded, 335 LPRFC1522INFO pRfc1522Info) 336 { 337 FIXME("stub\n"); 338 return E_NOTIMPL; 339 } 340 341 static HRESULT WINAPI MimeInternat_EncodeHeader(IMimeInternational *iface, HCHARSET hCharset, 342 LPPROPVARIANT pData, 343 LPSTR *ppszEncoded, 344 LPRFC1522INFO pRfc1522Info) 345 { 346 FIXME("stub\n"); 347 return E_NOTIMPL; 348 } 349 350 static HRESULT WINAPI MimeInternat_ConvertBuffer(IMimeInternational *iface, CODEPAGEID cpiSource, 351 CODEPAGEID cpiDest, LPBLOB pIn, LPBLOB pOut, 352 ULONG *pcbRead) 353 { 354 HRESULT hr; 355 IMultiLanguage *ml; 356 357 TRACE("(%p)->(%d, %d, %p, %p, %p)\n", iface, cpiSource, cpiDest, pIn, pOut, pcbRead); 358 359 *pcbRead = 0; 360 pOut->cbSize = 0; 361 362 /* Could call mlang.ConvertINetString() to avoid the COM overhead if need be. */ 363 364 hr = get_mlang(&ml); 365 if(SUCCEEDED(hr)) 366 { 367 DWORD mode = 0; 368 UINT in_size = pIn->cbSize, out_size; 369 370 hr = IMultiLanguage_ConvertString(ml, &mode, cpiSource, cpiDest, pIn->pBlobData, &in_size, 371 NULL, &out_size); 372 if(hr == S_OK) /* S_FALSE means the conversion could not be performed */ 373 { 374 pOut->pBlobData = CoTaskMemAlloc(out_size); 375 if(!pOut->pBlobData) 376 hr = E_OUTOFMEMORY; 377 else 378 { 379 mode = 0; 380 in_size = pIn->cbSize; 381 hr = IMultiLanguage_ConvertString(ml, &mode, cpiSource, cpiDest, pIn->pBlobData, &in_size, 382 pOut->pBlobData, &out_size); 383 384 if(hr == S_OK) 385 { 386 *pcbRead = in_size; 387 pOut->cbSize = out_size; 388 } 389 else 390 CoTaskMemFree(pOut->pBlobData); 391 } 392 } 393 IMultiLanguage_Release(ml); 394 } 395 396 return hr; 397 } 398 399 static HRESULT WINAPI MimeInternat_ConvertString(IMimeInternational *iface, CODEPAGEID cpiSource, 400 CODEPAGEID cpiDest, LPPROPVARIANT pIn, 401 LPPROPVARIANT pOut) 402 { 403 HRESULT hr; 404 int src_len; 405 IMultiLanguage *ml; 406 407 TRACE("(%p)->(%d, %d, %p %p)\n", iface, cpiSource, cpiDest, pIn, pOut); 408 409 switch(pIn->vt) 410 { 411 case VT_LPSTR: 412 if(cpiSource == CP_UNICODE) cpiSource = GetACP(); 413 src_len = strlen(pIn->u.pszVal); 414 break; 415 case VT_LPWSTR: 416 cpiSource = CP_UNICODE; 417 src_len = lstrlenW(pIn->u.pwszVal) * sizeof(WCHAR); 418 break; 419 default: 420 return E_INVALIDARG; 421 } 422 423 hr = get_mlang(&ml); 424 if(SUCCEEDED(hr)) 425 { 426 DWORD mode = 0; 427 UINT in_size = src_len, out_size; 428 429 hr = IMultiLanguage_ConvertString(ml, &mode, cpiSource, cpiDest, (BYTE*)pIn->u.pszVal, &in_size, 430 NULL, &out_size); 431 if(hr == S_OK) /* S_FALSE means the conversion could not be performed */ 432 { 433 out_size += (cpiDest == CP_UNICODE) ? sizeof(WCHAR) : sizeof(char); 434 435 pOut->u.pszVal = CoTaskMemAlloc(out_size); 436 if(!pOut->u.pszVal) 437 hr = E_OUTOFMEMORY; 438 else 439 { 440 mode = 0; 441 in_size = src_len; 442 hr = IMultiLanguage_ConvertString(ml, &mode, cpiSource, cpiDest, (BYTE*)pIn->u.pszVal, &in_size, 443 (BYTE*)pOut->u.pszVal, &out_size); 444 445 if(hr == S_OK) 446 { 447 if(cpiDest == CP_UNICODE) 448 { 449 pOut->u.pwszVal[out_size / sizeof(WCHAR)] = 0; 450 pOut->vt = VT_LPWSTR; 451 } 452 else 453 { 454 pOut->u.pszVal[out_size] = '\0'; 455 pOut->vt = VT_LPSTR; 456 } 457 } 458 else 459 CoTaskMemFree(pOut->u.pszVal); 460 } 461 } 462 IMultiLanguage_Release(ml); 463 } 464 return hr; 465 } 466 467 static HRESULT WINAPI MimeInternat_MLANG_ConvertInetReset(IMimeInternational *iface) 468 { 469 FIXME("stub\n"); 470 return E_NOTIMPL; 471 } 472 473 static HRESULT WINAPI MimeInternat_MLANG_ConvertInetString(IMimeInternational *iface, CODEPAGEID cpiSource, 474 CODEPAGEID cpiDest, 475 LPCSTR pSource, 476 int *pnSizeOfSource, 477 LPSTR pDestination, 478 int *pnDstSize) 479 { 480 FIXME("stub\n"); 481 return E_NOTIMPL; 482 } 483 484 static HRESULT WINAPI MimeInternat_Rfc1522Decode(IMimeInternational *iface, LPCSTR pszValue, 485 LPSTR pszCharset, 486 ULONG cchmax, 487 LPSTR *ppszDecoded) 488 { 489 FIXME("stub\n"); 490 return E_NOTIMPL; 491 } 492 493 static HRESULT WINAPI MimeInternat_Rfc1522Encode(IMimeInternational *iface, LPCSTR pszValue, 494 HCHARSET hCharset, 495 LPSTR *ppszEncoded) 496 { 497 FIXME("stub\n"); 498 return E_NOTIMPL; 499 } 500 501 static IMimeInternationalVtbl mime_internat_vtbl = 502 { 503 MimeInternat_QueryInterface, 504 MimeInternat_AddRef, 505 MimeInternat_Release, 506 MimeInternat_SetDefaultCharset, 507 MimeInternat_GetDefaultCharset, 508 MimeInternat_GetCodePageCharset, 509 MimeInternat_FindCharset, 510 MimeInternat_GetCharsetInfo, 511 MimeInternat_GetCodePageInfo, 512 MimeInternat_CanConvertCodePages, 513 MimeInternat_DecodeHeader, 514 MimeInternat_EncodeHeader, 515 MimeInternat_ConvertBuffer, 516 MimeInternat_ConvertString, 517 MimeInternat_MLANG_ConvertInetReset, 518 MimeInternat_MLANG_ConvertInetString, 519 MimeInternat_Rfc1522Decode, 520 MimeInternat_Rfc1522Encode 521 }; 522 523 static internat_impl *global_internat; 524 525 HRESULT MimeInternational_Construct(IMimeInternational **internat) 526 { 527 global_internat = HeapAlloc(GetProcessHeap(), 0, sizeof(*global_internat)); 528 global_internat->IMimeInternational_iface.lpVtbl = &mime_internat_vtbl; 529 global_internat->refs = 0; 530 InitializeCriticalSection(&global_internat->cs); 531 global_internat->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": global_internat.cs"); 532 533 list_init(&global_internat->charsets); 534 global_internat->next_charset_handle = 0; 535 global_internat->default_charset = NULL; 536 537 *internat = &global_internat->IMimeInternational_iface; 538 539 IMimeInternational_AddRef(*internat); 540 return S_OK; 541 } 542 543 HRESULT WINAPI MimeOleGetInternat(IMimeInternational **internat) 544 { 545 TRACE("(%p)\n", internat); 546 547 *internat = &global_internat->IMimeInternational_iface; 548 IMimeInternational_AddRef(*internat); 549 return S_OK; 550 } 551 552 HRESULT WINAPI MimeOleFindCharset(LPCSTR name, LPHCHARSET charset) 553 { 554 IMimeInternational *internat; 555 HRESULT hr; 556 557 TRACE("(%s, %p)\n", debugstr_a(name), charset); 558 559 hr = MimeOleGetInternat(&internat); 560 if(SUCCEEDED(hr)) 561 { 562 hr = IMimeInternational_FindCharset(internat, name, charset); 563 IMimeInternational_Release(internat); 564 } 565 return hr; 566 } 567 568 HRESULT WINAPI MimeOleGetCharsetInfo(HCHARSET hCharset, LPINETCSETINFO pCsetInfo) 569 { 570 IMimeInternational *internat; 571 HRESULT hr; 572 573 TRACE("(%p, %p)\n", hCharset, pCsetInfo); 574 575 hr = MimeOleGetInternat(&internat); 576 if(SUCCEEDED(hr)) 577 { 578 hr = IMimeInternational_GetCharsetInfo(internat, hCharset, pCsetInfo); 579 IMimeInternational_Release(internat); 580 } 581 return hr; 582 } 583 584 HRESULT WINAPI MimeOleGetDefaultCharset(LPHCHARSET charset) 585 { 586 IMimeInternational *internat; 587 HRESULT hr; 588 589 TRACE("(%p)\n", charset); 590 591 hr = MimeOleGetInternat(&internat); 592 if(SUCCEEDED(hr)) 593 { 594 hr = IMimeInternational_GetDefaultCharset(internat, charset); 595 IMimeInternational_Release(internat); 596 } 597 return hr; 598 } 599