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
35 #include "sipe-backend.h"
36 #include "sipe-buddy.h"
37 #include "sipe-core.h"
38 #include "sipe-core-private.h"
40 #include "sipe-http.h"
42 #include "sipe-ocs2005.h"
43 #include "sipe-ocs2007.h"
44 #include "sipe-schedule.h"
45 #include "sipe-utils.h"
48 /* Calendar backends */
50 #include "sipe-domino.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
61 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
67 <DayOrder>1</DayOrder>
69 <DayOfWeek>Sunday</DayOfWeek>
74 <DayOrder>2</DayOrder>
76 <DayOfWeek>Sunday</DayOfWeek>
81 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
82 <StartTimeInMinutes>600</StartTimeInMinutes>
83 <EndTimeInMinutes>1140</EndTimeInMinutes>
92 <DayOrder>short</DayOrder>
94 <DayOfWeek>Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday</DayOfWeek>
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 */
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",
136 sipe_cal_get_wday(char *wday_name
)
140 if (!wday_name
) return -1;
142 for (i
= 0; i
< 7; i
++) {
143 if (sipe_strequal(wday_names
[i
], wday_name
)) {
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
);
162 sipe_cal_events_free(GSList
*cal_events
)
164 GSList
*entry
= cal_events
;
166 if (!cal_events
) return;
169 struct sipe_cal_event
*cal_event
= entry
->data
;
170 sipe_cal_event_free(cal_event
);
174 g_slist_free(cal_events
);
178 sipe_cal_calendar_free(struct sipe_calendar
*cal
)
181 g_free(cal
->legacy_dn
);
182 g_free(cal
->auth_domain
);
183 g_free(cal
->auth_user
);
184 g_free(cal
->password
);
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
);
197 sipe_http_request_cancel(cal
->request
);
198 sipe_http_session_close(cal
->session
);
204 sipe_cal_calendar_init(struct sipe_core_private
*sipe_private
,
207 if (!sipe_private
->calendar
) {
208 struct sipe_calendar
*cal
;
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
);
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
);
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
: "",
282 #define ENVIRONMENT_TIMEZONE "TZ"
285 sipe_switch_tz(const char *tz
)
289 tz_orig
= g_strdup(g_getenv(ENVIRONMENT_TIMEZONE
));
290 g_setenv(ENVIRONMENT_TIMEZONE
, tz
, TRUE
);
296 sipe_reset_tz(gchar
*tz_orig
)
299 g_setenv(ENVIRONMENT_TIMEZONE
, tz_orig
, TRUE
);
302 g_unsetenv(ENVIRONMENT_TIMEZONE
);
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
315 sipe_mktime_tz(struct tm
*tm
,
321 tz_orig
= sipe_switch_tz(tz
);
323 sipe_reset_tz(tz_orig
);
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
336 sipe_localtime_tz(const time_t *time
,
342 tz_orig
= sipe_switch_tz(tz
);
343 ret
= localtime(time
);
344 sipe_reset_tz(tz_orig
);
350 sipe_cal_free_working_hours(struct sipe_cal_working_hours
*wh
)
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
);
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.
375 sipe_cal_get_std_dst_time(time_t now
,
377 struct sipe_cal_std_dst
* std_dst
,
378 struct sipe_cal_std_dst
* dst_std
)
381 time_t res
= TIME_NULL
;
382 struct tm
*gm_now_tm
;
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;
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
);
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
;
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;
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
;
431 if (!xn_std_dst_time
) return;
432 if (!std_dst
) return;
436 <Time>02:00:00</Time>
437 <DayOrder>1</DayOrder>
440 <DayOfWeek>Sunday</DayOfWeek>
444 if ((node
= sipe_xml_child(xn_std_dst_time
, "Bias"))) {
445 std_dst
->bias
= atoi(tmp
= sipe_xml_data(node
));
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
));
458 if ((node
= sipe_xml_child(xn_std_dst_time
, "Month"))) {
459 std_dst
->month
= atoi(tmp
= sipe_xml_data(node
));
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
);
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
;
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">
495 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
496 <StartTimeInMinutes>600</StartTimeInMinutes>
497 <EndTimeInMinutes>1140</EndTimeInMinutes>
499 </WorkingPeriodArray>
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");
508 buddy
->cal_working_hours
->bias
= atoi(tmp
= sipe_xml_data(xn_bias
));
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")));
530 buddy
->cal_working_hours
->end_time
=
531 atoi(tmp
= sipe_xml_data(sipe_xml_child(xn_working_period
, "EndTimeInMinutes")));
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
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);
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
;
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
)
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
) {
599 sipe_cal_get_status0(const gchar
*free_busy
,
602 time_t time_in_question
,
605 int res
= SIPE_CAL_NO_DATA
;
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);
616 res
= free_busy
[shift
] - '0';
622 * Returns time when current calendar state started
625 sipe_cal_get_since_time(const gchar
*free_busy
,
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
;
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
,
656 const char* free_busy
;
657 int ret
= SIPE_CAL_NO_DATA
;
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
,
677 buddy
->cal_granularity
,
680 state_since
= sipe_cal_get_since_time(free_busy
,
682 buddy
->cal_granularity
,
686 if (since
) *since
= state_since
;
691 sipe_cal_get_switch_time(const gchar
*free_busy
,
699 time_t ret
= TIME_NULL
;
701 if ((index
< 0) || ((size_t) (index
+ 1) > strlen(free_busy
))) {
702 *to_state
= SIPE_CAL_NO_DATA
;
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;
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
) {
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
) {
735 } else { /* South hemisphere - Australia */
736 if (time_in_question
>= dst_switch_time
|| time_in_question
< std_switch_time
) {
749 sipe_cal_mktime_of_day(struct tm
*sample_today_tm
,
750 const int shift_minutes
,
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.
766 sipe_cal_get_today_work_hours(struct sipe_cal_working_hours
*wh
,
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
]))) {
779 *next_start
= TIME_NULL
;
783 *end
= sipe_cal_mktime_of_day(remote_now_tm
, wh
->end_time
, tz
);
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
]))) {
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
));
803 sipe_cal_is_in_work_hours(const time_t time_in_question
,
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.
815 sipe_cal_get_until(const time_t now
,
816 const time_t switch_time
,
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
;
828 if (IS(start
) && start
> now
&& (start
- now
) < min_diff
) {
829 min_diff
= start
- now
;
832 if (IS(end
) && end
> now
&& (end
- now
) < min_diff
) {
833 min_diff
= end
- now
;
836 if (IS(next_start
) && next_start
> now
&& (next_start
- now
) < min_diff
) {
837 min_diff
= next_start
- now
;
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
) {
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
861 11, Out of facility (Oo)
863 http://msdn.microsoft.com/en-us/library/aa566048.aspx
867 3 Out of Office (OOF)
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';
882 return buddy
->cal_free_busy
;
886 sipe_cal_get_freebusy_base64(const char* freebusy_hex
)
890 guint shift_factor
= 0;
895 if (!freebusy_hex
) return NULL
;
897 len
= strlen(freebusy_hex
);
898 res_len
= len
/ 4 + 1;
899 res
= g_malloc0(res_len
);
901 res
[j
] |= (freebusy_hex
[i
++] - '0') << shift_factor
;
903 if (shift_factor
== 8) {
909 res_base64
= g_base64_encode(res
, shift_factor
? res_len
: res_len
- 1);
915 sipe_cal_get_description(struct sipe_buddy
*buddy
)
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
;
925 int to_state
= SIPE_CAL_NO_DATA
;
926 time_t until
= TIME_NULL
;
928 gboolean has_working_hours
= (buddy
->cal_working_hours
!= NULL
);
929 const char *free_busy
;
930 const char *cal_states
[] = {_("Free"),
936 if (buddy
->cal_granularity
!= 15) {
937 SIPE_DEBUG_INFO("sipe_cal_get_description: granularity %d is unsupported, exiting.", buddy
->cal_granularity
);
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");
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.");
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
))) : "");
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)
1006 if (!until && (cal_period_end > now + 8H))
1007 until = cal_period_end
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"
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))
1022 "%s", current_cal_state
1023 " until %.2d:%.2d", until
1025 "Currently %", current_cal_state
1026 if (work_hours && until !in work_hours(until))
1027 ". Outside of working hours at at %.2d:%.2d", until
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
);
1035 until
= switch_time
;
1038 if (!IS(until
) && (cal_end
- now
> 8*60*60))
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"));
1050 return g_strdup_printf(_("%s for next 8 hours"), cal_states
[current_cal_state
]);
1054 if (current_cal_state
< 1) { /* Free */
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");
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 */
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
);
1076 res
= g_strdup_printf(_("%s. %s at %.2d:%.2d"), tmp
, cal_states
[to_state
], until_tm
->tm_hour
, until_tm
->tm_min
);
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.");
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
);
1096 /* @TODO: UNIX integration missing */
1097 sipe_domino_update_calendar(SIPE_CORE_PRIVATE
);
1100 /* schedule repeat */
1101 sipe_schedule_seconds(SIPE_CORE_PRIVATE
,
1102 "<+update-calendar>",
1104 UPDATE_CALENDAR_INTERVAL
,
1105 (sipe_schedule_action
)sipe_core_update_calendar
,
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
);
1118 sipe_ocs2007_category_publish(sipe_private
);
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>",
1134 UPDATE_CALENDAR_DELAY
,
1135 (sipe_schedule_action
) sipe_core_update_calendar
,
1139 void sipe_cal_http_authentication(struct sipe_calendar
*cal
)
1141 if (cal
->auth_user
) {
1142 sipe_http_request_authentication(cal
->request
,