1 #include "wvtnefconv.h"
2 #include "wvtnefconv_private.h"
5 #include "wvstringlist.h"
11 /* This is a collection of functions used for converting
12 * TNEFs into VCards/VCals/VEvents (etc) and back again,
17 // Reading integers from a buffer
18 uint64_t read8u(WvDynBuf
&buf
)
20 const uint64_t *ptr
= (const uint64_t *)buf
.get(8);
24 return (uint64_t)swap8(*ptr
);
27 uint32_t read4u(WvDynBuf
&buf
)
29 const uint32_t *ptr
= (const uint32_t *)buf
.get(4);
33 return (uint32_t)swap4(*ptr
);
36 uint16_t read2u(WvDynBuf
&buf
)
38 const uint16_t *ptr
= (const uint16_t *)buf
.get(2);
42 return (uint16_t)swap2(*ptr
);
45 int64_t read8(WvDynBuf
&buf
)
47 const int64_t *ptr
= (const int64_t *)buf
.get(8);
51 return (int64_t)swap8(*ptr
);
54 int32_t read4(WvDynBuf
&buf
)
56 const int32_t *ptr
= (const int32_t *)buf
.get(4);
60 return (int32_t)swap4(*ptr
);
63 int16_t read2(WvDynBuf
&buf
)
65 const int16_t *ptr
= (const int16_t *)buf
.get(2);
69 return (int16_t)swap2(*ptr
);
72 // Appending integers to a buffer
73 void append2(uint16_t i
, WvDynBuf
&buf
)
79 void append4(uint32_t i
, WvDynBuf
&buf
)
85 void append8(uint64_t i
, WvDynBuf
&buf
)
91 inline WvString
escape_newline(WvStringParm str
)
94 for(unsigned int i
= 0; i
< str
.len(); ++i
)
100 s
.append("%c", str
[i
]);
104 bool is_vcard(const WvTnef
&tnef
)
106 string
const *s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_MESSAGE_CLASS
));
107 return (s
&& WvString("IPM.Contact") == (char *)s
->c_str());
110 bool is_vcal(const WvTnef
&tnef
)
112 string
const *s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_MESSAGE_CLASS
));
113 return (s
&& WvString("IPM.Appointment") == (char *)s
->c_str());
116 WvString
get_vcal(const WvTnef
&tnef
)
119 return WvString::null
;
121 string defguid
= str2guid(GUID_1
);
122 string wvidguid
= str2guid(WVIDGUID
);
126 WvString
ret("BEGIN:VEVENT\n");
128 s
= tnef
.get_property(wvidguid
, str2ustr("EvoId"));
130 ret
.append("UID:%s\n", s
->c_str());
132 s
= tnef
.get_property(defguid
, COMMON_START
);
133 if (s
&& s
->length())
135 ret
.append("DTSTART:");
136 Date dtr
= get_dateinfo(s
);
137 ret
.append("%04s%02s%02sT%02s%02s%02s\n",
138 dtr
.year
, dtr
.month
, dtr
.day
,
139 dtr
.hour
, dtr
.minute
, dtr
.second
);
142 s
= tnef
.get_property(defguid
, COMMON_END
);
143 if (s
&& s
->length())
145 ret
.append("DTEND:");
146 Date dtr
= get_dateinfo(s
);
147 ret
.append("%04s%02s%02sT%02s%02s%02s\n",
148 dtr
.year
, dtr
.month
, dtr
.day
,
149 dtr
.hour
, dtr
.minute
, dtr
.second
);
153 ret
.append("TRANSP:OPAQUE\n");
154 ret
.append("SEQUENCE:2\n");
156 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_SUBJECT
));
158 ret
.append("SUMMARY:%s\n", s
->c_str());
161 ret
.append("CLASS:PUBLIC\n");
163 s
= tnef
.get_property(defguid
, LOCATION
);
165 ret
.append("LOCATION:%s\n", s
->c_str());
167 // ret.append("CATEGORIES:%s\n", tnef.get_property(MAPI_TAG());
169 s
= tnef
.get_property(MAPI_TAG(PT_SYSTIME
, PR_LAST_MODIFICATION_TIME
));
170 if (s
&& s
->length())
172 ret
.append("LAST-MODIFIED:");
173 Date dtr
= get_dateinfo(s
);
174 ret
.append("%04s%02s%02sT%02s%02s%02s\n",
175 dtr
.year
, dtr
.month
, dtr
.day
,
176 dtr
.hour
, dtr
.minute
, dtr
.second
);
179 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_BODY
));
181 ret
.append("DESCRIPTION:%s\n", s
->c_str());
183 // Whee! Recurrences are fun.. mmm'kay?
184 s
= tnef
.get_property(defguid
, RECURRENCE
);
185 if (s
&& s
->length())
187 Recurrence recur
= get_recurrence(s
);
188 ret
.append(get_rrule(recur
));
189 ret
.append(get_exdates(recur
));
192 ret
.append("END:VEVENT\n");
196 WvString
get_vcard(const WvTnef
&tnef
)
199 return WvString::null
;
201 string defguid
= str2guid(GUID_2
);
204 WvString
ret("BEGIN:VCARD\n"
207 append_to_card(ret
, "X-EVOLUTION-FILE-AS:%s\n", defguid
, INDEX_NAME
, tnef
);
208 append_to_card(ret
, "FN:%s\n", MAPI_TAG(PT_STRING8
, PR_DISPLAY_NAME
), tnef
);
211 append_to_card(ret
, "%s", MAPI_TAG(PT_STRING8
, PR_SURNAME
), tnef
);
213 append_to_card(ret
, "%s", MAPI_TAG(PT_STRING8
, PR_GIVEN_NAME
), tnef
);
215 append_to_card(ret
, "%s", MAPI_TAG(PT_STRING8
, PR_MIDDLE_NAME
), tnef
);
217 append_to_card(ret
, "%s", MAPI_TAG(PT_STRING8
, PR_DISPLAY_NAME_PREFIX
), tnef
);
219 append_to_card(ret
, "%s", MAPI_TAG(PT_STRING8
, PR_GENERATION
), tnef
);
222 append_to_card(ret
, "NOTE:%s\n",
223 MAPI_TAG(PT_STRING8
, PR_BODY
), tnef
);
224 append_to_card(ret
, "ROLE:%s\n",
225 MAPI_TAG(PT_STRING8
, PR_PROFESSION
), tnef
);
226 append_to_card(ret
, "TITLE:%s\n",
227 MAPI_TAG(PT_STRING8
, PR_TITLE
), tnef
);
228 append_to_card(ret
, "NICKNAME:%s\n",
229 MAPI_TAG(PT_STRING8
, PR_NICKNAME
), tnef
);
230 append_to_card(ret
, "X-EVOLUTION-SPOUSE:%s\n",
231 MAPI_TAG(PT_STRING8
, PR_SPOUSE_NAME
), tnef
);
232 append_to_card(ret
, "X-EVOLUTION-MANAGER:%s\n",
233 MAPI_TAG(PT_STRING8
, PR_MANAGER_NAME
), tnef
);
234 append_to_card(ret
, "X-EVOLUTION-ASSISTANT:%s\n",
235 MAPI_TAG(PT_STRING8
, PR_ASSISTANT
), tnef
);
236 append_to_card(ret
, "X-EVOLUTION-OFFICE:%s\n",
237 MAPI_TAG(PT_STRING8
, PR_OFFICE_LOCATION
), tnef
);
238 append_to_card(ret
, "URL:%s\n", defguid
, COMMON_URL
, tnef
);
240 // Just line up the email addies; Evo makes an EList outta them
241 append_to_card(ret
, "EMAIL;INTERNET:%s\n", defguid
, EMAIL_1
, tnef
);
242 append_to_card(ret
, "EMAIL;INTERNET:%s\n", defguid
, EMAIL_2
, tnef
);
243 append_to_card(ret
, "EMAIL:INTERNET:%s\n", defguid
, EMAIL_3
, tnef
);
245 append_to_card(ret
, "FBURL:%s\n", defguid
, COMMON_FBURL
, tnef
);
247 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_COMPANY_NAME
));
250 ret
.append("ORG:%s", s
->c_str());
251 string
const *s2
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_DEPARTMENT_NAME
));
253 ret
.append(";%s", s2
->c_str());
257 ret
.append("ADR;QUOTED-PRINTABLE;WORK:");
259 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_POST_OFFICE_BOX
));
261 ret
.append("%s", escape_newline(s
->c_str()));
264 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_STREET_ADDRESS
));
266 ret
.append("%s", escape_newline((char *)s
->c_str()));
269 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_LOCALITY
));
271 ret
.append("%s", escape_newline((char *)s
->c_str()));
274 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_STATE_OR_PROVINCE
));
276 ret
.append("%s", escape_newline((char *)s
->c_str()));
279 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_POSTAL_CODE
));
281 ret
.append("%s", escape_newline((char *)s
->c_str()));
284 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_COUNTRY
));
286 ret
.append("%s", escape_newline((char *)s
->c_str()));
289 ret
.append("ADR;QUOTED-PRINTABLE;HOME:");
291 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_HOME_ADDRESS_POST_OFFICE_BOX
));
293 ret
.append("%s", escape_newline((char *)s
->c_str()));
296 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_HOME_ADDRESS_STREET
));
298 ret
.append("%s", escape_newline((char *)s
->c_str()));
301 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_HOME_ADDRESS_CITY
));
303 ret
.append("%s", escape_newline((char *)s
->c_str()));
306 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_HOME_ADDRESS_STATE_OR_PROVINCE
));
308 ret
.append("%s", escape_newline((char *)s
->c_str()));
311 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_HOME_ADDRESS_POSTAL_CODE
));
313 ret
.append("%s", escape_newline((char *)s
->c_str()));
316 s
= tnef
.get_property(MAPI_TAG(PT_STRING8
, PR_HOME_ADDRESS_COUNTRY
));
318 ret
.append("%s", escape_newline((char *)s
->c_str()));
321 ret
.append("ADR;QUOTED-PRINTABLE;OTHER:");
323 append_to_card(ret
, "%s",
324 MAPI_TAG(PT_STRING8
, PR_OTHER_ADDRESS_POST_OFFICE_BOX
), tnef
);
326 append_to_card(ret
, "%s",
327 MAPI_TAG(PT_STRING8
, PR_OTHER_ADDRESS_STREET
), tnef
);
329 append_to_card(ret
, "%s",
330 MAPI_TAG(PT_STRING8
, PR_OTHER_ADDRESS_CITY
), tnef
);
332 append_to_card(ret
, "%s",
333 MAPI_TAG(PT_STRING8
, PR_OTHER_ADDRESS_STATE_OR_PROVINCE
), tnef
);
335 append_to_card(ret
, "%s",
336 MAPI_TAG(PT_STRING8
, PR_OTHER_ADDRESS_POSTAL_CODE
), tnef
);
338 append_to_card(ret
, "%s",
339 MAPI_TAG(PT_STRING8
, PR_OTHER_ADDRESS_COUNTRY
), tnef
);
342 append_to_card(ret
, "TEL;X-EVOLUTION-TTYTDD:%s\n",
343 MAPI_TAG(PT_STRING8
, PR_TTYTDD_PHONE_NUMBER
), tnef
);
344 append_to_card(ret
, "TEL;X-EVOLUTION-TELEX:%s\n",
345 MAPI_TAG(PT_STRING8
, PR_TELEX_NUMBER
), tnef
);
346 append_to_card(ret
, "TEL;X-EVOLUTION-ASSISTANT:%s\n",
347 MAPI_TAG(PT_STRING8
, PR_ASSISTANT_TELEPHONE_NUMBER
), tnef
);
348 append_to_card(ret
, "TEL;X-EVOLUTION-CALLBACK:%s\n",
349 MAPI_TAG(PT_STRING8
, PR_CALLBACK_TELEPHONE_NUMBER
), tnef
);
350 append_to_card(ret
, "TEL;X-EVOLUTION-RADIO:%s\n",
351 MAPI_TAG(PT_STRING8
, PR_RADIO_TELEPHONE_NUMBER
), tnef
);
352 append_to_card(ret
, "TEL;PREF:%s\n",
353 MAPI_TAG(PT_STRING8
, PR_PRIMARY_TELEPHONE_NUMBER
), tnef
);
354 append_to_card(ret
, "TEL;CELL:%s\n",
355 MAPI_TAG(PT_STRING8
, PR_MOBILE_TELEPHONE_NUMBER
), tnef
);
356 append_to_card(ret
, "TEL;CAR:%s\n",
357 MAPI_TAG(PT_STRING8
, PR_CAR_TELEPHONE_NUMBER
), tnef
);
358 append_to_card(ret
, "TEL;VOICE:%s\n",
359 MAPI_TAG(PT_STRING8
, PR_OTHER_TELEPHONE_NUMBER
), tnef
);
360 append_to_card(ret
, "TEL;PAGER:%s\n",
361 MAPI_TAG(PT_STRING8
, PR_PAGER_TELEPHONE_NUMBER
), tnef
);
362 append_to_card(ret
, "TEL;ISDN:%s\n",
363 MAPI_TAG(PT_STRING8
, PR_ISDN_NUMBER
), tnef
);
364 append_to_card(ret
, "TEL;HOME:%s\n",
365 MAPI_TAG(PT_STRING8
, PR_HOME2_TELEPHONE_NUMBER
), tnef
);
366 append_to_card(ret
, "TEL;HOME;VOICE:%s\n",
367 MAPI_TAG(PT_STRING8
, PR_HOME_TELEPHONE_NUMBER
), tnef
);
368 append_to_card(ret
, "TEL;WORK:%s\n",
369 MAPI_TAG(PT_STRING8
, PR_COMPANY_MAIN_PHONE_NUMBER
), tnef
);
370 append_to_card(ret
, "TEL;WORK;VOICE:%s\n",
371 MAPI_TAG(PT_STRING8
, PR_BUSINESS2_TELEPHONE_NUMBER
), tnef
);
372 append_to_card(ret
, "TEL;PREF;FAX:%s\n",
373 MAPI_TAG(PT_STRING8
, PR_PRIMARY_FAX_NUMBER
), tnef
);
374 append_to_card(ret
, "TEL;WORK;FAX:%s\n",
375 MAPI_TAG(PT_STRING8
, PR_BUSINESS_FAX_NUMBER
), tnef
);
376 append_to_card(ret
, "TEL;HOME;FAX:%s\n",
377 MAPI_TAG(PT_STRING8
, PR_HOME_FAX_NUMBER
), tnef
);
378 append_to_card(ret
, "URL:%s\n",
379 MAPI_TAG(PT_STRING8
, PR_BUSINESS_HOME_PAGE
), tnef
);
381 s
= tnef
.get_property(MAPI_TAG(PT_SYSTIME
, PR_BIRTHDAY
));
384 Date d
= get_dateinfo(s
);
385 ret
.append("BDAY:%s-%02s-%02s\n", d
.year
, d
.month
, d
.day
);
388 s
= tnef
.get_property(MAPI_TAG(PT_SYSTIME
, PR_WEDDING_ANNIVERSARY
));
391 Date d
= get_dateinfo(s
);
392 ret
.append("X-EVOLUTION-ANNIVERSARY:%s-%02s-%02s\n",
393 d
.year
, d
.month
, d
.day
);
395 ret
.append("END:VCARD\n");
400 inline void append_to_card(WvString
&dest
, WvStringParm format
,
401 uint32_t property
, const WvTnef
&tnef
)
403 string
const *s
= tnef
.get_property(property
);
405 dest
.append(format
, s
->c_str());
408 inline void append_to_card(WvString
&dest
, WvStringParm format
,
409 const string
&guid
, uint32_t id
,
412 string
const *s
= tnef
.get_property(guid
, id
);
414 dest
.append(format
, s
->c_str());
417 inline void append_to_card(WvString
&dest
, WvStringParm format
,
418 const string
&guid
, const string
&name_str
,
421 string
const *s
= tnef
.get_property(guid
, name_str
);
423 dest
.append(format
, s
->c_str());
426 time_t get_timet(string
const *data
)
428 uint64_t d
= swap8(*(uint64_t *)(data
->data()));
430 if (MAX_OUTLOOK_TIME_S
== d
)
432 // Maximum Outlook time; return maximum UNIX time
433 return MAX_UNIX_TIME
;
436 // Subtract bias, convert from hundreds of nanoseconds to seconds
439 d
/= HUNDREDS_OF_NANOSECONDS_PER_SECOND
;
445 string
*get_outlook_time(time_t timet
)
449 if (timet
== MAX_UNIX_TIME
)
450 d
= MAX_OUTLOOK_TIME_S
;
454 // Add bias, convert from seconds to hundreds of nanoseconds
455 d
*= HUNDREDS_OF_NANOSECONDS_PER_SECOND
;
460 char *x
= (char *)(&d
);
461 string
*time_string
= new string(x
, 8);
466 string
*create_tnefdate(WvStringParm date_property
)
468 return get_outlook_time(vcaldate_to_time(date_property
, true));
471 Date
get_dateinfo(string
const *data
)
478 time_t timet
= get_timet(data
);
479 struct tm
*time
= localtime(&timet
);
481 ret
.second
= time
->tm_sec
;
482 ret
.minute
= time
->tm_min
;
483 ret
.hour
= time
->tm_hour
;
484 ret
.year
= time
->tm_year
+ 1900;
485 ret
.weekday
= time
->tm_wday
;
486 ret
.month
= time
->tm_mon
+ 1;
487 ret
.day
= time
->tm_mday
;
492 // Pretty much the same as mich's split_binary8216
493 Recurrence
get_recurrence(string
const *data
)
495 WvLog
log("get_recurrence", WvLog::Debug3
);
496 log("data = %s", data
->c_str());
505 recbuf
.put(data
->data(), data
->length());
509 ret
.recur_type
= read4u(recbuf
);
513 ret
.cycle_position
= read4u(recbuf
);
514 ret
.interval
= read4u(recbuf
);
520 ret
.relative_position
= 0;
522 if (ret
.recur_type
!= DAILY_NDAYS
)
523 ret
.day_info
= read4u(recbuf
);
524 if (ret
.recur_type
== MONTHLY_NTH_FOO_MTH_MONTH
525 || ret
.recur_type
== YEARLY_NTH_FOO_MTH_MONTH
)
526 ret
.relative_position
= read4u(recbuf
);
528 ret
.end_date_mode
= read4u(recbuf
);
529 ret
.occurrences
= read4u(recbuf
);
533 ret
.occurrences_count
= read4u(recbuf
);
535 for (unsigned int i
= 0; i
< ret
.occurrences_count
; i
++)
536 ret
.occurrence_dates
->append(new uint32_t(read4u(recbuf
)), true);
538 ret
.exceptions_count
= read4u(recbuf
);
540 for (unsigned int i
= 0; i
< ret
.exceptions_count
; i
++)
541 ret
.exceptions_dates
->append(new uint32_t(read4u(recbuf
)), true);
543 ret
.recur_start_day_minutes
= read4u(recbuf
);
544 ret
.recur_end_day_minutes
= read4u(recbuf
);
549 ret
.start_minutes
= read4u(recbuf
);
550 ret
.end_minutes
= read4u(recbuf
);
551 ret
.exceptions_details_count
= read2u(recbuf
);
553 int left
= recbuf
.used();
554 ret
.exceptions_details
.assign((const char *)recbuf
.get(left
), left
);
559 WvString
get_rrule(const Recurrence
&recur
)
561 WvString
ret("RRULE:");
563 if (0 == recur
.recur_type
)
564 return WvString::null
;
577 // recur end day in time_t and struct tm format
578 time_t end_day_timet
;
579 struct tm
*end_day_tm
;
581 // recur start day in time_t and struct tm format
582 time_t start_day_timet
= (recur
.recur_start_day_minutes
- EPOCH_DIFF_M
)*60;
583 struct tm
*start_day_tm
= gmtime(&start_day_timet
);
584 start_day_tm
->tm_year
+= 1900;
585 start_day_tm
->tm_mon
+= 1;
587 if (MAX_OUTLOOK_TIME_M
!= recur
.recur_end_day_minutes
)
588 end_day_timet
= (recur
.recur_end_day_minutes
- EPOCH_DIFF_M
)*60;
590 end_day_timet
= MAX_UNIX_TIME
;
592 end_day_tm
= gmtime(&end_day_timet
);
593 end_day_tm
->tm_year
+= 1900;
594 end_day_tm
->tm_mon
+= 1;
596 if (MAX_OUTLOOK_TIME_M
!= recur
.recur_end_day_minutes
)
597 until
= WvString("%04s%02s%02s",
600 end_day_tm
->tm_mday
);
602 switch(recur
.recur_type
)
604 // daily every n days
607 freq
= WvString("DAILY");
609 // 1440 minutes in one day
610 interval
= WvString("%s", (recur
.interval
/ 1440));
613 // daily every weekday (actually weekly)
616 freq
= WvString("WEEKLY");
617 byday
= WvString("MO,TU,WE,TH,FR");
620 // weekly every n weeks
623 freq
= WvString("WEEKLY");
624 byday
= get_byday(recur
.day_info
);
627 // monthly day n of every mth month
628 case MONTHLY_N_MTH_MONTH
:
630 freq
= WvString("MONTHLY");
631 bymonthday
= WvString("%s", recur
.day_info
);
634 // monthly the nth foo of every mth month
635 // (eg. "the second tuesday of every other month")
636 case MONTHLY_NTH_FOO_MTH_MONTH
:
638 freq
= WvString("MONTHLY");
639 byday
= get_byday(recur
.day_info
);
640 bymonth
= start_day_tm
->tm_mon
;
641 bysetpos
= recur
.relative_position
;
644 // yearly every month, day
645 // (eg. "every January 2nd")
646 case YEARLY_MONTH_DAY
:
648 freq
= WvString("YEARLY");
649 bymonthday
= WvString("%s", recur
.day_info
);
652 // yearly the nth foo of every mth month
653 // (eg. "the third Sunday of October every year")
654 case YEARLY_NTH_FOO_MTH_MONTH
:
656 freq
= WvString("YEARLY");
657 byday
= get_byday(recur
.day_info
);
658 bymonth
= start_day_tm
->tm_mon
;
659 bysetpos
= recur
.relative_position
;
663 // er, we're kinda screwed
664 return WvString::null
;
668 // shouldn't be possible, just to be sure
670 return WvString::null
;
672 // we maybe set it for daily, otherwise set it here
674 interval
= WvString("%s", recur
.interval
);
676 ret
.append("FREQ=%s", freq
);
678 if (!!until
) ret
.append(";UNTIL=%s", until
);
679 if (!!interval
) ret
.append(";INTERVAL=%s", interval
);
680 if (!!byday
) ret
.append(";BYDAY=%s", byday
);
681 if (!!bymonthday
) ret
.append(";BYMONTHDAY=%s", bymonthday
);
682 if (!!byyearday
) ret
.append(";BYYEARDAY=%s", byyearday
);
683 if (!!byweekno
) ret
.append(";BYWEEKNO=%s", byweekno
);
684 if (!!bymonth
) ret
.append(";BYMONTH=%s", bymonth
);
685 if (!!bysetpos
) ret
.append(";BYSETPOS=%s", bysetpos
);
686 if (!!wkst
) ret
.append(";WKST=%s", wkst
);
693 WvString
get_exdates(const Recurrence
&recur
)
696 WvStringList exdates
;
698 WvMap
<uint32_t, uint32_t, OpEqComp
, WvScatterHash
> exceptions_dates(32);
699 Uint32List::Iter
i(*recur
.exceptions_dates
);
701 // Do it the same way mich does; set all the exceptions_dates
702 // in a hash table, making it easy to check if a certain date
704 for (i
.rewind(); i
.next(); )
705 exceptions_dates
.set(*i
, 1, true);
707 i
= Uint32List::Iter(*recur
.occurrence_dates
);
708 Uint32List deleted_occurrences
;
710 for (i
.rewind(); i
.next(); )
712 if (!exceptions_dates
.exists(*i
))
713 deleted_occurrences
.append(new uint32_t(*i
), true);
716 i
= Uint32List::Iter(deleted_occurrences
);
718 for (i
.rewind(); i
.next(); )
720 time_t timet
= (*i
- EPOCH_DIFF_M
)*60;
721 struct tm
*time
= gmtime(&timet
);
723 time
->tm_year
+= 1900;
726 // FIXME: do exception dates actually deserve times,
727 // and not just dates? evolution doesn't seem to pay
729 exdates
.append(new WvString("%04s%02s%02s",
730 time
->tm_year
, time
->tm_mon
,
731 time
->tm_mday
), true);
734 exceptions_dates
.zap();
736 if (0 == exdates
.count())
737 return WvString::null
;
739 WvStringList::Iter
j(exdates
);
741 for (j
.rewind(); j
.next(); )
742 ret
.append("EXDATE;VALUE=DATE:%s\n", *j
);
747 WvString
get_byday(uint32_t day_info
)
749 WvStringList daylist
;
753 daylist
.append("SU");
755 daylist
.append("MO");
757 daylist
.append("TU");
759 daylist
.append("WE");
761 daylist
.append("TH");
763 daylist
.append("FR");
765 daylist
.append("SA");
767 if (!daylist
.isempty())
768 return daylist
.join(",");
770 return WvString::null
;
773 string
*pack_recur(const Recurrence
&recur
)
777 append4(0x30043004, buf
);
778 append4(recur
.recur_type
, buf
);
780 append4(recur
.cycle_position
, buf
);
781 append4(recur
.interval
, buf
);
784 if (recur
.recur_type
!= DAILY_NDAYS
)
785 append4(recur
.day_info
, buf
);
786 if (recur
.recur_type
== MONTHLY_NTH_FOO_MTH_MONTH
||
787 recur
.recur_type
== YEARLY_NTH_FOO_MTH_MONTH
)
788 append4(recur
.relative_position
, buf
);
790 append4(recur
.end_date_mode
, buf
);
791 append4(recur
.occurrences
, buf
);
793 append4(recur
.occurrences_count
, buf
);
795 Uint32List::Iter
i(*recur
.occurrence_dates
);
796 for (i
.rewind(); i
.next(); )
799 append4(recur
.exceptions_count
, buf
);
801 i
= Uint32List::Iter(*recur
.exceptions_dates
);
802 for (i
.rewind(); i
.next(); )
805 append4(recur
.recur_start_day_minutes
, buf
);
806 append4(recur
.recur_end_day_minutes
, buf
);
807 append4(0x3006, buf
);
808 append4(0x3007, buf
);
809 append4(recur
.start_minutes
, buf
);
810 append4(recur
.end_minutes
, buf
);
811 append2(recur
.exceptions_details_count
, buf
);
813 buf
.put(recur
.exceptions_details
.data(),
814 recur
.exceptions_details
.length());
816 return new string((const char *)buf
.get(buf
.used()), buf
.used());
819 time_t vcaldate_to_time(WvStringParm str
, const bool local
)
821 if (str
.len() != 8 && str
.len() != 15)
828 sscanf(str
.cstr(), "%04d%02d%02d",
829 &time
.tm_year
, &time
.tm_mon
, &time
.tm_mday
);
830 time
.tm_hour
= time
.tm_min
= time
.tm_sec
= 0;
834 sscanf(str
.cstr(), "%04d%02d%02dT%02d%02d%02d",
835 &time
.tm_year
, &time
.tm_mon
, &time
.tm_mday
,
836 &time
.tm_hour
, &time
.tm_min
, &time
.tm_sec
);
838 // adjust into tm format
839 time
.tm_wday
= 0; // FIXME: claiming info not available
840 time
.tm_yday
= 0; // FIXME: claiming info not available
841 time
.tm_year
-= 1900;
843 time
.tm_isdst
= (-1); // FIXME: claiming info not available
846 return timelocal(&time
);
848 return timegm(&time
);
851 time_t vcaldate_to_timelocal(WvStringParm str
)
853 return vcaldate_to_time(str
, true);
856 time_t vcaldate_to_timegm(WvStringParm str
)
858 return vcaldate_to_time(str
, false);
861 uint32_t time_to_minutes(time_t t
, bool local
)
865 (local
) ? time
= localtime(&t
) : time
= gmtime(&t
);
867 return time
->tm_hour
*60 + time
->tm_min
;
870 uint32_t time_to_minutes_local(time_t t
)
872 return time_to_minutes(t
, true);
875 uint32_t time_to_minutes_gm(time_t t
)
877 return time_to_minutes(t
, false);
880 uint64_t time_to_systime(time_t t
)
889 Uint32List
*only_in(const Uint32List
*a
, const Uint32List
*b
, bool in_a
)
891 if (NULL
== a
|| NULL
== b
)
894 Uint32List::Iter
i(in_a
? *a
: *b
);
895 Uint32List::Iter
j(in_a
? *b
: *a
);
897 Uint32List
*ret
= new Uint32List
;
899 for (i
.rewind(); i
.next(); )
903 for (j
.rewind(); j
.next(); )
913 ret
->append(new uint32_t(*i
), true);
919 Uint32List
*only_in_a(const Uint32List
*a
, const Uint32List
*b
)
921 return only_in(a
, b
, true);
924 Uint32List
*only_in_b(const Uint32List
*a
, const Uint32List
*b
)
926 return only_in(a
, b
, false);
929 void tnef_set_type(WvTnef
&tnef
, WvStringParm type
)
931 string
*s
= new string(type
);
932 tnef
.set_property(MAPI_TAG(PT_STRING8
, PR_MESSAGE_CLASS
), s
);
935 void tnef_set_uid(WvTnef
&tnef
, WvStringParm uid
)
937 string
*s
= new string(uid
);
938 string wvidguid
= str2guid(WVIDGUID
);
939 tnef
.set_property(wvidguid
, str2ustr("EvoId"),
943 void tnef_set_dtstart(WvTnef
&tnef
, WvStringParm date
, const bool is_recurring
)
945 string
*s
= create_tnefdate(date
);
946 tnef
.set_property(str2guid(GUID_1
), COMMON_START
,
949 tnef
.set_property(str2guid(GUID_3
), RECUR_START_DAY
,
950 new string(*s
), PT_SYSTIME
);
953 void tnef_set_dtend(WvTnef
&tnef
, WvStringParm date
, const bool is_recurring
)
955 string
*s
= create_tnefdate(date
);
956 tnef
.set_property(str2guid(GUID_1
), COMMON_END
,
960 tnef
.set_property(str2guid(GUID_3
), RECUR_END_DAY
,
961 new string(*s
), PT_SYSTIME
);
964 void tnef_set_summary(WvTnef
&tnef
, WvStringParm summary
)
966 string
*s
= new string(summary
);
967 tnef
.set_property(MAPI_TAG(PT_STRING8
, PR_SUBJECT
), s
);
970 void tnef_set_location(WvTnef
&tnef
, WvStringParm location
)
972 string
*s
= new string(location
);
973 tnef
.set_property(str2guid(GUID_1
), LOCATION
,
977 void tnef_set_description(WvTnef
&tnef
, WvStringParm description
)
979 string
*s
= new string(description
);
980 tnef
.set_property(MAPI_TAG(PT_STRING8
, PR_BODY
), s
);
983 void tnef_set_last_modified(WvTnef
&tnef
, WvStringParm date
)
985 string
*s
= create_tnefdate(date
);
986 tnef
.set_property(MAPI_TAG(PT_SYSTIME
, PR_LAST_MODIFICATION_TIME
), s
);
989 // This function takes a rrule, and exdates as input, and rewrites the
990 // recurrence inside a tnef.
991 // What makes this function particularly ugly is that you can not
992 // straighforwardly take properties out of the vcal and write them to
993 // the TNEF: some of the variables needed to rewrite the TNEF may depend on
994 // other data we've extracted from the vcal (and then modified).
995 bool tnef_rewrite_recur(WvStringParm rrule
, WvStringList
*exdates
, WvTnef
&tnef
)
998 WvString vcal_interval
;
1000 WvString vcal_byday
;
1001 WvString vcal_bymonthday
;
1002 WvString vcal_byyearday
;
1003 WvString vcal_byweekno
;
1004 WvString vcal_bymonth
;
1005 WvString vcal_bysetpos
;
1008 uint32_t recur_type
= 0;
1009 uint32_t interval
= 1;
1010 uint32_t recur_interval
= 0;
1011 uint32_t cycle_length
= 0;
1012 uint32_t pattern_type
= 0;
1013 uint32_t event_recurdays
= 0;
1014 uint32_t relative_position
= 0;
1018 WvString
recur_string("");
1019 WvLog
log("rewrite_recur", WvLog::Debug1
);
1021 // Get start + end times
1022 if (!_tnef_prop_to_timet(event_start
, tnef
, COMMON_START
) ||
1023 !_tnef_prop_to_timet(event_end
, tnef
, COMMON_END
)) {
1027 _vcal_parse_rrule_string(rrule
, vcal_freq
,
1028 vcal_interval
, vcal_until
,
1029 vcal_byday
, vcal_bymonthday
,
1030 vcal_byyearday
, vcal_byweekno
,
1031 vcal_bymonth
, vcal_bysetpos
,
1034 _vcal_get_recur_interval(vcal_interval
, recur_interval
);
1035 _vcal_get_recur_interval(vcal_interval
, interval
); // EXPERIMENTAL: shoved in to fix yearly recurrences
1037 if (!_vcal_get_recur_types(vcal_freq
, vcal_byday
, vcal_bysetpos
,
1038 recur_interval
, recur_type
, pattern_type
))
1041 _vcal_get_recur_days(vcal_byday
, event_recurdays
);
1043 if (!!vcal_bysetpos
)
1044 relative_position
= vcal_bysetpos
.num();
1046 log("event_end: %s", event_end
);
1048 _vcal_parse_freq(vcal_freq
, recur_interval
,
1049 event_start
, event_end
,
1050 event_recurdays
, interval
,
1051 cycle_length
, recur_string
,
1054 // Helper variables for calculating the start + end of the
1056 time_t recur_start_day
;
1057 uint32_t start_year
, start_month
, start_day
;
1058 // The following values are defaults in case there is no end date
1059 uint32_t recur_minutes_span
;
1060 uint32_t occurrences
= 10;
1061 uint32_t end_date_mode
= 0x2023;
1062 uint32_t recur_end_day_minutes
= 0x5ae980df;
1063 uint32_t recur_start_day_minutes
= 0;
1064 time_t recur_end_day
= 0x7fffffff;
1066 _get_recur_start_time(event_start
, recur_start_day
, recur_start_day_minutes
,
1067 start_year
, start_month
, start_day
);
1069 _get_recur_end_time(vcal_freq
, vcal_until
, event_start
, recur_start_day
, interval
, event_recurdays
,
1070 recur_start_day_minutes
,
1071 start_year
, start_month
, start_day
,
1072 recur_end_day
, end_date_mode
, recur_end_day_minutes
,
1073 recur_minutes_span
, occurrences
);
1075 uint32_t start_minutes
= time_to_minutes_local(event_start
);
1076 uint32_t end_minutes
= time_to_minutes_local(event_end
);
1078 uint32_t cycle_position
;
1080 _get_recur_cycle_position(vcal_freq
, recur_start_day_minutes
, interval
, event_start
,
1081 cycle_length
, pattern_type
, start_day
,
1082 start_month
, start_year
, cycle_position
, event_recurdays
);
1084 _update_recur_string_for_pattern(pattern_type
, interval
, start_day
, start_month
,
1085 start_year
, recur_string
);
1090 uint32_t occurrences_count
;
1091 uint32_t exceptions_count
, exceptions_details_count
; // Lengths of all the lists
1093 _get_recur_exceptions(exdates
, tnef
, recur
, occurrences_count
,
1094 exceptions_count
, exceptions_details_count
);
1096 // Set all the fields of the recurrence
1097 recur
.recur_type
= recur_type
;
1098 recur
.cycle_position
= cycle_position
;
1099 recur
.interval
= interval
;
1100 recur
.day_info
= event_recurdays
;
1101 recur
.relative_position
= relative_position
;
1102 recur
.end_date_mode
= end_date_mode
;
1103 recur
.occurrences
= occurrences
;
1104 recur
.occurrences_count
= occurrences_count
;
1105 recur
.exceptions_count
= exceptions_count
;
1106 recur
.recur_start_day_minutes
= recur_start_day_minutes
;
1107 recur
.recur_end_day_minutes
= recur_end_day_minutes
;
1108 recur
.start_minutes
= start_minutes
;
1109 recur
.end_minutes
= end_minutes
;
1110 recur
.exceptions_details_count
= exceptions_details_count
;
1112 /* Information useful for debugging, but which just gets in the way normally */
1114 log("recur_type: %s, cycle_position: %s, interval: %s, event_recurdays: %s, relative_position: %s, end_date_mode: %s, occurences: %s\n",
1115 recur_type
, cycle_position
, interval
, event_recurdays
, relative_position
, end_date_mode
, occurrences
);
1116 log("occurrences_count: %s, exceptions_count: %s, recur_start_day_minutes: %s, recur_end_day_minutes: %s, start_minutes: %s, end_minutes: %s\n",
1117 occurrences_count
, exceptions_count
, recur_start_day_minutes
, recur_end_day_minutes
, start_minutes
, end_minutes
);
1118 log("exceptions_details_count: %s\n", exceptions_details_count
);
1119 log("recur_string: %s\n", recur_string
.cstr());
1120 log("pattern_type: %s\n", pattern_type
);
1122 log("freq: %s until: %s, event_start: %s, recur_start_day: %s, interval: %s, event_recurdays: %s, recur_start_day_minutes: %s\n",
1123 vcal_freq
, vcal_until
, event_start
, recur_start_day
, interval
, event_recurdays
, recur_start_day_minutes
);
1124 log("start_year: %s, start_month: %s, start_day: %s, recur_end_day: %s, end_date_mode: %s, recur_end_day_minutes: %s\n",
1125 start_year
, start_month
, start_day
, recur_end_day
, end_date_mode
, recur_end_day_minutes
);
1126 log("recur_minutes_span: %s, occurences: %s\n", recur_minutes_span
, occurrences
);
1128 uint32_t istrue
= 1;
1130 uint64_t start_day_sys
= time_to_systime(recur_start_day
);
1131 uint64_t end_day_sys
= time_to_systime(recur_end_day
);
1133 tnef
.set_property(str2guid(GUID_1
), RECURRENCE
, pack_recur(recur
));
1134 tnef
.set_property(str2guid(GUID_1
), RECUR_PATTERN
,
1135 new string((const char *)&pattern_type
, 4), PT_LONG
);
1136 tnef
.set_property(str2guid(GUID_1
), RECUR_START_DAY
,
1137 new string((const char *)&start_day_sys
, 8), PT_SYSTIME
);
1138 tnef
.set_property(str2guid(GUID_1
), RECUR_END_DAY
,
1139 new string((const char *)&end_day_sys
, 8), PT_SYSTIME
);
1140 tnef
.set_property(str2guid(GUID_1
), RECUR_STRING
,
1141 new string(recur_string
), PT_STRING8
);
1142 tnef
.set_property(str2guid(GUID_1
), IS_RECURRING
,
1143 new string((const char *)&istrue
, 4), PT_BOOLEAN
);