This file is indexed.

/usr/lib/thunderbird-addons/extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/calendar/content/calendar/calendar-dialog-utils.js is in xul-ext-lightning 1:24.4.0+build1-0ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Components.utils.import("resource://gre/modules/PluralForm.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/iteratorUtils.jsm");

Components.utils.import("resource://calendar/modules/calAlarmUtils.jsm");
Components.utils.import("resource://calendar/modules/calIteratorUtils.jsm");
Components.utils.import("resource://calendar/modules/calRecurrenceUtils.jsm");

/**
 * Dispose of controlling operations of this event dialog. Uses
 * window.arguments[0].job.dispose()
 */
function dispose() {
    var args = window.arguments[0];
    if (args.job && args.job.dispose) {
        args.job.dispose();
    }
    resetDialogId(document.documentElement);
}

/**
 * Sets the id of a Dialog to another value to allow different window-icons to be displayed.
 * The original name is stored as new Attribute of the Dialog to set it back later.
 *
 * @param aDialog               The Dialog to be changed.
 * @param aNewId                The new ID as String.
 */
function setDialogId(aDialog, aNewId) {
    aDialog.setAttribute("originalId", aDialog.getAttribute("id"));
    aDialog.setAttribute("id", aNewId);
}

/**
 * Sets the Dialog id back to previously stored one,
 * so that the persisted values are correctly saved.
 *
 * @param aDialog               The Dialog which is to be restored.
 */
function resetDialogId(aDialog) {
    let id = aDialog.getAttribute("originalId");
    if (id != "") {
        aDialog.setAttribute("id", id);
    }
    aDialog.removeAttribute("originalId");
}

/**
 * Create a calIAlarm from the given menuitem. The menuitem must have the
 * following attributes: unit, length, origin, relation.
 *
 * @param menuitem      The menuitem to create the alarm from.
 * @return              The calIAlarm with information from the menuitem.
 */
function createReminderFromMenuitem(aMenuitem) {
    let reminder = aMenuitem.reminder || cal.createAlarm();
    let offset = cal.createDuration();
    offset[aMenuitem.getAttribute("unit")] = aMenuitem.getAttribute("length");
    offset.normalize();
    offset.isNegative = (aMenuitem.getAttribute("origin") == "before");
    reminder.related = (aMenuitem.getAttribute("relation") == "START" ?
                        reminder.ALARM_RELATED_START : reminder.ALARM_RELATED_END);
    reminder.offset = offset;
    reminder.action = getDefaultAlarmType();
    return reminder;
}

/**
 * This function opens the needed dialogs to edit the reminder. Note however
 * that calling this function from an extension is not recommended. To allow an
 * extension to open the reminder dialog, set the menulist "item-alarm" to the
 * custom menuitem and call updateReminder().
 */
function editReminder() {
    let customItem =  document.getElementById("reminder-custom-menuitem");
    let args = {};
    args.reminders = customItem.reminders;
    args.item = window.calendarItem;
    args.timezone = (window.gStartTimezone ||
                     window.gEndTimezone ||
                     calendarDefaultTimezone());

    args.calendar = getCurrentCalendar();
    let savedWindow = window;

    // While these are "just" callbacks, the dialog is opened modally, so aside
    // from whats needed to set up the reminders, nothing else needs to be done.
    args.onOk = function(reminders) {
        customItem.reminders = reminders;
    };
    args.onCancel = function() {
        document.getElementById("item-alarm").selectedIndex = gLastAlarmSelection;
    };

    window.setCursor("wait");

    // open the dialog modally
    openDialog(
        "chrome://calendar/content/calendar-event-dialog-reminder.xul",
        "_blank",
        "chrome,titlebar,modal,resizable",
        args);
}

/**
 * Update the reminder details from the selected alarm. This shows a string
 * describing the reminder set, or nothing in case a preselected reminder was
 * chosen.
 */
