CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / extensions / pref / autoconfig / src / nsAutoConfig.cpp
blob2f68482d03321e479ada940e18a25814a3ca1bd7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Mitesh Shah <mitesh@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #ifdef MOZ_LOGGING
40 // sorry, this has to be before the pre-compiled header
41 #define FORCE_PR_LOG /* Allow logging in the release build */
42 #endif
43 #include "nsAutoConfig.h"
44 #include "nsIURI.h"
45 #include "nsIHttpChannel.h"
46 #include "nsIFileStreams.h"
47 #include "nsThreadUtils.h"
48 #include "nsAppDirectoryServiceDefs.h"
49 #include "prmem.h"
50 #include "nsIProfile.h"
51 #include "nsIObserverService.h"
52 #include "nsLiteralString.h"
53 #include "nsIPromptService.h"
54 #include "nsIServiceManager.h"
55 #include "nsIStringBundle.h"
56 #include "nsCRT.h"
57 #include "nspr.h"
59 PRLogModuleInfo *MCD;
61 extern nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length,
62 const char *filename,
63 PRBool bGlobalContext,
64 PRBool bCallbacks,
65 PRBool skipFirstLine);
67 // nsISupports Implementation
69 NS_IMPL_THREADSAFE_ISUPPORTS6(nsAutoConfig, nsIAutoConfig, nsITimerCallback, nsIStreamListener, nsIObserver, nsIRequestObserver, nsISupportsWeakReference)
71 nsAutoConfig::nsAutoConfig()
75 nsresult nsAutoConfig::Init()
77 // member initializers and constructor code
79 nsresult rv;
80 mLoaded = PR_FALSE;
82 // Registering the object as an observer to the profile-after-change topic
83 nsCOMPtr<nsIObserverService> observerService =
84 do_GetService("@mozilla.org/observer-service;1", &rv);
85 if (NS_FAILED(rv))
86 return rv;
88 rv = observerService->AddObserver(this,"profile-after-change", PR_TRUE);
90 return rv;
93 nsAutoConfig::~nsAutoConfig()
97 // attribute string configURL
98 NS_IMETHODIMP nsAutoConfig::GetConfigURL(char **aConfigURL)
100 if (!aConfigURL)
101 return NS_ERROR_NULL_POINTER;
103 if (mConfigURL.IsEmpty()) {
104 *aConfigURL = nsnull;
105 return NS_OK;
108 *aConfigURL = ToNewCString(mConfigURL);
109 if (!*aConfigURL)
110 return NS_ERROR_OUT_OF_MEMORY;
111 return NS_OK;
113 NS_IMETHODIMP nsAutoConfig::SetConfigURL(const char *aConfigURL)
115 if (!aConfigURL)
116 return NS_ERROR_NULL_POINTER;
117 mConfigURL.Assign(aConfigURL);
118 return NS_OK;
121 NS_IMETHODIMP
122 nsAutoConfig::OnStartRequest(nsIRequest *request, nsISupports *context)
124 return NS_OK;
128 NS_IMETHODIMP
129 nsAutoConfig::OnDataAvailable(nsIRequest *request,
130 nsISupports *context,
131 nsIInputStream *aIStream,
132 PRUint32 aSourceOffset,
133 PRUint32 aLength)
135 PRUint32 amt, size;
136 nsresult rv;
137 char buf[1024];
139 while (aLength) {
140 size = PR_MIN(aLength, sizeof(buf));
141 rv = aIStream->Read(buf, size, &amt);
142 if (NS_FAILED(rv))
143 return rv;
144 mBuf.Append(buf, amt);
145 aLength -= amt;
147 return NS_OK;
151 NS_IMETHODIMP
152 nsAutoConfig::OnStopRequest(nsIRequest *request, nsISupports *context,
153 nsresult aStatus)
155 nsresult rv;
157 // If the request is failed, go read the failover.jsc file
158 if (NS_FAILED(aStatus)) {
159 PR_LOG(MCD, PR_LOG_DEBUG, ("mcd request failed with status %x\n", aStatus));
160 return readOfflineFile();
163 // Checking for the http response, if failure go read the failover file.
164 nsCOMPtr<nsIHttpChannel> pHTTPCon(do_QueryInterface(request));
165 if (pHTTPCon) {
166 PRUint32 httpStatus;
167 pHTTPCon->GetResponseStatus(&httpStatus);
168 if (httpStatus != 200)
170 PR_LOG(MCD, PR_LOG_DEBUG, ("mcd http request failed with status %x\n", httpStatus));
171 return readOfflineFile();
175 // Send the autoconfig.jsc to javascript engine.
177 rv = EvaluateAdminConfigScript(mBuf.get(), mBuf.Length(),
178 nsnull, PR_FALSE,PR_TRUE, PR_FALSE);
179 if (NS_SUCCEEDED(rv)) {
181 // Write the autoconfig.jsc to failover.jsc (cached copy)
182 rv = writeFailoverFile();
184 if (NS_FAILED(rv))
185 NS_WARNING("Error writing failover.jsc file");
187 // Releasing the lock to allow the main thread to start execution
188 mLoaded = PR_TRUE;
190 return NS_OK;
192 // there is an error in parsing of the autoconfig file.
193 NS_WARNING("Error reading autoconfig.jsc from the network, reading the offline version");
194 return readOfflineFile();
197 // Notify method as a TimerCallBack function
198 NS_IMETHODIMP nsAutoConfig::Notify(nsITimer *timer)
200 downloadAutoConfig();
201 return NS_OK;
204 /* Observe() is called twice: once at the instantiation time and other
205 after the profile is set. It doesn't do anything but return NS_OK during the
206 creation time. Second time it calls downloadAutoConfig().
209 NS_IMETHODIMP nsAutoConfig::Observe(nsISupports *aSubject,
210 const char *aTopic,
211 const PRUnichar *someData)
213 nsresult rv = NS_OK;
214 if (!nsCRT::strcmp(aTopic, "profile-after-change")) {
216 // Getting the current profile name since we already have the
217 // pointer to the object.
218 nsCOMPtr<nsIProfile> profile = do_QueryInterface(aSubject);
219 if (profile) {
220 nsXPIDLString profileName;
221 rv = profile->GetCurrentProfile(getter_Copies(profileName));
222 if (NS_SUCCEEDED(rv)) {
223 // setting the member variable to the current profile name
224 CopyUTF16toUTF8(profileName, mCurrProfile);
226 else {
227 NS_WARNING("nsAutoConfig::GetCurrentProfile() failed");
231 // We will be calling downloadAutoConfig even if there is no profile
232 // name. Nothing will be passed as a parameter to the URL and the
233 // default case will be picked up by the script.
235 rv = downloadAutoConfig();
239 return rv;
242 nsresult nsAutoConfig::downloadAutoConfig()
244 nsresult rv;
245 nsCAutoString emailAddr;
246 nsXPIDLCString urlName;
247 PRBool appendMail = PR_FALSE, offline = PR_FALSE;
248 static PRBool firstTime = PR_TRUE;
250 if (mConfigURL.IsEmpty()) {
251 PR_LOG(MCD, PR_LOG_DEBUG, ("global config url is empty - did you set autoadmin.global_config_url?\n"));
252 NS_WARNING("AutoConfig called without global_config_url");
253 return NS_OK;
256 // If there is an email address appended as an argument to the ConfigURL
257 // in the previous read, we need to remove it when timer kicks in and
258 // downloads the autoconfig file again.
259 // If necessary, the email address will be added again as an argument.
260 PRInt32 index = mConfigURL.RFindChar((PRUnichar)'?');
261 if (index != -1)
262 mConfigURL.Truncate(index);
264 // Clean up the previous read, the new read is going to use the same buffer
265 if (!mBuf.IsEmpty())
266 mBuf.Truncate(0);
268 // Get the preferences branch and save it to the member variable
269 if (!mPrefBranch) {
271 nsCOMPtr<nsIPrefService> prefs =
272 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
273 if (NS_FAILED(rv))
274 return rv;
276 rv = prefs->GetBranch(nsnull,getter_AddRefs(mPrefBranch));
277 if (NS_FAILED(rv))
278 return rv;
281 // Check to see if the network is online/offline
282 nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
283 if (NS_FAILED(rv))
284 return rv;
286 rv = ios->GetOffline(&offline);
287 if (NS_FAILED(rv))
288 return rv;
290 if (offline) {
292 PRBool offlineFailover = PR_FALSE;
293 rv = mPrefBranch->GetBoolPref("autoadmin.offline_failover",
294 &offlineFailover);
296 // Read the failover.jsc if the network is offline and the pref says so
297 if (offlineFailover) {
298 return readOfflineFile();
303 /* Append user's identity at the end of the URL if the pref says so.
304 First we are checking for the user's email address but if it is not
305 available in the case where the client is used without messenger, user's
306 profile name will be used as an unique identifier
309 rv = mPrefBranch->GetBoolPref("autoadmin.append_emailaddr", &appendMail);
311 if (NS_SUCCEEDED(rv) && appendMail) {
312 rv = getEmailAddr(emailAddr);
313 if (NS_SUCCEEDED(rv) && emailAddr.get()) {
315 /* Adding the unique identifier at the end of autoconfig URL.
316 In this case the autoconfig URL is a script and
317 emailAddr as passed as an argument
319 mConfigURL.Append("?");
320 mConfigURL.Append(emailAddr);
324 // create a new url
325 nsCOMPtr<nsIURI> url;
326 nsCOMPtr<nsIChannel> channel;
328 rv = NS_NewURI(getter_AddRefs(url), mConfigURL.get(), nsnull, nsnull);
329 if (NS_FAILED(rv))
331 PR_LOG(MCD, PR_LOG_DEBUG, ("failed to create URL - is autoadmin.global_config_url valid? - %s\n", mConfigURL.get()));
332 return rv;
335 PR_LOG(MCD, PR_LOG_DEBUG, ("running MCD url %s\n", mConfigURL.get()));
336 // open a channel for the url
337 rv = NS_NewChannel(getter_AddRefs(channel),url, nsnull, nsnull, nsnull, nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE);
338 if (NS_FAILED(rv))
339 return rv;
342 rv = channel->AsyncOpen(this, nsnull);
343 if (NS_FAILED(rv)) {
344 readOfflineFile();
345 return rv;
348 // Set a repeating timer if the pref is set.
349 // This is to be done only once.
350 // Also We are having the event queue processing only for the startup
351 // It is not needed with the repeating timer.
352 if (firstTime) {
354 firstTime = PR_FALSE;
356 // Getting the current thread. If we start an AsyncOpen, the thread
357 // needs to wait before the reading of autoconfig is done
359 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
360 NS_ENSURE_STATE(thread);
362 /* process events until we're finished. AutoConfig.jsc reading needs
363 to be finished before the browser starts loading up
364 We are waiting for the mLoaded which will be set through
365 onStopRequest or readOfflineFile methods
366 There is a possibility of deadlock so we need to make sure
367 that mLoaded will be set to true in any case (success/failure)
370 while (!mLoaded)
371 NS_ENSURE_STATE(NS_ProcessNextEvent(thread));
373 PRInt32 minutes = 0;
374 rv = mPrefBranch->GetIntPref("autoadmin.refresh_interval",
375 &minutes);
376 if (NS_SUCCEEDED(rv) && minutes > 0) {
378 // Create a new timer and pass this nsAutoConfig
379 // object as a timer callback.
380 mTimer = do_CreateInstance("@mozilla.org/timer;1",&rv);
381 if (NS_FAILED(rv))
382 return rv;
383 rv = mTimer->InitWithCallback(this, minutes * 60 * 1000,
384 nsITimer::TYPE_REPEATING_SLACK);
385 if (NS_FAILED(rv))
386 return rv;
389 } //first_time
391 return NS_OK;
393 } // nsPref::downloadAutoConfig()
397 nsresult nsAutoConfig::readOfflineFile()
399 PRBool failCache = PR_TRUE;
400 nsresult rv;
401 PRBool offline;
403 /* Releasing the lock to allow main thread to start
404 execution. At this point we do not need to stall
405 the thread since all network activities are done.
407 mLoaded = PR_TRUE;
409 rv = mPrefBranch->GetBoolPref("autoadmin.failover_to_cached", &failCache);
411 if (failCache == PR_FALSE) {
413 // disable network connections and return.
415 nsCOMPtr<nsIIOService> ios =
416 do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
417 if (NS_FAILED(rv))
418 return rv;
420 rv = ios->GetOffline(&offline);
421 if (NS_FAILED(rv))
422 return rv;
424 if (!offline) {
425 rv = ios->SetOffline(PR_TRUE);
426 if (NS_FAILED(rv))
427 return rv;
430 // lock the "network.online" prference so user cannot toggle back to
431 // online mode.
432 rv = mPrefBranch->SetBoolPref("network.online", PR_FALSE);
433 if (NS_FAILED(rv))
434 return rv;
435 mPrefBranch->LockPref("network.online");
436 return NS_OK;
439 /* faiover_to_cached is set to true so
440 Open the file and read the content.
441 execute the javascript file
444 nsCOMPtr<nsIFile> failoverFile;
445 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
446 getter_AddRefs(failoverFile));
447 if (NS_FAILED(rv))
448 return rv;
450 failoverFile->AppendNative(NS_LITERAL_CSTRING("failover.jsc"));
451 rv = evaluateLocalFile(failoverFile);
452 if (NS_FAILED(rv))
453 NS_WARNING("Couldn't open failover.jsc, going back to default prefs");
454 return NS_OK;
457 nsresult nsAutoConfig::evaluateLocalFile(nsIFile *file)
459 nsresult rv;
460 nsCOMPtr<nsIInputStream> inStr;
462 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), file);
463 if (NS_FAILED(rv))
464 return rv;
466 PRInt64 fileSize;
467 PRUint32 fs, amt=0;
468 file->GetFileSize(&fileSize);
469 LL_L2UI(fs, fileSize); // Converting 64 bit structure to unsigned int
470 char *buf = (char *)PR_Malloc(fs * sizeof(char));
471 if (!buf)
472 return NS_ERROR_OUT_OF_MEMORY;
474 rv = inStr->Read(buf, fs, &amt);
475 if (NS_SUCCEEDED(rv)) {
476 EvaluateAdminConfigScript(buf, fs, nsnull, PR_FALSE,
477 PR_TRUE, PR_FALSE);
479 inStr->Close();
480 PR_Free(buf);
481 return rv;
484 nsresult nsAutoConfig::writeFailoverFile()
486 nsresult rv;
487 nsCOMPtr<nsIFile> failoverFile;
488 nsCOMPtr<nsIOutputStream> outStr;
489 PRUint32 amt;
491 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
492 getter_AddRefs(failoverFile));
493 if (NS_FAILED(rv))
494 return rv;
496 failoverFile->AppendNative(NS_LITERAL_CSTRING("failover.jsc"));
498 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStr), failoverFile);
499 if (NS_FAILED(rv))
500 return rv;
501 rv = outStr->Write(mBuf.get(),mBuf.Length(),&amt);
502 outStr->Close();
503 return rv;
506 nsresult nsAutoConfig::getEmailAddr(nsACString & emailAddr)
509 nsresult rv;
510 nsXPIDLCString prefValue;
512 /* Getting an email address through set of three preferences:
513 First getting a default account with
514 "mail.accountmanager.defaultaccount"
515 second getting an associated id with the default account
516 Third getting an email address with id
519 rv = mPrefBranch->GetCharPref("mail.accountmanager.defaultaccount",
520 getter_Copies(prefValue));
522 if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) {
523 emailAddr = NS_LITERAL_CSTRING("mail.account.") +
524 prefValue + NS_LITERAL_CSTRING(".identities");
525 rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
526 getter_Copies(prefValue));
527 if (NS_FAILED(rv) || prefValue.IsEmpty())
528 return PromptForEMailAddress(emailAddr);
529 PRInt32 commandIndex = prefValue.FindChar(',');
530 if (commandIndex != kNotFound)
531 prefValue.Truncate(commandIndex);
532 emailAddr = NS_LITERAL_CSTRING("mail.identity.") +
533 prefValue + NS_LITERAL_CSTRING(".useremail");
534 rv = mPrefBranch->GetCharPref(PromiseFlatCString(emailAddr).get(),
535 getter_Copies(prefValue));
536 if (NS_FAILED(rv) || prefValue.IsEmpty())
537 return PromptForEMailAddress(emailAddr);
538 emailAddr = prefValue;
540 else {
541 // look for 4.x pref in case we just migrated.
542 rv = mPrefBranch->GetCharPref("mail.identity.useremail",
543 getter_Copies(prefValue));
544 if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty())
545 emailAddr = prefValue;
546 else if (NS_FAILED(PromptForEMailAddress(emailAddr)) && (!mCurrProfile.IsEmpty()))
547 emailAddr = mCurrProfile;
550 return NS_OK;
553 nsresult nsAutoConfig::PromptForEMailAddress(nsACString &emailAddress)
555 nsresult rv;
556 nsCOMPtr<nsIPromptService> promptService = do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
557 NS_ENSURE_SUCCESS(rv, rv);
558 nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
559 NS_ENSURE_SUCCESS(rv, rv);
561 nsCOMPtr<nsIStringBundle> bundle;
562 rv = bundleService->CreateBundle("chrome://autoconfig/locale/autoconfig.properties",
563 getter_AddRefs(bundle));
564 NS_ENSURE_SUCCESS(rv, rv);
566 nsXPIDLString title;
567 rv = bundle->GetStringFromName(NS_LITERAL_STRING("emailPromptTitle").get(), getter_Copies(title));
568 NS_ENSURE_SUCCESS(rv, rv);
570 nsXPIDLString err;
571 rv = bundle->GetStringFromName(NS_LITERAL_STRING("emailPromptMsg").get(), getter_Copies(err));
572 NS_ENSURE_SUCCESS(rv, rv);
573 PRBool check = PR_FALSE;
574 nsXPIDLString emailResult;
575 PRBool success;
576 rv = promptService->Prompt(nsnull, title.get(), err.get(), getter_Copies(emailResult), nsnull, &check, &success);
577 if (!success)
578 return NS_ERROR_FAILURE;
579 NS_ENSURE_SUCCESS(rv, rv);
580 LossyCopyUTF16toASCII(emailResult, emailAddress);
581 return NS_OK;