/* sunone-connection.c * * Copyright (C) 2002-2004 Sun Microsystems, Inc * * AUTHORS * Jack Jia * Harry Lu * Alfred Peng * Jedy Wang * Rodrigo Moya * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "sunone-util.h" #include "sunone-connection.h" #include "sunone-message.h" #include "calendar/cal-backend-wcap.h" #define REFRESH_INTERVAL 5 * 60 * 1000 #define IS_CONNECTED(connection,return_value) {if (!connection->priv->wcap_session_id) return return_value;} #define NEW_URI(uri) {uri = soup_uri_new (connection->priv->server_uri); \ if (uri && uri->path) {g_free (uri->path); uri->path = NULL;} \ if (uri && uri->query) {g_free (uri->query); uri->query = NULL;}} \ if (uri) uri->broken_encoding = TRUE; #define IS_CNC_WCAP_2_0(connection) ( !strncmp (connection->priv->wcap_version, "2.0", 3) ) struct _SunOneConnectionPrivate { SoupSession *soup_session; char *server_uri; char *user; char *password; char *wcap_session_id; char *wcap_version; SunOneConnectionPreferences *prefs; GHashTable *calendar_properties; guint timeout_id; gboolean is_open; GHashTable *wcaps; }; static gchar *get_acls_string (GList *acls); static gboolean reconnect_timeout_cb (gpointer user_data); static void sunone_connection_class_init (SunOneConnectionClass *klass); static void sunone_connection_init (SunOneConnection *connection); static void sunone_connection_dispose (GObject *object); static void sunone_connection_finalize (GObject *object); static guint sunone_connection_storeevents_by_mail (SunOneConnection *connection, const char *calid, icalcomponent *icalcomp, SunOneMethod method, SunOneModType mod, gboolean expand, gboolean allday, icalcomponent **return_icalcomp, gboolean mail, const char *account_email); static GObjectClass *parent_class = NULL; static char * query_encode (const char *part, gboolean escape_unsafe, const char *escape_extra) { char *work, *p; /* worst case scenario = 3 times the initial */ p = work = g_malloc (3 * strlen (part) + 1); while (*part) { if (((guchar) *part >= 127) || ((guchar) *part <= ' ') || (escape_unsafe && strchr ("\"%#<>{}|\\^~[]`", *part)) || (escape_extra && strchr (escape_extra, *part))) { sprintf (p, "%%%.02hX", (guchar) *part++); p += 3; } else *p++ = *part++; } *p = '\0'; return work; } static void append_to_query (SoupUri *uri, const char *param, const char *value) { char *tmp, *enc; if (!value) return; enc = query_encode (value, FALSE, " &:+"); tmp = g_strdup_printf ("%s&%s=%s", uri->query, param, enc); g_free (enc); g_free (uri->query); uri->query = tmp; } static void append_to_query_int (SoupUri *uri, const char *param, int value) { char *tmp; tmp = g_strdup_printf ("%s&%s=%d", uri->query, param, value); g_free (uri->query); uri->query = tmp; } static char * get_stringv (const char **stringv) { const char **s; char *string; string = NULL; for (s = stringv; *s != NULL; s++) { char *tmp = string; if (string) { string = g_strconcat (string, ";", *s, NULL); g_free (tmp); } else { string = g_strdup (*s); } } return string; } static void append_to_query_stringv (SoupUri *uri, const char *param, const char **stringv) { char *string; string = get_stringv (stringv); if (string) append_to_query (uri, param, string); else append_to_query (uri, param, ""); if (string) g_free (string); } static void append_to_query_text (SoupUri *uri, const char *param, ECalComponentText *text) { if (text->value) append_to_query (uri, param, text->value); else append_to_query (uri, param, ""); } static void append_to_query_text_list (SoupUri *uri, const char *param, GSList *text_list) { GSList *l; char *string; string = NULL; for (l = text_list; l != NULL; l = l->next) { ECalComponentText *text = l->data; char *tmp = string; if (string) { string = g_strconcat (string, ";", text->value, NULL); g_free (tmp); } else { string = g_strdup (text->value); } } append_to_query (uri, param, string ? string : ""); e_cal_component_free_text_list (text_list); if (string) g_free (string); } static void append_to_query_datetime (SoupUri *uri, const char *param, ECalComponentDateTime *dt, gboolean zero) { const char *string; if (!dt->value || !icaltime_is_valid_time (*dt->value)) { if (zero) append_to_query (uri, param, "0"); return; } string = icaltime_as_ical_string (*dt->value); append_to_query (uri, param, string); e_cal_component_free_datetime (dt); } static void append_to_query_attendees (SoupUri *uri, const char *param, GSList *attendee_list, SunOneConnection *connection, const char *calid, const char *email) { GSList *l; GString *string; string = g_string_new (NULL); for (l = attendee_list; l != NULL; l = l->next) { ECalComponentAttendee *attendee = l->data; /* PARTSTAT */ switch (attendee->status) { case ICAL_PARTSTAT_NEEDSACTION: g_string_append (string, "PARTSTAT=NEEDS-ACTION"); break; case ICAL_PARTSTAT_ACCEPTED: g_string_append (string, "PARTSTAT=ACCEPTED"); break; case ICAL_PARTSTAT_DECLINED: g_string_append (string, "PARTSTAT=DECLINED"); break; case ICAL_PARTSTAT_TENTATIVE: g_string_append (string, "PARTSTAT=TENTATIVE"); break; case ICAL_PARTSTAT_DELEGATED: g_string_append (string, "PARTSTAT=DELEGATED"); break; case ICAL_PARTSTAT_COMPLETED: g_string_append (string, "PARTSTAT=COMPLETED"); break; case ICAL_PARTSTAT_INPROCESS: g_string_append (string, "PARTSTAT=INPROCESS"); break; default: g_string_append (string, "PARTSTAT=NEEDS-ACTION"); } /* CUTYPE */ switch (attendee->cutype) { case ICAL_CUTYPE_INDIVIDUAL: g_string_append (string, "^CUTYPE=INDIVIDUAL"); break; case ICAL_CUTYPE_GROUP: g_string_append (string, "^CUTYPE=GROUP"); break; case ICAL_CUTYPE_RESOURCE: g_string_append (string, "^CUTYPE=RESOURCE"); break; case ICAL_CUTYPE_ROOM: g_string_append (string, "^CUTYPE=ROOM"); break; default: g_string_append (string, "^CUTYPE=INDIVIDUAL"); } /* MEMBER */ if (attendee->member) g_string_append_printf (string, "^MEMBER=%s", attendee->member); /* ROLE */ switch (attendee->role) { case ICAL_ROLE_CHAIR: g_string_append (string, "^ROLE=CHAIR"); break; case ICAL_ROLE_REQPARTICIPANT: g_string_append (string, "^ROLE=REQ-PARTICIPANT"); break; case ICAL_ROLE_OPTPARTICIPANT: g_string_append (string, "^ROLE=OPT-PARTICIPANT"); break; case ICAL_ROLE_NONPARTICIPANT: g_string_append (string, "^ROLE=NON-PARTICIPANT"); break; default: g_string_append (string, "^ROLE=REQ-PARTICIPANT"); } /* RSVP */ g_string_append_printf (string, "^RSVP=%s", attendee->rsvp ? "TRUE" : "FALSE"); /* DELEGATED-TO */ if (attendee->delto) g_string_append_printf (string, "^DELEGATED-TO=%s", attendee->delto); /* DELEGATED-FROM */ if (attendee->delfrom) g_string_append_printf (string, "^DELEGATED-FROM=%s", attendee->delfrom); /* SENT-BY */ if (attendee->sentby) g_string_append_printf (string, "^SENT-BY=%s", attendee->sentby); /* CN */ if (attendee->cn) g_string_append_printf (string, "^CN=%s", attendee->cn); /* LANG */ if (attendee->language) g_string_append_printf (string, "^LANGUAGE=%s", attendee->language); /* Email Address */ if (!strncasecmp (attendee->value, "mailto:", 7)) { if (strchr (attendee->value, '@')) { /* mailto a real email address, * if account is in the attendees, * change its email to calid */ if(!strcasecmp (email, attendee->value + 7)) { g_string_append_printf (string, "^%s;", calid); } else g_string_append_printf (string, "^%s;", attendee->value); } else /* mailto, but a calid, strip it */ g_string_append_printf (string, "^%s;", attendee->value + 7); } else g_string_append_printf (string, "^%s;", attendee->value); } if (string->len > 0) { char *enc; g_string_truncate (string, string->len - 1); enc = query_encode (string->str, FALSE, " "); append_to_query (uri, param, enc); g_free (enc); } e_cal_component_free_attendee_list (attendee_list); g_string_free (string, TRUE); } static void append_to_query_exdate_list (SoupUri *uri, const char *param, GSList *dt_list) { GSList *l; char *string; string = NULL; for (l = dt_list; l != NULL; l = l->next) { ECalComponentDateTime *dt = l->data; char *tmp = string; if (!dt->value) continue; if (string) { string = g_strconcat (string, ";", icaltime_as_ical_string (*dt->value), NULL); g_free (tmp); } else { string = g_strdup (icaltime_as_ical_string (*dt->value)); } } if (string) append_to_query (uri, param, string); e_cal_component_free_exdate_list (dt_list); g_free (string); } static void append_to_query_recur_list (SoupUri *uri, const char *param, GSList *r_list) { GSList *l; char *string; string = NULL; for (l = r_list; l != NULL; l = l->next) { struct icalrecurrencetype *recur = l->data; char *tmp = string, *enc; enc = query_encode (icalrecurrencetype_as_string (recur), FALSE, ";="); if (string) { string = g_strconcat (string, ";\"", enc, "\"", NULL); g_free (tmp); } else { string = g_strdup_printf ("\"%s\"", enc); } g_free (enc); } if (string) append_to_query (uri, param, string); e_cal_component_free_recur_list (r_list); g_free (string); } static void append_to_query_classification (SoupUri *uri, const char *param, ECalComponentClassification class) { switch (class) { case E_CAL_COMPONENT_CLASS_PUBLIC: append_to_query (uri, param, "PUBLIC"); break; case E_CAL_COMPONENT_CLASS_PRIVATE: append_to_query (uri, param, "PRIVATE"); break; case E_CAL_COMPONENT_CLASS_CONFIDENTIAL: append_to_query (uri, param, "CONFIDENTIAL"); break; default: break; } } static void append_to_query_status (SoupUri *uri, const char *param, icalproperty_status status) { switch (status) { case ICAL_STATUS_CONFIRMED: append_to_query_int (uri, param, 0); break; case ICAL_STATUS_CANCELLED: append_to_query_int (uri, param, 1); break; case ICAL_STATUS_TENTATIVE: append_to_query_int (uri, param, 2); break; case ICAL_STATUS_NEEDSACTION: append_to_query_int (uri, param, 3); break; case ICAL_STATUS_COMPLETED: append_to_query_int (uri, param, 4); break; case ICAL_STATUS_INPROCESS: append_to_query_int (uri, param, 5); break; case ICAL_STATUS_DRAFT: append_to_query_int (uri, param, 6); break; case ICAL_STATUS_FINAL: append_to_query_int (uri, param, 7); break; default: break; } } static void append_to_query_compstate (SoupUri *uri, const char *param, SunOneCompState state) { const char *statev[7]; int i; i = 0; if (state == REQUEST_ALL || state & (REPLY_DECLINED & REPLY_ACCEPTED & REQUEST_COMPLETED & REQUEST_NEEDS_ACTION & REQUEST_NEEDS_NOACTION & REQUEST_PENDING & REQUEST_WAITFORREPLY)) { statev[i++] = "ALL"; goto append; } if (state & REPLY_DECLINED) statev[i++] = "REPLY-DECLINED"; if (state & REPLY_ACCEPTED) statev[i++] = "REPLY-ACCEPTED"; if (state & REQUEST_COMPLETED) statev[i++] = "REQUEST-COMPLETED"; if (state & REQUEST_NEEDS_ACTION) statev[i++] = "REQUEST-NEEDS-ACTION"; if (state & REQUEST_NEEDS_NOACTION) statev[i++] = "REQUEST-NEEDSNOACTION"; if (state & REQUEST_PENDING) statev[i++] = "REQUEST-PENDING"; if (state & REQUEST_WAITFORREPLY) statev[i++] = "REQUEST-WAITFORREPLY"; append: statev[i] = NULL; if (i > 0) append_to_query_stringv (uri, param, statev); } static void append_to_query_geo (SoupUri *uri, const char *param, struct icalgeotype *geo) { char *tmp; if (!geo) return; tmp = g_strdup_printf ("%s&%s=%f;%f", uri->query, param, geo->lat, geo->lon); g_free (uri->query); uri->query = tmp; } static void append_to_query_alarms (SoupUri *uri, SunOneConnection *connection, ECalComponent *comp) { GList *auids, *l; ECalComponentAlarm *alarm; ECalComponentAlarmAction action; ECalComponentAlarmTrigger trigger; char *atype; const char *dtstring; if (!e_cal_component_has_alarms (comp)) { append_to_query (uri, "alarmAudio", ""); append_to_query (uri, "alarmPopup", ""); append_to_query (uri, "alarmStart", ""); append_to_query (uri, "alarmEmails", ""); append_to_query (uri, "alarmDescription", ""); return; } auids = e_cal_component_get_alarm_uids (comp); for (l = auids; l != NULL; l = l->next) { const char *auid = l->data; alarm = e_cal_component_get_alarm (comp, auid); e_cal_component_alarm_get_action (alarm, &action); switch (action) { case E_CAL_COMPONENT_ALARM_AUDIO: atype = "alarmAudio"; break; case E_CAL_COMPONENT_ALARM_DISPLAY: atype = "alarmPopup"; break; case E_CAL_COMPONENT_ALARM_EMAIL: atype = "alarmStart"; break; case E_CAL_COMPONENT_ALARM_PROCEDURE: default: atype = NULL; } e_cal_component_alarm_get_trigger (alarm, &trigger); if (trigger.type == E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE) dtstring = icaltime_as_ical_string (trigger.u.abs_time); else dtstring = icaldurationtype_as_ical_string (trigger.u.rel_duration); if (atype != NULL && dtstring != NULL) { ECalComponentText text; append_to_query (uri, atype, dtstring); e_cal_component_alarm_get_description (alarm, &text); if (text.value) append_to_query (uri, "alarmDescription", text.value); if (action == E_CAL_COMPONENT_ALARM_EMAIL) { if (e_cal_component_alarm_has_attendees (alarm)) { GSList *attendee_list, *l; GString *string; e_cal_component_alarm_get_attendee_list (alarm, &attendee_list); string = g_string_new (NULL); for (l = attendee_list; l != NULL; l = l->next) { ECalComponentAttendee *attendee = l->data; /* Email Address */ if (!attendee->value) continue; if (!strncasecmp (attendee->value, "mailto:", 7)) g_string_append_printf (string, "%s;", attendee->value + 7); else g_string_append_printf (string, "%s;", attendee->value); } if (string->len > 0) { g_string_truncate (string, string->len - 1); append_to_query (uri, "alarmEmails", string->str); } e_cal_component_free_attendee_list (attendee_list); g_string_free (string, TRUE); } } } e_cal_component_alarm_free (alarm); } cal_obj_uid_list_free (auids); } static char * type_to_string (SunOneCompType type) { switch (type) { case TYPE_TODO: return "todo"; case TYPE_EVENT: return "event"; case TYPE_ALL: return "all"; } return "all"; } G_DEFINE_TYPE (SunOneConnection, sunone_connection, G_TYPE_OBJECT) static void sunone_connection_class_init (SunOneConnectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_ref (G_TYPE_OBJECT); object_class->dispose = sunone_connection_dispose; object_class->finalize = sunone_connection_finalize; } static void sunone_connection_init (SunOneConnection *object) { SunOneConnection *connection = SUNONE_CONNECTION (object); SunOneConnectionPrivate *priv; connection->priv = g_new0 (SunOneConnectionPrivate, 1); priv = connection->priv; priv->soup_session = soup_session_sync_new (); priv->calendar_properties = g_hash_table_new (g_str_hash, g_str_equal); priv->timeout_id = -1; priv->is_open = FALSE; priv->wcaps = g_hash_table_new (g_str_hash, g_str_equal); } static gboolean parse_session_object (SunOneConnection *connection, SoupMessage *msg) { SunOneConnectionPrivate *priv = connection->priv; icalcomponent *icalcomp; icalcomponent_kind kind; icalproperty *icalprop; g_return_val_if_fail (msg != NULL, FALSE); icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!icalcomp) return FALSE; kind = icalcomponent_isa (icalcomp); if (kind != ICAL_VCALENDAR_COMPONENT) { icalcomponent_free (icalcomp); return FALSE; } /* look for the properties */ icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (icalprop) { const char *x_name, *x_val; x_name = icalproperty_get_x_name (icalprop); x_val = icalproperty_get_x (icalprop); if (!strcmp (x_name, "X-NSCP-WCAP-SESSION-ID")) { if (priv->wcap_session_id) g_free (priv->wcap_session_id); priv->wcap_session_id = g_strdup (x_val); } icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } icalcomponent_free (icalcomp); return priv->wcap_session_id != NULL; } static guint sunone_login (SunOneConnection *connection, gboolean refresh) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *login_uri; SoupMessage *msg; guint error_code = SOUP_STATUS_CANT_CONNECT; char *encode_password = NULL; /* build the URI to connect to the server */ NEW_URI (login_uri); if (!login_uri) { g_message ("'%s' is an invalid URI", priv->server_uri); return SOUP_STATUS_BAD_REQUEST; } /* escape some chars in password */ if (priv->password) encode_password = query_encode (priv->password, FALSE, "=+&"); login_uri->path = g_strdup ("/login.wcap"); login_uri->query = g_strdup_printf ("user=%s&password=%s&fmt-out=text/calendar", priv->user ? priv->user : "", encode_password ? encode_password : ""); if (encode_password) g_free (encode_password); /* send the connection request to the server */ msg = sunone_message_new_from_uri (login_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); error_code = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { if (parse_session_object (connection, msg)) { if (priv->timeout_id == -1) priv->timeout_id = g_timeout_add (REFRESH_INTERVAL, (GSourceFunc) reconnect_timeout_cb, connection); priv->is_open = TRUE; } } g_object_unref (msg); soup_uri_free (login_uri); return error_code; } static guint sunone_logout (SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *logout_uri; SoupMessage *msg; guint error_code; if (priv->timeout_id != -1) { g_source_remove (priv->timeout_id); priv->timeout_id = -1; } IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* build the URI to connect to the server */ NEW_URI (logout_uri); if (!logout_uri) { g_message ("'%s' is an invalid URI", priv->server_uri); return FALSE; } logout_uri->path = g_strdup ("/logout.wcap"); logout_uri->query = g_strdup_printf ("id=%s&fmt-out=text/calendar", priv->wcap_session_id); /* send the connection request to the server */ msg = sunone_message_new_from_uri (logout_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); error_code = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (error_code)) { if (priv->wcap_session_id) g_free (priv->wcap_session_id); priv->wcap_session_id = NULL; priv->is_open = FALSE; } g_object_unref (msg); soup_uri_free (logout_uri); return error_code; } static gboolean reconnect_timeout_cb (gpointer user_data) { SunOneConnection *connection = (SunOneConnection *) user_data; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), FALSE); sunone_login (connection, TRUE); return TRUE; } static void free_calprops (gpointer key, gpointer value, gpointer user_data) { g_free (key); sunone_connection_free_calprops (value); } static void free_wcaps (gpointer key, gpointer value, gpointer user_data) { g_free (key); } static void sunone_connection_dispose (GObject *object) { SunOneConnection *connection = (SunOneConnection *) object; SunOneConnectionPrivate *priv = connection->priv; g_return_if_fail (IS_SUNONE_CONNECTION (connection)); sunone_logout (connection); /* free memory */ if (priv) { if (priv->server_uri) { g_free (priv->server_uri); priv->server_uri = NULL; } if (priv->user) { g_free (priv->user); priv->user = NULL; } if (priv->password) { g_free (priv->password); priv->password = NULL; } if (priv->wcap_session_id) { g_free (priv->wcap_session_id); priv->wcap_session_id = NULL; } if (priv->wcap_version) { g_free (priv->wcap_version); priv->wcap_version = NULL; } if (priv->prefs) { sunone_connection_free_preferences (priv->prefs); priv->prefs = NULL; } if (priv->calendar_properties) { g_hash_table_foreach (priv->calendar_properties, (GHFunc) free_calprops, NULL); g_hash_table_destroy (priv->calendar_properties); priv->calendar_properties = NULL; } if (priv->wcaps) { g_hash_table_foreach (priv->wcaps, (GHFunc)free_wcaps, NULL); g_hash_table_destroy (priv->wcaps); priv->wcaps = NULL; } g_free (priv); connection->priv = NULL; } if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); } static void sunone_connection_finalize (GObject *object) { if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (object); } SunOneConnection * sunone_connection_new (const char *server_uri, const char *proxy_uri, const char *user, const char *password) { SunOneConnection *connection; SunOneConnectionPrivate *priv; SoupUri *suri; connection = g_object_new (SUNONE_CONNECTION_TYPE, NULL); priv = connection->priv; priv->server_uri = g_strdup (server_uri); priv->user = g_strdup (user); priv->password = g_strdup (password); priv->is_open = FALSE; if (proxy_uri && priv->soup_session) { suri = soup_uri_new (proxy_uri); g_object_set (G_OBJECT (priv->soup_session), SOUP_SESSION_PROXY_URI, suri, NULL); soup_uri_free (suri); } return connection; } guint sunone_connection_login (SunOneConnection *connection) { return sunone_login (connection, FALSE); } guint sunone_connection_logout (SunOneConnection *connection) { return sunone_logout (connection); } gboolean sunone_connection_is_open (SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), FALSE); return priv->is_open; } static void copy_subscription_item (gpointer item, gpointer user_data) { SunOneCalendarSubscription *copy, *orig; GList **list = (GList **) user_data; orig = (SunOneCalendarSubscription *) item; if (!orig) return; copy = g_new0 (SunOneCalendarSubscription, 1); copy->calid = g_strdup (orig->calid); copy->display_name = g_strdup (orig->display_name); *list = g_list_append (*list, copy); } static SunOneConnectionPreferences * clone_prefs (SunOneConnectionPreferences *prefs) { SunOneConnectionPreferences *copy; copy = g_new0 (SunOneConnectionPreferences, 1); copy->common_name = g_strdup (prefs->common_name); copy->given_name = g_strdup (prefs->given_name); copy->mail = g_strdup (prefs->mail); copy->preferred_language = g_strdup (prefs->preferred_language); copy->surname = g_strdup (prefs->surname); copy->default_calendar = g_strdup (prefs->default_calendar); copy->timezone = g_strdup (prefs->timezone); g_list_foreach (prefs->subscriptions, (GFunc) copy_subscription_item, ©->subscriptions); copy->freebusy = g_strdup (prefs->freebusy); copy->allow_change_password = prefs->allow_change_password; copy->allow_create_calendars = prefs->allow_create_calendars; copy->allow_delete_calendars = prefs->allow_delete_calendars; copy->allow_public_writable_calendars = prefs->allow_public_writable_calendars; return copy; } static gboolean already_in_subscriptions (GList *subscriptions, const char *calid) { GList *l; for (l = subscriptions; l != NULL; l = l->next) { SunOneCalendarSubscription *sub = l->data; if (!strcmp (sub->calid, calid)) return TRUE; } return FALSE; } static void parse_subscriptions (SunOneConnection *connection, const gchar *val) { SunOneConnectionPrivate *priv = connection->priv; gchar **items; gchar **parts; SunOneCalendarSubscription *sub; gint n = 0; items = g_strsplit (val, ",", 0); if (!items) return; while (items[n]) { parts = g_strsplit (items[n], "$", 0); if (parts) { if (parts[0] != NULL && !already_in_subscriptions (priv->prefs->subscriptions, parts[0])) { sub = g_new0 (SunOneCalendarSubscription, 1); sub->calid = g_strdup (parts[0]); if (parts[1] != NULL) sub->display_name = g_strdup (parts[1]); else sub->display_name = g_strdup (parts[0]); priv->prefs->subscriptions = g_list_append ( priv->prefs->subscriptions, sub); } g_strfreev (parts); } n++; } g_strfreev (items); } #define STRING_TO_BOOLEAN(str) ((str) && !strcmp ((str), "yes") ? TRUE : FALSE) const char * sunone_connection_get_wcap_version(SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); return (const char *)priv->wcap_version; } void sunone_connection_set_wcap_version (SunOneConnection *connection, const char *wcap_version) { SunOneConnectionPrivate *priv = connection->priv; g_return_if_fail (IS_SUNONE_CONNECTION (connection)); priv->wcap_version = g_strdup (wcap_version); } const char * sunone_connection_get_user(SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); return (const char *)priv->user; } void sunone_connection_set_user (SunOneConnection *connection, const char *user) { SunOneConnectionPrivate *priv = connection->priv; g_return_if_fail (IS_SUNONE_CONNECTION (connection)); g_free (priv->user); priv->user = g_strdup (user); } static void check_default_calendar (SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; GList *l; SunOneCalendarSubscription *sub; gboolean found = FALSE; /* Not great but it is possible to have an empty icsCalendar value */ if (!priv->prefs->default_calendar) priv->prefs->default_calendar = g_strdup (priv->user); /* Make sure default_calendar is in subscriptions */ for (l = priv->prefs->subscriptions; l!= NULL; l = l->next) { sub = l->data; if (!strcmp (priv->prefs->default_calendar, sub->calid)) { found = TRUE; break; } } if (!found) { sub = g_new0 (SunOneCalendarSubscription, 1); sub->calid = g_strdup (priv->prefs->default_calendar); sub->display_name = g_strdup (priv->prefs->default_calendar); priv->prefs->subscriptions = g_list_append ( priv->prefs->subscriptions, sub); } } SunOneConnectionPreferences * sunone_connection_get_preferences (SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *get_uri; SoupMessage *msg; guint ret; icalcomponent *icalcomp; icalproperty *icalprop; SunOneConnectionPreferences *clone; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); IS_CONNECTED (connection, NULL); g_static_mutex_lock (&mutex); if (priv->prefs) { sunone_connection_free_preferences (priv->prefs); priv->prefs = NULL; } /* prepare the URI */ NEW_URI (get_uri); get_uri->path = g_strdup ("/get_userprefs.wcap"); get_uri->query = g_strdup_printf ( "id=%s&fmt-out=text/calendar", priv->wcap_session_id); /* send the request to the server */ msg = sunone_message_new_from_uri (get_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); ret = msg->status_code; soup_uri_free (get_uri); if (!SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { g_object_unref (msg); g_static_mutex_unlock (&mutex); return NULL; } /* parse the returned data */ icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); g_object_unref (msg); if (!icalcomp) { g_static_mutex_unlock (&mutex); return NULL; } priv->prefs = g_new0 (SunOneConnectionPreferences, 1); icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (icalprop) { const char *x_name, *x_val; x_name = icalproperty_get_x_name (icalprop); x_val = icalproperty_get_x (icalprop); if (!strcmp (x_name, "X-NSCP-WCAP-PREF-cn")) priv->prefs->common_name = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-givenName")) priv->prefs->given_name = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-mail")) priv->prefs->mail = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-preferredlanguage")) priv->prefs->preferred_language = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-sn")) priv->prefs->surname = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-icsCalendar")) priv->prefs->default_calendar = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-icsTimezone")) priv->prefs->timezone = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-icsSubscribed")) parse_subscriptions (connection, x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-icsFreeBusy")) priv->prefs->freebusy = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-PREF-ceDefaultAlarmEmail")) priv->prefs->alarm_mail = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-SERVER-PREF-ceSingleCalendarTZID")) priv->prefs->single_calendar_tzid = STRING_TO_BOOLEAN (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-SERVER-PREF-allowchangepassword")) priv->prefs->allow_change_password = STRING_TO_BOOLEAN (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-SERVER-PREF-allowcreatecalendars")) priv->prefs->allow_create_calendars = STRING_TO_BOOLEAN (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-SERVER-PREF-allowdeletecalendars")) priv->prefs->allow_delete_calendars = STRING_TO_BOOLEAN (x_val); else if (!strcmp (x_name, "X-NSCP-WCAP-SERVER-PREF-allowpublicwritablecalendars")) priv->prefs->allow_public_writable_calendars = STRING_TO_BOOLEAN (x_val); icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } icalcomponent_free (icalcomp); check_default_calendar (connection); clone = clone_prefs (priv->prefs); g_static_mutex_unlock (&mutex); return clone; } static gchar * replace_colons (const gchar *s) { gint i; gchar *tmp; tmp = g_strdup (s); for (i = 0; i < strlen (tmp); i++) { if (tmp[i] == ':') tmp[i] = '^'; } return tmp; } static gchar * subscriptions_to_string (SunOneConnection *connection, const gchar *default_calendar, GList *list) { GString *str; gchar *retval; gchar *tmp; SunOneCalendarSubscription *sub; GList *l; gboolean first_time; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); /* add the other subscriptions */ first_time = TRUE; str = g_string_new (""); for (l = list; l != NULL; l = l->next) { sub = l->data; if (!sub) continue; if (!strcmp (sub->calid, default_calendar)) continue; tmp = replace_colons (sub->calid); if (first_time) { str = g_string_append (str, "&add_attrs=icsSubscribed="); first_time = FALSE; } else str = g_string_append (str, ":icsSubscribed="); str = g_string_append (str, tmp); g_free (tmp); str = g_string_append (str, "$"); if (sub->display_name && *sub->display_name) { tmp = replace_colons (sub->display_name); str = g_string_append (str, tmp); g_free (tmp); } } retval = query_encode (str->str, TRUE, ","); g_string_free (str, TRUE); return retval; } guint sunone_connection_set_preferences (SunOneConnection *connection, SunOneConnectionPreferences *prefs) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *set_uri; SoupMessage *msg; gchar *s, *s1 = NULL; guint retval; GList *l; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (prefs != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* first, set subscriptions to the default calendar */ NEW_URI (set_uri); set_uri->path = g_strdup ("/set_userprefs.wcap"); for (l = prefs->subscriptions; l != NULL; l = l->next) { SunOneCalendarSubscription *sub = l->data; if (!strcmp (sub->calid, prefs->default_calendar)) { s1 = g_strdup_printf ("%s$%s", sub->calid, sub->display_name); s = replace_colons (s1); g_free (s1); s1 = query_encode (s, TRUE, ","); set_uri->query = g_strdup_printf ( "id=%s&fmt-out=text/calendar&convertCalid=1&set_attrs=icsSubscribed=%s", priv->wcap_session_id, s1); msg = sunone_message_new_from_uri (set_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; g_free (s); g_free (s1); g_object_unref (msg); soup_uri_free (set_uri); if (!SUNONE_ERROR_IS_SUCCESSFUL (retval)) return retval; break; } } /* prepare the URI */ NEW_URI (set_uri); set_uri->path = g_strdup ("/set_userprefs.wcap"); /* FIXME: we only set subscriptions so far */ s = subscriptions_to_string (connection, prefs->default_calendar, prefs->subscriptions); if (!s) { soup_uri_free (set_uri); return SOUP_STATUS_MALFORMED; } set_uri->query = g_strdup_printf ("id=%s&fmt-out=text/calendar&convertCalid=1%s", priv->wcap_session_id, s); g_free (s); /* send the request to the server */ msg = sunone_message_new_from_uri (set_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (retval)) { sunone_connection_free_preferences (priv->prefs); priv->prefs = clone_prefs (prefs); } else g_warning ("Error: %s", msg->reason_phrase); g_object_unref (msg); soup_uri_free (set_uri); return retval; } void sunone_connection_free_subscription (SunOneCalendarSubscription *sub) { if (!sub) return; g_free (sub->calid); g_free (sub->display_name); g_free (sub); } void sunone_connection_free_preferences (SunOneConnectionPreferences *prefs) { if (!prefs) return; g_free (prefs->common_name); g_free (prefs->given_name); g_free (prefs->mail); g_free (prefs->preferred_language); g_free (prefs->surname); g_free (prefs->default_calendar); g_free (prefs->timezone); g_free (prefs->freebusy); g_free (prefs->alarm_mail); g_list_foreach (prefs->subscriptions, (GFunc) sunone_connection_free_subscription, NULL); g_list_free (prefs->subscriptions); g_free (prefs); } static void add_ace (GList **acls, SunOneACEUserType utype, SunOneACEContext context, SunOneACEPermission perms) { SunOneACE *ace; ace = sunone_ace_new (); sunone_ace_set_user_type (ace, utype); sunone_ace_set_context (ace, context); sunone_ace_set_permissions (ace, perms); sunone_ace_set_access_type (ace, SUNONE_ACE_ACCESSTYPE_GRANT); *acls = g_list_append (*acls, ace); } guint sunone_connection_createcalendar (SunOneConnection *connection, const gchar *calid, gchar **real_calid) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *suri; SoupMessage *msg; guint retval; GList *acls = NULL; gchar *aclstr, *calidtmp = NULL; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* set ACLS to the same value used in the Web UI */ add_ace (&acls, SUNONE_ACE_USERTYPE_OWNERS, SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, SUNONE_ACE_PERMISSION_WRITE | SUNONE_ACE_PERMISSION_DELETE | SUNONE_ACE_PERMISSION_REPLY | SUNONE_ACE_PERMISSION_INVITE | SUNONE_ACE_PERMISSION_CANCEL); add_ace (&acls, SUNONE_ACE_USERTYPE_OWNERS, SUNONE_ACE_CONTEXT_ENTIRE_CALENDAR, SUNONE_ACE_PERMISSION_READ | SUNONE_ACE_PERMISSION_SCHEDULE | SUNONE_ACE_PERMISSION_FREEBUSY); add_ace (&acls, SUNONE_ACE_USERTYPE_ALL_USERS, SUNONE_ACE_CONTEXT_ENTIRE_CALENDAR, SUNONE_ACE_PERMISSION_SCHEDULE | SUNONE_ACE_PERMISSION_FREEBUSY); add_ace (&acls, SUNONE_ACE_USERTYPE_ALL_USERS, SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, SUNONE_ACE_PERMISSION_NONE); add_ace (&acls, SUNONE_ACE_USERTYPE_ALL_USERS, SUNONE_ACE_CONTEXT_CALENDAR_PROPERTIES, SUNONE_ACE_PERMISSION_READ); aclstr = get_acls_string (acls); /* prepare the URI */ NEW_URI (suri); suri->path = g_strdup ("/createcalendar.wcap"); suri->query = g_strdup_printf ( "id=%s&calid=%s&fmt-out=text/calendar&set_calprops=1%s", priv->wcap_session_id, calid, aclstr); /* send the request to the server */ msg = sunone_message_new_from_uri (suri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (retval)) { icalcomponent *icalcomp; /* parse the response to get the calid as generated in the server */ icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (icalcomp) { icalproperty *icalprop; icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (icalprop) { const gchar *x_name, *x_val; x_name = icalproperty_get_x_name (icalprop); x_val = icalproperty_get_x (icalprop); if (!strcmp (x_name, "X-NSCP-CALPROPS-RELATIVE-CALID")) { calidtmp = g_strdup (x_val); break; } icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } icalcomponent_free (icalcomp); } } g_object_unref (msg); soup_uri_free (suri); g_free (aclstr); g_list_foreach (acls, (GFunc) g_object_unref, NULL); g_list_free (acls); if (SUNONE_ERROR_IS_SUCCESSFUL (retval)) { SunOneConnectionPreferences *prefs; /* auto-subscribe to new folder */ prefs = sunone_connection_get_preferences (connection); if (prefs) { SunOneCalendarSubscription *sub; sub = g_new0 (SunOneCalendarSubscription, 1); sub->calid = g_strdup_printf (calidtmp); sub->display_name = g_strdup (calid); prefs->subscriptions = g_list_append (prefs->subscriptions, sub); if (!SUNONE_ERROR_IS_SUCCESSFUL (sunone_connection_set_preferences (connection, prefs))) g_warning ("Could not autosubscribe to folder %s", calid); sunone_connection_free_preferences (prefs); if (real_calid) *real_calid = calidtmp; else g_free (calidtmp); } else { g_warning ("Could not get preferences for calid %s", calid); g_free (calidtmp); } } else g_free (calidtmp); return retval; } guint sunone_connection_deletecalendar (SunOneConnection *connection, const gchar *calid) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *suri; SoupMessage *msg; guint retval; SunOneConnectionPreferences *prefs; SunOneCalendarSubscription *sub = NULL; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); prefs = sunone_connection_get_preferences (connection); if (prefs) { GList *l; gboolean found = FALSE; /* don't allow removal of the default calendar */ if (!strcmp (calid, prefs->default_calendar)) { sunone_connection_free_preferences (prefs); return SUNONE_ERROR_ACCESS_DENIED; } for (l = prefs->subscriptions; l != NULL; l = l->next) { sub = l->data; if (!strcmp (calid, sub->calid)) { found = TRUE; break; } } if (!found) { sunone_connection_free_preferences (prefs); return SUNONE_ERROR_CALENDAR_DOES_NOT_EXIST; } } else { g_warning ("Could not get preferences for connection"); return SUNONE_ERROR_CALENDAR_DOES_NOT_EXIST; } /* prepare the URI */ NEW_URI (suri); suri->path = g_strdup ("/deletecalendar.wcap"); suri->query = g_strdup_printf ( "id=%s&calid=%s&fmt-out=text/calendar", priv->wcap_session_id, sub->calid); /* send the request to the server */ msg = sunone_message_new_from_uri (suri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; g_object_unref (msg); soup_uri_free (suri); if (SUNONE_ERROR_IS_SUCCESSFUL (retval)) { /* auto-unsubscribe from folder */ prefs->subscriptions = g_list_remove (prefs->subscriptions, sub); if (!SUNONE_ERROR_IS_SUCCESSFUL (sunone_connection_set_preferences (connection, prefs))) g_warning ("Could not autounsubscribe from folder %s", calid); } sunone_connection_free_preferences (prefs); return retval; } static SunOneCalendarProperties * clone_calprops (SunOneCalendarProperties *props) { SunOneCalendarProperties *copy; GList *l; copy = g_new0 (SunOneCalendarProperties, 1); copy->relative_calid = g_strdup (props->relative_calid); copy->display_name = g_strdup (props->display_name); copy->last_modified = props->last_modified; copy->created = props->created; copy->language = g_strdup (props->language); copy->primary_owner = g_strdup (props->primary_owner); for (l = props->owners; l != NULL; l = l->next) copy->owners = g_list_append (copy->owners, g_strdup (l->data)); copy->timezone = g_strdup (props->timezone); copy->is_resource = props->is_resource; copy->acls = NULL; for (l = props->acls; l != NULL; l = l->next) copy->acls = g_list_append (copy->acls, sunone_ace_clone (SUNONE_ACE (l->data))); return copy; } static SunOneCalendarProperties * calprops_from_icalcomp (icalcomponent *icalcomp) { SunOneCalendarProperties *calprops; icalproperty *icalprop; calprops = g_new0 (SunOneCalendarProperties, 1); icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (icalprop) { const gchar *x_name, *x_val; x_name = icalproperty_get_x_name (icalprop); x_val = icalproperty_get_x (icalprop); if (!strcmp (x_name, "X-NSCP-CALPROPS-RELATIVE-CALID")) calprops->relative_calid = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-LAST-MODIFIED")) calprops->last_modified = icaltime_from_string (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-CREATED")) calprops->created = icaltime_from_string (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-NAME")) calprops->display_name = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-LANGUAGE")) calprops->language = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-PRIMARY-OWNER")) calprops->primary_owner = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-OWNERS")) calprops->owners = g_list_append (calprops->owners, g_strdup (x_val)); else if (!strcmp (x_name, "X-NSCP-CALPROPS-TZID")) calprops->timezone = g_strdup (x_val); else if (!strcmp (x_name, "X-NSCP-CALPROPS-ACCESS-CONTROL-ENTRY")) { SunOneACE *ace; ace = sunone_ace_new_from_string (x_val); if (IS_SUNONE_ACE (ace)) calprops->acls = g_list_append (calprops->acls, ace); } else if (!strcmp (x_name, "X-NSCP-CALPROPS-READ")) { } else if (!strcmp (x_name, "X-NSCP-CALPROPS-WRITE")) { } else if (!strcmp (x_name, "X-NSCP-CALPROPS-RESOURCE")) calprops->is_resource = !strcmp (x_val, "1") ? TRUE : FALSE; icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } return calprops; } SunOneCalendarProperties * sunone_connection_get_calprops (SunOneConnection *connection, const gchar *calid, gboolean use_cache) { SunOneConnectionPrivate *priv = connection->priv; gchar *calid_hash = NULL; SunOneCalendarProperties *calprops = NULL; SoupUri *get_uri; SoupMessage *msg; icalcomponent *icalcomp; SunOneCalendarProperties *clone; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); g_return_val_if_fail (calid != NULL, NULL); IS_CONNECTED (connection, NULL); g_static_mutex_lock (&mutex); if (!g_hash_table_lookup_extended (priv->calendar_properties, calid, (gpointer *) &calid_hash, (gpointer *) &calprops) || !use_cache) { /* prepare the URI */ NEW_URI (get_uri); get_uri->path = g_strdup ("/get_calprops.wcap"); get_uri->query = g_strdup_printf ("id=%s&fmt-out=text/calendar&calid=%s", priv->wcap_session_id, calid); /* send the request to the server */ msg = sunone_message_new_from_uri (get_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); soup_uri_free (get_uri); if (!SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { g_warning ("Error: %s", msg->reason_phrase); g_object_unref (msg); g_static_mutex_unlock (&mutex); return NULL; } /* parse the returned data */ icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); g_object_unref (msg); if (!icalcomp) { g_static_mutex_unlock (&mutex); return NULL; } /* free old calprops from hashtable */ if (calid_hash) { g_hash_table_remove (priv->calendar_properties, calid_hash); free_calprops (calid_hash, calprops, NULL); } calprops = calprops_from_icalcomp (icalcomp); g_hash_table_insert (priv->calendar_properties, g_strdup (calid), calprops); icalcomponent_free (icalcomp); } clone = clone_calprops (calprops); g_static_mutex_unlock (&mutex); return clone; } static gchar * get_acls_string (GList *acls) { GString *str = NULL; gchar *retval; GList *l; g_return_val_if_fail (acls != NULL, NULL); for (l = acls; l != NULL; l = l->next) { gchar *acestr; SunOneACE *ace = SUNONE_ACE (l->data); acestr = sunone_ace_to_string (ace); if (acestr) { if (!str) str = g_string_new ("&acl="); else str = g_string_append (str, ";"); str = g_string_append (str, acestr); g_free (acestr); } } retval = str->str; g_string_free (str, FALSE); return retval; } static gchar * get_owners_string (GList *owners) { GString *str = NULL; GList *l; for (l = owners; l != NULL; l = l->next) { if (!str) str = g_string_new ((const gchar *) l->data); else { str = g_string_append (str, ";"); str = g_string_append (str, (const gchar *) l->data); } } if (str) { char *retval = str->str; g_string_free (str, FALSE); return retval; } return NULL; } guint sunone_connection_set_calprops (SunOneConnection *connection, const gchar *calid, SunOneCalendarProperties *calprops) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *set_uri; SoupMessage *msg; guint retval; gchar *s; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (set_uri); set_uri->path = g_strdup ("/set_calprops.wcap"); s = get_acls_string (calprops->acls); set_uri->query = g_strdup_printf ( "id=%s&calid=%s&fmt-out=text/calendar%s", priv->wcap_session_id, calid, s ? s : ""); g_free (s); if (calprops->display_name != NULL) append_to_query (set_uri, "name", calprops->display_name); s = get_owners_string (calprops->owners); append_to_query (set_uri, "owners", s ? s :""); g_free (s); /* send the request to the server */ msg = sunone_message_new_from_uri (set_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { gchar *calid_hash; SunOneCalendarProperties *calprops_hash; if (g_hash_table_lookup_extended (priv->calendar_properties, calid, (gpointer *) &calid_hash, (gpointer *) &calprops_hash)) { g_hash_table_remove (priv->calendar_properties, calid); g_free (calid_hash); sunone_connection_free_calprops (calprops_hash); } g_hash_table_insert (priv->calendar_properties, g_strdup (calid), clone_calprops (calprops)); } g_object_unref (msg); soup_uri_free (set_uri); return retval; } GList * sunone_connection_list_calids (SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *get_uri; SoupMessage *msg; icalcomponent *icalcomp, *subcomp; SunOneCalendarProperties *calprops; GList *calids = NULL; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); IS_CONNECTED (connection, NULL); /* prepare the URI */ NEW_URI (get_uri); get_uri->path = g_strdup ("/search_calprops.wcap"); get_uri->query = g_strdup_printf ("id=%s&fmt-out=text/calendar&calid=1&searchOpts=0&search-string=", priv->wcap_session_id); /* send the request to the server */ msg = sunone_message_new_from_uri (get_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); soup_uri_free (get_uri); if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (icalcomp) { subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VCALENDAR_COMPONENT); while (subcomp) { calprops = calprops_from_icalcomp (subcomp); if (calprops) calids = g_list_append (calids, calprops); subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VCALENDAR_COMPONENT); } icalcomponent_free (icalcomp); } else g_warning ("Error: could not parse response's body"); } else g_warning ("Error: %s", msg->reason_phrase); g_object_unref (msg); return calids; } GList * sunone_connection_search_calids (SunOneConnection *connection, SunOneSearchField field, SunOneSearchBy by, const char *search_str) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *get_uri; SoupMessage *msg; icalcomponent *icalcomp, *subcomp; SunOneCalendarProperties *calprops; GList *calids = NULL; gint calid = 0, name = 0, primary_owner = 0; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), NULL); IS_CONNECTED (connection, NULL); switch (field) { case NAME_OR_ID: calid = name = 1; break; case NAME: name = 1; break; case PRIMARY_OWNER: primary_owner = 1; break; case ID: calid = 1; break; } /* prepare the URI */ NEW_URI (get_uri); get_uri->path = g_strdup ("/search_calprops.wcap"); get_uri->query = g_strdup_printf ("id=%s&fmt-out=text/calendar&calid=%d&name=%d&primaryOwner=%d&searchOpts=%d&search-string=%s", priv->wcap_session_id, calid, name, primary_owner, by, search_str); /* send the request to the server */ msg = sunone_message_new_from_uri (get_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); soup_uri_free (get_uri); if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { char *tmp_str; char *vcal_str; tmp_str = g_strndup (msg->response.body, msg->response.length); if (tmp_str) { vcal_str = g_strdup_printf ("BEGIN:VCALENDAR\n%s\nEND:VCALENDAR\n", tmp_str); g_free (tmp_str); if (vcal_str) { icalcomp = sunone_util_icalparser_parse_string (vcal_str, strlen (vcal_str)); g_free (vcal_str); if (icalcomp) { subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VCALENDAR_COMPONENT); while (subcomp) { /* Because we wrap the result in a vcalender, * we always get at least one sub component in icalcompoent. * So we use ICAL_X_PROPERTY to identify if this sub component * is the only one in search result or it is just fake. */ if (icalcomponent_get_first_property (subcomp, ICAL_X_PROPERTY) != NULL) { calprops = calprops_from_icalcomp (subcomp); if (calprops) calids = g_list_append (calids, calprops); } subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VCALENDAR_COMPONENT); } icalcomponent_free (icalcomp); } else g_warning ("Error: could not parse response's body"); } } } else g_warning ("Error: %s", msg->reason_phrase); g_object_unref (msg); return calids; } void sunone_connection_free_calprops (SunOneCalendarProperties *calprops) { g_return_if_fail (calprops != NULL); g_free (calprops->relative_calid); g_free (calprops->display_name); g_free (calprops->language); g_free (calprops->primary_owner); g_list_foreach (calprops->owners, (GFunc) g_free, NULL); g_list_free (calprops->owners); g_free (calprops->timezone); g_list_foreach (calprops->acls, (GFunc) g_object_unref, NULL); g_list_free (calprops->acls); g_free (calprops); } guint sunone_connection_get_all_timezones (SunOneConnection *connection, icalcomponent **icalcomp) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/get_all_timezones.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&fmt-out=text/calendar", priv->wcap_session_id); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } guint sunone_connection_deletetodos_by_id (SunOneConnection *connection, const char *calid, const char *uid, const char *rid, SunOneModType mod) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/deletetodos_by_id.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&uid=%s&rid=%s&mod=%d&fmt-out=text/calendar", priv->wcap_session_id, calid, uid, rid, mod); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } guint sunone_connection_deleteevents_by_id (SunOneConnection *connection, const char *calid, const char *uid, const char *rid, SunOneModType mod) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/deleteevents_by_id.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&uid=%s&rid=%s&mod=%d&fmt-out=text/calendar", priv->wcap_session_id, calid, uid, rid, mod); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } static time_t parse_http_date (const char *date) { /* Do not internationalize. */ static const char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; struct tm tm; char *p; if (strlen (date) < 29 || date[3] != ',' || date[4] != ' ') return -1; memset (&tm, 0, sizeof (tm)); p = (char *)date + 5; tm.tm_mday = strtol (p, &p, 10); p++; for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) { if (!strncmp (p, months[tm.tm_mon], 3)) break; } p += 3; tm.tm_year = strtol (p, &p, 10) - 1900; tm.tm_hour = strtol (p, &p, 10); p++; tm.tm_min = strtol (p, &p, 10); p++; tm.tm_sec = strtol (p, &p, 10); return e_mktime_utc (&tm); } guint sunone_connection_fetchcomponents_by_lastmod (SunOneConnection *connection, const char *calid, struct icaltimetype dtstart, struct icaltimetype dtend, SunOneCompType type, SunOneCompState state, icalcomponent **icalcomp, time_t *server_time) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/fetchcomponents_by_lastmod.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&dtstart=%s&dtend=%s&component-type=%s&relativealarm=4&fmt-out=text/calendar&compressed=1", priv->wcap_session_id, calid, !icaltime_is_null_time (dtstart) ? icaltime_as_ical_string (dtstart) : "0", !icaltime_is_null_time (dtend) ? icaltime_as_ical_string (dtend) : "0", type_to_string (type)); append_to_query_compstate (fetch_uri, "compstate", state); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; if (server_time) { const char *date; date = soup_message_get_header (msg->response_headers, "Date"); *server_time = parse_http_date (date); if (*server_time < 0) *server_time = 0; } } g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } guint sunone_connection_fetch_deletedcomponents (SunOneConnection *connection, const char *calid, struct icaltimetype dtstart, struct icaltimetype dtend, SunOneCompType type, icalcomponent **icalcomp) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/fetch_deletedcomponents.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&dtstart=%s&dtend=%s&component-type=%s&fmt-out=text/calendar&recurring=0", priv->wcap_session_id, calid, !icaltime_is_null_time (dtstart) ? icaltime_as_ical_string (dtstart) : "0", !icaltime_is_null_time (dtend) ? icaltime_as_ical_string (dtend) : "0", type_to_string (type)); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } guint sunone_connection_get_freebusy (SunOneConnection *connection, const char *calid, struct icaltimetype dtstart, struct icaltimetype dtend, icalcomponent **icalcomp) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/get_freebusy.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&dtstart=%s&dtend=%s&tzid=UTC&tzidout=UTC&busyonly=1&fmt-out=text/calendar&compressed=1", priv->wcap_session_id, calid, !icaltime_is_null_time (dtstart) ? icaltime_as_ical_string (dtstart) : "0", !icaltime_is_null_time (dtend) ? icaltime_as_ical_string (dtend) : "0"); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); /* For WCAP 3.0, maybe calid is a mail address, try again */ if (!IS_CNC_WCAP_2_0(connection) && !SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/get_freebusy.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&mail=%s&dtstart=%s&dtend=%s&tzid=UTC&tzidout=UTC&busyonly=1&fmt-out=text/calendar&compressed=1", priv->wcap_session_id, calid, !icaltime_is_null_time (dtstart) ? icaltime_as_ical_string (dtstart) : "0", !icaltime_is_null_time (dtend) ? icaltime_as_ical_string (dtend) : "0"); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); } return retval; } guint sunone_connection_import (SunOneConnection *connection, const char *calid, icalcomponent *icalcomp) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *uri; SoupMessage *msg; guint retval; char *ical_string, *import_msg, *content_type, *err; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); g_return_val_if_fail (icalcomp != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); ical_string = icalcomponent_as_ical_string (icalcomp); if (!ical_string) return SOUP_STATUS_CANCELLED; /* prepare the URI */ NEW_URI (uri); uri->path = g_strdup ("/import.wcap"); uri->query = g_strdup_printf ( "id=%s&calid=%s&content-in=text/calendar", priv->wcap_session_id, calid); /* prepare message */ import_msg = g_strdup_printf ("-----------------------------33111928916708\r\nContent-Disposition: form-data; name=\"Upload\"; filename=\"ical1.ics\"\r\n\r\n%s-----------------------------33111928916708--", ical_string); content_type = "multipart/form-data;boundary=---------------------------33111928916708"; msg = sunone_message_new_full_from_uri (uri, SOUP_METHOD_POST, content_type, SOUP_BUFFER_SYSTEM_OWNED, import_msg, strlen (import_msg)); /* send the request to the server */ sunone_message_send (connection, msg); retval = msg->status_code; if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) retval = msg->status_code; if (msg->response.body) { if (IS_CNC_WCAP_2_0 (connection)) { err = strstr (msg->response.body, "var errno="); err += 10; } else { err = strstr (msg->response.body, "X-NSCP-WCAP-ERRNO:"); err += 18; } if (err) { retval = 1001 + atoi (err); } else { retval = SUNONE_ERROR_COMMAND_FAILED; } } else { retval = SUNONE_ERROR_COMMAND_FAILED; } g_object_unref (msg); soup_uri_free (uri); return retval; } static void remove_all_but (icalcomponent *icalcomp, icalcomponent_kind kind) { icalcomponent_kind child_kind; icalcomponent *subcomp; subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT); while (subcomp) { child_kind = icalcomponent_isa (subcomp); if (child_kind != ICAL_VTIMEZONE_COMPONENT && child_kind != ICAL_VALARM_COMPONENT && child_kind != kind) { icalcomponent_remove_component (icalcomp, subcomp); icalcomponent_free (subcomp); } subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT); } } static guint sunone_connection_storeevents_by_mail (SunOneConnection *connection, const char *calid, icalcomponent *icalcomp, SunOneMethod method, SunOneModType mod, gboolean expand, gboolean allday, icalcomponent **return_icalcomp, gboolean mail, const char *account_email) { SunOneConnectionPrivate *priv = connection->priv; ECalComponent *comp; SoupUri *fetch_uri; SoupMessage *msg; guint retval; icalcomponent *real_icalcomp; icalproperty *prop; GSList *slist; ECalComponentText text; ECalComponentDateTime dt; ECalComponentOrganizer organizer; ECalComponentClassification class; ECalComponentRange recur; ECalComponentTransparency trans; struct icalgeotype *geo = NULL; icalproperty_status status; const char *const_string; int *num; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); g_return_val_if_fail (icalcomp != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); real_icalcomp = icalcomponent_new_clone (icalcomp); remove_all_but (real_icalcomp, ICAL_VEVENT_COMPONENT); comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, real_icalcomp); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/storeevents.wcap" ); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&fetch=1&method=%d&mod=%d&replace=1&rchange=%s&compressed=1&fmt-out=text/calendar", priv->wcap_session_id, calid, method, mod, expand ? "1" : "0"); /* Alarms */ append_to_query_alarms (fetch_uri, connection, comp); /* Attachments */ /* FIXME */ /* Attendees */ if (e_cal_component_has_attendees (comp)) { e_cal_component_get_attendee_list (comp, &slist); append_to_query_attendees (fetch_uri, "attendees", slist, connection, calid, account_email); } /* Categories */ e_cal_component_get_categories (comp, &const_string); append_to_query (fetch_uri, "categories", const_string ? const_string : ""); /* Contacts */ e_cal_component_get_contact_list (comp, &slist); append_to_query_text_list (fetch_uri, "contacts", slist); /* Description */ e_cal_component_get_description_list (comp, &slist); if (slist) append_to_query_text (fetch_uri, "desc", slist->data); else append_to_query (fetch_uri, "desc", ""); /* Dtend */ e_cal_component_get_dtend (comp, &dt); append_to_query_datetime (fetch_uri, "dtend", &dt, FALSE); /* Dtstart */ e_cal_component_get_dtstart (comp, &dt); append_to_query_datetime (fetch_uri, "dtstart", &dt, FALSE); /* Exdates */ e_cal_component_get_exdate_list (comp, &slist); append_to_query_exdate_list (fetch_uri, "exdates", slist); /* Exrules */ e_cal_component_get_exrule_list (comp, &slist); append_to_query_recur_list (fetch_uri, "exrules", slist); /* Transparency for wcap 3.0 */ if (!IS_CNC_WCAP_2_0(connection)) { e_cal_component_get_transparency (comp, &trans); append_to_query (fetch_uri, "transparent", trans == E_CAL_COMPONENT_TRANSP_TRANSPARENT ? "1":"0"); } /* Geo */ e_cal_component_get_geo (comp, &geo); append_to_query_geo (fetch_uri, "geo", geo); /* Classification */ e_cal_component_get_classification (comp, &class); append_to_query_classification (fetch_uri, "icsClass", class); /* URL */ e_cal_component_get_url (comp, &const_string); if (const_string) append_to_query (fetch_uri, "icsUrl", const_string); else append_to_query (fetch_uri, "icsUrl", ""); /* isAllDay */ append_to_query (fetch_uri, "isAllDay", allday ? "1" : "0"); /* Language */ /* FIXME */ /* Location */ e_cal_component_get_location (comp, &const_string); if (const_string) append_to_query (fetch_uri, "location", const_string); else append_to_query (fetch_uri, "location", ""); /* OrgUID for wcap 2.0, OrgCalid for wcap 3.0 */ if (e_cal_component_has_organizer (comp)) { e_cal_component_get_organizer (comp, &organizer); if (!strncasecmp (organizer.value, "mailto:", 7)) organizer.value += 7; if (IS_CNC_WCAP_2_0(connection)) append_to_query (fetch_uri, "orgUID", organizer.value); else { if (!mail) { append_to_query (fetch_uri, "orgCalid", organizer.value); } else { append_to_query (fetch_uri, "orgMail", priv->prefs->mail); } } } /* Priority */ e_cal_component_get_priority (comp, &num); if (num) { append_to_query_int (fetch_uri, "priority", *num); e_cal_component_free_priority (num); } /* RDates */ /* FIXME */ /* Relatedto */ /* FIXME */ /* Resources */ /* FIXME */ /* Rid */ if (e_cal_component_is_instance (comp)) { e_cal_component_get_recurid (comp, &recur); append_to_query_datetime (fetch_uri, "rid", &recur.datetime, FALSE); } /* Rrules */ e_cal_component_get_rrule_list (comp, &slist); append_to_query_recur_list (fetch_uri, "rrules", slist); /* Sequence */ e_cal_component_get_sequence (comp, &num); if (num) { append_to_query_int (fetch_uri, "seq", *num); e_cal_component_free_sequence (num); } /* Status */ e_cal_component_get_status (comp, &status); append_to_query_status (fetch_uri, "status", status); /* Summary */ e_cal_component_get_summary (comp, &text); append_to_query_text (fetch_uri, "summary", &text); /* UID */ e_cal_component_get_uid (comp, &const_string); append_to_query (fetch_uri, "uid", const_string); /* tzid */ prop = icalcomponent_get_first_property (real_icalcomp, ICAL_X_PROPERTY); while (prop) { const char *x_name, *x_val; x_name = icalproperty_get_x_name (prop); x_val = icalproperty_get_x (prop); if (!strcmp (x_name, "X-NSCP-DTSTART-TZID") && strcmp (x_val, "UTC")) append_to_query (fetch_uri, "tzid", x_val); prop = icalcomponent_get_next_property (real_icalcomp, ICAL_X_PROPERTY); } /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code) && return_icalcomp) { *return_icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*return_icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); /* will free real_icalcomp when comp is freed */ g_object_unref (comp); return retval; } guint sunone_connection_storeevents (SunOneConnection *connection, const char *calid, icalcomponent *icalcomp, SunOneMethod method, SunOneModType mod, gboolean expand, gboolean allday, icalcomponent **return_icalcomp, const char *account_email) { SunOneConnectionPrivate *priv = connection->priv; guint retval; retval = sunone_connection_storeevents_by_mail (connection, calid, icalcomp, method, mod, expand, allday, return_icalcomp, FALSE, account_email); if (!SUNONE_ERROR_IS_SUCCESSFUL (retval) && !IS_CNC_WCAP_2_0 (connection) && priv->prefs->mail) { retval = sunone_connection_storeevents_by_mail (connection, calid, icalcomp, method, mod, expand, allday, return_icalcomp, TRUE, account_email); } return retval; } guint sunone_connection_storetodos (SunOneConnection *connection, const char *calid, icalcomponent *icalcomp, SunOneMethod method, SunOneModType mod, gboolean expand, gboolean allday, icalcomponent **return_icalcomp, const char *email) { SunOneConnectionPrivate *priv = connection->priv; ECalComponent *comp; SoupUri *fetch_uri; SoupMessage *msg; guint retval; icalcomponent *real_icalcomp; icalproperty *prop; GSList *slist; ECalComponentText text; ECalComponentDateTime dt; ECalComponentOrganizer organizer; ECalComponentClassification class; ECalComponentRange recur; struct icaltimetype *t; struct icalgeotype *geo = NULL; icalproperty_status status; const char *const_string; int *num; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); g_return_val_if_fail (icalcomp != NULL, SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); real_icalcomp = icalcomponent_new_clone (icalcomp); remove_all_but (real_icalcomp, ICAL_VEVENT_COMPONENT); comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, real_icalcomp); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/storetodos.wcap" ); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&fetch=1&method=%d&mod=%d&replace=1&rchange=%s&compressed=1&fmt-out=text/calendar", priv->wcap_session_id, calid, method, mod, expand ? "1" : "0"); /* Alarms */ append_to_query_alarms (fetch_uri, connection, comp); /* Attendees */ if (e_cal_component_has_attendees (comp)) { e_cal_component_get_attendee_list (comp, &slist); append_to_query_attendees (fetch_uri, "attendees", slist, connection, calid, email); } /* Categories */ e_cal_component_get_categories (comp, &const_string); append_to_query (fetch_uri, "categories", const_string ? const_string : ""); /* Contacts */ e_cal_component_get_contact_list (comp, &slist); append_to_query_text_list (fetch_uri, "contacts", slist); /* Completed */ e_cal_component_get_completed (comp, &t); append_to_query (fetch_uri, "completed", t ? icaltime_as_ical_string (*t) : "0"); /* Description */ e_cal_component_get_description_list (comp, &slist); if (slist) append_to_query_text (fetch_uri, "desc", slist->data); else append_to_query (fetch_uri, "desc", ""); /* Dtstart */ e_cal_component_get_dtstart (comp, &dt); append_to_query_datetime (fetch_uri, "dtstart", &dt, FALSE); /* Due */ e_cal_component_get_due (comp, &dt); append_to_query_datetime (fetch_uri, "due", &dt, TRUE); /* Exdates */ e_cal_component_get_exdate_list (comp, &slist); append_to_query_exdate_list (fetch_uri, "exdates", slist); /* Exrules */ e_cal_component_get_exrule_list (comp, &slist); append_to_query_recur_list (fetch_uri, "exrules", slist); /* Geo */ e_cal_component_get_geo (comp, &geo); append_to_query_geo (fetch_uri, "geo", geo); /* Classification */ e_cal_component_get_classification (comp, &class); append_to_query_classification (fetch_uri, "icsClass", class); /* URL */ e_cal_component_get_url (comp, &const_string); if (const_string) append_to_query (fetch_uri, "icsUrl", const_string); else append_to_query (fetch_uri, "icsUrl", ""); /* isAllDay */ append_to_query (fetch_uri, "isAllDay", allday ? "1" : "0"); /* Language */ /* FIXME */ /* Location */ e_cal_component_get_location (comp, &const_string); append_to_query (fetch_uri, "location", const_string); /* OrgUID for wcap 2.0, OrgCalid for wcap 3.0 */ if (e_cal_component_has_organizer (comp)) { e_cal_component_get_organizer (comp, &organizer); if (!strncasecmp (organizer.value, "mailto:", 7)) organizer.value += 7; if (IS_CNC_WCAP_2_0(connection)) append_to_query (fetch_uri, "orgUID", organizer.value); else append_to_query (fetch_uri, "orgCalid", organizer.value); } /* Percent */ e_cal_component_get_percent (comp, &num); if (num) { append_to_query_int (fetch_uri, "percent", *num); e_cal_component_free_percent (num); } else { /*When one marks a completed task as uncompleted, evolution will remove *percent property, this would cause problem on some calendar servers, *so we need to set percent property to 0 */ append_to_query_int (fetch_uri, "percent", 0); } /* Priority */ e_cal_component_get_priority (comp, &num); if (num) { append_to_query_int (fetch_uri, "priority", *num); e_cal_component_free_priority (num); } /* RDates */ /* FIXME */ /* Relatedto */ /* FIXME */ /* Resources */ /* FIXME */ /* Rid */ if (e_cal_component_is_instance (comp)) { e_cal_component_get_recurid (comp, &recur); append_to_query_datetime (fetch_uri, "rid", &recur.datetime, FALSE); } /* Rrules */ e_cal_component_get_rrule_list (comp, &slist); append_to_query_recur_list (fetch_uri, "rrules", slist); /* Sequence */ e_cal_component_get_sequence (comp, &num); if (num) { append_to_query_int (fetch_uri, "seq", *num); e_cal_component_free_sequence (num); } /* Status */ e_cal_component_get_status (comp, &status); append_to_query_status (fetch_uri, "status", status); /* Summary */ e_cal_component_get_summary (comp, &text); append_to_query_text (fetch_uri, "summary", &text); /* UID */ e_cal_component_get_uid (comp, &const_string); append_to_query (fetch_uri, "uid", const_string); /* tzid */ prop = icalcomponent_get_first_property (real_icalcomp, ICAL_X_PROPERTY); while (prop) { const char *x_name, *x_val; x_name = icalproperty_get_x_name (prop); x_val = icalproperty_get_x (prop); if (!strcmp (x_name, "X-NSCP-DTSTART-TZID")) append_to_query (fetch_uri, "tzid", x_val); prop = icalcomponent_get_next_property (real_icalcomp, ICAL_X_PROPERTY); } /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code) && return_icalcomp) { *return_icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*return_icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); /* will free real_icalcomp when comp is freed */ g_object_unref (comp); return retval; } guint sunone_connection_verifyevents_by_ids (SunOneConnection *connection, const char *calid, const char **uidv, const char **ridv, icalcomponent **icalcomp) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; char *post_string, *content_type; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); g_return_val_if_fail (calid != NULL, SUNONE_ERROR_ILLEGAL_CALID_NAME); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/verifyevents_by_ids.wcap"); fetch_uri->query = g_strdup_printf ("id=%s&calid=%s&fmt-out=text/calendar", priv->wcap_session_id, calid); append_to_query_stringv (fetch_uri, "uid", uidv); append_to_query_stringv (fetch_uri, "rid", ridv); post_string = fetch_uri->query; fetch_uri->query = NULL; content_type = "application/x-www-form-urlencoded"; /* send the request to the server */ msg = sunone_message_new_full_from_uri (fetch_uri, SOUP_METHOD_POST, content_type, SOUP_BUFFER_SYSTEM_OWNED, post_string, strlen (post_string)); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } guint sunone_connection_verifytodos_by_ids (SunOneConnection *connection, const char *calid, const char **uidv, const char **ridv, icalcomponent **icalcomp) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; char *post_string, *content_type; guint retval; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/verifytodos_by_ids.wcap"); fetch_uri->query = g_strdup_printf ( "id=%s&calid=%s&fmt-out=text/calendar", priv->wcap_session_id, calid); append_to_query_stringv (fetch_uri, "uid", uidv); append_to_query_stringv (fetch_uri, "rid", ridv); post_string = fetch_uri->query; fetch_uri->query = NULL; content_type = "application/x-www-form-urlencoded"; /* send the request to the server */ msg = sunone_message_new_full_from_uri (fetch_uri, SOUP_METHOD_POST, content_type, SOUP_BUFFER_SYSTEM_OWNED, post_string, strlen (post_string)); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { *icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!*icalcomp) retval = SOUP_STATUS_MALFORMED; } g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } void sunone_connection_add_wcap (SunOneConnection *connection, const char *key, gpointer wcap) { SunOneConnectionPrivate *priv = connection->priv; if (g_hash_table_lookup(priv->wcaps, key)) { return; } g_hash_table_insert (priv->wcaps, g_strdup (key), wcap); } gpointer sunone_connection_get_wcap (SunOneConnection *connection, const char *key) { SunOneConnectionPrivate *priv = connection->priv; return g_hash_table_lookup (priv->wcaps, key); } guint sunone_connection_version (SunOneConnection *connection) { SunOneConnectionPrivate *priv = connection->priv; SoupUri *fetch_uri; SoupMessage *msg; guint retval; icalcomponent *icalcomp; icalproperty *icalprop; g_return_val_if_fail (IS_SUNONE_CONNECTION (connection), SOUP_STATUS_CANCELLED); IS_CONNECTED (connection, SOUP_STATUS_CANT_CONNECT); /* prepare the URI */ NEW_URI (fetch_uri); fetch_uri->path = g_strdup ("/version.wcap"); fetch_uri->query = g_strdup_printf ( "fmt-out=text/calendar"); /* send the request to the server */ msg = sunone_message_new_from_uri (fetch_uri, SOUP_METHOD_GET); sunone_message_send (connection, msg); retval = msg->status_code; if (SUNONE_ERROR_IS_SUCCESSFUL (msg->status_code)) { icalcomp = sunone_util_icalparser_parse_string (msg->response.body, msg->response.length); if (!icalcomp) retval = SOUP_STATUS_MALFORMED; else { icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); while (icalprop) { const char *x_name, *x_val; x_name = icalproperty_get_x_name (icalprop); x_val = icalproperty_get_x (icalprop); if (!strcmp (x_name, "X-NSCP-WCAPVERSION")) priv->wcap_version = g_strdup (x_val); icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); } icalcomponent_free (icalcomp); } } if (!priv->wcap_version) priv->wcap_version = g_strdup ("2.0"); g_object_unref (msg); soup_uri_free (fetch_uri); return retval; } SoupStatusClass sunone_message_send (SunOneConnection *connection, SoupMessage *msg) { SunOneConnectionPrivate *priv = connection->priv; SoupStatusClass status_class; setup_message (msg); status_class = soup_session_send_message (priv->soup_session, msg); if (status_class != SOUP_STATUS_CLASS_TRANSPORT_ERROR) parse_server_response (msg, &status_class); return status_class; } SoupStatusClass sunone_message_send_no_parse (SunOneConnection *connection, SoupMessage *msg) { SunOneConnectionPrivate *priv = connection->priv; setup_message (msg); return soup_session_send_message (priv->soup_session, msg); }