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