LCOV - code coverage report
Current view: top level - src/plugins/sms - valent-sms-utils.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 86.1 % 151 130
Test Date: 2024-04-23 06:02:46 Functions: 100.0 % 12 12
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 43.8 % 160 70

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-sms-utils"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <adwaita.h>
       9                 :             : #include <gdk/gdk.h>
      10                 :             : #include <glib/gi18n.h>
      11                 :             : #include <valent.h>
      12                 :             : 
      13                 :             : #include "valent-sms-utils.h"
      14                 :             : 
      15                 :             : 
      16         [ +  + ]:          22 : G_DEFINE_QUARK (VALENT_CONTACT_ICON, valent_contact_icon)
      17         [ +  + ]:          26 : G_DEFINE_QUARK (VALENT_CONTACT_PAINTABLE, valent_contact_paintable)
      18                 :             : 
      19                 :             : 
      20                 :             : static GLoadableIcon *
      21                 :          11 : _e_contact_get_icon (EContact *contact)
      22                 :             : {
      23                 :          11 :   GLoadableIcon *icon = NULL;
      24                 :          22 :   g_autoptr (EContactPhoto) photo = NULL;
      25                 :          11 :   const unsigned char *data;
      26                 :          11 :   size_t len;
      27                 :          11 :   const char *uri;
      28                 :             : 
      29   [ +  -  +  -  :          11 :   g_assert (E_IS_CONTACT (contact));
             -  +  -  - ]
      30                 :             : 
      31                 :          11 :   icon = g_object_get_qdata (G_OBJECT (contact), valent_contact_icon_quark ());
      32                 :             : 
      33   [ -  +  -  -  :          11 :   if (G_IS_LOADABLE_ICON (icon))
             -  -  -  - ]
      34                 :             :     return icon;
      35                 :             : 
      36         [ +  - ]:          11 :   if ((photo = e_contact_get (contact, E_CONTACT_PHOTO)) == NULL)
      37                 :             :     return NULL;
      38                 :             : 
      39         [ +  - ]:          11 :   if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED &&
      40         [ +  - ]:          11 :       (data = e_contact_photo_get_inlined (photo, &len)))
      41                 :             :     {
      42         [ +  - ]:          11 :       g_autoptr (GBytes) bytes = NULL;
      43                 :             : 
      44                 :          11 :       bytes = g_bytes_new (data, len);
      45         [ +  - ]:          11 :       icon = G_LOADABLE_ICON (g_bytes_icon_new (bytes));
      46                 :             :     }
      47         [ #  # ]:           0 :   else if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
      48         [ #  # ]:           0 :            (uri = e_contact_photo_get_uri (photo)))
      49                 :             :     {
      50                 :          11 :       g_autoptr (GFile) file = NULL;
      51                 :             : 
      52                 :           0 :       file = g_file_new_for_uri (uri);
      53         [ #  # ]:           0 :       icon = G_LOADABLE_ICON (g_file_icon_new (file));
      54                 :             :     }
      55                 :             : 
      56   [ +  -  +  -  :          11 :   if (G_IS_LOADABLE_ICON (icon))
             +  -  +  - ]
      57                 :             :     {
      58                 :          11 :       g_object_set_qdata_full (G_OBJECT (contact),
      59                 :             :                                valent_contact_icon_quark (),
      60                 :             :                                icon,
      61                 :             :                                g_object_unref);
      62                 :             :     }
      63                 :             : 
      64                 :          11 :   return icon;
      65                 :             : }
      66                 :             : 
      67                 :             : static GdkPaintable *
      68                 :          15 : _e_contact_get_paintable (EContact  *contact,
      69                 :             :                           int        size,
      70                 :             :                           int        scale,
      71                 :             :                           GError   **error)
      72                 :             : {
      73                 :          15 :   GdkPaintable *paintable = NULL;
      74                 :          15 :   GLoadableIcon *icon = NULL;
      75                 :          30 :   g_autoptr (GInputStream) stream = NULL;
      76         [ +  + ]:          15 :   g_autoptr (GdkPixbuf) pixbuf = NULL;
      77                 :             : 
      78   [ +  -  +  -  :          15 :   g_assert (E_IS_CONTACT (contact));
             -  +  -  - ]
      79         [ -  + ]:          15 :   g_assert (size > 0);
      80         [ -  + ]:          15 :   g_assert (scale > 0);
      81                 :             : 
      82                 :          15 :   paintable = g_object_get_qdata (G_OBJECT (contact),
      83                 :             :                                   valent_contact_paintable_quark ());
      84                 :             : 
      85         [ +  + ]:          15 :   if (GDK_IS_PAINTABLE (paintable))
      86                 :             :     return paintable;
      87                 :             : 
      88         [ +  - ]:          11 :   if ((icon = _e_contact_get_icon (contact)) == NULL)
      89                 :             :     return NULL;
      90                 :             : 
      91         [ -  + ]:          11 :   if ((stream = g_loadable_icon_load (icon, -1, NULL, NULL, error)) == NULL)
      92                 :             :     return NULL;
      93                 :             : 
      94                 :          11 :   pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
      95                 :             :                                                 size,
      96                 :             :                                                 size,
      97                 :             :                                                 TRUE,
      98                 :             :                                                 NULL,
      99                 :             :                                                 error);
     100                 :             : 
     101         [ +  - ]:          11 :   if (pixbuf == NULL)
     102                 :             :     return NULL;
     103                 :             : 
     104                 :          11 :   paintable = GDK_PAINTABLE (gdk_texture_new_for_pixbuf (pixbuf));
     105                 :          11 :   g_object_set_qdata_full (G_OBJECT (contact),
     106                 :             :                            valent_contact_paintable_quark (),
     107                 :             :                            paintable,
     108                 :             :                            g_object_unref);
     109                 :             : 
     110                 :          11 :   return paintable;
     111                 :             : }
     112                 :             : 
     113                 :             : /**
     114                 :             :  * valent_sms_avatar_from_contact:
     115                 :             :  * @avatar: a `AdwAvatar`
     116                 :             :  * @contact: a `EContact`
     117                 :             :  *
     118                 :             :  * Set the `GdkPaintable` for @avatar from @contact.
     119                 :             :  */
     120                 :             : void
     121                 :          15 : valent_sms_avatar_from_contact (AdwAvatar *avatar,
     122                 :             :                                 EContact  *contact)
     123                 :             : {
     124                 :          15 :   GdkPaintable *paintable;
     125                 :          15 :   const char *name;
     126                 :          15 :   int size, scale;
     127                 :             : 
     128         [ +  - ]:          15 :   g_return_if_fail (ADW_IS_AVATAR (avatar));
     129   [ +  -  +  -  :          15 :   g_return_if_fail (E_IS_CONTACT (contact));
             -  +  -  - ]
     130                 :             : 
     131                 :          15 :   size = adw_avatar_get_size (avatar);
     132                 :          15 :   scale = gtk_widget_get_scale_factor (GTK_WIDGET (avatar));
     133                 :          15 :   paintable = _e_contact_get_paintable (contact, size, scale, NULL);
     134                 :             : 
     135                 :          15 :   name = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
     136                 :          15 :   adw_avatar_set_text (avatar, name);
     137                 :             : 
     138                 :          15 :   adw_avatar_set_custom_image (avatar, paintable);
     139                 :          15 :   adw_avatar_set_show_initials (avatar, paintable != NULL);
     140                 :             : }
     141                 :             : 
     142                 :             : static void
     143                 :           4 : valent_sms_contact_from_phone_cb (ValentContactStore *store,
     144                 :             :                                   GAsyncResult       *result,
     145                 :             :                                   gpointer            user_data)
     146                 :             : {
     147                 :           4 :   g_autoptr (GTask) task = user_data;
     148                 :           4 :   const char *number = g_task_get_task_data (task);
     149   [ -  -  +  - ]:           4 :   g_autoslist (GObject) contacts = NULL;
     150                 :           4 :   EContact *contact = NULL;
     151                 :           4 :   GError *error = NULL;
     152                 :             : 
     153                 :           4 :   contacts = valent_contact_store_query_finish (store, result, &error);
     154                 :             : 
     155         [ -  + ]:           4 :   if (error != NULL)
     156                 :           0 :     return g_task_return_error (task, error);
     157                 :             : 
     158                 :             :   /* Prefer using libphonenumber */
     159         [ +  - ]:           4 :   if (e_phone_number_is_supported ())
     160                 :             :     {
     161         [ +  - ]:           4 :       if (contacts != NULL)
     162                 :           4 :         contact = g_object_ref (contacts->data);
     163                 :             :     }
     164                 :             :   else
     165                 :             :     {
     166                 :           0 :       g_autofree char *normalized = NULL;
     167                 :             : 
     168                 :           0 :       normalized = valent_phone_number_normalize (number);
     169                 :             : 
     170         [ #  # ]:           0 :       for (const GSList *iter = contacts; iter; iter = iter->next)
     171                 :             :         {
     172         [ #  # ]:           0 :           if (valent_phone_number_of_contact (iter->data, normalized))
     173                 :             :             {
     174                 :           0 :               contact = g_object_ref (iter->data);
     175                 :           0 :               break;
     176                 :             :             }
     177                 :             :         }
     178                 :             :     }
     179                 :             : 
     180         [ -  + ]:           4 :   if (contact == NULL)
     181                 :             :     {
     182                 :           0 :       contact = e_contact_new ();
     183                 :           0 :       e_contact_set (contact, E_CONTACT_FULL_NAME, number);
     184                 :           0 :       e_contact_set (contact, E_CONTACT_PHONE_OTHER, number);
     185                 :             :     }
     186                 :             : 
     187                 :           4 :   g_task_return_pointer (task, contact, g_object_unref);
     188                 :             : }
     189                 :             : 
     190                 :             : /**
     191                 :             :  * valent_sms_contact_from_phone:
     192                 :             :  * @store: a `ValentContactStore`
     193                 :             :  * @phone: a phone number
     194                 :             :  * @cancellable: (nullable): `GCancellable`
     195                 :             :  * @callback: (scope async): a `GAsyncReadyCallback`
     196                 :             :  * @user_data: (closure): user supplied data
     197                 :             :  *
     198                 :             :  * A convenience wrapper around [method@Valent.ContactStore.query] for finding a
     199                 :             :  * contact by phone number.
     200                 :             :  *
     201                 :             :  * Call valent_sms_contact_from_phone_finish() to get the result.
     202                 :             :  */
     203                 :             : void
     204                 :           5 : valent_sms_contact_from_phone (ValentContactStore  *store,
     205                 :             :                                const char          *number,
     206                 :             :                                GCancellable        *cancellable,
     207                 :             :                                GAsyncReadyCallback  callback,
     208                 :             :                                gpointer             user_data)
     209                 :             : {
     210                 :           0 :   g_autoptr (GTask) task = NULL;
     211                 :           0 :   g_autoptr (EBookQuery) query = NULL;
     212         [ +  - ]:           5 :   g_autofree char *sexp = NULL;
     213                 :             : 
     214         [ +  - ]:           5 :   g_return_if_fail (VALENT_IS_CONTACT_STORE (store));
     215   [ +  -  -  + ]:           5 :   g_return_if_fail (number != NULL && *number != '\0');
     216   [ -  +  -  -  :           5 :   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
             -  -  -  - ]
     217                 :             : 
     218                 :           5 :   task = g_task_new (store, cancellable, callback, user_data);
     219         [ +  - ]:           5 :   g_task_set_source_tag (task, valent_sms_contact_from_phone);
     220         [ -  + ]:          10 :   g_task_set_task_data (task, g_strdup (number), g_free);
     221                 :             : 
     222                 :             :   /* Prefer using libphonenumber */
     223         [ +  - ]:           5 :   if (e_phone_number_is_supported ())
     224                 :             :     {
     225                 :           5 :       query = e_book_query_field_test (E_CONTACT_TEL,
     226                 :             :                                        E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER,
     227                 :             :                                        number);
     228                 :           5 :       sexp = e_book_query_to_string (query);
     229                 :             :     }
     230                 :             :   else
     231                 :             :     {
     232                 :           0 :       query = e_book_query_field_exists (E_CONTACT_TEL);
     233                 :           0 :       sexp = e_book_query_to_string (query);
     234                 :             :     }
     235                 :             : 
     236                 :           5 :   valent_contact_store_query (store,
     237                 :             :                               sexp,
     238                 :             :                               cancellable,
     239                 :             :                               (GAsyncReadyCallback)valent_sms_contact_from_phone_cb,
     240                 :             :                               g_steal_pointer (&task));
     241                 :             : }
     242                 :             : 
     243                 :             : /**
     244                 :             :  * valent_sms_contact_from_phone_finish:
     245                 :             :  * @store: a `ValentContactStore`
     246                 :             :  * @result: a `GAsyncResult`
     247                 :             :  * @error: (nullable): a `GError`
     248                 :             :  *
     249                 :             :  * Finish an operation started by valent_sms_contact_from_phone().
     250                 :             :  *
     251                 :             :  * Returns: (transfer full): an `EContact`
     252                 :             :  */
     253                 :             : EContact *
     254                 :           4 : valent_sms_contact_from_phone_finish (ValentContactStore  *store,
     255                 :             :                                       GAsyncResult        *result,
     256                 :             :                                       GError             **error)
     257                 :             : {
     258         [ +  - ]:           4 :   g_return_val_if_fail (VALENT_IS_CONTACT_STORE (store), NULL);
     259         [ -  + ]:           4 :   g_return_val_if_fail (g_task_is_valid (result, store), NULL);
     260   [ +  -  -  + ]:           4 :   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
     261                 :             : 
     262                 :           4 :   return g_task_propagate_pointer (G_TASK (result), error);
     263                 :             : }
     264                 :             : 
     265                 :             : /**
     266                 :             :  * valent_phone_number_normalize:
     267                 :             :  * @number: a phone number string
     268                 :             :  *
     269                 :             :  * Return a normalized version of @number.
     270                 :             :  *
     271                 :             :  * Returns: (transfer full): a normalized phone number string
     272                 :             :  *
     273                 :             :  * Since: 1.0
     274                 :             :  */
     275                 :             : char *
     276                 :          15 : valent_phone_number_normalize (const char *number)
     277                 :             : {
     278                 :          30 :   g_autofree char *normalized = NULL;
     279                 :          15 :   size_t i, len;
     280                 :          15 :   const char *s = number;
     281                 :             : 
     282         [ +  - ]:          15 :   g_return_val_if_fail (number != NULL, NULL);
     283                 :             : 
     284                 :             : #ifndef __clang_analyzer__
     285                 :          15 :   i = 0;
     286                 :          15 :   len = strlen (number);
     287                 :          15 :   normalized = g_new (char, len + 1);
     288                 :             : 
     289         [ +  + ]:          19 :   while (*s == '0')
     290                 :           4 :     s++;
     291                 :             : 
     292         [ +  + ]:         212 :   while (*s != '\0')
     293                 :             :     {
     294         [ +  + ]:         197 :       if G_LIKELY (g_ascii_isdigit (*s))
     295                 :         152 :         normalized[i++] = *s;
     296                 :             : 
     297                 :         197 :       s++;
     298                 :             :     }
     299                 :          15 :   normalized[i++] = '\0';
     300                 :          15 :   normalized = g_realloc (normalized, i * sizeof (char));
     301                 :             : 
     302                 :             :   /* If we fail or the number is stripped completely, return the original */
     303         [ -  + ]:          15 :   if G_UNLIKELY (*normalized == '\0')
     304         [ #  # ]:           0 :     return g_strdup (number);
     305                 :             : #endif /* __clang_analyzer__ */
     306                 :             : 
     307                 :             :   return g_steal_pointer (&normalized);
     308                 :             : }
     309                 :             : 
     310                 :             : static inline gboolean
     311                 :           1 : valent_phone_number_compare_normalized (const char *number1,
     312                 :             :                                         const char *number2)
     313                 :             : {
     314                 :           1 :   size_t number1_len, number2_len;
     315                 :             : 
     316         [ +  - ]:           1 :   g_assert (number1 != NULL);
     317         [ -  + ]:           1 :   g_assert (number2 != NULL);
     318                 :             : 
     319                 :           1 :   number1_len = strlen (number1);
     320                 :           1 :   number2_len = strlen (number2);
     321                 :             : 
     322         [ -  + ]:           1 :   if (number1_len > number2_len)
     323                 :           0 :     return g_str_equal (number1 + number1_len - number2_len, number2);
     324                 :             :   else
     325                 :           1 :     return g_str_equal (number2 + number2_len - number1_len, number1);
     326                 :             : }
     327                 :             : 
     328                 :             : /**
     329                 :             :  * valent_phone_number_equal:
     330                 :             :  * @number1: a phone number string
     331                 :             :  * @number2: a phone number string
     332                 :             :  *
     333                 :             :  * Normalize and compare @number1 with @number2 and return %TRUE if they match
     334                 :             :  * or %FALSE if they don't.
     335                 :             :  *
     336                 :             :  * Returns: %TRUE or %FALSE indicating equality
     337                 :             :  *
     338                 :             :  * Since: 1.0
     339                 :             :  */
     340                 :             : gboolean
     341                 :           4 : valent_phone_number_equal (const char *number1,
     342                 :             :                            const char *number2)
     343                 :             : {
     344                 :           8 :   g_autofree char *normalized1 = NULL;
     345                 :           4 :   g_autofree char *normalized2 = NULL;
     346                 :           4 :   size_t num1_len, num2_len;
     347                 :             : 
     348         [ +  - ]:           4 :   g_return_val_if_fail (number1 != NULL, FALSE);
     349         [ -  + ]:           4 :   g_return_val_if_fail (number2 != NULL, FALSE);
     350                 :             : 
     351                 :           4 :   normalized1 = valent_phone_number_normalize (number1);
     352                 :           4 :   normalized2 = valent_phone_number_normalize (number2);
     353                 :             : 
     354                 :           4 :   num1_len = strlen (normalized1);
     355                 :           4 :   num2_len = strlen (normalized2);
     356                 :             : 
     357         [ -  + ]:           4 :   if (num1_len > num2_len)
     358                 :           0 :     return g_str_equal (normalized1 + num1_len - num2_len, normalized2);
     359                 :             :   else
     360                 :           4 :     return g_str_equal (normalized2 + num2_len - num1_len, normalized1);
     361                 :             : }
     362                 :             : 
     363                 :             : /**
     364                 :             :  * valent_phone_number_of_contact:
     365                 :             :  * @contact: an `EContact`
     366                 :             :  * @number: a normalized phone number
     367                 :             :  *
     368                 :             :  * Check if @contact has @number as one of it's phone numbers.
     369                 :             :  *
     370                 :             :  * Since this function is typically used to test against a series of contacts, it is expected that
     371                 :             :  * @number has already been normalized with valent_phone_number_normalize().
     372                 :             :  *
     373                 :             :  * Returns: %TRUE if @number belongs to the contact
     374                 :             :  */
     375                 :             : gboolean
     376                 :           1 : valent_phone_number_of_contact (EContact   *contact,
     377                 :             :                                 const char *number)
     378                 :             : {
     379                 :           1 :   GList *numbers = NULL;
     380                 :           1 :   gboolean ret = FALSE;
     381                 :             : 
     382   [ +  -  +  -  :           1 :   g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
             -  +  -  - ]
     383         [ -  + ]:           1 :   g_return_val_if_fail (number != NULL, FALSE);
     384                 :             : 
     385                 :           1 :   numbers = e_contact_get (contact, E_CONTACT_TEL);
     386                 :             : 
     387         [ +  - ]:           1 :   for (const GList *iter = numbers; iter; iter = iter->next)
     388                 :             :     {
     389                 :           1 :       g_autofree char *normalized = NULL;
     390                 :             : 
     391                 :           1 :       normalized = valent_phone_number_normalize (iter->data);
     392                 :             : 
     393         [ -  + ]:           1 :       if ((ret = valent_phone_number_compare_normalized (number, normalized)))
     394                 :             :         break;
     395                 :             :     }
     396                 :           1 :   g_list_free_full (numbers, g_free);
     397                 :             : 
     398                 :           1 :   return ret;
     399                 :             : }
     400                 :             : 
        

Generated by: LCOV version 2.0-1