xref: /reactos/dll/win32/inetcomm/mimeintl.c (revision 139a3d66)
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