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