Gammu internals  1.38.0
gsmcal.c
Go to the documentation of this file.
1 /* (c) 2002-2004 by Marcin Wiacek, 2005-2007 by Michal Cihar */
2 
13 #define _GNU_SOURCE
14 #include <string.h>
15 #include <time.h>
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 
20 #include <gammu-calendar.h>
21 #include <gammu-unicode.h>
22 
23 #include "../misc/misc.h"
24 #include "../debug.h"
25 #include "gsmmisc.h"
26 #include "gsmcal.h"
27 #include "../misc/coding/coding.h"
28 
29 #include "../../helper/string.h"
30 
31 void GSM_SetCalendarRecurranceRepeat(GSM_Debug_Info *di, unsigned char *rec, unsigned char *endday, GSM_CalendarEntry *entry)
32 {
33  int i;
34  int start=-1,frequency=-1,dow=-1,day=-1,month=-1,end=-1,Recurrance = 0, Repeat=0, j;
35  GSM_DateTime DT;
36  time_t t_time1,t_time2;
37 
38  rec[0] = 0;
39  rec[1] = 0;
40 
41  for (j = 0; j < entry->EntriesNum; j++) {
42  if (entry->Entries[j].EntryType == CAL_START_DATETIME) start = j;
43  if (entry->Entries[j].EntryType == CAL_REPEAT_FREQUENCY) frequency = j;
44  if (entry->Entries[j].EntryType == CAL_REPEAT_DAYOFWEEK) dow = j;
45  if (entry->Entries[j].EntryType == CAL_REPEAT_DAY) day = j;
46  if (entry->Entries[j].EntryType == CAL_REPEAT_MONTH) month = j;
47  if (entry->Entries[j].EntryType == CAL_REPEAT_STOPDATE) end = j;
48  }
49  if (start == -1) return;
50 
51  if (frequency != -1 && dow == -1 && day == -1 && month == -1) {
52  if (entry->Entries[frequency].Number == 1) {
53  /* each day */
54  Recurrance = 24;
55  }
56  }
57 
58  i = GetDayOfWeek(entry->Entries[start].Date.Year,
59  entry->Entries[start].Date.Month,
60  entry->Entries[start].Date.Day);
61 
62  if (frequency != -1 && dow != -1 && day == -1 && month == -1) {
63  if (entry->Entries[frequency].Number == 1 &&
64  entry->Entries[dow].Number == i) {
65  /* one week */
66  Recurrance = 24*7;
67  }
68  }
69  if (frequency != -1 && dow != -1 && day == -1 && month == -1) {
70  if (entry->Entries[frequency].Number == 2 &&
71  entry->Entries[dow].Number == i) {
72  /* two weeks */
73  Recurrance = 24*14;
74  }
75  }
76  if (frequency != -1 && dow == -1 && day != -1 && month == -1) {
77  if (entry->Entries[frequency].Number == 1 &&
78  entry->Entries[day].Number == entry->Entries[start].Date.Day) {
79  /* month */
80  Recurrance = 0xffff-1;
81  }
82  }
83  if (frequency != -1 && dow == -1 && day != -1 && month != -1) {
84  if (entry->Entries[frequency].Number == 1 &&
85  entry->Entries[day].Number == entry->Entries[start].Date.Day &&
86  entry->Entries[month].Number == entry->Entries[start].Date.Month) {
87  /* year */
88  Recurrance = 0xffff;
89  }
90  }
91 
92  rec[0] = Recurrance / 256;
93  rec[1] = Recurrance % 256;
94 
95  if (endday == NULL) return;
96 
97  endday[0] = 0;
98  endday[1] = 0;
99 
100  if (end == -1) return;
101 
102  t_time1 = Fill_Time_T(entry->Entries[start].Date);
103  t_time2 = Fill_Time_T(entry->Entries[end].Date);
104  if (t_time2 - t_time1 <= 0) return;
105 
106  switch (Recurrance) {
107  case 24:
108  case 24*7:
109  case 24*14:
110  Repeat = (t_time2 - t_time1) / (60*60*Recurrance) + 1;
111  break;
112  case 0xffff-1:
113  memcpy(&DT,&entry->Entries[start].Date,sizeof(GSM_DateTime));
114  while (1) {
115  if ((DT.Year == entry->Entries[end].Date.Year && DT.Month > entry->Entries[end].Date.Month) ||
116  (DT.Year > entry->Entries[end].Date.Year)) break;
117  if (DT.Month == 12) {
118  DT.Month = 1;
119  DT.Year++;
120  } else {
121  DT.Month++;
122  }
123  Repeat++;
124  }
125  break;
126  case 0xffff:
127  Repeat = entry->Entries[end].Date.Year-entry->Entries[start].Date.Year+1;
128  break;
129  }
130 
131  endday[0] = Repeat/256;
132  endday[1] = Repeat%256;
133 
134  smfprintf(di, "Repeat number: %i\n",Repeat);
135 }
136 
137 void GSM_GetCalendarRecurranceRepeat(GSM_Debug_Info *di, unsigned char *rec, unsigned char *endday, GSM_CalendarEntry *entry)
138 {
139  int Recurrance,num=-1,i;
140 
141  Recurrance = rec[0]*256 + rec[1];
142  if (Recurrance == 0) return;
143  /* dct3 and dct4: 65535 (0xffff) is 1 year */
144  if (Recurrance == 0xffff) Recurrance=24*365;
145  /* dct3: unavailable, dct4: 65534 (0xffff-1) is 30 days */
146  if (Recurrance == 0xffff-1) Recurrance=24*30;
147  smfprintf(di, "Recurrance : %i hours\n",Recurrance);
148 
149  for (i=0;i<entry->EntriesNum;i++) {
150  if (entry->Entries[i].EntryType == CAL_START_DATETIME) {
151  num = i;
152  break;
153  }
154  }
155  if (num == -1) return;
156 
157  if (Recurrance == 24 || Recurrance == 24*7 ||
158  Recurrance == 24*30 || Recurrance == 24*365) {
160  entry->Entries[entry->EntriesNum].Number = 1;
161  entry->EntriesNum++;
162  }
163  if (Recurrance == 24*14) {
165  entry->Entries[entry->EntriesNum].Number = 2;
166  entry->EntriesNum++;
167  }
168  if (Recurrance == 24*7 || Recurrance == 24*14) {
170  entry->Entries[entry->EntriesNum].Number = GetDayOfWeek(entry->Entries[num].Date.Year,
171  entry->Entries[num].Date.Month,
172  entry->Entries[num].Date.Day);
173  entry->EntriesNum++;
174  }
175  if (Recurrance == 24*30) {
176  entry->Entries[entry->EntriesNum].EntryType = CAL_REPEAT_DAY;
177  entry->Entries[entry->EntriesNum].Number = entry->Entries[num].Date.Day;
178  entry->EntriesNum++;
179  }
180  if (Recurrance == 24*365) {
181  entry->Entries[entry->EntriesNum].EntryType = CAL_REPEAT_DAY;
182  entry->Entries[entry->EntriesNum].Number = entry->Entries[num].Date.Day;
183  entry->EntriesNum++;
184  entry->Entries[entry->EntriesNum].EntryType = CAL_REPEAT_MONTH;
185  entry->Entries[entry->EntriesNum].Number = entry->Entries[num].Date.Month;
186  entry->EntriesNum++;
187  }
188  if (endday == NULL || endday[0]*256+endday[1] == 0) return;
189  smfprintf(di, "Repeat : %i times\n",endday[0]*256+endday[1]);
190  memcpy(&entry->Entries[entry->EntriesNum].Date,&entry->Entries[num].Date,sizeof(GSM_DateTime));
192  switch (Recurrance) {
193  case 24:
194  case 24*7:
195  case 24*14:
196  GetTimeDifference(60*60*Recurrance*(endday[0]*256+endday[1]-1), &entry->Entries[entry->EntriesNum].Date, TRUE, 1);
197  entry->EntriesNum++;
198  break;
199  case 24*30:
200  for (i=0;i<endday[0]*256+endday[1]-1;i++) {
201  if (entry->Entries[entry->EntriesNum].Date.Month == 12) {
202  entry->Entries[entry->EntriesNum].Date.Month = 1;
203  entry->Entries[entry->EntriesNum].Date.Year++;
204  } else {
205  entry->Entries[entry->EntriesNum].Date.Month++;
206  }
207  }
208  entry->EntriesNum++;
209  break;
210  case 24*365:
211  entry->Entries[entry->EntriesNum].Date.Year += (endday[0]*256+endday[1] - 1);
212  entry->EntriesNum++;
213  break;
214  }
215  smfprintf(di, "End Repeat Time: %04i-%02i-%02i %02i:%02i:%02i\n",
216  entry->Entries[entry->EntriesNum-1].Date.Year,
217  entry->Entries[entry->EntriesNum-1].Date.Month,
218  entry->Entries[entry->EntriesNum-1].Date.Day,
219  entry->Entries[entry->EntriesNum-1].Date.Hour,
220  entry->Entries[entry->EntriesNum-1].Date.Minute,
221  entry->Entries[entry->EntriesNum-1].Date.Second);
222 }
223 
225 {
226  gboolean Past = TRUE, Repeating = FALSE, RepeatingEnd = FALSE;
227  int i,End=-1;
228  GSM_DateTime DT;
229  char rec[20],endday[20];
230 
232  for (i = 0; i < note->EntriesNum; i++) {
233  switch (note->Entries[i].EntryType) {
234  case CAL_START_DATETIME :
235  if (note->Entries[i].Date.Year > DT.Year) Past = FALSE;
236  if (note->Entries[i].Date.Year == DT.Year &&
237  note->Entries[i].Date.Month > DT.Month) Past = FALSE;
238  if (note->Entries[i].Date.Year == DT.Year &&
239  note->Entries[i].Date.Month == DT.Month &&
240  note->Entries[i].Date.Day >= DT.Day) Past = FALSE;
241  break;
242  case CAL_REPEAT_STOPDATE:
243  End = i;
244  RepeatingEnd = TRUE;
245  break;
247  case CAL_REPEAT_DAY:
250  case CAL_REPEAT_MONTH:
252  Repeating = TRUE;
253  break;
254  default:
255  break;
256  }
257  if (!Past) break;
258  }
259  if (note->Type == GSM_CAL_BIRTHDAY) Past = FALSE;
260  GSM_SetCalendarRecurranceRepeat(NULL, rec, endday, note);
261  if (rec[0] != 0 || rec[1] != 0) {
262  if (End == -1) {
263  Past = FALSE;
264  } else {
265  if (note->Entries[End].Date.Year > DT.Year) Past = FALSE;
266  if (note->Entries[End].Date.Year == DT.Year &&
267  note->Entries[End].Date.Month > DT.Month) Past = FALSE;
268  if (note->Entries[End].Date.Year == DT.Year &&
269  note->Entries[End].Date.Month == DT.Month &&
270  note->Entries[End].Date.Day >= DT.Day) Past = FALSE;
271  }
272  }
273  if (Repeating && ! RepeatingEnd) {
274  return FALSE;
275  }
276  return Past;
277 }
278 
279 void GSM_CalendarFindDefaultTextTimeAlarmPhone(GSM_CalendarEntry *entry, int *Text, int *Time, int *Alarm, int *Phone, int *EndTime, int *Location)
280 {
281  int i;
282 
283  *Text = -1;
284  *Time = -1;
285  *Alarm = -1;
286  *Phone = -1;
287  *EndTime = -1;
288  *Location = -1;
289  for (i = 0; i < entry->EntriesNum; i++) {
290  switch (entry->Entries[i].EntryType) {
291  case CAL_START_DATETIME :
292  if (*Time == -1) *Time = i;
293  break;
294  case CAL_END_DATETIME :
295  if (*EndTime == -1) *EndTime = i;
296  break;
299  if (*Alarm == -1) *Alarm = i;
300  break;
301  case CAL_TEXT:
302  if (*Text == -1) *Text = i;
303  break;
304  case CAL_PHONE:
305  if (*Phone == -1) *Phone = i;
306  break;
307  case CAL_LOCATION:
308  if (*Location == -1) *Location = i;
309  break;
310  default:
311  break;
312  }
313  }
314 }
315 
316 
321 {
322  int dt;
323  struct tm talarm, ttime;
324  GSM_DateTime delta;
325 
326  talarm.tm_mday = Alarm->Day;
327  talarm.tm_mon = Alarm->Month-1;
328  talarm.tm_year = Alarm->Year - 1900;
329  talarm.tm_hour = Alarm->Hour;
330  talarm.tm_min = Alarm->Minute;
331  talarm.tm_sec = Alarm->Second;
332  talarm.tm_isdst = 0;
333 
334  ttime.tm_mday = Time->Day;
335  ttime.tm_mon = Time->Month-1;
336  ttime.tm_year = Time->Year - 1900;
337  ttime.tm_hour = Time->Hour;
338  ttime.tm_min = Time->Minute;
339  ttime.tm_sec = Time->Second;
340  ttime.tm_isdst = 0;
341 
342  dt = mktime(&ttime) - mktime(&talarm);
343 
344  if (dt <= 0) dt = 0;
345 
346  /* Mozilla Calendar only accepts relative times for alarm.
347  Maximum representation of time differences is in days.*/
348  delta.Year = 0;
349  delta.Month = 0;
350  delta.Day = dt / 86400 ; dt = dt - delta.Day * 86400;
351  delta.Hour = dt / 3600 ; dt = dt - delta.Hour * 3600;
352  delta.Minute = dt / 60 ; dt = dt - delta.Minute * 60;
353  delta.Second = dt;
354 
355  /* Use only one representation. If delta has minutes convert all to minutes etc.*/
356  if (delta.Minute !=0) {
357  delta.Minute = delta.Day * 24*60 + delta.Hour * 60 + delta.Minute;
358  delta.Day=0; delta.Hour=0;
359  } else if (delta.Hour !=0) {
360  delta.Hour = delta.Day * 24 + delta.Hour;
361  delta.Day=0;
362  }
363 
364  delta.Timezone = 0;
365 
366  return delta;
367 }
368 
369 
371 {
372  /* Mozilla has user defined categories. These must be converted to GSM_CAL_xxx types.
373  TODO: For now we use hardcoded conversions. Should be user configurable. */
374 
375  if (strstr(string,"MEETING")) *Type = GSM_CAL_MEETING;
376  else if (strstr(string,"REMINDER")) *Type = GSM_CAL_REMINDER;
377  else if (strstr(string,"DATE")) *Type = GSM_CAL_REMINDER; /* SE */
378  else if (strstr(string,"TRAVEL")) *Type = GSM_CAL_TRAVEL; /* SE */
379  else if (strstr(string,"VACATION")) *Type = GSM_CAL_VACATION; /* SE */
380  else if (strstr(string,"MISCELLANEOUS")) *Type = GSM_CAL_MEMO;
381  else if (strstr(string,"PHONE CALL")) *Type = GSM_CAL_CALL;
382  else if (strstr(string,"SPECIAL OCCASION")) *Type = GSM_CAL_BIRTHDAY;
383  else if (strstr(string,"ANNIVERSARY")) *Type = GSM_CAL_BIRTHDAY;
384  else if (strstr(string,"APPOINTMENT")) *Type = GSM_CAL_MEETING;
385  else if (strstr(string,"SHOPPING LIST")) *Type = GSM_CAL_SHOPPING;
386  /* These are the Nokia 6230i categories in local language. */
387  else if (strstr(string,"Erinnerung")) *Type = GSM_CAL_REMINDER;
388  else if (strstr(string,"Besprechung")) *Type = GSM_CAL_MEETING;
389  else if (strstr(string,"Anrufen")) *Type = GSM_CAL_CALL;
390  else if (strstr(string,"Geburtstag")) *Type = GSM_CAL_BIRTHDAY;
391  else if (strstr(string,"Notiz")) *Type = GSM_CAL_MEMO;
392 
393  else if (strstr(string,"Reminder")) *Type = GSM_CAL_REMINDER;
394  else if (strstr(string,"Meeting")) *Type = GSM_CAL_MEETING;
395  else if (strstr(string,"Call")) *Type = GSM_CAL_CALL;
396  else if (strstr(string,"Birthday")) *Type = GSM_CAL_BIRTHDAY;
397  else if (strstr(string,"Memo")) *Type = GSM_CAL_MEMO;
398  /* default */
399  else *Type = GSM_CAL_MEETING;
400 
401  return ERR_NONE;
402 }
403 
405 {
406  switch (Type) {
407  case GSM_CAL_CALL:
408  strcpy(string, "PHONE CALL");
409  break;
410  case GSM_CAL_MEETING:
411  strcpy(string, "MEETING");
412  break;
413  case GSM_CAL_REMINDER:
414  strcpy(string, "DATE");
415  break;
416  case GSM_CAL_TRAVEL:
417  strcpy(string, "TRAVEL");
418  break;
419  case GSM_CAL_VACATION:
420  strcpy(string, "VACATION");
421  break;
422  case GSM_CAL_BIRTHDAY:
423  strcpy(string, "ANNIVERSARY");
424  break;
425  case GSM_CAL_SHOPPING:
426  strcpy(string, "SHOPPING LIST");
427  break;
428  case GSM_CAL_MEMO:
429  default:
430  strcpy(string, "MISCELLANEOUS");
431  break;
432  }
433 
434  return ERR_NONE;
435 }
436 
441 GSM_Error GSM_EncodeVCAL_RRULE(char *Buffer, const size_t buff_len, size_t *Length, GSM_CalendarEntry *note, int TimePos UNUSED, GSM_VCalendarVersion Version)
442 {
443  int i;
444  int j;
445  const char *DaysOfWeek[8] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA", "SU"};
446  gboolean repeating = FALSE;
447  int repeat_dayofweek = -1;
448  int repeat_day = -1;
449  int repeat_dayofyear = -1;
450  int repeat_weekofmonth = -1;
451  int repeat_month = -1;
452  int repeat_count = -1;
453  int repeat_frequency = -1;
454  gboolean header;
455 #if 0
456  GSM_DateTime repeat_startdate = {0,0,0,0,0,0,0};
457 #endif
458  GSM_DateTime repeat_stopdate = {0,0,0,0,0,0,0};
459  GSM_Error error;
460 
461  /* First scan for entry, whether there is recurrence at all */
462  for (i = 0; i < note->EntriesNum; i++) {
463  switch (note->Entries[i].EntryType) {
464  /* We don't care about following here */
465  case CAL_PRIVATE:
466  case CAL_CONTACTID:
467  case CAL_START_DATETIME:
468  case CAL_END_DATETIME:
469  case CAL_LAST_MODIFIED:
472  case CAL_TEXT:
473  case CAL_DESCRIPTION:
474  case CAL_PHONE:
475  case CAL_LOCATION:
476  case CAL_LUID:
477  break;
479  repeat_dayofweek = note->Entries[i].Number;
480  repeating = TRUE;
481  break;
482  case CAL_REPEAT_DAY:
483  repeat_day = note->Entries[i].Number;
484  repeating = TRUE;
485  break;
487  repeat_dayofyear = note->Entries[i].Number;
488  repeating = TRUE;
489  break;
491  repeat_weekofmonth = note->Entries[i].Number;
492  repeating = TRUE;
493  break;
494  case CAL_REPEAT_MONTH:
495  repeat_month = note->Entries[i].Number;
496  repeating = TRUE;
497  break;
499  repeat_frequency = note->Entries[i].Number;
500  repeating = TRUE;
501  break;
502  case CAL_REPEAT_COUNT:
503  repeat_count = note->Entries[i].Number;
504  repeating = TRUE;
505  break;
507 #if 0
508  repeat_startdate = note->Entries[i].Date;
509  repeating = TRUE;
510 #endif
511  break;
512  case CAL_REPEAT_STOPDATE:
513  repeat_stopdate = note->Entries[i].Date;
514  repeating = TRUE;
515  break;
516  }
517  }
518  /* Did we found something? */
519  if (repeating) {
520  error = VC_Store(Buffer, buff_len, Length, "RRULE:");
521  if (error != ERR_NONE) return error;
522 
523  /* Safe fallback */
524  if (repeat_frequency == -1) {
525  repeat_frequency = 1;
526  }
527 
528  if ((repeat_dayofyear != -1) || (Version == Siemens_VCalendar && repeat_day != -1 && repeat_month != -1)) {
529  /* Yearly by day */
530  if (Version == Mozilla_iCalendar) {
531  error = VC_Store(Buffer, buff_len, Length, "FREQ=YEARLY");
532  if (error != ERR_NONE) return error;
533  } else {
534  error = VC_Store(Buffer, buff_len, Length, "YD%d", repeat_frequency);
535  if (error != ERR_NONE) return error;
536  }
537 
538  /* Store month numbers */
539  header = FALSE;
540  for (i = 0; i < note->EntriesNum; i++) {
541  if (note->Entries[i].EntryType == CAL_REPEAT_DAYOFYEAR) {
542  if (Version == Mozilla_iCalendar) {
543  if (!header) {
544  error = VC_Store(Buffer, buff_len, Length, ";BYYEARDAY=%d", note->Entries[i].Number);
545  if (error != ERR_NONE) return error;
546  header = TRUE;
547  } else {
548  error = VC_Store(Buffer, buff_len, Length, ",%d", note->Entries[i].Number);
549  if (error != ERR_NONE) return error;
550  }
551  } else {
552  error = VC_Store(Buffer, buff_len, Length, " %d", note->Entries[i].Number);
553  if (error != ERR_NONE) return error;
554  }
555  }
556  }
557  } else if (repeat_day != -1 && repeat_month != -1) {
558  /* Yearly by month and day */
559  if (Version == Mozilla_iCalendar) {
560  error = VC_Store(Buffer, buff_len, Length, "FREQ=YEARLY");
561  if (error != ERR_NONE) return error;
562  } else {
563  error = VC_Store(Buffer, buff_len, Length, "YM%d", repeat_frequency);
564  if (error != ERR_NONE) return error;
565  }
566 
567  /* Store month numbers */
568  header = FALSE;
569  for (i = 0; i < note->EntriesNum; i++) {
570  if (note->Entries[i].EntryType == CAL_REPEAT_MONTH) {
571  if (Version == Mozilla_iCalendar) {
572  if (!header) {
573  error = VC_Store(Buffer, buff_len, Length, ";BYMONTH=%d", note->Entries[i].Number);
574  if (error != ERR_NONE) return error;
575  header = TRUE;
576  } else {
577  error = VC_Store(Buffer, buff_len, Length, ",%d", note->Entries[i].Number);
578  if (error != ERR_NONE) return error;
579  }
580  } else {
581  error = VC_Store(Buffer, buff_len, Length, " %d", note->Entries[i].Number);
582  if (error != ERR_NONE) return error;
583  }
584  }
585  }
586  } else if (repeat_day != -1) {
587  /* Monthly by day */
588  if (Version == Mozilla_iCalendar) {
589  error = VC_Store(Buffer, buff_len, Length, "FREQ=MONTHLY");
590  if (error != ERR_NONE) return error;
591  } else {
592  error = VC_Store(Buffer, buff_len, Length, "MD%d", repeat_frequency);
593  if (error != ERR_NONE) return error;
594  }
595 
596  /* Store day numbers */
597  header = FALSE;
598  for (i = 0; i < note->EntriesNum; i++) {
599  if (note->Entries[i].EntryType == CAL_REPEAT_DAY) {
600  if (Version == Mozilla_iCalendar) {
601  if (!header) {
602  error = VC_Store(Buffer, buff_len, Length, ";BYMONTHDAY=%d", note->Entries[i].Number);
603  if (error != ERR_NONE) return error;
604  header = TRUE;
605  } else {
606  error = VC_Store(Buffer, buff_len, Length, ",%d", note->Entries[i].Number);
607  if (error != ERR_NONE) return error;
608  }
609  } else {
610  error = VC_Store(Buffer, buff_len, Length, " %d", note->Entries[i].Number);
611  if (error != ERR_NONE) return error;
612  }
613  }
614  }
615  } else if (repeat_dayofweek != -1 && repeat_weekofmonth != -1) {
616  /* Monthly by day and week */
617  if (Version == Mozilla_iCalendar) {
618  error = VC_Store(Buffer, buff_len, Length, "FREQ=MONTHLY");
619  if (error != ERR_NONE) return error;
620  } else {
621  error = VC_Store(Buffer, buff_len, Length, "MP%d", repeat_frequency);
622  if (error != ERR_NONE) return error;
623  }
624 
625  /* Store week numbers and week days */
626  if (Version != Mozilla_iCalendar) {
627  for (i = 0; i < note->EntriesNum; i++) {
628  if (note->Entries[i].EntryType == CAL_REPEAT_WEEKOFMONTH) {
629  error = VC_Store(Buffer, buff_len, Length, " %d+", note->Entries[i].Number);
630  if (error != ERR_NONE) return error;
631  for (j = 0; j < note->EntriesNum; j++) {
632  if (note->Entries[j].EntryType == CAL_REPEAT_DAYOFWEEK) {
633  error = VC_Store(Buffer, buff_len, Length, " %s", DaysOfWeek[note->Entries[j].Number]);
634  if (error != ERR_NONE) return error;
635  }
636  }
637  }
638  }
639  } else {
640  header = FALSE;
641  for (i = 0; i < note->EntriesNum; i++) {
642  if (note->Entries[i].EntryType == CAL_REPEAT_WEEKOFMONTH) {
643  if (!header) {
644  error = VC_Store(Buffer, buff_len, Length, ";BYSETPOS=%d", note->Entries[i].Number);
645  if (error != ERR_NONE) return error;
646  header = TRUE;
647  } else {
648  error = VC_Store(Buffer, buff_len, Length, ",%d", note->Entries[i].Number);
649  if (error != ERR_NONE) return error;
650  }
651  }
652  }
653  header = FALSE;
654  for (j = 0; j < note->EntriesNum; j++) {
655  if (note->Entries[j].EntryType == CAL_REPEAT_DAYOFWEEK) {
656  if (!header) {
657  error = VC_Store(Buffer, buff_len, Length, ";BYDAY=%s", DaysOfWeek[note->Entries[j].Number]);
658  if (error != ERR_NONE) return error;
659  header = TRUE;
660  } else {
661  error = VC_Store(Buffer, buff_len, Length, ",%s", DaysOfWeek[note->Entries[j].Number]);
662  if (error != ERR_NONE) return error;
663  }
664  }
665  }
666  }
667  } else if (repeat_dayofweek != -1) {
668  /* Weekly by day */
669  if (Version == Mozilla_iCalendar) {
670  error = VC_Store(Buffer, buff_len, Length, "FREQ=WEEKLY");
671  if (error != ERR_NONE) return error;
672  } else {
673  error = VC_Store(Buffer, buff_len, Length, "W%d", repeat_frequency);
674  if (error != ERR_NONE) return error;
675  }
676 
677  /* Store week days */
678  header = FALSE;
679  for (i = 0; i < note->EntriesNum; i++) {
680  if (note->Entries[i].EntryType == CAL_REPEAT_DAYOFWEEK) {
681  if (Version == Mozilla_iCalendar) {
682  if (!header) {
683  error = VC_Store(Buffer, buff_len, Length, ";BYDAY=%s", DaysOfWeek[note->Entries[i].Number]);
684  if (error != ERR_NONE) return error;
685  header = TRUE;
686  } else {
687  error = VC_Store(Buffer, buff_len, Length, ",%s", DaysOfWeek[note->Entries[i].Number]);
688  if (error != ERR_NONE) return error;
689  }
690  } else {
691  error = VC_Store(Buffer, buff_len, Length, " %s", DaysOfWeek[note->Entries[i].Number]);
692  if (error != ERR_NONE) return error;
693  }
694  }
695  }
696  } else {
697  /* Daily */
698  if (Version == Mozilla_iCalendar) {
699  error = VC_Store(Buffer, buff_len, Length, "FREQ=DAILY");
700  if (error != ERR_NONE) return error;
701  } else {
702  error = VC_Store(Buffer, buff_len, Length, "D%d", repeat_frequency);
703  if (error != ERR_NONE) return error;
704  }
705  }
706 
707  /* Store frequency */
708  if (Version == Mozilla_iCalendar && repeat_frequency > 1) {
709  error = VC_Store(Buffer, buff_len, Length, ";INTERVAL=%d", repeat_frequency);
710  if (error != ERR_NONE) return error;
711  }
712 
713  /* Store number of repetitions if available */
714  if (repeat_count != -1) {
715  if (Version == Mozilla_iCalendar) {
716  if (repeat_count > 0) {
717  error = VC_Store(Buffer, buff_len, Length, ";COUNT=%d", repeat_count);
718  if (error != ERR_NONE) return error;
719  }
720  } else {
721  error = VC_Store(Buffer, buff_len, Length, " #%d", repeat_count);
722  if (error != ERR_NONE) return error;
723  }
724  }
725 
726  /* Store end of repetition date if available */
727  if (repeat_stopdate.Day != 0) {
728  if (Version == Mozilla_iCalendar) {
729  error = VC_Store(Buffer, buff_len, Length, ";UNTIL=");
730  if (error != ERR_NONE) return error;
731  }
732  error = VC_StoreDate(Buffer, buff_len, Length, &repeat_stopdate, NULL);
733  if (error != ERR_NONE) return error;
734  } else {
735  /* Add EOL */
736  error = VC_StoreLine(Buffer, buff_len, Length, "");
737  if (error != ERR_NONE) return error;
738  }
739 
740  return ERR_NONE;
741  }
742  return ERR_EMPTY;
743 }
744 
749 {
750  int i;
751 
752  /* Loop over entries */
753  for (i=0; i < note->EntriesNum; i++) {
754  switch (note->Entries[i].EntryType) {
755  case CAL_START_DATETIME :
756  case CAL_END_DATETIME :
759  case CAL_LAST_MODIFIED:
761  case CAL_REPEAT_STOPDATE:
762  note->Entries[i].Date = GSM_AddTime(note->Entries[i].Date, *delta);
763  break;
764  case CAL_TEXT:
765  case CAL_DESCRIPTION:
766  case CAL_PHONE:
767  case CAL_LOCATION:
768  case CAL_LUID:
770  case CAL_REPEAT_DAY:
772  case CAL_REPEAT_MONTH:
775  case CAL_REPEAT_COUNT:
776  case CAL_PRIVATE:
777  case CAL_CONTACTID:
778  /* No need to care */
779  break;
780  }
781  }
782 }
783 
785 {
786  int i;
787 
788  /* Loop over entries */
789  for (i=0; i < note->EntriesNum; i++) {
790  switch (note->Entries[i].EntryType) {
791  case TODO_END_DATETIME :
792  case TODO_ALARM_DATETIME :
794  case TODO_LAST_MODIFIED:
795  case TODO_START_DATETIME:
797  note->Entries[i].Date = GSM_AddTime(note->Entries[i].Date, *delta);
798  break;
799  case TODO_TEXT:
800  case TODO_DESCRIPTION:
801  case TODO_PHONE:
802  case TODO_LOCATION:
803  case TODO_LUID:
804  case TODO_PRIVATE:
805  case TODO_COMPLETED:
806  case TODO_CONTACTID:
807  case TODO_CATEGORY:
808  /* No need to care */
809  break;
810  }
811  }
812 }
813 
814 GSM_Error GSM_EncodeVCALENDAR(char *Buffer, const size_t buff_len, size_t *Length, GSM_CalendarEntry *note, const gboolean header, const GSM_VCalendarVersion Version)
815 {
816  GSM_DateTime deltatime;
817  char dtstr[20];
818  char category[100];
819  int i, alarm_pos = -1, date_pos = -1;
820  GSM_Error error;
821 
822  /* Write header */
823  if (header) {
824  error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VCALENDAR");
825  if (error != ERR_NONE) return error;
826  error = VC_StoreLine(Buffer, buff_len, Length, "VERSION:%s", Version == Mozilla_iCalendar ? "2.0" : "1.0");
827  if (error != ERR_NONE) return error;
828  }
829  error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VEVENT");
830  if (error != ERR_NONE) return error;
831 
832  if (Version == Mozilla_iCalendar) {
833  /* Mozilla Calendar needs UIDs. http://www.innerjoin.org/iCalendar/events-and-uids.html */
834  error = VC_StoreLine(Buffer, buff_len, Length, "UID:calendar-%i",note->Location);
835  if (error != ERR_NONE) return error;
836  error = VC_StoreLine(Buffer, buff_len, Length, "STATUS:CONFIRMED");
837  if (error != ERR_NONE) return error;
838  }
839 
840  /* Store category */
841  GSM_Translate_Category_To_VCal(category, note->Type);
842  error = VC_StoreLine(Buffer, buff_len, Length, "CATEGORIES:%s", category);
843  if (error != ERR_NONE) return error;
844 
845  /* Loop over entries */
846  for (i=0; i < note->EntriesNum; i++) {
847  switch (note->Entries[i].EntryType) {
848  case CAL_START_DATETIME :
849  date_pos = i;
850  if (Version == Mozilla_iCalendar && (note->Type == GSM_CAL_MEMO || note->Type == GSM_CAL_BIRTHDAY)) {
851  error = VC_StoreDate(Buffer, buff_len, Length, &note->Entries[i].Date, "DTSTART;VALUE=DATE");
852  if (error != ERR_NONE) return error;
853  } else {
854  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "DTSTART");
855  if (error != ERR_NONE) return error;
856  }
857  break;
858  case CAL_END_DATETIME :
859  if (Version == Mozilla_iCalendar && (note->Type == GSM_CAL_MEMO || note->Type == GSM_CAL_BIRTHDAY)) {
860  error = VC_StoreDate(Buffer, buff_len, Length, &note->Entries[i].Date, "DTEND;VALUE=DATE");
861  if (error != ERR_NONE) return error;
862  } else {
863  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "DTEND");
864  if (error != ERR_NONE) return error;
865  }
866  break;
868  alarm_pos = i;
869  /* Disable alarm for birthday entries. Mozilla would generate an alarm before birth! */
870  if (Version != Mozilla_iCalendar || note->Type != GSM_CAL_BIRTHDAY) {
871  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "AALARM");
872  if (error != ERR_NONE) return error;
873  }
874  break;
876  alarm_pos = i;
877  /* Disable alarm for birthday entries. Mozilla would generate an alarm before birth! */
878  if (Version != Mozilla_iCalendar || note->Type != GSM_CAL_BIRTHDAY) {
879  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "DALARM");
880  if (error != ERR_NONE) return error;
881  }
882  break;
883  case CAL_LAST_MODIFIED:
884  if (Version == Mozilla_iCalendar && (note->Type == GSM_CAL_MEMO || note->Type == GSM_CAL_BIRTHDAY)) {
885  error = VC_StoreDate(Buffer, buff_len, Length, &note->Entries[i].Date, "LAST-MODIFIED;VALUE=DATE");
886  if (error != ERR_NONE) return error;
887  } else {
888  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "LAST-MODIFIED");
889  if (error != ERR_NONE) return error;
890  }
891  break;
892  case CAL_TEXT:
893  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "SUMMARY", Version == Mozilla_iCalendar);
894  if (error != ERR_NONE) return error;
895  break;
896  case CAL_DESCRIPTION:
897  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "DESCRIPTION", Version == Mozilla_iCalendar);
898  if (error != ERR_NONE) return error;
899  break;
900  case CAL_PHONE:
901  /* There is no specific field for phone number, use description */
902  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "DESCRIPTION", Version == Mozilla_iCalendar);
903  if (error != ERR_NONE) return error;
904  break;
905  case CAL_LOCATION:
906  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "LOCATION", Version == Mozilla_iCalendar);
907  if (error != ERR_NONE) return error;
908  break;
909  case CAL_LUID:
910  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "X-IRMC-LUID", Version == Mozilla_iCalendar);
911  if (error != ERR_NONE) return error;
912  break;
914  case CAL_REPEAT_DAY:
916  case CAL_REPEAT_MONTH:
919  case CAL_REPEAT_STOPDATE:
921  case CAL_REPEAT_COUNT:
922  /* Handled later */
923  break;
924  case CAL_PRIVATE:
925  if (note->Entries[i].Number == 0) {
926  error = VC_StoreLine(Buffer, buff_len, Length, "CLASS:PUBLIC");
927  if (error != ERR_NONE) return error;
928  } else {
929  error = VC_StoreLine(Buffer, buff_len, Length, "CLASS:PRIVATE");
930  if (error != ERR_NONE) return error;
931  }
932  break;
933  case CAL_CONTACTID:
934  /* Not supported */
935  break;
936  }
937  }
938 
939  /* Handle recurrance */
940  if (note->Type == GSM_CAL_BIRTHDAY) {
941  if (Version == Mozilla_iCalendar) {
942  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-RECUR-DEFAULT-UNITS:years");
943  if (error != ERR_NONE) return error;
944  } else if (Version == Siemens_VCalendar) {
945  error = VC_StoreLine(Buffer, buff_len, Length, "RRULE:YD1");
946  if (error != ERR_NONE) return error;
947  } else {
948  error = VC_StoreLine(Buffer, buff_len, Length, "RRULE:YM1");
949  if (error != ERR_NONE) return error;
950  }
951  } else {
952  error = GSM_EncodeVCAL_RRULE(Buffer, buff_len, Length, note, date_pos, Version);
953  if (error != ERR_NONE && error != ERR_EMPTY) return error;
954  }
955 
956  /* Include mozilla specific alarm encoding */
957  if (Version == Mozilla_iCalendar && alarm_pos != -1 && date_pos != -1) {
958  deltatime = VCALTimeDiff(&note->Entries[alarm_pos].Date, &note->Entries[date_pos].Date);
959 
960  dtstr[0]='\0';
961  if (deltatime.Minute !=0) {
962  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-ALARM-DEFAULT-UNITS:minutes");
963  if (error != ERR_NONE) return error;
964  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-ALARM-DEFAULT-LENGTH:%i", deltatime.Minute);
965  if (error != ERR_NONE) return error;
966  sprintf(dtstr,"-PT%iM",deltatime.Minute);
967  } else if (deltatime.Hour !=0) {
968  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-ALARM-DEFAULT-UNITS:hours");
969  if (error != ERR_NONE) return error;
970  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-ALARM-DEFAULT-LENGTH:%i", deltatime.Hour);
971  if (error != ERR_NONE) return error;
972  sprintf(dtstr,"-PT%iH",deltatime.Hour);
973  } else if (deltatime.Day !=0) {
974  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-ALARM-DEFAULT-UNITS:days");
975  if (error != ERR_NONE) return error;
976  error = VC_StoreLine(Buffer, buff_len, Length, "X-MOZILLA-ALARM-DEFAULT-LENGTH:%i", deltatime.Day);
977  if (error != ERR_NONE) return error;
978  sprintf(dtstr,"-P%iD",deltatime.Day);
979  }
980  if (dtstr[0] != '\0') {
981  error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VALARM");
982  if (error != ERR_NONE) return error;
983  error = VC_StoreLine(Buffer, buff_len, Length, "TRIGGER;VALUE=DURATION");
984  if (error != ERR_NONE) return error;
988  error = VC_StoreLine(Buffer, buff_len, Length, " :%s",dtstr);
989  if (error != ERR_NONE) return error;
990  error = VC_StoreLine(Buffer, buff_len, Length, "END:VALARM");
991  if (error != ERR_NONE) return error;
992  }
993  }
994 
995  error = VC_StoreLine(Buffer, buff_len, Length, "END:VEVENT");
996  if (error != ERR_NONE) return error;
997  if (header) {
998  error = VC_StoreLine(Buffer, buff_len, Length, "END:VCALENDAR");
999  if (error != ERR_NONE) return error;
1000  }
1001 
1002  return ERR_NONE;
1003 }
1004 
1005 void GSM_ToDoFindDefaultTextTimeAlarmCompleted(GSM_ToDoEntry *entry, int *Text, int *Alarm, int *Completed, int *EndTime, int *Phone)
1006 {
1007  int i;
1008 
1009  *Text = -1;
1010  *EndTime = -1;
1011  *Alarm = -1;
1012  *Completed = -1;
1013  *Phone = -1;
1014  for (i = 0; i < entry->EntriesNum; i++) {
1015  switch (entry->Entries[i].EntryType) {
1016  case TODO_END_DATETIME :
1017  if (*EndTime == -1) *EndTime = i;
1018  break;
1019  case TODO_ALARM_DATETIME :
1021  if (*Alarm == -1) *Alarm = i;
1022  break;
1023  case TODO_TEXT:
1024  if (*Text == -1) *Text = i;
1025  break;
1026  case TODO_COMPLETED:
1027  if (*Completed == -1) *Completed = i;
1028  break;
1029  case TODO_PHONE:
1030  if (*Phone == -1) *Phone = i;
1031  break;
1032  default:
1033  break;
1034  }
1035  }
1036 }
1037 
1038 GSM_Error GSM_EncodeVTODO(char *Buffer, const size_t buff_len, size_t *Length, const GSM_ToDoEntry *note, const gboolean header, const GSM_VToDoVersion Version)
1039 {
1040  char category[100];
1041  int i;
1042  GSM_Error error;
1043 
1044  /* Write header */
1045  if (header) {
1046  error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VCALENDAR");
1047  if (error != ERR_NONE) return error;
1048  error = VC_StoreLine(Buffer, buff_len, Length, "VERSION:%s", Version == Mozilla_VToDo ? "2.0" : "1.0");
1049  if (error != ERR_NONE) return error;
1050  }
1051  error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VTODO");
1052  if (error != ERR_NONE) return error;
1053 
1054  if (Version == Mozilla_VToDo) {
1055  /* Mozilla Calendar needs UIDs. http://www.innerjoin.org/iCalendar/events-and-uids.html */
1056  error = VC_StoreLine(Buffer, buff_len, Length, "UID:calendar-%i",note->Location);
1057  if (error != ERR_NONE) return error;
1058  error = VC_StoreLine(Buffer, buff_len, Length, "STATUS:CONFIRMED");
1059  if (error != ERR_NONE) return error;
1060  }
1061 
1062  if (Version == Mozilla_VToDo) {
1063  switch (note->Priority) {
1064  case GSM_Priority_None :
1065  case GSM_Priority_INVALID:
1066  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:0");
1067  if (error != ERR_NONE) return error;
1068  break;
1069  case GSM_Priority_Low :
1070  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:1");
1071  if (error != ERR_NONE) return error;
1072  break;
1073  case GSM_Priority_Medium:
1074  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:5");
1075  if (error != ERR_NONE) return error;
1076  break;
1077  case GSM_Priority_High :
1078  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:9");
1079  if (error != ERR_NONE) return error;
1080  break;
1081  }
1082  } else {
1083  switch (note->Priority) {
1084  case GSM_Priority_None :
1085  case GSM_Priority_INVALID:
1086  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:0");
1087  if (error != ERR_NONE) return error;
1088  break;
1089  case GSM_Priority_Low :
1090  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:1");
1091  if (error != ERR_NONE) return error;
1092  break;
1093  case GSM_Priority_Medium:
1094  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:2");
1095  if (error != ERR_NONE) return error;
1096  break;
1097  case GSM_Priority_High :
1098  error = VC_StoreLine(Buffer, buff_len, Length, "PRIORITY:3");
1099  if (error != ERR_NONE) return error;
1100  break;
1101  }
1102  }
1103  /* Store category */
1104  GSM_Translate_Category_To_VCal(category, note->Type);
1105  error = VC_StoreLine(Buffer, buff_len, Length, "CATEGORIES:%s", category);
1106  if (error != ERR_NONE) return error;
1107 
1108  /* Loop over entries */
1109  for (i=0; i < note->EntriesNum; i++) {
1110  switch (note->Entries[i].EntryType) {
1111  case TODO_END_DATETIME :
1112  if (note->Entries[i].Date.Year != 2037 &&
1113  note->Entries[i].Date.Month != 12 &&
1114  note->Entries[i].Date.Day != 31 &&
1115  note->Entries[i].Date.Hour != 23 &&
1116  note->Entries[i].Date.Minute != 59 ) {
1117  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "DUE");
1118  if (error != ERR_NONE) return error;
1119  }
1120  break;
1121  case TODO_ALARM_DATETIME :
1122  /* Disable alarm for birthday entries. Mozilla would generate an alarm before birth! */
1123  if (Version != Mozilla_VToDo || note->Type != GSM_CAL_BIRTHDAY) {
1124  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "AALARM");
1125  if (error != ERR_NONE) return error;
1126  }
1127  break;
1129  /* Disable alarm for birthday entries. Mozilla would generate an alarm before birth! */
1130  if (Version != Mozilla_VToDo || note->Type != GSM_CAL_BIRTHDAY) {
1131  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "DALARM");
1132  if (error != ERR_NONE) return error;
1133  }
1134  break;
1135  case TODO_START_DATETIME:
1136  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "DTSTART");
1137  if (error != ERR_NONE) return error;
1138  break;
1140  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "COMPLETED");
1141  if (error != ERR_NONE) return error;
1142  break;
1143  case TODO_LAST_MODIFIED:
1144  error = VC_StoreDateTime(Buffer, buff_len, Length, &note->Entries[i].Date, "LAST-MODIFIED");
1145  if (error != ERR_NONE) return error;
1146  break;
1147  case TODO_TEXT:
1148  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "SUMMARY", Version == Mozilla_VToDo);
1149  if (error != ERR_NONE) return error;
1150  break;
1151  case TODO_DESCRIPTION:
1152  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "DESCRIPTION", Version == Mozilla_VToDo);
1153  if (error != ERR_NONE) return error;
1154  break;
1155  case TODO_PHONE:
1156  /* There is no specific field for phone number, use description */
1157  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "DESCRIPTION", Version == Mozilla_VToDo);
1158  if (error != ERR_NONE) return error;
1159  break;
1160  case TODO_LOCATION:
1161  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "LOCATION", Version == Mozilla_VToDo);
1162  if (error != ERR_NONE) return error;
1163  break;
1164  case TODO_LUID:
1165  error = VC_StoreText(Buffer, buff_len, Length, note->Entries[i].Text, "X-IRMC-LUID", Version == Mozilla_VToDo);
1166  if (error != ERR_NONE) return error;
1167  break;
1168  case TODO_PRIVATE:
1169  if (note->Entries[i].Number == 0) {
1170  error = VC_StoreLine(Buffer, buff_len, Length, "CLASS:PUBLIC");
1171  if (error != ERR_NONE) return error;
1172  } else {
1173  error = VC_StoreLine(Buffer, buff_len, Length, "CLASS:PRIVATE");
1174  if (error != ERR_NONE) return error;
1175  }
1176  break;
1177  case TODO_COMPLETED:
1178  if (note->Entries[i].Number == 1) {
1179  error = VC_StoreLine(Buffer, buff_len, Length, "STATUS:COMPLETED");
1180  if (error != ERR_NONE) return error;
1181  error = VC_StoreLine(Buffer, buff_len, Length, "PERCENT-COMPLETE:100");
1182  if (error != ERR_NONE) return error;
1183  } else {
1184  error = VC_StoreLine(Buffer, buff_len, Length, "STATUS:NEEDS ACTION");
1185  if (error != ERR_NONE) return error;
1186  }
1187  break;
1188  case TODO_CONTACTID:
1189  case TODO_CATEGORY:
1190  /* Not supported */
1191  break;
1192  }
1193  }
1194 
1195  error = VC_StoreLine(Buffer, buff_len, Length, "END:VTODO");
1196  if (error != ERR_NONE) return error;
1197  if (header) {
1198  error = VC_StoreLine(Buffer, buff_len, Length, "END:VCALENDAR");
1199  if (error != ERR_NONE) return error;
1200  }
1201 
1202  return ERR_NONE;
1203 }
1204 
1205 GSM_DeltaTime ReadVCALTriggerTime (unsigned char *Buffer)
1206 {
1207  GSM_DeltaTime dt;
1208  int sign = 1;
1209  int pos = 0;
1210  int val;
1211  char unit;
1212 
1213  dt.Timezone = 0;
1214  dt.Year = 0 ; dt.Day = 0; dt.Month = 0; dt.Hour = 0; dt.Minute = 0; dt.Second = 0;
1215 
1216  if (Buffer[pos] == '+') {
1217  sign = 1; pos++;
1218  } else if (Buffer[pos] == '-') {
1219  sign = -1; pos++;
1220  }
1221  if (Buffer[pos] == 'P') pos++;
1222  if (Buffer[pos] == 'T') pos++;
1223 
1224  if ( !sscanf(Buffer+pos,"%i%c",&val,&unit) ) return dt;
1225 
1226  switch (unit) {
1227  case 'D': dt.Day = sign * val ; break;
1228  case 'H': dt.Hour = sign * val ; break;
1229  case 'M': dt.Minute = sign * val ; break;
1230  case 'S': dt.Second = sign * val ; break;
1231  }
1232 
1233  return dt;
1234 }
1235 
1239 int GSM_Make_VCAL_Lines (unsigned char *Buffer, int *lBuffer)
1240 {
1241  int src=0;
1242  int dst=0;
1243 
1244  for (src=0; src <= *lBuffer; src++) {
1245  if (Buffer[src] == '\r') src++;
1246  if (Buffer[src] == '\n') {
1247  if (Buffer[src+1] == ' ' && Buffer[src+2] == ':' ) src = src + 2;
1248  if (Buffer[src+1] == ' ' && Buffer[src+2] == ';' ) src = src + 2;
1249  }
1250  if (dst > src) return ERR_UNKNOWN;
1251  Buffer[dst] = Buffer[src];
1252  dst++;
1253  }
1254  *lBuffer = dst-1;
1255  return ERR_NONE;
1256 }
1257 
1261 GSM_Error GSM_DecodeVCAL_DOW(const char *Buffer, int *Output)
1262 {
1263  if (toupper((int)Buffer[0])== 'M' && toupper((int)Buffer[1]) == 'O') {
1264  *Output = 1;
1265  return ERR_NONE;
1266  } else if (toupper((int)Buffer[0])== 'T' && toupper((int)Buffer[1]) == 'U') {
1267  *Output = 2;
1268  return ERR_NONE;
1269  } else if (toupper((int)Buffer[0])== 'W' && toupper((int)Buffer[1]) == 'E') {
1270  *Output = 3;
1271  return ERR_NONE;
1272  } else if (toupper((int)Buffer[0])== 'T' && toupper((int)Buffer[1]) == 'H') {
1273  *Output = 4;
1274  return ERR_NONE;
1275  } else if (toupper((int)Buffer[0])== 'F' && toupper((int)Buffer[1]) == 'R') {
1276  *Output = 5;
1277  return ERR_NONE;
1278  } else if (toupper((int)Buffer[0])== 'S' && toupper((int)Buffer[1]) == 'A') {
1279  *Output = 6;
1280  return ERR_NONE;
1281  } else if (toupper((int)Buffer[0])== 'S' && toupper((int)Buffer[1]) == 'U') {
1282  *Output = 7;
1283  return ERR_NONE;
1284  }
1285  return ERR_UNKNOWN;
1286 }
1287 
1404 GSM_Error GSM_DecodeVCAL_RRULE(GSM_Debug_Info *di, const char *Buffer, GSM_CalendarEntry *Calendar, int TimePos)
1405 {
1406  const char *pos = Buffer;
1407  gboolean have_info;
1408 
1409 /* Skip spaces */
1410 #define NEXT_NOSPACE(terminate) \
1411  while (isspace((int)*pos) && *pos) pos++; \
1412  if (terminate && *pos == 0) return ERR_NONE;
1413 /* Skip numbers */
1414 #define NEXT_NONUMBER(terminate) \
1415  while (isdigit((int)*pos) && *pos) pos++; \
1416  if (terminate && *pos == 0) return ERR_NONE;
1417 /* Go to next char */
1418 #define NEXT_CHAR(terminate) \
1419  if (*pos) pos++; \
1420  if (terminate && *pos == 0) return ERR_UNKNOWN;
1421 /* Go to next char */
1422 #define NEXT_CHAR_NOERR(terminate) \
1423  if (*pos) pos++; \
1424  if (terminate && *pos == 0) return ERR_NONE;
1425 
1426 #define GET_DOW(type, terminate) \
1427  Calendar->Entries[Calendar->EntriesNum].EntryType = type; \
1428  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE; \
1429  if (GSM_DecodeVCAL_DOW(pos, &(Calendar->Entries[Calendar->EntriesNum].Number)) != ERR_NONE) return ERR_UNKNOWN; \
1430  Calendar->EntriesNum++; \
1431  NEXT_CHAR(1); \
1432  NEXT_CHAR_NOERR(terminate);
1433 
1434 #define GET_NUMBER(type, terminate) \
1435  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE; \
1436  Calendar->Entries[Calendar->EntriesNum].EntryType = type; \
1437  Calendar->Entries[Calendar->EntriesNum].Number = atoi(pos); \
1438  Calendar->EntriesNum++; \
1439  NEXT_NONUMBER(terminate);
1440 
1441 #define GET_FREQUENCY(terminate) \
1442  GET_NUMBER(CAL_REPEAT_FREQUENCY, terminate);
1443 
1444  /* This should not happen */
1445  if (TimePos == -1) {
1446  return ERR_UNKNOWN;
1447  }
1448 
1449  NEXT_NOSPACE(1);
1450 
1451  /* Detect primary rule type */
1452  switch (*pos) {
1453  /* Daily */
1454  case 'D':
1455  NEXT_CHAR(1);
1456  GET_FREQUENCY(1);
1457  break;
1458  /* Weekly */
1459  case 'W':
1460  NEXT_CHAR(1);
1461  GET_FREQUENCY(0);
1462  NEXT_NOSPACE(0);
1463  /* There might be now list of months, if there is none, we use date */
1464  have_info = FALSE;
1465 
1466  while (isalpha((int)*pos)) {
1467  have_info = TRUE;
1469  NEXT_NOSPACE(0);
1470  }
1471 
1472  if (!have_info) {
1473  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_DAYOFWEEK;
1474  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1475  Calendar->Entries[Calendar->EntriesNum].Number =
1476  GetDayOfWeek(
1477  Calendar->Entries[TimePos].Date.Year,
1478  Calendar->Entries[TimePos].Date.Month,
1479  Calendar->Entries[TimePos].Date.Day);
1480  if (Calendar->Entries[Calendar->EntriesNum].Number == 0) {
1481  Calendar->Entries[Calendar->EntriesNum].Number = 7;
1482  }
1483  Calendar->EntriesNum++;
1484  }
1485  break;
1486  /* Monthly */
1487  case 'M':
1488  NEXT_CHAR(1);
1489  switch (*pos) {
1490  /* Monthly by position */
1491  case 'P':
1492  NEXT_CHAR(1);
1493  GET_FREQUENCY(0);
1494  NEXT_NOSPACE(0);
1495  if (isdigit((int)*pos)) {
1497  if (*pos == '+') {
1498  pos++;
1499  } else if (*pos == '-') {
1500  pos++;
1501  smfprintf(di, "WARNING: Negative week position not supported!");
1502  }
1503  NEXT_NOSPACE(0);
1504 
1505  while (isalpha((int)*pos)) {
1507  NEXT_NOSPACE(0);
1508  }
1509  } else {
1510  /* Need to fill in info from current date */
1511  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_WEEKOFMONTH;
1512  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1513  Calendar->Entries[Calendar->EntriesNum].Number =
1515  Calendar->Entries[TimePos].Date.Year,
1516  Calendar->Entries[TimePos].Date.Month,
1517  Calendar->Entries[TimePos].Date.Day);
1518  Calendar->EntriesNum++;
1519 
1520  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_DAYOFWEEK;
1521  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1522  Calendar->Entries[Calendar->EntriesNum].Number =
1523  GetDayOfWeek(
1524  Calendar->Entries[TimePos].Date.Year,
1525  Calendar->Entries[TimePos].Date.Month,
1526  Calendar->Entries[TimePos].Date.Day);
1527  if (Calendar->Entries[Calendar->EntriesNum].Number == 0) {
1528  Calendar->Entries[Calendar->EntriesNum].Number = 7;
1529  }
1530  Calendar->EntriesNum++;
1531  }
1532  break;
1533  /* Monthly by day */
1534  case 'D':
1535  NEXT_CHAR(1);
1536  GET_FREQUENCY(0);
1537  NEXT_NOSPACE(0);
1538  if (isdigit((int)*pos)) {
1539  while (isdigit((int)*pos)) {
1541  if (*pos == '+') {
1542  pos++;
1543  } else if (*pos == '-') {
1544  pos++;
1545  smfprintf(di, "WARNING: Negative day position not supported!");
1546  }
1547  NEXT_NOSPACE(0);
1548  }
1549  } else {
1550  /* Need to fill in info from current date */
1551  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_DAY;
1552  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1553  Calendar->Entries[Calendar->EntriesNum].Number = Calendar->Entries[TimePos].Date.Day;
1554  Calendar->EntriesNum++;
1555  }
1556 
1557  break;
1558  default:
1559  smfprintf(di, "Could not decode recurrency: %s\n", pos);
1560  return ERR_UNKNOWN;
1561  }
1562  break;
1563  /* Yearly */
1564  case 'Y':
1565  NEXT_CHAR(1);
1566  switch (*pos) {
1567  /* Yearly by month */
1568  case 'M':
1569  NEXT_CHAR(1);
1570  GET_FREQUENCY(0);
1571  NEXT_NOSPACE(0);
1572  /* We need date of event */
1573  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_DAY;
1574  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1575  Calendar->Entries[Calendar->EntriesNum].Number =
1576  Calendar->Entries[TimePos].Date.Day;
1577  Calendar->EntriesNum++;
1578  /* There might be now list of months, if there is none, we use date */
1579  have_info = FALSE;
1580 
1581  while (isdigit((int)*pos)) {
1582  have_info = TRUE;
1584  NEXT_NOSPACE(0);
1585  }
1586 
1587  if (!have_info) {
1588  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_MONTH;
1589  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1590  Calendar->Entries[Calendar->EntriesNum].Number =
1591  Calendar->Entries[TimePos].Date.Month;
1592  Calendar->EntriesNum++;
1593  }
1594  break;
1595  /* Yearly by day */
1596  case 'D':
1597  NEXT_CHAR(1);
1598  GET_FREQUENCY(0);
1599  NEXT_NOSPACE(0);
1600  /* There might be now list of days, if there is none, we use date */
1601  have_info = FALSE;
1602 
1603  while (isdigit((int)*pos)) {
1604  have_info = TRUE;
1606  NEXT_NOSPACE(0);
1607  }
1608 
1609  if (!have_info) {
1610 #if 0
1611  /*
1612  * This seems to be according to specification,
1613  * however several vendors (Siemens, some web based
1614  * calendars use YD1 for simple year repeating. So
1615  * we handle this as YM1 just to be compatbile with
1616  * those.
1617  */
1618  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_DAYOFYEAR;
1619  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1620  Calendar->Entries[Calendar->EntriesNum].Number =
1621  GetDayOfYear(
1622  Calendar->Entries[TimePos].Date.Year,
1623  Calendar->Entries[TimePos].Date.Month,
1624  Calendar->Entries[TimePos].Date.Day);
1625  Calendar->EntriesNum++;
1626 #endif
1627  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_DAY;
1628  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1629  Calendar->Entries[Calendar->EntriesNum].Number =
1630  Calendar->Entries[TimePos].Date.Day;
1631  Calendar->EntriesNum++;
1632 
1633  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_MONTH;
1634  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1635  Calendar->Entries[Calendar->EntriesNum].Number =
1636  Calendar->Entries[TimePos].Date.Month;
1637  Calendar->EntriesNum++;
1638  }
1639  break;
1640  default:
1641  smfprintf(di, "Could not decode recurrency: %s\n", pos);
1642  return ERR_UNKNOWN;
1643  }
1644  break;
1645  default:
1646  smfprintf(di, "Could not decode recurrency: %s\n", pos);
1647  return ERR_UNKNOWN;
1648  }
1649 
1650  /* Go to duration */
1651  NEXT_NOSPACE(1);
1652 
1653  /* Do we have duration encoded? */
1654  if (*pos == '#') {
1655  pos++;
1656  if (*pos == 0) return ERR_UNKNOWN;
1658  }
1659 
1660  /* Go to end date */
1661  NEXT_NOSPACE(1);
1662 
1663  /* Do we have end date encoded? */
1664  if (ReadVCALDateTime(pos, &(Calendar->Entries[Calendar->EntriesNum].Date))) {
1665  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_REPEAT_STOPDATE;
1666  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1667  Calendar->EntriesNum++;
1668  }
1669 
1670  return ERR_NONE;
1671 }
1672 
1673 GSM_Error GSM_DecodeVCALENDAR_VTODO(GSM_Debug_Info *di, char *Buffer, size_t *Pos, GSM_CalendarEntry *Calendar,
1674  GSM_ToDoEntry *ToDo, GSM_VCalendarVersion CalVer, GSM_VToDoVersion ToDoVer)
1675 {
1676  unsigned char Line[2000],Buff[2000];
1677  int Level = 0;
1678  GSM_DateTime Date;
1679  GSM_DeltaTime OneHour = {0, 0, 0, 1, 0, 0, 0};
1680  GSM_DeltaTime trigger;
1681  GSM_Error error;
1682  int deltatime = 0;
1683  int dstflag = 0;
1684  gboolean is_date_only;
1685  gboolean date_only = FALSE;
1686  int lBuffer;
1687  int Time=-1;
1688  char *rrule = NULL;
1689 
1690  if (!Buffer) return ERR_EMPTY;
1691 
1692  Calendar->EntriesNum = 0;
1693  ToDo->EntriesNum = 0;
1694  ToDo->Location = 0;
1695  lBuffer = strlen(Buffer);
1696  trigger.Timezone = -999 * 3600;
1697 
1698  if (CalVer == Mozilla_iCalendar && *Pos ==0) {
1699  error = GSM_Make_VCAL_Lines (Buffer, &lBuffer);
1700  if (error != ERR_NONE) return error;
1701  }
1702 
1703  while (1) {
1704  error = MyGetLine(Buffer, Pos, Line, lBuffer, sizeof(Line), TRUE);
1705  if (error != ERR_NONE) return error;
1706  if (strlen(Line) == 0) break;
1707 
1708  switch (Level) {
1709  case 0:
1710  if (strstr(Line,"BEGIN:VEVENT")) {
1711  Calendar->Type = 0;
1712  date_only = TRUE;
1713  dstflag = 0;
1714  Time=-1;
1715  Level = 1;
1716  }
1717  if (strstr(Line,"BEGIN:VTODO")) {
1718  ToDo->Priority = GSM_Priority_None;
1719  ToDo->Type = GSM_CAL_MEMO;
1720  dstflag = 0;
1721  Time=-1;
1722  Level = 2;
1723  }
1724  break;
1725  case 1: /* Calendar note */
1726  if (strstr(Line,"END:VEVENT")) {
1727  if (Time == -1) {
1728  smfprintf(di, "vCalendar without date!\n");
1729  return ERR_UNKNOWN;
1730  }
1731  if (rrule != NULL) {
1732  if (CalVer == Mozilla_iCalendar) {
1733  /* @todo: We don't have parser for this right now */
1734  error = ERR_NONE;
1735  } else {
1736  error = GSM_DecodeVCAL_RRULE(di, rrule, Calendar, Time);
1737  }
1738  free(rrule);
1739  rrule=NULL;
1740 
1741  if (error != ERR_NONE) {
1742  return error;
1743  }
1744  }
1745  if (Calendar->EntriesNum == 0) return ERR_EMPTY;
1746 
1747  if (trigger.Timezone != -999 * 3600) {
1748  Calendar->Entries[Calendar->EntriesNum].Date = GSM_AddTime (Calendar->Entries[Time].Date, trigger);
1749  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_TONE_ALARM_DATETIME;
1750  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1751  Calendar->EntriesNum++;
1752  }
1753 
1754  if (dstflag != 0) {
1755  /*
1756  * Day saving time was active while entry was created,
1757  * add one hour to adjust it.
1758  */
1759  if (dstflag == 4) {
1760  GSM_Calendar_AdjustDate(Calendar, &OneHour);
1761  smfprintf(di, "Adjusting DST: %i\n", dstflag);
1762  } else {
1763  smfprintf(di, "Unknown DST flag: %i\n", dstflag);
1764  }
1765  }
1766 
1767  /* If event type is undefined choose appropriate type. Memos carry dates only, no times.
1768  Use Meetings for events with full date+time settings. */
1769  if (Calendar->Type == 0) {
1770  if (date_only)
1771  Calendar->Type = GSM_CAL_MEMO;
1772  else
1773  Calendar->Type = GSM_CAL_MEETING;
1774  }
1775 
1776  return ERR_NONE;
1777  }
1778 
1779  /* Read Mozilla calendar entries. Some of them will not be used here. Notably alarm time
1780  can defined in several ways. We will use the trigger value only since this is the value
1781  Mozilla calendar uses when importing ics-files. */
1782  if (ReadVCALText(Line, "UID", Buff, CalVer == Mozilla_iCalendar, NULL)) {
1783  /* Any use for UIDs? */
1784  break;
1785  }
1786 
1787  if (strstr(Line,"X-MOZILLA-ALARM-DEFAULT-LENGTH:")) {
1788  if (ReadVCALInt(Line, "X-MOZILLA-ALARM-DEFAULT-LENGTH", &deltatime)) {
1789  break;
1790  }
1791  }
1792 
1793  if (strstr(Line,"BEGIN:VALARM")) {
1794  error = MyGetLine(Buffer, Pos, Line, lBuffer, sizeof(Line), TRUE);
1795  if (error != ERR_NONE) return error;
1796  if (strlen(Line) == 0) break;
1797  if (ReadVCALText(Line, "TRIGGER;VALUE=DURATION", Buff, CalVer == Mozilla_iCalendar, NULL)) {
1798  trigger = ReadVCALTriggerTime(DecodeUnicodeString(Buff));
1799  break;
1800  }
1801  }
1802 
1803  /* Event type. Must be set correctly to let phone calendar work as expected. For example
1804  without GSM_CAL_MEETING the time part of an event date/time will be dropped. */
1805  if (strstr(Line,"CATEGORIES:")) {
1806  GSM_Translate_Category_From_VCal(Line + 11, &Calendar->Type);
1807  break;
1808  }
1809 
1810  if (strstr(Line,"RRULE:")) {
1811  if (rrule == NULL) {
1812  rrule = strdup(Line + 6);
1813  } else {
1814  smfprintf(di, "Ignoring second recurrence: %s\n", Line);
1815  }
1816  break;
1817  }
1818 
1819  if ((ReadVCALText(Line, "SUMMARY", Buff, CalVer == Mozilla_iCalendar, NULL))) {
1820  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_TEXT;
1821  CopyUnicodeString(Calendar->Entries[Calendar->EntriesNum].Text, Buff);
1822  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1823  Calendar->EntriesNum++;
1824  }
1825  if ((ReadVCALText(Line, "DESCRIPTION", Buff, CalVer == Mozilla_iCalendar, NULL))) {
1826  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_DESCRIPTION;
1827  CopyUnicodeString(Calendar->Entries[Calendar->EntriesNum].Text, Buff);
1828  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1829  Calendar->EntriesNum++;
1830  }
1831  if (ReadVCALText(Line, "LOCATION", Buff, CalVer == Mozilla_iCalendar, NULL)) {
1832  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_LOCATION;
1833  CopyUnicodeString(Calendar->Entries[Calendar->EntriesNum].Text, Buff);
1834  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1835  Calendar->EntriesNum++;
1836  }
1837  if ((ReadVCALText(Line, "X-IRMC-LUID", Buff, CalVer == Mozilla_iCalendar, NULL))) {
1838  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_LUID;
1839  CopyUnicodeString(Calendar->Entries[Calendar->EntriesNum].Text, Buff);
1840  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1841  Calendar->EntriesNum++;
1842  }
1843  if ((ReadVCALText(Line, "CLASS", Buff, CalVer == Mozilla_iCalendar, NULL))) {
1844  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_PRIVATE;
1845  if (mywstrncasecmp(Buff, "\0P\0U\0B\0L\0I\0C\0\0", 0)) {
1846  Calendar->Entries[Calendar->EntriesNum].Number = 0;
1847  } else {
1848  Calendar->Entries[Calendar->EntriesNum].Number = 1;
1849  }
1850  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1851  Calendar->EntriesNum++;
1852  }
1853  if (ReadVCALDate(Line, "DTSTART", &Date, &is_date_only)) {
1854  Calendar->Entries[Calendar->EntriesNum].Date = Date;
1855  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_START_DATETIME;
1856  Time = Calendar->EntriesNum;
1857  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1858  Calendar->EntriesNum++;
1859  if (!is_date_only) date_only = FALSE;
1860  }
1861  if (ReadVCALDate(Line, "DTEND", &Date, &is_date_only)) {
1862  Calendar->Entries[Calendar->EntriesNum].Date = Date;
1863  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_END_DATETIME;
1864  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1865  Calendar->EntriesNum++;
1866  if (!is_date_only) date_only = FALSE;
1867  }
1868  if (ReadVCALDate(Line, "DALARM", &Date, &is_date_only)) {
1869  Calendar->Entries[Calendar->EntriesNum].Date = Date;
1870  if (CalVer == Siemens_VCalendar) {
1871  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_TONE_ALARM_DATETIME;
1872  } else {
1873  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_SILENT_ALARM_DATETIME;
1874  }
1875  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1876  Calendar->EntriesNum++;
1877  }
1878  if (ReadVCALDate(Line, "AALARM", &Date, &is_date_only)) {
1879  Calendar->Entries[Calendar->EntriesNum].Date = Date;
1880  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_TONE_ALARM_DATETIME;
1881  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1882  Calendar->EntriesNum++;
1883  }
1884  if (ReadVCALDate(Line, "LAST-MODIFIED", &Date, &is_date_only)) {
1885  Calendar->Entries[Calendar->EntriesNum].Date = Date;
1886  Calendar->Entries[Calendar->EntriesNum].EntryType = CAL_LAST_MODIFIED;
1887  Calendar->Entries[Calendar->EntriesNum].AddError = ERR_NONE;
1888  Calendar->EntriesNum++;
1889  }
1890  if (strstr(Line,"X-SONYERICSSON-DST:")) {
1891  if (ReadVCALInt(Line, "X-SONYERICSSON-DST", &dstflag)) {
1892  break;
1893  }
1894  }
1895  break;
1896 
1897  case 2: /* ToDo note */
1898  if (strstr(Line,"END:VTODO")) {
1899  if (ToDo->EntriesNum == 0) return ERR_EMPTY;
1900 
1901  if (dstflag != 0) {
1902  /*
1903  * Day saving time was active while entry was created,
1904  * add one hour to adjust it.
1905  */
1906  if (dstflag == 4) {
1907  GSM_ToDo_AdjustDate(ToDo, &OneHour);
1908  smfprintf(di, "Adjusting DST: %i\n", dstflag);
1909  } else {
1910  smfprintf(di, "Unknown DST flag: %i\n", dstflag);
1911  }
1912  }
1913 
1914  return ERR_NONE;
1915  }
1916 
1917  if (strstr(Line,"CATEGORIES:")) {
1918  GSM_Translate_Category_From_VCal(Line+11, &ToDo->Type);
1919  }
1920 
1921  if (ReadVCALText(Line, "UID", Buff, ToDoVer == Mozilla_VToDo, NULL)){
1922  /* Any use for UIDs? */
1923  break;
1924  }
1925 
1926  if (ReadVCALInt(Line, "X-MOZILLA-ALARM-DEFAULT-LENGTH", &deltatime)) {
1927  break;
1928  }
1929  if (ReadVCALInt(Line, "X-SONYERICSSON-DST", &dstflag)) {
1930  break;
1931  }
1932 
1933  if (ReadVCALDate(Line, "DUE", &Date, &is_date_only)) {
1934  if (ToDo->Entries[ToDo->EntriesNum].Date.Year != 2037 &&
1935  ToDo->Entries[ToDo->EntriesNum].Date.Month != 12 &&
1936  ToDo->Entries[ToDo->EntriesNum].Date.Day != 31 &&
1937  ToDo->Entries[ToDo->EntriesNum].Date.Hour != 23 &&
1938  ToDo->Entries[ToDo->EntriesNum].Date.Minute != 59 ) {
1939  ToDo->Entries[ToDo->EntriesNum].Date = Date;
1941  ToDo->EntriesNum++;
1942  }
1943  }
1944  if (ReadVCALDate(Line, "COMLETED", &Date, &is_date_only)) {
1945  ToDo->Entries[ToDo->EntriesNum].Date = Date;
1947  ToDo->EntriesNum++;
1948  }
1949  if (ReadVCALDate(Line, "DTSTART", &Date, &is_date_only)) {
1950  ToDo->Entries[ToDo->EntriesNum].Date = Date;
1952  ToDo->EntriesNum++;
1953  }
1954  if (ReadVCALDate(Line, "DALARM", &Date, &is_date_only)) {
1955  ToDo->Entries[ToDo->EntriesNum].Date = Date;
1957  ToDo->EntriesNum++;
1958  }
1959  if (ReadVCALDate(Line, "LAST-MODIFIED", &Date, &is_date_only)) {
1960  ToDo->Entries[ToDo->EntriesNum].Date = Date;
1962  ToDo->EntriesNum++;
1963  }
1964  if (ReadVCALDate(Line, "AALARM", &Date, &is_date_only)) {
1965  ToDo->Entries[ToDo->EntriesNum].Date = Date;
1967  ToDo->EntriesNum++;
1968  }
1969 
1970  if ((ReadVCALText(Line, "SUMMARY", Buff, ToDoVer == Mozilla_VToDo, NULL))) {
1971  ToDo->Entries[ToDo->EntriesNum].EntryType = TODO_TEXT;
1972  CopyUnicodeString(ToDo->Entries[ToDo->EntriesNum].Text, Buff);
1973  ToDo->EntriesNum++;
1974  }
1975  if ((ReadVCALText(Line, "DESCRIPTION", Buff, ToDoVer == Mozilla_VToDo, NULL))) {
1977  CopyUnicodeString(ToDo->Entries[ToDo->EntriesNum].Text, Buff);
1978  ToDo->EntriesNum++;
1979  }
1980  if ((ReadVCALText(Line, "LOCATION", Buff, ToDoVer == Mozilla_VToDo, NULL))) {
1981  ToDo->Entries[ToDo->EntriesNum].EntryType = TODO_LOCATION;
1982  CopyUnicodeString(ToDo->Entries[ToDo->EntriesNum].Text, Buff);
1983  ToDo->EntriesNum++;
1984  }
1985  if (ReadVCALText(Line, "PRIORITY", Buff, ToDoVer == Mozilla_VToDo, NULL)) {
1986  if (ToDoVer == Mozilla_VToDo) {
1987  if (atoi(DecodeUnicodeString(Buff))>=9) ToDo->Priority = GSM_Priority_High;
1988  else if (atoi(DecodeUnicodeString(Buff))>=5) ToDo->Priority = GSM_Priority_Medium;
1989  else if (atoi(DecodeUnicodeString(Buff))>=1) ToDo->Priority = GSM_Priority_Low;
1990  else ToDo->Priority = GSM_Priority_None;
1991  } else {
1992  if (atoi(DecodeUnicodeString(Buff))==3) ToDo->Priority = GSM_Priority_Low;
1993  else if (atoi(DecodeUnicodeString(Buff))==2) ToDo->Priority = GSM_Priority_Medium;
1994  else if (atoi(DecodeUnicodeString(Buff))==1) ToDo->Priority = GSM_Priority_High;
1995  else ToDo->Priority = GSM_Priority_None;
1996  }
1997  }
1998  if (strstr(Line,"STATUS:COMPLETED")) {
1999  ToDo->Entries[ToDo->EntriesNum].EntryType = TODO_COMPLETED;
2000  ToDo->Entries[ToDo->EntriesNum].Number = 1;
2001  ToDo->EntriesNum++;
2002  }
2003  if (strstr(Line,"STATUS:NEEDS ACTION")) {
2004  ToDo->Entries[ToDo->EntriesNum].EntryType = TODO_COMPLETED;
2005  ToDo->Entries[ToDo->EntriesNum].Number = 0;
2006  ToDo->EntriesNum++;
2007  }
2008  if ((ReadVCALText(Line, "X-IRMC-LUID", Buff, ToDoVer == Mozilla_VToDo, NULL))) {
2009  ToDo->Entries[ToDo->EntriesNum].EntryType = TODO_LUID;
2010  CopyUnicodeString(ToDo->Entries[ToDo->EntriesNum].Text, Buff);
2011  ToDo->EntriesNum++;
2012  }
2013  if ((ReadVCALText(Line, "CLASS", Buff, ToDoVer == Mozilla_VToDo, NULL))) {
2014  ToDo->Entries[ToDo->EntriesNum].EntryType = TODO_PRIVATE;
2015  if (mywstrncasecmp(Buff, "\0P\0U\0B\0L\0I\0C\0", 0)) {
2016  ToDo->Entries[ToDo->EntriesNum].Number = 0;
2017  } else {
2018  ToDo->Entries[ToDo->EntriesNum].Number = 1;
2019  }
2020  ToDo->EntriesNum++;
2021  }
2022  break;
2023  }
2024  }
2025 
2026  if (Calendar->EntriesNum == 0 && ToDo->EntriesNum == 0) return ERR_EMPTY;
2027  return ERR_NONE;
2028 }
2029 
2030 GSM_Error GSM_DecodeVNOTE(char *Buffer, size_t *Pos, GSM_NoteEntry *Note)
2031 {
2032  unsigned char Line[2000],Buff[2000];
2033  int Level = 0;
2034  GSM_Error error;
2035  gboolean empty = TRUE;
2036 
2037  Note->Text[0] = 0;
2038  Note->Text[1] = 0;
2039 
2040  while (1) {
2041  error = MyGetLine(Buffer, Pos, Line, strlen(Buffer), sizeof(Line), TRUE);
2042  if (error != ERR_NONE) return error;
2043  if (strlen(Line) == 0) break;
2044  switch (Level) {
2045  case 0:
2046  if (strstr(Line,"BEGIN:VNOTE")) Level = 1;
2047  empty = TRUE;
2048  break;
2049  case 1:
2050  if (strstr(Line,"END:VNOTE")) {
2051  if (UnicodeLength(Note->Text) == 0) return ERR_EMPTY;
2052  return ERR_NONE;
2053  }
2054  if (ReadVCALText(Line, "BODY", Buff, FALSE, NULL)) {
2055  CopyUnicodeString(Note->Text, Buff);
2056  empty = FALSE;
2057  }
2058  break;
2059  }
2060  }
2061  if (empty) return ERR_EMPTY;
2062 
2063  return ERR_NONE;
2064 }
2065 
2066 GSM_Error GSM_EncodeVNTFile(char *Buffer, const size_t buff_len, size_t *Length, GSM_NoteEntry *Note)
2067 {
2068  GSM_Error error;
2069 
2070  error = VC_StoreLine(Buffer, buff_len, Length, "BEGIN:VNOTE");
2071  if (error != ERR_NONE) return error;
2072  error = VC_StoreLine(Buffer, buff_len, Length, "VERSION:1.1");
2073  if (error != ERR_NONE) return error;
2074  error = VC_StoreText(Buffer, buff_len, Length, Note->Text, "BODY", FALSE);
2075  if (error != ERR_NONE) return error;
2076  error = VC_StoreLine(Buffer, buff_len, Length, "END:VNOTE");
2077  if (error != ERR_NONE) return error;
2078 
2079  return ERR_NONE;
2080 }
2083 /* How should editor hadle tabs in this file? Add editor commands here.
2084  * vim: noexpandtab sw=8 ts=8 sts=8:
2085  */
GSM_Error GSM_EncodeVCALENDAR(char *Buffer, const size_t buff_len, size_t *Length, GSM_CalendarEntry *note, const gboolean header, const GSM_VCalendarVersion Version)
Definition: gsmcal.c:814
void GSM_ToDoFindDefaultTextTimeAlarmCompleted(GSM_ToDoEntry *entry, int *Text, int *Alarm, int *Completed, int *EndTime, int *Phone)
Definition: gsmcal.c:1005
int GSM_Make_VCAL_Lines(unsigned char *Buffer, int *lBuffer)
Definition: gsmcal.c:1239
char * DecodeUnicodeString(const unsigned char *src)
Definition: coding.c:245
void CopyUnicodeString(unsigned char *Dest, const unsigned char *Source)
Definition: coding.c:1192
#define GET_FREQUENCY(terminate)
GSM_ToDo_Priority Priority
GSM_Error GSM_DecodeVCAL_DOW(const char *Buffer, int *Output)
Definition: gsmcal.c:1261
GSM_VCalendarVersion
unsigned int Number
int GetWeekOfMonth(unsigned int year, unsigned int month, unsigned int day)
Definition: misc.c:105
GSM_Error VC_StoreDateTime(char *Buffer, const size_t buff_len, size_t *Pos, const GSM_DateTime *Date, const char *Start)
Definition: gsmmisc.c:224
GSM_CalendarNoteType Type
GSM_VToDoVersion
void GetTimeDifference(unsigned long diff, GSM_DateTime *DT, gboolean Plus, int multi)
Definition: misc.c:247
GSM_Error VC_StoreText(char *Buffer, const size_t buff_len, size_t *Pos, const unsigned char *Text, const char *Start, const gboolean UTF8)
Definition: gsmmisc.c:360
GSM_Error
Definition: gammu-error.h:23
int GetDayOfYear(unsigned int year, unsigned int month, unsigned int day)
Definition: misc.c:93
gboolean mywstrncasecmp(unsigned const char *a, unsigned const char *b, int num)
Definition: coding.c:1437
#define NEXT_NOSPACE(terminate)
void GSM_Calendar_AdjustDate(GSM_CalendarEntry *note, GSM_DeltaTime *delta)
Definition: gsmcal.c:748
int GetDayOfWeek(unsigned int year, unsigned int month, unsigned int day)
Definition: misc.c:117
size_t UnicodeLength(const unsigned char *str)
Definition: coding.c:186
gboolean ReadVCALDate(char *Buffer, const char *Start, GSM_DateTime *Date, gboolean *is_date_only)
Definition: gsmmisc.c:335
void GSM_SetCalendarRecurranceRepeat(GSM_Debug_Info *di, unsigned char *rec, unsigned char *endday, GSM_CalendarEntry *entry)
Definition: gsmcal.c:31
GSM_Error VC_StoreLine(char *Buffer, const size_t buff_len, size_t *Pos, const char *format,...)
Definition: gsmmisc.c:187
gboolean GSM_IsCalendarNoteFromThePast(GSM_CalendarEntry *note)
Definition: gsmcal.c:224
GSM_ToDoType EntryType
void GSM_GetCalendarRecurranceRepeat(GSM_Debug_Info *di, unsigned char *rec, unsigned char *endday, GSM_CalendarEntry *entry)
Definition: gsmcal.c:137
gboolean ReadVCALDateTime(const char *Buffer, GSM_DateTime *dt)
Definition: gsmmisc.c:254
void GSM_CalendarFindDefaultTextTimeAlarmPhone(GSM_CalendarEntry *entry, int *Text, int *Time, int *Alarm, int *Phone, int *EndTime, int *Location)
Definition: gsmcal.c:279
#define GET_NUMBER(type, terminate)
GSM_Error VC_Store(char *Buffer, const size_t buff_len, size_t *Pos, const char *format,...)
Definition: gsmmisc.c:208
char Text[(GSM_MAX_NOTE_TEXT_LENGTH+1) *2]
int gboolean
Definition: gammu-types.h:23
gboolean ReadVCALInt(char *Buffer, const char *Start, int *Value)
Definition: gsmmisc.c:313
GSM_Error GSM_EncodeVCAL_RRULE(char *Buffer, const size_t buff_len, size_t *Length, GSM_CalendarEntry *note, int TimePos UNUSED, GSM_VCalendarVersion Version)
Definition: gsmcal.c:441
GSM_CalendarNoteType Type
#define NEXT_CHAR(terminate)
#define FALSE
Definition: gammu-types.h:25
GSM_SubToDoEntry Entries[GSM_TODO_ENTRIES]
GSM_DateTime VCALTimeDiff(GSM_DateTime *Alarm, GSM_DateTime *Time)
Definition: gsmcal.c:320
GSM_Error GSM_EncodeVTODO(char *Buffer, const size_t buff_len, size_t *Length, const GSM_ToDoEntry *note, const gboolean header, const GSM_VToDoVersion Version)
Definition: gsmcal.c:1038
GSM_Error GSM_DecodeVCALENDAR_VTODO(GSM_Debug_Info *di, char *Buffer, size_t *Pos, GSM_CalendarEntry *Calendar, GSM_ToDoEntry *ToDo, GSM_VCalendarVersion CalVer, GSM_VToDoVersion ToDoVer)
Definition: gsmcal.c:1673
GSM_CalendarType EntryType
#define GET_DOW(type, terminate)
GSM_Error GSM_EncodeVNTFile(char *Buffer, const size_t buff_len, size_t *Length, GSM_NoteEntry *Note)
Definition: gsmcal.c:2066
void GSM_ToDo_AdjustDate(GSM_ToDoEntry *note, GSM_DeltaTime *delta)
Definition: gsmcal.c:784
GSM_CalendarNoteType
GSM_Error GSM_DecodeVNOTE(char *Buffer, size_t *Pos, GSM_NoteEntry *Note)
Definition: gsmcal.c:2030
gboolean ReadVCALText(char *Buffer, const char *Start, unsigned char *Value, const gboolean UTF8, GSM_EntryLocation *location)
Definition: gsmmisc.c:468
GSM_SubCalendarEntry Entries[GSM_CALENDAR_ENTRIES]
void GSM_GetCurrentDateTime(GSM_DateTime *Date)
Definition: misc.c:184
time_t Fill_Time_T(GSM_DateTime DT)
Definition: misc.c:189
int smfprintf(GSM_Debug_Info *d, const char *format,...)
Definition: debug.c:240
GSM_DeltaTime ReadVCALTriggerTime(unsigned char *Buffer)
Definition: gsmcal.c:1205
GSM_DateTime GSM_AddTime(GSM_DateTime DT, GSM_DeltaTime delta)
Definition: misc.c:223
GSM_Error GSM_Translate_Category_To_VCal(char *string, const GSM_CalendarNoteType Type)
Definition: gsmcal.c:404
GSM_Error GSM_DecodeVCAL_RRULE(GSM_Debug_Info *di, const char *Buffer, GSM_CalendarEntry *Calendar, int TimePos)
Definition: gsmcal.c:1404
#define TRUE
Definition: gammu-types.h:28
unsigned char Text[(GSM_MAX_TODO_TEXT_LENGTH+1) *2]
#define UNUSED
Definition: gammu-misc.h:105
GSM_Error MyGetLine(char *Buffer, size_t *Pos, char *OutBuffer, size_t MaxLen, size_t MaxOutLen, gboolean MergeLines)
Definition: coding.c:1580
GSM_DateTime Date
GSM_Error VC_StoreDate(char *Buffer, const size_t buff_len, size_t *Pos, const GSM_DateTime *Date, const char *Start)
Definition: gsmmisc.c:240
unsigned char Text[(GSM_MAX_CALENDAR_TEXT_LENGTH+1) *2]
GSM_Error GSM_Translate_Category_From_VCal(const char *string, GSM_CalendarNoteType *Type)
Definition: gsmcal.c:370