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