Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "DownloadPlatform.h"
6 : #include "nsAutoPtr.h"
7 : #include "nsNetUtil.h"
8 : #include "nsString.h"
9 : #include "nsINestedURI.h"
10 : #include "nsIProtocolHandler.h"
11 : #include "nsIURI.h"
12 : #include "nsIFile.h"
13 : #include "nsIObserverService.h"
14 : #include "nsISupportsPrimitives.h"
15 : #include "nsDirectoryServiceDefs.h"
16 :
17 : #include "mozilla/Preferences.h"
18 : #include "mozilla/Services.h"
19 :
20 : #define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
21 :
22 : #ifdef XP_WIN
23 : #include <shlobj.h>
24 : #include <urlmon.h>
25 : #include "nsILocalFileWin.h"
26 : #endif
27 :
28 : #ifdef XP_MACOSX
29 : #include <CoreFoundation/CoreFoundation.h>
30 : #include "../../../../xpcom/io/CocoaFileUtils.h"
31 : #endif
32 :
33 : #ifdef MOZ_WIDGET_ANDROID
34 : #include "FennecJNIWrappers.h"
35 : #endif
36 :
37 : #ifdef MOZ_WIDGET_GTK
38 : #include <gtk/gtk.h>
39 : #endif
40 :
41 : using namespace mozilla;
42 :
43 : DownloadPlatform *DownloadPlatform::gDownloadPlatformService = nullptr;
44 :
45 0 : NS_IMPL_ISUPPORTS(DownloadPlatform, mozIDownloadPlatform);
46 :
47 0 : DownloadPlatform* DownloadPlatform::GetDownloadPlatform()
48 : {
49 0 : if (!gDownloadPlatformService) {
50 0 : gDownloadPlatformService = new DownloadPlatform();
51 : }
52 :
53 0 : NS_ADDREF(gDownloadPlatformService);
54 :
55 : #if defined(MOZ_WIDGET_GTK)
56 0 : g_type_init();
57 : #endif
58 :
59 0 : return gDownloadPlatformService;
60 : }
61 :
62 : #ifdef MOZ_WIDGET_GTK
63 0 : static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data)
64 : {
65 0 : GError *err = nullptr;
66 0 : g_file_set_attributes_finish(G_FILE(source_obj), res, nullptr, &err);
67 0 : if (err) {
68 : #ifdef DEBUG
69 0 : NS_DebugBreak(NS_DEBUG_WARNING, "Set file metadata failed: ", err->message, __FILE__, __LINE__);
70 : #endif
71 0 : g_error_free(err);
72 : }
73 0 : }
74 : #endif
75 :
76 : #ifdef XP_MACOSX
77 : // Caller is responsible for freeing any result (CF Create Rule)
78 : CFURLRef CreateCFURLFromNSIURI(nsIURI *aURI) {
79 : nsAutoCString spec;
80 : if (aURI) {
81 : aURI->GetSpec(spec);
82 : }
83 :
84 : CFStringRef urlStr = ::CFStringCreateWithCString(kCFAllocatorDefault,
85 : spec.get(),
86 : kCFStringEncodingUTF8);
87 : if (!urlStr) {
88 : return NULL;
89 : }
90 :
91 : CFURLRef url = ::CFURLCreateWithString(kCFAllocatorDefault,
92 : urlStr,
93 : NULL);
94 :
95 : ::CFRelease(urlStr);
96 :
97 : return url;
98 : }
99 : #endif
100 :
101 0 : nsresult DownloadPlatform::DownloadDone(nsIURI* aSource, nsIURI* aReferrer, nsIFile* aTarget,
102 : const nsACString& aContentType, bool aIsPrivate)
103 : {
104 : #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) \
105 : || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GONK)
106 :
107 0 : nsAutoString path;
108 0 : if (aTarget && NS_SUCCEEDED(aTarget->GetPath(path))) {
109 : #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
110 : // On Windows and Gtk, add the download to the system's "recent documents"
111 : // list, with a pref to disable.
112 : {
113 0 : bool addToRecentDocs = Preferences::GetBool(PREF_BDM_ADDTORECENTDOCS);
114 : #ifdef MOZ_WIDGET_ANDROID
115 : if (jni::IsFennec() && addToRecentDocs) {
116 : java::DownloadsIntegration::ScanMedia(path, aContentType);
117 : }
118 : #else
119 0 : if (addToRecentDocs && !aIsPrivate) {
120 : #ifdef XP_WIN
121 : ::SHAddToRecentDocs(SHARD_PATHW, path.get());
122 : #elif defined(MOZ_WIDGET_GTK)
123 0 : GtkRecentManager* manager = gtk_recent_manager_get_default();
124 :
125 0 : gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
126 0 : nullptr, nullptr);
127 0 : if (uri) {
128 0 : gtk_recent_manager_add_item(manager, uri);
129 0 : g_free(uri);
130 : }
131 : #endif
132 : }
133 : #endif
134 : #ifdef MOZ_WIDGET_GTK
135 : // Use GIO to store the source URI for later display in the file manager.
136 0 : GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get());
137 0 : nsCString source_uri;
138 0 : nsresult rv = aSource->GetSpec(source_uri);
139 0 : NS_ENSURE_SUCCESS(rv, rv);
140 0 : GFileInfo *file_info = g_file_info_new();
141 0 : g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get());
142 : g_file_set_attributes_async(gio_file,
143 : file_info,
144 : G_FILE_QUERY_INFO_NONE,
145 : G_PRIORITY_DEFAULT,
146 0 : nullptr, gio_set_metadata_done, nullptr);
147 0 : g_object_unref(file_info);
148 0 : g_object_unref(gio_file);
149 : #endif
150 : }
151 : #endif
152 :
153 : #ifdef XP_MACOSX
154 : // On OS X, make the downloads stack bounce.
155 : CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
156 : NS_ConvertUTF16toUTF8(path).get(),
157 : kCFStringEncodingUTF8);
158 : CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
159 : ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
160 : observedObject, nullptr, TRUE);
161 : ::CFRelease(observedObject);
162 :
163 : // Add OS X origin and referrer file metadata
164 : CFStringRef pathCFStr = NULL;
165 : if (!path.IsEmpty()) {
166 : pathCFStr = ::CFStringCreateWithCharacters(kCFAllocatorDefault,
167 : (const UniChar*)path.get(),
168 : path.Length());
169 : }
170 : if (pathCFStr) {
171 : bool isFromWeb = IsURLPossiblyFromWeb(aSource);
172 :
173 : CFURLRef sourceCFURL = CreateCFURLFromNSIURI(aSource);
174 : CFURLRef referrerCFURL = CreateCFURLFromNSIURI(aReferrer);
175 :
176 : CocoaFileUtils::AddOriginMetadataToFile(pathCFStr,
177 : sourceCFURL,
178 : referrerCFURL);
179 : CocoaFileUtils::AddQuarantineMetadataToFile(pathCFStr,
180 : sourceCFURL,
181 : referrerCFURL,
182 : isFromWeb);
183 :
184 : ::CFRelease(pathCFStr);
185 : if (sourceCFURL) {
186 : ::CFRelease(sourceCFURL);
187 : }
188 : if (referrerCFURL) {
189 : ::CFRelease(referrerCFURL);
190 : }
191 : }
192 : #endif
193 : }
194 :
195 : #endif
196 :
197 0 : return NS_OK;
198 : }
199 :
200 0 : nsresult DownloadPlatform::MapUrlToZone(const nsAString& aURL,
201 : uint32_t* aZone)
202 : {
203 : #ifdef XP_WIN
204 : RefPtr<IInternetSecurityManager> inetSecMgr;
205 : if (FAILED(CoCreateInstance(CLSID_InternetSecurityManager, NULL,
206 : CLSCTX_ALL, IID_IInternetSecurityManager,
207 : getter_AddRefs(inetSecMgr)))) {
208 : return NS_ERROR_UNEXPECTED;
209 : }
210 :
211 : DWORD zone;
212 : if (inetSecMgr->MapUrlToZone(PromiseFlatString(aURL).get(),
213 : &zone, 0) != S_OK) {
214 : return NS_ERROR_UNEXPECTED;
215 : } else {
216 : *aZone = zone;
217 : }
218 :
219 : return NS_OK;
220 : #else
221 0 : return NS_ERROR_NOT_IMPLEMENTED;
222 : #endif
223 : }
224 :
225 : // Check if a URI is likely to be web-based, by checking its URI flags.
226 : // If in doubt (e.g. if anything fails during the check) claims things
227 : // are from the web.
228 0 : bool DownloadPlatform::IsURLPossiblyFromWeb(nsIURI* aURI)
229 : {
230 0 : nsCOMPtr<nsIIOService> ios = do_GetIOService();
231 0 : nsCOMPtr<nsIURI> uri = aURI;
232 0 : if (!ios) {
233 0 : return true;
234 : }
235 :
236 0 : while (uri) {
237 : // We're not using nsIIOService::ProtocolHasFlags because it doesn't
238 : // take per-URI flags into account. We're also not using
239 : // NS_URIChainHasFlags because we're checking for *any* of 3 flags
240 : // to be present on *all* of the nested URIs, which it can't do.
241 0 : nsAutoCString scheme;
242 0 : nsresult rv = uri->GetScheme(scheme);
243 0 : if (NS_FAILED(rv)) {
244 0 : return true;
245 : }
246 0 : nsCOMPtr<nsIProtocolHandler> ph;
247 0 : rv = ios->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
248 0 : if (NS_FAILED(rv)) {
249 0 : return true;
250 : }
251 : uint32_t flags;
252 0 : rv = ph->DoGetProtocolFlags(uri, &flags);
253 0 : if (NS_FAILED(rv)) {
254 0 : return true;
255 : }
256 : // If not dangerous to load, not a UI resource and not a local file,
257 : // assume this is from the web:
258 0 : if (!(flags & nsIProtocolHandler::URI_DANGEROUS_TO_LOAD) &&
259 0 : !(flags & nsIProtocolHandler::URI_IS_UI_RESOURCE) &&
260 0 : !(flags & nsIProtocolHandler::URI_IS_LOCAL_FILE)) {
261 0 : return true;
262 : }
263 : // Otherwise, check if the URI is nested, and if so go through
264 : // the loop again:
265 0 : nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
266 0 : uri = nullptr;
267 0 : if (nestedURI) {
268 0 : rv = nestedURI->GetInnerURI(getter_AddRefs(uri));
269 0 : if (NS_FAILED(rv)) {
270 0 : return true;
271 : }
272 : }
273 : }
274 0 : return false;
275 : }
|