function updateReminderDetails() {
    // find relevant elements in the document
    let reminderList = document.getElementById("item-alarm");
    let reminderMultipleLabel = document.getElementById("reminder-multiple-alarms-label");
    let iconBox = document.getElementById("reminder-icon-box");
    let reminderSingleLabel = document.getElementById("reminder-single-alarms-label");
    let reminders = document.getElementById("reminder-custom-menuitem").reminders || [];
    let calendar = getCurrentCalendar();
    let actionValues = calendar.getProperty("capabilities.alarms.actionValues") || ["DISPLAY"];
    let actionMap = {};
    for each (var action in actionValues) {
        actionMap[action] = true;
    }

    // Filter out any unsupported action types.
    reminders = reminders.filter(function(x) x.action in actionMap);

    if (reminderList.value == "custom") {
        // Depending on how many alarms we have, show either the "Multiple Alarms"
        // label or the single reminder label.
        setElementValue(reminderMultipleLabel,
                        reminders.length < 2 && "true",
                        "hidden");
        setElementValue(reminderSingleLabel,
                        reminders.length > 1 && "true",
                        "hidden");

        cal.alarms.addReminderImages(iconBox, reminders);

        // If there is only one reminder, display the reminder string
        if (reminders.length == 1) {
            setElementValue(reminderSingleLabel,
                            reminders[0].toString(window.calendarItem));
        }
    } else {
        hideElement(reminderMultipleLabel);
        hideElement(reminderSingleLabel);
        if (reminderList.value != "none") {
            // This is one of the predefined dropdown items. We should show a single
            // icon in the icons box to tell the user what kind of alarm this will
            // be.
            let mockAlarm = cal.createAlarm();
            mockAlarm.action = getDefaultAlarmType();
            cal.alarms.addReminderImages(iconBox, [mockAlarm]);
        } else {
            // No reminder selected means show no icons.
            removeChildren(iconBox);
        }
    }
}

var gLastAlarmSelection = 0;

function matchCustomReminderToMenuitem(reminder) {
    let defaultAlarmType = getDefaultAlarmType();
    let reminderList = document.getElementById("item-alarm");
    let reminderPopup = reminderList.firstChild;
    if (reminder.related != Components.interfaces.calIAlarm.ALARM_RELATED_ABSOLUTE &&
        reminder.offset &&
        reminder.action == defaultAlarmType) {
        // Exactly one reminder thats not absolute, we may be able to match up
        // popup items.
        let relation = (reminder.related == reminder.ALARM_RELATED_START ? "START" : "END");
        let origin;

        // If the time duration for offset is 0, means the reminder is '0 minutes before'
        if (reminder.offset.inSeconds == 0 || reminder.offset.isNegative) {
            origin = "before";
        } else {
            origin = "after";
        }

        let unitMap = {
          days: 86400,
          hours: 3600,
          minutes: 60
        };

        for each (let menuitem in Array.slice(reminderPopup.childNodes)) {
            if (menuitem.localName == "menuitem" &&
                menuitem.hasAttribute("length") &&
                menuitem.getAttribute("origin") == origin &&
                menuitem.getAttribute("relation") == relation) {
                let unitMult = unitMap[menuitem.getAttribute("unit")] || 1;
                let length = menuitem.getAttribute("length") * unitMult;

                if (Math.abs(reminder.offset.inSeconds) == length) {
                    menuitem.reminder = reminder.clone();
                    reminderList.selectedItem = menuitem;
                    // We've selected an item, so we are done here.
                    return true;
                }
            }
        }
    }

    return false;
}
/**
 * Load an item's reminders into the dialog
 *
 * @param reminders     An array of calIAlarms to load. 
 */
function loadReminders(reminders) {
    // select 'no reminder' by default
    let reminderList = document.getElementById("item-alarm");
    let reminderPopup = reminderList.firstChild;
    let customItem = document.getElementById("reminder-custom-menuitem");
    reminderList.selectedIndex = 0;
    gLastAlarmSelection = 0;

    if (!reminders || !reminders.length) {
        // No reminders selected, we are done
        return;
    }

    if (reminders.length > 1 ||
        !matchCustomReminderToMenuitem(reminders[0])) {
        // If more than one alarm is selected, or we didn't find a matching item
        // above, then select the "custom" item and attach the item's reminders to
        // it.
        reminderList.value = 'custom';
        customItem.reminders = reminders;
    }

    // remember the selected index
    gLastAlarmSelection = reminderList.selectedIndex;
}

/**
 * Save the selected reminder into the passed item.
 *
 * @param item      The item save the reminder into.
 */
