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
37 #include "sipe-utils.h"
41 #define TIME_NULL (time_t)-1
42 #define IS(time) (time != TIME_NULL)
45 http://msdn.microsoft.com/en-us/library/aa565001.aspx
48 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
54 <DayOrder>1</DayOrder>
56 <DayOfWeek>Sunday</DayOfWeek>
61 <DayOrder>2</DayOrder>
63 <DayOfWeek>Sunday</DayOfWeek>
68 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
69 <StartTimeInMinutes>600</StartTimeInMinutes>
70 <EndTimeInMinutes>1140</EndTimeInMinutes>
79 <DayOrder>short</DayOrder>
81 <DayOfWeek>Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday</DayOfWeek>
86 struct sipe_cal_std_dst
{
87 int bias
; /* Ex.: -60 */
88 gchar
*time
; /* hh:mm:ss, 02:00:00 */
89 int day_order
; /* 1..5 */
90 int month
; /* 1..12 */
91 gchar
*day_of_week
; /* Sunday or Monday or Tuesday or Wednesday or Thursday or Friday or Saturday */
92 gchar
*year
; /* YYYY */
97 struct sipe_cal_working_hours
{
98 int bias
; /* Ex.: 480 */
99 struct sipe_cal_std_dst std
; /* StandardTime */
100 struct sipe_cal_std_dst dst
; /* DaylightTime */
101 gchar
*days_of_week
; /* Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday separated by space */
102 int start_time
; /* 0...1440 */
103 int end_time
; /* 0...1440 */
105 gchar
*tz
; /* aggregated timezone string as in TZ environment variable.
106 Ex.: TST+8TDT+7,M3.2.0/02:00:00,M11.1.0/02:00:00 */
107 /** separate simple strings for Windows platform as the proper TZ does not work there.
108 * anyway, dynamic timezones would't work with just TZ
110 gchar
*tz_std
; /* Ex.: TST8 */
111 gchar
*tz_dst
; /* Ex.: TDT7 */
114 /* not for translation, a part of XML Schema definitions */
115 static const char *wday_names
[] = {"Sunday",
123 sipe_cal_get_wday(char *wday_name
)
127 if (!wday_name
) return -1;
129 for (i
= 0; i
< 7; i
++) {
130 if (sipe_strequal(wday_names
[i
], wday_name
)) {
139 sipe_cal_event_free(struct sipe_cal_event
* cal_event
)
141 if (!cal_event
) return;
143 g_free(cal_event
->subject
);
144 g_free(cal_event
->location
);
149 sipe_cal_event_describe(struct sipe_cal_event
* cal_event
)
151 GString
* str
= g_string_new(NULL
);
152 const char *status
= "";
154 switch(cal_event
->cal_status
) {
155 case SIPE_CAL_FREE
: status
= "SIPE_CAL_FREE"; break;
156 case SIPE_CAL_TENTATIVE
: status
= "SIPE_CAL_TENTATIVE"; break;
157 case SIPE_CAL_BUSY
: status
= "SIPE_CAL_BUSY"; break;
158 case SIPE_CAL_OOF
: status
= "SIPE_CAL_OOF"; break;
159 case SIPE_CAL_NO_DATA
: status
= "SIPE_CAL_NO_DATA"; break;
162 g_string_append_printf(str
, "\t%s: %s", "start_time",
163 IS(cal_event
->start_time
) ? asctime(localtime(&cal_event
->start_time
)) : "\n");
164 g_string_append_printf(str
, "\t%s: %s", "end_time ",
165 IS(cal_event
->end_time
) ? asctime(localtime(&cal_event
->end_time
)) : "\n");
166 g_string_append_printf(str
, "\t%s: %s\n", "cal_status", status
);
167 g_string_append_printf(str
, "\t%s: %s\n", "subject ", cal_event
->subject
? cal_event
->subject
: "");
168 g_string_append_printf(str
, "\t%s: %s\n", "location ", cal_event
->location
? cal_event
->location
: "");
169 g_string_append_printf(str
, "\t%s: %s\n", "is_meeting", cal_event
->is_meeting
? "TRUE" : "FALSE");
171 return g_string_free(str
, FALSE
);
175 sipe_cal_event_hash(struct sipe_cal_event
* event
)
177 /* no end_time as it dos not get published */
178 /* no cal_status as it can change on publication */
179 return g_strdup_printf("<%d><%s><%s><%d>",
180 (int)event
->start_time
,
181 event
->subject
? event
->subject
: "",
182 event
->location
? event
->location
: "",
187 sipe_setenv(const char *name
,
191 setenv(name
, value
, 1);
193 int len
= strlen(name
) + 1 + strlen(value
) + 1;
194 char *str
= g_malloc0(len
);
195 sprintf(str
, "%s=%s", name
, value
);
201 sipe_unsetenv(const char *name
)
206 int len
= strlen(name
) + 1 + 1;
207 char *str
= g_malloc0(len
);
208 sprintf(str
, "%s=", name
);
214 * Converts struct tm to Epoch time_t considering timezone.
216 * @param tz as defined for TZ environment variable.
218 * Reference: see timegm(3) - Linux man page
221 sipe_mktime_tz(struct tm
*tm
,
227 tz_old
= getenv("TZ");
228 sipe_setenv("TZ", tz
);
234 sipe_setenv("TZ", tz_old
);
244 * Converts Epoch time_t to struct tm considering timezone.
246 * @param tz as defined for TZ environment variable.
248 * Reference: see timegm(3) - Linux man page
251 sipe_localtime_tz(const time_t *time
,
257 tz_old
= getenv("TZ");
258 sipe_setenv("TZ", tz
);
261 ret
= localtime(time
);
264 sipe_setenv("TZ", tz_old
);
274 sipe_cal_free_working_hours(struct sipe_cal_working_hours
*wh
)
278 g_free(wh
->std
.time
);
279 g_free(wh
->std
.day_of_week
);
280 g_free(wh
->std
.year
);
282 g_free(wh
->dst
.time
);
283 g_free(wh
->dst
.day_of_week
);
284 g_free(wh
->dst
.year
);
286 g_free(wh
->days_of_week
);
294 * Returns time_t of daylight savings time start/end
295 * in the provided timezone or otherwise
296 * (time_t)-1 if no daylight savings time.
299 sipe_cal_get_std_dst_time(time_t now
,
301 struct sipe_cal_std_dst
* std_dst
,
302 struct sipe_cal_std_dst
* dst_std
)
305 time_t res
= TIME_NULL
;
306 struct tm
*gm_now_tm
;
309 if (std_dst
->month
== 0) return TIME_NULL
;
311 gm_now_tm
= gmtime(&now
);
312 time_arr
= g_strsplit(std_dst
->time
, ":", 0);
314 switch_tm
.tm_sec
= atoi(time_arr
[2]);
315 switch_tm
.tm_min
= atoi(time_arr
[1]);
316 switch_tm
.tm_hour
= atoi(time_arr
[0]);
317 g_strfreev(time_arr
);
318 switch_tm
.tm_mday
= std_dst
->year
? std_dst
->day_order
: 1 /* to adjust later */ ;
319 switch_tm
.tm_mon
= std_dst
->month
- 1;
320 switch_tm
.tm_year
= std_dst
->year
? atoi(std_dst
->year
) - 1900 : gm_now_tm
->tm_year
;
321 switch_tm
.tm_isdst
= 0;
323 res
= sipe_mktime_tz(&switch_tm
, "UTC");
325 /* if not dynamic, calculate right tm_mday */
326 if (!std_dst
->year
) {
327 int switch_wday
= sipe_cal_get_wday(std_dst
->day_of_week
);
329 /* get first desired wday in the month */
330 int delta
= switch_wday
>= switch_tm
.tm_wday
? (switch_wday
- switch_tm
.tm_wday
) : (switch_wday
+ 7 - switch_tm
.tm_wday
);
331 switch_tm
.tm_mday
= 1 + delta
;
333 switch_tm
.tm_mday
+= (std_dst
->day_order
- 1) * 7;
334 needed_month
= switch_tm
.tm_mon
;
335 /* to set settle date if ahead of allowed month dates */
336 res
= sipe_mktime_tz(&switch_tm
, "UTC");
337 if (needed_month
!= switch_tm
.tm_mon
) {
338 /* moving 1 week back to stay within required month */
339 switch_tm
.tm_mday
-= 7;
340 /* to fix date again */
341 res
= sipe_mktime_tz(&switch_tm
, "UTC");
344 /* note: bias is taken from "switch to" structure */
345 return res
+ (bias
+ dst_std
->bias
)*60;
349 sipe_cal_parse_std_dst(xmlnode
*xn_std_dst_time
,
350 struct sipe_cal_std_dst
* std_dst
)
355 if (!xn_std_dst_time
) return;
356 if (!std_dst
) return;
360 <Time>02:00:00</Time>
361 <DayOrder>1</DayOrder>
363 <DayOfWeek>Sunday</DayOfWeek>
367 if ((node
= xmlnode_get_child(xn_std_dst_time
, "Bias"))) {
368 std_dst
->bias
= atoi(tmp
= xmlnode_get_data(node
));
372 if ((node
= xmlnode_get_child(xn_std_dst_time
, "Time"))) {
373 std_dst
->time
= xmlnode_get_data(node
);
376 if ((node
= xmlnode_get_child(xn_std_dst_time
, "DayOrder"))) {
377 std_dst
->day_order
= atoi(tmp
= xmlnode_get_data(node
));
381 if ((node
= xmlnode_get_child(xn_std_dst_time
, "Month"))) {
382 std_dst
->month
= atoi(tmp
= xmlnode_get_data(node
));
386 if ((node
= xmlnode_get_child(xn_std_dst_time
, "DayOfWeek"))) {
387 std_dst
->day_of_week
= xmlnode_get_data(node
);
390 if ((node
= xmlnode_get_child(xn_std_dst_time
, "Year"))) {
391 std_dst
->year
= xmlnode_get_data(node
);
396 sipe_cal_parse_working_hours(xmlnode
*xn_working_hours
,
397 struct sipe_buddy
*buddy
)
400 xmlnode
*xn_working_period
;
401 xmlnode
*xn_standard_time
;
402 xmlnode
*xn_daylight_time
;
404 time_t now
= time(NULL
);
405 struct sipe_cal_std_dst
* std
;
406 struct sipe_cal_std_dst
* dst
;
408 if (!xn_working_hours
) return;
410 <WorkingHours xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
417 <DayOfWeek>Monday Tuesday Wednesday Thursday Friday</DayOfWeek>
418 <StartTimeInMinutes>600</StartTimeInMinutes>
419 <EndTimeInMinutes>1140</EndTimeInMinutes>
421 </WorkingPeriodArray>
424 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
425 buddy
->cal_working_hours
= g_new0(struct sipe_cal_working_hours
, 1);
427 xn_bias
= xmlnode_get_descendant(xn_working_hours
, "TimeZone", "Bias", NULL
);
429 buddy
->cal_working_hours
->bias
= atoi(tmp
= xmlnode_get_data(xn_bias
));
433 xn_standard_time
= xmlnode_get_descendant(xn_working_hours
, "TimeZone", "StandardTime", NULL
);
434 xn_daylight_time
= xmlnode_get_descendant(xn_working_hours
, "TimeZone", "DaylightTime", NULL
);
436 std
= &((*buddy
->cal_working_hours
).std
);
437 dst
= &((*buddy
->cal_working_hours
).dst
);
438 sipe_cal_parse_std_dst(xn_standard_time
, std
);
439 sipe_cal_parse_std_dst(xn_daylight_time
, dst
);
441 xn_working_period
= xmlnode_get_descendant(xn_working_hours
, "WorkingPeriodArray", "WorkingPeriod", NULL
);
442 if (xn_working_period
) {
443 buddy
->cal_working_hours
->days_of_week
=
444 xmlnode_get_data(xmlnode_get_child(xn_working_period
, "DayOfWeek"));
446 buddy
->cal_working_hours
->start_time
=
447 atoi(tmp
= xmlnode_get_data(xmlnode_get_child(xn_working_period
, "StartTimeInMinutes")));
450 buddy
->cal_working_hours
->end_time
=
451 atoi(tmp
= xmlnode_get_data(xmlnode_get_child(xn_working_period
, "EndTimeInMinutes")));
455 std
->switch_time
= sipe_cal_get_std_dst_time(now
, buddy
->cal_working_hours
->bias
, std
, dst
);
456 dst
->switch_time
= sipe_cal_get_std_dst_time(now
, buddy
->cal_working_hours
->bias
, dst
, std
);
458 /* TST8TDT7,M3.2.0/02:00:00,M11.1.0/02:00:00 */
459 buddy
->cal_working_hours
->tz
=
460 g_strdup_printf("TST%dTDT%d,M%d.%d.%d/%s,M%d.%d.%d/%s",
461 (buddy
->cal_working_hours
->bias
+ buddy
->cal_working_hours
->std
.bias
) / 60,
462 (buddy
->cal_working_hours
->bias
+ buddy
->cal_working_hours
->dst
.bias
) / 60,
464 buddy
->cal_working_hours
->dst
.month
,
465 buddy
->cal_working_hours
->dst
.day_order
,
466 sipe_cal_get_wday(buddy
->cal_working_hours
->dst
.day_of_week
),
467 buddy
->cal_working_hours
->dst
.time
,
469 buddy
->cal_working_hours
->std
.month
,
470 buddy
->cal_working_hours
->std
.day_order
,
471 sipe_cal_get_wday(buddy
->cal_working_hours
->std
.day_of_week
),
472 buddy
->cal_working_hours
->std
.time
475 buddy
->cal_working_hours
->tz_std
=
476 g_strdup_printf("TST%d",
477 (buddy
->cal_working_hours
->bias
+ buddy
->cal_working_hours
->std
.bias
) / 60);
479 buddy
->cal_working_hours
->tz_dst
=
480 g_strdup_printf("TDT%d",
481 (buddy
->cal_working_hours
->bias
+ buddy
->cal_working_hours
->dst
.bias
) / 60);
484 struct sipe_cal_event
*
485 sipe_cal_get_event(GSList
*cal_events
,
486 time_t time_in_question
)
488 GSList
*entry
= cal_events
;
489 struct sipe_cal_event
* cal_event
;
490 struct sipe_cal_event
* res
= NULL
;
492 if (!cal_events
|| !IS(time_in_question
)) return NULL
;
495 cal_event
= entry
->data
;
496 /* event is in the past or in the future */
497 if (cal_event
->start_time
> time_in_question
||
498 cal_event
->end_time
<= time_in_question
)
507 int res_status
= (res
->cal_status
== SIPE_CAL_NO_DATA
) ? -1 : res
->cal_status
;
508 int cal_status
= (cal_event
->cal_status
== SIPE_CAL_NO_DATA
) ? -1 : cal_event
->cal_status
;
509 if (res_status
< cal_status
) {
519 sipe_cal_get_status0(const gchar
*free_busy
,
522 time_t time_in_question
,
525 int res
= SIPE_CAL_NO_DATA
;
527 time_t cal_end
= cal_start
+ strlen(free_busy
)*granularity
*60 - 1;
529 if (!(time_in_question
>= cal_start
&& time_in_question
<= cal_end
)) return res
;
531 shift
= (time_in_question
- cal_start
) / (granularity
*60);
536 res
= free_busy
[shift
] - '0';
542 * Returns time when current calendar state started
545 sipe_cal_get_since_time(const gchar
*free_busy
,
553 if ((index
< 0) || ((size_t)(index
+ 1) > strlen(free_busy
))) return 0;
555 for (i
= index
; i
>= 0; i
--) {
556 int temp_status
= free_busy
[i
] - '0';
558 if (current_state
!= temp_status
) {
559 return calStart
+ (i
+ 1)*granularity
*60;
562 if (i
== 0) return calStart
;
568 sipe_cal_get_free_busy(struct sipe_buddy
*buddy
);
571 sipe_cal_get_status(struct sipe_buddy
*buddy
,
572 time_t time_in_question
,
576 const char* free_busy
;
577 int ret
= SIPE_CAL_NO_DATA
;
581 if (!buddy
|| !buddy
->cal_start_time
|| !buddy
->cal_granularity
) {
582 purple_debug_info("sipe", "sipe_cal_get_status: no calendar data1 for %s, exiting\n",
583 buddy
? (buddy
->name
? buddy
->name
: "") : "");
584 return SIPE_CAL_NO_DATA
;
587 if (!(free_busy
= sipe_cal_get_free_busy(buddy
))) {
588 purple_debug_info("sipe", "sipe_cal_get_status: no calendar data2 for %s, exiting\n", buddy
->name
);
589 return SIPE_CAL_NO_DATA
;
591 purple_debug_info("sipe", "sipe_cal_get_description: buddy->cal_free_busy=\n%s\n", free_busy
);
593 cal_start
= sipe_utils_str_to_time(buddy
->cal_start_time
);
595 ret
= sipe_cal_get_status0(free_busy
,
597 buddy
->cal_granularity
,
600 state_since
= sipe_cal_get_since_time(free_busy
,
602 buddy
->cal_granularity
,
606 if (since
) *since
= state_since
;
611 sipe_cal_get_switch_time(const gchar
*free_busy
,
619 time_t ret
= TIME_NULL
;
621 if ((index
< 0) || ((size_t) (index
+ 1) > strlen(free_busy
))) {
622 *to_state
= SIPE_CAL_NO_DATA
;
626 for (i
= index
+ 1; i
< strlen(free_busy
); i
++) {
627 int temp_status
= free_busy
[i
] - '0';
629 if (current_state
!= temp_status
) {
630 *to_state
= temp_status
;
631 return calStart
+ i
*granularity
*60;
639 sipe_cal_get_tz(struct sipe_cal_working_hours
*wh
,
640 time_t time_in_question
)
642 time_t dst_switch_time
= (*wh
).dst
.switch_time
;
643 time_t std_switch_time
= (*wh
).std
.switch_time
;
644 gboolean is_dst
= FALSE
;
646 /* No daylight savings */
647 if (dst_switch_time
== TIME_NULL
) {
651 if (dst_switch_time
< std_switch_time
) { /* North hemosphere - Europe, US */
652 if (time_in_question
>= dst_switch_time
&& time_in_question
< std_switch_time
) {
655 } else { /* South hemisphere - Australia */
656 if (time_in_question
>= dst_switch_time
|| time_in_question
< std_switch_time
) {
669 sipe_cal_mktime_of_day(struct tm
*sample_today_tm
,
670 const int shift_minutes
,
673 sample_today_tm
->tm_sec
= 0;
674 sample_today_tm
->tm_min
= shift_minutes
% 60;
675 sample_today_tm
->tm_hour
= shift_minutes
/ 60;
677 return sipe_mktime_tz(sample_today_tm
, tz
);
681 * Returns work day start and end in Epoch time
682 * considering the initial values are provided
683 * in contact's local time zone.
686 sipe_cal_get_today_work_hours(struct sipe_cal_working_hours
*wh
,
691 time_t now
= time(NULL
);
692 const char *tz
= sipe_cal_get_tz(wh
, now
);
693 struct tm
*remote_now_tm
= sipe_localtime_tz(&now
, tz
);
695 if (!strstr(wh
->days_of_week
, wday_names
[remote_now_tm
->tm_wday
])) { /* not a work day */
698 *next_start
= TIME_NULL
;
702 *end
= sipe_cal_mktime_of_day(remote_now_tm
, wh
->end_time
, tz
);
705 *start
= sipe_cal_mktime_of_day(remote_now_tm
, wh
->start_time
, tz
);
706 *next_start
= TIME_NULL
;
707 } else { /* calculate start of tomorrow's work day if any */
708 time_t tom
= now
+ 24*60*60;
709 struct tm
*remote_tom_tm
= sipe_localtime_tz(&tom
, sipe_cal_get_tz(wh
, tom
));
711 if (!strstr(wh
->days_of_week
, wday_names
[remote_tom_tm
->tm_wday
])) { /* not a work day */
712 *next_start
= TIME_NULL
;
715 *next_start
= sipe_cal_mktime_of_day(remote_tom_tm
, wh
->start_time
, sipe_cal_get_tz(wh
, tom
));
721 sipe_cal_is_in_work_hours(const time_t time_in_question
,
725 return !((time_in_question
>= end
) || (IS(start
) && time_in_question
< start
));
729 * Returns time closest to now. Choses only from times ahead of now.
730 * Returns TIME_NULL otherwise.
733 sipe_cal_get_until(const time_t now
,
734 const time_t switch_time
,
737 const time_t next_start
)
739 time_t ret
= TIME_NULL
;
740 int min_diff
= now
- ret
;
742 if (IS(switch_time
) && switch_time
> now
&& (switch_time
- now
) < min_diff
) {
743 min_diff
= switch_time
- now
;
746 if (IS(start
) && start
> now
&& (start
- now
) < min_diff
) {
747 min_diff
= start
- now
;
750 if (IS(end
) && end
> now
&& (end
- now
) < min_diff
) {
751 min_diff
= end
- now
;
754 if (IS(next_start
) && next_start
> now
&& (next_start
- now
) < min_diff
) {
755 min_diff
= next_start
- now
;
762 sipe_cal_get_free_busy(struct sipe_buddy
*buddy
)
764 /* do lazy decode if necessary */
765 if (!buddy
->cal_free_busy
&& buddy
->cal_free_busy_base64
) {
771 cal_dec64
= purple_base64_decode(buddy
->cal_free_busy_base64
, &cal_dec64_len
);
773 buddy
->cal_free_busy
= g_malloc0(cal_dec64_len
* 4 + 1);
775 http://msdn.microsoft.com/en-us/library/dd941537%28office.13%29.aspx
779 11, Out of facility (Oo)
781 http://msdn.microsoft.com/en-us/library/aa566048.aspx
785 3 Out of Office (OOF)
788 for (i
= 0; i
< cal_dec64_len
; i
++) {
789 #define TWO_BIT_MASK 0x03
790 char tmp
= cal_dec64
[i
];
791 buddy
->cal_free_busy
[j
++] = (tmp
& TWO_BIT_MASK
) + '0';
792 buddy
->cal_free_busy
[j
++] = ((tmp
>> 2) & TWO_BIT_MASK
) + '0';
793 buddy
->cal_free_busy
[j
++] = ((tmp
>> 4) & TWO_BIT_MASK
) + '0';
794 buddy
->cal_free_busy
[j
++] = ((tmp
>> 6) & TWO_BIT_MASK
) + '0';
796 buddy
->cal_free_busy
[j
++] = '\0';
800 return buddy
->cal_free_busy
;
804 sipe_cal_get_freebusy_base64(const char* freebusy_hex
)
808 guint shift_factor
= 0;
813 if (!freebusy_hex
) return NULL
;
815 len
= strlen(freebusy_hex
);
816 res_len
= len
/ 4 + 1;
817 res
= g_malloc0(res_len
);
819 res
[j
] |= (freebusy_hex
[i
++] - '0') << shift_factor
;
821 if (shift_factor
== 8) {
827 res_base64
= purple_base64_encode(res
, shift_factor
? res_len
: res_len
- 1);
833 sipe_cal_get_description(struct sipe_buddy
*buddy
)
837 int current_cal_state
;
838 time_t now
= time(NULL
);
839 time_t start
= TIME_NULL
;
840 time_t end
= TIME_NULL
;
841 time_t next_start
= TIME_NULL
;
843 int to_state
= SIPE_CAL_NO_DATA
;
844 time_t until
= TIME_NULL
;
846 gboolean has_working_hours
= (buddy
->cal_working_hours
!= NULL
);
847 const char *free_busy
;
848 const char *cal_states
[] = {_("Free"),
854 if (buddy
->cal_granularity
!= 15) {
855 purple_debug_info("sipe", "sipe_cal_get_description: granularity %d is unsupported, exiting.\n", buddy
->cal_granularity
);
859 /* to lazy load if needed */
860 free_busy
= sipe_cal_get_free_busy(buddy
);
861 purple_debug_info("sipe", "sipe_cal_get_description: buddy->cal_free_busy=\n%s\n", free_busy
? free_busy
: "");
863 if (!buddy
->cal_free_busy
|| !buddy
->cal_granularity
|| !buddy
->cal_start_time
) {
864 purple_debug_info("sipe", "sipe_cal_get_description: no calendar data, exiting");
868 cal_start
= sipe_utils_str_to_time(buddy
->cal_start_time
);
869 cal_end
= cal_start
+ 60 * (buddy
->cal_granularity
) * strlen(buddy
->cal_free_busy
);
871 current_cal_state
= sipe_cal_get_status0(free_busy
, cal_start
, buddy
->cal_granularity
, time(NULL
), &index
);
872 if (current_cal_state
== SIPE_CAL_NO_DATA
) {
873 purple_debug_info("sipe", "sipe_cal_get_description: calendar is undefined for present moment, exiting.\n");
877 switch_time
= sipe_cal_get_switch_time(free_busy
, cal_start
, buddy
->cal_granularity
, index
, current_cal_state
, &to_state
);
879 purple_debug_info("sipe", "\n* Calendar *\n");
880 if (buddy
->cal_working_hours
) {
881 sipe_cal_get_today_work_hours(buddy
->cal_working_hours
, &start
, &end
, &next_start
);
883 purple_debug_info("sipe", "Remote now timezone : %s\n", sipe_cal_get_tz(buddy
->cal_working_hours
, now
));
884 purple_debug_info("sipe", "std.switch_time(GMT): %s",
885 IS((*buddy
->cal_working_hours
).std
.switch_time
) ? asctime(gmtime(&((*buddy
->cal_working_hours
).std
.switch_time
))) : "\n");
886 purple_debug_info("sipe", "dst.switch_time(GMT): %s",
887 IS((*buddy
->cal_working_hours
).dst
.switch_time
) ? asctime(gmtime(&((*buddy
->cal_working_hours
).dst
.switch_time
))) : "\n");
888 purple_debug_info("sipe", "Remote now time : %s",
889 asctime(sipe_localtime_tz(&now
, sipe_cal_get_tz(buddy
->cal_working_hours
, now
))));
890 purple_debug_info("sipe", "Remote start time : %s",
891 IS(start
) ? asctime(sipe_localtime_tz(&start
, sipe_cal_get_tz(buddy
->cal_working_hours
, start
))) : "\n");
892 purple_debug_info("sipe", "Remote end time : %s",
893 IS(end
) ? asctime(sipe_localtime_tz(&end
, sipe_cal_get_tz(buddy
->cal_working_hours
, end
))) : "\n");
894 purple_debug_info("sipe", "Rem. next_start time: %s",
895 IS(next_start
) ? asctime(sipe_localtime_tz(&next_start
, sipe_cal_get_tz(buddy
->cal_working_hours
, next_start
))) : "\n");
896 purple_debug_info("sipe", "Remote switch time : %s",
897 IS(switch_time
) ? asctime(sipe_localtime_tz(&switch_time
, sipe_cal_get_tz(buddy
->cal_working_hours
, switch_time
))) : "\n");
899 purple_debug_info("sipe", "Local now time : %s",
900 asctime(localtime(&now
)));
901 purple_debug_info("sipe", "Local switch time : %s",
902 IS(switch_time
) ? asctime(localtime(&switch_time
)) : "\n");
904 purple_debug_info("sipe", "Calendar End (GMT) : %s", asctime(gmtime(&cal_end
)));
905 purple_debug_info("sipe", "current cal state : %s\n", cal_states
[current_cal_state
]);
906 purple_debug_info("sipe", "switch cal state : %s\n", cal_states
[to_state
] );
908 /* Calendar: string calculations */
911 ALGORITHM (don't delete)
912 (c)2009,2010 pier11 <pier11@operamail.com>
914 SOD = Start of Work Day
915 EOD = End of Work Day
916 NSOD = Start of tomorrow's Work Day
917 SW = Calendar status switch time
919 if current_cal_state == Free
920 until = min_t of SOD, EOD, NSOD, SW (min_t(x) = min(x-now) where x>now only)
924 if (!until && (cal_period_end > now + 8H))
925 until = cal_period_end
928 return "Currently %", current_cal_state
930 if (until - now > 8H)
931 if (current_cal_state == Free && (work_hours && !in work_hours(now)))
932 return "Outside of working hours for next 8 hours"
934 return "%s for next 8 hours", current_cal_state
936 if (current_cal_state == Free)
937 if (work_hours && until !in work_hours(now))
940 "%s", current_cal_state
941 " until %.2d:%.2d", until
943 "Currently %", current_cal_state
944 if (work_hours && until !in work_hours(until))
945 ". Outside of working hours at at %.2d:%.2d", until
947 ". %s at %.2d:%.2d", to_state, until
950 if (current_cal_state
< 1) { /* Free */
951 until
= sipe_cal_get_until(now
, switch_time
, start
, end
, next_start
);
956 if (!IS(until
) && (cal_end
- now
> 8*60*60))
960 return g_strdup_printf(_("Currently %s"), cal_states
[current_cal_state
]);
963 if (until
- now
> 8*60*60) {
964 /* Free & outside work hours */
965 if (current_cal_state
< 1 && has_working_hours
&& !sipe_cal_is_in_work_hours(now
, start
, end
)) {
966 return g_strdup(_("Outside of working hours for next 8 hours"));
968 return g_strdup_printf(_("%s for next 8 hours"), cal_states
[current_cal_state
]);
972 if (current_cal_state
< 1) { /* Free */
974 struct tm
*until_tm
= localtime(&until
);
976 if (has_working_hours
&& !sipe_cal_is_in_work_hours(now
, start
, end
)) {
977 tmp
= _("Not working");
979 tmp
= cal_states
[current_cal_state
];
981 return g_strdup_printf(_("%s until %.2d:%.2d"), tmp
, until_tm
->tm_hour
, until_tm
->tm_min
);
982 } else { /* Tentative or Busy or OOF */
985 struct tm
*until_tm
= localtime(&until
);
987 tmp
= g_strdup_printf(_("Currently %s"), cal_states
[current_cal_state
]);
988 if (has_working_hours
&& !sipe_cal_is_in_work_hours(until
, start
, end
)) {
989 res
= g_strdup_printf(_("%s. Outside of working hours at %.2d:%.2d"),
990 tmp
, until_tm
->tm_hour
, until_tm
->tm_min
);
994 res
= g_strdup_printf(_("%s. %s at %.2d:%.2d"), tmp
, cal_states
[to_state
], until_tm
->tm_hour
, until_tm
->tm_min
);
999 /* End of - Calendar: string calculations */