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
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.
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 ***** */
40 // sorry, this has to be before the pre-compiled header
41 #define FORCE_PR_LOG /* Allow logging in the release build */
43 #include "nsAutoConfig.h"
45 #include "nsIHttpChannel.h"
46 #include "nsIFileStreams.h"
47 #include "nsThreadUtils.h"
48 #include "nsAppDirectoryServiceDefs.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"
61 extern nsresult
EvaluateAdminConfigScript(const char *js_buffer
, size_t length
,
63 PRBool bGlobalContext
,
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
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
);
88 rv
= observerService
->AddObserver(this,"profile-after-change", PR_TRUE
);
93 nsAutoConfig::~nsAutoConfig()
97 // attribute string configURL
98 NS_IMETHODIMP
nsAutoConfig::GetConfigURL(char **aConfigURL
)
101 return NS_ERROR_NULL_POINTER
;
103 if (mConfigURL
.IsEmpty()) {
104 *aConfigURL
= nsnull
;
108 *aConfigURL
= ToNewCString(mConfigURL
);
110 return NS_ERROR_OUT_OF_MEMORY
;
113 NS_IMETHODIMP
nsAutoConfig::SetConfigURL(const char *aConfigURL
)
116 return NS_ERROR_NULL_POINTER
;
117 mConfigURL
.Assign(aConfigURL
);
122 nsAutoConfig::OnStartRequest(nsIRequest
*request
, nsISupports
*context
)
129 nsAutoConfig::OnDataAvailable(nsIRequest
*request
,
130 nsISupports
*context
,
131 nsIInputStream
*aIStream
,
132 PRUint32 aSourceOffset
,
140 size
= PR_MIN(aLength
, sizeof(buf
));
141 rv
= aIStream
->Read(buf
, size
, &amt
);
144 mBuf
.Append(buf
, amt
);
152 nsAutoConfig::OnStopRequest(nsIRequest
*request
, nsISupports
*context
,
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
));
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();
185 NS_WARNING("Error writing failover.jsc file");
187 // Releasing the lock to allow the main thread to start execution
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();
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
,
211 const PRUnichar
*someData
)
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
);
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
);
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();
242 nsresult
nsAutoConfig::downloadAutoConfig()
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");
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
)'?');
262 mConfigURL
.Truncate(index
);
264 // Clean up the previous read, the new read is going to use the same buffer
268 // Get the preferences branch and save it to the member variable
271 nsCOMPtr
<nsIPrefService
> prefs
=
272 do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
276 rv
= prefs
->GetBranch(nsnull
,getter_AddRefs(mPrefBranch
));
281 // Check to see if the network is online/offline
282 nsCOMPtr
<nsIIOService
> ios
= do_GetService(NS_IOSERVICE_CONTRACTID
, &rv
);
286 rv
= ios
->GetOffline(&offline
);
292 PRBool offlineFailover
= PR_FALSE
;
293 rv
= mPrefBranch
->GetBoolPref("autoadmin.offline_failover",
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
);
325 nsCOMPtr
<nsIURI
> url
;
326 nsCOMPtr
<nsIChannel
> channel
;
328 rv
= NS_NewURI(getter_AddRefs(url
), mConfigURL
.get(), nsnull
, nsnull
);
331 PR_LOG(MCD
, PR_LOG_DEBUG
, ("failed to create URL - is autoadmin.global_config_url valid? - %s\n", mConfigURL
.get()));
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
);
342 rv
= channel
->AsyncOpen(this, nsnull
);
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.
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)
371 NS_ENSURE_STATE(NS_ProcessNextEvent(thread
));
374 rv
= mPrefBranch
->GetIntPref("autoadmin.refresh_interval",
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
);
383 rv
= mTimer
->InitWithCallback(this, minutes
* 60 * 1000,
384 nsITimer::TYPE_REPEATING_SLACK
);
393 } // nsPref::downloadAutoConfig()
397 nsresult
nsAutoConfig::readOfflineFile()
399 PRBool failCache
= PR_TRUE
;
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.
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
);
420 rv
= ios
->GetOffline(&offline
);
425 rv
= ios
->SetOffline(PR_TRUE
);
430 // lock the "network.online" prference so user cannot toggle back to
432 rv
= mPrefBranch
->SetBoolPref("network.online", PR_FALSE
);
435 mPrefBranch
->LockPref("network.online");
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
));
450 failoverFile
->AppendNative(NS_LITERAL_CSTRING("failover.jsc"));
451 rv
= evaluateLocalFile(failoverFile
);
453 NS_WARNING("Couldn't open failover.jsc, going back to default prefs");
457 nsresult
nsAutoConfig::evaluateLocalFile(nsIFile
*file
)
460 nsCOMPtr
<nsIInputStream
> inStr
;
462 rv
= NS_NewLocalFileInputStream(getter_AddRefs(inStr
), file
);
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));
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
,
484 nsresult
nsAutoConfig::writeFailoverFile()
487 nsCOMPtr
<nsIFile
> failoverFile
;
488 nsCOMPtr
<nsIOutputStream
> outStr
;
491 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
492 getter_AddRefs(failoverFile
));
496 failoverFile
->AppendNative(NS_LITERAL_CSTRING("failover.jsc"));
498 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outStr
), failoverFile
);
501 rv
= outStr
->Write(mBuf
.get(),mBuf
.Length(),&amt
);
506 nsresult
nsAutoConfig::getEmailAddr(nsACString
& emailAddr
)
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
;
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
;
553 nsresult
nsAutoConfig::PromptForEMailAddress(nsACString
&emailAddress
)
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
);
567 rv
= bundle
->GetStringFromName(NS_LITERAL_STRING("emailPromptTitle").get(), getter_Copies(title
));
568 NS_ENSURE_SUCCESS(rv
, rv
);
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
;
576 rv
= promptService
->Prompt(nsnull
, title
.get(), err
.get(), getter_Copies(emailResult
), nsnull
, &check
, &success
);
578 return NS_ERROR_FAILURE
;
579 NS_ENSURE_SUCCESS(rv
, rv
);
580 LossyCopyUTF16toASCII(emailResult
, emailAddress
);