/* * Seahorse * * Copyright (C) 2008 Stefan Walter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, see * . */ #include "config.h" #include "seahorse-pgp-key.h" #include "seahorse-pgp-uid.h" #include "seahorse-pgp-signature.h" #include #include enum { PROP_0, PROP_PARENT, PROP_SIGNATURES, PROP_VALIDITY, PROP_NAME, PROP_EMAIL, PROP_COMMENT }; typedef struct _SeahorsePgpUidPrivate { SeahorsePgpKey *parent; GListModel *signatures; SeahorseValidity validity; gboolean realized; char *name; char *email; char *comment; } SeahorsePgpUidPrivate; G_DEFINE_TYPE_WITH_PRIVATE (SeahorsePgpUid, seahorse_pgp_uid, SEAHORSE_TYPE_OBJECT); /* ----------------------------------------------------------------------------- * INTERNAL HELPERS */ static char * convert_string (const char *str) { if (!str) return NULL; /* If not utf8 valid, assume latin 1 */ if (!g_utf8_validate (str, -1, NULL)) return g_convert (str, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); return g_strdup (str); } #ifndef HAVE_STRSEP /* code taken from glibc-2.2.1/sysdeps/generic/strsep.c */ char * strsep (char **stringp, const char *delim) { char *begin, *end; begin = *stringp; if (begin == NULL) return NULL; /* A frequent case is when the delimiter string contains only one character. Here we don't need to call the expensive `strpbrk' function and instead work using `strchr'. */ if (delim[0] == '\0' || delim[1] == '\0') { char ch = delim[0]; if (ch == '\0') end = NULL; else { if (*begin == ch) end = begin; else if (*begin == '\0') end = NULL; else end = strchr (begin + 1, ch); } } else /* Find the end of the token. */ end = strpbrk (begin, delim); if (end) { /* Terminate the token and set *STRINGP past NUL character. */ *end++ = '\0'; *stringp = end; } else /* No more delimiters; this is the last token. */ *stringp = NULL; return begin; } #endif /*HAVE_STRSEP*/ /* Copied from GPGME */ static void parse_user_id (const char *uid, char **name, char **email, char **comment) { char *src, *tail; g_autofree char *x = NULL; int in_name = 0; int in_email = 0; int in_comment = 0; x = tail = src = g_strdup (uid); while (*src) { if (in_email) { if (*src == '<') /* Not legal but anyway. */ in_email++; else if (*src == '>') { if (!--in_email && !*email) { *email = tail; *src = 0; tail = src + 1; } } } else if (in_comment) { if (*src == '(') in_comment++; else if (*src == ')') { if (!--in_comment && !*comment) { *comment = tail; *src = 0; tail = src + 1; } } } else if (*src == '<') { if (in_name) { if (!*name) { *name = tail; *src = 0; tail = src + 1; } in_name = 0; } else tail = src + 1; in_email = 1; } else if (*src == '(') { if (in_name) { if (!*name) { *name = tail; *src = 0; tail = src + 1; } in_name = 0; } in_comment = 1; } else if (!in_name && *src != ' ' && *src != '\t') { in_name = 1; } src++; } if (in_name) { if (!*name) { *name = tail; *src = 0; tail = src + 1; } } /* Let unused parts point to an EOS. */ *name = g_strdup (*name ? *name : ""); *email = g_strdup (*email ? *email : ""); *comment = g_strdup (*comment ? *comment : ""); g_strstrip (*name); g_strstrip (*email); g_strstrip (*comment); } /* ----------------------------------------------------------------------------- * OBJECT */ void seahorse_pgp_uid_realize (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_autofree char *markup = NULL; g_autofree char *label = NULL; /* Don't realize if no name present */ if (!priv->name) return; priv->realized = TRUE; label = seahorse_pgp_uid_calc_label (priv->name, priv->email, priv->comment); markup = seahorse_pgp_uid_calc_markup (priv->name, priv->email, priv->comment, 0); g_object_set (self, "markup", markup, "label", label, NULL); } static void seahorse_pgp_uid_init (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); priv->signatures = G_LIST_MODEL (g_list_store_new (SEAHORSE_PGP_TYPE_SIGNATURE)); g_object_set (self, "icon", NULL, "usage", SEAHORSE_USAGE_IDENTITY, NULL); } static void seahorse_pgp_uid_constructed (GObject *object) { G_OBJECT_CLASS (seahorse_pgp_uid_parent_class)->constructed (object); seahorse_pgp_uid_realize (SEAHORSE_PGP_UID (object)); } static void seahorse_pgp_uid_get_property (GObject *object, unsigned int prop_id, GValue *value, GParamSpec *pspec) { SeahorsePgpUid *self = SEAHORSE_PGP_UID (object); switch (prop_id) { case PROP_SIGNATURES: g_value_set_object (value, seahorse_pgp_uid_get_signatures (self)); break; case PROP_PARENT: g_value_set_object (value, seahorse_pgp_uid_get_parent (self)); break; case PROP_VALIDITY: g_value_set_uint (value, seahorse_pgp_uid_get_validity (self)); break; case PROP_NAME: g_value_set_string (value, seahorse_pgp_uid_get_name (self)); break; case PROP_EMAIL: g_value_set_string (value, seahorse_pgp_uid_get_email (self)); break; case PROP_COMMENT: g_value_set_string (value, seahorse_pgp_uid_get_comment (self)); break; } } static void seahorse_pgp_uid_set_property (GObject *object, unsigned int prop_id, const GValue *value, GParamSpec *pspec) { SeahorsePgpUid *self = SEAHORSE_PGP_UID (object); SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); switch (prop_id) { case PROP_PARENT: g_return_if_fail (priv->parent == NULL); priv->parent = g_value_get_object (value); break; case PROP_VALIDITY: seahorse_pgp_uid_set_validity (self, g_value_get_uint (value)); break; case PROP_NAME: seahorse_pgp_uid_set_name (self, g_value_get_string (value)); break; case PROP_EMAIL: seahorse_pgp_uid_set_email (self, g_value_get_string (value)); break; case PROP_COMMENT: seahorse_pgp_uid_set_comment (self, g_value_get_string (value)); break; } } static void seahorse_pgp_uid_object_finalize (GObject *gobject) { SeahorsePgpUid *self = SEAHORSE_PGP_UID (gobject); SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_clear_object (&priv->signatures); g_clear_pointer (&priv->name, g_free); g_clear_pointer (&priv->email, g_free); g_clear_pointer (&priv->comment, g_free); G_OBJECT_CLASS (seahorse_pgp_uid_parent_class)->finalize (gobject); } static void seahorse_pgp_uid_class_init (SeahorsePgpUidClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructed = seahorse_pgp_uid_constructed; gobject_class->finalize = seahorse_pgp_uid_object_finalize; gobject_class->set_property = seahorse_pgp_uid_set_property; gobject_class->get_property = seahorse_pgp_uid_get_property; g_object_class_install_property (gobject_class, PROP_VALIDITY, g_param_spec_uint ("validity", "Validity", "Validity of this identity", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_PARENT, g_param_spec_object ("parent", "Parent Key", "Parent Key", SEAHORSE_PGP_TYPE_KEY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Name", "User ID name", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EMAIL, g_param_spec_string ("email", "Email", "User ID email", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_COMMENT, g_param_spec_string ("comment", "Comment", "User ID comment", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_SIGNATURES, g_param_spec_object ("signatures", "Signatures", "Signatures on this UID", G_TYPE_LIST_MODEL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } /* ----------------------------------------------------------------------------- * PUBLIC */ SeahorsePgpUid * seahorse_pgp_uid_new (SeahorsePgpKey *parent, const char *uid_string) { g_autofree char *name = NULL; g_autofree char *email = NULL; g_autofree char *comment = NULL; g_return_val_if_fail (SEAHORSE_PGP_IS_KEY (parent), NULL); if (uid_string) parse_user_id (uid_string, &name, &email, &comment); return g_object_new (SEAHORSE_PGP_TYPE_UID, "parent", parent, "name", name, "email", email, "comment", comment, NULL); } /** * seahorse_pgp_uid_get_signatures: * @self: A uid * * Returns: (transfer none): The PGP key this UID belongs to */ SeahorsePgpKey * seahorse_pgp_uid_get_parent (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_val_if_fail (SEAHORSE_PGP_IS_UID (self), NULL); return priv->parent; } /** * seahorse_pgp_uid_get_signatures: * @self: A uid * * Returns: (transfer none): The list of #SeahorsePgpSignatures */ GListModel * seahorse_pgp_uid_get_signatures (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_val_if_fail (SEAHORSE_PGP_IS_UID (self), NULL); return priv->signatures; } void seahorse_pgp_uid_add_signature (SeahorsePgpUid *self, SeahorsePgpSignature *signature) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); const char *keyid; g_return_if_fail (SEAHORSE_PGP_IS_UID (self)); g_return_if_fail (SEAHORSE_PGP_IS_SIGNATURE (signature)); keyid = seahorse_pgp_signature_get_keyid (signature); /* Don't add signature of the parent key */ if (seahorse_pgp_key_has_keyid (priv->parent, keyid)) return; /* Don't allow duplicates */ for (unsigned i = 0; i < g_list_model_get_n_items (priv->signatures); i++) { g_autoptr(SeahorsePgpSignature) sig = g_list_model_get_item (priv->signatures, i); const char *sig_keyid; sig = g_list_model_get_item (priv->signatures, i); sig_keyid = seahorse_pgp_signature_get_keyid (sig); if (seahorse_pgp_keyid_equal (keyid, sig_keyid)) return; } g_list_store_append (G_LIST_STORE (priv->signatures), signature); } void seahorse_pgp_uid_remove_signature (SeahorsePgpUid *self, SeahorsePgpSignature *signature) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_if_fail (SEAHORSE_PGP_IS_UID (self)); g_return_if_fail (SEAHORSE_PGP_IS_SIGNATURE (signature)); for (unsigned i = 0; i < g_list_model_get_n_items (priv->signatures); i++) { g_autoptr(SeahorsePgpSignature) sig = NULL; sig = g_list_model_get_item (priv->signatures, i); if (signature == sig) { g_list_store_remove (G_LIST_STORE (priv->signatures), i); break; } } } SeahorseValidity seahorse_pgp_uid_get_validity (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_val_if_fail (SEAHORSE_PGP_IS_UID (self), SEAHORSE_VALIDITY_UNKNOWN); return priv->validity; } void seahorse_pgp_uid_set_validity (SeahorsePgpUid *self, SeahorseValidity validity) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_if_fail (SEAHORSE_PGP_IS_UID (self)); priv->validity = validity; g_object_notify (G_OBJECT (self), "validity"); } /** * seahorse_pgp_uid_get_name: * @self: A uid * * Returns: (transfer none): The name part of the UID */ const char * seahorse_pgp_uid_get_name (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_val_if_fail (SEAHORSE_PGP_IS_UID (self), NULL); if (!priv->name) priv->name = g_strdup (""); return priv->name; } void seahorse_pgp_uid_set_name (SeahorsePgpUid *self, const char *name) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); GObject *obj; g_return_if_fail (SEAHORSE_PGP_IS_UID (self)); g_free (priv->name); priv->name = convert_string (name); obj = G_OBJECT (self); g_object_freeze_notify (obj); if (!priv->realized) seahorse_pgp_uid_realize (self); g_object_notify (obj, "name"); g_object_thaw_notify (obj); } /** * seahorse_pgp_uid_get_email: * @self: A uid * * Returns: (transfer none): The email part of the UID (if empty, returns "") */ const char * seahorse_pgp_uid_get_email (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_val_if_fail (SEAHORSE_PGP_IS_UID (self), NULL); if (!priv->email) priv->email = g_strdup (""); return priv->email; } void seahorse_pgp_uid_set_email (SeahorsePgpUid *self, const char *email) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); GObject *obj = G_OBJECT (self); g_return_if_fail (SEAHORSE_PGP_IS_UID (self)); g_free (priv->email); priv->email = convert_string (email); g_object_freeze_notify (obj); if (!priv->realized) seahorse_pgp_uid_realize (self); g_object_notify (obj, "email"); g_object_thaw_notify (obj); } /** * seahorse_pgp_uid_get_comment: * @self: A uid * * Returns: (transfer none): The comment part of the UID (if empty, returns "") */ const char * seahorse_pgp_uid_get_comment (SeahorsePgpUid *self) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); g_return_val_if_fail (SEAHORSE_PGP_IS_UID (self), NULL); if (!priv->comment) priv->comment = g_strdup (""); return priv->comment; } void seahorse_pgp_uid_set_comment (SeahorsePgpUid *self, const char *comment) { SeahorsePgpUidPrivate *priv = seahorse_pgp_uid_get_instance_private (self); GObject *obj = G_OBJECT (self); g_return_if_fail (SEAHORSE_PGP_IS_UID (self)); g_free (priv->comment); priv->comment = convert_string (comment); g_object_freeze_notify (obj); if (!priv->realized) seahorse_pgp_uid_realize (self); g_object_notify (obj, "comment"); g_object_thaw_notify (obj); } /** * seahorse_pgp_uid_calc_label: * @name: * @email: (nullable): * @command: (nullable): * * Builds a PGP UID from the name (and if available) name and comment in the * form of "name (comment) " * * Returns: (transfer full): The PGP UID string */ char * seahorse_pgp_uid_calc_label (const char *name, const char *email, const char *comment) { GString *string; g_return_val_if_fail (name, NULL); string = g_string_new (""); g_string_append (string, name); if (email && email[0]) { g_string_append (string, " <"); g_string_append (string, email); g_string_append (string, ">"); } if (comment && comment[0]) { g_string_append (string, " ("); g_string_append (string, comment); g_string_append (string, ")"); } return g_string_free (string, FALSE); } char * seahorse_pgp_uid_calc_markup (const char *name, const char *email, const char *comment, unsigned int flags) { const char *format; gboolean strike = FALSE; gboolean grayed = FALSE; g_return_val_if_fail (name, NULL); if (flags & SEAHORSE_FLAG_EXPIRED || flags & SEAHORSE_FLAG_REVOKED || flags & SEAHORSE_FLAG_DISABLED) strike = TRUE; if (!(flags & SEAHORSE_FLAG_TRUSTED)) grayed = TRUE; if (strike && grayed) format = "%s%s%s%s%s%s"; else if (grayed) format = "%s%s%s%s%s%s"; else if (strike) format = "%s%s%s%s%s%s"; else format = "%s%s%s%s%s%s"; return g_markup_printf_escaped (format, name, email && email[0] ? " " : "", email && email[0] ? email : "", comment && comment[0] ? " '" : "", comment && comment[0] ? comment : "", comment && comment[0] ? "'" : ""); }