function saveReminder(item) {
    // We want to compare the old alarms with the new ones. If these are not
    // the same, then clear the snooze/dismiss times
    let oldAlarmMap = {};
    for each (let alarm in item.getAlarms({})) {
        oldAlarmMap[alarm.icalString] = true;
    }

    // Clear the alarms so we can add our new ones.
    item.clearAlarms();

    let reminderList = document.getElementById("item-alarm");
    if (reminderList.value != 'none') {
        let menuitem = reminderList.selectedItem;
        let reminders;

        if (menuitem.reminders) {
            // Custom reminder entries carry their own reminder object with
            // them. Make sure to clone in case these are the original item's
            // reminders.

            // XXX do we need to clone here?
            reminders = menuitem.reminders.map(function(x) x.clone());
        } else {
            // Pre-defined entries specify the necessary information
            // as attributes attached to the menuitem elements.
            reminders = [createReminderFromMenuitem(menuitem)];
        }

        let alarmCaps = item.calendar.getProperty("capabilities.alarms.actionValues") ||
                        ["DISPLAY"];
        let alarmActions = {};
        for each (let action in alarmCaps) {
            alarmActions[action] = true;
        }

        // Make sure only alarms are saved that work in the given calendar.
        reminders.filter(function(x) x.action in alarmActions)
                 .forEach(item.addAlarm, item);
    }

    // Compare alarms to see if something changed.
    for each (let alarm in item.getAlarms({})) {
        let ics = alarm.icalString;
        if (ics in oldAlarmMap) {
            // The new alarm is also in the old set, remember this
            delete oldAlarmMap[ics];
        } else {
            // The new alarm is not in the old set, this means the alarms
            // differ and we can break out.
            oldAlarmMap[ics] = true;
            break;
       }
    }

    // If the alarms differ, clear the snooze/dismiss properties
    if (Object.keys(oldAlarmMap).length > 0) {
        let cmp = "X-MOZ-SNOOZE-TIME";
        let cmpLength = cmp.length;

        // Recurring item alarms potentially have more snooze props, remove them
        // all.
        let propIterator = fixIterator(item.propertyEnumerator, Components.interfaces.nsIProperty);
        let propsToDelete = [
            prop.name
            for each (prop in propIterator)
            if (prop.name.substr(0, cmpLength) == cmp)
        ];

        item.alarmLastAck = null;
        propsToDelete.forEach(item.deleteProperty, item);
    }
}

/**
 * Get the default alarm type for the currently selected calendar. If the
 * calendar supports DISPLAY alarms, this is the default. Otherwise it is the
 * first alarm action the calendar supports.
 *
 * @return      The default alarm type.
 */
function getDefaultAlarmType() {
    let calendar = getCurrentCalendar();
    let alarmCaps = calendar.getProperty("capabilities.alarms.actionValues") ||
                    ["DISPLAY"];
    return (alarmCaps.indexOf("DISPLAY") < 0 ? alarmCaps[0] : "DISPLAY");
}

/**
 * Get the currently selected calendar. For dialogs with a menulist of
 * calendars, this is the currently chosen calendar, otherwise its the fixed
 * calendar from the window's item.
 *
 * @return      The currently selected calendar.
 */
function getCurrentCalendar() {
    let calendarNode = document.getElementById("item-calendar");
    return (calendarNode && calendarNode.selectedItem ?
                calendarNode.selectedItem.calendar :
                window.calendarItem.calendar);
}

/**
 * Common update functions for both event dialogs. Called when a reminder has
 * been selected from the menulist.
 *
 * @param aSuppressDialogs     If true, controls are updated without prompting
 *                               for changes with the dialog
 */
