Imported Upstream version 0.8~alpha1
[platform/upstream/syncevolution.git] / src / client-api / src / c++ / windows / vocl / WinEventSIF.cpp
1 /*
2  * Funambol is a mobile platform developed by Funambol, Inc. 
3  * Copyright (C) 2003 - 2007 Funambol, Inc.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Affero General Public License version 3 as published by
7  * the Free Software Foundation with the addition of the following permission 
8  * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
9  * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE 
10  * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
11  * 
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  * 
17  * You should have received a copy of the GNU Affero General Public License 
18  * along with this program; if not, see http://www.gnu.org/licenses or write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  * MA 02110-1301 USA.
21  * 
22  * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite 
23  * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
24  * 
25  * The interactive user interfaces in modified source and object code versions
26  * of this program must display Appropriate Legal Notices, as required under
27  * Section 5 of the GNU Affero General Public License version 3.
28  * 
29  * In accordance with Section 7(b) of the GNU Affero General Public License
30  * version 3, these Appropriate Legal Notices must retain the display of the
31  * "Powered by Funambol" logo. If the display of the logo is not reasonably 
32  * feasible for technical reasons, the Appropriate Legal Notices must display
33  * the words "Powered by Funambol".
34  */
35
36 #include "vocl/WinEventSIF.h"
37 #include "vocl/VConverter.h"
38 #include "vocl/constants.h"
39 #include "base/stringUtils.h"
40 #include "vocl/sifUtils.h"
41 #include "base/globalsdef.h"
42
43 USE_NAMESPACE
44
45 using namespace std;
46
47
48 // Constructor
49 WinEventSIF::WinEventSIF() {
50     sifFields = NULL;
51     sif = L"";
52 }
53
54 // Constructor: fills propertyMap parsing the passed SIF string
55 WinEventSIF::WinEventSIF(const wstring dataString, const wchar_t** fields, const wchar_t** recFields) {
56     
57     sif = L"";
58     sifFields = fields;
59     recPatternSIF.setSifFields(recFields);
60
61     parse(dataString);
62 }
63
64 // Destructor
65 WinEventSIF::~WinEventSIF() {
66 }
67
68
69
70 wstring& WinEventSIF::toString() {
71     
72     wstring propertyValue;
73     sif = L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";   
74     sif += L"<appointment>\n";
75
76     sif += L"<SIFVersion>";
77     sif += SIF_VERSION;
78     sif += L"</SIFVersion>\n";
79
80     //
81     // Add all appointment properties
82     //
83     map<wstring,wstring>::iterator it = propertyMap.begin();       
84     while (it != propertyMap.end()) {        
85         propertyValue = it->second;      
86         propertyValue = adaptToSIFSpecs(it->first, propertyValue);
87         addPropertyToSIF(sif, it->first, propertyValue);
88         it ++;
89     }
90
91     //
92     // Append recurrence properties
93     //
94     wstring element;
95     if (getProperty(L"IsRecurring", element)) {
96         wstring recurrenceSIF = recPatternSIF.toString();
97         if(recurrenceSIF != L"") {
98             sif += recurrenceSIF;
99         }
100
101         //
102         // If recurring, append events exceptions
103         //
104         bool isRec = (element != TEXT("0"));
105         if(isRec) {
106             list<wstring>::iterator it;
107
108             if (!excludeDate.size() && !includeDate.size()) {
109                 sif += L"<Exceptions/>\n";
110             }
111             else {
112                 sif += L"<Exceptions>\n";
113
114                 // Exceptions: ExcludeDate
115                 if (excludeDate.size() > 0) {
116                     for (it  = excludeDate.begin(); it != excludeDate.end(); it++) {
117                         wstring exDate = adaptToSIFSpecs(L"ExcludeDate", *it);
118                         addPropertyToSIF(sif, L"ExcludeDate", exDate);
119                     }
120                 }
121                 // Exceptions: IncludeDate (should be empty for Outlook and WM)
122                 if (includeDate.size() > 0) {
123                     for (it  = includeDate.begin(); it != includeDate.end(); it++) {
124                         wstring rDate = adaptToSIFSpecs(L"IncludeDate", *it);
125                         addPropertyToSIF(sif, L"IncludeDate", rDate);
126                     }
127                 }
128
129                 sif += L"</Exceptions>\n";
130             }
131
132             //
133             // TIMEZONE (only if recurring and tzInfo is used).
134             //
135             if (useTimezone) {
136                 addTimezone(sif);
137             }
138         }
139     }
140
141     //
142     // TODO: format recipients
143     //
144
145     sif += L"</appointment>";
146
147     return sif;
148 }
149
150
151
152 int WinEventSIF::parse(const wstring data) {
153     
154     if (!sifFields) {
155         LOG.error(ERR_SIFFIELDS_NULL);
156         return 1;
157     }
158     propertyMap.clear();
159     
160     // Check if <appointment> tag is present...
161     wstring::size_type pos = 0;
162     wstring itemTypeTag = L"<appointment>";    
163     pos = data.find(itemTypeTag, 0);
164     if (pos == wstring::npos) {
165         LOG.error("Tag '%ls' not found", itemTypeTag.c_str());
166         return 1;
167     }
168     wstring propertyValue;
169
170     //
171     // Set appointment properties
172     //
173     for (int i=0; sifFields[i]; i++) {
174         // Set only properties found!
175         if (!getElementContent(data, sifFields[i], propertyValue, 0)) {
176
177             replaceAll(L"&lt;",  L"<", propertyValue);
178             replaceAll(L"&gt;",  L">", propertyValue);
179             replaceAll(L"&amp;", L"&", propertyValue);
180             
181             propertyValue = adaptFromSIFSpecs(sifFields[i], propertyValue);
182             setProperty(sifFields[i], propertyValue);
183         }
184     } 
185
186     //
187     // Set recurrence properties
188     //
189     wstring element;
190     if (getProperty(L"IsRecurring", element)) {
191         bool isRec = (element != TEXT("0"));
192         if(isRec) {
193
194             // Fill recPatternSIF propertyMap.
195             recPatternSIF.parse(data);
196
197             // Fill exceptions lists.
198             parseExceptions(data);
199
200             // TIMEZONE
201             bool tzFound = parseTimezone(data);
202             useTimezone = tzFound;
203             getRecPattern()->setUseTimezone(tzFound);
204         }
205     }
206
207     //
208     // TODO: parse recipients and fill recipients list
209     //
210
211     return 0;
212 }
213
214
215 WinRecurrenceSIF* WinEventSIF::getRecPattern() {
216     return &recPatternSIF;
217 }
218
219
220
221 wstring WinEventSIF::adaptToSIFSpecs(const wstring& propName, const wstring& propValue) {
222     
223     wstring propertyValue = L"";
224
225     if (propValue.length() == 8) {
226         if ( propName == L"Start"       || 
227              propName == L"ExcludeDate" ||
228              propName == L"IncludeDate" ) {
229             propertyValue = formatDateWithMinus(propValue);    
230         }
231         else if (propName == L"End") {
232             // the End value must be decremented of a day in allDayEvent appointment
233             DATE d;
234             stringTimeToDouble(propValue, &d);
235             d -= 1;
236             doubleToStringTime(propertyValue, d, true);
237             propertyValue = formatDateWithMinus(propertyValue);
238         }
239     }
240
241     if (propertyValue != L"") {
242         return propertyValue;
243     } 
244     return propValue;
245 }
246
247
248
249 wstring WinEventSIF::adaptFromSIFSpecs(const wstring& propName, const wstring& propValue) {
250
251     wstring propertyValue = L"";
252
253     // the End value must be incremented of a day in allDayEvent appointment
254     if ( propName == L"End" && 
255         (propValue.size() == 8 || propValue.size() == 10)) { // both format yyyyMMdd and yyyy-MM-dd
256         DATE d;
257         stringTimeToDouble(propValue, &d);
258         d += 1;
259         doubleToStringTime(propertyValue, d, true);                                
260     }
261
262
263     if (propertyValue != L"") {
264         return propertyValue;
265     } 
266     return propValue;
267 }
268
269
270
271 void WinEventSIF::parseExceptions(const wstring& sifString) {
272
273     wstring exString;
274
275     if (getElementContent(sifString, L"Exceptions", exString)) {
276         // Not found: nothing to do.
277         return;
278     }
279
280     // <Exceptions> not empty -> parse all exceptions
281     if (exString.size() > 0) {
282         wstring exDate, rDate;
283         wstring::size_type pos, start, end;
284
285         // Parse all <ExcludeDate>
286         pos = 0;
287         while ( !getElementContent(exString, L"ExcludeDate", exDate, pos, start, end) ) {
288             pos = end;
289             if (exDate.size() > 0) {
290                 excludeDate.push_back(exDate);
291             }
292         }
293
294         // Parse all <IncludeDate>
295         pos = start = end = 0;
296         while ( !getElementContent(exString, L"IncludeDate", rDate, pos, start, end) ) {
297             pos = end;
298             if (rDate.size() > 0) {
299                 includeDate.push_back(rDate);
300             }
301         }
302     }
303 }
304
305
306 void WinEventSIF::addTimezone(wstring& sif) {
307
308     sif += L"<Timezone>\n";
309
310     //
311     // <BasicOffset> = - (Bias + StandardBias)     [StandardBias is usually = 0]
312     //
313     sif += L"<BasicOffset>";
314     sif += formatBias(tzInfo.Bias + tzInfo.StandardBias);
315     sif += L"</BasicOffset>\n";
316
317     //
318     // <DayLight> = list of tag for every year that this appointment occurr.
319     //
320     int yearBegin = 0;
321     int yearEnd   = 5000;
322     getIntervalOfRecurrence(&yearBegin, &yearEnd);
323
324
325     // DSTOffset = - (Bias + StandardBias + DaylightBias)
326     // [StandardBias is usually = 0]
327     bool hasDST = false;
328     int diffBias = tzInfo.Bias +  + tzInfo.StandardBias + tzInfo.DaylightBias;
329     wstring daylightBias;
330     if (diffBias != 0) { 
331         hasDST = true;
332         daylightBias = formatBias(diffBias);
333     }
334
335     // Max 6 iterations (for infinite recurrences).
336     if (yearEnd - yearBegin > MAX_DAYLIGHT_PROPS) {
337         yearEnd = yearBegin + MAX_DAYLIGHT_PROPS;
338     }
339
340     if (hasDST) {
341         // Add a DayLight tag for every year that this appointment occurr. (max = 6)
342         for (int year = yearBegin; year < yearEnd; year++) {
343
344             wstring daylightDate = getDateFromTzRule(year, tzInfo.DaylightDate);
345             wstring standardDate = getDateFromTzRule(year, tzInfo.StandardDate);
346
347             sif += L"<DayLight>\n";
348             sif += L"<DSTOffset>" ;    sif += daylightBias;     sif += L"</DSTOffset>";
349             sif += L"<DSTStart>"  ;    sif += daylightDate;     sif += L"</DSTStart>" ;
350             sif += L"<DSTEnd>"    ;    sif += standardDate;     sif += L"</DSTEnd>"   ;
351             if (wcslen(tzInfo.StandardName) > 0) {
352                 sif += L"<StandardName>";  sif += tzInfo.StandardName;   sif += L"</StandardName>";
353             }
354             else { 
355                 sif += L"<StandardName/>";
356             }
357             if (wcslen(tzInfo.StandardName) > 0) {
358                 sif += L"<DSTName>";       sif += tzInfo.DaylightName;   sif += L"</DSTName>\n";
359             }
360             else {
361                 sif += L"<DSTName/>";
362             }
363             sif += L"</DayLight>\n";
364         }
365     }
366     else {
367         // No daylight for this timezone
368         sif += L"<DayLight/>\n";
369     }
370
371     sif += L"</Timezone>\n";
372 }
373
374
375 bool WinEventSIF::parseTimezone(const wstring& data) {
376
377     wstring timezone;
378     if (getElementContent(data, L"Timezone", timezone, 0)) {
379         return false;
380     }
381     if (timezone.size() == 0) {
382         return false;
383     }
384
385     bool found = false;
386     wstring element;
387     if ( !getElementContent(timezone, L"BasicOffset", element, 0) && element.size() ) {
388
389         int bias = parseBias(element.c_str());
390
391         wstring dstOffset, standardName, daylightName;
392         list<wstring> daylightDates;
393         list<wstring> standardDates;
394
395         //
396         // Search all <DayLight> inside <Timezone> (one for every year)
397         //
398         wstring::size_type start = 0, end = 0;
399         bool dstFlag = false;
400         wstring daylight;
401         while (!getElementContent(timezone, L"DayLight", daylight, end, start, end)) {
402             if (daylight.size()) {
403                 // Found a DayLight tag. Many props are redundant, now are overwritten.
404                 dstFlag = true;
405                 getElementContent(daylight, L"DSTOffset",    dstOffset);
406                 getElementContent(daylight, L"DSTStart",     element);
407                 daylightDates.push_back(element);
408                 getElementContent(daylight, L"DSTEnd",       element);
409                 standardDates.push_back(element);
410                 getElementContent(daylight, L"StandardName", standardName);
411                 getElementContent(daylight, L"DSTName",      daylightName);
412             }
413             else {
414                 // Empty <DayLight/> = no daylight for this timezone
415                 dstFlag = false;
416                 break;
417             }
418         }
419
420
421         //
422         // If we have all required data, fill the tzInfo structure.
423         //
424         if (dstFlag == false) {
425             // Easy timezone, no DST
426             found = true;
427             tzInfo.Bias         = bias;
428             tzInfo.StandardBias = 0;        // Cannot retrieve it, assume = 0 (usually is 0)
429             tzInfo.DaylightBias = 0;
430             //tzInfo.DaylightDate = 0;
431             //tzInfo.StandardDate = 0;
432             wcsncpy(tzInfo.StandardName, standardName.c_str(), 32);
433             wcsncpy(tzInfo.DaylightName, daylightName.c_str(), 32);
434         }
435         else if (dstOffset.size() && daylightDates.size() && standardDates.size() ) {
436             // Standard timezone, the DST rules are extracted from list of dates
437             // >> Bias = -TZ
438             // >> StandardBias = 0  (Cannot retrieve it, assume = 0 as usually is 0)
439             // >> DaylightBias = - (DSTOffset + Bias)
440             found = true;
441             tzInfo.Bias         = bias;
442             tzInfo.StandardBias = 0;
443             tzInfo.DaylightBias = parseBias(dstOffset.c_str()) - bias;
444             tzInfo.DaylightDate = getTzRuleFromDates(daylightDates);
445             tzInfo.StandardDate = getTzRuleFromDates(standardDates);
446             wcsncpy(tzInfo.StandardName, standardName.c_str(), 32);
447             wcsncpy(tzInfo.DaylightName, daylightName.c_str(), 32);
448         }
449     }
450     else {
451         // <BasicOffset> missing (it's mandatory)
452         found = false;
453     }
454     
455     return found;
456 }