6 * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
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 * OCS2005 specific code
32 #include "sipe-common.h"
33 #include "http-conn.h" /* sipe-cal.h requires this */
35 #include "sipe-backend.h"
37 #include "sipe-core.h"
38 #include "sipe-core-private.h"
40 #include "sipe-ocs2005.h"
41 #include "sipe-schedule.h"
42 #include "sipe-status.h"
43 #include "sipe-utils.h"
47 void sipe_ocs2005_user_info_has_updated(struct sipe_core_private
*sipe_private
,
48 const sipe_xml
*xn_userinfo
)
50 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
51 const sipe_xml
*xn_states
;
53 g_free(sip
->user_states
);
54 sip
->user_states
= NULL
;
55 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
56 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
58 /* this is a hack-around to remove added newline after inner element,
59 * state in this case, where it shouldn't be.
60 * After several use of sipe_xml_stringify, amount of added newlines
61 * grows significantly.
64 gchar c
, *stripped
= orig
;
65 while ((c
= *orig
++)) {
66 if ((c
!= '\n') /* && (c != '\r') */) {
74 /* Publish initial state if not yet.
75 * Assuming this happens on initial responce to self subscription
76 * so we've already updated our UserInfo.
78 if (!sip
->initial_state_published
) {
79 sipe_ocs2005_presence_publish(sipe_private
, FALSE
);
81 sipe_cal_delayed_calendar_update(sipe_private
);
86 * OCS2005 presence XML messages
88 * Calendar publication entry
90 * @param legacy_dn (%s) Ex.: /o=EXCHANGE/ou=BTUK02/cn=Recipients/cn=AHHBTT
91 * @param fb_start_time_str (%s) Ex.: 2009-12-06T17:15:00Z
92 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAA......
94 #define SIPE_SOAP_SET_PRESENCE_CALENDAR \
95 "<calendarInfo xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\" mailboxId=\"%s\" startTime=\"%s\" granularity=\"PT15M\">%s</calendarInfo>"
98 * Note publication entry
100 * @param note (%s) Ex.: Working from home
102 #define SIPE_SOAP_SET_PRESENCE_NOTE_XML "<note>%s</note>"
105 * Note's OOF publication entry
107 #define SIPE_SOAP_SET_PRESENCE_OOF_XML "<oof></oof>"
110 * States publication entry for User State
112 * @param avail (%d) Availability 2007-style. Ex.: 9500
113 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z
114 * @param device_id (%s) epid. Ex.: 4c77e6ec72
115 * @param activity_token (%s) Ex.: do-not-disturb
117 #define SIPE_SOAP_SET_PRESENCE_STATES \
119 "<state avail=\"%d\" since=\"%s\" validWith=\"any-device\" deviceId=\"%s\" set=\"manual\" xsi:type=\"userState\">%s</state>"\
123 * Presentity publication entry.
125 * @param uri (%s) SIP URI without 'sip:' prefix. Ex.: fox@atlanta.local
126 * @param aggr_availability (%d) Ex.: 300
127 * @param aggr_activity (%d) Ex.: 600
128 * @param host_name (%s) Uppercased. Ex.: ATLANTA
129 * @param note_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_NOTE_XML
130 * @param oof_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_OOF_XML
131 * @param states_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_STATES
132 * @param calendar_info_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_CALENDAR
133 * @param device_id (%s) epid. Ex.: 4c77e6ec72
134 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z
135 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z
136 * @param user_input (%s) active, idle
138 #define SIPE_SOAP_SET_PRESENCE \
140 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" \
141 " xmlns:m=\"http://schemas.microsoft.com/winrtc/2002/11/sip\"" \
145 "<m:presentity xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" m:uri=\"sip:%s\">"\
146 "<m:availability m:aggregate=\"%d\"/>"\
147 "<m:activity m:aggregate=\"%d\"/>"\
148 "<deviceName xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\" name=\"%s\"/>"\
149 "<rtc:devicedata xmlns:rtc=\"http://schemas.microsoft.com/winrtc/2002/11/sip\" namespace=\"rtcService\">"\
150 "<![CDATA[<caps><renders_gif/><renders_isf/></caps>]]></rtc:devicedata>"\
151 "<userInfo xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\">"\
156 "<device xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\" deviceId=\"%s\" since=\"%s\" >"\
157 "<userInput since=\"%s\" >%s</userInput>"\
164 static void send_presence_soap(struct sipe_core_private
*sipe_private
,
165 gboolean do_publish_calendar
,
166 gboolean do_reset_status
)
168 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
169 struct sipe_calendar
* cal
= sip
->cal
;
173 gchar
*res_note
= NULL
;
174 gchar
*res_oof
= NULL
;
175 const gchar
*note_pub
= NULL
;
176 gchar
*states
= NULL
;
177 gchar
*calendar_data
= NULL
;
178 gchar
*epid
= get_epid(sipe_private
);
179 gchar
*from
= sip_uri_self(sipe_private
);
180 time_t now
= time(NULL
);
181 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
182 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
183 const char *user_input
;
184 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
186 if (oof_note
&& sip
->note
) {
187 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
188 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
191 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
193 if (!sip
->initial_state_published
||
195 sipe_set_initial_status(sipe_private
);
200 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
201 cal
->published
= TRUE
;
202 } else if (sip
->note
) {
203 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
206 sip
->is_oof_note
= FALSE
;
209 note_pub
= sip
->note
;
210 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
216 /* to protocol internal plain text format */
217 tmp
= sipe_backend_markup_strip_html(note_pub
);
218 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
223 if (!do_reset_status
) {
224 if (sipe_status_changed_by_user(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
226 const gchar
*activity_token
;
227 int avail_2007
= sipe_ocs2007_availability_from_status(sip
->status
,
230 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
236 else /* preserve existing publication */
238 if (sip
->user_states
) {
239 states
= g_strdup(sip
->user_states
);
243 /* do nothing - then User state will be erased */
245 sip
->initial_state_published
= TRUE
;
248 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
250 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
251 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
252 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
253 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
256 g_free(fb_start_str
);
257 g_free(free_busy_base64
);
260 user_input
= (sipe_status_changed_by_user(sipe_private
) ||
261 sipe_is_user_available(sipe_private
)) ?
265 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
266 sipe_private
->username
,
267 sipe_ocs2005_availability_from_status(sipe_private
),
268 sipe_ocs2005_activity_from_status(sipe_private
),
269 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
270 res_note
? res_note
: "",
271 res_oof
? res_oof
: "",
272 states
? states
: "",
273 calendar_data
? calendar_data
: "",
282 g_free(calendar_data
);
283 g_free(since_time_str
);
286 sip_soap_raw_request_cb(sipe_private
, from
, body
, NULL
, NULL
);
291 void sipe_ocs2005_presence_publish(struct sipe_core_private
*sipe_private
,
292 gboolean do_publish_calendar
)
294 return send_presence_soap(sipe_private
, do_publish_calendar
, FALSE
);
297 void sipe_ocs2005_reset_status(struct sipe_core_private
*sipe_private
)
299 return send_presence_soap(sipe_private
, FALSE
, TRUE
);
302 static void update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
303 struct sipe_buddy
*sbuddy
,
304 struct sipe_core_private
*sipe_private
)
306 sipe_apply_calendar_status(sipe_private
, sbuddy
, NULL
);
310 * Updates contact's status
311 * based on their calendar information.
313 static void update_calendar_status(struct sipe_core_private
*sipe_private
,
314 SIPE_UNUSED_PARAMETER
void *unused
)
316 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
317 g_hash_table_foreach(sipe_private
->buddies
,
318 (GHFunc
)update_calendar_status_cb
,
321 /* repeat scheduling */
322 sipe_ocs2005_schedule_status_update(sipe_private
,
323 time(NULL
) + 3 * 60 /* 3 min */);
327 * Schedules process of contacts' status update
328 * based on their calendar information.
329 * Should be scheduled to the beginning of every
330 * 15 min interval, like:
331 * 13:00, 13:15, 13:30, 13:45, etc.
333 void sipe_ocs2005_schedule_status_update(struct sipe_core_private
*sipe_private
,
334 time_t calculate_from
)
336 #define SCHEDULE_INTERVAL 15 * 60 /* 15 min */
338 /* start of the beginning of closest 15 min interval. */
339 time_t next_start
= (calculate_from
/ SCHEDULE_INTERVAL
+ 1) * SCHEDULE_INTERVAL
;
341 SIPE_DEBUG_INFO("sipe_ocs2005_schedule_status_update: calculate_from time: %s",
342 asctime(localtime(&calculate_from
)));
343 SIPE_DEBUG_INFO("sipe_ocs2005_schedule_status_update: next start time : %s",
344 asctime(localtime(&next_start
)));
346 sipe_schedule_seconds(sipe_private
,
347 "<+2005-cal-status>",
349 next_start
- time(NULL
),
350 update_calendar_status
,