|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifdef MOZ_LOGGING |
|
7 // sorry, this has to be before the pre-compiled header |
|
8 #define FORCE_PR_LOG /* Allow logging in the release build */ |
|
9 #endif |
|
10 #include "nsAutoConfig.h" |
|
11 #include "nsIURI.h" |
|
12 #include "nsIHttpChannel.h" |
|
13 #include "nsIFileStreams.h" |
|
14 #include "nsThreadUtils.h" |
|
15 #include "nsAppDirectoryServiceDefs.h" |
|
16 #include "prmem.h" |
|
17 #include "nsIObserverService.h" |
|
18 #include "nsLiteralString.h" |
|
19 #include "nsIPromptService.h" |
|
20 #include "nsIServiceManager.h" |
|
21 #include "nsIStringBundle.h" |
|
22 #include "nsCRT.h" |
|
23 #include "nspr.h" |
|
24 #include <algorithm> |
|
25 |
|
26 PRLogModuleInfo *MCD; |
|
27 |
|
28 extern nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length, |
|
29 const char *filename, |
|
30 bool bGlobalContext, |
|
31 bool bCallbacks, |
|
32 bool skipFirstLine); |
|
33 |
|
34 // nsISupports Implementation |
|
35 |
|
36 NS_IMPL_ISUPPORTS(nsAutoConfig, nsIAutoConfig, nsITimerCallback, nsIStreamListener, nsIObserver, nsIRequestObserver, nsISupportsWeakReference) |
|
37 |
|
38 nsAutoConfig::nsAutoConfig() |
|
39 { |
|
40 } |
|
41 |
|
42 nsresult nsAutoConfig::Init() |
|
43 { |
|
44 // member initializers and constructor code |
|
45 |
|
46 nsresult rv; |
|
47 mLoaded = false; |
|
48 |
|
49 // Registering the object as an observer to the profile-after-change topic |
|
50 nsCOMPtr<nsIObserverService> observerService = |
|
51 do_GetService("@mozilla.org/observer-service;1", &rv); |
|
52 if (NS_FAILED(rv)) |
|
53 return rv; |
|
54 |
|
55 rv = observerService->AddObserver(this,"profile-after-change", true); |
|
56 |
|
57 return rv; |
|
58 } |
|
59 |
|
60 nsAutoConfig::~nsAutoConfig() |
|
61 { |
|
62 } |
|
63 |
|
64 // attribute string configURL |
|
65 NS_IMETHODIMP nsAutoConfig::GetConfigURL(char **aConfigURL) |
|
66 { |
|
67 if (!aConfigURL) |
|
68 return NS_ERROR_NULL_POINTER; |
|
69 |
|
70 if (mConfigURL.IsEmpty()) { |
|
71 *aConfigURL = nullptr; |
|
72 return NS_OK; |
|
73 } |
|
74 |
|
75 *aConfigURL = ToNewCString(mConfigURL); |
|
76 if (!*aConfigURL) |
|
77 return NS_ERROR_OUT_OF_MEMORY; |
|
78 return NS_OK; |
|
79 } |
|
80 NS_IMETHODIMP nsAutoConfig::SetConfigURL(const char *aConfigURL) |
|
81 { |
|
82 if (!aConfigURL) |
|
83 return NS_ERROR_NULL_POINTER; |
|
84 mConfigURL.Assign(aConfigURL); |
|
85 return NS_OK; |
|
86 } |
|
87 |
|
88 NS_IMETHODIMP |
|
89 nsAutoConfig::OnStartRequest(nsIRequest *request, nsISupports *context) |
|
90 { |
|
91 return NS_OK; |
|
92 } |
|
93 |
|
94 |
|
95 NS_IMETHODIMP |
|
96 nsAutoConfig::OnDataAvailable(nsIRequest *request, |
|
97 nsISupports *context, |
|
98 nsIInputStream *aIStream, |
|
99 uint64_t aSourceOffset, |
|
100 uint32_t aLength) |
|
101 { |
|
102 uint32_t amt, size; |
|
103 nsresult rv; |
|
104 char buf[1024]; |
|
105 |
|
106 while (aLength) { |
|
107 size = std::min<size_t>(aLength, sizeof(buf)); |
|
108 rv = aIStream->Read(buf, size, &amt); |
|
109 if (NS_FAILED(rv)) |
|
110 return rv; |
|
111 mBuf.Append(buf, amt); |
|
112 aLength -= amt; |
|
113 } |
|
114 return NS_OK; |
|
115 } |
|
116 |
|
117 |
|
118 NS_IMETHODIMP |
|
119 nsAutoConfig::OnStopRequest(nsIRequest *request, nsISupports *context, |
|
120 nsresult aStatus) |
|
121 { |
|
122 nsresult rv; |
|
123 |
|
124 // If the request is failed, go read the failover.jsc file |
|
125 if (NS_FAILED(aStatus)) { |
|
126 PR_LOG(MCD, PR_LOG_DEBUG, ("mcd request failed with status %x\n", aStatus)); |
|
127 return readOfflineFile(); |
|
128 } |
|
129 |
|
130 // Checking for the http response, if failure go read the failover file. |
|
131 nsCOMPtr<nsIHttpChannel> pHTTPCon(do_QueryInterface(request)); |
|
132 if (pHTTPCon) { |
|
133 uint32_t httpStatus; |
|
134 pHTTPCon->GetResponseStatus(&httpStatus); |
|
135 if (httpStatus != 200) |
|
136 { |
|
137 PR_LOG(MCD, PR_LOG_DEBUG, ("mcd http request failed with status %x\n", httpStatus)); |
|
138 return readOfflineFile(); |
|
139 } |
|
140 } |
|
141 |
|
142 // Send the autoconfig.jsc to javascript engine. |
|
143 |
|
144 rv = EvaluateAdminConfigScript(mBuf.get(), mBuf.Length(), |
|
145 nullptr, false,true, false); |
|
146 if (NS_SUCCEEDED(rv)) { |
|
147 |
|
148 // Write the autoconfig.jsc to failover.jsc (cached copy) |
|
149 rv = writeFailoverFile(); |
|
150 |
|
151 if (NS_FAILED(rv)) |
|
152 NS_WARNING("Error writing failover.jsc file"); |
|
153 |
|
154 // Releasing the lock to allow the main thread to start execution |
|
155 mLoaded = true; |
|
156 |
|
157 return NS_OK; |
|
158 } |
|
159 // there is an error in parsing of the autoconfig file. |
|
160 NS_WARNING("Error reading autoconfig.jsc from the network, reading the offline version"); |
|
161 return readOfflineFile(); |
|
162 } |
|
163 |
|
164 // Notify method as a TimerCallBack function |
|
165 NS_IMETHODIMP nsAutoConfig::Notify(nsITimer *timer) |
|
166 { |
|
167 downloadAutoConfig(); |
|
168 return NS_OK; |
|
169 } |
|
170 |
|
171 /* Observe() is called twice: once at the instantiation time and other |
|
172 after the profile is set. It doesn't do anything but return NS_OK during the |
|
173 creation time. Second time it calls downloadAutoConfig(). |
|
174 */ |
|
175 |
|
176 NS_IMETHODIMP nsAutoConfig::Observe(nsISupports *aSubject, |
|
177 const char *aTopic, |
|
178 const char16_t *someData) |
|
179 { |
|
180 nsresult rv = NS_OK; |
|
181 if (!nsCRT::strcmp(aTopic, "profile-after-change")) { |
|
182 |
|
183 // We will be calling downloadAutoConfig even if there is no profile |
|
184 // name. Nothing will be passed as a parameter to the URL and the |
|
185 // default case will be picked up by the script. |
|
186 |
|
187 rv = downloadAutoConfig(); |
|
188 |
|
189 } |
|
190 |
|
191 return rv; |
|
192 } |
|
193 |
|
194 nsresult nsAutoConfig::downloadAutoConfig() |
|
195 { |
|
196 nsresult rv; |
|
197 nsAutoCString emailAddr; |
|
198 nsXPIDLCString urlName; |
|
199 static bool firstTime = true; |
|
200 |
|
201 if (mConfigURL.IsEmpty()) { |
|
202 PR_LOG(MCD, PR_LOG_DEBUG, ("global config url is empty - did you set autoadmin.global_config_url?\n")); |
|
203 NS_WARNING("AutoConfig called without global_config_url"); |
|
204 return NS_OK; |
|
205 } |
|
206 |
|
207 // If there is an email address appended as an argument to the ConfigURL |
|
208 // in the previous read, we need to remove it when timer kicks in and |
|
209 // downloads the autoconfig file again. |
|
210 // If necessary, the email address will be added again as an argument. |
|
211 int32_t index = mConfigURL.RFindChar((char16_t)'?'); |
|
212 if (index != -1) |
|
213 mConfigURL.Truncate(index); |
|
214 |
|
215 // Clean up the previous read, the new read is going to use the same buffer |
|
216 if (!mBuf.IsEmpty()) |
|
217 mBuf.Truncate(0); |
|
218 |
|
219 // Get the preferences branch and save it to the member variable |
|
220 if (!mPrefBranch) { |
|
221 nsCOMPtr<nsIPrefService> prefs = |
|
222 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); |
|
223 if (NS_FAILED(rv)) |
|
224 return rv; |
|
225 |
|
226 rv = prefs->GetBranch(nullptr,getter_AddRefs(mPrefBranch)); |
|
227 if (NS_FAILED(rv)) |
|
228 return rv; |
|
229 } |
|
230 |
|
231 // Check to see if the network is online/offline |
|
232 nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); |
|
233 if (NS_FAILED(rv)) |
|
234 return rv; |
|
235 |
|
236 bool offline; |
|
237 rv = ios->GetOffline(&offline); |
|
238 if (NS_FAILED(rv)) |
|
239 return rv; |
|
240 |
|
241 if (offline) { |
|
242 bool offlineFailover; |
|
243 rv = mPrefBranch->GetBoolPref("autoadmin.offline_failover", |
|
244 &offlineFailover); |
|
245 // Read the failover.jsc if the network is offline and the pref says so |
|
246 if (NS_SUCCEEDED(rv) && offlineFailover) |
|
247 return readOfflineFile(); |
|
248 } |
|
249 |
|
250 /* Append user's identity at the end of the URL if the pref says so. |
|
251 First we are checking for the user's email address but if it is not |
|
252 available in the case where the client is used without messenger, user's |
|
253 profile name will be used as an unique identifier |
|
254 */ |
|
255 bool appendMail; |
|
256 rv = mPrefBranch->GetBoolPref("autoadmin.append_emailaddr", &appendMail); |
|
257 if (NS_SUCCEEDED(rv) && appendMail) { |
|
258 rv = getEmailAddr(emailAddr); |
|
259 if (NS_SUCCEEDED(rv) && emailAddr.get()) { |
|
260 /* Adding the unique identifier at the end of autoconfig URL. |
|
261 In this case the autoconfig URL is a script and |
|
262 emailAddr as passed as an argument |
|
263 */ |
|
264 mConfigURL.Append("?"); |
|
265 mConfigURL.Append(emailAddr); |
|
266 } |
|
267 } |
|
268 |
|
269 // create a new url |
|
270 nsCOMPtr<nsIURI> url; |
|
271 nsCOMPtr<nsIChannel> channel; |
|
272 |
|
273 rv = NS_NewURI(getter_AddRefs(url), mConfigURL.get(), nullptr, nullptr); |
|
274 if (NS_FAILED(rv)) |
|
275 { |
|
276 PR_LOG(MCD, PR_LOG_DEBUG, ("failed to create URL - is autoadmin.global_config_url valid? - %s\n", mConfigURL.get())); |
|
277 return rv; |
|
278 } |
|
279 |
|
280 PR_LOG(MCD, PR_LOG_DEBUG, ("running MCD url %s\n", mConfigURL.get())); |
|
281 // open a channel for the url |
|
282 rv = NS_NewChannel(getter_AddRefs(channel),url, nullptr, nullptr, nullptr, nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE); |
|
283 if (NS_FAILED(rv)) |
|
284 return rv; |
|
285 |
|
286 rv = channel->AsyncOpen(this, nullptr); |
|
287 if (NS_FAILED(rv)) { |
|
288 readOfflineFile(); |
|
289 return rv; |
|
290 } |
|
291 |
|
292 // Set a repeating timer if the pref is set. |
|
293 // This is to be done only once. |
|
294 // Also We are having the event queue processing only for the startup |
|
295 // It is not needed with the repeating timer. |
|
296 if (firstTime) { |
|
297 firstTime = false; |
|
298 |
|
299 // Getting the current thread. If we start an AsyncOpen, the thread |
|
300 // needs to wait before the reading of autoconfig is done |
|
301 |
|
302 nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); |
|
303 NS_ENSURE_STATE(thread); |
|
304 |
|
305 /* process events until we're finished. AutoConfig.jsc reading needs |
|
306 to be finished before the browser starts loading up |
|
307 We are waiting for the mLoaded which will be set through |
|
308 onStopRequest or readOfflineFile methods |
|
309 There is a possibility of deadlock so we need to make sure |
|
310 that mLoaded will be set to true in any case (success/failure) |
|
311 */ |
|
312 |
|
313 while (!mLoaded) |
|
314 NS_ENSURE_STATE(NS_ProcessNextEvent(thread)); |
|
315 |
|
316 int32_t minutes; |
|
317 rv = mPrefBranch->GetIntPref("autoadmin.refresh_interval", |
|
318 &minutes); |
|
319 if (NS_SUCCEEDED(rv) && minutes > 0) { |
|
320 // Create a new timer and pass this nsAutoConfig |
|
321 // object as a timer callback. |
|
322 mTimer = do_CreateInstance("@mozilla.org/timer;1",&rv); |
|
323 if (NS_FAILED(rv)) |
|
324 return rv; |
|
325 rv = mTimer->InitWithCallback(this, minutes * 60 * 1000, |
|
326 nsITimer::TYPE_REPEATING_SLACK); |
|
327 if (NS_FAILED(rv)) |
|
328 return rv; |
|
329 } |
|
330 } //first_time |
|
331 |
|
332 return NS_OK; |
|
333 } // nsPref::downloadAutoConfig() |
|
334 |
|
335 |
|
336 |
|
337 nsresult nsAutoConfig::readOfflineFile() |
|
338 { |
|
339 nsresult rv; |
|
340 |
|
341 /* Releasing the lock to allow main thread to start |
|
342 execution. At this point we do not need to stall |
|
343 the thread since all network activities are done. |
|
344 */ |
|
345 mLoaded = true; |
|
346 |
|
347 bool failCache; |
|
348 rv = mPrefBranch->GetBoolPref("autoadmin.failover_to_cached", &failCache); |
|
349 if (NS_SUCCEEDED(rv) && !failCache) { |
|
350 // disable network connections and return. |
|
351 |
|
352 nsCOMPtr<nsIIOService> ios = |
|
353 do_GetService(NS_IOSERVICE_CONTRACTID, &rv); |
|
354 if (NS_FAILED(rv)) |
|
355 return rv; |
|
356 |
|
357 bool offline; |
|
358 rv = ios->GetOffline(&offline); |
|
359 if (NS_FAILED(rv)) |
|
360 return rv; |
|
361 |
|
362 if (!offline) { |
|
363 rv = ios->SetOffline(true); |
|
364 if (NS_FAILED(rv)) |
|
365 return rv; |
|
366 } |
|
367 |
|
368 // lock the "network.online" prference so user cannot toggle back to |
|
369 // online mode. |
|
370 rv = mPrefBranch->SetBoolPref("network.online", false); |
|
371 if (NS_FAILED(rv)) |
|
372 return rv; |
|
373 |
|
374 mPrefBranch->LockPref("network.online"); |
|
375 return NS_OK; |
|
376 } |
|
377 |
|
378 /* faiover_to_cached is set to true so |
|
379 Open the file and read the content. |
|
380 execute the javascript file |
|
381 */ |
|
382 |
|
383 nsCOMPtr<nsIFile> failoverFile; |
|
384 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, |
|
385 getter_AddRefs(failoverFile)); |
|
386 if (NS_FAILED(rv)) |
|
387 return rv; |
|
388 |
|
389 failoverFile->AppendNative(NS_LITERAL_CSTRING("failover.jsc")); |
|
390 rv = evaluateLocalFile(failoverFile); |
|
391 if (NS_FAILED(rv)) |
|
392 NS_WARNING("Couldn't open failover.jsc, going back to default prefs"); |
|
393 return NS_OK; |
|
394 } |
|
395 |
|
396 nsresult nsAutoConfig::evaluateLocalFile(nsIFile *file) |
|
397 { |
|
398 nsresult rv; |
|
399 nsCOMPtr<nsIInputStream> inStr; |
|
400 |
|
401 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), file); |
|
402 if (NS_FAILED(rv)) |
|
403 return rv; |
|
404 |
|
405 int64_t fileSize; |
|
406 file->GetFileSize(&fileSize); |
|
407 uint32_t fs = fileSize; // Converting 64 bit structure to unsigned int |
|
408 char *buf = (char *)PR_Malloc(fs * sizeof(char)); |
|
409 if (!buf) |
|
410 return NS_ERROR_OUT_OF_MEMORY; |
|
411 |
|
412 uint32_t amt = 0; |
|
413 rv = inStr->Read(buf, fs, &amt); |
|
414 if (NS_SUCCEEDED(rv)) { |
|
415 EvaluateAdminConfigScript(buf, fs, nullptr, false, |
|
416 true, false); |
|
417 } |
|
418 inStr->Close(); |
|
419 PR_Free(buf); |
|
420 return rv; |
|
421 } |
|
422 |
|
423 nsresult nsAutoConfig::writeFailoverFile() |
|
424 { |
|
425 nsresult rv; |
|
426 nsCOMPtr<nsIFile> failoverFile; |
|
427 nsCOMPtr<nsIOutputStream> outStr; |
|
428 uint32_t amt; |
|
429 |
|
430 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, |
|
431 getter_AddRefs(failoverFile)); |
|
432 if (NS_FAILED(rv)) |
|
433 return rv; |
|
434 |
|
435 failoverFile->AppendNative(NS_LITERAL_CSTRING("failover.jsc")); |
|
436 |
|
437 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStr), failoverFile); |
|
438 if (NS_FAILED(rv)) |
|
439 return rv; |
|
440 rv = outStr->Write(mBuf.get(),mBuf.Length(),&amt); |
|
441 outStr->Close(); |
|
442 return rv; |
|
443 } |
|
444 |
|
445 nsresult nsAutoConfig::getEmailAddr(nsACString & emailAddr) |
|
446 { |
|
447 |
|
448 nsresult rv; |
|
449 nsXPIDLCString prefValue; |
|
450 |
|
451 /* Getting an email address through set of three preferences: |
|
452 First getting a default account with |
|
453 "mail.accountmanager.defaultaccount" |
|
454 second getting an associated id with the default account |
|
455 Third getting an email address with id |
|
456 */ |
|
457 |
|
458 rv = mPrefBranch->GetCharPref("mail.accountmanager.defaultaccount", |
|
459 getter_Copies(prefValue)); |
|
460 if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) { |
|
461 emailAddr = NS_LITERAL_CSTRING("mail.account.") + |
|
462 prefValue + NS_LITERAL_CSTRING(".identities"); |
|
463 rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(), |
|
464 getter_Copies(prefValue)); |
|
465 if (NS_FAILED(rv) || prefValue.IsEmpty()) |
|
466 return PromptForEMailAddress(emailAddr); |
|
467 int32_t commandIndex = prefValue.FindChar(','); |
|
468 if (commandIndex != kNotFound) |
|
469 prefValue.Truncate(commandIndex); |
|
470 emailAddr = NS_LITERAL_CSTRING("mail.identity.") + |
|
471 prefValue + NS_LITERAL_CSTRING(".useremail"); |
|
472 rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(), |
|
473 getter_Copies(prefValue)); |
|
474 if (NS_FAILED(rv) || prefValue.IsEmpty()) |
|
475 return PromptForEMailAddress(emailAddr); |
|
476 emailAddr = prefValue; |
|
477 } |
|
478 else { |
|
479 // look for 4.x pref in case we just migrated. |
|
480 rv = mPrefBranch->GetCharPref("mail.identity.useremail", |
|
481 getter_Copies(prefValue)); |
|
482 if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) |
|
483 emailAddr = prefValue; |
|
484 else |
|
485 PromptForEMailAddress(emailAddr); |
|
486 } |
|
487 |
|
488 return NS_OK; |
|
489 } |
|
490 |
|
491 nsresult nsAutoConfig::PromptForEMailAddress(nsACString &emailAddress) |
|
492 { |
|
493 nsresult rv; |
|
494 nsCOMPtr<nsIPromptService> promptService = do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv); |
|
495 NS_ENSURE_SUCCESS(rv, rv); |
|
496 nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); |
|
497 NS_ENSURE_SUCCESS(rv, rv); |
|
498 |
|
499 nsCOMPtr<nsIStringBundle> bundle; |
|
500 rv = bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties", |
|
501 getter_AddRefs(bundle)); |
|
502 NS_ENSURE_SUCCESS(rv, rv); |
|
503 |
|
504 nsXPIDLString title; |
|
505 rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptTitle"), getter_Copies(title)); |
|
506 NS_ENSURE_SUCCESS(rv, rv); |
|
507 |
|
508 nsXPIDLString err; |
|
509 rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptMsg"), getter_Copies(err)); |
|
510 NS_ENSURE_SUCCESS(rv, rv); |
|
511 bool check = false; |
|
512 nsXPIDLString emailResult; |
|
513 bool success; |
|
514 rv = promptService->Prompt(nullptr, title.get(), err.get(), getter_Copies(emailResult), nullptr, &check, &success); |
|
515 if (!success) |
|
516 return NS_ERROR_FAILURE; |
|
517 NS_ENSURE_SUCCESS(rv, rv); |
|
518 LossyCopyUTF16toASCII(emailResult, emailAddress); |
|
519 return NS_OK; |
|
520 } |