1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 
10 //! A URL type for Core Foundation.
11 
12 pub use core_foundation_sys::url::*;
13 
14 use base::{TCFType, CFIndex};
15 use string::{CFString};
16 
17 use core_foundation_sys::base::{kCFAllocatorDefault, Boolean};
18 use std::fmt;
19 use std::ptr;
20 use std::path::{Path, PathBuf};
21 use std::mem;
22 
23 use libc::{strlen, PATH_MAX};
24 
25 #[cfg(unix)]
26 use std::os::unix::ffi::OsStrExt;
27 #[cfg(unix)]
28 use std::ffi::OsStr;
29 
30 
31 declare_TCFType!(CFURL, CFURLRef);
32 impl_TCFType!(CFURL, CFURLRef, CFURLGetTypeID);
33 
34 impl fmt::Debug for CFURL {
35     #[inline]
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result36     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37         unsafe {
38             let string: CFString = TCFType::wrap_under_get_rule(CFURLGetString(self.0));
39             write!(f, "{}", string.to_string())
40         }
41     }
42 }
43 
44 impl CFURL {
from_path<P: AsRef<Path>>(path: P, isDirectory: bool) -> Option<CFURL>45     pub fn from_path<P: AsRef<Path>>(path: P, isDirectory: bool) -> Option<CFURL> {
46         let path_bytes;
47         #[cfg(unix)]
48         {
49             path_bytes = path.as_ref().as_os_str().as_bytes()
50         }
51         #[cfg(not(unix))]
52         {
53             // XXX: Getting non-valid UTF8 paths into CoreFoundation on Windows is going to be unpleasant
54             // CFURLGetWideFileSystemRepresentation might help
55             path_bytes = match path.as_ref().to_str() {
56                 Some(path) => path,
57                 None => return None,
58             }
59         }
60 
61         unsafe {
62             let url_ref = CFURLCreateFromFileSystemRepresentation(ptr::null_mut(), path_bytes.as_ptr(), path_bytes.len() as CFIndex, isDirectory as u8);
63             if url_ref.is_null() {
64                 return None;
65             }
66             Some(TCFType::wrap_under_create_rule(url_ref))
67         }
68     }
69 
from_file_system_path(filePath: CFString, pathStyle: CFURLPathStyle, isDirectory: bool) -> CFURL70     pub fn from_file_system_path(filePath: CFString, pathStyle: CFURLPathStyle, isDirectory: bool) -> CFURL {
71         unsafe {
72             let url_ref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath.as_concrete_TypeRef(), pathStyle, isDirectory as u8);
73             TCFType::wrap_under_create_rule(url_ref)
74         }
75     }
76 
77     #[cfg(unix)]
to_path(&self) -> Option<PathBuf>78     pub fn to_path(&self) -> Option<PathBuf> {
79         // implementing this on Windows is more complicated because of the different OsStr representation
80         unsafe {
81             let mut buf: [u8; PATH_MAX as usize] = mem::uninitialized();
82             let result = CFURLGetFileSystemRepresentation(self.0, true as Boolean, buf.as_mut_ptr(), buf.len() as CFIndex);
83             if result == false as Boolean {
84                 return None;
85             }
86             let len = strlen(buf.as_ptr() as *const i8);
87             let path = OsStr::from_bytes(&buf[0..len]);
88             Some(PathBuf::from(path))
89         }
90     }
91 
get_string(&self) -> CFString92     pub fn get_string(&self) -> CFString {
93         unsafe {
94             TCFType::wrap_under_get_rule(CFURLGetString(self.0))
95         }
96     }
97 
get_file_system_path(&self, pathStyle: CFURLPathStyle) -> CFString98     pub fn get_file_system_path(&self, pathStyle: CFURLPathStyle) -> CFString {
99         unsafe {
100             TCFType::wrap_under_create_rule(CFURLCopyFileSystemPath(self.as_concrete_TypeRef(), pathStyle))
101         }
102     }
103 
absolute(&self) -> CFURL104     pub fn absolute(&self) -> CFURL {
105         unsafe {
106             TCFType::wrap_under_create_rule(CFURLCopyAbsoluteURL(self.as_concrete_TypeRef()))
107         }
108     }
109 }
110 
111 #[test]
file_url_from_path()112 fn file_url_from_path() {
113     let path = "/usr/local/foo/";
114     let cfstr_path = CFString::from_static_string(path);
115     let cfurl = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true);
116     assert_eq!(cfurl.get_string().to_string(), "file:///usr/local/foo/");
117 }
118 
119 #[cfg(unix)]
120 #[test]
non_utf8()121 fn non_utf8() {
122     use std::ffi::OsStr;
123     let path = Path::new(OsStr::from_bytes(b"/\xC0/blame"));
124     let cfurl = CFURL::from_path(path, false).unwrap();
125     assert_eq!(cfurl.to_path().unwrap(), path);
126     let len = unsafe { CFURLGetBytes(cfurl.as_concrete_TypeRef(), ptr::null_mut(), 0) };
127     assert_eq!(len, 17);
128 }
129 
130 #[test]
absolute_file_url()131 fn absolute_file_url() {
132     use core_foundation_sys::url::CFURLCreateWithFileSystemPathRelativeToBase;
133     use std::path::PathBuf;
134 
135     let path = "/usr/local/foo";
136     let file = "bar";
137 
138     let cfstr_path = CFString::from_static_string(path);
139     let cfstr_file = CFString::from_static_string(file);
140     let cfurl_base = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true);
141     let cfurl_relative: CFURL = unsafe {
142         let url_ref = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorDefault,
143             cfstr_file.as_concrete_TypeRef(),
144             kCFURLPOSIXPathStyle,
145             false as u8,
146             cfurl_base.as_concrete_TypeRef());
147         TCFType::wrap_under_create_rule(url_ref)
148     };
149 
150     let mut absolute_path = PathBuf::from(path);
151     absolute_path.push(file);
152 
153     assert_eq!(cfurl_relative.get_file_system_path(kCFURLPOSIXPathStyle).to_string(), file);
154     assert_eq!(cfurl_relative.absolute().get_file_system_path(kCFURLPOSIXPathStyle).to_string(),
155         absolute_path.to_str().unwrap());
156 }
157