1 /*
2 * text-channel.h - high level API for Text channels
3 *
4 * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * SECTION:text-channel
23 * @title: TpTextChannel
24 * @short_description: proxy object for a text channel
25 *
26 * #TpTextChannel is a sub-class of #TpChannel providing convenient API
27 * to send and receive #TpMessage.
28 */
29
30 /**
31 * TpTextChannel:
32 *
33 * Data structure representing a #TpTextChannel.
34 *
35 * Since: 0.13.10
36 */
37
38 /**
39 * TpTextChannelClass:
40 *
41 * The class of a #TpTextChannel.
42 *
43 * Since: 0.13.10
44 */
45
46 #include "config.h"
47
48 #include "telepathy-glib/text-channel.h"
49
50 #include <telepathy-glib/contact.h>
51 #include <telepathy-glib/dbus.h>
52 #include <telepathy-glib/enums.h>
53 #include <telepathy-glib/gnio-util.h>
54 #include <telepathy-glib/gtypes.h>
55 #include <telepathy-glib/interfaces.h>
56 #include <telepathy-glib/message-internal.h>
57 #include <telepathy-glib/proxy-internal.h>
58 #include <telepathy-glib/proxy-subclass.h>
59 #include <telepathy-glib/signalled-message-internal.h>
60 #include <telepathy-glib/util-internal.h>
61 #include <telepathy-glib/util.h>
62
63 #define DEBUG_FLAG TP_DEBUG_CHANNEL
64 #include "telepathy-glib/debug-internal.h"
65 #include "telepathy-glib/automatic-client-factory-internal.h"
66 #include "telepathy-glib/channel-internal.h"
67
68 #include <stdio.h>
69 #include <glib/gstdio.h>
70
71 G_DEFINE_TYPE (TpTextChannel, tp_text_channel, TP_TYPE_CHANNEL)
72
73 struct _TpTextChannelPrivate
74 {
75 GStrv supported_content_types;
76 TpMessagePartSupportFlags message_part_support_flags;
77 TpDeliveryReportingSupportFlags delivery_reporting_support;
78 GArray *message_types;
79
80 GSimpleAsyncResult *pending_messages_result;
81 guint n_preparing_pending_messages;
82
83 /* queue of owned TpSignalledMessage */
84 GQueue *pending_messages;
85 gboolean got_initial_messages;
86
87 gboolean is_sms_channel;
88 gboolean sms_flash;
89 };
90
91 enum
92 {
93 PROP_SUPPORTED_CONTENT_TYPES = 1,
94 PROP_MESSAGE_PART_SUPPORT_FLAGS,
95 PROP_DELIVERY_REPORTING_SUPPORT,
96 PROP_MESSAGE_TYPES,
97 PROP_IS_SMS_CHANNEL,
98 PROP_SMS_FLASH,
99 };
100
101 enum /* signals */
102 {
103 SIG_MESSAGE_RECEIVED,
104 SIG_PENDING_MESSAGE_REMOVED,
105 SIG_MESSAGE_SENT,
106 SIG_CONTACT_CHAT_STATE_CHANGED,
107 LAST_SIGNAL
108 };
109
110 static guint signals[LAST_SIGNAL] = { 0, };
111
112 static void
tp_text_channel_dispose(GObject * obj)113 tp_text_channel_dispose (GObject *obj)
114 {
115 TpTextChannel *self = (TpTextChannel *) obj;
116
117 tp_clear_pointer (&self->priv->supported_content_types, g_strfreev);
118 tp_clear_pointer (&self->priv->message_types, g_array_unref);
119
120 g_queue_foreach (self->priv->pending_messages, (GFunc) g_object_unref, NULL);
121 tp_clear_pointer (&self->priv->pending_messages, g_queue_free);
122
123 G_OBJECT_CLASS (tp_text_channel_parent_class)->dispose (obj);
124 }
125
126 static void
tp_text_channel_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)127 tp_text_channel_get_property (GObject *object,
128 guint property_id,
129 GValue *value,
130 GParamSpec *pspec)
131 {
132 TpTextChannel *self = (TpTextChannel *) object;
133
134 switch (property_id)
135 {
136 case PROP_SUPPORTED_CONTENT_TYPES:
137 g_value_set_boxed (value,
138 tp_text_channel_get_supported_content_types (self));
139 break;
140
141 case PROP_MESSAGE_PART_SUPPORT_FLAGS:
142 g_value_set_uint (value,
143 tp_text_channel_get_message_part_support_flags (self));
144 break;
145
146 case PROP_DELIVERY_REPORTING_SUPPORT:
147 g_value_set_uint (value,
148 tp_text_channel_get_delivery_reporting_support (self));
149 break;
150
151 case PROP_MESSAGE_TYPES:
152 g_value_set_boxed (value,
153 tp_text_channel_get_message_types (self));
154 break;
155
156 case PROP_IS_SMS_CHANNEL:
157 g_value_set_boolean (value, tp_text_channel_is_sms_channel (self));
158 break;
159
160 case PROP_SMS_FLASH:
161 g_value_set_boolean (value, tp_text_channel_get_sms_flash (self));
162 break;
163
164 default:
165 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
166 break;
167 }
168 }
169
170 static TpHandle
get_sender(TpTextChannel * self,const GPtrArray * message,TpContact ** contact,const gchar ** out_sender_id)171 get_sender (TpTextChannel *self,
172 const GPtrArray *message,
173 TpContact **contact,
174 const gchar **out_sender_id)
175 {
176 const GHashTable *header;
177 TpHandle handle;
178 const gchar *sender_id = NULL;
179 TpConnection *conn;
180
181 g_assert (contact != NULL);
182
183 header = g_ptr_array_index (message, 0);
184 handle = tp_asv_get_uint32 (header, "message-sender", NULL);
185 if (handle == 0)
186 {
187 DEBUG ("Message received on Channel %s doesn't have message-sender",
188 tp_proxy_get_object_path (self));
189
190 *contact = NULL;
191 goto out;
192 }
193
194 sender_id = tp_asv_get_string (header, "message-sender-id");
195
196 conn = tp_channel_get_connection ((TpChannel *) self);
197 *contact = tp_connection_dup_contact_if_possible (conn, handle, sender_id);
198
199 if (*contact == NULL)
200 {
201 if (!tp_connection_has_immortal_handles (conn))
202 DEBUG ("Connection %s don't have immortal handles, please fix CM",
203 tp_proxy_get_object_path (conn));
204 else if (tp_str_empty (sender_id))
205 DEBUG ("Message received on %s doesn't include message-sender-id, "
206 "please fix CM", tp_proxy_get_object_path (self));
207 }
208
209 out:
210 if (out_sender_id != NULL)
211 *out_sender_id = sender_id;
212
213 return handle;
214 }
215
216 static void
prepare_sender_async(TpTextChannel * self,const GPtrArray * message,gboolean fallback_to_self,GAsyncReadyCallback callback,gpointer user_data)217 prepare_sender_async (TpTextChannel *self,
218 const GPtrArray *message,
219 gboolean fallback_to_self,
220 GAsyncReadyCallback callback,
221 gpointer user_data)
222 {
223 TpChannel *channel = (TpChannel *) self;
224 TpContact *contact;
225 TpHandle handle;
226 const gchar *id;
227
228 handle = get_sender (self, message, &contact, &id);
229 if (contact == NULL && fallback_to_self)
230 {
231 TpConnection *conn;
232
233 conn = tp_channel_get_connection ((TpChannel *) self);
234
235 DEBUG ("Failed to get our self contact, please fix CM (%s)",
236 tp_proxy_get_object_path (conn));
237
238 /* Use the connection self contact as a fallback */
239 contact = tp_connection_get_self_contact (conn);
240 if (contact != NULL)
241 g_object_ref (contact);
242 }
243
244 if (contact != NULL)
245 {
246 GPtrArray *contacts = g_ptr_array_new_with_free_func (g_object_unref);
247
248 /* get_sender() refs the contact, we give that ref to the ptr-array */
249 g_ptr_array_add (contacts, contact);
250 _tp_channel_contacts_queue_prepare_async (channel,
251 contacts, callback, user_data);
252 g_ptr_array_unref (contacts);
253 }
254 else if (id != NULL)
255 {
256 GPtrArray *ids = g_ptr_array_new_with_free_func (g_free);
257
258 g_ptr_array_add (ids, g_strdup (id));
259 _tp_channel_contacts_queue_prepare_by_id_async (channel,
260 ids, callback, user_data);
261 g_ptr_array_unref (ids);
262 }
263 else if (handle != 0)
264 {
265 GArray *handles = g_array_new (FALSE, FALSE, sizeof (TpHandle));
266
267 g_array_append_val (handles, handle);
268 _tp_channel_contacts_queue_prepare_by_handle_async (channel,
269 handles, callback, user_data);
270 g_array_unref (handles);
271 }
272 else
273 {
274 /* No sender. Still need to go through the queue to prevent reordering */
275 _tp_channel_contacts_queue_prepare_async (channel,
276 NULL, callback, user_data);
277 }
278 }
279
280 static TpContact *
prepare_sender_finish(TpTextChannel * self,GAsyncResult * result,GError ** error)281 prepare_sender_finish (TpTextChannel *self,
282 GAsyncResult *result,
283 GError **error)
284 {
285 GPtrArray *contacts;
286 TpContact *sender = NULL;
287
288 _tp_channel_contacts_queue_prepare_finish ((TpChannel *) self, result,
289 &contacts, error);
290
291 if (contacts != NULL && contacts->len > 0)
292 sender = g_object_ref (g_ptr_array_index (contacts, 0));
293
294 tp_clear_pointer (&contacts, g_ptr_array_unref);
295
296 return sender;
297 }
298
299 static GPtrArray *
copy_parts(const GPtrArray * parts)300 copy_parts (const GPtrArray *parts)
301 {
302 return g_boxed_copy (TP_ARRAY_TYPE_MESSAGE_PART_LIST, parts);
303 }
304
305 static void
free_parts(GPtrArray * parts)306 free_parts (GPtrArray *parts)
307 {
308 g_boxed_free (TP_ARRAY_TYPE_MESSAGE_PART_LIST, parts);
309 }
310
311 typedef struct
312 {
313 GPtrArray *parts;
314 guint flags;
315 gchar *token;
316 } MessageSentData;
317
318 static void
message_sent_sender_ready_cb(GObject * object,GAsyncResult * result,gpointer user_data)319 message_sent_sender_ready_cb (GObject *object,
320 GAsyncResult *result,
321 gpointer user_data)
322 {
323 TpTextChannel *self = (TpTextChannel *) object;
324 MessageSentData *data = user_data;
325 TpContact *sender;
326 TpMessage *msg;
327
328 sender = prepare_sender_finish (self, result, NULL);
329 msg = _tp_signalled_message_new (data->parts, sender);
330
331 g_signal_emit (self, signals[SIG_MESSAGE_SENT], 0, msg, data->flags,
332 data->token);
333
334 g_object_unref (msg);
335 free_parts (data->parts);
336 g_free (data->token);
337 g_slice_free (MessageSentData, data);
338 }
339
340 static void
message_sent_cb(TpChannel * channel,const GPtrArray * parts,guint flags,const gchar * token,gpointer user_data,GObject * weak_object)341 message_sent_cb (TpChannel *channel,
342 const GPtrArray *parts,
343 guint flags,
344 const gchar *token,
345 gpointer user_data,
346 GObject *weak_object)
347 {
348 TpTextChannel *self = (TpTextChannel *) channel;
349 MessageSentData *data;
350
351 DEBUG ("New message sent");
352
353 data = g_slice_new (MessageSentData);
354 data->parts = copy_parts (parts);
355 data->flags = flags;
356 data->token = tp_str_empty (token) ? NULL : g_strdup (token);
357
358 prepare_sender_async (self, parts, TRUE,
359 message_sent_sender_ready_cb, data);
360 }
361
362 static void
chat_state_changed_cb(TpTextChannel * self,TpHandle handle,TpChannelChatState state)363 chat_state_changed_cb (TpTextChannel *self,
364 TpHandle handle,
365 TpChannelChatState state)
366 {
367 TpConnection *conn;
368 TpContact *contact;
369
370 /* We have only an handle, but since we guarantee "contact-chat-state-changed"
371 * to be emitted only if TP_CHANNEL_FEATURE_GROUP and
372 * TP_CHANNEL_FEATURE_CONTACTS has been prepared, we should already have its
373 * TpContact. If the TpContact does not exist, telling its chat state is
374 * useless anyway. */
375 conn = tp_channel_get_connection ((TpChannel *) self);
376 contact = tp_connection_dup_contact_if_possible (conn, handle, NULL);
377 if (contact == NULL)
378 return;
379
380 g_signal_emit (self, signals[SIG_CONTACT_CHAT_STATE_CHANGED], 0,
381 contact, state);
382
383 g_object_unref (contact);
384 }
385
386 static void
tp_text_channel_prepare_chat_states_async(TpProxy * proxy,const TpProxyFeature * feature,GAsyncReadyCallback callback,gpointer user_data)387 tp_text_channel_prepare_chat_states_async (TpProxy *proxy,
388 const TpProxyFeature *feature,
389 GAsyncReadyCallback callback,
390 gpointer user_data)
391 {
392 /* This feature depends on TP_CHANNEL_FEATURE_CHAT_STATES so it's already
393 * prepared. */
394 tp_simple_async_report_success_in_idle ((GObject *) proxy,
395 callback, user_data, tp_text_channel_prepare_chat_states_async);
396 }
397
398 static void
tp_text_channel_constructed(GObject * obj)399 tp_text_channel_constructed (GObject *obj)
400 {
401 TpTextChannel *self = (TpTextChannel *) obj;
402 void (*chain_up) (GObject *) =
403 ((GObjectClass *) tp_text_channel_parent_class)->constructed;
404 TpChannel *chan = (TpChannel *) obj;
405 GHashTable *props;
406 gboolean valid;
407 GError *err = NULL;
408
409 if (chain_up != NULL)
410 chain_up (obj);
411
412 if (tp_channel_get_channel_type_id (chan) !=
413 TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
414 {
415 GError error = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
416 "Channel is not of type Text" };
417
418 DEBUG ("Channel %s is not of type Text: %s",
419 tp_proxy_get_object_path (self), tp_channel_get_channel_type (chan));
420
421 tp_proxy_invalidate (TP_PROXY (self), &error);
422 return;
423 }
424
425 if (!tp_proxy_has_interface_by_id (self,
426 TP_IFACE_QUARK_CHANNEL_INTERFACE_MESSAGES))
427 {
428 GError error = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
429 "Channel does not implement the Messages interface" };
430
431 DEBUG ("Channel %s does not implement the Messages interface",
432 tp_proxy_get_object_path (self));
433
434 tp_proxy_invalidate (TP_PROXY (self), &error);
435 return;
436
437 }
438
439 /* Forward TpChannel::chat-state-changed as
440 * TpTextChannel::contact-chat-state-changed */
441 g_signal_connect (self, "chat-state-changed",
442 G_CALLBACK (chat_state_changed_cb), NULL);
443
444 props = _tp_channel_get_immutable_properties (TP_CHANNEL (self));
445
446 self->priv->supported_content_types = (GStrv) tp_asv_get_strv (props,
447 TP_PROP_CHANNEL_INTERFACE_MESSAGES_SUPPORTED_CONTENT_TYPES);
448 if (self->priv->supported_content_types == NULL)
449 {
450 const gchar * const plain[] = { "text/plain", NULL };
451
452 DEBUG ("Channel %s doesn't have Messages.SupportedContentTypes in its "
453 "immutable properties", tp_proxy_get_object_path (self));
454
455 /* spec mandates that plain text is always allowed. */
456 self->priv->supported_content_types = g_strdupv ((GStrv) plain);
457 }
458 else
459 {
460 self->priv->supported_content_types = g_strdupv (
461 self->priv->supported_content_types);
462 }
463
464 self->priv->message_part_support_flags = tp_asv_get_uint32 (props,
465 TP_PROP_CHANNEL_INTERFACE_MESSAGES_MESSAGE_PART_SUPPORT_FLAGS, &valid);
466 if (!valid)
467 {
468 DEBUG ("Channel %s doesn't have Messages.MessagePartSupportFlags in its "
469 "immutable properties", tp_proxy_get_object_path (self));
470 }
471
472 self->priv->delivery_reporting_support = tp_asv_get_uint32 (props,
473 TP_PROP_CHANNEL_INTERFACE_MESSAGES_DELIVERY_REPORTING_SUPPORT, &valid);
474 if (!valid)
475 {
476 DEBUG ("Channel %s doesn't have Messages.DeliveryReportingSupport in its "
477 "immutable properties", tp_proxy_get_object_path (self));
478 }
479
480 self->priv->message_types = tp_asv_get_boxed (props,
481 TP_PROP_CHANNEL_INTERFACE_MESSAGES_MESSAGE_TYPES, DBUS_TYPE_G_UINT_ARRAY);
482 if (self->priv->message_types != NULL)
483 {
484 self->priv->message_types = g_boxed_copy (DBUS_TYPE_G_UINT_ARRAY,
485 self->priv->message_types);
486 }
487 else
488 {
489 self->priv->message_types = g_array_new (FALSE, FALSE,
490 sizeof (TpChannelTextMessageType));
491
492 DEBUG ("Channel %s doesn't have Messages.MessageTypes in its "
493 "immutable properties", tp_proxy_get_object_path (self));
494 }
495
496 tp_cli_channel_interface_messages_connect_to_message_sent (chan,
497 message_sent_cb, NULL, NULL, NULL, &err);
498 if (err != NULL)
499 {
500 WARNING ("Failed to connect to MessageSent on %s: %s",
501 tp_proxy_get_object_path (self), err->message);
502 g_error_free (err);
503 }
504
505 /* SMS */
506 self->priv->sms_flash = tp_asv_get_boolean (props,
507 TP_PROP_CHANNEL_INTERFACE_SMS_FLASH, NULL);
508 }
509
510 static void
add_message_received(TpTextChannel * self,const GPtrArray * parts,TpContact * sender,gboolean fire_received)511 add_message_received (TpTextChannel *self,
512 const GPtrArray *parts,
513 TpContact *sender,
514 gboolean fire_received)
515 {
516 TpMessage *msg;
517
518 msg = _tp_signalled_message_new (parts, sender);
519
520 g_queue_push_tail (self->priv->pending_messages, msg);
521
522 if (fire_received)
523 g_signal_emit (self, signals[SIG_MESSAGE_RECEIVED], 0, msg);
524 }
525
526 static void
message_received_sender_ready_cb(GObject * object,GAsyncResult * result,gpointer user_data)527 message_received_sender_ready_cb (GObject *object,
528 GAsyncResult *result,
529 gpointer user_data)
530 {
531 TpTextChannel *self = (TpTextChannel *) object;
532 GPtrArray *parts = user_data;
533 TpContact *sender;
534
535 sender = prepare_sender_finish (self, result, NULL);
536 add_message_received (self, parts, sender, TRUE);
537
538 free_parts (parts);
539 }
540
541 static void
message_received_cb(TpChannel * proxy,const GPtrArray * message,gpointer user_data,GObject * weak_object)542 message_received_cb (TpChannel *proxy,
543 const GPtrArray *message,
544 gpointer user_data,
545 GObject *weak_object)
546 {
547 TpTextChannel *self = user_data;
548
549 /* If we are still retrieving pending messages, no need to add the message,
550 * it will be in the initial set of messages retrieved. */
551 if (!self->priv->got_initial_messages)
552 return;
553
554 DEBUG ("New message received");
555
556 prepare_sender_async (self, message, FALSE,
557 message_received_sender_ready_cb,
558 copy_parts (message));
559 }
560
561 static gint
find_msg_by_id(gconstpointer a,gconstpointer b)562 find_msg_by_id (gconstpointer a,
563 gconstpointer b)
564 {
565 TpMessage *msg = TP_MESSAGE (a);
566 guint id = GPOINTER_TO_UINT (b);
567 gboolean valid;
568 guint msg_id;
569
570 msg_id = _tp_signalled_message_get_pending_message_id (msg, &valid);
571 if (!valid)
572 return 1;
573
574 return msg_id != id;
575 }
576
577 static void
pending_messages_removed_ready_cb(GObject * object,GAsyncResult * result,gpointer user_data)578 pending_messages_removed_ready_cb (GObject *object,
579 GAsyncResult *result,
580 gpointer user_data)
581 {
582 TpTextChannel *self = (TpTextChannel *) object;
583 TpChannel *channel = (TpChannel *) self;
584 GArray *ids = user_data;
585 guint i;
586
587 _tp_channel_contacts_queue_prepare_finish (channel, result, NULL, NULL);
588
589 for (i = 0; i < ids->len; i++)
590 {
591 guint id = g_array_index (ids, guint, i);
592 GList *link_;
593 TpMessage *msg;
594
595 link_ = g_queue_find_custom (self->priv->pending_messages,
596 GUINT_TO_POINTER (id), find_msg_by_id);
597
598 if (link_ == NULL)
599 {
600 DEBUG ("Unable to find pending message having id %d", id);
601 continue;
602 }
603
604 msg = link_->data;
605
606 g_queue_delete_link (self->priv->pending_messages, link_);
607
608 g_signal_emit (self, signals[SIG_PENDING_MESSAGE_REMOVED], 0, msg);
609
610 g_object_unref (msg);
611 }
612
613 g_array_unref (ids);
614 }
615
616 static void
pending_messages_removed_cb(TpChannel * channel,const GArray * ids,gpointer user_data,GObject * weak_object)617 pending_messages_removed_cb (TpChannel *channel,
618 const GArray *ids,
619 gpointer user_data,
620 GObject *weak_object)
621 {
622 TpTextChannel *self = (TpTextChannel *) channel;
623 GArray *ids_dup;
624
625 if (!self->priv->got_initial_messages)
626 return;
627
628 /* We have nothing to prepare, but still we have to hook into the queue
629 * to preserve order. Messages removed here could still be in that queue. */
630 ids_dup = g_array_sized_new (FALSE, FALSE, sizeof (guint), ids->len);
631 g_array_append_vals (ids_dup, ids->data, ids->len);
632
633 _tp_channel_contacts_queue_prepare_async (channel, NULL,
634 pending_messages_removed_ready_cb, ids_dup);
635 }
636
637 static void
pending_message_sender_ready_cb(GObject * object,GAsyncResult * result,gpointer user_data)638 pending_message_sender_ready_cb (GObject *object,
639 GAsyncResult *result,
640 gpointer user_data)
641 {
642 TpTextChannel *self = (TpTextChannel *) object;
643 GPtrArray *parts = user_data;
644 TpContact *sender;
645
646 sender = prepare_sender_finish (self, result, NULL);
647 add_message_received (self, parts, sender, FALSE);
648
649 self->priv->n_preparing_pending_messages--;
650 if (self->priv->n_preparing_pending_messages == 0)
651 {
652 g_simple_async_result_complete (self->priv->pending_messages_result);
653 g_clear_object (&self->priv->pending_messages_result);
654 }
655
656 free_parts (parts);
657 }
658
659 /* There is no TP_ARRAY_TYPE_PENDING_TEXT_MESSAGE_LIST_LIST (fdo #32433) */
660 #define ARRAY_TYPE_PENDING_TEXT_MESSAGE_LIST_LIST dbus_g_type_get_collection (\
661 "GPtrArray", TP_ARRAY_TYPE_MESSAGE_PART_LIST)
662
663 static void
get_pending_messages_cb(TpProxy * proxy,const GValue * value,const GError * error,gpointer user_data,GObject * weak_object)664 get_pending_messages_cb (TpProxy *proxy,
665 const GValue *value,
666 const GError *error,
667 gpointer user_data,
668 GObject *weak_object)
669 {
670 TpTextChannel *self = (TpTextChannel *) proxy;
671 GPtrArray *messages;
672 guint i;
673
674 self->priv->got_initial_messages = TRUE;
675
676 if (error != NULL)
677 {
678 DEBUG ("Failed to get PendingMessages property: %s", error->message);
679
680 g_simple_async_result_set_error (self->priv->pending_messages_result,
681 error->domain, error->code,
682 "Failed to get PendingMessages property: %s", error->message);
683
684 g_simple_async_result_complete_in_idle (self->priv->pending_messages_result);
685 g_clear_object (&self->priv->pending_messages_result);
686 return;
687 }
688
689 if (!G_VALUE_HOLDS (value, ARRAY_TYPE_PENDING_TEXT_MESSAGE_LIST_LIST))
690 {
691 DEBUG ("PendingMessages property is of the wrong type");
692
693 g_simple_async_result_set_error (self->priv->pending_messages_result,
694 TP_ERROR, TP_ERROR_CONFUSED,
695 "PendingMessages property is of the wrong type");
696
697 g_simple_async_result_complete_in_idle (self->priv->pending_messages_result);
698 g_clear_object (&self->priv->pending_messages_result);
699 return;
700 }
701
702 messages = g_value_get_boxed (value);
703
704 if (messages->len == 0)
705 {
706 g_simple_async_result_complete_in_idle (self->priv->pending_messages_result);
707 g_clear_object (&self->priv->pending_messages_result);
708 return;
709 }
710
711 self->priv->n_preparing_pending_messages = messages->len;
712 for (i = 0; i < messages->len; i++)
713 {
714 GPtrArray *parts = g_ptr_array_index (messages, i);
715
716 prepare_sender_async (self, parts, FALSE,
717 pending_message_sender_ready_cb,
718 copy_parts (parts));
719 }
720 }
721
722 static void
tp_text_channel_prepare_incoming_messages_async(TpProxy * proxy,const TpProxyFeature * feature,GAsyncReadyCallback callback,gpointer user_data)723 tp_text_channel_prepare_incoming_messages_async (TpProxy *proxy,
724 const TpProxyFeature *feature,
725 GAsyncReadyCallback callback,
726 gpointer user_data)
727 {
728 TpTextChannel *self = (TpTextChannel *) proxy;
729 TpChannel *channel = (TpChannel *) self;
730 GError *error = NULL;
731
732 tp_cli_channel_interface_messages_connect_to_message_received (channel,
733 message_received_cb, proxy, NULL, G_OBJECT (proxy), &error);
734 if (error != NULL)
735 {
736 g_simple_async_report_take_gerror_in_idle ((GObject *) self,
737 callback, user_data, error);
738 return;
739 }
740
741 tp_cli_channel_interface_messages_connect_to_pending_messages_removed (
742 channel, pending_messages_removed_cb, proxy, NULL, G_OBJECT (proxy),
743 &error);
744 if (error != NULL)
745 {
746 g_simple_async_report_take_gerror_in_idle ((GObject *) self,
747 callback, user_data, error);
748 return;
749 }
750
751 g_assert (self->priv->pending_messages_result == NULL);
752 self->priv->pending_messages_result = g_simple_async_result_new (
753 (GObject *) proxy, callback, user_data,
754 tp_text_channel_prepare_incoming_messages_async);
755
756
757 tp_cli_dbus_properties_call_get (proxy, -1,
758 TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "PendingMessages",
759 get_pending_messages_cb, NULL, NULL, G_OBJECT (proxy));
760 }
761
762 static void
get_sms_channel_cb(TpProxy * proxy,const GValue * value,const GError * error,gpointer user_data,GObject * weak_object)763 get_sms_channel_cb (TpProxy *proxy,
764 const GValue *value,
765 const GError *error,
766 gpointer user_data,
767 GObject *weak_object)
768 {
769 TpTextChannel *self = (TpTextChannel *) proxy;
770 GSimpleAsyncResult *result = user_data;
771
772 if (error != NULL)
773 {
774 DEBUG ("Failed to get SMSChannel property: %s", error->message);
775
776 g_simple_async_result_set_error (result, error->domain, error->code,
777 "Failed to get SMSChannel property: %s", error->message);
778 goto out;
779 }
780
781 if (!G_VALUE_HOLDS (value, G_TYPE_BOOLEAN))
782 {
783 DEBUG ("SMSChannel property is of the wrong type");
784
785 g_simple_async_result_set_error (result, TP_ERROR, TP_ERROR_CONFUSED,
786 "SMSChannel property is of the wrong type");
787 goto out;
788 }
789
790 self->priv->is_sms_channel = g_value_get_boolean (value);
791
792 /* self->priv->is_sms_channel is set to FALSE by default, so only notify the
793 * property change is it is now set to TRUE. */
794 if (self->priv->is_sms_channel)
795 g_object_notify (G_OBJECT (self), "is-sms-channel");
796
797 out:
798 g_simple_async_result_complete_in_idle (result);
799 g_object_unref (result);
800 }
801
802 static void
sms_channel_changed_cb(TpChannel * proxy,gboolean sms,gpointer user_data,GObject * weak_object)803 sms_channel_changed_cb (TpChannel *proxy,
804 gboolean sms,
805 gpointer user_data,
806 GObject *weak_object)
807 {
808 TpTextChannel *self = (TpTextChannel *) proxy;
809
810 if (self->priv->is_sms_channel == sms)
811 return;
812
813 self->priv->is_sms_channel = sms;
814 g_object_notify (weak_object, "is-sms-channel");
815 }
816
817 static void
tp_text_channel_prepare_sms_async(TpProxy * proxy,const TpProxyFeature * feature,GAsyncReadyCallback callback,gpointer user_data)818 tp_text_channel_prepare_sms_async (TpProxy *proxy,
819 const TpProxyFeature *feature,
820 GAsyncReadyCallback callback,
821 gpointer user_data)
822 {
823 GSimpleAsyncResult *result;
824 GError *error = NULL;
825
826 result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
827 tp_text_channel_prepare_sms_async);
828
829 tp_cli_channel_interface_sms_connect_to_sms_channel_changed (
830 (TpChannel *) proxy, sms_channel_changed_cb, NULL, NULL,
831 G_OBJECT (proxy), &error);
832 if (error != NULL)
833 {
834 WARNING ("Failed to connect to SMS.SMSChannelChanged: %s",
835 error->message);
836 g_error_free (error);
837 }
838
839 tp_cli_dbus_properties_call_get (proxy, -1,
840 TP_IFACE_CHANNEL_INTERFACE_SMS, "SMSChannel",
841 get_sms_channel_cb, result, NULL, G_OBJECT (proxy));
842 }
843
844 enum {
845 FEAT_INCOMING_MESSAGES,
846 FEAT_SMS,
847 FEAT_CHAT_STATES,
848 N_FEAT
849 };
850
851 static const TpProxyFeature *
tp_text_channel_list_features(TpProxyClass * cls G_GNUC_UNUSED)852 tp_text_channel_list_features (TpProxyClass *cls G_GNUC_UNUSED)
853 {
854 static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
855 static GQuark need_sms[2] = {0, 0};
856 static GQuark depends_chat_state[2] = {0, 0};
857
858 if (G_LIKELY (features[0].name != 0))
859 return features;
860
861 features[FEAT_INCOMING_MESSAGES].name =
862 TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES;
863 features[FEAT_INCOMING_MESSAGES].prepare_async =
864 tp_text_channel_prepare_incoming_messages_async;
865
866 features[FEAT_SMS].name =
867 TP_TEXT_CHANNEL_FEATURE_SMS;
868 features[FEAT_SMS].prepare_async =
869 tp_text_channel_prepare_sms_async;
870 need_sms[0] = TP_IFACE_QUARK_CHANNEL_INTERFACE_SMS;
871 features[FEAT_SMS].interfaces_needed = need_sms;
872
873 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
874 features[FEAT_CHAT_STATES].name =
875 TP_TEXT_CHANNEL_FEATURE_CHAT_STATES;
876 features[FEAT_CHAT_STATES].prepare_async =
877 tp_text_channel_prepare_chat_states_async;
878 depends_chat_state[0] = TP_CHANNEL_FEATURE_CHAT_STATES;
879 features[FEAT_CHAT_STATES].depends_on = depends_chat_state;
880 G_GNUC_END_IGNORE_DEPRECATIONS
881
882 /* assert that the terminator at the end is there */
883 g_assert (features[N_FEAT].name == 0);
884
885 return features;
886 }
887
888 static void
tp_text_channel_class_init(TpTextChannelClass * klass)889 tp_text_channel_class_init (TpTextChannelClass *klass)
890 {
891 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
892 TpProxyClass *proxy_class = (TpProxyClass *) klass;
893 GParamSpec *param_spec;
894
895 gobject_class->constructed = tp_text_channel_constructed;
896 gobject_class->get_property = tp_text_channel_get_property;
897 gobject_class->dispose = tp_text_channel_dispose;
898
899 proxy_class->list_features = tp_text_channel_list_features;
900
901 /**
902 * TpTextChannel:supported-content-types:
903 *
904 * A #GStrv containing the MIME types supported by this channel, with more
905 * preferred MIME types appearing earlier in the array.
906 *
907 * Since: 0.13.10
908 */
909 param_spec = g_param_spec_boxed ("supported-content-types",
910 "SupportedContentTypes",
911 "The Messages.SupportedContentTypes property of the channel",
912 G_TYPE_STRV,
913 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
914 g_object_class_install_property (gobject_class, PROP_SUPPORTED_CONTENT_TYPES,
915 param_spec);
916
917 /**
918 * TpTextChannel:message-part-support-flags:
919 *
920 * A #TpMessagePartSupportFlags indicating the level of support for
921 * message parts on this channel.
922 *
923 * Since: 0.13.10
924 */
925 param_spec = g_param_spec_uint ("message-part-support-flags",
926 "MessagePartSupportFlags",
927 "The Messages.MessagePartSupportFlags property of the channel",
928 0, G_MAXUINT32, 0,
929 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
930 g_object_class_install_property (gobject_class,
931 PROP_MESSAGE_PART_SUPPORT_FLAGS, param_spec);
932
933 /**
934 * TpTextChannel:delivery-reporting-support:
935 *
936 * A #TpDeliveryReportingSupportFlags indicating features supported
937 * by this channel.
938 *
939 * Since: 0.13.10
940 */
941 param_spec = g_param_spec_uint ("delivery-reporting-support",
942 "DeliveryReportingSupport",
943 "The Messages.DeliveryReportingSupport property of the channel",
944 0, G_MAXUINT32, 0,
945 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
946 g_object_class_install_property (gobject_class,
947 PROP_DELIVERY_REPORTING_SUPPORT, param_spec);
948
949 /**
950 * TpTextChannel:message-types:
951 *
952 * A #GArray containing the #TpChannelTextMessageType which may be sent on
953 * this channel.
954 *
955 * Since: 0.13.16
956 */
957 param_spec = g_param_spec_boxed ("message-types",
958 "MessageTypes",
959 "The Messages.MessageTypes property of the channel",
960 DBUS_TYPE_G_UINT_ARRAY,
961 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
962 g_object_class_install_property (gobject_class,
963 PROP_MESSAGE_TYPES, param_spec);
964
965 /**
966 * TpTextChannel:is-sms-channel:
967 *
968 * %TRUE if messages sent and received on this channel are transmitted
969 * via SMS.
970 *
971 * This property is not guaranteed to have a meaningful value until
972 * TP_TEXT_CHANNEL_FEATURE_SMS has been prepared.
973 *
974 * Since: 0.15.1
975 */
976 param_spec = g_param_spec_boolean ("is-sms-channel",
977 "is SMS channel",
978 "The SMS.SMSChannel property of the channel",
979 FALSE,
980 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
981 g_object_class_install_property (gobject_class, PROP_IS_SMS_CHANNEL,
982 param_spec);
983
984 /**
985 * TpTextChannel:sms-flash:
986 *
987 * %TRUE if this channel is exclusively for receiving class 0 SMSes
988 * (and no SMSes can be sent using tp_text_channel_send_message_async()
989 * on this channel). If %FALSE, no incoming class 0 SMSes will appear
990 * on this channel.
991 *
992 * Since: 0.15.1
993 */
994 param_spec = g_param_spec_boolean ("sms-flash",
995 "SMS flash",
996 "The SMS.Flash property of the channel",
997 FALSE,
998 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
999 g_object_class_install_property (gobject_class, PROP_SMS_FLASH, param_spec);
1000
1001 /**
1002 * TpTextChannel::message-received:
1003 * @self: the #TpTextChannel
1004 * @message: a #TpSignalledMessage
1005 *
1006 * The ::message-received signal is emitted when a new message has been
1007 * received on @self.
1008 *
1009 * The same @message object will be used by the
1010 * #TpTextChannel::pending-message-removed signal once @message has been
1011 * acked so you can simply compare pointers to identify the message.
1012 *
1013 * Note that this signal is only fired once the
1014 * #TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES has been prepared.
1015 *
1016 * It is guaranteed that @message's #TpSignalledMessage:sender has all of the
1017 * features previously passed to
1018 * tp_simple_client_factory_add_contact_features() prepared.
1019 *
1020 * Since: 0.13.10
1021 */
1022 signals[SIG_MESSAGE_RECEIVED] = g_signal_new ("message-received",
1023 G_OBJECT_CLASS_TYPE (klass),
1024 G_SIGNAL_RUN_LAST,
1025 0, NULL, NULL, NULL,
1026 G_TYPE_NONE,
1027 1, TP_TYPE_SIGNALLED_MESSAGE);
1028
1029 /**
1030 * TpTextChannel::pending-message-removed:
1031 * @self: the #TpTextChannel
1032 * @message: a #TpSignalledMessage
1033 *
1034 * The ::pending-message-removed signal is emitted when @message
1035 * has been acked and so removed from the pending messages list.
1036 *
1037 * Note that this signal is only fired once the
1038 * #TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES has been prepared.
1039 *
1040 * It is guaranteed that @message's #TpSignalledMessage:sender has all of the
1041 * features previously passed to
1042 * tp_simple_client_factory_add_contact_features() prepared.
1043 *
1044 * Since: 0.13.10
1045 */
1046 signals[SIG_PENDING_MESSAGE_REMOVED] = g_signal_new (
1047 "pending-message-removed",
1048 G_OBJECT_CLASS_TYPE (klass),
1049 G_SIGNAL_RUN_LAST,
1050 0, NULL, NULL, NULL,
1051 G_TYPE_NONE,
1052 1, TP_TYPE_SIGNALLED_MESSAGE);
1053
1054 /**
1055 * TpTextChannel::message-sent:
1056 * @self: the #TpTextChannel
1057 * @message: a #TpSignalledMessage
1058 * @flags: the #TpMessageSendingFlags affecting how the message was sent
1059 * @token: an opaque token used to match any incoming delivery or failure
1060 * reports against this message, or %NULL if the message is not
1061 * readily identifiable.
1062 *
1063 * The ::message-sent signal is emitted when @message
1064 * has been submitted for sending.
1065 *
1066 * It is guaranteed that @message's #TpSignalledMessage:sender has all of the
1067 * features previously passed to
1068 * tp_simple_client_factory_add_contact_features() prepared.
1069 *
1070 * Since: 0.13.10
1071 */
1072 signals[SIG_MESSAGE_SENT] = g_signal_new (
1073 "message-sent",
1074 G_OBJECT_CLASS_TYPE (klass),
1075 G_SIGNAL_RUN_LAST,
1076 0, NULL, NULL, NULL,
1077 G_TYPE_NONE,
1078 3, TP_TYPE_SIGNALLED_MESSAGE, G_TYPE_UINT, G_TYPE_STRING);
1079
1080 g_type_class_add_private (gobject_class, sizeof (TpTextChannelPrivate));
1081
1082 /**
1083 * TpTextChannel::contact-chat-state-changed:
1084 * @self: a channel
1085 * @contact: a #TpContact for the local user or another contact
1086 * @state: the new #TpChannelChatState for the contact
1087 *
1088 * Emitted when a contact's chat state changes after tp_proxy_prepare_async()
1089 * has finished preparing features %TP_TEXT_CHANNEL_FEATURE_CHAT_STATES,
1090 * %TP_CHANNEL_FEATURE_GROUP and %TP_CHANNEL_FEATURE_CONTACTS.
1091 *
1092 * Since: 0.19.0
1093 */
1094 signals[SIG_CONTACT_CHAT_STATE_CHANGED] = g_signal_new (
1095 "contact-chat-state-changed",
1096 G_OBJECT_CLASS_TYPE (klass),
1097 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1098 0,
1099 NULL, NULL, NULL,
1100 G_TYPE_NONE, 2, TP_TYPE_CONTACT, G_TYPE_UINT);
1101 }
1102
1103 static void
tp_text_channel_init(TpTextChannel * self)1104 tp_text_channel_init (TpTextChannel *self)
1105 {
1106 self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_TEXT_CHANNEL,
1107 TpTextChannelPrivate);
1108
1109 self->priv->pending_messages = g_queue_new ();
1110 }
1111
1112
1113 /**
1114 * tp_text_channel_new:
1115 * @conn: a #TpConnection; may not be %NULL
1116 * @object_path: the object path of the channel; may not be %NULL
1117 * @immutable_properties: (transfer none) (element-type utf8 GObject.Value):
1118 * the immutable properties of the channel,
1119 * as signalled by the NewChannel D-Bus signal or returned by the
1120 * CreateChannel and EnsureChannel D-Bus methods: a mapping from
1121 * strings (D-Bus interface name + "." + property name) to #GValue instances
1122 * @error: used to indicate the error if %NULL is returned
1123 *
1124 * Convenient function to create a new #TpTextChannel
1125 *
1126 * Returns: (transfer full): a newly created #TpTextChannel
1127 *
1128 * Since: 0.13.10
1129 * Deprecated: Use tp_simple_client_factory_ensure_channel() instead.
1130 */
1131 TpTextChannel *
tp_text_channel_new(TpConnection * conn,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)1132 tp_text_channel_new (TpConnection *conn,
1133 const gchar *object_path,
1134 const GHashTable *immutable_properties,
1135 GError **error)
1136 {
1137 return _tp_text_channel_new_with_factory (NULL, conn, object_path,
1138 immutable_properties, error);
1139 }
1140
1141 TpTextChannel *
_tp_text_channel_new_with_factory(TpSimpleClientFactory * factory,TpConnection * conn,const gchar * object_path,const GHashTable * immutable_properties,GError ** error)1142 _tp_text_channel_new_with_factory (
1143 TpSimpleClientFactory *factory,
1144 TpConnection *conn,
1145 const gchar *object_path,
1146 const GHashTable *immutable_properties,
1147 GError **error)
1148 {
1149 TpProxy *conn_proxy = (TpProxy *) conn;
1150
1151 g_return_val_if_fail (TP_IS_CONNECTION (conn), NULL);
1152 g_return_val_if_fail (object_path != NULL, NULL);
1153 g_return_val_if_fail (immutable_properties != NULL, NULL);
1154
1155 if (!tp_dbus_check_valid_object_path (object_path, error))
1156 return NULL;
1157
1158 return g_object_new (TP_TYPE_TEXT_CHANNEL,
1159 "connection", conn,
1160 "dbus-daemon", conn_proxy->dbus_daemon,
1161 "bus-name", conn_proxy->bus_name,
1162 "object-path", object_path,
1163 "handle-type", (guint) TP_UNKNOWN_HANDLE_TYPE,
1164 "channel-properties", immutable_properties,
1165 "factory", factory,
1166 NULL);
1167 }
1168
1169 /**
1170 * tp_text_channel_get_supported_content_types:
1171 * @self: a #TpTextChannel
1172 *
1173 * Return the #TpTextChannel:supported-content-types property
1174 *
1175 * Returns: (transfer none) :
1176 * the value of #TpTextChannel:supported-content-types
1177 *
1178 * Since: 0.13.10
1179 */
1180 const gchar * const *
tp_text_channel_get_supported_content_types(TpTextChannel * self)1181 tp_text_channel_get_supported_content_types (TpTextChannel *self)
1182 {
1183 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), NULL);
1184
1185 return (const gchar * const *) self->priv->supported_content_types;
1186 }
1187
1188 /**
1189 * tp_text_channel_get_message_part_support_flags:
1190 * @self: a #TpTextChannel
1191 *
1192 * Return the #TpTextChannel:message-part-support-flags property
1193 *
1194 * Returns: the value of #TpTextChannel:message-part-support-flags
1195 *
1196 * Since: 0.13.10
1197 */
1198 TpMessagePartSupportFlags
tp_text_channel_get_message_part_support_flags(TpTextChannel * self)1199 tp_text_channel_get_message_part_support_flags (
1200 TpTextChannel *self)
1201 {
1202 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), 0);
1203
1204 return self->priv->message_part_support_flags;
1205 }
1206
1207 /**
1208 * tp_text_channel_get_delivery_reporting_support:
1209 * @self: a #TpTextChannel
1210 *
1211 * Return the #TpTextChannel:delivery-reporting-support property
1212 *
1213 * Returns: the value of #TpTextChannel:delivery-reporting-support property
1214 *
1215 * Since: 0.13.10
1216 */
1217 TpDeliveryReportingSupportFlags
tp_text_channel_get_delivery_reporting_support(TpTextChannel * self)1218 tp_text_channel_get_delivery_reporting_support (
1219 TpTextChannel *self)
1220 {
1221 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), 0);
1222
1223 return self->priv->delivery_reporting_support;
1224 }
1225
1226 /**
1227 * TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES:
1228 *
1229 * Expands to a call to a function that returns a quark representing the
1230 * incoming messages features of a #TpTextChannel.
1231 *
1232 * When this feature is prepared, tp_text_channel_dup_pending_messages() will
1233 * return a non-empty list if any unacknowledged messages are waiting, and the
1234 * #TpTextChannel::message-received and #TpTextChannel::pending-message-removed
1235 * signals will be emitted.
1236 *
1237 * One can ask for a feature to be prepared using the
1238 * tp_proxy_prepare_async() function, and waiting for it to callback.
1239 *
1240 * Since: 0.13.10
1241 */
1242 GQuark
tp_text_channel_get_feature_quark_incoming_messages(void)1243 tp_text_channel_get_feature_quark_incoming_messages (void)
1244 {
1245 return g_quark_from_static_string (
1246 "tp-text-channel-feature-incoming-messages");
1247 }
1248
1249 /**
1250 * tp_text_channel_get_pending_messages:
1251 * @self: a #TpTextChannel
1252 *
1253 * Return a newly allocated list of unacknowledged #TpSignalledMessage
1254 * objects.
1255 *
1256 * It is guaranteed that the #TpSignalledMessage:sender of each
1257 * #TpSignalledMessage has all of the features previously passed to
1258 * tp_simple_client_factory_add_contact_features() prepared.
1259 *
1260 * Returns: (transfer container) (element-type TelepathyGLib.SignalledMessage):
1261 * a #GList of borrowed #TpSignalledMessage
1262 *
1263 * Since: 0.13.10
1264 * Deprecated: Since 0.19.9. New code should use
1265 * tp_text_channel_dup_pending_messages() instead.
1266 */
1267 GList *
tp_text_channel_get_pending_messages(TpTextChannel * self)1268 tp_text_channel_get_pending_messages (TpTextChannel *self)
1269 {
1270 return g_list_copy (g_queue_peek_head_link (self->priv->pending_messages));
1271 }
1272
1273 /**
1274 * tp_text_channel_dup_pending_messages:
1275 * @self: a #TpTextChannel
1276 *
1277 * Return a newly allocated list of unacknowledged #TpSignalledMessage
1278 * objects.
1279 *
1280 * It is guaranteed that the #TpSignalledMessage:sender of each
1281 * #TpSignalledMessage has all of the features previously passed to
1282 * tp_simple_client_factory_add_contact_features() prepared.
1283 *
1284 * Returns: (transfer full) (element-type TelepathyGLib.SignalledMessage):
1285 * a #GList of reffed #TpSignalledMessage
1286 *
1287 * Since: 0.19.9
1288 */
1289 GList *
tp_text_channel_dup_pending_messages(TpTextChannel * self)1290 tp_text_channel_dup_pending_messages (TpTextChannel *self)
1291 {
1292 return _tp_g_list_copy_deep (
1293 g_queue_peek_head_link (self->priv->pending_messages),
1294 (GCopyFunc) g_object_ref, NULL);
1295 }
1296
1297 static void
send_message_cb(TpChannel * proxy,const gchar * token,const GError * error,gpointer user_data,GObject * weak_object)1298 send_message_cb (TpChannel *proxy,
1299 const gchar *token,
1300 const GError *error,
1301 gpointer user_data,
1302 GObject *weak_object)
1303 {
1304 GSimpleAsyncResult *result = user_data;
1305
1306 if (error != NULL)
1307 {
1308 DEBUG ("Failed to send message: %s", error->message);
1309
1310 g_simple_async_result_set_from_error (result, error);
1311 }
1312
1313 g_simple_async_result_set_op_res_gpointer (result,
1314 tp_str_empty (token) ? NULL : g_strdup (token), g_free);
1315
1316 g_simple_async_result_complete_in_idle (result);
1317 g_object_unref (result);
1318 }
1319
1320 /**
1321 * tp_text_channel_send_message_async:
1322 * @self: a #TpTextChannel
1323 * @message: a #TpClientMessage
1324 * @flags: flags affecting how the message is sent
1325 * @callback: a callback to call when the message has been submitted to the
1326 * server
1327 * @user_data: data to pass to @callback
1328 *
1329 * Submit a message to the server for sending. Once the message has been
1330 * submitted to the sever, @callback will be called. You can then call
1331 * tp_text_channel_send_message_finish() to get the result of the operation.
1332 *
1333 * Since: 0.13.10
1334 */
1335 void
tp_text_channel_send_message_async(TpTextChannel * self,TpMessage * message,TpMessageSendingFlags flags,GAsyncReadyCallback callback,gpointer user_data)1336 tp_text_channel_send_message_async (TpTextChannel *self,
1337 TpMessage *message,
1338 TpMessageSendingFlags flags,
1339 GAsyncReadyCallback callback,
1340 gpointer user_data)
1341 {
1342 GSimpleAsyncResult *result;
1343
1344 g_return_if_fail (TP_IS_TEXT_CHANNEL (self));
1345 g_return_if_fail (TP_IS_CLIENT_MESSAGE (message));
1346
1347 result = g_simple_async_result_new (G_OBJECT (self), callback,
1348 user_data, tp_text_channel_send_message_async);
1349
1350 tp_cli_channel_interface_messages_call_send_message (TP_CHANNEL (self),
1351 -1, message->parts, flags, send_message_cb, result, NULL, NULL);
1352 }
1353
1354 /**
1355 * tp_text_channel_send_message_finish:
1356 * @self: a #TpTextChannel
1357 * @result: a #GAsyncResult passed to the callback for tp_text_channel_send_message_async()
1358 * @token: (out) (transfer full): if not %NULL, used to return the
1359 * token of the sent message
1360 * @error: a #GError to fill
1361 *
1362 * Completes a call to tp_text_channel_send_message_async().
1363 *
1364 * @token can be used to match any incoming delivery or failure reports
1365 * against the sent message. If this function returns true but the returned
1366 * token is %NULL, the message was sent successfully but the protocol does not
1367 * provide a way to identify it later.
1368 *
1369 * Returns: %TRUE if the message has been submitted to the server, %FALSE
1370 * otherwise.
1371 *
1372 * Since: 0.13.10
1373 */
1374 gboolean
tp_text_channel_send_message_finish(TpTextChannel * self,GAsyncResult * result,gchar ** token,GError ** error)1375 tp_text_channel_send_message_finish (TpTextChannel *self,
1376 GAsyncResult *result,
1377 gchar **token,
1378 GError **error)
1379 {
1380 _tp_implement_finish_copy_pointer (self, tp_text_channel_send_message_async,
1381 g_strdup, token);
1382 }
1383
1384 static void
acknowledge_pending_messages_ready_cb(GObject * object,GAsyncResult * res,gpointer user_data)1385 acknowledge_pending_messages_ready_cb (GObject *object,
1386 GAsyncResult *res,
1387 gpointer user_data)
1388 {
1389 TpChannel *channel = (TpChannel *) object;
1390 GSimpleAsyncResult *result = user_data;
1391
1392 _tp_channel_contacts_queue_prepare_finish (channel, res, NULL, NULL);
1393
1394 g_simple_async_result_complete_in_idle (result);
1395 g_object_unref (result);
1396 }
1397
1398 static void
acknowledge_pending_messages_cb(TpChannel * channel,const GError * error,gpointer user_data,GObject * weak_object)1399 acknowledge_pending_messages_cb (TpChannel *channel,
1400 const GError *error,
1401 gpointer user_data,
1402 GObject *weak_object)
1403 {
1404 GSimpleAsyncResult *result = user_data;
1405
1406 if (error != NULL)
1407 {
1408 DEBUG ("Failed to ack messages: %s", error->message);
1409
1410 g_simple_async_result_set_from_error (result, error);
1411 g_simple_async_result_complete_in_idle (result);
1412 g_object_unref (result);
1413 return;
1414 }
1415
1416 /* We should have already got MessagesRemoved signal, but it could still be
1417 * in the queue. So we have to hook into that queue before complete to be
1418 * sure messages are removed from pending_messages */
1419 _tp_channel_contacts_queue_prepare_async (channel, NULL,
1420 acknowledge_pending_messages_ready_cb, result);
1421 }
1422
1423 /**
1424 * tp_text_channel_ack_messages_async:
1425 * @self: a #TpTextChannel
1426 * @messages: (element-type TelepathyGLib.SignalledMessage): a #GList of
1427 * #TpSignalledMessage
1428 * @callback: a callback to call when the message have been acked
1429 * @user_data: data to pass to @callback
1430 *
1431 * Acknowledge all the messages in @messages.
1432 * Once the messages have been acked, @callback will be called.
1433 * You can then call tp_text_channel_ack_messages_finish() to get the
1434 * result of the operation.
1435 *
1436 * You should use the #TpSignalledMessage received from
1437 * tp_text_channel_dup_pending_messages() or the
1438 * #TpTextChannel::message-received signal.
1439 *
1440 * See tp_text_channel_ack_message_async() about acknowledging messages.
1441 *
1442 * Since: 0.13.10
1443 */
1444 void
tp_text_channel_ack_messages_async(TpTextChannel * self,const GList * messages,GAsyncReadyCallback callback,gpointer user_data)1445 tp_text_channel_ack_messages_async (TpTextChannel *self,
1446 const GList *messages,
1447 GAsyncReadyCallback callback,
1448 gpointer user_data)
1449 {
1450 TpChannel *chan = (TpChannel *) self;
1451 GArray *ids;
1452 GList *l;
1453 GSimpleAsyncResult *result;
1454
1455 g_return_if_fail (TP_IS_TEXT_CHANNEL (self));
1456
1457 result = g_simple_async_result_new (G_OBJECT (self), callback,
1458 user_data, tp_text_channel_ack_messages_async);
1459
1460 if (messages == NULL)
1461 {
1462 /* Nothing to ack, succeed immediately */
1463 g_simple_async_result_complete_in_idle (result);
1464
1465 g_object_unref (result);
1466 return;
1467 }
1468
1469 ids = g_array_sized_new (FALSE, FALSE, sizeof (guint),
1470 g_list_length ((GList *) messages));
1471
1472 for (l = (GList *) messages; l != NULL; l = g_list_next (l))
1473 {
1474 TpMessage *msg = l->data;
1475 guint id;
1476 gboolean valid;
1477
1478 g_return_if_fail (TP_IS_SIGNALLED_MESSAGE (msg));
1479
1480 id = _tp_signalled_message_get_pending_message_id (msg, &valid);
1481 if (!valid)
1482 {
1483 DEBUG ("Message doesn't have pending-message-id ?!");
1484 continue;
1485 }
1486
1487 g_array_append_val (ids, id);
1488 }
1489
1490 tp_cli_channel_type_text_call_acknowledge_pending_messages (chan, -1, ids,
1491 acknowledge_pending_messages_cb, result, NULL, G_OBJECT (self));
1492
1493 g_array_unref (ids);
1494 }
1495
1496 /**
1497 * tp_text_channel_ack_messages_finish:
1498 * @self: a #TpTextChannel
1499 * @result: a #GAsyncResult passed to the callback for tp_text_channel_ack_messages_async()
1500 * @error: a #GError to fill
1501 *
1502 * Finishes acknowledging a list of messages.
1503 *
1504 * Returns: %TRUE if the messages have been acked, %FALSE otherwise.
1505 *
1506 * Since: 0.13.10
1507 */
1508 gboolean
tp_text_channel_ack_messages_finish(TpTextChannel * self,GAsyncResult * result,GError ** error)1509 tp_text_channel_ack_messages_finish (TpTextChannel *self,
1510 GAsyncResult *result,
1511 GError **error)
1512 {
1513 _tp_implement_finish_void (self, tp_text_channel_ack_messages_async)
1514 }
1515
1516 /**
1517 * tp_text_channel_ack_message_async:
1518 * @self: a #TpTextChannel
1519 * @message: a #TpSignalledMessage
1520 * @callback: a callback to call when the message have been acked
1521 * @user_data: data to pass to @callback
1522 *
1523 * Acknowledge @message. Once the message has been acked, @callback will be
1524 * called. You can then call tp_text_channel_ack_message_finish() to get the
1525 * result of the operation.
1526 *
1527 * A message should be acknowledged once it has been shown to the user by the
1528 * Handler of the channel. So Observers and Approvers should NOT acknowledge
1529 * messages themselves.
1530 * Once a message has been acknowledged, it is removed from the
1531 * pending-message queue and so the #TpTextChannel::pending-message-removed
1532 * signal is fired.
1533 *
1534 * You should use the #TpSignalledMessage received from
1535 * tp_text_channel_dup_pending_messages() or the
1536 * #TpTextChannel::message-received signal.
1537 *
1538 * Since: 0.13.10
1539 */
1540 void
tp_text_channel_ack_message_async(TpTextChannel * self,TpMessage * message,GAsyncReadyCallback callback,gpointer user_data)1541 tp_text_channel_ack_message_async (TpTextChannel *self,
1542 TpMessage *message,
1543 GAsyncReadyCallback callback,
1544 gpointer user_data)
1545 {
1546 TpChannel *chan = (TpChannel *) self;
1547 GSimpleAsyncResult *result;
1548 GArray *ids;
1549 guint id;
1550 gboolean valid;
1551
1552 g_return_if_fail (TP_IS_TEXT_CHANNEL (self));
1553 g_return_if_fail (TP_IS_SIGNALLED_MESSAGE (message));
1554
1555 id = _tp_signalled_message_get_pending_message_id (message, &valid);
1556 if (!valid)
1557 {
1558 g_simple_async_report_error_in_idle (G_OBJECT (self), callback, user_data,
1559 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
1560 "Message doesn't have a pending-message-id");
1561
1562 return;
1563 }
1564
1565 result = g_simple_async_result_new (G_OBJECT (self), callback,
1566 user_data, tp_text_channel_ack_message_async);
1567
1568 ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
1569 g_array_append_val (ids, id);
1570
1571 tp_cli_channel_type_text_call_acknowledge_pending_messages (chan, -1, ids,
1572 acknowledge_pending_messages_cb, result, NULL, G_OBJECT (self));
1573
1574 g_array_unref (ids);
1575 }
1576
1577 /**
1578 * tp_text_channel_ack_message_finish:
1579 * @self: a #TpTextChannel
1580 * @result: a #GAsyncResult passed to the callback for tp_text_channel_ack_message_async()
1581 * @error: a #GError to fill
1582 *
1583 * Finishes acknowledging a message.
1584 *
1585 * Returns: %TRUE if the message has been acked, %FALSE otherwise.
1586 *
1587 * Since: 0.13.10
1588 */
1589 gboolean
tp_text_channel_ack_message_finish(TpTextChannel * self,GAsyncResult * result,GError ** error)1590 tp_text_channel_ack_message_finish (TpTextChannel *self,
1591 GAsyncResult *result,
1592 GError **error)
1593 {
1594 _tp_implement_finish_void (self, tp_text_channel_ack_message_async)
1595 }
1596
1597 /**
1598 * TP_TEXT_CHANNEL_FEATURE_CHAT_STATES:
1599 *
1600 * Expands to a call to a function that returns a quark representing the
1601 * chat states feature on a #TpTextChannel.
1602 *
1603 * When this feature is prepared, tp_text_channel_get_chat_state() and the
1604 * #TpTextChannel::contact-chat-state-changed signal become useful.
1605 *
1606 * One can ask for a feature to be prepared using the
1607 * tp_proxy_prepare_async() function, and waiting for it to callback.
1608 *
1609 * Since: 0.19.0
1610 */
1611
1612 GQuark
tp_text_channel_get_feature_quark_chat_states(void)1613 tp_text_channel_get_feature_quark_chat_states (void)
1614 {
1615 return g_quark_from_static_string ("tp-text-channel-feature-chat-states");
1616 }
1617
1618 /**
1619 * tp_text_channel_get_chat_state:
1620 * @self: a channel
1621 * @contact: a #TpContact
1622 *
1623 * Return the chat state for the given contact. If tp_proxy_is_prepared()
1624 * would return %FALSE for the feature %TP_TEXT_CHANNEL_FEATURE_CHAT_STATES,
1625 * the result will always be %TP_CHANNEL_CHAT_STATE_INACTIVE.
1626 *
1627 * Returns: the chat state for @contact, or %TP_CHANNEL_CHAT_STATE_INACTIVE
1628 * if their chat state is not known
1629 * Since: 0.19.0
1630 */
1631 TpChannelChatState
tp_text_channel_get_chat_state(TpTextChannel * self,TpContact * contact)1632 tp_text_channel_get_chat_state (TpTextChannel *self,
1633 TpContact *contact)
1634 {
1635 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), 0);
1636
1637 /* Use the deprecated function internally to avoid duplicated introspection */
1638 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1639 return tp_channel_get_chat_state ((TpChannel *) self,
1640 tp_contact_get_handle (contact));
1641 G_GNUC_END_IGNORE_DEPRECATIONS
1642 }
1643
1644 static void
set_chat_state_cb(TpChannel * proxy,const GError * error,gpointer user_data,GObject * weak_object)1645 set_chat_state_cb (TpChannel *proxy,
1646 const GError *error,
1647 gpointer user_data,
1648 GObject *weak_object)
1649 {
1650 GSimpleAsyncResult *result = user_data;
1651
1652 if (error != NULL)
1653 {
1654 DEBUG ("SetChatState failed: %s", error->message);
1655
1656 g_simple_async_result_set_from_error (result, error);
1657 }
1658
1659 g_simple_async_result_complete_in_idle (result);
1660 g_object_unref (result);
1661 }
1662
1663 /**
1664 * tp_text_channel_set_chat_state_async:
1665 * @self: a #TpTextChannel
1666 * @state: a #TpChannelChatState to set
1667 * @callback: a callback to call when the chat state has been set
1668 * @user_data: data to pass to @callback
1669 *
1670 * Set the local state on channel @self to @state.
1671 * Once the state has been set, @callback will be called.
1672 * You can then call tp_text_channel_set_chat_state_finish() to get the
1673 * result of the operation.
1674 *
1675 * Since: 0.13.10
1676 */
1677 void
tp_text_channel_set_chat_state_async(TpTextChannel * self,TpChannelChatState state,GAsyncReadyCallback callback,gpointer user_data)1678 tp_text_channel_set_chat_state_async (TpTextChannel *self,
1679 TpChannelChatState state,
1680 GAsyncReadyCallback callback,
1681 gpointer user_data)
1682 {
1683 GSimpleAsyncResult *result;
1684
1685 result = g_simple_async_result_new (G_OBJECT (self), callback,
1686 user_data, tp_text_channel_set_chat_state_async);
1687
1688 tp_cli_channel_interface_chat_state_call_set_chat_state (TP_CHANNEL (self),
1689 -1, state, set_chat_state_cb, result, NULL, G_OBJECT (self));
1690 }
1691
1692 /**
1693 * tp_text_channel_set_chat_state_finish:
1694 * @self: a #TpTextChannel
1695 * @result: a #GAsyncResult passed to the callback for tp_text_channel_set_chat_state_async()
1696 * @error: a #GError to fill
1697 *
1698 * Completes a call to tp_text_channel_set_chat_state_async().
1699 *
1700 * Returns: %TRUE if the chat state has been changed, %FALSE otherwise.
1701 *
1702 * Since: 0.13.10
1703 */
1704 gboolean
tp_text_channel_set_chat_state_finish(TpTextChannel * self,GAsyncResult * result,GError ** error)1705 tp_text_channel_set_chat_state_finish (TpTextChannel *self,
1706 GAsyncResult *result,
1707 GError **error)
1708 {
1709 _tp_implement_finish_void (self, tp_text_channel_set_chat_state_async)
1710 }
1711
1712 /**
1713 * tp_text_channel_get_message_types:
1714 * @self: a #TpTextChannel
1715 *
1716 * Return the #TpTextChannel:message-types property
1717 *
1718 * Returns: (transfer none) (element-type TelepathyGLib.ChannelTextMessageType):
1719 * the value of #TpTextChannel:message-types
1720 *
1721 * Since: 0.13.16
1722 */
1723 GArray *
tp_text_channel_get_message_types(TpTextChannel * self)1724 tp_text_channel_get_message_types (TpTextChannel *self)
1725 {
1726 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), NULL);
1727
1728 return self->priv->message_types;
1729 }
1730
1731 /**
1732 * tp_text_channel_supports_message_type:
1733 * @self: a #TpTextChannel
1734 * @message_type: a #TpChannelTextMessageType
1735 *
1736 * Check if message of type @message_type can be sent on this channel.
1737 *
1738 * Returns: %TRUE if message of type @message_type can be sent on @self, %FALSE
1739 * otherwise
1740 *
1741 * Since: 0.13.16
1742 */
1743 gboolean
tp_text_channel_supports_message_type(TpTextChannel * self,TpChannelTextMessageType message_type)1744 tp_text_channel_supports_message_type (TpTextChannel *self,
1745 TpChannelTextMessageType message_type)
1746 {
1747 guint i;
1748
1749 for (i = 0; i < self->priv->message_types->len; i++)
1750 {
1751 TpChannelTextMessageType tmp = g_array_index (self->priv->message_types,
1752 TpChannelTextMessageType, i);
1753
1754 if (tmp == message_type)
1755 return TRUE;
1756 }
1757
1758 return FALSE;
1759 }
1760
1761 /**
1762 * TP_TEXT_CHANNEL_FEATURE_SMS:
1763 *
1764 * Expands to a call to a function that returns a quark representing the
1765 * SMS feature of a #TpTextChannel.
1766 *
1767 * When this feature is prepared, the TpTextChannel:is-sms-channel property
1768 * will have a meaningful value and will be updated when needed.
1769 *
1770 * One can ask for a feature to be prepared using the
1771 * tp_proxy_prepare_async() function, and waiting for it to callback.
1772 *
1773 * Since: 0.15.1
1774 */
1775 GQuark
tp_text_channel_get_feature_quark_sms(void)1776 tp_text_channel_get_feature_quark_sms (void)
1777 {
1778 return g_quark_from_static_string ("tp-text-channel-feature-sms");
1779 }
1780
1781 /**
1782 * tp_text_channel_is_sms_channel:
1783 * @self: a #TpTextChannel
1784 *
1785 * Return the #TpTextChannel:is-sms-channel property
1786 *
1787 * Returns: the value of #TpTextChannel:is-sms-channel property
1788 *
1789 * Since: 0.15.1
1790 */
1791 gboolean
tp_text_channel_is_sms_channel(TpTextChannel * self)1792 tp_text_channel_is_sms_channel (TpTextChannel *self)
1793 {
1794 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), FALSE);
1795
1796 return self->priv->is_sms_channel;
1797 }
1798
1799 /**
1800 * tp_text_channel_get_sms_flash:
1801 * @self: a #TpTextChannel
1802 *
1803 * Return the #TpTextChannel:sms-flash property
1804 *
1805 * Returns: the value of #TpTextChannel:sms-flash property
1806 *
1807 * Since: 0.15.1
1808 */
1809 gboolean
tp_text_channel_get_sms_flash(TpTextChannel * self)1810 tp_text_channel_get_sms_flash (TpTextChannel *self)
1811 {
1812 g_return_val_if_fail (TP_IS_TEXT_CHANNEL (self), FALSE);
1813
1814 return self->priv->sms_flash;
1815 }
1816
1817 typedef struct
1818 {
1819 guint chunks_required;
1820 gint remaining_characters;
1821 gint estimated_cost;
1822 } GetSmsLengthReturn;
1823
1824 static GetSmsLengthReturn *
get_sms_length_return_new(guint chunks_required,gint remaining_characters,gint estimated_cost)1825 get_sms_length_return_new (guint chunks_required,
1826 gint remaining_characters,
1827 gint estimated_cost)
1828 {
1829 GetSmsLengthReturn *result = g_slice_new (GetSmsLengthReturn);
1830
1831 result->chunks_required = chunks_required;
1832 result->remaining_characters = remaining_characters;
1833 result->estimated_cost = estimated_cost;
1834
1835 return result;
1836 }
1837
1838 static void
get_sms_length_return_free(GetSmsLengthReturn * r)1839 get_sms_length_return_free (GetSmsLengthReturn *r)
1840 {
1841 g_slice_free (GetSmsLengthReturn, r);
1842 }
1843
1844 static void
get_sms_length_cb(TpChannel * proxy,guint chunks_required,gint remaining_characters,gint estimated_cost,const GError * error,gpointer user_data,GObject * weak_object)1845 get_sms_length_cb (TpChannel *proxy,
1846 guint chunks_required,
1847 gint remaining_characters,
1848 gint estimated_cost,
1849 const GError *error,
1850 gpointer user_data,
1851 GObject *weak_object)
1852 {
1853 GSimpleAsyncResult *result = user_data;
1854 GetSmsLengthReturn *r;
1855
1856 if (error != NULL)
1857 {
1858 DEBUG ("Failed to get SMS length: %s", error->message);
1859
1860 g_simple_async_result_set_from_error (result, error);
1861 goto out;
1862 }
1863
1864 r = get_sms_length_return_new (chunks_required, remaining_characters,
1865 estimated_cost);
1866
1867 g_simple_async_result_set_op_res_gpointer (result, r,
1868 (GDestroyNotify) get_sms_length_return_free);
1869
1870 out:
1871 g_simple_async_result_complete_in_idle (result);
1872 }
1873
1874 /**
1875 * tp_text_channel_get_sms_length_async:
1876 * @self: a #TpTextChannel
1877 * @message: a #TpClientMessage
1878 * @callback: a callback to call when the request has been satisfied
1879 * @user_data: data to pass to @callback
1880 *
1881 * Starts an async call to get the number of 140 octet chunks required to
1882 * send a #message via SMS on #self, as well as the number of remaining
1883 * characters available in the final chunk and, if possible,
1884 * an estimate of the cost.
1885 *
1886 * Once the request has been satisfied, @callback will be called.
1887 * You can then call tp_text_channel_get_sms_length_finish() to get the
1888 * result of the operation.
1889 *
1890 * Since: 0.15.1
1891 */
1892 void
tp_text_channel_get_sms_length_async(TpTextChannel * self,TpMessage * message,GAsyncReadyCallback callback,gpointer user_data)1893 tp_text_channel_get_sms_length_async (TpTextChannel *self,
1894 TpMessage *message,
1895 GAsyncReadyCallback callback,
1896 gpointer user_data)
1897 {
1898 GSimpleAsyncResult *result;
1899
1900 result = g_simple_async_result_new ((GObject *) self, callback, user_data,
1901 tp_text_channel_get_sms_length_async);
1902
1903 tp_cli_channel_interface_sms_call_get_sms_length ((TpChannel *) self, -1,
1904 message->parts, get_sms_length_cb, result, g_object_unref,
1905 G_OBJECT (self));
1906 }
1907
1908
1909 /**
1910 * tp_text_channel_get_sms_length_finish:
1911 * @self: a #TpTextChannel
1912 * @result: a #GAsyncResult
1913 * @chunks_required: (out): if not %NULL used to return
1914 * the number of 140 octet chunks required to send the message.
1915 * @remaining_characters: (out): if not %NULL used to return
1916 * the number of further characters that can be fit in the final chunk.
1917 * A negative value indicates that the message will be truncated by
1918 * abs(@remaining_characters).
1919 * The value #G_MININT32 indicates the message will be truncated by
1920 * an unknown amount.
1921 * @estimated_cost: (out): if not %NULL used to return
1922 * the estimated cost of sending this message.
1923 * The currency and scale of this value are the same as the
1924 * values of the #TpConnection:balance-scale and
1925 * #TpConnection:balance-currency properties.
1926 * A value of -1 indicates the cost could not be estimated.
1927 * @error: a #GError to fill
1928 *
1929 * Finishes an async SMS length request.
1930 *
1931 * Returns: %TRUE if the number of 140 octet chunks required to send
1932 * the message has been retrieved, %FALSE otherwise.
1933 *
1934 * Since: 0.15.1
1935 */
1936 gboolean
tp_text_channel_get_sms_length_finish(TpTextChannel * self,GAsyncResult * result,guint * chunks_required,gint * remaining_characters,gint * estimated_cost,GError ** error)1937 tp_text_channel_get_sms_length_finish (TpTextChannel *self,
1938 GAsyncResult *result,
1939 guint *chunks_required,
1940 gint *remaining_characters,
1941 gint *estimated_cost,
1942 GError **error)
1943 {
1944 GSimpleAsyncResult *simple = (GSimpleAsyncResult *) result;
1945 GetSmsLengthReturn *r;
1946
1947 if (g_simple_async_result_propagate_error (simple, error))
1948 return FALSE;
1949
1950 g_return_val_if_fail (g_simple_async_result_is_valid (result,
1951 G_OBJECT (self), tp_text_channel_get_sms_length_async), FALSE);
1952
1953 r = g_simple_async_result_get_op_res_gpointer (simple);
1954
1955 if (chunks_required != NULL)
1956 *chunks_required = r->chunks_required;
1957
1958 if (remaining_characters != NULL)
1959 *remaining_characters = r->remaining_characters;
1960
1961 if (estimated_cost != NULL)
1962 *estimated_cost = r->estimated_cost;
1963
1964 return TRUE;
1965 }
1966
1967 /**
1968 * tp_text_channel_ack_all_pending_messages_async:
1969 * @self: a #TpTextChannel
1970 * @callback: a callback to call when the messages have been acked
1971 * @user_data: data to pass to @callback
1972 *
1973 * Acknowledge all the pending messages. This is equivalent of calling
1974 * tp_text_channel_ack_messages_async() with the list of #TpSignalledMessage
1975 * returned by tp_text_channel_dup_pending_messages().
1976 *
1977 * Once the messages have been acked, @callback will be called.
1978 * You can then call tp_text_channel_ack_all_pending_messages_finish() to get
1979 * the result of the operation.
1980 *
1981 * See tp_text_channel_ack_message_async() about acknowledging messages.
1982 *
1983 * Since: 0.15.3
1984 */
1985 void
tp_text_channel_ack_all_pending_messages_async(TpTextChannel * self,GAsyncReadyCallback callback,gpointer user_data)1986 tp_text_channel_ack_all_pending_messages_async (TpTextChannel *self,
1987 GAsyncReadyCallback callback,
1988 gpointer user_data)
1989 {
1990 GList *messages;
1991
1992 messages = g_queue_peek_head_link (self->priv->pending_messages);
1993
1994 tp_text_channel_ack_messages_async (self, messages,
1995 callback, user_data);
1996 }
1997
1998 /**
1999 * tp_text_channel_ack_all_pending_messages_finish:
2000 * @self: a #TpTextChannel
2001 * @result: a #GAsyncResult
2002 * @error: a #GError to fill
2003 *
2004 * Finish an asynchronous acknowledgement operation of all messages.
2005 *
2006 * Returns: %TRUE if the messages have been acked, %FALSE otherwise.
2007 *
2008 * Since: 0.15.3
2009 */
2010 gboolean
tp_text_channel_ack_all_pending_messages_finish(TpTextChannel * self,GAsyncResult * result,GError ** error)2011 tp_text_channel_ack_all_pending_messages_finish (TpTextChannel *self,
2012 GAsyncResult *result,
2013 GError **error)
2014 {
2015 return tp_text_channel_ack_messages_finish (self, result, error);
2016 }
2017