diff --git a/libpurple/purpletags.h b/libpurple/purpletags.h
--- a/libpurple/purpletags.h
+++ b/libpurple/purpletags.h
@@ -43,8 +43,11 @@
  * Tags is an object that can be used to keep track of arbitrary tags on
  * objects. Tags are simple strings that use the first ':' to delimit a value.
  * For example: `foo` is a tag with just a name and no value, but `foo:bar` is
- * a tag with a name and value. Please note this distinction when the API calls
- * for a name versus a tag which would be the name and the value.
+ * a tag with a name and value. Also, `foo:` is still considered a name and
+ * value, but the value is an empty string.
+ *
+ * Please note this distinction when the API calls for a name versus a tag
+ * which would be the name and the value.
  *
  * Since: 3.0
  */
@@ -64,6 +67,22 @@
 PurpleTags *purple_tags_new(void);
 
 /**
+ * purple_tags_exists:
+ * @tags: The instance.
+ * @tag: The tag data.
+ *
+ * Checks if @tag exists in @tags.
+ *
+ * This checks both the tag name and value.
+ *
+ * Returns: %TRUE if @tag is in @tags, otherwise %FALSE.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_tags_exists(PurpleTags *tags, const char *tag);
+
+/**
  * purple_tags_lookup:
  * @tags: The instance.
  * @name: The name of the tag to check if it exists.
@@ -236,6 +255,25 @@
 PURPLE_AVAILABLE_IN_3_0
 void purple_tag_parse(const char *tag, char **name, char **value);
 
+/**
+ * purple_tags_contains:
+ * @tags: The instance.
+ * @needle: The tags to find.
+ *
+ * Checks if all of the tags in @needle can be found in @tags.
+ *
+ * This checks tags, which means names and values, and not just tag names.
+ *
+ * For example, if @tags contains `foo:bar, baz`, and @needle contains
+ * `foo:bar, baz:1` this will return %FALSE because `baz:1` is not in @tags.
+ *
+ * Returns: %TRUE if all tags in @needle can be found in @tags.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_tags_contains(PurpleTags *tags, PurpleTags *needle);
+
 G_END_DECLS
 
 #endif /* PURPLE_TAGS_H */
# HG changeset patch
# Node ID e8703f13a9aa
# Parent  2f998aa86e1a
diff --git a/libpurple/purpletags.c b/libpurple/purpletags.c
--- a/libpurple/purpletags.c
+++ b/libpurple/purpletags.c
@@ -169,6 +169,22 @@
 	return g_object_new(PURPLE_TYPE_TAGS, NULL);
 }
 
+gboolean
+purple_tags_exists(PurpleTags *tags, const char *tag) {
+	g_return_val_if_fail(PURPLE_IS_TAGS(tags), FALSE);
+	g_return_val_if_fail(!purple_strempty(tag), FALSE);
+
+	for(GList *l = tags->tags; l != NULL; l = l->next) {
+		const char *existing = l->data;
+
+		if(purple_strequal(existing, tag)) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 const gchar *
 purple_tags_lookup(PurpleTags *tags, const gchar *name, gboolean *found) {
 	size_t name_len = 0;
@@ -382,3 +398,19 @@
 		}
 	}
 }
+
+gboolean
+purple_tags_contains(PurpleTags *tags, PurpleTags *needle) {
+	g_return_val_if_fail(PURPLE_IS_TAGS(tags), FALSE);
+	g_return_val_if_fail(PURPLE_IS_TAGS(needle), FALSE);
+
+	for(GList *tag = needle->tags; tag != NULL; tag = tag->next) {
+		const char *needle_tag = tag->data;
+
+		if(!purple_tags_exists(tags, needle_tag)) {
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
diff --git a/libpurple/tests/test_tags.c b/libpurple/tests/test_tags.c
--- a/libpurple/tests/test_tags.c
+++ b/libpurple/tests/test_tags.c
@@ -44,6 +44,25 @@
  * Tests
  *****************************************************************************/
 static void
+test_purple_tags_exists(void) {
+	PurpleTags *tags = NULL;
+
+	tags = purple_tags_new();
+
+	purple_tags_add(tags, "foo");
+	purple_tags_add(tags, "bar:1");
+	purple_tags_add(tags, "baz");
+
+	g_assert_true(purple_tags_exists(tags, "foo"));
+	g_assert_true(purple_tags_exists(tags, "bar:1"));
+
+	g_assert_false(purple_tags_exists(tags, "baz:"));
+	g_assert_false(purple_tags_exists(tags, "qux"));
+
+	g_assert_finalize_object(tags);
+}
+
+static void
 test_purple_tags_lookup_exists(void) {
 	PurpleTags *tags = NULL;
 	gboolean found = FALSE;
@@ -560,6 +579,65 @@
 	purple_tag_parse("", NULL, NULL);
 }
 
+static void
+test_purple_tag_contains_full(void) {
+	PurpleTags *tags = NULL;
+	PurpleTags *needle = NULL;
+
+	tags = purple_tags_new();
+	purple_tags_add(tags, "foo");
+	purple_tags_add(tags, "bar:");
+	purple_tags_add(tags, "baz:1");
+
+	needle = purple_tags_new();
+	purple_tags_add(needle, "foo");
+	purple_tags_add(needle, "bar:");
+	purple_tags_add(needle, "baz:1");
+
+	g_assert_true(purple_tags_contains(tags, needle));
+
+	g_assert_finalize_object(tags);
+	g_assert_finalize_object(needle);
+}
+
+static void
+test_purple_tag_contains_partial(void) {
+	PurpleTags *tags = NULL;
+	PurpleTags *needle = NULL;
+
+	tags = purple_tags_new();
+	purple_tags_add(tags, "foo");
+	purple_tags_add(tags, "bar:");
+	purple_tags_add(tags, "baz:1");
+
+	needle = purple_tags_new();
+	purple_tags_add(needle, "foo");
+	purple_tags_add(needle, "baz:1");
+
+	g_assert_true(purple_tags_contains(tags, needle));
+
+	g_assert_finalize_object(tags);
+	g_assert_finalize_object(needle);
+}
+
+static void
+test_purple_tag_contains_none(void) {
+	PurpleTags *tags = NULL;
+	PurpleTags *needle = NULL;
+
+	tags = purple_tags_new();
+	purple_tags_add(tags, "foo");
+	purple_tags_add(tags, "bar:1");
+
+	needle = purple_tags_new();
+	purple_tags_add(needle, "baz:qux");
+
+	g_assert_false(purple_tags_contains(tags, needle));
+
+	g_assert_finalize_object(tags);
+	g_assert_finalize_object(needle);
+}
+
 /******************************************************************************
  * Public API
  *****************************************************************************/
@@ -567,6 +645,8 @@
 main(gint argc, gchar **argv) {
 	g_test_init(&argc, &argv, NULL);
 
+	g_test_add_func("/tags/exists", test_purple_tags_exists);
+
 	g_test_add_func("/tags/lookup-exists", test_purple_tags_lookup_exists);
 	g_test_add_func("/tags/lookup-non-existent",
 	                test_purple_tags_lookup_non_existent);
@@ -616,5 +696,9 @@
 
 	g_test_add_func("/tag/parse", test_purple_tag_parse);
 
+	g_test_add_func("/tag/contains/full", test_purple_tag_contains_full);
+	g_test_add_func("/tag/contains/partial", test_purple_tag_contains_partial);
+	g_test_add_func("/tag/contains/none", test_purple_tag_contains_none);
+
 	return g_test_run();
 }
