cal: remvove http_conn
[siplcs.git] / src / core / sipe-cal.c
blob9b1e5c7837275377c7ef433408c0a6b852a5ea11
1 /**
2 * @file sipe-cal.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
33 #include <glib.h>
35 #include "sipe-backend.h"
36 #include "sipe-buddy.h"
37 #include "sipe-core.h"
38 #include "sipe-core-private.h"
39 #include "sipe-cal.h"
40 #include "sipe-http.h"
41 #include "sipe-nls.h"
42 #include "sipe-ocs2005.h"
43 #include "sipe-ocs2007.h"
44 #include "sipe-schedule.h"
45 #include "sipe-utils.h"
46 #include "sipe-xml.h"
48 /* Calendar backends */
49 #ifdef _WIN32
50 #include "sipe-domino.h"
51 #endif
52 #include "sipe-ews.h"
54 #define TIME_NULL (time_t)-1
55 #define IS(time) (time != TIME_NULL)
58 http://msdn.microsoft.com/en-us/library/aa565001.aspx
60 <?xml version="1.0"?>
61 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
62 <TimeZone>
63 <Bias>480</Bias>
64 <StandardTime>
65 <Bias>0</Bias>
66 <Time>02:00:00</Time>
67 <DayOrder>1</DayOrder>
68 <Month>11</Month>
69 <DayOfWeek>Sunday</DayOfWeek>
70 </StandardTime>
71 <DaylightTime>
72 <Bias>-60</Bias>
73 <Time>02:00:00</Time>
74 <DayOrder>2</DayOrder>
75 <Month>3</Month>
76 <DayOfWeek>Sunday</DayOfWeek>
77 </DaylightTime>
78 </TimeZone>
79 <WorkingPeriodArray>
80 <WorkingPeriod>
81 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
82 <StartTimeInMinutes>600</StartTimeInMinutes>
83 <EndTimeInMinutes>1140</EndTimeInMinutes>
84 </WorkingPeriod>
85 </WorkingPeriodArray>
86 </WorkingHours>
88 Desc:
89 <StandardTime>
90 <Bias>int</Bias>
91 <Time>string</Time>
92 <DayOrder>short</DayOrder>
93 <Month>short</Month>
94 <DayOfWeek>Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday</DayOfWeek>
95 <Year>string</Year>
96 </StandardTime>
99 struct sipe_cal_std_dst {
100 int bias; /* Ex.: -60 */
101 gchar *time; /* hh:mm:ss, 02:00:00 */
102 int day_order; /* 1..5 */
103 int month; /* 1..12 */
104 gchar *day_of_week; /* Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday */
105 gchar *year; /* YYYY */
107 time_t switch_time;
110 struct sipe_cal_working_hours {
111 int bias; /* Ex.: 480 */
112 struct sipe_cal_std_dst std; /* StandardTime */
113 struct sipe_cal_std_dst dst; /* DaylightTime */
114 gchar *days_of_week; /* Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday separated by space */
115 int start_time; /* 0...1440 */
116 int end_time; /* 0...1440 */
118 gchar *tz; /* aggregated timezone string as in TZ environment variable.
119 Ex.: TST+8TDT+7,M3.2.0/02:00:00,M11.1.0/02:00:00 */
120 /** separate simple strings for Windows platform as the proper TZ does not work there.
121 * anyway, dynamic timezones would't work with just TZ
123 gchar *tz_std; /* Ex.: TST8 */
124 gchar *tz_dst; /* Ex.: TDT7 */
127 /* not for translation, a part of XML Schema definitions */
128 static const char *wday_names[] = {"Sunday",
129 "Monday",
130 "Tuesday",
131 "Wednesday",
132 "Thursday",
133 "Friday",
134 "Saturday"};
135 static int
136 sipe_cal_get_wday(char *wday_name)
138 int i;
140 if (!wday_name) return -1;
142 for (i = 0; i < 7; i++) {
143 if (sipe_strequal(wday_names[i], wday_name)) {
144 return i;
148 return -1;
151 void
152 sipe_cal_event_free(struct sipe_cal_event* cal_event)
154 if (!cal_event) return;
156 g_free(cal_event->subject);
157 g_free(cal_event->location);
158 g_free(cal_event);
161 void
162 sipe_cal_events_free(GSList *cal_events)
164 GSList *entry = cal_events;
166 if (!cal_events) return;
168 while (entry) {
169 struct sipe_cal_event *cal_event = entry->data;
170 sipe_cal_event_free(cal_event);
171 entry = entry->next;
174 g_slist_free(cal_events);
177 void
178 sipe_cal_calendar_free(struct sipe_calendar *cal)
180 g_free(cal->email);
181 g_free(cal->legacy_dn);
182 g_free(cal->auth_domain);
183 g_free(cal->auth_user);
184 g_free(cal->password);
185 g_free(cal->as_url);
186 g_free(cal->oof_url);
187 g_free(cal->oab_url);
188 g_free(cal->domino_url);
189 g_free(cal->oof_state);
190 g_free(cal->oof_note);
191 g_free(cal->free_busy);
192 g_free(cal->working_hours_xml_str);
194 sipe_cal_events_free(cal->cal_events);
196 if (cal->request)
197 sipe_http_request_cancel(cal->request);
198 sipe_http_session_close(cal->session);
200 g_free(cal);
203 gboolean
204 sipe_cal_calendar_init(struct sipe_core_private *sipe_private,
205 gboolean *has_url)
207 if (!sipe_private->calendar) {
208 struct sipe_calendar *cal;
209 const char *value;
211 sipe_private->calendar = cal = g_new0(struct sipe_calendar, 1);
212 cal->sipe_private = sipe_private;
214 cal->email = g_strdup(sipe_private->email);
216 /* user specified a service URL? */
217 value = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_URL);
218 if (has_url) *has_url = !is_empty(value);
219 if (!is_empty(value)) {
220 cal->as_url = g_strdup(value);
221 cal->oof_url = g_strdup(value);
222 cal->domino_url = g_strdup(value);
225 /* user specified email login? */
226 value = sipe_backend_setting(SIPE_CORE_PUBLIC, SIPE_SETTING_EMAIL_LOGIN);
227 if (!is_empty(value)) {
228 /* Allowed domain-account separators are / or \ */
229 gchar **domain_user = g_strsplit_set(value, "/\\", 2);
230 gboolean has_domain = domain_user[1] != NULL;
232 cal->auth_domain = has_domain ? g_strdup(domain_user[0]) : NULL;
233 cal->auth_user = g_strdup(domain_user[has_domain ? 1 : 0]);
234 cal->password = g_strdup(sipe_backend_setting(SIPE_CORE_PUBLIC,
235 SIPE_SETTING_EMAIL_PASSWORD));
236 g_strfreev(domain_user);
238 return TRUE;
240 return FALSE;
244 char *
245 sipe_cal_event_describe(struct sipe_cal_event* cal_event)
247 GString* str = g_string_new(NULL);
248 const char *status = "";
250 switch(cal_event->cal_status) {
251 case SIPE_CAL_FREE: status = "SIPE_CAL_FREE"; break;
252 case SIPE_CAL_TENTATIVE: status = "SIPE_CAL_TENTATIVE"; break;
253 case SIPE_CAL_BUSY: status = "SIPE_CAL_BUSY"; break;
254 case SIPE_CAL_OOF: status = "SIPE_CAL_OOF"; break;
255 case SIPE_CAL_NO_DATA: status = "SIPE_CAL_NO_DATA"; break;
258 g_string_append_printf(str, "\t%s: %s", "start_time",
259 IS(cal_event->start_time) ? asctime(localtime(&cal_event->start_time)) : "\n");
260 g_string_append_printf(str, "\t%s: %s", "end_time ",
261 IS(cal_event->end_time) ? asctime(localtime(&cal_event->end_time)) : "\n");
262 g_string_append_printf(str, "\t%s: %s\n", "cal_status", status);
263 g_string_append_printf(str, "\t%s: %s\n", "subject ", cal_event->subject ? cal_event->subject : "");
264 g_string_append_printf(str, "\t%s: %s\n", "location ", cal_event->location ? cal_event->location : "");
265 g_string_append_printf(str, "\t%s: %s\n", "is_meeting", cal_event->is_meeting ? "TRUE" : "FALSE");
267 return g_string_free(str, FALSE);
270 char *
271 sipe_cal_event_hash(struct sipe_cal_event* event)
273 /* no end_time as it dos not get published */
274 /* no cal_status as it can change on publication */
275 return g_strdup_printf("<%d><%s><%s><%d>",
276 (int)event->start_time,
277 event->subject ? event->subject : "",
278 event->location ? event->location : "",
279 event->is_meeting);
282 #define ENVIRONMENT_TIMEZONE "TZ"
284 static gchar *
285 sipe_switch_tz(const char *tz)
287 gchar *tz_orig;
289 tz_orig = g_strdup(g_getenv(ENVIRONMENT_TIMEZONE));
290 g_setenv(ENVIRONMENT_TIMEZONE, tz, TRUE);
291 tzset();
292 return(tz_orig);
295 static void
296 sipe_reset_tz(gchar *tz_orig)
298 if (tz_orig) {
299 g_setenv(ENVIRONMENT_TIMEZONE, tz_orig, TRUE);
300 g_free(tz_orig);
301 } else {
302 g_unsetenv(ENVIRONMENT_TIMEZONE);
304 tzset();
308 * Converts struct tm to Epoch time_t considering timezone.
310 * @param tz as defined for TZ environment variable.
312 * Reference: see timegm(3) - Linux man page
314 time_t
315 sipe_mktime_tz(struct tm *tm,
316 const char* tz)
318 time_t ret;
319 gchar *tz_orig;
321 tz_orig = sipe_switch_tz(tz);
322 ret = mktime(tm);
323 sipe_reset_tz(tz_orig);
325 return ret;
329 * Converts Epoch time_t to struct tm considering timezone.
331 * @param tz as defined for TZ environment variable.
333 * Reference: see timegm(3) - Linux man page
335 static struct tm *
336 sipe_localtime_tz(const time_t *time,
337 const char* tz)
339 struct tm *ret;
340 gchar *tz_orig;
342 tz_orig = sipe_switch_tz(tz);
343 ret = localtime(time);
344 sipe_reset_tz(tz_orig);
346 return ret;
349 void
350 sipe_cal_free_working_hours(struct sipe_cal_working_hours *wh)
352 if (!wh) return;
354 g_free(wh->std.time);
355 g_free(wh->std.day_of_week);
356 g_free(wh->std.year);
358 g_free(wh->dst.time);
359 g_free(wh->dst.day_of_week);
360 g_free(wh->dst.year);
362 g_free(wh->days_of_week);
363 g_free(wh->tz);
364 g_free(wh->tz_std);
365 g_free(wh->tz_dst);
366 g_free(wh);
370 * Returns time_t of daylight savings time start/end
371 * in the provided timezone or otherwise
372 * (time_t)-1 if no daylight savings time.
374 static time_t
375 sipe_cal_get_std_dst_time(time_t now,
376 int bias,
377 struct sipe_cal_std_dst* std_dst,
378 struct sipe_cal_std_dst* dst_std)
380 struct tm switch_tm;
381 time_t res = TIME_NULL;
382 struct tm *gm_now_tm;
383 gchar **time_arr;
385 if (std_dst->month == 0) return TIME_NULL;
387 gm_now_tm = gmtime(&now);
388 time_arr = g_strsplit(std_dst->time, ":", 0);
390 switch_tm.tm_sec = atoi(time_arr[2]);
391 switch_tm.tm_min = atoi(time_arr[1]);
392 switch_tm.tm_hour = atoi(time_arr[0]);
393 g_strfreev(time_arr);
394 switch_tm.tm_mday = std_dst->year ? std_dst->day_order : 1 /* to adjust later */ ;
395 switch_tm.tm_mon = std_dst->month - 1;
396 switch_tm.tm_year = std_dst->year ? atoi(std_dst->year) - 1900 : gm_now_tm->tm_year;
397 switch_tm.tm_isdst = 0;
398 /* to set tm_wday */
399 res = sipe_mktime_tz(&switch_tm, "UTC");
401 /* if not dynamic, calculate right tm_mday */
402 if (!std_dst->year) {
403 int switch_wday = sipe_cal_get_wday(std_dst->day_of_week);
404 int needed_month;
405 /* get first desired wday in the month */
406 int delta = switch_wday >= switch_tm.tm_wday ? (switch_wday - switch_tm.tm_wday) : (switch_wday + 7 - switch_tm.tm_wday);
407 switch_tm.tm_mday = 1 + delta;
408 /* try nth order */
409 switch_tm.tm_mday += (std_dst->day_order - 1) * 7;
410 needed_month = switch_tm.tm_mon;
411 /* to set settle date if ahead of allowed month dates */
412 res = sipe_mktime_tz(&switch_tm, "UTC");
413 if (needed_month != switch_tm.tm_mon) {
414 /* moving 1 week back to stay within required month */
415 switch_tm.tm_mday -= 7;
416 /* to fix date again */
417 res = sipe_mktime_tz(&switch_tm, "UTC");
420 /* note: bias is taken from "switch to" structure */
421 return res + (bias + dst_std->bias)*60;
424 static void
425 sipe_cal_parse_std_dst(const sipe_xml *xn_std_dst_time,
426 struct sipe_cal_std_dst *std_dst)
428 const sipe_xml *node;
429 gchar *tmp;
431 if (!xn_std_dst_time) return;
432 if (!std_dst) return;
434 <StandardTime>
435 <Bias>0</Bias>
436 <Time>02:00:00</Time>
437 <DayOrder>1</DayOrder>
438 <Month>11</Month>
439 <Year>2009</Year>
440 <DayOfWeek>Sunday</DayOfWeek>
441 </StandardTime>
444 if ((node = sipe_xml_child(xn_std_dst_time, "Bias"))) {
445 std_dst->bias = atoi(tmp = sipe_xml_data(node));
446 g_free(tmp);
449 if ((node = sipe_xml_child(xn_std_dst_time, "Time"))) {
450 std_dst->time = sipe_xml_data(node);
453 if ((node = sipe_xml_child(xn_std_dst_time, "DayOrder"))) {
454 std_dst->day_order = atoi(tmp = sipe_xml_data(node));
455 g_free(tmp);
458 if ((node = sipe_xml_child(xn_std_dst_time, "Month"))) {
459 std_dst->month = atoi(tmp = sipe_xml_data(node));
460 g_free(tmp);
463 if ((node = sipe_xml_child(xn_std_dst_time, "DayOfWeek"))) {
464 std_dst->day_of_week = sipe_xml_data(node);
467 if ((node = sipe_xml_child(xn_std_dst_time, "Year"))) {
468 std_dst->year = sipe_xml_data(node);
472 void
473 sipe_cal_parse_working_hours(const sipe_xml *xn_working_hours,
474 struct sipe_buddy *buddy)
476 const sipe_xml *xn_bias;
477 const sipe_xml *xn_timezone;
478 const sipe_xml *xn_working_period;
479 const sipe_xml *xn_standard_time;
480 const sipe_xml *xn_daylight_time;
481 gchar *tmp;
482 time_t now = time(NULL);
483 struct sipe_cal_std_dst* std;
484 struct sipe_cal_std_dst* dst;
486 if (!xn_working_hours) return;
488 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
489 <TimeZone>
490 <Bias>480</Bias>
492 </TimeZone>
493 <WorkingPeriodArray>
494 <WorkingPeriod>
495 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
496 <StartTimeInMinutes>600</StartTimeInMinutes>
497 <EndTimeInMinutes>1140</EndTimeInMinutes>
498 </WorkingPeriod>
499 </WorkingPeriodArray>
500 </WorkingHours>
502 sipe_cal_free_working_hours(buddy->cal_working_hours);
503 buddy->cal_working_hours = g_new0(struct sipe_cal_working_hours, 1);
505 xn_timezone = sipe_xml_child(xn_working_hours, "TimeZone");
506 xn_bias = sipe_xml_child(xn_timezone, "Bias");
507 if (xn_bias) {
508 buddy->cal_working_hours->bias = atoi(tmp = sipe_xml_data(xn_bias));
509 g_free(tmp);
512 xn_standard_time = sipe_xml_child(xn_timezone, "StandardTime");
513 xn_daylight_time = sipe_xml_child(xn_timezone, "DaylightTime");
515 std = &((*buddy->cal_working_hours).std);
516 dst = &((*buddy->cal_working_hours).dst);
517 sipe_cal_parse_std_dst(xn_standard_time, std);
518 sipe_cal_parse_std_dst(xn_daylight_time, dst);
520 xn_working_period = sipe_xml_child(xn_working_hours, "WorkingPeriodArray/WorkingPeriod");
521 if (xn_working_period) {
522 /* NOTE: this can be NULL! */
523 buddy->cal_working_hours->days_of_week =
524 sipe_xml_data(sipe_xml_child(xn_working_period, "DayOfWeek"));
526 buddy->cal_working_hours->start_time =
527 atoi(tmp = sipe_xml_data(sipe_xml_child(xn_working_period, "StartTimeInMinutes")));
528 g_free(tmp);
530 buddy->cal_working_hours->end_time =
531 atoi(tmp = sipe_xml_data(sipe_xml_child(xn_working_period, "EndTimeInMinutes")));
532 g_free(tmp);
535 std->switch_time = sipe_cal_get_std_dst_time(now, buddy->cal_working_hours->bias, std, dst);
536 dst->switch_time = sipe_cal_get_std_dst_time(now, buddy->cal_working_hours->bias, dst, std);
538 /* TST8TDT7,M3.2.0/02:00:00,M11.1.0/02:00:00 */
539 buddy->cal_working_hours->tz =
540 g_strdup_printf("TST%dTDT%d,M%d.%d.%d/%s,M%d.%d.%d/%s",
541 (buddy->cal_working_hours->bias + buddy->cal_working_hours->std.bias) / 60,
542 (buddy->cal_working_hours->bias + buddy->cal_working_hours->dst.bias) / 60,
544 buddy->cal_working_hours->dst.month,
545 buddy->cal_working_hours->dst.day_order,
546 sipe_cal_get_wday(buddy->cal_working_hours->dst.day_of_week),
547 buddy->cal_working_hours->dst.time,
549 buddy->cal_working_hours->std.month,
550 buddy->cal_working_hours->std.day_order,
551 sipe_cal_get_wday(buddy->cal_working_hours->std.day_of_week),
552 buddy->cal_working_hours->std.time
554 /* TST8 */
555 buddy->cal_working_hours->tz_std =
556 g_strdup_printf("TST%d",
557 (buddy->cal_working_hours->bias + buddy->cal_working_hours->std.bias) / 60);
558 /* TDT7 */
559 buddy->cal_working_hours->tz_dst =
560 g_strdup_printf("TDT%d",
561 (buddy->cal_working_hours->bias + buddy->cal_working_hours->dst.bias) / 60);
564 struct sipe_cal_event*
565 sipe_cal_get_event(GSList *cal_events,
566 time_t time_in_question)
568 GSList *entry = cal_events;
569 struct sipe_cal_event* cal_event;
570 struct sipe_cal_event* res = NULL;
572 if (!cal_events || !IS(time_in_question)) return NULL;
574 while (entry) {
575 cal_event = entry->data;
576 /* event is in the past or in the future */
577 if (cal_event->start_time > time_in_question ||
578 cal_event->end_time <= time_in_question)
580 entry = entry->next;
581 continue;
584 if (!res) {
585 res = cal_event;
586 } else {
587 int res_status = (res->cal_status == SIPE_CAL_NO_DATA) ? -1 : res->cal_status;
588 int cal_status = (cal_event->cal_status == SIPE_CAL_NO_DATA) ? -1 : cal_event->cal_status;
589 if (res_status < cal_status) {
590 res = cal_event;
593 entry = entry->next;
595 return res;
598 static int
599 sipe_cal_get_status0(const gchar *free_busy,
600 time_t cal_start,
601 int granularity,
602 time_t time_in_question,
603 int *index)
605 int res = SIPE_CAL_NO_DATA;
606 int shift;
607 time_t cal_end = cal_start + strlen(free_busy)*granularity*60 - 1;
609 if (!(time_in_question >= cal_start && time_in_question <= cal_end)) return res;
611 shift = (time_in_question - cal_start) / (granularity*60);
612 if (index) {
613 *index = shift;
616 res = free_busy[shift] - '0';
618 return res;
622 * Returns time when current calendar state started
624 static time_t
625 sipe_cal_get_since_time(const gchar *free_busy,
626 time_t calStart,
627 int granularity,
628 int index,
629 int current_state)
631 int i;
633 if ((index < 0) || ((size_t)(index + 1) > strlen(free_busy))) return 0;
635 for (i = index; i >= 0; i--) {
636 int temp_status = free_busy[i] - '0';
638 if (current_state != temp_status) {
639 return calStart + (i + 1)*granularity*60;
642 if (i == 0) return calStart;
645 return 0;
647 static char*
648 sipe_cal_get_free_busy(struct sipe_buddy *buddy);
651 sipe_cal_get_status(struct sipe_buddy *buddy,
652 time_t time_in_question,
653 time_t *since)
655 time_t cal_start;
656 const char* free_busy;
657 int ret = SIPE_CAL_NO_DATA;
658 time_t state_since;
659 int index = -1;
661 if (!buddy || !buddy->cal_start_time || !buddy->cal_granularity) {
662 SIPE_DEBUG_INFO("sipe_cal_get_status: no calendar data1 for %s, exiting",
663 buddy ? (buddy->name ? buddy->name : "") : "");
664 return SIPE_CAL_NO_DATA;
667 if (!(free_busy = sipe_cal_get_free_busy(buddy))) {
668 SIPE_DEBUG_INFO("sipe_cal_get_status: no calendar data2 for %s, exiting", buddy->name);
669 return SIPE_CAL_NO_DATA;
671 SIPE_DEBUG_INFO("sipe_cal_get_description: buddy->cal_free_busy=\n%s", free_busy);
673 cal_start = sipe_utils_str_to_time(buddy->cal_start_time);
675 ret = sipe_cal_get_status0(free_busy,
676 cal_start,
677 buddy->cal_granularity,
678 time_in_question,
679 &index);
680 state_since = sipe_cal_get_since_time(free_busy,
681 cal_start,
682 buddy->cal_granularity,
683 index,
684 ret);
686 if (since) *since = state_since;
687 return ret;
690 static time_t
691 sipe_cal_get_switch_time(const gchar *free_busy,
692 time_t calStart,
693 int granularity,
694 int index,
695 int current_state,
696 int *to_state)
698 size_t i;
699 time_t ret = TIME_NULL;
701 if ((index < 0) || ((size_t) (index + 1) > strlen(free_busy))) {
702 *to_state = SIPE_CAL_NO_DATA;
703 return ret;
706 for (i = index + 1; i < strlen(free_busy); i++) {
707 int temp_status = free_busy[i] - '0';
709 if (current_state != temp_status) {
710 *to_state = temp_status;
711 return calStart + i*granularity*60;
715 return ret;
718 static const char*
719 sipe_cal_get_tz(struct sipe_cal_working_hours *wh,
720 time_t time_in_question)
722 time_t dst_switch_time = (*wh).dst.switch_time;
723 time_t std_switch_time = (*wh).std.switch_time;
724 gboolean is_dst = FALSE;
726 /* No daylight savings */
727 if (dst_switch_time == TIME_NULL) {
728 return wh->tz_std;
731 if (dst_switch_time < std_switch_time) { /* North hemosphere - Europe, US */
732 if (time_in_question >= dst_switch_time && time_in_question < std_switch_time) {
733 is_dst = TRUE;
735 } else { /* South hemisphere - Australia */
736 if (time_in_question >= dst_switch_time || time_in_question < std_switch_time) {
737 is_dst = TRUE;
741 if (is_dst) {
742 return wh->tz_dst;
743 } else {
744 return wh->tz_std;
748 static time_t
749 sipe_cal_mktime_of_day(struct tm *sample_today_tm,
750 const int shift_minutes,
751 const char *tz)
753 sample_today_tm->tm_sec = 0;
754 sample_today_tm->tm_min = shift_minutes % 60;
755 sample_today_tm->tm_hour = shift_minutes / 60;
757 return sipe_mktime_tz(sample_today_tm, tz);
761 * Returns work day start and end in Epoch time
762 * considering the initial values are provided
763 * in contact's local time zone.
765 static void
766 sipe_cal_get_today_work_hours(struct sipe_cal_working_hours *wh,
767 time_t *start,
768 time_t *end,
769 time_t *next_start)
771 time_t now = time(NULL);
772 const char *tz = sipe_cal_get_tz(wh, now);
773 struct tm *remote_now_tm = sipe_localtime_tz(&now, tz);
775 if (!(wh->days_of_week && strstr(wh->days_of_week, wday_names[remote_now_tm->tm_wday]))) {
776 /* not a work day */
777 *start = TIME_NULL;
778 *end = TIME_NULL;
779 *next_start = TIME_NULL;
780 return;
783 *end = sipe_cal_mktime_of_day(remote_now_tm, wh->end_time, tz);
785 if (now < *end) {
786 *start = sipe_cal_mktime_of_day(remote_now_tm, wh->start_time, tz);
787 *next_start = TIME_NULL;
788 } else { /* calculate start of tomorrow's work day if any */
789 time_t tom = now + 24*60*60;
790 struct tm *remote_tom_tm = sipe_localtime_tz(&tom, sipe_cal_get_tz(wh, tom));
792 if (!(wh->days_of_week && strstr(wh->days_of_week, wday_names[remote_tom_tm->tm_wday]))) {
793 /* not a work day */
794 *next_start = TIME_NULL;
797 *next_start = sipe_cal_mktime_of_day(remote_tom_tm, wh->start_time, sipe_cal_get_tz(wh, tom));
798 *start = TIME_NULL;
802 static int
803 sipe_cal_is_in_work_hours(const time_t time_in_question,
804 const time_t start,
805 const time_t end)
807 return !((time_in_question >= end) || (IS(start) && time_in_question < start));
811 * Returns time closest to now. Choses only from times ahead of now.
812 * Returns TIME_NULL otherwise.
814 static time_t
815 sipe_cal_get_until(const time_t now,
816 const time_t switch_time,
817 const time_t start,
818 const time_t end,
819 const time_t next_start)
821 time_t ret = TIME_NULL;
822 int min_diff = now - ret;
824 if (IS(switch_time) && switch_time > now && (switch_time - now) < min_diff) {
825 min_diff = switch_time - now;
826 ret = switch_time;
828 if (IS(start) && start > now && (start - now) < min_diff) {
829 min_diff = start - now;
830 ret = start;
832 if (IS(end) && end > now && (end - now) < min_diff) {
833 min_diff = end - now;
834 ret = end;
836 if (IS(next_start) && next_start > now && (next_start - now) < min_diff) {
837 min_diff = next_start - now;
838 ret = next_start;
840 return ret;
843 static char*
844 sipe_cal_get_free_busy(struct sipe_buddy *buddy)
846 /* do lazy decode if necessary */
847 if (!buddy->cal_free_busy && buddy->cal_free_busy_base64) {
848 gsize cal_dec64_len;
849 guchar *cal_dec64;
850 gsize i;
851 int j = 0;
853 cal_dec64 = g_base64_decode(buddy->cal_free_busy_base64, &cal_dec64_len);
855 buddy->cal_free_busy = g_malloc0(cal_dec64_len * 4 + 1);
857 http://msdn.microsoft.com/en-us/library/dd941537%28office.13%29.aspx
858 00, Free (Fr)
859 01, Tentative (Te)
860 10, Busy (Bu)
861 11, Out of facility (Oo)
863 http://msdn.microsoft.com/en-us/library/aa566048.aspx
864 0 Free
865 1 Tentative
866 2 Busy
867 3 Out of Office (OOF)
868 4 No data
870 for (i = 0; i < cal_dec64_len; i++) {
871 #define TWO_BIT_MASK 0x03
872 char tmp = cal_dec64[i];
873 buddy->cal_free_busy[j++] = (tmp & TWO_BIT_MASK) + '0';
874 buddy->cal_free_busy[j++] = ((tmp >> 2) & TWO_BIT_MASK) + '0';
875 buddy->cal_free_busy[j++] = ((tmp >> 4) & TWO_BIT_MASK) + '0';
876 buddy->cal_free_busy[j++] = ((tmp >> 6) & TWO_BIT_MASK) + '0';
878 buddy->cal_free_busy[j++] = '\0';
879 g_free(cal_dec64);
882 return buddy->cal_free_busy;
885 char *
886 sipe_cal_get_freebusy_base64(const char* freebusy_hex)
888 guint i = 0;
889 guint j = 0;
890 guint shift_factor = 0;
891 guint len, res_len;
892 guchar *res;
893 gchar *res_base64;
895 if (!freebusy_hex) return NULL;
897 len = strlen(freebusy_hex);
898 res_len = len / 4 + 1;
899 res = g_malloc0(res_len);
900 while (i < len) {
901 res[j] |= (freebusy_hex[i++] - '0') << shift_factor;
902 shift_factor += 2;
903 if (shift_factor == 8) {
904 shift_factor = 0;
905 j++;
909 res_base64 = g_base64_encode(res, shift_factor ? res_len : res_len - 1);
910 g_free(res);
911 return res_base64;
914 char *
915 sipe_cal_get_description(struct sipe_buddy *buddy)
917 time_t cal_start;
918 time_t cal_end;
919 int current_cal_state;
920 time_t now = time(NULL);
921 time_t start = TIME_NULL;
922 time_t end = TIME_NULL;
923 time_t next_start = TIME_NULL;
924 time_t switch_time;
925 int to_state = SIPE_CAL_NO_DATA;
926 time_t until = TIME_NULL;
927 int index = 0;
928 gboolean has_working_hours = (buddy->cal_working_hours != NULL);
929 const char *free_busy;
930 const char *cal_states[] = {_("Free"),
931 _("Tentative"),
932 _("Busy"),
933 _("Out of office"),
934 _("No data")};
936 if (buddy->cal_granularity != 15) {
937 SIPE_DEBUG_INFO("sipe_cal_get_description: granularity %d is unsupported, exiting.", buddy->cal_granularity);
938 return NULL;
941 /* to lazy load if needed */
942 free_busy = sipe_cal_get_free_busy(buddy);
943 SIPE_DEBUG_INFO("sipe_cal_get_description: buddy->cal_free_busy=\n%s", free_busy ? free_busy : "");
945 if (!buddy->cal_free_busy || !buddy->cal_granularity || !buddy->cal_start_time) {
946 SIPE_DEBUG_INFO_NOFORMAT("sipe_cal_get_description: no calendar data, exiting");
947 return NULL;
950 cal_start = sipe_utils_str_to_time(buddy->cal_start_time);
951 cal_end = cal_start + 60 * (buddy->cal_granularity) * strlen(buddy->cal_free_busy);
953 current_cal_state = sipe_cal_get_status0(free_busy, cal_start, buddy->cal_granularity, time(NULL), &index);
954 if (current_cal_state == SIPE_CAL_NO_DATA) {
955 SIPE_DEBUG_INFO_NOFORMAT("sipe_cal_get_description: calendar is undefined for present moment, exiting.");
956 return NULL;
959 switch_time = sipe_cal_get_switch_time(free_busy, cal_start, buddy->cal_granularity, index, current_cal_state, &to_state);
961 SIPE_DEBUG_INFO_NOFORMAT("\n* Calendar *");
962 if (buddy->cal_working_hours) {
963 sipe_cal_get_today_work_hours(buddy->cal_working_hours, &start, &end, &next_start);
965 SIPE_DEBUG_INFO("Remote now timezone : %s", sipe_cal_get_tz(buddy->cal_working_hours, now));
966 SIPE_DEBUG_INFO("std.switch_time(GMT): %s",
967 IS((*buddy->cal_working_hours).std.switch_time) ? asctime(gmtime(&((*buddy->cal_working_hours).std.switch_time))) : "");
968 SIPE_DEBUG_INFO("dst.switch_time(GMT): %s",
969 IS((*buddy->cal_working_hours).dst.switch_time) ? asctime(gmtime(&((*buddy->cal_working_hours).dst.switch_time))) : "");
970 SIPE_DEBUG_INFO("Remote now time : %s",
971 asctime(sipe_localtime_tz(&now, sipe_cal_get_tz(buddy->cal_working_hours, now))));
972 SIPE_DEBUG_INFO("Remote start time : %s",
973 IS(start) ? asctime(sipe_localtime_tz(&start, sipe_cal_get_tz(buddy->cal_working_hours, start))) : "");
974 SIPE_DEBUG_INFO("Remote end time : %s",
975 IS(end) ? asctime(sipe_localtime_tz(&end, sipe_cal_get_tz(buddy->cal_working_hours, end))) : "");
976 SIPE_DEBUG_INFO("Rem. next_start time: %s",
977 IS(next_start) ? asctime(sipe_localtime_tz(&next_start, sipe_cal_get_tz(buddy->cal_working_hours, next_start))) : "");
978 SIPE_DEBUG_INFO("Remote switch time : %s",
979 IS(switch_time) ? asctime(sipe_localtime_tz(&switch_time, sipe_cal_get_tz(buddy->cal_working_hours, switch_time))) : "");
980 } else {
981 SIPE_DEBUG_INFO("Local now time : %s",
982 asctime(localtime(&now)));
983 SIPE_DEBUG_INFO("Local switch time : %s",
984 IS(switch_time) ? asctime(localtime(&switch_time)) : "");
986 SIPE_DEBUG_INFO("Calendar End (GMT) : %s", asctime(gmtime(&cal_end)));
987 SIPE_DEBUG_INFO("current cal state : %s", cal_states[current_cal_state]);
988 SIPE_DEBUG_INFO("switch cal state : %s", cal_states[to_state] );
990 /* Calendar: string calculations */
993 ALGORITHM (don't delete)
994 (c)2009,2010 pier11 <pier11@operamail.com>
996 SOD = Start of Work Day
997 EOD = End of Work Day
998 NSOD = Start of tomorrow's Work Day
999 SW = Calendar status switch time
1001 if current_cal_state == Free
1002 until = min_t of SOD, EOD, NSOD, SW (min_t(x) = min(x-now) where x>now only)
1003 else
1004 until = SW
1006 if (!until && (cal_period_end > now + 8H))
1007 until = cal_period_end
1009 if (!until)
1010 return "Currently %", current_cal_state
1012 if (until - now > 8H)
1013 if (current_cal_state == Free && (work_hours && !in work_hours(now)))
1014 return "Outside of working hours for next 8 hours"
1015 else
1016 return "%s for next 8 hours", current_cal_state
1018 if (current_cal_state == Free)
1019 if (work_hours && until !in work_hours(now))
1020 "Not working"
1021 else
1022 "%s", current_cal_state
1023 " until %.2d:%.2d", until
1024 else
1025 "Currently %", current_cal_state
1026 if (work_hours && until !in work_hours(until))
1027 ". Outside of working hours at at %.2d:%.2d", until
1028 else
1029 ". %s at %.2d:%.2d", to_state, until
1032 if (current_cal_state < 1) { /* Free */
1033 until = sipe_cal_get_until(now, switch_time, start, end, next_start);
1034 } else {
1035 until = switch_time;
1038 if (!IS(until) && (cal_end - now > 8*60*60))
1039 until = cal_end;
1041 if (!IS(until)) {
1042 return g_strdup_printf(_("Currently %s"), cal_states[current_cal_state]);
1045 if (until - now > 8*60*60) {
1046 /* Free & outside work hours */
1047 if (current_cal_state < 1 && has_working_hours && !sipe_cal_is_in_work_hours(now, start, end)) {
1048 return g_strdup(_("Outside of working hours for next 8 hours"));
1049 } else {
1050 return g_strdup_printf(_("%s for next 8 hours"), cal_states[current_cal_state]);
1054 if (current_cal_state < 1) { /* Free */
1055 const char *tmp;
1056 struct tm *until_tm = localtime(&until);
1058 if (has_working_hours && !sipe_cal_is_in_work_hours(now, start, end)) {
1059 tmp = _("Not working");
1060 } else {
1061 tmp = cal_states[current_cal_state];
1063 return g_strdup_printf(_("%s until %.2d:%.2d"), tmp, until_tm->tm_hour, until_tm->tm_min);
1064 } else { /* Tentative or Busy or OOF */
1065 char *tmp;
1066 char *res;
1067 struct tm *until_tm = localtime(&until);
1069 tmp = g_strdup_printf(_("Currently %s"), cal_states[current_cal_state]);
1070 if (has_working_hours && !sipe_cal_is_in_work_hours(until, start, end)) {
1071 res = g_strdup_printf(_("%s. Outside of working hours at %.2d:%.2d"),
1072 tmp, until_tm->tm_hour, until_tm->tm_min);
1073 g_free(tmp);
1074 return res;
1075 } else {
1076 res = g_strdup_printf(_("%s. %s at %.2d:%.2d"), tmp, cal_states[to_state], until_tm->tm_hour, until_tm->tm_min);
1077 g_free(tmp);
1078 return res;
1081 /* End of - Calendar: string calculations */
1084 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
1086 void sipe_core_update_calendar(struct sipe_core_public *sipe_public)
1088 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
1090 /* Do in parallel.
1091 * If failed, the branch will be disabled for subsequent calls.
1092 * Can't rely that user turned the functionality on in account settings.
1094 sipe_ews_update_calendar(SIPE_CORE_PRIVATE);
1095 #ifdef _WIN32
1096 /* @TODO: UNIX integration missing */
1097 sipe_domino_update_calendar(SIPE_CORE_PRIVATE);
1098 #endif
1100 /* schedule repeat */
1101 sipe_schedule_seconds(SIPE_CORE_PRIVATE,
1102 "<+update-calendar>",
1103 NULL,
1104 UPDATE_CALENDAR_INTERVAL,
1105 (sipe_schedule_action)sipe_core_update_calendar,
1106 NULL);
1108 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
1111 void sipe_cal_presence_publish(struct sipe_core_private *sipe_private,
1112 gboolean do_publish_calendar)
1114 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1115 if (do_publish_calendar)
1116 sipe_ocs2007_presence_publish(sipe_private, NULL);
1117 else
1118 sipe_ocs2007_category_publish(sipe_private);
1119 } else {
1120 sipe_ocs2005_presence_publish(sipe_private,
1121 do_publish_calendar);
1125 void sipe_cal_delayed_calendar_update(struct sipe_core_private *sipe_private)
1127 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
1129 /* only start periodic calendar updating if user hasn't disabled it */
1130 if (!SIPE_CORE_PUBLIC_FLAG_IS(DONT_PUBLISH))
1131 sipe_schedule_seconds(sipe_private,
1132 "<+update-calendar>",
1133 NULL,
1134 UPDATE_CALENDAR_DELAY,
1135 (sipe_schedule_action) sipe_core_update_calendar,
1136 NULL);
1139 void sipe_cal_http_authentication(struct sipe_calendar *cal)
1141 if (cal->auth_user) {
1142 sipe_http_request_authentication(cal->request,
1143 cal->auth_domain,
1144 cal->auth_user,
1145 cal->password);
1150 Local Variables:
1151 mode: c
1152 c-file-style: "bsd"
1153 indent-tabs-mode: t
1154 tab-width: 8
1155 End: