Add support for tab-completion when selecting by rule
[alpine.git] / pith / osdep / mimedisp.c
blob7eab9923b539f03464f922549e146d7d139697cd
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include <system.h>
16 #include <general.h>
18 #include "../../c-client/fs.h"
20 #ifdef _WINDOWS
21 #include "../../pico/osdep/mswin.h"
22 #endif
24 #include "mimedisp.h"
25 #include "../charconv/utf8.h"
26 #ifdef OSX_TARGET
27 #include "../../pith/osdep/collate.h" /* for strucmp */
28 #include <Security/AuthSession.h>
29 #endif
33 * Internal prototypes
35 #ifdef _WINDOWS
36 int mswin_reg_viewer(LPTSTR mime_type, LPTSTR mime_ext,
37 char *cmd, int clen, int chk);
38 int mswin_reg_mime_type(LPTSTR file_ext, LPTSTR mime_type, size_t mime_type_len);
39 int mswin_reg_mime_ext(LPTSTR mime_type, LPTSTR file_ext, size_t file_ext_len);
41 #endif /* WINDOWS */
43 #if OSX_TARGET
45 int osx_build_mime_type_cmd(char *, char *, int, int *);
46 int osx_build_mime_ext_cmd(char *, char *, int, int *);
48 #endif /* OSX_TARGET */
53 * Determine if there is an OS-specific mechanism for accessing
54 * MIME and extension data. In the general *nix case this is all
55 * done through mailcap and mime.types files.
57 * Returns: 0 if there is no support (most *nix cases)
58 * 1 if there is support (Mac OS X, Windows)
60 int
61 mime_os_specific_access(void)
63 #ifdef _WINDOWS
64 return 1;
65 #elif OSX_TARGET
66 # ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
68 /*
69 * if we don't have the WidowSession then we should avoid using
70 * frameworks unless they call themselves daemon-safe
72 SecuritySessionId session_id;
73 SessionAttributeBits session_bits;
75 if((SessionGetInfo(callerSecuritySession, &session_id, &session_bits)
76 == errSessionSuccess)
77 && session_bits & sessionHasGraphicAccess)
78 return 1;
79 else
80 return 0;
82 # else
83 return 0;
84 # endif
85 #else
86 return 0;
87 #endif
92 * Return the command based on either the mimetype or the file
93 * extension. Mime-type always takes precedence.
95 * mime_type - mime-type of the file we're looking at
96 * mime_ext - file extension given us by the mime data
97 * cmd - buffer to copy the resulting command into
98 * chk - whether or not we should check the file extension
100 * Returns: 1 on success, 0 on failure
103 mime_get_os_mimetype_command(char *mime_type, char *mime_ext, char *cmd,
104 int clen, int chk, int *sp_hndlp)
106 #ifdef _WINDOWS
107 int ret;
108 LPTSTR mime_type_lpt, mime_ext_lpt;
110 mime_type_lpt = utf8_to_lptstr(mime_type);
111 mime_ext_lpt = utf8_to_lptstr(mime_ext);
113 ret = mswin_reg_viewer(mime_type_lpt, mime_ext_lpt, cmd, clen, chk);
115 if(mime_type_lpt)
116 fs_give((void **) &mime_type_lpt);
118 if(mime_ext_lpt)
119 fs_give((void **) &mime_ext_lpt);
121 return ret;
123 #elif OSX_TARGET
126 * if we wanted to be more like PC-Pine, we'd try checking
127 * the mime-type of mime_ext and seeing if that matches
128 * with our mime-type, which is safe for opening
130 if(!mime_os_specific_access())
131 return(0);
133 /* don't want to use Mail or something for a part alpine is good at */
134 if(!strucmp(mime_type, "message/rfc822"))
135 return(0);
137 return(osx_build_mime_type_cmd(mime_type, cmd, clen, sp_hndlp)
138 || (chk && mime_ext && *mime_ext &&
139 osx_build_mime_ext_cmd(mime_ext, cmd, clen, sp_hndlp)));
140 #else
141 return 0;
142 #endif
147 * Given a file extension, return the mime-type if there is one
149 * Returns: 1 on success, 0 on failure
152 mime_get_os_mimetype_from_ext(char *file_ext, char *mime_type, int mime_type_len)
154 #ifdef _WINDOWS
155 int ret;
156 LPTSTR x, file_ext_lpt, mime_type_lpt;
158 file_ext_lpt = utf8_to_lptstr(file_ext);
160 if(file_ext_lpt){
161 if(mime_type){
162 mime_type_lpt = (LPTSTR) fs_get(mime_type_len * sizeof(TCHAR));
163 mime_type_lpt[0] = '\0';
165 else
166 mime_type_lpt = NULL;
169 ret = mswin_reg_mime_type(file_ext_lpt, mime_type_lpt, (size_t) mime_type_len);
171 /* convert answer back to UTF-8 */
172 if(ret && mime_type_lpt && mime_type){
173 char *u;
175 u = lptstr_to_utf8(mime_type_lpt);
176 if(u){
177 strncpy(mime_type, u, mime_type_len);
178 mime_type[mime_type_len-1] = '\0';
179 fs_give((void **) &u);
183 if(file_ext_lpt)
184 fs_give((void **) &file_ext_lpt);
186 if(mime_type_lpt)
187 fs_give((void **) &mime_type_lpt);
189 return ret;
191 #elif OSX_TARGET
193 if(!mime_os_specific_access())
194 return(0);
195 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
196 CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL;
197 char buf[1024];
199 if(!file_ext || !*file_ext)
200 return 0;
202 /* This for if we built on OS X >= 10.3 but run on < 10.3 */
203 if(&UTTypeCreatePreferredIdentifierForTag == NULL)
204 return 0;
205 if((ext_ref = CFStringCreateWithCString(NULL, *file_ext == '.' ?
206 file_ext + 1 : file_ext,
207 kCFStringEncodingASCII)) == NULL)
208 return 0;
209 if((type_id_ref
210 = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
211 ext_ref, NULL)) == NULL)
212 return 0;
214 if((mime_ref = UTTypeCopyPreferredTagWithClass(type_id_ref,
215 kUTTagClassMIMEType)) == NULL)
216 return 0;
217 if(CFStringGetCString(mime_ref, mime_type,
218 (CFIndex)mime_type_len - 1,
219 kCFStringEncodingASCII) == false)
220 return 0;
222 mime_type[mime_type_len - 1] = '\0';
224 return 1;
225 #else
226 return 0;
227 #endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
229 #else
230 return 0;
231 #endif /* OSX_TARGET */
236 * Given a mime-type, return the file extension if there is one
238 * Returns: 1 on success, 0 on failure
241 mime_get_os_ext_from_mimetype(char *mime_type, char *file_ext, int file_ext_len)
243 #ifdef _WINDOWS
244 int ret;
245 LPTSTR x, mime_type_lpt, file_ext_lpt;
247 mime_type_lpt = utf8_to_lptstr(mime_type);
249 if(mime_type_lpt){
250 if(file_ext){
251 file_ext_lpt = (LPTSTR) fs_get(file_ext_len * sizeof(TCHAR));
252 file_ext_lpt[0] = '\0';
254 else
255 file_ext_lpt = NULL;
258 ret = mswin_reg_mime_ext(mime_type_lpt, file_ext_lpt, (size_t) file_ext_len);
260 /* convert answer back to UTF-8 */
261 if(ret && file_ext_lpt && file_ext){
262 char *u;
264 u = lptstr_to_utf8(file_ext_lpt);
265 if(u){
266 strncpy(file_ext, u, file_ext_len);
267 file_ext[file_ext_len-1] = '\0';
268 fs_give((void **) &u);
272 if(mime_type_lpt)
273 fs_give((void **) &mime_type_lpt);
275 if(file_ext_lpt)
276 fs_give((void **) &file_ext_lpt);
278 return ret;
280 #elif OSX_TARGET
282 if(!mime_os_specific_access())
283 return(0);
284 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
285 CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL;
287 if(!mime_type || !*mime_type)
288 return 0;
289 /* This for if we built on OS X >= 10.3 but run on < 10.3 */
290 if(&UTTypeCreatePreferredIdentifierForTag == NULL)
291 return 0;
292 if((mime_ref = CFStringCreateWithCString(NULL, mime_type,
293 kCFStringEncodingASCII)) == NULL)
294 return 0;
295 if((type_id_ref
296 = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
297 mime_ref, NULL)) == NULL)
298 return 0;
300 if((ext_ref = UTTypeCopyPreferredTagWithClass(type_id_ref,
301 kUTTagClassFilenameExtension)) == NULL)
302 return 0;
303 if((CFStringGetCString(ext_ref, file_ext, (CFIndex)file_ext_len - 1,
304 kCFStringEncodingASCII)) == false)
305 return 0;
307 file_ext[file_ext_len - 1] = '\0';
309 return 1;
310 #else
311 return 0;
312 #endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
314 #else
315 return 0;
316 #endif /* OSX_TARGET */
321 #ifdef _WINDOWS
324 * mswin_reg_viewer -
327 mswin_reg_viewer(LPTSTR mime_type, LPTSTR mime_ext, char *cmd, int clen, int chk)
329 TCHAR tmp[256];
330 LPTSTR ext;
332 tmp[0] = '\0';
335 * Everything's based on the file extension.
336 * So, sniff at registry's mapping for the given MIME type/subtype
337 * and sniff at clues on how to launch it, or
338 * extension mapping and then
339 * look for clues that some app will handle it.
342 return(((mswin_reg_mime_ext(mime_type, ext = tmp, sizeof(tmp)/sizeof(TCHAR))
343 || ((ext = mime_ext) && *mime_ext
344 && ((mswin_reg_mime_type(mime_ext, tmp, sizeof(tmp)/sizeof(TCHAR))
345 && mime_type && !_tcsicmp(mime_type, tmp))
346 || mime_type && !_tcsicmp(mime_type, TEXT("application/octet-stream")))))
347 && MSWRShellCanOpen(ext, cmd, clen, 0) == TRUE)
348 || (chk && MSWRShellCanOpen(ext, cmd, clen, 1) == TRUE));
353 * given a file name extension, fill in the provided buf with its
354 * corresponding MIME type
357 mswin_reg_mime_type(LPTSTR file_ext, LPTSTR mime_type, size_t mime_type_len)
359 TCHAR buf[64];
360 DWORD len = mime_type_len;
362 if(file_ext[0] != '.'){
363 *buf = '.';
364 _tcsncpy(buf + 1, file_ext, sizeof(buf)/sizeof(TCHAR)-1);
365 buf[sizeof(buf)/sizeof(TCHAR)-1] = '\0';
366 file_ext = buf;
369 return(MSWRPeek(HKEY_CLASSES_ROOT, file_ext, TEXT("content type"),
370 mime_type, &len) == TRUE);
375 * given a mime_type, fill in the provided buf with its
376 * corresponding file name extension
379 mswin_reg_mime_ext(LPTSTR mime_type, LPTSTR file_ext, size_t file_ext_len)
381 TCHAR keybuf[128];
382 DWORD len = file_ext_len;
384 if(mime_type && _tcslen(mime_type) < 50){
385 _sntprintf(keybuf, sizeof(keybuf), TEXT("MIME\\Database\\Content Type\\%s"), mime_type);
386 return(MSWRPeek(HKEY_CLASSES_ROOT, keybuf,
387 TEXT("extension"), file_ext, &len) == TRUE);
390 return FALSE;
392 #endif /* _WINDOWS */
396 #if OSX_TARGET
397 /* returns: 1 if success, 0 if failure */
399 osx_build_mime_type_cmd(mime_type, cmd, cmdlen, sp_hndlp)
400 char *mime_type, *cmd;
401 int cmdlen, *sp_hndlp;
403 int rv = 0;
405 if(!mime_os_specific_access())
406 return(0);
407 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
408 CFStringRef str_ref = NULL, ret_str_ref = NULL;
409 CFURLRef url_ref = NULL;
411 if(&LSCopyApplicationForMIMEType == NULL)
412 return 0;
413 if((str_ref = CFStringCreateWithCString(NULL, mime_type,
414 kCFStringEncodingASCII)) == NULL)
415 return 0;
416 if(LSCopyApplicationForMIMEType(str_ref, kLSRolesAll, &url_ref)
417 != kLSApplicationNotFoundErr){
418 if((ret_str_ref = CFURLGetString(url_ref)) == NULL)
419 return 0;
420 if(CFStringGetCString(ret_str_ref, cmd, (CFIndex)cmdlen,
421 kCFStringEncodingASCII) == false)
422 return 0;
423 if(sp_hndlp)
424 *sp_hndlp = 1;
425 rv = 1;
426 if(url_ref)
427 CFRelease(url_ref);
429 #endif /* AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER */
430 return rv;
434 /* returns: 1 if success, 0 if failure */
436 osx_build_mime_ext_cmd(mime_ext, cmd, cmdlen, sp_hndlp)
437 char *mime_ext, *cmd;
438 int cmdlen, *sp_hndlp;
440 int rv = 0;
442 if(!mime_os_specific_access())
443 return 0;
445 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
446 CFStringRef str_ref = NULL, ret_str_ref = NULL;
447 CFURLRef url_ref = NULL;
449 if((str_ref = CFStringCreateWithCString(NULL, (*mime_ext) == '.'
450 ? mime_ext+1 : mime_ext,
451 kCFStringEncodingASCII)) == NULL)
452 return 0;
453 if(LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
454 str_ref, kLSRolesAll, NULL, &url_ref)
455 != kLSApplicationNotFoundErr){
456 if((ret_str_ref = CFURLGetString(url_ref)) == NULL)
457 return 0;
458 if(CFStringGetCString(ret_str_ref, cmd, (CFIndex)cmdlen,
459 kCFStringEncodingASCII) == false)
460 return 0;
461 if(sp_hndlp)
462 *sp_hndlp = 1;
463 rv = 1;
464 if(url_ref)
465 CFRelease(url_ref);
467 #endif
468 return rv;
470 #endif /* OSX_TARGET */