Coverage Report

Created: 2018-09-25 14:53

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