Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/. */
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>
26 PRLogModuleInfo *MCD;
28 extern nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length,
29 const char *filename,
30 bool bGlobalContext,
31 bool bCallbacks,
32 bool skipFirstLine);
34 // nsISupports Implementation
36 NS_IMPL_ISUPPORTS(nsAutoConfig, nsIAutoConfig, nsITimerCallback, nsIStreamListener, nsIObserver, nsIRequestObserver, nsISupportsWeakReference)
38 nsAutoConfig::nsAutoConfig()
39 {
40 }
42 nsresult nsAutoConfig::Init()
43 {
44 // member initializers and constructor code
46 nsresult rv;
47 mLoaded = false;
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;
55 rv = observerService->AddObserver(this,"profile-after-change", true);
57 return rv;
58 }
60 nsAutoConfig::~nsAutoConfig()
61 {
62 }
64 // attribute string configURL
65 NS_IMETHODIMP nsAutoConfig::GetConfigURL(char **aConfigURL)
66 {
67 if (!aConfigURL)
68 return NS_ERROR_NULL_POINTER;
70 if (mConfigURL.IsEmpty()) {
71 *aConfigURL = nullptr;
72 return NS_OK;
73 }
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 }
88 NS_IMETHODIMP
89 nsAutoConfig::OnStartRequest(nsIRequest *request, nsISupports *context)
90 {
91 return NS_OK;
92 }
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];
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 }
118 NS_IMETHODIMP
119 nsAutoConfig::OnStopRequest(nsIRequest *request, nsISupports *context,
120 nsresult aStatus)
121 {
122 nsresult rv;
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 }
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 }
142 // Send the autoconfig.jsc to javascript engine.
144 rv = EvaluateAdminConfigScript(mBuf.get(), mBuf.Length(),
145 nullptr, false,true, false);
146 if (NS_SUCCEEDED(rv)) {
148 // Write the autoconfig.jsc to failover.jsc (cached copy)
149 rv = writeFailoverFile();
151 if (NS_FAILED(rv))
152 NS_WARNING("Error writing failover.jsc file");
154 // Releasing the lock to allow the main thread to start execution
155 mLoaded = true;
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 }
164 // Notify method as a TimerCallBack function
165 NS_IMETHODIMP nsAutoConfig::Notify(nsITimer *timer)
166 {
167 downloadAutoConfig();
168 return NS_OK;
169 }
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 */
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")) {
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.
187 rv = downloadAutoConfig();
189 }
191 return rv;
192 }
194 nsresult nsAutoConfig::downloadAutoConfig()
195 {
196 nsresult rv;
197 nsAutoCString emailAddr;
198 nsXPIDLCString urlName;
199 static bool firstTime = true;
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 }
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);
215 // Clean up the previous read, the new read is going to use the same buffer
216 if (!mBuf.IsEmpty())
217 mBuf.Truncate(0);
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;
226 rv = prefs->GetBranch(nullptr,getter_AddRefs(mPrefBranch));
227 if (NS_FAILED(rv))
228 return rv;
229 }
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;
236 bool offline;
237 rv = ios->GetOffline(&offline);
238 if (NS_FAILED(rv))
239 return rv;
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 }
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 }
269 // create a new url
270 nsCOMPtr<nsIURI> url;
271 nsCOMPtr<nsIChannel> channel;
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 }
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;
286 rv = channel->AsyncOpen(this, nullptr);
287 if (NS_FAILED(rv)) {
288 readOfflineFile();
289 return rv;
290 }
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;
299 // Getting the current thread. If we start an AsyncOpen, the thread
300 // needs to wait before the reading of autoconfig is done
302 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
303 NS_ENSURE_STATE(thread);
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 */
313 while (!mLoaded)
314 NS_ENSURE_STATE(NS_ProcessNextEvent(thread));
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
332 return NS_OK;
333 } // nsPref::downloadAutoConfig()
337 nsresult nsAutoConfig::readOfflineFile()
338 {
339 nsresult rv;
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;
347 bool failCache;
348 rv = mPrefBranch->GetBoolPref("autoadmin.failover_to_cached", &failCache);
349 if (NS_SUCCEEDED(rv) && !failCache) {
350 // disable network connections and return.
352 nsCOMPtr<nsIIOService> ios =
353 do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
354 if (NS_FAILED(rv))
355 return rv;
357 bool offline;
358 rv = ios->GetOffline(&offline);
359 if (NS_FAILED(rv))
360 return rv;
362 if (!offline) {
363 rv = ios->SetOffline(true);
364 if (NS_FAILED(rv))
365 return rv;
366 }
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;
374 mPrefBranch->LockPref("network.online");
375 return NS_OK;
376 }
378 /* faiover_to_cached is set to true so
379 Open the file and read the content.
380 execute the javascript file
381 */
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;
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 }
396 nsresult nsAutoConfig::evaluateLocalFile(nsIFile *file)
397 {
398 nsresult rv;
399 nsCOMPtr<nsIInputStream> inStr;
401 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), file);
402 if (NS_FAILED(rv))
403 return rv;
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;
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 }
423 nsresult nsAutoConfig::writeFailoverFile()
424 {
425 nsresult rv;
426 nsCOMPtr<nsIFile> failoverFile;
427 nsCOMPtr<nsIOutputStream> outStr;
428 uint32_t amt;
430 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
431 getter_AddRefs(failoverFile));
432 if (NS_FAILED(rv))
433 return rv;
435 failoverFile->AppendNative(NS_LITERAL_CSTRING("failover.jsc"));
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 }
445 nsresult nsAutoConfig::getEmailAddr(nsACString & emailAddr)
446 {
448 nsresult rv;
449 nsXPIDLCString prefValue;
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 */
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 }
488 return NS_OK;
489 }
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);
499 nsCOMPtr<nsIStringBundle> bundle;
500 rv = bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties",
501 getter_AddRefs(bundle));
502 NS_ENSURE_SUCCESS(rv, rv);
504 nsXPIDLString title;
505 rv = bundle->GetStringFromName(MOZ_UTF16("emailPromptTitle"), getter_Copies(title));
506 NS_ENSURE_SUCCESS(rv, rv);
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 }