function commonUpdateReminder(aSuppressDialogs) {
    // if a custom reminder has been selected, we show the appropriate
    // dialog in order to allow the user to specify the details.
    // the result will be placed in the 'reminder-custom-menuitem' tag.
    let reminderList = document.getElementById("item-alarm");
    if (reminderList.value == 'custom') {
        // Clear the reminder icons first, this will make sure that while the
        // dialog is open the default reminder image is not shown which may
        // confuse users.
        removeChildren("reminder-icon-box");

        // show the dialog. This call blocks until the dialog is closed. Don't
        // pop up the dialog if aSuppressDialogs was specified or if this
        // happens during initialization of the dialog
        if (!aSuppressDialogs && reminderList.hasAttribute("last-value")) {
            editReminder();
        }

        if (reminderList.value == 'custom') {
            // Only do this if the 'custom' item is still selected. If the edit
            // reminder dialog was canceled then the previously selected
            // menuitem is selected, which may not be the custom menuitem.

            // If one or no reminders were selected, we have a chance of mapping
            // them to the existing elements in the dropdown.
            let customItem = reminderList.selectedItem;
            if (customItem.reminders.length == 0) {
                // No reminder was selected
                reminderList.value = "none";
            } else if (customItem.reminders.length == 1) {
                // We might be able to match the custom reminder with one of the
                // default menu items.
                matchCustomReminderToMenuitem(customItem.reminders[0]);
            }
        }
    }

    // remember the current reminder drop down selection index.
    gLastAlarmSelection = reminderList.selectedIndex;
    reminderList.setAttribute("last-value", reminderList.value);

    // possibly the selected reminder conflicts with the item.
    // for example an end-relation combined with a task without duedate
    // is an invalid state we need to take care of. we take the same
    // approach as with recurring tasks. in case the reminder is related
    // to the entry date we check the entry date automatically and disable
    // the checkbox. the same goes for end related reminder and the due date.
    if (isToDo(window.calendarItem)) {
        // In general, (re-)enable the due/entry checkboxes. This will be
        // changed in case the alarms are related to START/END below.
        enableElementWithLock("todo-has-duedate", "reminder-lock");
        enableElementWithLock("todo-has-entrydate", "reminder-lock");

        let menuitem = reminderList.selectedItem;
        if (menuitem.value != 'none') {
            // In case a reminder is selected, retrieve the array of alarms from
            // it, or create one from the currently selected menuitem.
            let reminders = menuitem.reminders || [createReminderFromMenuitem(menuitem)];

            // If a reminder is related to the entry date...
            if (reminders.some(function(x) x.related == x.ALARM_RELATED_START)) {
                // ...automatically check 'has entrydate'.
                if (!getElementValue("todo-has-entrydate", "checked")) {
                    setElementValue("todo-has-entrydate", "true", "checked");

                    // Make sure gStartTime is properly initialized
                    updateEntryDate();
                }

                // Disable the checkbox to indicate that we need the entry-date.
                disableElementWithLock("todo-has-entrydate", "reminder-lock");
            }

            // If a reminder is related to the due date...
            if (reminders.some(function(x) x.related == x.ALARM_RELATED_END)) {
                // ...automatically check 'has duedate'.
                if (!getElementValue("todo-has-duedate", "checked")) {
                    setElementValue("todo-has-duedate", "true", "checked");

                    // Make sure gStartTime is properly initialized
                    updateDueDate();
                }

                // Disable the checkbox to indicate that we need the entry-date.
                disableElementWithLock("todo-has-duedate", "reminder-lock");
            }
        }
    }

    updateReminderDetails();
}

/**
 * Updates the related link on the dialog
 */
function updateLink() {
    var itemUrlString = window.calendarItem.getProperty("URL") || "";
    var linkCommand = document.getElementById("cmd_toggle_link");

    function hideOrShow(aBool) {
        setElementValue("event-grid-link-row", !aBool && "true", "hidden");
        var separator = document.getElementById("event-grid-link-separator");
        if (separator) {
            // The separator is not there in the summary dialog
            setElementValue("event-grid-link-separator", !aBool && "true", "hidden");
        }
    }

    if (linkCommand) {
        // Disable if there is no url
        setElementValue(linkCommand,
                        !itemUrlString.length && "true",
                        "disabled");
    }

    if ((linkCommand && linkCommand.getAttribute("checked") != "true") ||
        !itemUrlString.length) {
        // Hide if there is no url, or the menuitem was chosen so that the url
        // should be hidden
        hideOrShow(false);
    } else {
        var handler, uri;
        try {
            uri = makeURL(itemUrlString);
            handler = Services.io.getProtocolHandler(uri.scheme);
        } catch (e) {
            // No protocol handler for the given protocol, or invalid uri
            hideOrShow(false);
            return;
        }

        // Only show if its either an internal protcol handler, or its external
        // and there is an external app for the scheme
        handler = cal.wrapInstance(handler, Components.interfaces.nsIExternalProtocolHandler);
        hideOrShow(!handler||
                   handler.externalAppExistsForScheme(uri.scheme));

        setTimeout(function() {
          // HACK the url-link doesn't crop when setting the value in onLoad
          setElementValue("url-link", itemUrlString);
          setElementValue("url-link", itemUrlString, "href");
        }, 0);
    }
}