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