l10n: Updates to Portuguese (Brazilian) (pt_BR) translation
[siplcs.git] / src / core / sipe-cal.c
blob01af35385e2a5d11331a91e65a1f4d4a97e4ad00
1 /**
2 * @file sipe-cal.c
4 * pidgin-sipe
6 * Copyright (C) 2009 pier11 <pier11@operamail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
32 #include <glib.h>
34 #include "sip-sec.h"
35 #include "sipe-backend.h"
36 #include "sipe-cal.h"
37 #include "sipe-nls.h"
38 #include "sipe-utils.h"
39 #include "sipe-xml.h"
40 #include "sipe.h"
42 #define TIME_NULL (time_t)-1
43 #define IS(time) (time != TIME_NULL)
46 http://msdn.microsoft.com/en-us/library/aa565001.aspx
48 <?xml version="1.0"?>
49 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
50 <TimeZone>
51 <Bias>480</Bias>
52 <StandardTime>
53 <Bias>0</Bias>
54 <Time>02:00:00</Time>
55 <DayOrder>1</DayOrder>
56 <Month>11</Month>
57 <DayOfWeek>Sunday</DayOfWeek>
58 </StandardTime>
59 <DaylightTime>
60 <Bias>-60</Bias>
61 <Time>02:00:00</Time>
62 <DayOrder>2</DayOrder>
63 <Month>3</Month>
64 <DayOfWeek>Sunday</DayOfWeek>
65 </DaylightTime>
66 </TimeZone>
67 <WorkingPeriodArray>
68 <WorkingPeriod>
69 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
70 <StartTimeInMinutes>600</StartTimeInMinutes>
71 <EndTimeInMinutes>1140</EndTimeInMinutes>
72 </WorkingPeriod>
73 </WorkingPeriodArray>
74 </WorkingHours>
76 Desc:
77 <StandardTime>
78 <Bias>int</Bias>
79 <Time>string</Time>
80 <DayOrder>short</DayOrder>
81 <Month>short</Month>
82 <DayOfWeek>Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday</DayOfWeek>
83 <Year>string</Year>
84 </StandardTime>
87 struct sipe_cal_std_dst {
88 int bias; /* Ex.: -60 */
89 gchar *time; /* hh:mm:ss, 02:00:00 */
90 int day_order; /* 1..5 */
91 int month; /* 1..12 */
92 gchar *day_of_week; /* Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday */
93 gchar *year; /* YYYY */
95 time_t switch_time;
98 struct sipe_cal_working_hours {
99 int bias; /* Ex.: 480 */
100 struct sipe_cal_std_dst std; /* StandardTime */
101 struct sipe_cal_std_dst dst; /* DaylightTime */
102 gchar *days_of_week; /* Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday separated by space */
103 int start_time; /* 0...1440 */
104 int end_time; /* 0...1440 */
106 gchar *tz; /* aggregated timezone string as in TZ environment variable.
107 Ex.: TST+8TDT+7,M3.2.0/02:00:00,M11.1.0/02:00:00 */
108 /** separate simple strings for Windows platform as the proper TZ does not work there.
109 * anyway, dynamic timezones would't work with just TZ
111 gchar *tz_std; /* Ex.: TST8 */
112 gchar *tz_dst; /* Ex.: TDT7 */
115 /* not for translation, a part of XML Schema definitions */
116 static const char *wday_names[] = {"Sunday",
117 "Monday",
118 "Tuesday",
119 "Wednesday",
120 "Thursday",
121 "Friday",
122 "Saturday"};
123 static int
124 sipe_cal_get_wday(char *wday_name)
126 int i;
128 if (!wday_name) return -1;
130 for (i = 0; i < 7; i++) {
131 if (sipe_strequal(wday_names[i], wday_name)) {
132 return i;
136 return -1;
139 void
140 sipe_cal_event_free(struct sipe_cal_event* cal_event)
142 if (!cal_event) return;
144 g_free(cal_event->subject);
145 g_free(cal_event->location);
146 g_free(cal_event);
149 char *
150 sipe_cal_event_describe(struct sipe_cal_event* cal_event)
152 GString* str = g_string_new(NULL);
153 const char *status = "";
155 switch(cal_event->cal_status) {
156 case SIPE_CAL_FREE: status = "SIPE_CAL_FREE"; break;
157 case SIPE_CAL_TENTATIVE: status = "SIPE_CAL_TENTATIVE"; break;
158 case SIPE_CAL_BUSY: status = "SIPE_CAL_BUSY"; break;
159 case SIPE_CAL_OOF: status = "SIPE_CAL_OOF"; break;
160 case SIPE_CAL_NO_DATA: status = "SIPE_CAL_NO_DATA"; break;
163 g_string_append_printf(str, "\t%s: %s", "start_time",
164 IS(cal_event->start_time) ? asctime(localtime(&cal_event->start_time)) : "\n");
165 g_string_append_printf(str, "\t%s: %s", "end_time ",
166 IS(cal_event->end_time) ? asctime(localtime(&cal_event->end_time)) : "\n");
167 g_string_append_printf(str, "\t%s: %s\n", "cal_status", status);
168 g_string_append_printf(str, "\t%s: %s\n", "subject ", cal_event->subject ? cal_event->subject : "");
169 g_string_append_printf(str, "\t%s: %s\n", "location ", cal_event->location ? cal_event->location : "");
170 g_string_append_printf(str, "\t%s: %s\n", "is_meeting", cal_event->is_meeting ? "TRUE" : "FALSE");
172 return g_string_free(str, FALSE);
175 char *
176 sipe_cal_event_hash(struct sipe_cal_event* event)
178 /* no end_time as it dos not get published */
179 /* no cal_status as it can change on publication */
180 return g_strdup_printf("<%d><%s><%s><%d>",
181 (int)event->start_time,
182 event->subject ? event->subject : "",
183 event->location ? event->location : "",
184 event->is_meeting);
187 #define ENVIRONMENT_TIMEZONE "TZ"
189 static gchar *
190 sipe_switch_tz(const char *tz)
192 gchar *tz_orig;
194 tz_orig = g_strdup(g_getenv(ENVIRONMENT_TIMEZONE));
195 g_setenv(ENVIRONMENT_TIMEZONE, tz, TRUE);
196 tzset();
197 return(tz_orig);
200 static void
201 sipe_reset_tz(gchar *tz_orig)
203 if (tz_orig) {
204 g_setenv(ENVIRONMENT_TIMEZONE, tz_orig, TRUE);
205 g_free(tz_orig);
206 } else {
207 g_unsetenv(ENVIRONMENT_TIMEZONE);
209 tzset();
213 * Converts struct tm to Epoch time_t considering timezone.
215 * @param tz as defined for TZ environment variable.
217 * Reference: see timegm(3) - Linux man page
219 time_t
220 sipe_mktime_tz(struct tm *tm,
221 const char* tz)
223 time_t ret;
224 gchar *tz_orig;
226 tz_orig = sipe_switch_tz(tz);
227 ret = mktime(tm);
228 sipe_reset_tz(tz_orig);
230 return ret;
234 * Converts Epoch time_t to struct tm considering timezone.
236 * @param tz as defined for TZ environment variable.
238 * Reference: see timegm(3) - Linux man page
240 static struct tm *
241 sipe_localtime_tz(const time_t *time,
242 const char* tz)
244 struct tm *ret;
245 gchar *tz_orig;
247 tz_orig = sipe_switch_tz(tz);
248 ret = localtime(time);
249 sipe_reset_tz(tz_orig);
251 return ret;
254 void
255 sipe_cal_free_working_hours(struct sipe_cal_working_hours *wh)
257 if (!wh) return;
259 g_free(wh->std.time);
260 g_free(wh->std.day_of_week);
261 g_free(wh->std.year);
263 g_free(wh->dst.time);
264 g_free(wh->dst.day_of_week);
265 g_free(wh->dst.year);
267 g_free(wh->days_of_week);
268 g_free(wh->tz);
269 g_free(wh->tz_std);
270 g_free(wh->tz_dst);
271 g_free(wh);
275 * Returns time_t of daylight savings time start/end
276 * in the provided timezone or otherwise
277 * (time_t)-1 if no daylight savings time.
279 static time_t
280 sipe_cal_get_std_dst_time(time_t now,
281 int bias,
282 struct sipe_cal_std_dst* std_dst,
283 struct sipe_cal_std_dst* dst_std)
285 struct tm switch_tm;
286 time_t res = TIME_NULL;
287 struct tm *gm_now_tm;
288 gchar **time_arr;
290 if (std_dst->month == 0) return TIME_NULL;
292 gm_now_tm = gmtime(&now);
293 time_arr = g_strsplit(std_dst->time, ":", 0);
295 switch_tm.tm_sec = atoi(time_arr[2]);
296 switch_tm.tm_min = atoi(time_arr[1]);
297 switch_tm.tm_hour = atoi(time_arr[0]);
298 g_strfreev(time_arr);
299 switch_tm.tm_mday = std_dst->year ? std_dst->day_order : 1 /* to adjust later */ ;
300 switch_tm.tm_mon = std_dst->month - 1;
301 switch_tm.tm_year = std_dst->year ? atoi(std_dst->year) - 1900 : gm_now_tm->tm_year;
302 switch_tm.tm_isdst = 0;
303 /* to set tm_wday */
304 res = sipe_mktime_tz(&switch_tm, "UTC");
306 /* if not dynamic, calculate right tm_mday */
307 if (!std_dst->year) {
308 int switch_wday = sipe_cal_get_wday(std_dst->day_of_week);
309 int needed_month;
310 /* get first desired wday in the month */
311 int delta = switch_wday >= switch_tm.tm_wday ? (switch_wday - switch_tm.tm_wday) : (switch_wday + 7 - switch_tm.tm_wday);
312 switch_tm.tm_mday = 1 + delta;
313 /* try nth order */
314 switch_tm.tm_mday += (std_dst->day_order - 1) * 7;
315 needed_month = switch_tm.tm_mon;
316 /* to set settle date if ahead of allowed month dates */
317 res = sipe_mktime_tz(&switch_tm, "UTC");
318 if (needed_month != switch_tm.tm_mon) {
319 /* moving 1 week back to stay within required month */
320 switch_tm.tm_mday -= 7;
321 /* to fix date again */
322 res = sipe_mktime_tz(&switch_tm, "UTC");
325 /* note: bias is taken from "switch to" structure */
326 return res + (bias + dst_std->bias)*60;
329 static void
330 sipe_cal_parse_std_dst(const sipe_xml *xn_std_dst_time,
331 struct sipe_cal_std_dst *std_dst)
333 const sipe_xml *node;
334 gchar *tmp;
336 if (!xn_std_dst_time) return;
337 if (!std_dst) return;
339 <StandardTime>
340 <Bias>0</Bias>
341 <Time>02:00:00</Time>
342 <DayOrder>1</DayOrder>
343 <Month>11</Month>
344 <DayOfWeek>Sunday</DayOfWeek>
345 </StandardTime>
348 if ((node = sipe_xml_child(xn_std_dst_time, "Bias"))) {
349 std_dst->bias = atoi(tmp = sipe_xml_data(node));
350 g_free(tmp);
353 if ((node = sipe_xml_child(xn_std_dst_time, "Time"))) {
354 std_dst->time = sipe_xml_data(node);
357 if ((node = sipe_xml_child(xn_std_dst_time, "DayOrder"))) {
358 std_dst->day_order = atoi(tmp = sipe_xml_data(node));
359 g_free(tmp);
362 if ((node = sipe_xml_child(xn_std_dst_time, "Month"))) {
363 std_dst->month = atoi(tmp = sipe_xml_data(node));
364 g_free(tmp);
367 if ((node = sipe_xml_child(xn_std_dst_time, "DayOfWeek"))) {
368 std_dst->day_of_week = sipe_xml_data(node);
371 if ((node = sipe_xml_child(xn_std_dst_time, "Year"))) {
372 std_dst->year = sipe_xml_data(node);
376 void
377 sipe_cal_parse_working_hours(const sipe_xml *xn_working_hours,
378 struct sipe_buddy *buddy)
380 const sipe_xml *xn_bias;
381 const sipe_xml *xn_timezone;
382 const sipe_xml *xn_working_period;
383 const sipe_xml *xn_standard_time;
384 const sipe_xml *xn_daylight_time;
385 gchar *tmp;
386 time_t now = time(NULL);
387 struct sipe_cal_std_dst* std;
388 struct sipe_cal_std_dst* dst;
390 if (!xn_working_hours) return;
392 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
393 <TimeZone>
394 <Bias>480</Bias>
396 </TimeZone>
397 <WorkingPeriodArray>
398 <WorkingPeriod>
399 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
400 <StartTimeInMinutes>600</StartTimeInMinutes>
401 <EndTimeInMinutes>1140</EndTimeInMinutes>
402 </WorkingPeriod>
403 </WorkingPeriodArray>
404 </WorkingHours>
406 sipe_cal_free_working_hours(buddy->cal_working_hours);
407 buddy->cal_working_hours = g_new0(struct sipe_cal_working_hours, 1);
409 xn_timezone = sipe_xml_child(xn_working_hours, "TimeZone");
410 xn_bias = sipe_xml_child(xn_timezone, "Bias");
411 if (xn_bias) {
412 buddy->cal_working_hours->bias = atoi(tmp = sipe_xml_data(xn_bias));
413 g_free(tmp);
416 xn_standard_time = sipe_xml_child(xn_timezone, "StandardTime");
417 xn_daylight_time = sipe_xml_child(xn_timezone, "DaylightTime");
419 std = &((*buddy->cal_working_hours).std);
420 dst = &((*buddy->cal_working_hours).dst);
421 sipe_cal_parse_std_dst(xn_standard_time, std);
422 sipe_cal_parse_std_dst(xn_daylight_time, dst);
424 xn_working_period = sipe_xml_child(xn_working_hours, "WorkingPeriodArray/WorkingPeriod");
425 if (xn_working_period) {
426 buddy->cal_working_hours->days_of_week =
427 sipe_xml_data(sipe_xml_child(xn_working_period, "DayOfWeek"));
429 buddy->cal_working_hours->start_time =
430 atoi(tmp = sipe_xml_data(sipe_xml_child(xn_working_period, "StartTimeInMinutes")));
431 g_free(tmp);
433 buddy->cal_working_hours->end_time =
434 atoi(tmp = sipe_xml_data(sipe_xml_child(xn_working_period, "EndTimeInMinutes")));
435 g_free(tmp);
438 std->switch_time = sipe_cal_get_std_dst_time(now, buddy->cal_working_hours->bias, std, dst);
439 dst->switch_time = sipe_cal_get_std_dst_time(now, buddy->cal_working_hours->bias, dst, std);
441 /* TST8TDT7,M3.2.0/02:00:00,M11.1.0/02:00:00 */
442 buddy->cal_working_hours->tz =
443 g_strdup_printf("TST%dTDT%d,M%d.%d.%d/%s,M%d.%d.%d/%s",
444 (buddy->cal_working_hours->bias + buddy->cal_working_hours->std.bias) / 60,
445 (buddy->cal_working_hours->bias + buddy->cal_working_hours->dst.bias) / 60,
447 buddy->cal_working_hours->dst.month,
448 buddy->cal_working_hours->dst.day_order,
449 sipe_cal_get_wday(buddy->cal_working_hours->dst.day_of_week),
450 buddy->cal_working_hours->dst.time,
452 buddy->cal_working_hours->std.month,
453 buddy->cal_working_hours->std.day_order,
454 sipe_cal_get_wday(buddy->cal_working_hours->std.day_of_week),
455 buddy->cal_working_hours->std.time
457 /* TST8 */
458 buddy->cal_working_hours->tz_std =
459 g_strdup_printf("TST%d",
460 (buddy->cal_working_hours->bias + buddy->cal_working_hours->std.bias) / 60);
461 /* TDT7 */
462 buddy->cal_working_hours->tz_dst =
463 g_strdup_printf("TDT%d",
464 (buddy->cal_working_hours->bias + buddy->cal_working_hours->dst.bias) / 60);
467 struct sipe_cal_event*
468 sipe_cal_get_event(GSList *cal_events,
469 time_t time_in_question)
471 GSList *entry = cal_events;
472 struct sipe_cal_event* cal_event;
473 struct sipe_cal_event* res = NULL;
475 if (!cal_events || !IS(time_in_question)) return NULL;
477 while (entry) {
478 cal_event = entry->data;
479 /* event is in the past or in the future */
480 if (cal_event->start_time > time_in_question ||
481 cal_event->end_time <= time_in_question)
483 entry = entry->next;
484 continue;
487 if (!res) {
488 res = cal_event;
489 } else {
490 int res_status = (res->cal_status == SIPE_CAL_NO_DATA) ? -1 : res->cal_status;
491 int cal_status = (cal_event->cal_status == SIPE_CAL_NO_DATA) ? -1 : cal_event->cal_status;
492 if (res_status < cal_status) {
493 res = cal_event;
496 entry = entry->next;
498 return res;
501 static int
502 sipe_cal_get_status0(const gchar *free_busy,
503 time_t cal_start,
504 int granularity,
505 time_t time_in_question,
506 int *index)
508 int res = SIPE_CAL_NO_DATA;
509 int shift;
510 time_t cal_end = cal_start + strlen(free_busy)*granularity*60 - 1;
512 if (!(time_in_question >= cal_start && time_in_question <= cal_end)) return res;
514 shift = (time_in_question - cal_start) / (granularity*60);
515 if (index) {
516 *index = shift;
519 res = free_busy[shift] - '0';
521 return res;
525 * Returns time when current calendar state started
527 static time_t
528 sipe_cal_get_since_time(const gchar *free_busy,
529 time_t calStart,
530 int granularity,
531 int index,
532 int current_state)
534 int i;
536 if ((index < 0) || ((size_t)(index + 1) > strlen(free_busy))) return 0;
538 for (i = index; i >= 0; i--) {
539 int temp_status = free_busy[i] - '0';
541 if (current_state != temp_status) {
542 return calStart + (i + 1)*granularity*60;
545 if (i == 0) return calStart;
548 return 0;
550 static char*
551 sipe_cal_get_free_busy(struct sipe_buddy *buddy);
554 sipe_cal_get_status(struct sipe_buddy *buddy,
555 time_t time_in_question,
556 time_t *since)
558 time_t cal_start;
559 const char* free_busy;
560 int ret = SIPE_CAL_NO_DATA;
561 time_t state_since;
562 int index;
564 if (!buddy || !buddy->cal_start_time || !buddy->cal_granularity) {
565 SIPE_DEBUG_INFO("sipe_cal_get_status: no calendar data1 for %s, exiting",
566 buddy ? (buddy->name ? buddy->name : "") : "");
567 return SIPE_CAL_NO_DATA;
570 if (!(free_busy = sipe_cal_get_free_busy(buddy))) {
571 SIPE_DEBUG_INFO("sipe_cal_get_status: no calendar data2 for %s, exiting", buddy->name);
572 return SIPE_CAL_NO_DATA;
574 SIPE_DEBUG_INFO("sipe_cal_get_description: buddy->cal_free_busy=\n%s", free_busy);
576 cal_start = sipe_utils_str_to_time(buddy->cal_start_time);
578 ret = sipe_cal_get_status0(free_busy,
579 cal_start,
580 buddy->cal_granularity,
581 time_in_question,
582 &index);
583 state_since = sipe_cal_get_since_time(free_busy,
584 cal_start,
585 buddy->cal_granularity,
586 index,
587 ret);
589 if (since) *since = state_since;
590 return ret;
593 static time_t
594 sipe_cal_get_switch_time(const gchar *free_busy,
595 time_t calStart,
596 int granularity,
597 int index,
598 int current_state,
599 int *to_state)
601 size_t i;
602 time_t ret = TIME_NULL;
604 if ((index < 0) || ((size_t) (index + 1) > strlen(free_busy))) {
605 *to_state = SIPE_CAL_NO_DATA;
606 return ret;
609 for (i = index + 1; i < strlen(free_busy); i++) {
610 int temp_status = free_busy[i] - '0';
612 if (current_state != temp_status) {
613 *to_state = temp_status;
614 return calStart + i*granularity*60;
618 return ret;
621 static const char*
622 sipe_cal_get_tz(struct sipe_cal_working_hours *wh,
623 time_t time_in_question)
625 time_t dst_switch_time = (*wh).dst.switch_time;
626 time_t std_switch_time = (*wh).std.switch_time;
627 gboolean is_dst = FALSE;
629 /* No daylight savings */
630 if (dst_switch_time == TIME_NULL) {
631 return wh->tz_std;
634 if (dst_switch_time < std_switch_time) { /* North hemosphere - Europe, US */
635 if (time_in_question >= dst_switch_time && time_in_question < std_switch_time) {
636 is_dst = TRUE;
638 } else { /* South hemisphere - Australia */
639 if (time_in_question >= dst_switch_time || time_in_question < std_switch_time) {
640 is_dst = TRUE;
644 if (is_dst) {
645 return wh->tz_dst;
646 } else {
647 return wh->tz_std;
651 static time_t
652 sipe_cal_mktime_of_day(struct tm *sample_today_tm,
653 const int shift_minutes,
654 const char *tz)
656 sample_today_tm->tm_sec = 0;
657 sample_today_tm->tm_min = shift_minutes % 60;
658 sample_today_tm->tm_hour = shift_minutes / 60;
660 return sipe_mktime_tz(sample_today_tm, tz);
664 * Returns work day start and end in Epoch time
665 * considering the initial values are provided
666 * in contact's local time zone.
668 static void
669 sipe_cal_get_today_work_hours(struct sipe_cal_working_hours *wh,
670 time_t *start,
671 time_t *end,
672 time_t *next_start)
674 time_t now = time(NULL);
675 const char *tz = sipe_cal_get_tz(wh, now);
676 struct tm *remote_now_tm = sipe_localtime_tz(&now, tz);
678 if (!strstr(wh->days_of_week, wday_names[remote_now_tm->tm_wday])) { /* not a work day */
679 *start = TIME_NULL;
680 *end = TIME_NULL;
681 *next_start = TIME_NULL;
682 return;
685 *end = sipe_cal_mktime_of_day(remote_now_tm, wh->end_time, tz);
687 if (now < *end) {
688 *start = sipe_cal_mktime_of_day(remote_now_tm, wh->start_time, tz);
689 *next_start = TIME_NULL;
690 } else { /* calculate start of tomorrow's work day if any */
691 time_t tom = now + 24*60*60;
692 struct tm *remote_tom_tm = sipe_localtime_tz(&tom, sipe_cal_get_tz(wh, tom));
694 if (!strstr(wh->days_of_week, wday_names[remote_tom_tm->tm_wday])) { /* not a work day */
695 *next_start = TIME_NULL;
698 *next_start = sipe_cal_mktime_of_day(remote_tom_tm, wh->start_time, sipe_cal_get_tz(wh, tom));
699 *start = TIME_NULL;
703 static int
704 sipe_cal_is_in_work_hours(const time_t time_in_question,
705 const time_t start,
706 const time_t end)
708 return !((time_in_question >= end) || (IS(start) && time_in_question < start));
712 * Returns time closest to now. Choses only from times ahead of now.
713 * Returns TIME_NULL otherwise.
715 static time_t
716 sipe_cal_get_until(const time_t now,
717 const time_t switch_time,
718 const time_t start,
719 const time_t end,
720 const time_t next_start)
722 time_t ret = TIME_NULL;
723 int min_diff = now - ret;
725 if (IS(switch_time) && switch_time > now && (switch_time - now) < min_diff) {
726 min_diff = switch_time - now;
727 ret = switch_time;
729 if (IS(start) && start > now && (start - now) < min_diff) {
730 min_diff = start - now;
731 ret = start;
733 if (IS(end) && end > now && (end - now) < min_diff) {
734 min_diff = end - now;
735 ret = end;
737 if (IS(next_start) && next_start > now && (next_start - now) < min_diff) {
738 min_diff = next_start - now;
739 ret = next_start;
741 return ret;
744 static char*
745 sipe_cal_get_free_busy(struct sipe_buddy *buddy)
747 /* do lazy decode if necessary */
748 if (!buddy->cal_free_busy && buddy->cal_free_busy_base64) {
749 gsize cal_dec64_len;
750 guchar *cal_dec64;
751 gsize i;
752 int j = 0;
754 cal_dec64 = g_base64_decode(buddy->cal_free_busy_base64, &cal_dec64_len);
756 buddy->cal_free_busy = g_malloc0(cal_dec64_len * 4 + 1);
758 http://msdn.microsoft.com/en-us/library/dd941537%28office.13%29.aspx
759 00, Free (Fr)
760 01, Tentative (Te)
761 10, Busy (Bu)
762 11, Out of facility (Oo)
764 http://msdn.microsoft.com/en-us/library/aa566048.aspx
765 0 Free
766 1 Tentative
767 2 Busy
768 3 Out of Office (OOF)
769 4 No data
771 for (i = 0; i < cal_dec64_len; i++) {
772 #define TWO_BIT_MASK 0x03
773 char tmp = cal_dec64[i];
774 buddy->cal_free_busy[j++] = (tmp & TWO_BIT_MASK) + '0';
775 buddy->cal_free_busy[j++] = ((tmp >> 2) & TWO_BIT_MASK) + '0';
776 buddy->cal_free_busy[j++] = ((tmp >> 4) & TWO_BIT_MASK) + '0';
777 buddy->cal_free_busy[j++] = ((tmp >> 6) & TWO_BIT_MASK) + '0';
779 buddy->cal_free_busy[j++] = '\0';
780 g_free(cal_dec64);
783 return buddy->cal_free_busy;
786 char *
787 sipe_cal_get_freebusy_base64(const char* freebusy_hex)
789 guint i = 0;
790 guint j = 0;
791 guint shift_factor = 0;
792 guint len, res_len;
793 guchar *res;
794 gchar *res_base64;
796 if (!freebusy_hex) return NULL;
798 len = strlen(freebusy_hex);
799 res_len = len / 4 + 1;
800 res = g_malloc0(res_len);
801 while (i < len) {
802 res[j] |= (freebusy_hex[i++] - '0') << shift_factor;
803 shift_factor += 2;
804 if (shift_factor == 8) {
805 shift_factor = 0;
806 j++;
810 res_base64 = g_base64_encode(res, shift_factor ? res_len : res_len - 1);
811 g_free(res);
812 return res_base64;
815 char *
816 sipe_cal_get_description(struct sipe_buddy *buddy)
818 time_t cal_start;
819 time_t cal_end;
820 int current_cal_state;
821 time_t now = time(NULL);
822 time_t start = TIME_NULL;
823 time_t end = TIME_NULL;
824 time_t next_start = TIME_NULL;
825 time_t switch_time;
826 int to_state = SIPE_CAL_NO_DATA;
827 time_t until = TIME_NULL;
828 int index = 0;
829 gboolean has_working_hours = (buddy->cal_working_hours != NULL);
830 const char *free_busy;
831 const char *cal_states[] = {_("Free"),
832 _("Tentative"),
833 _("Busy"),
834 _("Out of office"),
835 _("No data")};
837 if (buddy->cal_granularity != 15) {
838 SIPE_DEBUG_INFO("sipe_cal_get_description: granularity %d is unsupported, exiting.", buddy->cal_granularity);
839 return NULL;
842 /* to lazy load if needed */
843 free_busy = sipe_cal_get_free_busy(buddy);
844 SIPE_DEBUG_INFO("sipe_cal_get_description: buddy->cal_free_busy=\n%s", free_busy ? free_busy : "");
846 if (!buddy->cal_free_busy || !buddy->cal_granularity || !buddy->cal_start_time) {
847 SIPE_DEBUG_INFO_NOFORMAT("sipe_cal_get_description: no calendar data, exiting");
848 return NULL;
851 cal_start = sipe_utils_str_to_time(buddy->cal_start_time);
852 cal_end = cal_start + 60 * (buddy->cal_granularity) * strlen(buddy->cal_free_busy);
854 current_cal_state = sipe_cal_get_status0(free_busy, cal_start, buddy->cal_granularity, time(NULL), &index);
855 if (current_cal_state == SIPE_CAL_NO_DATA) {
856 SIPE_DEBUG_INFO_NOFORMAT("sipe_cal_get_description: calendar is undefined for present moment, exiting.");
857 return NULL;
860 switch_time = sipe_cal_get_switch_time(free_busy, cal_start, buddy->cal_granularity, index, current_cal_state, &to_state);
862 SIPE_DEBUG_INFO_NOFORMAT("\n* Calendar *");
863 if (buddy->cal_working_hours) {
864 sipe_cal_get_today_work_hours(buddy->cal_working_hours, &start, &end, &next_start);
866 SIPE_DEBUG_INFO("Remote now timezone : %s", sipe_cal_get_tz(buddy->cal_working_hours, now));
867 SIPE_DEBUG_INFO("std.switch_time(GMT): %s",
868 IS((*buddy->cal_working_hours).std.switch_time) ? asctime(gmtime(&((*buddy->cal_working_hours).std.switch_time))) : "");
869 SIPE_DEBUG_INFO("dst.switch_time(GMT): %s",
870 IS((*buddy->cal_working_hours).dst.switch_time) ? asctime(gmtime(&((*buddy->cal_working_hours).dst.switch_time))) : "");
871 SIPE_DEBUG_INFO("Remote now time : %s",
872 asctime(sipe_localtime_tz(&now, sipe_cal_get_tz(buddy->cal_working_hours, now))));
873 SIPE_DEBUG_INFO("Remote start time : %s",
874 IS(start) ? asctime(sipe_localtime_tz(&start, sipe_cal_get_tz(buddy->cal_working_hours, start))) : "");
875 SIPE_DEBUG_INFO("Remote end time : %s",
876 IS(end) ? asctime(sipe_localtime_tz(&end, sipe_cal_get_tz(buddy->cal_working_hours, end))) : "");
877 SIPE_DEBUG_INFO("Rem. next_start time: %s",
878 IS(next_start) ? asctime(sipe_localtime_tz(&next_start, sipe_cal_get_tz(buddy->cal_working_hours, next_start))) : "");
879 SIPE_DEBUG_INFO("Remote switch time : %s",
880 IS(switch_time) ? asctime(sipe_localtime_tz(&switch_time, sipe_cal_get_tz(buddy->cal_working_hours, switch_time))) : "");
881 } else {
882 SIPE_DEBUG_INFO("Local now time : %s",
883 asctime(localtime(&now)));
884 SIPE_DEBUG_INFO("Local switch time : %s",
885 IS(switch_time) ? asctime(localtime(&switch_time)) : "");
887 SIPE_DEBUG_INFO("Calendar End (GMT) : %s", asctime(gmtime(&cal_end)));
888 SIPE_DEBUG_INFO("current cal state : %s", cal_states[current_cal_state]);
889 SIPE_DEBUG_INFO("switch cal state : %s", cal_states[to_state] );
891 /* Calendar: string calculations */
894 ALGORITHM (don't delete)
895 (c)2009,2010 pier11 <pier11@operamail.com>
897 SOD = Start of Work Day
898 EOD = End of Work Day
899 NSOD = Start of tomorrow's Work Day
900 SW = Calendar status switch time
902 if current_cal_state == Free
903 until = min_t of SOD, EOD, NSOD, SW (min_t(x) = min(x-now) where x>now only)
904 else
905 until = SW
907 if (!until && (cal_period_end > now + 8H))
908 until = cal_period_end
910 if (!until)
911 return "Currently %", current_cal_state
913 if (until - now > 8H)
914 if (current_cal_state == Free && (work_hours && !in work_hours(now)))
915 return "Outside of working hours for next 8 hours"
916 else
917 return "%s for next 8 hours", current_cal_state
919 if (current_cal_state == Free)
920 if (work_hours && until !in work_hours(now))
921 "Not working"
922 else
923 "%s", current_cal_state
924 " until %.2d:%.2d", until
925 else
926 "Currently %", current_cal_state
927 if (work_hours && until !in work_hours(until))
928 ". Outside of working hours at at %.2d:%.2d", until
929 else
930 ". %s at %.2d:%.2d", to_state, until
933 if (current_cal_state < 1) { /* Free */
934 until = sipe_cal_get_until(now, switch_time, start, end, next_start);
935 } else {
936 until = switch_time;
939 if (!IS(until) && (cal_end - now > 8*60*60))
940 until = cal_end;
942 if (!IS(until)) {
943 return g_strdup_printf(_("Currently %s"), cal_states[current_cal_state]);
946 if (until - now > 8*60*60) {
947 /* Free & outside work hours */
948 if (current_cal_state < 1 && has_working_hours && !sipe_cal_is_in_work_hours(now, start, end)) {
949 return g_strdup(_("Outside of working hours for next 8 hours"));
950 } else {
951 return g_strdup_printf(_("%s for next 8 hours"), cal_states[current_cal_state]);
955 if (current_cal_state < 1) { /* Free */
956 const char *tmp;
957 struct tm *until_tm = localtime(&until);
959 if (has_working_hours && !sipe_cal_is_in_work_hours(now, start, end)) {
960 tmp = _("Not working");
961 } else {
962 tmp = cal_states[current_cal_state];
964 return g_strdup_printf(_("%s until %.2d:%.2d"), tmp, until_tm->tm_hour, until_tm->tm_min);
965 } else { /* Tentative or Busy or OOF */
966 char *tmp;
967 char *res;
968 struct tm *until_tm = localtime(&until);
970 tmp = g_strdup_printf(_("Currently %s"), cal_states[current_cal_state]);
971 if (has_working_hours && !sipe_cal_is_in_work_hours(until, start, end)) {
972 res = g_strdup_printf(_("%s. Outside of working hours at %.2d:%.2d"),
973 tmp, until_tm->tm_hour, until_tm->tm_min);
974 g_free(tmp);
975 return res;
976 } else {
977 res = g_strdup_printf(_("%s. %s at %.2d:%.2d"), tmp, cal_states[to_state], until_tm->tm_hour, until_tm->tm_min);
978 g_free(tmp);
979 return res;
982 /* End of - Calendar: string calculations */
986 Local Variables:
987 mode: c
988 c-file-style: "bsd"
989 indent-tabs-mode: t
990 tab-width: 8
991 End: