Fix
bug 419007 - Invalid ics files don\'t trigger INVALID_TIMEZONE error, times are assumed as floating. r=philipp
--- a/calendar/base/src/Makefile.in
+++ b/calendar/base/src/Makefile.in
@@ -108,16 +108,20 @@ EXTRA_SCRIPTS = \
calProviderUtils.js \
calWeekInfoService.js \
calTransactionManager.js \
calFreeBusyService.js \
calCalendarSearchService.js \
calTimezoneService.js \
$(NULL)
+EXTRA_JS_MODULES = \
+ calUtils.jsm \
+ $(NULL)
+
# Use NSINSTALL to make the directory, as there's no mtime to preserve.
libs:: $(EXTRA_SCRIPTS)
if test ! -d $(FINAL_TARGET)/js; then $(NSINSTALL) -D $(FINAL_TARGET)/js; fi
$(INSTALL) $^ $(FINAL_TARGET)/js
# The install target must use SYSINSTALL, which is NSINSTALL in copy mode.
install:: $(EXTRA_SCRIPTS)
$(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/js
--- a/calendar/base/src/calDateTime.cpp
+++ b/calendar/base/src/calDateTime.cpp
@@ -475,17 +475,21 @@ void calDateTime::FromIcalTime(icaltimet
} else {
mTimezone = cal::detectTimezone(t, nsnull);
}
#if defined(DEBUG)
if (mTimezone) {
if (t.is_utc) {
NS_ASSERTION(SameCOMIdentity(mTimezone, cal::UTC()), "UTC mismatch!");
} else if (!t.zone) {
- NS_ASSERTION(SameCOMIdentity(mTimezone, cal::floating()), "floating mismatch!");
+ nsCAutoString tzid;
+ mTimezone->GetTzid(tzid);
+ if (tzid.EqualsLiteral("floating")) {
+ NS_ASSERTION(SameCOMIdentity(mTimezone, cal::floating()), "floating mismatch!");
+ }
} else {
nsCAutoString tzid;
mTimezone->GetTzid(tzid);
NS_ASSERTION(tzid.Equals(icaltimezone_get_tzid(const_cast<icaltimezone *>(t.zone))),
"tzid mismatch!");
}
}
#endif
--- a/calendar/base/src/calICSService.cpp
+++ b/calendar/base/src/calICSService.cpp
@@ -433,16 +433,19 @@ nsresult calIcalProperty::getDatetime_(c
if (!icaltimezone_set_component(clonedZone, clonedZoneComp)) {
icaltimezone_free(clonedZone, 1 /* free struct */);
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<calIIcalComponent> const tzComp(new calIcalComponent(clonedZone, clonedZoneComp));
CAL_ENSURE_MEMORY(tzComp);
tz = new calTimezone(tzid, tzComp);
CAL_ENSURE_MEMORY(tz);
+ } else { // install phantom timezone, so the data could be repaired:
+ tz = new calTimezone(tzid, nsnull);
+ CAL_ENSURE_MEMORY(tz);
}
}
}
if (comp && tz) {
// assure timezone is known:
comp->AddTimezoneReference(tz);
}
}
@@ -476,18 +479,19 @@ calIcalComponent::~calIcalComponent()
NS_IMETHODIMP
calIcalComponent::AddTimezoneReference(calITimezone *aTimezone)
{
NS_ENSURE_ARG_POINTER(aTimezone);
nsCAutoString tzid;
nsresult rv = aTimezone->GetTzid(tzid);
NS_ENSURE_SUCCESS(rv, rv);
- if (!mReferencedTimezones.Put(tzid, aTimezone))
+ if (!mReferencedTimezones.Put(tzid, aTimezone)) {
return NS_ERROR_OUT_OF_MEMORY;
+ }
return NS_OK;
}
PR_STATIC_CALLBACK(PLDHashOperator)
TimezoneHashToTimezoneArray(nsACString const& /*tzid*/, calITimezone * tz, void * arg)
{
calITimezone *** const arrayPtr = static_cast<calITimezone ***>(arg);
NS_ADDREF(**arrayPtr = tz);
@@ -759,25 +763,38 @@ nsresult calIcalProperty::setDatetime_(c
{
NS_ENSURE_ARG_POINTER(prop);
NS_ENSURE_ARG_POINTER(dt);
icaltimetype itt;
dt->ToIcalTime(&itt);
if (parent) {
- if (!itt.is_utc && itt.zone) {
+ if (!itt.is_utc) {
nsCOMPtr<calITimezone> tz;
nsresult rv = dt->GetTimezone(getter_AddRefs(tz));
NS_ENSURE_SUCCESS(rv, rv);
- rv = parent->getParentVCalendarOrThis()->AddTimezoneReference(tz);
- NS_ENSURE_SUCCESS(rv, rv);
- icalparameter * const param = icalparameter_new_from_value_string(
- ICAL_TZID_PARAMETER, icaltimezone_get_tzid(const_cast<icaltimezone *>(itt.zone)));
- icalproperty_set_parameter(prop, param);
+ if (itt.zone) {
+ rv = parent->getParentVCalendarOrThis()->AddTimezoneReference(tz);
+ NS_ENSURE_SUCCESS(rv, rv);
+ icalparameter * const param = icalparameter_new_from_value_string(
+ ICAL_TZID_PARAMETER, icaltimezone_get_tzid(const_cast<icaltimezone *>(itt.zone)));
+ icalproperty_set_parameter(prop, param);
+ } else { // either floating or phantom:
+ PRBool b = PR_FALSE;
+ if (NS_FAILED(tz->GetIsFloating(&b)) || !b) {
+ // restore the same phantom TZID:
+ nsCAutoString tzid;
+ rv = tz->GetTzid(tzid);
+ NS_ENSURE_SUCCESS(rv, rv);
+ icalparameter * const param = icalparameter_new_from_value_string(ICAL_TZID_PARAMETER,
+ tzid.get());
+ icalproperty_set_parameter(prop, param);
+ }
+ }
}
} else if (!itt.is_date && !itt.is_utc && itt.zone) {
// no parent to add the CTIMEZONE to: coerce DATETIMEs to UTC, DATEs to floating
icaltimezone_convert_time(&itt,
const_cast<icaltimezone *>(itt.zone),
icaltimezone_get_utc_timezone());
itt.zone = icaltimezone_get_utc_timezone();
itt.is_utc = 1;
--- a/calendar/base/src/calIcsParser.js
+++ b/calendar/base/src/calIcsParser.js
@@ -30,16 +30,18 @@
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
+Components.utils.import("resource://calendar/modules/calUtils.jsm");
+
function calIcsParser() {
this.wrappedJSObject = this;
this.mItems = new Array();
this.mParentlessItems = new Array();
this.mComponents = new Array();
this.mProperties = new Array();
}
@@ -65,16 +67,36 @@ function ip_parseString(aICSString, aTzP
} else {
calComp = rootComp.getFirstSubcomponent('VCALENDAR');
}
var unexpandedItems = [];
var uid2parent = {};
var excItems = [];
+ let tzErrors = {};
+ function checkTimezone(item, dt) {
+ if (dt && cal.isPhantomTimezone(dt.timezone)) {
+ let tzid = dt.timezone.tzid;
+ let hid = item.hashId + "#" + tzid;
+ if (tzErrors[hid] === undefined) {
+ // For now, publish errors to console and alert user.
+ // In future, maybe make them available through an interface method
+ // so this UI code can be removed from the parser, and caller can
+ // choose whether to alert, or show user the problem items and ask
+ // for fixes, or something else.
+ let msg = (calGetString("calendar", "unknownTimezoneInItem",
+ [tzid, item.title, cal.getDateFormatter().formatDateTime(dt)]) +
+ "\n" + item.icalString);
+ cal.ERROR(msg);
+ tzErrors[hid] = true;
+ }
+ }
+ }
+
while (calComp) {
// Get unknown properties
var prop = calComp.getFirstProperty("ANY");
while (prop) {
if (prop.propertyName != "VERSION" &&
prop.propertyName != "PRODID") {
this.mProperties.push(prop);
@@ -88,35 +110,38 @@ function ip_parseString(aICSString, aTzP
isFromOldSunbird = prodId.value == "-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN";
}
var subComp = calComp.getFirstSubcomponent("ANY");
while (subComp) {
var item = null;
switch (subComp.componentType) {
case "VEVENT":
- item = Components.classes["@mozilla.org/calendar/event;1"]
- .createInstance(Components.interfaces.calIEvent);
+ item = cal.createEvent();
+ item.icalComponent = subComp;
+ checkTimezone(item, item.startDate);
+ checkTimezone(item, item.endDate);
break;
case "VTODO":
- item = Components.classes["@mozilla.org/calendar/todo;1"]
- .createInstance(Components.interfaces.calITodo);
+ item = cal.createTodo();
+ item.icalComponent = subComp;
+ checkTimezone(item, item.entryDate);
+ checkTimezone(item, item.dueDate);
+ // completed is defined to be in UTC
break;
case "VTIMEZONE":
// this should already be attached to the relevant
// events in the calendar, so there's no need to
// do anything with it here.
break;
default:
this.mComponents.push(subComp);
}
if (item) {
- item.icalComponent = subComp;
-
// Only try to fix ICS from Sunbird 0.2 (and earlier) if it
// has an EXDATE.
hasExdate = subComp.getFirstProperty("EXDATE");
if (isFromOldSunbird && hasExdate) {
item = fixOldSunbirdExceptions(item);
}
var rid = item.recurrenceId;
@@ -144,16 +169,29 @@ function ip_parseString(aICSString, aTzP
} else { // a parentless one
this.mParentlessItems.push(item);
}
}
for each (var item in unexpandedItems) {
this.mItems.push(item);
}
+
+ for (let e in tzErrors) { // if any error has occurred
+ // Use an alert rather than a prompt because problems may appear in
+ // remote subscribed calendars the user cannot change.
+ if (Components.classes["@mozilla.org/alerts-service;1"]) {
+ let notifier = Components.classes["@mozilla.org/alerts-service;1"]
+ .getService(Components.interfaces.nsIAlertsService);
+ let title = calGetString("calendar", "TimezoneErrorsAlertTitle")
+ let text = calGetString("calendar", "TimezoneErrorsSeeConsole");
+ notifier.showAlertNotification("", title, text, false, null, null, title);
+ }
+ break;
+ }
};
calIcsParser.prototype.parseFromStream =
function ip_parseFromStream(aStream, aTzProvider) {
// Read in the string. Note that it isn't a real string at this point,
// because likely, the file is utf8. The multibyte chars show up as multiple
// 'chars' in this string. So call it an array of octets for now.
--- a/calendar/base/src/calTimezone.cpp
+++ b/calendar/base/src/calTimezone.cpp
@@ -54,17 +54,17 @@ NS_IMETHODIMP
calTimezone::GetIsUTC(PRBool * _retval) {
NS_ENSURE_ARG_POINTER(_retval);
*_retval = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
calTimezone::GetDisplayName(nsAString & _retval) {
- _retval.SetIsVoid(PR_TRUE);
+ _retval = NS_ConvertUTF8toUTF16(mTzid);
return NS_OK;
}
NS_IMETHODIMP
calTimezone::GetLatitude(nsACString & _retval) {
_retval.SetIsVoid(PR_TRUE);
return NS_OK;
}
@@ -79,11 +79,15 @@ NS_IMETHODIMP
calTimezone::GetProvider(calITimezoneProvider ** _retval) {
NS_ENSURE_ARG_POINTER(_retval);
*_retval = nsnull;
return NS_OK;
}
NS_IMETHODIMP
calTimezone::ToString(nsACString & aResult) {
- return mIcalComponent->ToString(aResult);
+ if (mIcalComponent) {
+ return mIcalComponent->ToString(aResult);
+ } else {
+ return GetTzid(aResult);
+ }
}
--- a/calendar/base/src/calUtils.cpp
+++ b/calendar/base/src/calUtils.cpp
@@ -198,17 +198,17 @@ icaltimezone * getIcalTimezone(calITimez
tz->GetIsUTC(&b);
if (b) {
icaltz = icaltimezone_get_utc_timezone();
} else {
nsCOMPtr<calIIcalComponent> tzComp;
tz->GetIcalComponent(getter_AddRefs(tzComp));
if (tzComp) {
icaltz = tzComp->GetIcalTimezone();
- } // else floating
+ } // else floating or phantom timezone
}
return icaltz;
}
XpcomBase::~XpcomBase() {
}
}
new file mode 100644
--- /dev/null
+++ b/calendar/base/src/calUtils.jsm
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Sun Microsystems code.
+ *
+ * The Initial Developer of the Original Code is
+ * Sun Microsystems, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Daniel Boelzle <daniel.boelzle@sun.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// New code must not load/import calUtils.js, but should use calUtils.jsm.
+
+var EXPORTED_SYMBOLS = ["cal"];
+let cal = {
+ // new code should land here,
+ // and more code should be moved from calUtils.js into this object to avoid
+ // clashes with other extensions
+
+ getIOService: generateServiceAccessor("@mozilla.org/network/io-service;1",
+ Components.interfaces.nsIIOService2),
+
+ /**
+ * Checks whether a timezone lacks a definition.
+ */
+ isPhantomTimezone: function cal_isPhantomTimezone(tz) {
+ return (!tz.icalComponent && !tz.isUTC && !tz.isFloating);
+ }
+};
+
+// local to this module;
+// will be used to generate service accessor functions, getIOService()
+function generateServiceAccessor(id, iface) {
+ return function this_() {
+ if (this_.mService === undefined) {
+ this_.mService = Components.classes[id].getService(iface);
+ }
+ return this_.mService;
+ };
+}
+
+// Interim import of all symbols into cal:
+// This should serve as a clean start for new code, e.g. new code could use
+// cal.createDatetime instead of plain createDatetime NOW.
+let calUtils = __LOCATION__.parent.parent.clone();
+calUtils.append("js");
+calUtils.append("calUtils.js");
+Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Components.interfaces.mozIJSSubScriptLoader)
+ .loadSubScript(cal.getIOService().newFileURI(calUtils).spec, cal);
+
--- a/calendar/lightning/jar.mn
+++ b/calendar/lightning/jar.mn
@@ -1,9 +1,10 @@
lightning.jar:
+% resource calendar .
% content lightning %content/lightning/
% content messagebody %content/messagebody/ contentaccessible=yes
% override chrome://messagebody/skin/imip.css chrome://lightning/skin/imip.css
% overlay chrome://messenger/content/messenger.xul chrome://lightning/content/lightning-migration.xul
% overlay chrome://messenger/content/msgAccountCentral.xul chrome://lightning/content/messenger-overlay-accountCentral.xul
% overlay chrome://messenger/content/messenger.xul chrome://lightning/content/messenger-overlay-sidebar.xul
% overlay chrome://messenger/content/messageWindow.xul chrome://lightning/content/imip-bar-overlay.xul
% overlay chrome://messenger/content/messageWindow.xul chrome://lightning/content/messenger-overlay-messageWindow.xul
--- a/calendar/locales/en-US/chrome/calendar/calendar.properties
+++ b/calendar/locales/en-US/chrome/calendar/calendar.properties
@@ -96,16 +96,21 @@ unableToWrite=Unable to write to file:
defaultFileName=MozillaCalEvents
HTMLTitle=Mozilla Calendar
timezoneError=An unknown and undefined timezone was found while reading %1$S.
duplicateError=%1$S item(s) were ignored since they exist in both the destination calendar and %2$S.
unableToCreateProvider=An error was encountered preparing the calendar located at %1$S for use. It will not be available.
unknownTimezonesError=An error was encountered preparing the calendar located at %1$S for use. The calendar might refer to unknown timezones. Please install the latest calendar-timezones.xpi.
missingCalendarTimezonesError=No timezones found! Please install calendar-timezones.xpi.
+# Sample: Unknown timezone "USPacific" in "Dentist Appt". Using the 'floating' local timezone instead: 2008/02/28 14:00:00
+unknownTimezoneInItem=Unknown timezone "%1$S" in "%2$S". Treated as 'floating' local timezone instead: %3$S
+TimezoneErrorsAlertTitle=Timezone Errors
+TimezoneErrorsSeeConsole=See Error Console: Unknown timezones are treated as the 'floating' local timezone.
+
unsubscribeCalendarTitle=Unsubscribe from Calendar
unsubscribeCalendarMessage=Are you sure you want to unsubscribe from calendar "%1$S"?
WeekTitle=Week %1$S
# Used to format the Multiweek's labels, ie Weeks 2 - 7
WeeksTitle=Weeks %1$S-%2$S
None=None
--- a/calendar/sunbird/base/jar.mn
+++ b/calendar/sunbird/base/jar.mn
@@ -1,9 +1,10 @@
calendar.jar:
+% resource calendar .
% content calendar %content/calendar/
* content/calendar/aboutDialog.css (content/aboutDialog.css)
* content/calendar/aboutDialog.js (content/aboutDialog.js)
* content/calendar/aboutDialog.xul (content/aboutDialog.xul)
* content/calendar/calendar.xul (content/calendar.xul)
content/calendar/calendar-gotodate-dialog.xul (content/calendar-gotodate-dialog.xul)
content/calendar/calendar-offline.js (content/calendar-offline.js)
* content/calendar/credits.xhtml (content/credits.xhtml)