* New version 2.21.999
[alpine.git] / pith / osdep / mimedisp.c
blob1dad4b5f5e406a96db3e198cfbc2831e3eb312a3
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mimedisp.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include <system.h>
20 #include <general.h>
22 #include "../../c-client/fs.h"
24 #ifdef _WINDOWS
25 #include "../../pico/osdep/mswin.h"
26 #endif
28 #include "mimedisp.h"
29 #include "../charconv/utf8.h"
30 #ifdef OSX_TARGET
31 #include "../../pith/osdep/collate.h" /* for strucmp */
32 #include <Security/AuthSession.h>
33 #endif
37 * Internal prototypes
39 #ifdef _WINDOWS
40 int mswin_reg_viewer(LPTSTR mime_type, LPTSTR mime_ext,
41 char *cmd, int clen, int chk);
42 int mswin_reg_mime_type(LPTSTR file_ext, LPTSTR mime_type, size_t mime_type_len);
43 int mswin_reg_mime_ext(LPTSTR mime_type, LPTSTR file_ext, size_t file_ext_len);
45 #endif /* WINDOWS */
47 #if OSX_TARGET
49 int osx_build_mime_type_cmd(char *, char *, int, int *);
50 int osx_build_mime_ext_cmd(char *, char *, int, int *);
52 #endif /* OSX_TARGET */
57 * Determine if there is an OS-specific mechanism for accessing
58 * MIME and extension data. In the general *nix case this is all
59 * done through mailcap and mime.types files.
61 * Returns: 0 if there is no support (most *nix cases)
62 * 1 if there is support (Mac OS X, Windows)
64 int
65 mime_os_specific_access(void)
67 #ifdef _WINDOWS
68 return 1;
69 #elif OSX_TARGET
70 # ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
72 /*
73 * if we don't have the WidowSession then we should avoid using
74 * frameworks unless they call themselves daemon-safe
76 SecuritySessionId session_id;
77 SessionAttributeBits session_bits;
79 if((SessionGetInfo(callerSecuritySession, &session_id, &session_bits)
80 == errSessionSuccess)
81 && session_bits & sessionHasGraphicAccess)
82 return 1;
83 else
84 return 0;
86 # else
87 return 0;
88 # endif
89 #else
90 return 0;
91 #endif
96 * Return the command based on either the mimetype or the file
97 * extension. Mime-type always takes precedence.
99 * mime_type - mime-type of the file we're looking at
100 * mime_ext - file extension given us by the mime data
101 * cmd - buffer to copy the resulting command into
102 * chk - whether or not we should check the file extension
104 * Returns: 1 on success, 0 on failure
107 mime_get_os_mimetype_command(char *mime_type, char *mime_ext, char *cmd,
108 int clen, int chk, int *sp_hndlp)
110 #ifdef _WINDOWS
111 int ret;
112 LPTSTR mime_type_lpt, mime_ext_lpt;
114 mime_type_lpt = utf8_to_lptstr(mime_type);
115 mime_ext_lpt = utf8_to_lptstr(mime_ext);
117 ret = mswin_reg_viewer(mime_type_lpt, mime_ext_lpt, cmd, clen, chk);
119 if(mime_type_lpt)
120 fs_give((void **) &mime_type_lpt);
122 if(mime_ext_lpt)
123 fs_give((void **) &mime_ext_lpt);
125 return ret;
127 #elif OSX_TARGET
130 * if we wanted to be more like PC-Pine, we'd try checking
131 * the mime-type of mime_ext and seeing if that matches
132 * with our mime-type, which is safe for opening
134 if(!mime_os_specific_access())
135 return(0);
137 /* don't want to use Mail or something for a part alpine is good at */
138 if(!strucmp(mime_type, "message/rfc822"))
139 return(0);
141 return(osx_build_mime_type_cmd(mime_type, cmd, clen, sp_hndlp)
142 || (chk && mime_ext && *mime_ext &&
143 osx_build_mime_ext_cmd(mime_ext, cmd, clen, sp_hndlp)));
144 #else
145 return 0;
146 #endif
151 * Given a file extension, return the mime-type if there is one
153 * Returns: 1 on success, 0 on failure
156 mime_get_os_mimetype_from_ext(char *file_ext, char *mime_type, int mime_type_len)
158 #ifdef _WINDOWS
159 int ret;
160 LPTSTR x, file_ext_lpt, mime_type_lpt;
162 file_ext_lpt = utf8_to_lptstr(file_ext);
164 if(file_ext_lpt){
165 if(mime_type){
166 mime_type_lpt = (LPTSTR) fs_get(mime_type_len * sizeof(TCHAR));
167 mime_type_lpt[0] = '\0';
169 else
170 mime_type_lpt = NULL;
173 ret = mswin_reg_mime_type(file_ext_lpt, mime_type_lpt, (size_t) mime_type_len);
175 /* convert answer back to UTF-8 */
176 if(ret && mime_type_lpt && mime_type){
177 char *u;
179 u = lptstr_to_utf8(mime_type_lpt);
180 if(u){
181 strncpy(mime_type, u, mime_type_len);
182 mime_type[mime_type_len-1] = '\0';
183 fs_give((void **) &u);
187 if(file_ext_lpt)
188 fs_give((void **) &file_ext_lpt);
190 if(mime_type_lpt)
191 fs_give((void **) &mime_type_lpt);
193 return ret;
195 #elif OSX_TARGET
197 if(!mime_os_specific_access())
198 return(0);
199 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
200 CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL;
201 char buf[1024];
203 if(!file_ext || !*file_ext)
204 return 0;
206 /* This for if we built on OS X >= 10.3 but run on < 10.3 */
207 if(&UTTypeCreatePreferredIdentifierForTag == NULL)
208 return 0;
209 if((ext_ref = CFStringCreateWithCString(NULL, *file_ext == '.' ?
210 file_ext + 1 : file_ext,
211 kCFStringEncodingASCII)) == NULL)
212 return 0;
213 if((type_id_ref
214 = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
215 ext_ref, NULL)) == NULL)
216 return 0;
218 if((mime_ref = UTTypeCopyPreferredTagWithClass(type_id_ref,
219 kUTTagClassMIMEType)) == NULL)
220 return 0;
221 if(CFStringGetCString(mime_ref, mime_type,
222 (CFIndex)mime_type_len - 1,
223 kCFStringEncodingASCII) == false)
224 return 0;
226 mime_type[mime_type_len - 1] = '\0';
228 return 1;
229 #else
230 return 0;
231 #endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
233 #else
234 return 0;
235 #endif /* OSX_TARGET */
240 * Given a mime-type, return the file extension if there is one
242 * Returns: 1 on success, 0 on failure
245 mime_get_os_ext_from_mimetype(char *mime_type, char *file_ext, int file_ext_len)
247 #ifdef _WINDOWS
248 int ret;
249 LPTSTR x, mime_type_lpt, file_ext_lpt;
251 mime_type_lpt = utf8_to_lptstr(mime_type);
253 if(mime_type_lpt){
254 if(file_ext){
255 file_ext_lpt = (LPTSTR) fs_get(file_ext_len * sizeof(TCHAR));
256 file_ext_lpt[0] = '\0';
258 else
259 file_ext_lpt = NULL;
262 ret = mswin_reg_mime_ext(mime_type_lpt, file_ext_lpt, (size_t) file_ext_len);
264 /* convert answer back to UTF-8 */
265 if(ret && file_ext_lpt && file_ext){
266 char *u;
268 u = lptstr_to_utf8(file_ext_lpt);
269 if(u){
270 strncpy(file_ext, u, file_ext_len);
271 file_ext[file_ext_len-1] = '\0';
272 fs_give((void **) &u);
276 if(mime_type_lpt)
277 fs_give((void **) &mime_type_lpt);
279 if(file_ext_lpt)
280 fs_give((void **) &file_ext_lpt);
282 return ret;
284 #elif OSX_TARGET
286 if(!mime_os_specific_access())
287 return(0);
288 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
289 CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL;
291 if(!mime_type || !*mime_type)
292 return 0;
293 /* This for if we built on OS X >= 10.3 but run on < 10.3 */
294 if(&UTTypeCreatePreferredIdentifierForTag == NULL)
295 return 0;
296 if((mime_ref = CFStringCreateWithCString(NULL, mime_type,
297 kCFStringEncodingASCII)) == NULL)
298 return 0;
299 if((type_id_ref
300 = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
301 mime_ref, NULL)) == NULL)
302 return 0;
304 if((ext_ref = UTTypeCopyPreferredTagWithClass(type_id_ref,
305 kUTTagClassFilenameExtension)) == NULL)
306 return 0;
307 if((CFStringGetCString(ext_ref, file_ext, (CFIndex)file_ext_len - 1,
308 kCFStringEncodingASCII)) == false)
309 return 0;
311 file_ext[file_ext_len - 1] = '\0';
313 return 1;
314 #else
315 return 0;
316 #endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
318 #else
319 return 0;
320 #endif /* OSX_TARGET */
325 #ifdef _WINDOWS
328 * mswin_reg_viewer -
331 mswin_reg_viewer(LPTSTR mime_type, LPTSTR mime_ext, char *cmd, int clen, int chk)
333 TCHAR tmp[256];
334 LPTSTR ext;
336 tmp[0] = '\0';
339 * Everything's based on the file extension.
340 * So, sniff at registry's mapping for the given MIME type/subtype
341 * and sniff at clues on how to launch it, or
342 * extension mapping and then
343 * look for clues that some app will handle it.
346 return(((mswin_reg_mime_ext(mime_type, ext = tmp, sizeof(tmp)/sizeof(TCHAR))
347 || ((ext = mime_ext) && *mime_ext
348 && ((mswin_reg_mime_type(mime_ext, tmp, sizeof(tmp)/sizeof(TCHAR))
349 && mime_type && !_tcsicmp(mime_type, tmp))
350 || mime_type && !_tcsicmp(mime_type, TEXT("application/octet-stream")))))
351 && MSWRShellCanOpen(ext, cmd, clen, 0) == TRUE)
352 || (chk && MSWRShellCanOpen(ext, cmd, clen, 1) == TRUE));
357 * given a file name extension, fill in the provided buf with its
358 * corresponding MIME type
361 mswin_reg_mime_type(LPTSTR file_ext, LPTSTR mime_type, size_t mime_type_len)
363 TCHAR buf[64];
364 DWORD len = mime_type_len;
366 if(file_ext[0] != '.'){
367 *buf = '.';
368 _tcsncpy(buf + 1, file_ext, sizeof(buf)/sizeof(TCHAR)-1);
369 buf[sizeof(buf)/sizeof(TCHAR)-1] = '\0';
370 file_ext = buf;
373 return(MSWRPeek(HKEY_CLASSES_ROOT, file_ext, TEXT("content type"),
374 mime_type, &len) == TRUE);
379 * given a mime_type, fill in the provided buf with its
380 * corresponding file name extension
383 mswin_reg_mime_ext(LPTSTR mime_type, LPTSTR file_ext, size_t file_ext_len)
385 TCHAR keybuf[128];
386 DWORD len = file_ext_len;
388 if(mime_type && _tcslen(mime_type) < 50){
389 _sntprintf(keybuf, sizeof(keybuf), TEXT("MIME\\Database\\Content Type\\%s"), mime_type);
390 return(MSWRPeek(HKEY_CLASSES_ROOT, keybuf,
391 TEXT("extension"), file_ext, &len) == TRUE);
394 return FALSE;
396 #endif /* _WINDOWS */
400 #if OSX_TARGET
401 /* returns: 1 if success, 0 if failure */
403 osx_build_mime_type_cmd(mime_type, cmd, cmdlen, sp_hndlp)
404 char *mime_type, *cmd;
405 int cmdlen, *sp_hndlp;
407 int rv = 0;
409 if(!mime_os_specific_access())
410 return(0);
411 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
412 CFStringRef str_ref = NULL, ret_str_ref = NULL;
413 CFURLRef url_ref = NULL;
415 if(&LSCopyApplicationForMIMEType == NULL)
416 return 0;
417 if((str_ref = CFStringCreateWithCString(NULL, mime_type,
418 kCFStringEncodingASCII)) == NULL)
419 return 0;
420 if(LSCopyApplicationForMIMEType(str_ref, kLSRolesAll, &url_ref)
421 != kLSApplicationNotFoundErr){
422 if((ret_str_ref = CFURLGetString(url_ref)) == NULL)
423 return 0;
424 if(CFStringGetCString(ret_str_ref, cmd, (CFIndex)cmdlen,
425 kCFStringEncodingASCII) == false)
426 return 0;
427 if(sp_hndlp)
428 *sp_hndlp = 1;
429 rv = 1;
430 if(url_ref)
431 CFRelease(url_ref);
433 #endif /* AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER */
434 return rv;
438 /* returns: 1 if success, 0 if failure */
440 osx_build_mime_ext_cmd(mime_ext, cmd, cmdlen, sp_hndlp)
441 char *mime_ext, *cmd;
442 int cmdlen, *sp_hndlp;
444 int rv = 0;
446 if(!mime_os_specific_access())
447 return 0;
449 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
450 CFStringRef str_ref = NULL, ret_str_ref = NULL;
451 CFURLRef url_ref = NULL;
453 if((str_ref = CFStringCreateWithCString(NULL, (*mime_ext) == '.'
454 ? mime_ext+1 : mime_ext,
455 kCFStringEncodingASCII)) == NULL)
456 return 0;
457 if(LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
458 str_ref, kLSRolesAll, NULL, &url_ref)
459 != kLSApplicationNotFoundErr){
460 if((ret_str_ref = CFURLGetString(url_ref)) == NULL)
461 return 0;
462 if(CFStringGetCString(ret_str_ref, cmd, (CFIndex)cmdlen,
463 kCFStringEncodingASCII) == false)
464 return 0;
465 if(sp_hndlp)
466 *sp_hndlp = 1;
467 rv = 1;
468 if(url_ref)
469 CFRelease(url_ref);
471 #endif
472 return rv;
474 #endif /* OSX_TARGET */