pidgin/pidgin

Remove the Sametime protocol plugin

18 months ago, Gary Kramlich
8bdccbde0536
Parents 3bc50beccf03
Children 168bba42c41e
Remove the Sametime protocol plugin

We haven't been able to test this in forever, and as such, it hasn't received
any real maintenance. So rather than continue this cycle we're going to cut our
loses.

Testing Done:
Compiled and greped everything. I've left the man pages for now as they need their own overhaul.

Reviewed at https://reviews.imfreedom.org/r/2022/
--- a/libpurple/plugins/psychic/psychic.c Sat Nov 12 15:37:03 2022 -0600
+++ b/libpurple/plugins/psychic/psychic.c Sat Nov 12 15:38:17 2022 -0600
@@ -27,8 +27,7 @@
#define PLUGIN_CATEGORY N_("Utility")
#define PLUGIN_SUMMARY N_("Psychic mode for incoming conversation")
#define PLUGIN_DESC N_("Causes conversation windows to appear as other" \
- " users begin to message you. This works for" \
- " AIM, ICQ, XMPP, and Sametime")
+ " users begin to message you. This works for XMPP")
#define PLUGIN_AUTHORS { "Christopher O'Brien <siege@preoccupied.net>", NULL }
--- a/libpurple/protocols/meson.build Sat Nov 12 15:37:03 2022 -0600
+++ b/libpurple/protocols/meson.build Sat Nov 12 15:38:17 2022 -0600
@@ -5,4 +5,3 @@
subdir('irc')
subdir('ircv3')
subdir('jabber')
-subdir('sametime')
--- a/libpurple/protocols/sametime/im_mime.c Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,355 +0,0 @@
-/*
- * purple - Sametime Protocol Plugin
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- */
-
-#include <glib.h>
-#include <gmime/gmime.h>
-
-#include <purple.h>
-
-/* plugin includes */
-#include "sametime.h"
-#include "im_mime.h"
-
-
-/** generate "cid:908@20582notesbuddy" from "<908@20582notesbuddy>" */
-static char *
-make_cid(const char *cid)
-{
- g_return_val_if_fail(cid != NULL, NULL);
-
- return g_strdup_printf("cid:%s", cid);
-}
-
-
-/* Create a MIME parser from some input data. */
-static GMimeParser *
-create_parser(const char *data)
-{
- GMimeStream *stream;
- GMimeFilter *filter;
- GMimeStream *filtered_stream;
- GMimeParser *parser;
-
- stream = g_mime_stream_mem_new_with_buffer(data, strlen(data));
-
- filtered_stream = g_mime_stream_filter_new(stream);
- g_object_unref(G_OBJECT(stream));
-
- /* Add a dos2unix filter so in-message newlines are simplified. */
- filter = g_mime_filter_dos2unix_new(FALSE);
- g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream), filter);
- g_object_unref(G_OBJECT(filter));
-
- parser = g_mime_parser_new_with_stream(filtered_stream);
- g_object_unref(G_OBJECT(filtered_stream));
-
- return parser;
-}
-
-
-/* Replace each IMG tag's SRC attribute with an ID attribute. This
- actually modifies the contents of str */
-static void
-replace_img_src_by_id(GString *str, GHashTable *img_by_cid)
-{
- GData *attribs;
- char *start, *end;
- char *tmp = str->str;
-
- while (*tmp &&
- purple_markup_find_tag("img", tmp,
- (const char **) &start, (const char **) &end,
- &attribs)) {
-
- char *alt, *align, *border, *src;
- guint img = 0;
-
- alt = g_datalist_get_data(&attribs, "alt");
- align = g_datalist_get_data(&attribs, "align");
- border = g_datalist_get_data(&attribs, "border");
- src = g_datalist_get_data(&attribs, "src");
-
- if (src) {
- img = GPOINTER_TO_UINT(g_hash_table_lookup(img_by_cid, src));
- }
-
- if (img) {
- GString *atstr;
- gsize len = (end - start);
- gsize mov;
-
- atstr = g_string_new("");
- if (alt) {
- g_string_append_printf(atstr, " alt=\"%s\"", alt);
- }
- if (align) {
- g_string_append_printf(atstr, " align=\"%s\"", align);
- }
- if (border) {
- g_string_append_printf(atstr, " border=\"%s\"", border);
- }
-
- mov = g_snprintf(start, len,
- "<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL
- "%u\"%s", img, atstr->str);
- while (mov < len) start[mov++] = ' ';
-
- g_string_free(atstr, TRUE);
- }
-
- g_datalist_clear(&attribs);
- tmp = end + 1;
- }
-}
-
-gchar *
-im_mime_parse(const char *data)
-{
- GHashTable *img_by_cid;
-
- GString *str;
-
- GMimeParser *parser;
- GMimeObject *doc;
- int i, count;
-
- img_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-
- /* don't want the contained string to ever be NULL */
- str = g_string_new("");
-
- parser = create_parser(data);
- doc = g_mime_parser_construct_part(parser, NULL);
- if (!GMIME_IS_MULTIPART(doc)) {
- g_object_unref(G_OBJECT(doc));
- g_object_unref(G_OBJECT(parser));
- return g_string_free(str, FALSE);
- }
-
- /* handle all the MIME parts */
- count = g_mime_multipart_get_count(GMIME_MULTIPART(doc));
- for (i = 0; i < count; i++) {
- GMimeObject *obj = g_mime_multipart_get_part(GMIME_MULTIPART(doc), i);
- GMimeContentType *type = g_mime_object_get_content_type(obj);
-
- if (!type) {
- ; /* feh */
-
- } else if (g_mime_content_type_is_type(type, "image", "*")) {
- /* put images into the image store */
-
- GMimePart *part = GMIME_PART(obj);
- GMimeDataWrapper *wrapper;
- GMimeStream *stream;
- GByteArray *bytearray;
- GBytes *data;
- char *cid;
- PurpleImage *image;
- guint imgid;
-
- /* obtain and unencode the data */
- bytearray = g_byte_array_new();
- stream = g_mime_stream_mem_new_with_byte_array(bytearray);
- g_mime_stream_mem_set_owner(GMIME_STREAM_MEM(stream), FALSE);
- wrapper = g_mime_part_get_content(part);
- g_mime_data_wrapper_write_to_stream(wrapper, stream);
- data = g_byte_array_free_to_bytes(bytearray);
- g_clear_object(&stream);
-
- /* look up the content id */
- cid = make_cid(g_mime_part_get_content_id(part));
-
- /* add image to the purple image store */
- image = purple_image_new_from_bytes(data);
- purple_image_set_friendly_filename(image, cid);
- g_bytes_unref(data);
-
- /* map the cid to the image store identifier */
- imgid = purple_image_store_add(image);
- g_hash_table_insert(img_by_cid, cid, GUINT_TO_POINTER(imgid));
-
- } else if (GMIME_IS_TEXT_PART(obj)) {
- /* concatenate all the text parts together */
- char *data;
-
- data = g_mime_text_part_get_text(GMIME_TEXT_PART(obj));
- g_string_append(str, data);
- g_free(data);
- }
- }
-
- g_object_unref(G_OBJECT(doc));
- g_object_unref(G_OBJECT(parser));
-
- replace_img_src_by_id(str, img_by_cid);
-
- /* clean up the cid table */
- g_hash_table_destroy(img_by_cid);
-
- return g_string_free(str, FALSE);
-}
-
-
-/** generates a random-ish content id string */
-static char *
-im_mime_content_id(void)
-{
- gint id = g_random_int();
- return g_strdup_printf("%03x@%05xmeanwhile", (id & 0xfff00000) >> 20,
- id & 0xfffff);
-}
-
-
-/** generates a random-ish boundary value */
-static char *
-im_mime_boundary(void)
-{
- gint id = g_random_int();
- return g_strdup_printf("related_MW%03x_%04x", (id & 0xfff0000) >> 16,
- id & 0xffff);
-}
-
-/** create MIME image from purple image */
-static GMimePart *
-im_mime_img_to_part(PurpleImage *img)
-{
- const gchar *mimetype;
- GMimePart *part;
- GByteArray *data;
- GMimeStream *stream;
- GMimeDataWrapper *wrapper;
-
- mimetype = purple_image_get_mimetype(img);
- if (mimetype && g_str_has_prefix(mimetype, "image/")) {
- mimetype += sizeof("image/") - 1;
- }
-
- part = g_mime_part_new_with_type("image", mimetype);
- g_mime_object_set_disposition(GMIME_OBJECT(part),
- GMIME_DISPOSITION_ATTACHMENT);
- g_mime_part_set_content_encoding(part, GMIME_CONTENT_ENCODING_BASE64);
- g_mime_part_set_filename(part, purple_image_get_friendly_filename(img));
-
- data = g_bytes_unref_to_array(purple_image_get_contents(img));
- stream = g_mime_stream_mem_new_with_byte_array(data);
-
- wrapper = g_mime_data_wrapper_new_with_stream(stream,
- GMIME_CONTENT_ENCODING_BINARY);
- g_object_unref(G_OBJECT(stream));
-
- g_mime_part_set_content(part, wrapper);
- g_object_unref(G_OBJECT(wrapper));
-
- return part;
-}
-
-
-gchar *
-im_mime_generate(const char *message)
-{
- GString *str;
- GMimeMultipart *doc;
- GMimeFormatOptions *opts;
- GMimeTextPart *text;
-
- GData *attr;
- char *tmp, *start, *end;
-
- str = g_string_new(NULL);
-
- doc = g_mime_multipart_new_with_subtype("related");
-
- g_mime_object_set_header(GMIME_OBJECT(doc), "Mime-Version", "1.0", NULL);
- g_mime_object_set_disposition(GMIME_OBJECT(doc), GMIME_DISPOSITION_INLINE);
-
- tmp = im_mime_boundary();
- g_mime_multipart_set_boundary(doc, tmp);
- g_free(tmp);
-
- tmp = (char *)message;
- while (*tmp &&
- purple_markup_find_tag("img", tmp, (const char **) &start,
- (const char **) &end, &attr)) {
- gchar *uri;
- PurpleImage *img = NULL;
-
- gsize len = (start - tmp);
-
- /* append the in-between-tags text */
- if (len) {
- g_string_append_len(str, tmp, len);
- }
-
- uri = g_datalist_get_data(&attr, "src");
- if (uri) {
- img = purple_image_store_get_from_uri(uri);
- }
-
- if (img) {
- GMimePart *part;
- char *cid;
-
- cid = im_mime_content_id();
- part = im_mime_img_to_part(img);
- g_mime_part_set_content_id(part, cid);
- g_mime_multipart_add(doc, GMIME_OBJECT(part));
- g_object_unref(G_OBJECT(part));
-
- /* append the modified tag */
- g_string_append_printf(str, "<img src=\"cid:%s\">", cid);
- g_free(cid);
-
- } else {
- /* append the literal image tag, since we couldn't find
- a relative PurpleImage object */
- gsize len = (end - start) + 1;
- g_string_append_len(str, start, len);
- }
-
- g_datalist_clear(&attr);
- tmp = end + 1;
- }
-
- /* append left-overs */
- g_string_append(str, tmp);
-
- /* add the text/html part */
- text = g_mime_text_part_new_with_subtype("html");
- g_mime_object_set_disposition(GMIME_OBJECT(text),
- GMIME_DISPOSITION_INLINE);
-
- tmp = purple_utf8_ncr_encode(str->str);
- g_mime_text_part_set_text(text, tmp);
- g_free(tmp);
-
- g_mime_multipart_insert(doc, 0, GMIME_OBJECT(text));
- g_object_unref(G_OBJECT(text));
- g_string_free(str, TRUE);
-
- opts = g_mime_format_options_new();
- g_mime_format_options_set_newline_format(opts, GMIME_NEWLINE_FORMAT_DOS);
- tmp = g_mime_object_to_string(GMIME_OBJECT(doc), opts);
- g_mime_format_options_free(opts);
- g_object_unref(G_OBJECT(doc));
- return tmp;
-}
--- a/libpurple/protocols/sametime/im_mime.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * purple - Sametime Protocol Plugin
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- */
-
-#ifndef PURPLE_SAMETIME_IM_MIME_H
-#define PURPLE_SAMETIME_IM_MIME_H
-
-#include <glib.h>
-
-/**
- * Parse a multi-part MIME document into purple format with embedded images.
- */
-gchar *im_mime_parse(const char *data);
-
-/**
- * Turn an IM with embedded images into a multi-part MIME document.
- */
-gchar *im_mime_generate(const char *message);
-
-#endif /* PURPLE_SAMETIME_IM_MIME_H */
--- a/libpurple/protocols/sametime/meson.build Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-SAMETIME_SOURCES = [
- 'sametime.c',
- 'sametime.h',
- 'im_mime.c',
- 'im_mime.h',
-]
-
-if DYNAMIC_SAMETIME
- sametime_resources = gnome.compile_resources('sametimeresource',
- 'resources/sametime.gresource.xml',
- source_dir : 'resources',
- c_name : 'sametime')
- SAMETIME_SOURCES += sametime_resources
-
- sametime_prpl = shared_library('sametime', SAMETIME_SOURCES,
- c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-Sametime"'],
- dependencies : [meanwhile, gmime, libpurple_dep, glib],
- install : true, install_dir : PURPLE_PLUGINDIR)
-
- devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir())
-
- subdir('tests')
-endif
Binary file libpurple/protocols/sametime/resources/icons/16x16/apps/im-meanwhile.png has changed
--- a/libpurple/protocols/sametime/resources/icons/16x16/apps/scalable/im-meanwhile.svg Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="16px"
- height="16px"
- id="svg4239"
- sodipodi:version="0.32"
- inkscape:version="0.46"
- sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/protocols/16/scalable"
- sodipodi:docname="meanwhile.svg"
- inkscape:export-filename="/home/hbons/Bureaublad/meanwhile.png"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- inkscape:output_extension="org.inkscape.output.svg.inkscape">
- <defs
- id="defs4241">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient8675">
- <stop
- style="stop-color:#fcaf3e;stop-opacity:1;"
- offset="0"
- id="stop8677" />
- <stop
- style="stop-color:#f57900;stop-opacity:1"
- offset="1"
- id="stop8679" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient8667">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop8669" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop8671" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient8650">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop8652" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop8654" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient8642">
- <stop
- style="stop-color:#ce5c00;stop-opacity:1;"
- offset="0"
- id="stop8644" />
- <stop
- style="stop-color:#9f3400;stop-opacity:1"
- offset="1"
- id="stop8646" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient8634">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop8636" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop8638" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient8634"
- id="linearGradient8640"
- x1="9.2996664"
- y1="-1.1926658"
- x2="22.00433"
- y2="12.53125"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient8642"
- id="linearGradient8648"
- x1="8.701582"
- y1="8.7882452"
- x2="14.751598"
- y2="21.009844"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient8650"
- id="linearGradient8656"
- x1="5.3650107"
- y1="-2.7280362"
- x2="16.534615"
- y2="7.1899891"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient8642"
- id="linearGradient8661"
- gradientUnits="userSpaceOnUse"
- x1="8.701582"
- y1="8.7882452"
- x2="14.751598"
- y2="21.009844" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient8667"
- id="linearGradient8673"
- x1="2.0414412"
- y1="-0.75497365"
- x2="26.409824"
- y2="23.59227"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient8675"
- id="linearGradient8681"
- x1="13.609824"
- y1="12.881368"
- x2="13.609824"
- y2="19.507627"
- gradientUnits="userSpaceOnUse" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="27.3287"
- inkscape:cx="20.064702"
- inkscape:cy="8.5317934"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:grid-bbox="true"
- inkscape:document-units="px"
- inkscape:window-width="1440"
- inkscape:window-height="847"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:snap-bbox="true"
- inkscape:snap-nodes="false"
- objecttolerance="10"
- gridtolerance="10">
- <inkscape:grid
- type="xygrid"
- id="grid7858"
- visible="true"
- enabled="true" />
- </sodipodi:namedview>
- <metadata
- id="metadata4244">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- inkscape:label="Layer 1"
- inkscape:groupmode="layer">
- <path
- sodipodi:type="arc"
- style="fill:#fcaf3e;fill-opacity:1;stroke:#ce5c00;stroke-width:2.02997327000000016;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4302"
- sodipodi:cx="10.555883"
- sodipodi:cy="4.0385542"
- sodipodi:rx="5.074944"
- sodipodi:ry="5.074944"
- d="M 15.630827,4.0385542 A 5.074944,5.074944 0 1 1 5.4809394,4.0385542 A 5.074944,5.074944 0 1 1 15.630827,4.0385542 z"
- transform="matrix(0.4926164,0,0,0.4926165,2.7999985,1.0105418)" />
- <path
- sodipodi:type="arc"
- style="opacity:1;fill:none;fill-opacity:1;stroke:url(#linearGradient8656);stroke-width:3.38329315000000008;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4273"
- sodipodi:cx="10.555883"
- sodipodi:cy="4.0385542"
- sodipodi:rx="5.074944"
- sodipodi:ry="5.074944"
- d="M 15.630827,4.0385542 A 5.074944,5.074944 0 1 1 5.4809394,4.0385542 A 5.074944,5.074944 0 1 1 15.630827,4.0385542 z"
- transform="matrix(0.2955698,0,0,0.2955699,4.8799997,1.8063249)" />
- <path
- style="fill:url(#linearGradient8681);fill-opacity:1;stroke:url(#linearGradient8648);stroke-width:1.38365983999999997;stroke-miterlimit:4;stroke-opacity:1"
- d="M 18.764798,2.8497364 C 18.764798,6.4835545 17.015921,10.340055 12.458352,10.340055 C 7.9302336,10.340055 6.1696302,5.9347403 6.1696302,3.0077289 L 3.5191672,5.0743501 C 3.5191672,11.592509 7.7563904,12.376951 7.7563904,13.61498 C 7.7563904,14.942901 3.5055266,16.358274 3.5055266,21.425015 L 9.1348464,21.369834 C 9.1348464,19.244403 10.433516,15.786914 12.492742,15.786914 C 14.516622,15.786914 15.962656,19.213604 15.962656,21.335386 L 21.491428,21.351408 C 21.491428,15.947962 17.306969,15.024839 17.306969,13.58672 C 17.306969,12.395751 21.452377,11.632096 21.452377,5.0650054 L 18.764798,2.8497364 z"
- id="rect2213"
- sodipodi:nodetypes="czccccczccccc"
- transform="matrix(0.725563,0,0,0.719891,-1.060965,0.139182)" />
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="-1.3863388"
- inkscape:original="M 18.75 2.84375 C 18.750001 6.477568 17.026319 10.34375 12.46875 10.34375 C 7.9406315 10.34375 6.15625 5.9270114 6.15625 3 L 3.53125 5.0625 C 3.53125 11.580659 7.75 12.386971 7.75 13.625 C 7.7499998 14.952921 3.5 16.370759 3.5 21.4375 L 9.125 21.375 C 9.1249997 19.249568 10.440774 15.78125 12.5 15.78125 C 14.52388 15.78125 15.96875 19.221968 15.96875 21.34375 L 21.5 21.34375 C 21.5 15.940304 17.3125 15.031869 17.3125 13.59375 C 17.3125 12.402781 21.4375 11.629591 21.4375 5.0625 L 18.75 2.84375 z "
- style="fill:none;fill-opacity:1;stroke:url(#linearGradient8673);stroke-width:1.38365983999999997;stroke-miterlimit:4;stroke-opacity:1"
- id="path8663"
- d="M 19.8125,5.5 C 19.553993,6.6500796 19.130624,7.7658345 18.4375,8.75 C 17.214147,10.487035 15.139545,11.71875 12.46875,11.71875 C 9.7539446,11.71875 7.6786908,10.319904 6.46875,8.5625 C 5.8161471,7.614613 5.3955271,6.5877094 5.125,5.5625 L 5,5.65625 C 5.1326144,8.1177354 5.8516259,9.5128134 6.65625,10.40625 C 7.0969248,10.895565 7.5574921,11.26294 8,11.65625 C 8.2212539,11.852905 8.4668482,12.050594 8.6875,12.34375 C 8.9081518,12.636906 9.125,13.076568 9.125,13.625 C 9.1249998,14.696474 8.5196644,15.229886 8.0625,15.6875 C 7.6053356,16.145114 7.1243156,16.554217 6.65625,17.0625 C 5.973649,17.803752 5.4431444,18.79741 5.15625,20.03125 L 7.90625,20 C 8.0647552,19.034828 8.3367207,18.075404 8.8125,17.15625 C 9.5156693,15.797803 10.692058,14.40625 12.5,14.40625 C 14.29756,14.40625 15.483981,15.747434 16.21875,17.09375 C 16.715676,18.004266 16.982752,18.998 17.15625,19.96875 L 19.84375,19.96875 C 19.573522,18.630932 19.078136,17.632125 18.40625,16.9375 C 17.956341,16.472365 17.46334,16.095102 17,15.65625 C 16.53666,15.217398 15.9375,14.626068 15.9375,13.59375 C 15.9375,13.044739 16.156655,12.598898 16.375,12.3125 C 16.593345,12.026102 16.816217,11.846739 17.03125,11.65625 C 17.461317,11.275273 17.91606,10.919853 18.34375,10.4375 C 19.12911,9.5517623 19.845064,8.1414304 19.96875,5.625 L 19.8125,5.5 z"
- transform="matrix(0.725563,0,0,0.719891,-1.060965,0.139182)" />
- </g>
-</svg>
Binary file libpurple/protocols/sametime/resources/icons/22x22/apps/im-meanwhile.png has changed
--- a/libpurple/protocols/sametime/resources/icons/22x22/apps/scalable/im-meanwhile.svg Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,172 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="24"
- height="24"
- id="svg2"
- sodipodi:version="0.32"
- inkscape:version="0.46"
- version="1.0"
- sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols"
- sodipodi:docname="meanwhile.svg"
- inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/meanwhile.png"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- inkscape:output_extension="org.inkscape.output.svg.inkscape">
- <defs
- id="defs4">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient2230">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop2232" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop2234" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient2222">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop2224" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop2226" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient3150">
- <stop
- style="stop-color:#2e3436;stop-opacity:1;"
- offset="0"
- id="stop3152" />
- <stop
- style="stop-color:#2e3436;stop-opacity:0;"
- offset="1"
- id="stop3154" />
- </linearGradient>
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3150"
- id="radialGradient4330"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
- cx="10.748654"
- cy="10.457643"
- fx="10.748654"
- fy="10.457643"
- r="6.6449099" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2222"
- id="linearGradient2228"
- x1="13.090998"
- y1="0.78084093"
- x2="13.090998"
- y2="17.595036"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2230"
- id="linearGradient2236"
- x1="10.063863"
- y1="-0.14116688"
- x2="10.063863"
- y2="7.1152339"
- gradientUnits="userSpaceOnUse" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="13.788818"
- inkscape:cx="4.860805"
- inkscape:cy="9.6839037"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- fill="#f57900"
- showguides="true"
- inkscape:guide-bbox="true"
- inkscape:window-width="1268"
- inkscape:window-height="971"
- inkscape:window-x="6"
- inkscape:window-y="21" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <path
- sodipodi:type="arc"
- style="opacity:0.3;fill:url(#radialGradient4330);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path3140"
- sodipodi:cx="10.748654"
- sodipodi:cy="10.457643"
- sodipodi:rx="6.6449099"
- sodipodi:ry="2.3675451"
- d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
- transform="matrix(1.805894,0,0,1.512171,-7.410928,4.606121)" />
- <path
- sodipodi:type="arc"
- style="opacity:1;fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:1.45242631;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4302"
- sodipodi:cx="10.555883"
- sodipodi:cy="4.0385542"
- sodipodi:rx="5.074944"
- sodipodi:ry="5.074944"
- d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
- transform="matrix(0.688186,0,0,0.688819,4.743084,1.209666)" />
- <path
- sodipodi:type="arc"
- style="opacity:0.45;fill:url(#linearGradient2236);fill-opacity:1.0;stroke:#ffffff;stroke-width:2.02834463;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4273"
- sodipodi:cx="10.555883"
- sodipodi:cy="4.0385542"
- sodipodi:rx="5.074944"
- sodipodi:ry="5.074944"
- d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
- transform="matrix(0.492994,0,0,0.493032,6.809086,2.006754)" />
- <path
- style="fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:1.00000119;stroke-miterlimit:4;stroke-opacity:1"
- d="M 17.512653,3.583459 C 17.512653,5.7137825 16.562033,9.5267857 12.004464,9.5267857 C 7.4763459,9.5267857 6.5140012,5.7551862 6.5140012,3.5976845 L 2.5044094,4.4125109 C 2.5044094,11.110279 9.0401786,13.178571 9.0401786,13.178571 C 9.0401786,13.178571 4.4464285,16.409103 4.4464285,21.475844 L 9.5714286,21.522322 C 9.5714286,21.522322 10.279685,16.46875 12.089286,16.46875 C 13.943529,16.46875 15.401786,21.508929 15.401786,21.508929 L 20.502628,21.524951 C 20.502628,16.121505 15.254464,13.120535 15.254464,13.120535 C 15.254464,13.120535 21.508928,11.277721 21.508928,4.4267748 L 17.512653,3.583459 z "
- id="rect2213"
- sodipodi:nodetypes="czccccczccccc" />
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="-0.9525249"
- inkscape:original="M 6.5 3.59375 L 2.5 4.40625 C 2.5000001 11.104018 9.03125 13.1875 9.03125 13.1875 C 9.0312503 13.1875 4.4375 16.402009 4.4375 21.46875 L 9.5625 21.53125 C 9.5625003 21.53125 10.284149 16.46875 12.09375 16.46875 C 13.947993 16.46875 15.40625 21.5 15.40625 21.5 L 20.5 21.53125 C 20.5 16.127803 15.25 13.125 15.25 13.125 C 15.25 13.125 21.5 11.288446 21.5 4.4375 L 17.5 3.59375 C 17.5 5.7240736 16.557569 9.53125 12 9.53125 C 7.4718818 9.5312498 6.5 5.7512517 6.5 3.59375 z "
- xlink:href="#rect2213"
- style="fill:url(#linearGradient2228);fill-opacity:1.0;stroke:#ffffff;stroke-width:1.00000119;stroke-miterlimit:4;stroke-opacity:1;opacity:0.45"
- id="path2220"
- inkscape:href="#rect2213"
- d="M 5.6875,4.75 L 3.53125,5.1875 C 3.766386,7.7468676 5.0307437,9.4444151 6.375,10.5625 C 7.8598463,11.797521 9.3125,12.28125 9.3125,12.28125 C 9.6563341,12.393071 9.9072283,12.689904 9.9602145,13.047561 C 10.013201,13.405218 9.8591447,13.762043 9.5625,13.96875 C 9.5625,13.96875 6.1077373,16.59996 5.5625,20.53125 L 8.8125,20.59375 C 8.8948827,20.11702 8.9443109,19.67024 9.25,18.71875 C 9.4690397,18.036965 9.7468494,17.32194 10.15625,16.71875 C 10.565651,16.11556 11.197223,15.53125 12.09375,15.53125 C 12.98807,15.53125 13.639348,16.079917 14.125,16.65625 C 14.610652,17.232583 14.993863,17.890429 15.3125,18.5625 C 15.787201,19.563742 15.924647,20.125238 16.0625,20.5625 L 19.34375,20.59375 C 18.746486,16.428966 14.78125,13.9375 14.78125,13.9375 C 14.463324,13.745958 14.285599,13.387197 14.325851,13.018218 C 14.366103,12.649239 14.617002,12.337237 14.96875,12.21875 C 14.96875,12.21875 16.343303,11.809333 17.75,10.625 C 19.032471,9.5452564 20.256269,7.8458143 20.46875,5.1875 L 18.3125,4.75 C 18.187549,5.7582509 17.936893,6.8360203 17.25,7.875 C 16.2955,9.3187557 14.544016,10.46875 12,10.46875 C 9.4708902,10.46875 7.7086662,9.3456824 6.75,7.90625 C 6.0601086,6.8703815 5.8152026,5.7643315 5.6875,4.75 z " />
- </g>
-</svg>
Binary file libpurple/protocols/sametime/resources/icons/48x48/apps/im-meanwhile.png has changed
--- a/libpurple/protocols/sametime/resources/icons/scalable/apps/im-meanwhile.svg Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="48"
- height="48"
- id="svg2"
- sodipodi:version="0.32"
- inkscape:version="0.46"
- version="1.0"
- sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/protocols/48"
- sodipodi:docname="meanwhile.svg"
- inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/protocols/48/meanwhile.png"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- inkscape:output_extension="org.inkscape.output.svg.inkscape">
- <defs
- id="defs4">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient2195">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop2197" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop2199" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient2230">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop2232" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop2234" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- id="linearGradient3150">
- <stop
- style="stop-color:#2e3436;stop-opacity:1;"
- offset="0"
- id="stop3152" />
- <stop
- style="stop-color:#2e3436;stop-opacity:0;"
- offset="1"
- id="stop3154" />
- </linearGradient>
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3150"
- id="radialGradient4330"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
- cx="10.748654"
- cy="10.457643"
- fx="10.748654"
- fy="10.457643"
- r="6.6449099" />
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2230"
- id="linearGradient2236"
- x1="10.177145"
- y1="-1.3535745"
- x2="10.177145"
- y2="8.1371298"
- gradientUnits="userSpaceOnUse" />
- <radialGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2195"
- id="radialGradient2211"
- cx="30.5625"
- cy="23.172834"
- fx="30.5625"
- fy="23.172834"
- r="23.5"
- gradientTransform="matrix(1.330958,-1.200379e-32,1.144509e-32,1.269011,-10.11491,-9.053927)"
- gradientUnits="userSpaceOnUse" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="16.976026"
- inkscape:cx="38.972041"
- inkscape:cy="25.781032"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- fill="#f57900"
- showguides="true"
- inkscape:guide-bbox="true"
- inkscape:window-width="1268"
- inkscape:window-height="971"
- inkscape:window-x="6"
- inkscape:window-y="21" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <path
- sodipodi:type="arc"
- style="opacity:0.3;fill:url(#radialGradient4330);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path3140"
- sodipodi:cx="10.748654"
- sodipodi:cy="10.457643"
- sodipodi:rx="6.6449099"
- sodipodi:ry="2.3675451"
- d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
- transform="matrix(3.583369,0,0,3.000545,-14.70523,9.779617)" />
- <path
- sodipodi:type="arc"
- style="opacity:1;fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:0.67679459;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4302"
- sodipodi:cx="10.555883"
- sodipodi:cy="4.0385542"
- sodipodi:rx="5.074944"
- sodipodi:ry="5.074944"
- d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
- transform="matrix(1.477556,0,0,1.477551,8.403577,4.033323)" />
- <path
- sodipodi:type="arc"
- style="opacity:0.6;fill:url(#linearGradient2236);fill-opacity:1;stroke:#ffffff;stroke-width:0.78084576;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path4273"
- sodipodi:cx="10.555883"
- sodipodi:cy="4.0385542"
- sodipodi:rx="5.074944"
- sodipodi:ry="5.074944"
- d="M 15.630827 4.0385542 A 5.074944 5.074944 0 1 1 5.4809394,4.0385542 A 5.074944 5.074944 0 1 1 15.630827 4.0385542 z"
- transform="matrix(1.280662,0,0,1.280662,10.48149,4.827989)" />
- <path
- style="fill:#f57900;fill-opacity:1;stroke:#ce5c00;stroke-width:1.00000119;stroke-miterlimit:4;stroke-opacity:1"
- d="M 34.49131,8.6205284 C 34.49131,13.938683 32.849731,20.518688 23.898198,20.518688 C 15.00451,20.518688 13.526711,13.679495 13.526711,8.6600767 L 5.4949228,9.8762415 C 5.4949228,23.237275 17.664236,26.553794 17.664236,26.553794 C 17.664236,26.553794 9.1712538,34.298165 9.1712538,44.405547 L 19.237281,44.498262 C 19.237281,44.498262 21.456328,35.564348 25.010569,35.564348 C 28.652494,35.564348 30.6887,44.471546 30.6887,44.471546 L 40.362294,44.503507 C 40.362294,33.010164 30.381957,26.578174 30.381957,26.578174 C 30.381957,26.578174 42.614757,23.170121 42.614757,9.5035195 L 34.49131,8.6205284 z "
- id="rect2213"
- sodipodi:nodetypes="czccccczccccc" />
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="-0.9525249"
- inkscape:original="M 34.5 8.625 C 34.5 13.943155 32.857783 20.53125 23.90625 20.53125 C 15.012563 20.53125 13.53125 13.675668 13.53125 8.65625 L 5.5 9.875 C 5.5000002 23.236033 17.65625 26.5625 17.65625 26.5625 C 17.65625 26.562499 9.15625 34.298868 9.15625 44.40625 L 19.25 44.5 C 19.249999 44.500002 21.445759 35.5625 25 35.5625 C 28.641925 35.562502 30.6875 44.46875 30.6875 44.46875 L 40.375 44.5 C 40.375001 33.006658 30.375 26.59375 30.375 26.59375 C 30.375001 26.593749 42.625 23.166602 42.625 9.5 L 34.5 8.625 z "
- xlink:href="#rect2213"
- style="opacity:0.6;fill:url(#radialGradient2211);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path2220"
- inkscape:href="#rect2213"
- d="M 45.40625,11.75 C 45.26383,14.467634 44.656918,17.754917 42.6875,20.65625 C 40.459732,23.938183 36.552927,26.5 30.5,26.5 C 24.488156,26.5 20.637234,23.994802 18.4375,20.75 C 16.4845,17.869153 15.883719,14.581533 15.75,11.84375 L 7.5625,13.15625 C 7.8022289,20.855399 11.189172,25.378007 14.65625,28.03125 C 18.250337,30.781689 21.8125,31.5 21.8125,31.5 C 22.153479,31.568346 22.430121,31.817069 22.53422,32.148884 C 22.638319,32.4807 22.553332,32.842875 22.3125,33.09375 C 22.3125,33.09375 12.834342,43.464627 12.34375,55.4375 L 23.625,55.5625 C 23.775583,54.968024 24.284703,52.850143 25.46875,50.125 C 26.139238,48.581838 26.957479,47.047605 27.96875,45.8125 C 28.980021,44.577395 30.249533,43.59375 31.8125,43.59375 C 33.392834,43.59375 34.682884,44.544922 35.6875,45.78125 C 36.692116,47.017578 37.481015,48.583576 38.125,50.125 C 39.262583,52.847886 39.73782,54.93884 39.875,55.53125 L 50.59375,55.5625 C 49.991815,42.90975 37.8125,33.21875 37.8125,33.21875 C 37.516383,32.984647 37.386427,32.597781 37.481159,32.232384 C 37.575892,31.866987 37.877442,31.591993 38.25,31.53125 C 38.25,31.53125 42.11079,30.902899 45.96875,28.21875 C 49.692789,25.627775 53.310428,21.149688 53.5625,13.28125 L 45.40625,11.75 z " />
- </g>
-</svg>
--- a/libpurple/protocols/sametime/resources/sametime.gresource.xml Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<gresources>
- <gresource prefix="/im/pidgin/libpurple/sametime">
- <file>icons/16x16/apps/im-meanwhile.png</file>
- <file>icons/16x16/apps/scalable/im-meanwhile.svg</file>
- <file>icons/22x22/apps/im-meanwhile.png</file>
- <file>icons/22x22/apps/scalable/im-meanwhile.svg</file>
- <file>icons/48x48/apps/im-meanwhile.png</file>
- <file>icons/scalable/apps/im-meanwhile.svg</file>
- </gresource>
-</gresources>
--- a/libpurple/protocols/sametime/sametime.c Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,5546 +0,0 @@
-
-/*
- Meanwhile Protocol Plugin for Purple
- Adds Lotus Sametime support to Purple using the Meanwhile library
-
- Copyright (C) 2004 Christopher (siege) O'Brien <siege@preoccupied.net>
-
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
- USA.
-*/
-
-#include <glib/gi18n-lib.h>
-
-#define BUF_LEN (2048)
-#define BUF_LONG (BUF_LEN * 2)
-
-/* system includes */
-#include <stdlib.h>
-#include <time.h>
-
-/* glib includes */
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <gmime/gmime.h>
-
-#include <gplugin.h>
-#include <gplugin-native.h>
-
-/* purple includes */
-#include <purple.h>
-
-/* meanwhile includes */
-#include <mw_cipher.h>
-#include <mw_common.h>
-#include <mw_error.h>
-#include <mw_service.h>
-#include <mw_session.h>
-#include <mw_srvc_aware.h>
-#include <mw_srvc_conf.h>
-#include <mw_srvc_ft.h>
-#include <mw_srvc_im.h>
-#include <mw_srvc_place.h>
-#include <mw_srvc_resolve.h>
-#include <mw_srvc_store.h>
-#include <mw_st_list.h>
-
-/* plugin includes */
-#include "sametime.h"
-#include "im_mime.h"
-
-struct _mwProtocol {
- PurpleProtocol parent;
-};
-
-static PurpleProtocol *my_protocol = NULL;
-
-/* considering that there's no display of this information for protocols,
- I don't know why I even bother providing these. Oh valiant reader,
- I do it all for you. */
-/* scratch that, I just added it to the protocol options panel */
-#define PLUGIN_ID "prpl-sametime"
-#define PLUGIN_NAME "Sametime Protocol"
-#define PLUGIN_CATEGORY "Protocol"
-#define PLUGIN_SUMMARY "Sametime Protocol Plugin"
-#define PLUGIN_DESC "Open implementation of a Lotus Sametime client"
-#define PLUGIN_HOMEPAGE "http://meanwhile.sourceforge.net/"
-#define PLUGIN_AUTHORS \
- { "Christopher (siege) O'Brien <siege@preoccupied.net>", NULL }
-
-
-/* plugin preference names */
-#define MW_PROTOCOL_OPT_BASE "/plugins/prpl/meanwhile"
-#define MW_PROTOCOL_OPT_BLIST_ACTION MW_PROTOCOL_OPT_BASE "/blist_action"
-#define MW_PROTOCOL_OPT_PSYCHIC MW_PROTOCOL_OPT_BASE "/psychic"
-#define MW_PROTOCOL_OPT_FORCE_LOGIN MW_PROTOCOL_OPT_BASE "/force_login"
-#define MW_PROTOCOL_OPT_SAVE_DYNAMIC MW_PROTOCOL_OPT_BASE "/save_dynamic"
-
-
-/* stages of connecting-ness */
-#define MW_CONNECT_STEPS 11
-
-
-/* stages of consciousness */
-#define MW_STATE_OFFLINE "offline"
-#define MW_STATE_ACTIVE "active"
-#define MW_STATE_AWAY "away"
-#define MW_STATE_BUSY "dnd"
-#define MW_STATE_MESSAGE "message"
-#define MW_STATE_ENLIGHTENED "buddha"
-
-
-/* keys to get/set chat information */
-#define CHAT_KEY_CREATOR "chat.creator"
-#define CHAT_KEY_NAME "chat.name"
-#define CHAT_KEY_TOPIC "chat.topic"
-#define CHAT_KEY_INVITE "chat.invite"
-#define CHAT_KEY_IS_PLACE "chat.is_place"
-
-
-/* key for associating a mwLoginType with a buddy */
-#define BUDDY_KEY_CLIENT "meanwhile.client"
-
-/* store the remote alias so that we can re-create it easily */
-#define BUDDY_KEY_NAME "meanwhile.shortname"
-
-/* enum mwSametimeUserType */
-#define BUDDY_KEY_TYPE "meanwhile.type"
-
-
-/* key for the real group name for a meanwhile group */
-#define GROUP_KEY_NAME "meanwhile.group"
-
-/* enum mwSametimeGroupType */
-#define GROUP_KEY_TYPE "meanwhile.type"
-
-/* NAB group owning account */
-#define GROUP_KEY_OWNER "meanwhile.account"
-
-/* key gtk blist uses to indicate a collapsed group */
-#define GROUP_KEY_COLLAPSED "collapsed"
-
-
-/* verification replacement */
-#define mwSession_NO_SECRET "meanwhile.no_secret"
-
-
-/* keys to get/set purple plugin information */
-#define MW_KEY_HOST "server"
-#define MW_KEY_PORT "port"
-#define MW_KEY_FORCE "force_login"
-#define MW_KEY_FAKE_IT "fake_client_id"
-#define MW_KEY_CLIENT "client_id_val"
-#define MW_KEY_MAJOR "client_major"
-#define MW_KEY_MINOR "client_minor"
-
-
-/** number of seconds from the first blist change before a save to the
- storage service occurs. */
-#define BLIST_SAVE_SECONDS 15
-
-
-/** the possible buddy list storage settings */
-enum blist_choice {
- blist_choice_LOCAL = 1, /**< local only */
- blist_choice_MERGE = 2, /**< merge from server */
- blist_choice_STORE = 3, /**< merge from and save to server */
- blist_choice_SYNCH = 4 /**< sync with server */
-};
-
-
-/** the default blist storage option */
-#define BLIST_CHOICE_DEFAULT blist_choice_SYNCH
-
-
-/* testing for the above */
-#define BLIST_PREF_IS(n) (purple_prefs_get_int(MW_PROTOCOL_OPT_BLIST_ACTION)==(n))
-#define BLIST_PREF_IS_LOCAL() BLIST_PREF_IS(blist_choice_LOCAL)
-#define BLIST_PREF_IS_MERGE() BLIST_PREF_IS(blist_choice_MERGE)
-#define BLIST_PREF_IS_STORE() BLIST_PREF_IS(blist_choice_STORE)
-#define BLIST_PREF_IS_SYNCH() BLIST_PREF_IS(blist_choice_SYNCH)
-
-
-/* debugging output */
-#define DEBUG_ERROR(...) purple_debug_error(G_LOG_DOMAIN, __VA_ARGS__)
-#define DEBUG_INFO(...) purple_debug_info(G_LOG_DOMAIN, __VA_ARGS__)
-#define DEBUG_MISC(...) purple_debug_misc(G_LOG_DOMAIN, __VA_ARGS__)
-#define DEBUG_WARN(...) purple_debug_warning(G_LOG_DOMAIN, __VA_ARGS__)
-
-
-/** calibrates distinct secure channel nomenclature */
-static const unsigned char no_secret[] = {
- 0x2d, 0x2d, 0x20, 0x73, 0x69, 0x65, 0x67, 0x65,
- 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x6a,
- 0x65, 0x6e, 0x6e, 0x69, 0x20, 0x61, 0x6e, 0x64,
- 0x20, 0x7a, 0x6f, 0x65, 0x20, 0x2d, 0x2d, 0x00,
-};
-
-
-/** handler IDs from g_log_set_handler in mw_plugin_init */
-static guint log_handler[2] = { 0, 0 };
-
-
-/** the purple plugin data.
- available as purple_connection_get_protocol_data(gc) and mwSession_getClientData */
-struct mwPurpleProtocolData {
- struct mwSession *session;
-
- struct mwServiceAware *srvc_aware;
- struct mwServiceConference *srvc_conf;
- struct mwServiceFileTransfer *srvc_ft;
- struct mwServiceIm *srvc_im;
- struct mwServicePlace *srvc_place;
- struct mwServiceResolve *srvc_resolve;
- struct mwServiceStorage *srvc_store;
-
- /** map of PurpleGroup:mwAwareList and mwAwareList:PurpleGroup */
- GHashTable *group_list_map;
-
- /** event id for the buddy list save callback */
- guint save_event;
-
- GCancellable *cancellable;
- GSocketClient *client;
- GIOStream *stream;
- GInputStream *input;
- PurpleQueuedOutputStream *output;
- guint inpa; /* input watcher */
-
- PurpleConnection *gc;
-};
-
-
-typedef struct {
- PurpleBuddy *buddy;
- PurpleGroup *group;
-} BuddyAddData;
-
-
-/* blist and aware functions */
-
-static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist);
-
-static void blist_store(struct mwPurpleProtocolData *pd);
-
-static void blist_schedule(struct mwPurpleProtocolData *pd);
-
-static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist);
-
-static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist);
-
-static gboolean buddy_is_external(PurpleBuddy *b);
-
-static void buddy_add(struct mwPurpleProtocolData *pd, PurpleBuddy *buddy);
-
-static PurpleBuddy *
-buddy_ensure(PurpleConnection *gc, PurpleGroup *group,
- struct mwSametimeUser *stuser);
-
-static void group_add(struct mwPurpleProtocolData *pd, PurpleGroup *group);
-
-static PurpleGroup *
-group_ensure(PurpleConnection *gc, struct mwSametimeGroup *stgroup);
-
-static struct mwAwareList *
-list_ensure(struct mwPurpleProtocolData *pd, PurpleGroup *group);
-
-
-/* session functions */
-
-static struct mwSession *
-gc_to_session(PurpleConnection *gc);
-
-static PurpleConnection *session_to_gc(struct mwSession *session);
-
-
-/* conference functions */
-
-static struct mwConference *
-conf_find_by_id(struct mwPurpleProtocolData *pd, int id);
-
-
-/* conversation functions */
-
-struct convo_msg {
- enum mwImSendType type;
- gpointer data;
- GDestroyNotify clear;
-};
-
-
-struct convo_data {
- struct mwConversation *conv;
- GList *queue; /**< outgoing message queue, list of convo_msg */
-};
-
-static void convo_data_new(struct mwConversation *conv);
-
-static void convo_data_free(struct convo_data *conv);
-
-static void convo_features(struct mwConversation *conv);
-
-static PurpleConversation *convo_get_im(struct mwConversation *conv);
-
-
-/* name and id */
-
-struct named_id {
- char *id;
- char *name;
-};
-
-
-/* connection functions */
-
-static void connect_cb(GObject *source_object, GAsyncResult *result, gpointer data);
-
-
-/* ----- session ------ */
-
-
-/** resolves a mwSession from a PurpleConnection */
-static struct mwSession *gc_to_session(PurpleConnection *gc) {
- struct mwPurpleProtocolData *pd;
-
- g_return_val_if_fail(gc != NULL, NULL);
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_val_if_fail(pd != NULL, NULL);
-
- return pd->session;
-}
-
-
-/** resolves a PurpleConnection from a mwSession */
-static PurpleConnection *session_to_gc(struct mwSession *session) {
- struct mwPurpleProtocolData *pd;
-
- g_return_val_if_fail(session != NULL, NULL);
-
- pd = mwSession_getClientData(session);
- g_return_val_if_fail(pd != NULL, NULL);
-
- return pd->gc;
-}
-
-static void
-write_cb(GObject *source_object, GAsyncResult *result, gpointer data)
-{
- PurpleQueuedOutputStream *stream = PURPLE_QUEUED_OUTPUT_STREAM(source_object);
- struct mwPurpleProtocolData *pd = data;
- GError *error = NULL;
-
- DEBUG_INFO("write_cb\n");
-
- if (!purple_queued_output_stream_push_bytes_finish(stream, result, &error)) {
- purple_queued_output_stream_clear_queue(stream);
-
- if (error->code != G_IO_ERROR_CANCELLED) {
- g_prefix_error(&error, "%s", _("Lost connection with server: "));
- purple_connection_take_error(pd->gc, error);
- } else {
- g_error_free(error);
- }
- }
-}
-
-
-static int mw_session_io_write(struct mwSession *session,
- const guchar *buf, gsize len) {
- struct mwPurpleProtocolData *pd;
- GBytes *output;
-
- pd = mwSession_getClientData(session);
-
- /* the session was already closed. */
- if (pd->stream == NULL) {
- return 1;
- }
-
- output = g_bytes_new(buf, len);
- purple_queued_output_stream_push_bytes_async(
- pd->output, output, G_PRIORITY_DEFAULT, pd->cancellable,
- write_cb, pd);
- g_bytes_unref(output);
-
- return 0;
-}
-
-
-static void mw_session_io_close(struct mwSession *session) {
- struct mwPurpleProtocolData *pd;
-
- pd = mwSession_getClientData(session);
- g_return_if_fail(pd != NULL);
-
- g_cancellable_cancel(pd->cancellable);
-
- if (pd->stream) {
- if (pd->inpa) {
- g_source_remove(pd->inpa);
- pd->inpa = 0;
- }
-
- purple_gio_graceful_close(pd->stream, pd->input, G_OUTPUT_STREAM(pd->output));
- }
-
- pd->input = NULL;
- g_clear_object(&pd->output);
- g_clear_object(&pd->stream);
-}
-
-
-static void mw_session_clear(struct mwSession *session) {
- ; /* nothing for now */
-}
-
-
-/* ----- aware list ----- */
-
-
-static void blist_resolve_alias_cb(struct mwServiceResolve *srvc,
- guint32 id, guint32 code, GList *results,
- gpointer data) {
- struct mwResolveResult *result;
- struct mwResolveMatch *match;
-
- g_return_if_fail(results != NULL);
-
- result = results->data;
- g_return_if_fail(result != NULL);
- g_return_if_fail(result->matches != NULL);
-
- match = result->matches->data;
- g_return_if_fail(match != NULL);
-
- purple_buddy_set_server_alias(data, match->name);
- purple_blist_node_set_string(data, BUDDY_KEY_NAME, match->name);
-}
-
-
-static void mw_aware_list_on_aware(struct mwAwareList *list,
- struct mwAwareSnapshot *aware) {
-
- PurpleConnection *gc;
- PurpleAccount *acct;
-
- struct mwPurpleProtocolData *pd;
- guint32 idle;
- guint stat;
- const char *id;
- const char *status = MW_STATE_ACTIVE;
-
- gc = mwAwareList_getClientData(list);
- acct = purple_connection_get_account(gc);
-
- pd = purple_connection_get_protocol_data(gc);
- idle = aware->status.time;
- stat = aware->status.status;
- id = aware->id.user;
-
- if(idle) {
- guint32 idle_len; /*< how long a client has been idle */
- guint32 ugly_idle_len; /*< how long a broken client has been idle */
-
- DEBUG_INFO("%s has idle value 0x%x\n", id, idle);
-
- idle_len = time(NULL) - idle;
- ugly_idle_len = ((time(NULL) * 1000) - idle) / 1000;
-
- if(idle > ugly_idle_len)
- ugly_idle_len = 0;
- else
- ugly_idle_len = (ugly_idle_len - idle) / 1000;
-
- /*
- what's the deal here? Well, good clients are smart enough to
- publish their idle time by using an attribute to indicate that
- they went idle at some time UTC, in seconds since epoch. Bad
- clients use milliseconds since epoch. So we're going to compute
- the idle time for either method, then figure out the lower of
- the two and use that. Blame the ST 7.5 development team for
- this.
- */
-
- DEBUG_INFO("idle time: %u, ugly idle time: %u\n", idle_len, ugly_idle_len);
-
-#if 1
- if(idle_len <= ugly_idle_len) {
- ; /* DEBUG_INFO("sane idle value, let's use it\n"); */
- } else {
- idle = time(NULL) - ugly_idle_len;
- }
-
-#else
- if(idle < 0 || idle > time(NULL)) {
- DEBUG_INFO("hiding a messy idle value 0x%x\n", id, idle);
- idle = -1;
- }
-#endif
- }
-
- switch(stat) {
- case mwStatus_ACTIVE:
- status = MW_STATE_ACTIVE;
- idle = 0;
- break;
-
- case mwStatus_IDLE:
- if(! idle) idle = -1;
- break;
-
- case mwStatus_AWAY:
- status = MW_STATE_AWAY;
- break;
-
- case mwStatus_BUSY:
- status = MW_STATE_BUSY;
- break;
- }
-
- /* NAB group members */
- if(aware->group) {
- PurpleGroup *group;
- PurpleBuddy *buddy;
- PurpleBlistNode *bnode;
-
- group = g_hash_table_lookup(pd->group_list_map, list);
- buddy = purple_blist_find_buddy_in_group(acct, id, group);
- bnode = (PurpleBlistNode *) buddy;
-
- if(! buddy) {
- struct mwServiceResolve *srvc;
- GList *query;
-
- buddy = purple_buddy_new(acct, id, NULL);
- purple_blist_add_buddy(buddy, NULL, group, NULL);
-
- bnode = (PurpleBlistNode *) buddy;
-
- srvc = pd->srvc_resolve;
- query = g_list_append(NULL, (char *) id);
-
- mwServiceResolve_resolve(srvc, query, mwResolveFlag_USERS,
- blist_resolve_alias_cb, buddy, NULL);
- g_list_free(query);
- }
-
- purple_blist_node_set_int(bnode, BUDDY_KEY_TYPE, mwSametimeUser_NORMAL);
- }
-
- if(aware->online) {
- purple_protocol_got_user_status(acct, id, status, NULL);
- purple_protocol_got_user_idle(acct, id, !!idle, (time_t) idle);
-
- } else {
- purple_protocol_got_user_status(acct, id, MW_STATE_OFFLINE, NULL);
- }
-}
-
-
-static void mw_aware_list_on_attrib(struct mwAwareList *list,
- struct mwAwareIdBlock *id,
- struct mwAwareAttribute *attrib) {
-
- ; /* nothing. We'll get attribute data as we need it */
-}
-
-
-static void mw_aware_list_clear(struct mwAwareList *list) {
- ; /* nothing for now */
-}
-
-
-static struct mwAwareListHandler mw_aware_list_handler = {
- mw_aware_list_on_aware,
- mw_aware_list_on_attrib,
- mw_aware_list_clear,
-};
-
-
-/** Ensures that an Aware List is associated with the given group, and
- returns that list. */
-static struct mwAwareList *
-list_ensure(struct mwPurpleProtocolData *pd, PurpleGroup *group) {
-
- struct mwAwareList *list;
-
- g_return_val_if_fail(pd != NULL, NULL);
- g_return_val_if_fail(group != NULL, NULL);
-
- list = g_hash_table_lookup(pd->group_list_map, group);
- if(! list) {
- list = mwAwareList_new(pd->srvc_aware, &mw_aware_list_handler);
- mwAwareList_setClientData(list, pd->gc, NULL);
-
- mwAwareList_watchAttributes(list,
- mwAttribute_AV_PREFS_SET,
- mwAttribute_MICROPHONE,
- mwAttribute_SPEAKERS,
- mwAttribute_VIDEO_CAMERA,
- mwAttribute_FILE_TRANSFER,
- NULL);
-
- g_hash_table_replace(pd->group_list_map, group, list);
- g_hash_table_insert(pd->group_list_map, list, group);
- }
-
- return list;
-}
-
-
-static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist) {
- /* - find the account for this connection
- - iterate through the buddy list
- - add each buddy matching this account to the stlist
- */
-
- PurpleAccount *acct;
- PurpleBlistNode *gn, *cn, *bn;
- PurpleGroup *grp;
- PurpleBuddy *bdy;
-
- struct mwSametimeGroup *stg = NULL;
- struct mwIdBlock idb = { NULL, NULL };
-
- acct = purple_connection_get_account(gc);
- g_return_if_fail(acct != NULL);
-
- for (gn = purple_blist_get_default_root(); gn;
- gn = purple_blist_node_get_sibling_next(gn)) {
- const char *owner;
- const char *gname;
- enum mwSametimeGroupType gtype;
- gboolean gopen;
-
- if(! PURPLE_IS_GROUP(gn)) continue;
- grp = (PurpleGroup *) gn;
-
- /* the group's type (normal or dynamic) */
- gtype = purple_blist_node_get_int(gn, GROUP_KEY_TYPE);
- if(! gtype) gtype = mwSametimeGroup_NORMAL;
-
- /* if it's a normal group with none of our people in it, skip it */
- if(gtype == mwSametimeGroup_NORMAL && !purple_group_on_account(grp, acct))
- continue;
-
- /* if the group has an owner and we're not it, skip it */
- owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER);
- if(owner && !purple_strequal(owner, purple_account_get_username(acct)))
- continue;
-
- /* the group's actual name may be different from the purple group's
- name. Find whichever is there */
- gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
- if(! gname) gname = purple_group_get_name(grp);
-
- /* we save this, but never actually honor it */
- gopen = ! purple_blist_node_get_bool(gn, GROUP_KEY_COLLAPSED);
-
- stg = mwSametimeGroup_new(stlist, gtype, gname);
- mwSametimeGroup_setAlias(stg, purple_group_get_name(grp));
- mwSametimeGroup_setOpen(stg, gopen);
-
- /* don't attempt to put buddies in a dynamic group, it breaks
- other clients */
- if(gtype == mwSametimeGroup_DYNAMIC)
- continue;
-
- for(cn = purple_blist_node_get_first_child(gn);
- cn;
- cn = purple_blist_node_get_sibling_next(cn)) {
- if(! PURPLE_IS_META_CONTACT(cn)) continue;
-
- for(bn = purple_blist_node_get_first_child(cn);
- bn;
- bn = purple_blist_node_get_sibling_next(bn)) {
- if(! PURPLE_IS_BUDDY(bn)) continue;
- if(purple_blist_node_is_transient(bn)) continue;
-
- bdy = (PurpleBuddy *) bn;
-
- if(purple_buddy_get_account(bdy) == acct) {
- struct mwSametimeUser *stu;
- enum mwSametimeUserType utype;
-
- idb.user = (char *)purple_buddy_get_name(bdy);
-
- utype = purple_blist_node_get_int(bn, BUDDY_KEY_TYPE);
- if(! utype) utype = mwSametimeUser_NORMAL;
-
- stu = mwSametimeUser_new(stg, utype, &idb);
- mwSametimeUser_setShortName(stu, purple_buddy_get_server_alias(bdy));
- mwSametimeUser_setAlias(stu, purple_buddy_get_local_alias(bdy));
- }
- }
- }
- }
-}
-
-
-static void blist_store(struct mwPurpleProtocolData *pd) {
-
- struct mwSametimeList *stlist;
- struct mwServiceStorage *srvc;
- struct mwStorageUnit *unit;
-
- PurpleConnection *gc;
-
- struct mwPutBuffer *b;
- struct mwOpaque *o;
-
- g_return_if_fail(pd != NULL);
-
- srvc = pd->srvc_store;
- g_return_if_fail(srvc != NULL);
-
- gc = pd->gc;
-
- if(BLIST_PREF_IS_LOCAL() || BLIST_PREF_IS_MERGE()) {
- DEBUG_INFO("preferences indicate not to save remote blist\n");
- return;
-
- } else if(MW_SERVICE_IS_DEAD(srvc)) {
- DEBUG_INFO("aborting save of blist: storage service is not alive\n");
- return;
-
- } else if(BLIST_PREF_IS_STORE() || BLIST_PREF_IS_SYNCH()) {
- DEBUG_INFO("saving remote blist\n");
-
- } else {
- g_return_if_reached();
- }
-
- /* create and export to a list object */
- stlist = mwSametimeList_new();
- blist_export(gc, stlist);
-
- /* write it to a buffer */
- b = mwPutBuffer_new();
- mwSametimeList_put(b, stlist);
- mwSametimeList_free(stlist);
-
- /* put the buffer contents into a storage unit */
- unit = mwStorageUnit_new(mwStore_AWARE_LIST);
- o = mwStorageUnit_asOpaque(unit);
- mwPutBuffer_finalize(o, b);
-
- /* save the storage unit to the service */
- mwServiceStorage_save(srvc, unit, NULL, NULL, NULL);
-}
-
-
-static gboolean blist_save_cb(gpointer data) {
- struct mwPurpleProtocolData *pd = data;
-
- blist_store(pd);
- pd->save_event = 0;
- return FALSE;
-}
-
-
-/** schedules the buddy list to be saved to the server */
-static void blist_schedule(struct mwPurpleProtocolData *pd) {
- if(pd->save_event) return;
-
- pd->save_event = g_timeout_add_seconds(BLIST_SAVE_SECONDS,
- blist_save_cb, pd);
-}
-
-
-static gboolean buddy_is_external(PurpleBuddy *b) {
- g_return_val_if_fail(b != NULL, FALSE);
- return g_str_has_prefix(purple_buddy_get_name(b), "@E ");
-}
-
-
-/** Actually add a buddy to the aware service, and schedule the buddy
- list to be saved to the server */
-static void buddy_add(struct mwPurpleProtocolData *pd,
- PurpleBuddy *buddy) {
-
- struct mwAwareIdBlock idb = { mwAware_USER, (char *) purple_buddy_get_name(buddy), NULL };
- struct mwAwareList *list;
-
- PurpleGroup *group;
- GList *add;
-
- add = g_list_prepend(NULL, &idb);
-
- group = purple_buddy_get_group(buddy);
- list = list_ensure(pd, group);
-
- if(mwAwareList_addAware(list, add)) {
- purple_blist_remove_buddy(buddy);
- }
-
- blist_schedule(pd);
-
- g_list_free(add);
-}
-
-
-/** ensure that a PurpleBuddy exists in the group with data
- appropriately matching the st user entry from the st list */
-static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group,
- struct mwSametimeUser *stuser) {
-
- struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc);
- PurpleBuddy *buddy;
- PurpleAccount *acct = purple_connection_get_account(gc);
-
- const char *id = mwSametimeUser_getUser(stuser);
- const char *name = mwSametimeUser_getShortName(stuser);
- const char *alias = mwSametimeUser_getAlias(stuser);
- enum mwSametimeUserType type = mwSametimeUser_getType(stuser);
-
- g_return_val_if_fail(id != NULL, NULL);
- g_return_val_if_fail(*id, NULL);
-
- buddy = purple_blist_find_buddy_in_group(acct, id, group);
- if(! buddy) {
- buddy = purple_buddy_new(acct, id, alias);
-
- purple_blist_add_buddy(buddy, NULL, group, NULL);
- buddy_add(pd, buddy);
- }
-
- purple_buddy_set_local_alias(buddy, alias);
- purple_buddy_set_server_alias(buddy, name);
- purple_blist_node_set_string((PurpleBlistNode *) buddy, BUDDY_KEY_NAME, name);
- purple_blist_node_set_int((PurpleBlistNode *) buddy, BUDDY_KEY_TYPE, type);
-
- return buddy;
-}
-
-
-/** add aware watch for a dynamic group */
-static void group_add(struct mwPurpleProtocolData *pd,
- PurpleGroup *group) {
-
- struct mwAwareIdBlock idb = { mwAware_GROUP, NULL, NULL };
- struct mwAwareList *list;
- const char *n;
- GList *add;
-
- n = purple_blist_node_get_string((PurpleBlistNode *) group, GROUP_KEY_NAME);
- if(! n) n = purple_group_get_name(group);
-
- idb.user = (char *) n;
- add = g_list_prepend(NULL, &idb);
-
- list = list_ensure(pd, group);
- mwAwareList_addAware(list, add);
- g_list_free(add);
-}
-
-
-/** ensure that a PurpleGroup exists in the blist with data
- appropriately matching the st group entry from the st list */
-static PurpleGroup *group_ensure(PurpleConnection *gc,
- struct mwSametimeGroup *stgroup) {
- PurpleAccount *acct;
- PurpleGroup *group = NULL;
- PurpleBuddyList *blist;
- PurpleBlistNode *gn;
- const char *name, *alias, *owner;
- enum mwSametimeGroupType type;
-
- acct = purple_connection_get_account(gc);
- owner = purple_account_get_username(acct);
-
- blist = purple_blist_get_default();
- g_return_val_if_fail(blist != NULL, NULL);
-
- name = mwSametimeGroup_getName(stgroup);
- alias = mwSametimeGroup_getAlias(stgroup);
- type = mwSametimeGroup_getType(stgroup);
-
- if (!name) {
- DEBUG_WARN("Can't ensure a null group\n");
- return NULL;
- }
-
- DEBUG_INFO("attempting to ensure group %s, called %s\n", name, alias);
-
- /* first attempt at finding the group, by the name key */
- for (gn = purple_blist_get_default_root(); gn;
- gn = purple_blist_node_get_sibling_next(gn)) {
- const char *n, *o;
- if(! PURPLE_IS_GROUP(gn)) continue;
- n = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
- o = purple_blist_node_get_string(gn, GROUP_KEY_OWNER);
-
- DEBUG_INFO("found group named %s, owned by %s\n", n, o);
-
- if(n && purple_strequal(n, name)) {
- if(!o || purple_strequal(o, owner)) {
- DEBUG_INFO("that'll work\n");
- group = (PurpleGroup *) gn;
- break;
- }
- }
- }
-
- /* try again, by alias */
- if(! group) {
- DEBUG_INFO("searching for group by alias %s\n", alias);
- group = purple_blist_find_group(alias);
- }
-
- /* oh well, no such group. Let's create it! */
- if(! group) {
- DEBUG_INFO("creating group\n");
- group = purple_group_new(alias);
- purple_blist_add_group(group, NULL);
- }
-
- gn = (PurpleBlistNode *) group;
- purple_blist_node_set_string(gn, GROUP_KEY_NAME, name);
- purple_blist_node_set_int(gn, GROUP_KEY_TYPE, type);
-
- if(type == mwSametimeGroup_DYNAMIC) {
- purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner);
- group_add(purple_connection_get_protocol_data(gc), group);
- }
-
- return group;
-}
-
-
-/** merge the entries from a st list into the purple blist */
-static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist) {
- struct mwSametimeGroup *stgroup;
- struct mwSametimeUser *stuser;
-
- PurpleGroup *group;
-
- GList *gl, *gtl, *ul, *utl;
-
- gl = gtl = mwSametimeList_getGroups(stlist);
- for(; gl; gl = gl->next) {
-
- stgroup = (struct mwSametimeGroup *) gl->data;
- group = group_ensure(gc, stgroup);
-
- ul = utl = mwSametimeGroup_getUsers(stgroup);
- for(; ul; ul = ul->next) {
-
- stuser = (struct mwSametimeUser *) ul->data;
- buddy_ensure(gc, group, stuser);
- }
- g_list_free(utl);
- }
- g_list_free(gtl);
-}
-
-
-/** remove all buddies on account from group. If del is TRUE and group
- is left empty, remove group as well */
-static void group_clear(PurpleGroup *group, PurpleAccount *acct, gboolean del) {
- PurpleConnection *gc;
- GList *prune = NULL;
- PurpleBlistNode *gn, *cn, *bn;
-
- g_return_if_fail(group != NULL);
-
- DEBUG_INFO("clearing members from pruned group %s\n", purple_group_get_name(group));
-
- gc = purple_account_get_connection(acct);
- g_return_if_fail(gc != NULL);
-
- gn = (PurpleBlistNode *) group;
-
- for(cn = purple_blist_node_get_first_child(gn);
- cn;
- cn = purple_blist_node_get_sibling_next(cn)) {
- if(! PURPLE_IS_META_CONTACT(cn)) continue;
-
- for(bn = purple_blist_node_get_first_child(cn);
- bn;
- bn = purple_blist_node_get_sibling_next(bn)) {
- PurpleBuddy *gb = (PurpleBuddy *) bn;
-
- if(! PURPLE_IS_BUDDY(bn)) continue;
-
- if(purple_buddy_get_account(gb) == acct) {
- DEBUG_INFO("clearing %s from group\n", purple_buddy_get_name(gb));
- prune = g_list_prepend(prune, gb);
- }
- }
- }
-
- /* quickly unsubscribe from presence for the entire group */
- purple_account_remove_group(acct, group);
-
- /* remove blist entries that need to go */
- while(prune) {
- purple_blist_remove_buddy(prune->data);
- prune = g_list_delete_link(prune, prune);
- }
- DEBUG_INFO("cleared buddies\n");
-
- /* optionally remove group from blist */
- if(del && !purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(group))) {
- DEBUG_INFO("removing empty group\n");
- purple_blist_remove_group(group);
- }
-}
-
-
-/** prune out group members that shouldn't be there */
-static void group_prune(PurpleConnection *gc, PurpleGroup *group,
- struct mwSametimeGroup *stgroup) {
-
- PurpleAccount *acct;
- PurpleBlistNode *gn, *cn, *bn;
-
- GHashTable *stusers;
- GList *prune = NULL;
- GList *ul, *utl;
-
- g_return_if_fail(group != NULL);
-
- DEBUG_INFO("pruning membership of group %s\n", purple_group_get_name(group));
-
- acct = purple_connection_get_account(gc);
- g_return_if_fail(acct != NULL);
-
- stusers = g_hash_table_new(g_str_hash, g_str_equal);
-
- /* build a hash table for quick lookup while pruning the group
- contents */
- utl = mwSametimeGroup_getUsers(stgroup);
- for(ul = utl; ul; ul = ul->next) {
- const char *id = mwSametimeUser_getUser(ul->data);
- g_hash_table_insert(stusers, (char *) id, ul->data);
- DEBUG_INFO("server copy has %s\n", id);
- }
- g_list_free(utl);
-
- gn = (PurpleBlistNode *) group;
-
- for(cn = purple_blist_node_get_first_child(gn);
- cn;
- cn = purple_blist_node_get_sibling_next(cn)) {
- if(! PURPLE_IS_META_CONTACT(cn)) continue;
-
- for(bn = purple_blist_node_get_first_child(cn);
- bn;
- bn = purple_blist_node_get_sibling_next(bn)) {
- PurpleBuddy *gb = (PurpleBuddy *) bn;
-
- if(! PURPLE_IS_BUDDY(bn)) continue;
-
- /* if the account is correct and they're not in our table, mark
- them for pruning */
- if(purple_buddy_get_account(gb) == acct && !g_hash_table_lookup(stusers, purple_buddy_get_name(gb))) {
- DEBUG_INFO("marking %s for pruning\n", purple_buddy_get_name(gb));
- prune = g_list_prepend(prune, gb);
- }
- }
- }
- DEBUG_INFO("done marking\n");
-
- g_hash_table_destroy(stusers);
-
- if(prune) {
- purple_account_remove_buddies(acct, prune, NULL);
- while(prune) {
- purple_blist_remove_buddy(prune->data);
- prune = g_list_delete_link(prune, prune);
- }
- }
-}
-
-
-/** synch the entries from a st list into the purple blist, removing any
- existing buddies that aren't in the st list */
-static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist) {
-
- PurpleAccount *acct;
- PurpleBuddyList *blist;
- PurpleBlistNode *gn;
-
- GHashTable *stgroups;
- GList *g_prune = NULL;
-
- GList *gl, *gtl;
-
- const char *acct_n;
-
- DEBUG_INFO("synchronizing local buddy list from server list\n");
-
- acct = purple_connection_get_account(gc);
- g_return_if_fail(acct != NULL);
-
- acct_n = purple_account_get_username(acct);
-
- blist = purple_blist_get_default();
- g_return_if_fail(blist != NULL);
-
- /* build a hash table for quick lookup while pruning the local
- list, mapping group name to group structure */
- stgroups = g_hash_table_new(g_str_hash, g_str_equal);
-
- gtl = mwSametimeList_getGroups(stlist);
- for(gl = gtl; gl; gl = gl->next) {
- const char *name = mwSametimeGroup_getName(gl->data);
- g_hash_table_insert(stgroups, (char *) name, gl->data);
- }
- g_list_free(gtl);
-
- /* find all groups which should be pruned from the local list */
- for (gn = purple_blist_get_default_root(); gn;
- gn = purple_blist_node_get_sibling_next(gn)) {
- PurpleGroup *grp = (PurpleGroup *) gn;
- const char *gname, *owner;
- struct mwSametimeGroup *stgrp;
-
- if(! PURPLE_IS_GROUP(gn)) continue;
-
- /* group not belonging to this account */
- if(! purple_group_on_account(grp, acct))
- continue;
-
- /* dynamic group belonging to this account. don't prune contents */
- owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER);
- if(owner && purple_strequal(owner, acct_n))
- continue;
-
- /* we actually are syncing by this key as opposed to the group
- title, which can be different things in the st list */
- gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
- if(! gname) gname = purple_group_get_name(grp);
-
- stgrp = g_hash_table_lookup(stgroups, gname);
- if(! stgrp) {
- /* remove the whole group */
- DEBUG_INFO("marking group %s for pruning\n", purple_group_get_name(grp));
- g_prune = g_list_prepend(g_prune, grp);
-
- } else {
- /* synch the group contents */
- group_prune(gc, grp, stgrp);
- }
- }
- DEBUG_INFO("done marking groups\n");
-
- /* don't need this anymore */
- g_hash_table_destroy(stgroups);
-
- /* prune all marked groups */
- while(g_prune) {
- PurpleGroup *grp = g_prune->data;
- PurpleBlistNode *gn = (PurpleBlistNode *) grp;
- const char *owner;
- gboolean del = TRUE;
-
- owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER);
- if(owner && !purple_strequal(owner, acct_n)) {
- /* it's a specialty group belonging to another account with some
- of our members in it, so don't fully delete it */
- del = FALSE;
- }
-
- group_clear(g_prune->data, acct, del);
- g_prune = g_list_delete_link(g_prune, g_prune);
- }
-
- /* done with the pruning, let's merge in the additions */
- blist_merge(gc, stlist);
-}
-
-
-/** callback passed to the storage service when it's told to load the
- st list */
-static void fetch_blist_cb(struct mwServiceStorage *srvc,
- guint32 result, struct mwStorageUnit *item,
- gpointer data) {
-
- struct mwPurpleProtocolData *pd = data;
- struct mwSametimeList *stlist;
-
- struct mwGetBuffer *b;
-
- g_return_if_fail(result == ERR_SUCCESS);
-
- /* check our preferences for loading */
- if(BLIST_PREF_IS_LOCAL()) {
- DEBUG_INFO("preferences indicate not to load remote buddy list\n");
- return;
- }
-
- b = mwGetBuffer_wrap(mwStorageUnit_asOpaque(item));
-
- stlist = mwSametimeList_new();
- mwSametimeList_get(b, stlist);
-
- /* merge or synch depending on preferences */
- if(BLIST_PREF_IS_MERGE() || BLIST_PREF_IS_STORE()) {
- blist_merge(pd->gc, stlist);
-
- } else if(BLIST_PREF_IS_SYNCH()) {
- blist_sync(pd->gc, stlist);
- }
-
- mwSametimeList_free(stlist);
- mwGetBuffer_free(b);
-}
-
-
-/** signal triggered when a conversation is opened in Purple */
-static void conversation_created_cb(PurpleConversation *g_conv,
- struct mwPurpleProtocolData *pd) {
-
- /* we need to tell the IM service to negotiate features for the
- conversation right away, otherwise it'll wait until the first
- message is sent before offering NotesBuddy features. Therefore
- whenever Purple creates a conversation, we'll immediately open the
- channel to the other side and figure out what the target can
- handle. Unfortunately, this makes us vulnerable to Psychic Mode,
- whereas a more lazy negotiation based on the first message
- would not */
-
- PurpleConnection *gc;
- struct mwIdBlock who = { 0, 0 };
- struct mwConversation *conv;
-
- gc = purple_conversation_get_connection(g_conv);
- if(pd->gc != gc)
- return; /* not ours */
-
- if(!PURPLE_IS_IM_CONVERSATION(g_conv))
- return; /* wrong type */
-
- who.user = (char *) purple_conversation_get_name(g_conv);
- conv = mwServiceIm_getConversation(pd->srvc_im, &who);
-
- convo_features(conv);
-
- if(mwConversation_isClosed(conv))
- mwConversation_open(conv);
-}
-
-
-static void blist_menu_nab(PurpleBlistNode *node, gpointer data) {
- struct mwPurpleProtocolData *pd = data;
- PurpleConnection *gc;
-
- PurpleGroup *group = (PurpleGroup *) node;
-
- GString *str;
- char *tmp;
- const char *gname;
-
- g_return_if_fail(pd != NULL);
-
- gc = pd->gc;
- g_return_if_fail(gc != NULL);
-
- g_return_if_fail(PURPLE_IS_GROUP(node));
-
- str = g_string_new(NULL);
-
- tmp = (char *) purple_blist_node_get_string(node, GROUP_KEY_NAME);
- gname = purple_group_get_name(group);
-
- g_string_append_printf(str, _("<b>Group Title:</b> %s<br>"), gname);
- g_string_append_printf(str, _("<b>Notes Group ID:</b> %s<br>"), tmp);
-
- tmp = g_strdup_printf(_("Info for Group %s"), gname);
-
- purple_notify_formatted(gc, tmp, _("Notes Address Book Information"),
- NULL, str->str, NULL, NULL);
-
- g_free(tmp);
- g_string_free(str, TRUE);
-}
-
-
-/** The normal blist menu protocol function doesn't get called for groups,
- so we use the blist-node-extended-menu signal to trigger this
- handler */
-static void blist_node_menu_cb(PurpleBlistNode *node,
- GList **menu, struct mwPurpleProtocolData *pd) {
- const char *owner;
- PurpleAccount *acct;
- PurpleActionMenu *act;
-
- /* we only want groups */
- if(! PURPLE_IS_GROUP(node)) return;
-
- acct = purple_connection_get_account(pd->gc);
- g_return_if_fail(acct != NULL);
-
- /* better make sure we're connected */
- if(! purple_account_is_connected(acct)) return;
-
- /* check if it's a NAB group for this account */
- owner = purple_blist_node_get_string(node, GROUP_KEY_OWNER);
- if(owner && purple_strequal(owner, purple_account_get_username(acct))) {
- act = purple_action_menu_new(_("Get Notes Address Book Info"),
- G_CALLBACK(blist_menu_nab), pd, NULL);
- *menu = g_list_append(*menu, act);
- }
-}
-
-
-/* lifted this from oldstatus, since HEAD doesn't do this at login
- anymore. */
-static void blist_init(PurpleAccount *acct) {
- PurpleBlistNode *gnode, *cnode, *bnode;
- GList *add_buds = NULL;
-
- for (gnode = purple_blist_get_default_root(); gnode;
- gnode = purple_blist_node_get_sibling_next(gnode)) {
- if(! PURPLE_IS_GROUP(gnode)) continue;
-
- for(cnode = purple_blist_node_get_first_child(gnode);
- cnode;
- cnode = purple_blist_node_get_sibling_next(cnode)) {
- if(! PURPLE_IS_META_CONTACT(cnode))
- continue;
- for(bnode = purple_blist_node_get_first_child(cnode);
- bnode;
- bnode = purple_blist_node_get_sibling_next(bnode)) {
- PurpleBuddy *b;
- if(!PURPLE_IS_BUDDY(bnode))
- continue;
-
- b = (PurpleBuddy *)bnode;
- if(purple_buddy_get_account(b) == acct) {
- add_buds = g_list_append(add_buds, b);
- }
- }
- }
- }
-
- if(add_buds) {
- purple_account_add_buddies(acct, add_buds, NULL);
- g_list_free(add_buds);
- }
-}
-
-
-/** Last thing to happen from a started session */
-static void services_starting(struct mwPurpleProtocolData *pd) {
-
- PurpleConnection *gc;
- PurpleAccount *acct;
- struct mwStorageUnit *unit;
- PurpleBlistNode *l;
-
- gc = pd->gc;
- acct = purple_connection_get_account(gc);
-
- /* grab the buddy list from the server */
- unit = mwStorageUnit_new(mwStore_AWARE_LIST);
- mwServiceStorage_load(pd->srvc_store, unit, fetch_blist_cb, pd, NULL);
-
- /* find all the NAB groups and subscribe to them */
- for (l = purple_blist_get_default_root(); l;
- l = purple_blist_node_get_sibling_next(l)) {
- PurpleGroup *group = (PurpleGroup *) l;
- enum mwSametimeGroupType gt;
- const char *owner;
-
- if(! PURPLE_IS_GROUP(l)) continue;
-
- /* if the group is ownerless, or has an owner and we're not it,
- skip it */
- owner = purple_blist_node_get_string(l, GROUP_KEY_OWNER);
- if(!owner || !purple_strequal(owner, purple_account_get_username(acct)))
- continue;
-
- gt = purple_blist_node_get_int(l, GROUP_KEY_TYPE);
- if(gt == mwSametimeGroup_DYNAMIC)
- group_add(pd, group);
- }
-
- /* set the aware attributes */
- /* indicate we understand what AV prefs are, but don't support any */
- mwServiceAware_setAttributeBoolean(pd->srvc_aware,
- mwAttribute_AV_PREFS_SET, TRUE);
- mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_MICROPHONE);
- mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_SPEAKERS);
- mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_VIDEO_CAMERA);
-
- /* ... but we can do file transfers! */
- mwServiceAware_setAttributeBoolean(pd->srvc_aware,
- mwAttribute_FILE_TRANSFER, TRUE);
-
- blist_init(acct);
-}
-
-
-static void session_loginRedirect(struct mwSession *session,
- const char *host) {
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleAccount *account;
- guint port;
- const char *current_host;
-
- pd = mwSession_getClientData(session);
- gc = pd->gc;
- account = purple_connection_get_account(gc);
- port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT);
- current_host = purple_account_get_string(account, MW_KEY_HOST,
- MW_PLUGIN_DEFAULT_HOST);
-
- if(purple_account_get_bool(account, MW_KEY_FORCE, FALSE) ||
- !host || purple_strequal(current_host, host)) {
- /* if we're configured to force logins, or if we're being
- redirected to the already configured host,
- we'll force the login instead */
-
- mwSession_forceLogin(session);
- return;
- }
-
- pd->client = purple_gio_socket_client_new(account, NULL);
- if (pd->client == NULL) {
- /* if we couldn't connect to the new host, we'll force the login instead */
-
- mwSession_forceLogin(session);
- return;
- }
-
- g_socket_client_connect_to_host_async(
- pd->client, host,
- port, pd->cancellable,
- connect_cb, pd);
-}
-
-
-static void mw_protocol_set_status(PurpleProtocolServer *protocol_server, PurpleAccount *acct, PurpleStatus *status);
-
-
-/** called from mw_session_stateChange when the session's state is
- mwSession_STARTED. Any finalizing of start-up stuff should go
- here */
-static void session_started(struct mwPurpleProtocolData *pd) {
- PurpleStatus *status;
- PurpleAccount *acct;
-
- /* set out initial status */
- acct = purple_connection_get_account(pd->gc);
- status = purple_account_get_active_status(acct);
- mw_protocol_set_status(NULL, acct, status);
-
- /* start watching for new conversations */
- purple_signal_connect(purple_conversations_get_handle(),
- "conversation-created", pd,
- G_CALLBACK(conversation_created_cb), pd);
-
- /* watch for group extended menu items */
- purple_signal_connect(purple_blist_get_handle(),
- "blist-node-extended-menu", pd,
- G_CALLBACK(blist_node_menu_cb), pd);
-
- /* use our services to do neat things */
- services_starting(pd);
-}
-
-
-static void session_stopping(struct mwPurpleProtocolData *pd) {
- /* stop watching the signals from session_started */
- purple_signals_disconnect_by_handle(pd);
-}
-
-
-static void mw_session_stateChange(struct mwSession *session,
- enum mwSessionState state,
- gpointer info) {
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
-
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- switch(state) {
-
- case mwSession_LOGIN_REDIR:
- session_loginRedirect(session, info);
- break;
-
- case mwSession_STARTED:
- session_started(pd);
-
- purple_connection_set_state(gc, PURPLE_CONNECTION_STATE_CONNECTED);
- break;
-
- case mwSession_STOPPING:
-
- session_stopping(pd);
-
- if(GPOINTER_TO_UINT(info) & ERR_FAILURE) {
- char *err = mwError(GPOINTER_TO_UINT(info));
- PurpleConnectionError reason;
- switch (GPOINTER_TO_UINT(info)) {
- case VERSION_MISMATCH:
- reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
- break;
-
- case USER_RESTRICTED:
- case INCORRECT_LOGIN:
- case USER_UNREGISTERED:
- case GUEST_IN_USE:
- reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
- break;
-
- case ENCRYPT_MISMATCH:
- case ERR_ENCRYPT_NO_SUPPORT:
- case ERR_NO_COMMON_ENCRYPT:
- reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
- break;
-
- case VERIFICATION_DOWN:
- reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE;
- break;
-
- case MULTI_SERVER_LOGIN:
- case MULTI_SERVER_LOGIN2:
- reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
- break;
-
- default:
- reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
- }
- purple_connection_error(gc, reason, err);
- g_free(err);
- }
- break;
-
- case mwSession_STOPPED:
- break;
-
- case mwSession_UNKNOWN:
- default:
- DEBUG_WARN("session in unknown state\n");
- }
-}
-
-
-static void mw_session_setPrivacyInfo(struct mwSession *session) {
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleAccount *acct;
- struct mwPrivacyInfo *privacy;
- GSList *list;
- guint count;
-
- DEBUG_INFO("privacy information set from server\n");
-
- g_return_if_fail(session != NULL);
-
- pd = mwSession_getClientData(session);
- g_return_if_fail(pd != NULL);
-
- gc = pd->gc;
- g_return_if_fail(gc != NULL);
-
- acct = purple_connection_get_account(gc);
- g_return_if_fail(acct != NULL);
-
- privacy = mwSession_getPrivacyInfo(session);
- count = privacy->count;
-
- if (privacy->deny) {
- while ((list = purple_account_privacy_get_denied(acct))) {
- purple_account_privacy_deny_remove(acct, list->data, TRUE);
- }
- while (count--) {
- struct mwUserItem *u = privacy->users + count;
- purple_account_privacy_deny_add(acct, u->id, TRUE);
- }
- } else {
- while ((list = purple_account_privacy_get_permitted(acct))) {
- purple_account_privacy_permit_remove(acct, list->data, TRUE);
- }
- while (count--) {
- struct mwUserItem *u = privacy->users + count;
- purple_account_privacy_permit_add(acct, u->id, TRUE);
- }
- }
-}
-
-
-static void mw_session_setUserStatus(struct mwSession *session) {
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- struct mwAwareIdBlock idb = { mwAware_USER, NULL, NULL };
- struct mwUserStatus *stat;
-
- g_return_if_fail(session != NULL);
-
- pd = mwSession_getClientData(session);
- g_return_if_fail(pd != NULL);
-
- gc = pd->gc;
- g_return_if_fail(gc != NULL);
-
- idb.user = mwSession_getProperty(session, mwSession_AUTH_USER_ID);
- stat = mwSession_getUserStatus(session);
-
- /* trigger an update of our own status if we're in the buddy list */
- mwServiceAware_setStatus(pd->srvc_aware, &idb, stat);
-}
-
-
-static void mw_session_admin(struct mwSession *session,
- const char *text) {
- PurpleConnection *gc;
- PurpleAccount *acct;
- const char *host;
- const char *msg;
- char *prim;
-
- gc = session_to_gc(session);
- g_return_if_fail(gc != NULL);
-
- acct = purple_connection_get_account(gc);
- g_return_if_fail(acct != NULL);
-
- host = purple_account_get_string(acct, MW_KEY_HOST, NULL);
-
- msg = _("A Sametime administrator has issued the following announcement"
- " on server %s");
- prim = g_strdup_printf(msg, host);
-
- purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO,
- _("Sametime Administrator Announcement"),
- prim, text, NULL, NULL,
- purple_request_cpar_from_connection(gc));
-
- g_free(prim);
-}
-
-
-/** callback triggered from GPollableInputStream, watches the stream for
- available data to be processed by the session */
-static gboolean
-read_cb(GObject *stream, gpointer data)
-{
- struct mwPurpleProtocolData *pd = data;
- guchar buf[BUF_LEN];
- gssize len;
- GError *error = NULL;
-
- g_return_val_if_fail(pd != NULL, G_SOURCE_REMOVE);
-
- do {
- len = g_pollable_input_stream_read_nonblocking(
- G_POLLABLE_INPUT_STREAM(stream), buf, sizeof(buf) - 1,
- pd->cancellable, &error);
- if (len == 0) {
- DEBUG_INFO("connection reset\n");
- purple_connection_error(pd->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Server closed the connection"));
- pd->inpa = 0;
- return G_SOURCE_REMOVE;
- } else if (len < 0) {
- /* read problem occurred if we're here, so we'll need to take care of
- it and clean up internal state */
- if (error->code == G_IO_ERROR_WOULD_BLOCK) {
- g_error_free(error);
- return G_SOURCE_CONTINUE;
- } else if (error->code == G_IO_ERROR_CANCELLED) {
- g_error_free(error);
- } else {
- g_prefix_error(&error, "%s",
- _("Lost connection with server: "));
- purple_connection_take_error(pd->gc, error);
- }
- pd->inpa = 0;
- pd->input = NULL;
- g_clear_object(&pd->output);
- g_clear_object(&pd->stream);
- return G_SOURCE_REMOVE;
- }
-
- mwSession_recv(pd->session, buf, len);
- } while (len > 0);
-
- /* normal operation ends here */
- return G_SOURCE_CONTINUE;
-}
-
-
-/* Callback passed to g_socket_client_connect_to_host_async when an account is
- * logged in, and if the session logging in receives a redirect message */
-static void
-connect_cb(GObject *source_object, GAsyncResult *result, gpointer data)
-{
- GSocketClient *client = G_SOCKET_CLIENT(source_object);
- struct mwPurpleProtocolData *pd = data;
- GSocketConnection *sockconn = NULL;
- GSource *source = NULL;
- GError *error = NULL;
-
- sockconn = g_socket_client_connect_to_host_finish(client, result, &error);
- if (sockconn == NULL) {
- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- /* connection was cancelled */
- g_error_free(error);
- } else if (pd->stream) {
- /* this is a redirect connect, force login on existing socket */
- mwSession_forceLogin(pd->session);
-
- } else {
- /* this is a regular connect, error out */
- purple_connection_take_error(pd->gc, error);
- }
-
- return;
- }
-
- if (pd->stream) {
- /* stop any existing login attempt */
- mwSession_stop(pd->session, ERR_SUCCESS);
- }
-
- pd->stream = G_IO_STREAM(sockconn);
- pd->input = g_object_ref(g_io_stream_get_input_stream(pd->stream));
- pd->output = purple_queued_output_stream_new(
- g_io_stream_get_output_stream(pd->stream));
- source = g_pollable_input_stream_create_source(
- G_POLLABLE_INPUT_STREAM(pd->input), pd->cancellable);
- g_source_set_callback(source, (GSourceFunc)read_cb, pd, NULL);
- pd->inpa = g_source_attach(source, NULL);
- g_source_unref(source);
-
- mwSession_start(pd->session);
-}
-
-
-static void mw_session_announce(struct mwSession *s,
- struct mwLoginInfo *from,
- gboolean may_reply,
- const char *text) {
- struct mwPurpleProtocolData *pd;
- PurpleAccount *acct;
- PurpleConversation *im;
- PurpleConversationManager *manager;
- PurpleBuddy *buddy;
- char *who = from->user_id;
- char *msg, *msg2;
-
- pd = mwSession_getClientData(s);
- acct = purple_connection_get_account(pd->gc);
-
- manager = purple_conversation_manager_get_default();
- im = purple_conversation_manager_find_im(manager, acct, who);
- if(!PURPLE_IS_IM_CONVERSATION(im)) {
- im = purple_im_conversation_new(acct, who);
- }
-
- buddy = purple_blist_find_buddy(acct, who);
- if(buddy) who = (char *) purple_buddy_get_contact_alias(buddy);
-
- who = g_strdup_printf(_("Announcement from %s"), who);
- msg = purple_markup_linkify(text);
- if (msg && msg[0])
- msg2 = g_strdup_printf("%s: %s", who, msg);
- else
- msg2 = g_strdup(who);
-
- purple_conversation_write_system_message(im, msg2, 0);
- g_free(who);
- g_free(msg);
- g_free(msg2);
-}
-
-
-static struct mwSessionHandler mw_session_handler = {
- mw_session_io_write,
- mw_session_io_close,
- mw_session_clear,
- mw_session_stateChange,
- mw_session_setPrivacyInfo,
- mw_session_setUserStatus,
- mw_session_admin,
- mw_session_announce,
-};
-
-
-static void mw_aware_on_attrib(struct mwServiceAware *srvc,
- struct mwAwareAttribute *attrib) {
-
- ; /** @todo handle server attributes. There may be some stuff we
- actually want to look for, but I'm not aware of anything right
- now.*/
-}
-
-
-static void mw_aware_clear(struct mwServiceAware *srvc) {
- ; /* nothing for now */
-}
-
-
-static struct mwAwareHandler mw_aware_handler = {
- mw_aware_on_attrib,
- mw_aware_clear,
-};
-
-
-static struct mwServiceAware *mw_srvc_aware_new(struct mwSession *s) {
- struct mwServiceAware *srvc;
- srvc = mwServiceAware_new(s, &mw_aware_handler);
- return srvc;
-};
-
-
-static void mw_conf_invited(struct mwConference *conf,
- struct mwLoginInfo *inviter,
- const char *invitation) {
-
- struct mwServiceConference *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
-
- char *c_inviter, *c_name, *c_topic, *c_invitation;
- GHashTable *ht;
-
- srvc = mwConference_getService(conf);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
-
- c_inviter = g_strdup(inviter->user_id);
- g_hash_table_insert(ht, CHAT_KEY_CREATOR, c_inviter);
-
- c_name = g_strdup(mwConference_getName(conf));
- g_hash_table_insert(ht, CHAT_KEY_NAME, c_name);
-
- c_topic = g_strdup(mwConference_getTitle(conf));
- g_hash_table_insert(ht, CHAT_KEY_TOPIC, c_topic);
-
- c_invitation = g_strdup(invitation);
- g_hash_table_insert(ht, CHAT_KEY_INVITE, c_invitation);
-
- DEBUG_INFO("received invitation from '%s' to join ('%s','%s'): '%s'\n",
- c_inviter, c_name,
- c_topic, c_invitation);
-
- if(! c_topic) c_topic = "(no title)";
- if(! c_invitation) c_invitation = "(no message)";
- purple_serv_got_chat_invite(gc, c_topic, c_inviter, c_invitation, ht);
-}
-
-
-/* The following mess helps us relate a mwConference to a PurpleChatConversation
- in the various forms by which either may be indicated */
-
-#define CONF_TO_ID(conf) (GPOINTER_TO_INT(conf))
-#define ID_TO_CONF(pd, id) (conf_find_by_id((pd), (id)))
-
-#define CHAT_TO_CONF(pd, chat) (ID_TO_CONF((pd), CHAT_TO_ID(chat)))
-#define CONF_TO_CHAT(conf) (ID_TO_CHAT(CONF_TO_ID(conf)))
-
-static struct mwConference *
-conf_find_by_id(struct mwPurpleProtocolData *pd, int id) {
-
- struct mwServiceConference *srvc = pd->srvc_conf;
- struct mwConference *conf = NULL;
- GList *l, *ll;
-
- ll = mwServiceConference_getConferences(srvc);
- for(l = ll; l; l = l->next) {
- struct mwConference *c = l->data;
- PurpleChatConversation *h = mwConference_getClientData(c);
-
- if(purple_chat_conversation_get_id(h) == id) {
- conf = c;
- break;
- }
- }
- g_list_free(ll);
-
- return conf;
-}
-
-
-static void mw_conf_opened(struct mwConference *conf, GList *members) {
- struct mwServiceConference *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleConversation *g_conf;
-
- const char *n = mwConference_getName(conf);
- const char *t = mwConference_getTitle(conf);
-
- DEBUG_INFO("conf %s opened, %u initial members\n", n, g_list_length(members));
-
- srvc = mwConference_getService(conf);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- if(! t) t = "(no title)";
- g_conf = purple_serv_got_joined_chat(gc, CONF_TO_ID(conf), t);
-
- mwConference_setClientData(conf, g_conf, NULL);
-
- for(; members; members = members->next) {
- struct mwLoginInfo *peer = members->data;
- purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(g_conf), peer->user_id,
- NULL, PURPLE_CHAT_USER_NONE, FALSE);
- }
-}
-
-
-static void mw_conf_closed(struct mwConference *conf, guint32 reason) {
- struct mwServiceConference *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
-
- const char *n = mwConference_getName(conf);
- char *msg = mwError(reason);
-
- DEBUG_INFO("conf %s closed, 0x%08x\n", n, reason);
-
- srvc = mwConference_getService(conf);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- purple_serv_got_chat_left(gc, CONF_TO_ID(conf));
-
- purple_notify_error(gc, _("Conference Closed"), NULL, msg,
- purple_request_cpar_from_connection(gc));
- g_free(msg);
-}
-
-
-static void mw_conf_peer_joined(struct mwConference *conf,
- struct mwLoginInfo *peer) {
-
- PurpleChatConversation *g_conf;
-
- const char *n = mwConference_getName(conf);
-
- DEBUG_INFO("%s joined conf %s\n", peer->user_id, n);
-
- g_conf = mwConference_getClientData(conf);
- g_return_if_fail(g_conf != NULL);
-
- purple_chat_conversation_add_user(g_conf, peer->user_id,
- NULL, PURPLE_CHAT_USER_NONE, TRUE);
-}
-
-
-static void mw_conf_peer_parted(struct mwConference *conf,
- struct mwLoginInfo *peer) {
-
- PurpleChatConversation *g_conf;
-
- const char *n = mwConference_getName(conf);
-
- DEBUG_INFO("%s left conf %s\n", peer->user_id, n);
-
- g_conf = mwConference_getClientData(conf);
- g_return_if_fail(g_conf != NULL);
-
- purple_chat_conversation_remove_user(g_conf, peer->user_id, NULL);
-}
-
-
-static void mw_conf_text(struct mwConference *conf,
- struct mwLoginInfo *who, const char *text) {
-
- struct mwServiceConference *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- char *esc;
-
- if(! text) return;
-
- srvc = mwConference_getService(conf);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- esc = g_markup_escape_text(text, -1);
- purple_serv_got_chat_in(gc, CONF_TO_ID(conf), who->user_id, PURPLE_MESSAGE_RECV, esc, time(NULL));
- g_free(esc);
-}
-
-
-static void mw_conf_typing(struct mwConference *conf,
- struct mwLoginInfo *who, gboolean typing) {
-
- /* purple really has no good way to expose this to the user. */
-
- const char *n = mwConference_getName(conf);
- const char *w = who->user_id;
-
- if(typing) {
- DEBUG_INFO("%s in conf %s: <typing>\n", w, n);
-
- } else {
- DEBUG_INFO("%s in conf %s: <stopped typing>\n", w, n);
- }
-}
-
-
-static void mw_conf_clear(struct mwServiceConference *srvc) {
- ;
-}
-
-
-static struct mwConferenceHandler mw_conference_handler = {
- mw_conf_invited,
- mw_conf_opened,
- mw_conf_closed,
- mw_conf_peer_joined,
- mw_conf_peer_parted,
- mw_conf_text,
- mw_conf_typing,
- mw_conf_clear,
-};
-
-
-static struct mwServiceConference *mw_srvc_conf_new(struct mwSession *s) {
- struct mwServiceConference *srvc;
- srvc = mwServiceConference_new(s, &mw_conference_handler);
- return srvc;
-}
-
-
-/** size of an outgoing file transfer chunk */
-#define MW_FT_LEN (BUF_LONG * 2)
-
-#define MW_TYPE_XFER (mw_xfer_get_type())
-G_DECLARE_FINAL_TYPE(mwXfer, mw_xfer, MW, XFER, PurpleXfer)
-
-struct _mwXfer {
- PurpleXfer parent;
-
- struct mwFileTransfer *ft;
-};
-
-G_DEFINE_DYNAMIC_TYPE(mwXfer, mw_xfer, PURPLE_TYPE_XFER);
-
-
-static void ft_incoming_cancel(PurpleXfer *xfer) {
- /* incoming transfer rejected or cancelled in-progress */
- mwXfer *mw_xfer = MW_XFER(xfer);
- if(mw_xfer->ft) mwFileTransfer_reject(mw_xfer->ft);
-}
-
-
-static void ft_incoming_init(PurpleXfer *xfer) {
- /* incoming transfer accepted */
-
- /* - accept the mwFileTransfer
- - open/create the local FILE "wb"
- - stick the FILE's fp in xfer->dest_fp
- */
-
- mwXfer *mw_xfer = MW_XFER(xfer);
-
- purple_xfer_start(xfer, -1, NULL, 0);
- mwFileTransfer_accept(mw_xfer->ft);
-}
-
-
-static void ft_outgoing_init(PurpleXfer *xfer) {
- PurpleAccount *acct;
- PurpleConnection *gc;
-
- struct mwPurpleProtocolData *pd;
- struct mwServiceFileTransfer *srvc;
- struct mwFileTransfer *ft;
-
- const char *filename;
- gsize filesize;
- FILE *fp;
- char *remote_user = NULL;
-
- struct mwIdBlock idb = { NULL, NULL };
-
- DEBUG_INFO("ft_outgoing_init\n");
-
- acct = purple_xfer_get_account(xfer);
- gc = purple_account_get_connection(acct);
- pd = purple_connection_get_protocol_data(gc);
- srvc = pd->srvc_ft;
-
- remote_user = g_strdup(purple_xfer_get_remote_user(xfer));
-
- filename = purple_xfer_get_local_filename(xfer);
- filesize = purple_xfer_get_size(xfer);
- idb.user = remote_user;
-
- /* test that we can actually send the file */
- fp = g_fopen(filename, "rb");
- if(! fp) {
- char *msg = g_strdup_printf(_("Error reading file %s: \n%s\n"),
- filename, g_strerror(errno));
- purple_xfer_error(purple_xfer_get_xfer_type(xfer), acct, purple_xfer_get_remote_user(xfer), msg);
- g_free(msg);
- g_free(remote_user);
- return;
- }
- fclose(fp);
-
- {
- char *tmp = strrchr(filename, G_DIR_SEPARATOR);
- if(tmp++) filename = tmp;
- }
-
- ft = mwFileTransfer_new(srvc, &idb, NULL, filename, filesize);
-
- g_object_ref(xfer);
- mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) g_object_unref);
-
- mwFileTransfer_offer(ft);
- g_free(remote_user);
-}
-
-
-static void ft_init(PurpleXfer *xfer) {
- switch(purple_xfer_get_xfer_type(xfer)) {
- case PURPLE_XFER_TYPE_SEND:
- ft_outgoing_init(xfer);
- break;
- case PURPLE_XFER_TYPE_RECEIVE:
- ft_incoming_init(xfer);
- break;
- case PURPLE_XFER_TYPE_UNKNOWN:
- default:
- g_return_if_reached();
- break;
- }
-}
-
-static void mw_ft_offered(struct mwFileTransfer *ft) {
- /*
- - create a purple ft object
- - offer it
- */
-
- struct mwServiceFileTransfer *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleAccount *acct;
- const char *who;
- PurpleXfer *xfer;
-
- /* @todo add some safety checks */
- srvc = mwFileTransfer_getService(ft);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
- acct = purple_connection_get_account(gc);
-
- who = mwFileTransfer_getUser(ft)->user;
-
- DEBUG_INFO("file transfer %p offered\n", ft);
- DEBUG_INFO(" from: %s\n", who);
- DEBUG_INFO(" file: %s\n", mwFileTransfer_getFileName(ft));
- DEBUG_INFO(" size: %u\n", mwFileTransfer_getFileSize(ft));
- DEBUG_INFO(" text: %s\n", mwFileTransfer_getMessage(ft));
-
- xfer = purple_xfer_new(acct, PURPLE_XFER_TYPE_RECEIVE, who);
- if (xfer)
- {
- g_object_ref(xfer);
- mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) g_object_unref);
-
- purple_xfer_set_filename(xfer, mwFileTransfer_getFileName(ft));
- purple_xfer_set_size(xfer, mwFileTransfer_getFileSize(ft));
- purple_xfer_set_message(xfer, mwFileTransfer_getMessage(ft));
-
- purple_xfer_request(xfer);
- }
-}
-
-
-static void ft_send(struct mwFileTransfer *ft) {
- guchar buf[MW_FT_LEN];
- struct mwOpaque o = { MW_FT_LEN, buf };
- guint32 rem;
- PurpleXfer *xfer;
-
- xfer = mwFileTransfer_getClientData(ft);
-
- rem = mwFileTransfer_getRemaining(ft);
- if(rem < MW_FT_LEN) o.len = rem;
-
- if(purple_xfer_read_file(xfer, buf, (size_t) o.len) == (gssize)o.len) {
-
- /* calculate progress and display it */
- purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + o.len);
-
- mwFileTransfer_send(ft, &o);
-
- } else {
- int err = errno;
- DEBUG_WARN("problem reading from file %s: %s\n",
- mwFileTransfer_getFileName(ft), g_strerror(err));
-
- mwFileTransfer_cancel(ft);
- }
-}
-
-
-static void mw_ft_opened(struct mwFileTransfer *ft) {
- /*
- - get purple ft from client data in ft
- - set the state to active
- */
-
- PurpleXfer *xfer;
-
- xfer = mwFileTransfer_getClientData(ft);
-
- if(! xfer) {
- mwFileTransfer_cancel(ft);
- mwFileTransfer_free(ft);
- g_return_if_reached();
- }
-
- if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
- purple_xfer_start(xfer, -1, NULL, 0);
- ft_send(ft);
- }
-}
-
-
-static void mw_ft_closed(struct mwFileTransfer *ft, guint32 code) {
- /*
- - get purple ft from client data in ft
- - indicate rejection/cancellation/completion
- - free the file transfer itself
- */
-
- PurpleXfer *xfer;
-
- xfer = mwFileTransfer_getClientData(ft);
- if(xfer) {
- if(! mwFileTransfer_getRemaining(ft)) {
- purple_xfer_set_completed(xfer, TRUE);
- purple_xfer_end(xfer);
-
- } else if(mwFileTransfer_isCancelLocal(ft)) {
- /* calling purple_xfer_cancel_local is redundant, since that's
- probably what triggered this function to be called */
- ;
-
- } else if(mwFileTransfer_isCancelRemote(ft)) {
- /* steal the reference for the xfer */
- mwFileTransfer_setClientData(ft, NULL, NULL);
- purple_xfer_cancel_remote(xfer);
-
- /* drop the stolen reference */
- g_object_unref(xfer);
- return;
- }
- }
-
- mwFileTransfer_free(ft);
-}
-
-
-static void mw_ft_recv(struct mwFileTransfer *ft,
- struct mwOpaque *data) {
- /*
- - get purple ft from client data in ft
- - update transferred percentage
- - if done, destroy the ft, disassociate from purple ft
- */
-
- PurpleXfer *xfer;
-
- xfer = mwFileTransfer_getClientData(ft);
- g_return_if_fail(xfer != NULL);
-
- /* we must collect and save our precious data */
- if (!purple_xfer_write_file(xfer, data->data, data->len)) {
- DEBUG_ERROR("failed to write data\n");
- purple_xfer_cancel_local(xfer);
- return;
- }
-
- /* update the progress */
- purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + data->len);
-
- /* let the other side know we got it, and to send some more */
- mwFileTransfer_ack(ft);
-}
-
-
-static void mw_ft_ack(struct mwFileTransfer *ft) {
- PurpleXfer *xfer;
-
- xfer = mwFileTransfer_getClientData(ft);
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(purple_xfer_get_watcher(xfer) == 0);
-
- if(! mwFileTransfer_getRemaining(ft)) {
- purple_xfer_set_completed(xfer, TRUE);
- purple_xfer_end(xfer);
-
- } else if(mwFileTransfer_isOpen(ft)) {
- ft_send(ft);
- }
-}
-
-
-static void mw_ft_clear(struct mwServiceFileTransfer *srvc) {
- ;
-}
-
-
-static struct mwFileTransferHandler mw_ft_handler = {
- mw_ft_offered,
- mw_ft_opened,
- mw_ft_closed,
- mw_ft_recv,
- mw_ft_ack,
- mw_ft_clear,
-};
-
-
-static struct mwServiceFileTransfer *mw_srvc_ft_new(struct mwSession *s) {
- struct mwServiceFileTransfer *srvc;
- GHashTable *ft_map;
-
- ft_map = g_hash_table_new(g_direct_hash, g_direct_equal);
-
- srvc = mwServiceFileTransfer_new(s, &mw_ft_handler);
- mwService_setClientData(MW_SERVICE(srvc), ft_map,
- (GDestroyNotify) g_hash_table_destroy);
-
- return srvc;
-}
-
-
-static void ft_outgoing_cancel(PurpleXfer *xfer) {
- mwXfer *mw_xfer = MW_XFER(xfer);
-
- DEBUG_INFO("ft_outgoing_cancel called\n");
-
- if(mw_xfer->ft) mwFileTransfer_cancel(mw_xfer->ft);
-}
-
-
-static void
-mw_xfer_init(mwXfer *xfer) {
-
-}
-
-static void
-mw_xfer_class_finalize(mwXferClass *klass) {
-
-}
-
-static void
-mw_xfer_class_init(mwXferClass *klass) {
- PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
-
- xfer_class->init = ft_init;
- xfer_class->cancel_send = ft_outgoing_cancel;
- xfer_class->cancel_recv = ft_incoming_cancel;
- xfer_class->request_denied = ft_incoming_cancel;
-}
-
-
-static PurpleXfer *mw_protocol_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) {
- PurpleAccount *acct;
-
- acct = purple_connection_get_account(gc);
-
- return g_object_new(
- MW_TYPE_XFER,
- "account", acct,
- "type", PURPLE_XFER_TYPE_SEND,
- "remote-user", who,
- NULL
- );
-
-}
-
-
-static void convo_data_free(struct convo_data *cd) {
- GList *l;
-
- /* clean the queue */
- for(l = cd->queue; l; l = g_list_delete_link(l, l)) {
- struct convo_msg *m = l->data;
- if(m->clear) m->clear(m->data);
- g_free(m);
- }
-
- g_free(cd);
-}
-
-
-/** allocates a convo_data structure and associates it with the
- conversation in the client data slot */
-static void convo_data_new(struct mwConversation *conv) {
- struct convo_data *cd;
-
- g_return_if_fail(conv != NULL);
-
- if(mwConversation_getClientData(conv))
- return;
-
- cd = g_new0(struct convo_data, 1);
- cd->conv = conv;
-
- mwConversation_setClientData(conv, cd, (GDestroyNotify) convo_data_free);
-}
-
-
-static PurpleConversation *convo_get_im(struct mwConversation *conv) {
- struct mwServiceIm *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleConversationManager *manager;
- PurpleAccount *acct;
-
- struct mwIdBlock *idb;
-
- srvc = mwConversation_getService(conv);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
- acct = purple_connection_get_account(gc);
-
- idb = mwConversation_getTarget(conv);
-
- manager = purple_conversation_manager_get_default();
-
- return purple_conversation_manager_find_im(manager, acct, idb->user);
-}
-
-
-static void convo_queue(struct mwConversation *conv,
- enum mwImSendType type, gconstpointer data) {
-
- struct convo_data *cd;
- struct convo_msg *m;
-
- convo_data_new(conv);
- cd = mwConversation_getClientData(conv);
-
- m = g_new0(struct convo_msg, 1);
- m->type = type;
-
- switch(type) {
- case mwImSend_PLAIN:
- m->data = g_strdup(data);
- m->clear = g_free;
- break;
-
- case mwImSend_TYPING:
- default:
- m->data = (gpointer) data;
- m->clear = NULL;
- }
-
- cd->queue = g_list_append(cd->queue, m);
-}
-
-
-/* Does what it takes to get an error displayed for a conversation */
-static void convo_error(struct mwConversation *conv, guint32 err) {
- PurpleConversation *im;
- PurpleConnection *pc;
- char *tmp, *text;
- struct mwIdBlock *idb;
-
- idb = mwConversation_getTarget(conv);
-
- tmp = mwError(err);
- text = g_strconcat(_("Unable to send message: "), tmp, NULL);
-
- im = convo_get_im(conv);
- if(im && !purple_conversation_present_error(idb->user,
- purple_conversation_get_account(im), text)) {
-
- g_free(text);
- text = g_strdup_printf(_("Unable to send message to %s:"),
- (idb->user)? idb->user: "(unknown)");
- pc = purple_account_get_connection(purple_conversation_get_account(im));
- purple_notify_error(pc, NULL, text, tmp, purple_request_cpar_from_connection(pc));
- }
-
- g_free(tmp);
- g_free(text);
-}
-
-
-static void convo_queue_send(struct mwConversation *conv) {
- struct convo_data *cd;
- GList *l;
-
- cd = mwConversation_getClientData(conv);
-
- for(l = cd->queue; l; l = g_list_delete_link(l, l)) {
- struct convo_msg *m = l->data;
-
- mwConversation_send(conv, m->type, m->data);
-
- if(m->clear) m->clear(m->data);
- g_free(m);
- }
-
- cd->queue = NULL;
-}
-
-
-/** called when a mw conversation leaves a purple conversation to
- inform the purple conversation that it's unsafe to offer any *cool*
- features. */
-static void convo_nofeatures(struct mwConversation *conv) {
- PurpleConversation *im;
- PurpleConnection *gc;
-
- im = convo_get_im(conv);
- if(! im) return;
-
- gc = purple_conversation_get_connection(im);
- if(! gc) return;
-
- purple_conversation_set_features(im, purple_connection_get_flags(gc));
-}
-
-
-/** called when a mw conversation and purple conversation come together,
- to inform the purple conversation of what features to offer the
- user */
-static void convo_features(struct mwConversation *conv) {
- PurpleConversation *im;
- PurpleConnectionFlags feat;
-
- im = convo_get_im(conv);
- if(! im) return;
-
- feat = purple_conversation_get_features(im);
-
- if(mwConversation_isOpen(conv)) {
- if(mwConversation_supports(conv, mwImSend_HTML)) {
- feat |= PURPLE_CONNECTION_FLAG_HTML;
- } else {
- feat &= ~PURPLE_CONNECTION_FLAG_HTML;
- }
-
- if(mwConversation_supports(conv, mwImSend_MIME)) {
- feat &= ~PURPLE_CONNECTION_FLAG_NO_IMAGES;
- } else {
- feat |= PURPLE_CONNECTION_FLAG_NO_IMAGES;
- }
-
- DEBUG_INFO("conversation features set to 0x%04x\n", feat);
- purple_conversation_set_features(im, feat);
-
- } else {
- convo_nofeatures(conv);
- }
-}
-
-
-static void mw_conversation_opened(struct mwConversation *conv) {
- struct mwServiceIm *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleAccount *acct;
-
- struct convo_dat *cd;
-
- srvc = mwConversation_getService(conv);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
- acct = purple_connection_get_account(gc);
-
- /* set up the queue */
- cd = mwConversation_getClientData(conv);
- if(cd) {
- convo_queue_send(conv);
-
- if(! convo_get_im(conv)) {
- mwConversation_free(conv);
- return;
- }
-
- } else {
- convo_data_new(conv);
- }
-
- { /* record the client key for the buddy */
- PurpleBuddy *buddy;
- struct mwLoginInfo *info;
- info = mwConversation_getTargetInfo(conv);
-
- buddy = purple_blist_find_buddy(acct, info->user_id);
- if(buddy) {
- purple_blist_node_set_int((PurpleBlistNode *) buddy,
- BUDDY_KEY_CLIENT, info->type);
- }
- }
-
- convo_features(conv);
-}
-
-
-static void mw_conversation_closed(struct mwConversation *conv,
- guint32 reason) {
-
- struct convo_data *cd;
-
- g_return_if_fail(conv != NULL);
-
- /* if there's an error code and a non-typing message in the queue,
- print an error message to the conversation */
- cd = mwConversation_getClientData(conv);
- if(reason && cd && cd->queue) {
- GList *l;
- for(l = cd->queue; l; l = l->next) {
- struct convo_msg *m = l->data;
- if(m->type != mwImSend_TYPING) {
- convo_error(conv, reason);
- break;
- }
- }
- }
-
- mwConversation_removeClientData(conv);
-}
-
-
-static void im_recv_text(struct mwConversation *conv,
- struct mwPurpleProtocolData *pd,
- const char *msg) {
-
- struct mwIdBlock *idb;
- char *txt, *esc;
- const char *t;
-
- idb = mwConversation_getTarget(conv);
-
- txt = purple_utf8_try_convert(msg);
- t = txt? txt: msg;
-
- esc = g_markup_escape_text(t, -1);
- purple_serv_got_im(pd->gc, idb->user, esc, 0, time(NULL));
- g_free(esc);
-
- g_free(txt);
-}
-
-
-static void im_recv_typing(struct mwConversation *conv,
- struct mwPurpleProtocolData *pd,
- gboolean typing) {
-
- struct mwIdBlock *idb;
- idb = mwConversation_getTarget(conv);
-
- purple_serv_got_typing(pd->gc, idb->user, 0,
- typing? PURPLE_IM_TYPING: PURPLE_IM_NOT_TYPING);
-}
-
-
-static void im_recv_html(struct mwConversation *conv,
- struct mwPurpleProtocolData *pd,
- const char *msg) {
- struct mwIdBlock *idb;
- char *t1, *t2;
- const char *t;
-
- idb = mwConversation_getTarget(conv);
-
- /* ensure we're receiving UTF8 */
- t1 = purple_utf8_try_convert(msg);
- t = t1? t1: msg;
-
- /* convert entities to UTF8 so they'll log correctly */
- t2 = purple_utf8_ncr_decode(t);
- t = t2? t2: t;
-
- purple_serv_got_im(pd->gc, idb->user, t, 0, time(NULL));
-
- g_free(t1);
- g_free(t2);
-}
-
-
-static void im_recv_subj(struct mwConversation *conv,
- struct mwPurpleProtocolData *pd,
- const char *subj) {
-
- /** @todo somehow indicate receipt of a conversation subject. It
- would also be nice if we added a /topic command for the
- protocol */
- ;
-}
-
-
-static void mw_conversation_recv(struct mwConversation *conv,
- enum mwImSendType type,
- gconstpointer msg) {
- struct mwServiceIm *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
-
- srvc = mwConversation_getService(conv);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
-
- switch(type) {
- case mwImSend_PLAIN:
- im_recv_text(conv, pd, msg);
- break;
-
- case mwImSend_TYPING:
- im_recv_typing(conv, pd, !! msg);
- break;
-
- case mwImSend_HTML:
- im_recv_html(conv, pd, msg);
- break;
-
- case mwImSend_SUBJECT:
- im_recv_subj(conv, pd, msg);
- break;
-
- case mwImSend_MIME: {
- gchar *parsed_message = im_mime_parse(msg);
- im_recv_html(conv, pd, parsed_message);
- g_free(parsed_message);
- break;
- }
-
- default:
- DEBUG_INFO("conversation received strange type, 0x%04x\n", type);
- ; /* erm... */
- }
-}
-
-
-static void mw_place_invite(struct mwConversation *conv,
- const char *message,
- const char *title, const char *name) {
- struct mwServiceIm *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
-
- struct mwIdBlock *idb;
- GHashTable *ht;
-
- srvc = mwConversation_getService(conv);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
-
- idb = mwConversation_getTarget(conv);
-
- ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
- g_hash_table_insert(ht, CHAT_KEY_CREATOR, g_strdup(idb->user));
- g_hash_table_insert(ht, CHAT_KEY_NAME, g_strdup(name));
- g_hash_table_insert(ht, CHAT_KEY_TOPIC, g_strdup(title));
- g_hash_table_insert(ht, CHAT_KEY_INVITE, g_strdup(message));
- g_hash_table_insert(ht, CHAT_KEY_IS_PLACE, g_strdup("")); /* ugh */
-
- if(! title) title = "(no title)";
- if(! message) message = "(no message)";
- purple_serv_got_chat_invite(pd->gc, title, idb->user, message, ht);
-
- mwConversation_close(conv, ERR_SUCCESS);
- mwConversation_free(conv);
-}
-
-
-static void mw_im_clear(struct mwServiceIm *srvc) {
- ;
-}
-
-
-static struct mwImHandler mw_im_handler = {
- mw_conversation_opened,
- mw_conversation_closed,
- mw_conversation_recv,
- mw_place_invite,
- mw_im_clear,
-};
-
-
-static struct mwServiceIm *mw_srvc_im_new(struct mwSession *s) {
- struct mwServiceIm *srvc;
- srvc = mwServiceIm_new(s, &mw_im_handler);
- mwServiceIm_setClientType(srvc, mwImClient_NOTESBUDDY);
- return srvc;
-}
-
-
-/* The following helps us relate a mwPlace to a PurpleChatConversation in the
- various forms by which either may be indicated. Uses some of
- the similar macros from the conference service above */
-
-#define PLACE_TO_ID(place) (GPOINTER_TO_INT(place))
-#define ID_TO_PLACE(pd, id) (place_find_by_id((pd), (id)))
-
-#define CHAT_TO_PLACE(pd, chat) (ID_TO_PLACE((pd), CHAT_TO_ID(chat)))
-#define PLACE_TO_CHAT(place) (ID_TO_CHAT(PLACE_TO_ID(place)))
-
-
-static struct mwPlace *
-place_find_by_id(struct mwPurpleProtocolData *pd, int id) {
- struct mwServicePlace *srvc = pd->srvc_place;
- struct mwPlace *place = NULL;
- GList *l;
-
- l = (GList *) mwServicePlace_getPlaces(srvc);
- for(; l; l = l->next) {
- struct mwPlace *p = l->data;
- PurpleChatConversation *h = mwPlace_getClientData(p);
-
- if(purple_chat_conversation_get_id(h) == id) {
- place = p;
- break;
- }
- }
-
- return place;
-}
-
-
-static void mw_place_opened(struct mwPlace *place) {
- struct mwServicePlace *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- PurpleConversation *gconf;
-
- GList *members, *l;
-
- const char *n = mwPlace_getName(place);
- const char *t = mwPlace_getTitle(place);
-
- srvc = mwPlace_getService(place);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- members = mwPlace_getMembers(place);
-
- DEBUG_INFO("place %s opened, %u initial members\n",
- n, g_list_length(members));
-
- if(! t) t = "(no title)";
- gconf = purple_serv_got_joined_chat(gc, PLACE_TO_ID(place), t);
-
- mwPlace_setClientData(place, gconf, NULL);
-
- for(l = members; l; l = l->next) {
- struct mwIdBlock *idb = l->data;
- purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(gconf), idb->user,
- NULL, PURPLE_CHAT_USER_NONE, FALSE);
- }
- g_list_free(members);
-}
-
-
-static void mw_place_closed(struct mwPlace *place, guint32 code) {
- struct mwServicePlace *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
-
- const char *n = mwPlace_getName(place);
- char *msg = mwError(code);
-
- DEBUG_INFO("place %s closed, 0x%08x\n", n, code);
-
- srvc = mwPlace_getService(place);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- purple_serv_got_chat_left(gc, PLACE_TO_ID(place));
-
- purple_notify_error(gc, _("Place Closed"), NULL, msg,
- purple_request_cpar_from_connection(gc));
- g_free(msg);
-}
-
-
-static void mw_place_peerJoined(struct mwPlace *place,
- const struct mwIdBlock *peer) {
- PurpleChatConversation *gconf;
-
- const char *n = mwPlace_getName(place);
-
- DEBUG_INFO("%s joined place %s\n", peer->user, n);
-
- gconf = mwPlace_getClientData(place);
- g_return_if_fail(gconf != NULL);
-
- purple_chat_conversation_add_user(gconf, peer->user,
- NULL, PURPLE_CHAT_USER_NONE, TRUE);
-}
-
-
-static void mw_place_peerParted(struct mwPlace *place,
- const struct mwIdBlock *peer) {
- PurpleChatConversation *gconf;
-
- const char *n = mwPlace_getName(place);
-
- DEBUG_INFO("%s left place %s\n", peer->user, n);
-
- gconf = mwPlace_getClientData(place);
- g_return_if_fail(gconf != NULL);
-
- purple_chat_conversation_remove_user(gconf, peer->user, NULL);
-}
-
-
-static void mw_place_peerSetAttribute(struct mwPlace *place,
- const struct mwIdBlock *peer,
- guint32 attr, struct mwOpaque *o) {
- ;
-}
-
-
-static void mw_place_peerUnsetAttribute(struct mwPlace *place,
- const struct mwIdBlock *peer,
- guint32 attr) {
- ;
-}
-
-
-static void mw_place_message(struct mwPlace *place,
- const struct mwIdBlock *who,
- const char *msg) {
- struct mwServicePlace *srvc;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
- char *esc;
-
- if(! msg) return;
-
- srvc = mwPlace_getService(place);
- session = mwService_getSession(MW_SERVICE(srvc));
- pd = mwSession_getClientData(session);
- gc = pd->gc;
-
- esc = g_markup_escape_text(msg, -1);
- purple_serv_got_chat_in(gc, PLACE_TO_ID(place), who->user,
- PURPLE_MESSAGE_RECV, esc, time(NULL));
- g_free(esc);
-}
-
-
-static void mw_place_clear(struct mwServicePlace *srvc) {
- ;
-}
-
-
-static struct mwPlaceHandler mw_place_handler = {
- mw_place_opened,
- mw_place_closed,
- mw_place_peerJoined,
- mw_place_peerParted,
- mw_place_peerSetAttribute,
- mw_place_peerUnsetAttribute,
- mw_place_message,
- mw_place_clear,
-};
-
-
-static struct mwServicePlace *mw_srvc_place_new(struct mwSession *s) {
- struct mwServicePlace *srvc;
- srvc = mwServicePlace_new(s, &mw_place_handler);
- return srvc;
-}
-
-
-static struct mwServiceResolve *mw_srvc_resolve_new(struct mwSession *s) {
- struct mwServiceResolve *srvc;
- srvc = mwServiceResolve_new(s);
- return srvc;
-}
-
-
-static struct mwServiceStorage *mw_srvc_store_new(struct mwSession *s) {
- struct mwServiceStorage *srvc;
- srvc = mwServiceStorage_new(s);
- return srvc;
-}
-
-
-/** allocate and associate a mwPurpleProtocolData with a PurpleConnection */
-static struct mwPurpleProtocolData *mwPurpleProtocolData_new(PurpleConnection *gc) {
- struct mwPurpleProtocolData *pd;
-
- g_return_val_if_fail(gc != NULL, NULL);
-
- pd = g_new0(struct mwPurpleProtocolData, 1);
- pd->gc = gc;
- pd->cancellable = g_cancellable_new();
- pd->session = mwSession_new(&mw_session_handler);
- pd->srvc_aware = mw_srvc_aware_new(pd->session);
- pd->srvc_conf = mw_srvc_conf_new(pd->session);
- pd->srvc_ft = mw_srvc_ft_new(pd->session);
- pd->srvc_im = mw_srvc_im_new(pd->session);
- pd->srvc_place = mw_srvc_place_new(pd->session);
- pd->srvc_resolve = mw_srvc_resolve_new(pd->session);
- pd->srvc_store = mw_srvc_store_new(pd->session);
- pd->group_list_map = g_hash_table_new(g_direct_hash, g_direct_equal);
-
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_aware));
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_conf));
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_ft));
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_im));
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_place));
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_resolve));
- mwSession_addService(pd->session, MW_SERVICE(pd->srvc_store));
-
- mwSession_addCipher(pd->session, mwCipher_new_RC2_40(pd->session));
- mwSession_addCipher(pd->session, mwCipher_new_RC2_128(pd->session));
-
- mwSession_setClientData(pd->session, pd, NULL);
- purple_connection_set_protocol_data(gc, pd);
-
- return pd;
-}
-
-
-static void mwPurpleProtocolData_free(struct mwPurpleProtocolData *pd) {
- g_return_if_fail(pd != NULL);
-
- purple_connection_set_protocol_data(pd->gc, NULL);
-
- mwSession_removeService(pd->session, mwService_AWARE);
- mwSession_removeService(pd->session, mwService_CONFERENCE);
- mwSession_removeService(pd->session, mwService_FILE_TRANSFER);
- mwSession_removeService(pd->session, mwService_IM);
- mwSession_removeService(pd->session, mwService_PLACE);
- mwSession_removeService(pd->session, mwService_RESOLVE);
- mwSession_removeService(pd->session, mwService_STORAGE);
-
- mwService_free(MW_SERVICE(pd->srvc_aware));
- mwService_free(MW_SERVICE(pd->srvc_conf));
- mwService_free(MW_SERVICE(pd->srvc_ft));
- mwService_free(MW_SERVICE(pd->srvc_im));
- mwService_free(MW_SERVICE(pd->srvc_place));
- mwService_free(MW_SERVICE(pd->srvc_resolve));
- mwService_free(MW_SERVICE(pd->srvc_store));
-
- mwCipher_free(mwSession_getCipher(pd->session, mwCipher_RC2_40));
- mwCipher_free(mwSession_getCipher(pd->session, mwCipher_RC2_128));
-
- mwSession_free(pd->session);
-
- g_clear_object(&pd->cancellable);
- g_hash_table_destroy(pd->group_list_map);
- g_clear_object(&pd->input);
- g_clear_object(&pd->output);
- g_clear_object(&pd->stream);
-
- g_free(pd);
-}
-
-static GList *
-mw_protocol_get_account_options(PurpleProtocol *protocol) {
- PurpleAccountOption *opt;
- GList *opts = NULL;
-
- /* port to connect to */
- opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT,
- MW_PLUGIN_DEFAULT_PORT);
- opts = g_list_append(opts, opt);
-
- { /* copy the old force login setting from prefs if it's
- there. Don't delete the preference, since there may be more
- than one account that wants to check for it. */
- gboolean b = FALSE;
- const gchar *label = _("Force login (ignore server redirects)");
-
- if (purple_prefs_exists(MW_PROTOCOL_OPT_FORCE_LOGIN))
- b = purple_prefs_get_bool(MW_PROTOCOL_OPT_FORCE_LOGIN);
-
- opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b);
- opts = g_list_append(opts, opt);
- }
-
- /* pretend to be Sametime Connect */
- opt = purple_account_option_bool_new(_("Hide client identity"),
- MW_KEY_FAKE_IT, FALSE);
- opts = g_list_append(opts, opt);
-
- return opts;
-}
-
-static GList *
-mw_protocol_get_user_splits(PurpleProtocol *protocol) {
- PurpleAccountUserSplit *split;
- GList *splits = NULL;
-
- /* set up account ID as user:server */
- split = purple_account_user_split_new(_("Server"),
- MW_PLUGIN_DEFAULT_HOST, ':');
- splits = g_list_append(splits, split);
-
- return splits;
-}
-
-static const char *
-mw_protocol_list_emblem(PurpleProtocolClient *client, PurpleBuddy *b) {
- if(buddy_is_external(b)) {
- return "external";
- }
-
- return NULL;
-}
-
-
-static char *
-mw_protocol_status_text(PurpleProtocolClient * client, PurpleBuddy *b) {
- PurpleConnection *gc;
- struct mwPurpleProtocolData *pd;
- struct mwAwareIdBlock t = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL };
- const char *ret = NULL;
-
- if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
- && (pd = purple_connection_get_protocol_data(gc)))
- ret = mwServiceAware_getText(pd->srvc_aware, &t);
-
- return (ret && g_utf8_validate(ret, -1, NULL)) ? g_markup_escape_text(ret, -1): NULL;
-}
-
-
-static const char *status_text(PurpleBuddy *b) {
- PurplePresence *presence;
- PurpleStatus *status;
-
- presence = purple_buddy_get_presence(b);
- status = purple_presence_get_active_status(presence);
-
- return purple_status_get_name(status);
-}
-
-
-static gboolean user_supports(struct mwServiceAware *srvc,
- const char *who, guint32 feature) {
-
- const struct mwAwareAttribute *attr;
- struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL };
-
- attr = mwServiceAware_getAttribute(srvc, &idb, feature);
- return (attr != NULL) && mwAwareAttribute_asBoolean(attr);
-}
-
-
-static char *user_supports_text(struct mwServiceAware *srvc, const char *who) {
- const char *feat[] = {NULL, NULL, NULL, NULL, NULL};
- const char **f = feat;
-
- if(user_supports(srvc, who, mwAttribute_AV_PREFS_SET)) {
- gboolean mic, speak, video;
-
- mic = user_supports(srvc, who, mwAttribute_MICROPHONE);
- speak = user_supports(srvc, who, mwAttribute_SPEAKERS);
- video = user_supports(srvc, who, mwAttribute_VIDEO_CAMERA);
-
- if(mic) *f++ = _("Microphone");
- if(speak) *f++ = _("Speakers");
- if(video) *f++ = _("Video Camera");
- }
-
- if(user_supports(srvc, who, mwAttribute_FILE_TRANSFER))
- *f++ = _("File Transfer");
-
- return (*feat)? g_strjoinv(", ", (char **)feat): NULL;
- /* jenni loves siege */
-}
-
-
-static void
-mw_protocol_tooltip_text(PurpleProtocolClient *client, PurpleBuddy *b,
- PurpleNotifyUserInfo *user_info, gboolean full)
-{
- PurpleConnection *gc;
- struct mwPurpleProtocolData *pd = NULL;
- struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL };
-
- const char *message = NULL;
- const char *status;
- char *tmp;
-
- if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
- && (pd = purple_connection_get_protocol_data(gc)))
- message = mwServiceAware_getText(pd->srvc_aware, &idb);
-
- status = status_text(b);
-
- if(message != NULL && g_utf8_validate(message, -1, NULL) && purple_utf8_strcasecmp(status, message)) {
- purple_notify_user_info_add_pair_plaintext(user_info, status, message);
-
- } else {
- purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status);
- }
-
- if(full && pd != NULL) {
- tmp = user_supports_text(pd->srvc_aware, purple_buddy_get_name(b));
- if(tmp) {
- purple_notify_user_info_add_pair_plaintext(user_info, _("Supports"), tmp);
- g_free(tmp);
- }
-
- if(buddy_is_external(b)) {
- purple_notify_user_info_add_pair_plaintext(user_info, NULL, _("External User"));
- }
- }
-}
-
-static GList *
-mw_protocol_status_types(G_GNUC_UNUSED PurpleProtocol *protocol,
- PurpleAccount *acct)
-{
- GList *types = NULL;
- PurpleStatusType *type;
-
- type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
- MW_STATE_ACTIVE, NULL, TRUE, TRUE, FALSE,
- MW_STATE_MESSAGE, _("Message"), purple_value_new(G_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
- MW_STATE_AWAY, NULL, TRUE, TRUE, FALSE,
- MW_STATE_MESSAGE, _("Message"), purple_value_new(G_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
- MW_STATE_BUSY, _("Do Not Disturb"), TRUE, TRUE, FALSE,
- MW_STATE_MESSAGE, _("Message"), purple_value_new(G_TYPE_STRING),
- NULL);
- types = g_list_append(types, type);
-
- type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
- MW_STATE_OFFLINE, NULL, TRUE, TRUE, FALSE);
- types = g_list_append(types, type);
-
- return types;
-}
-
-
-static void conf_create_prompt_cancel(PurpleBuddy *buddy,
- PurpleRequestFields *fields) {
- ; /* nothing to do */
-}
-
-
-static void conf_create_prompt_join(PurpleBuddy *buddy,
- PurpleRequestFields *fields) {
- PurpleAccount *acct;
- PurpleConnection *gc;
- struct mwPurpleProtocolData *pd;
- struct mwServiceConference *srvc;
-
- PurpleRequestField *f;
-
- const char *topic, *invite;
- struct mwConference *conf;
- struct mwIdBlock idb = { NULL, NULL };
-
- acct = purple_buddy_get_account(buddy);
- gc = purple_account_get_connection(acct);
- pd = purple_connection_get_protocol_data(gc);
- srvc = pd->srvc_conf;
-
- f = purple_request_fields_get_field(fields, CHAT_KEY_TOPIC);
- topic = purple_request_field_string_get_value(f);
-
- f = purple_request_fields_get_field(fields, CHAT_KEY_INVITE);
- invite = purple_request_field_string_get_value(f);
-
- conf = mwConference_new(srvc, topic);
- mwConference_open(conf);
-
- idb.user = (char *)purple_buddy_get_name(buddy);
- mwConference_invite(conf, &idb, invite);
-}
-
-
-static void blist_menu_conf_create(PurpleBuddy *buddy, const char *msg) {
-
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
-
- PurpleAccount *acct;
- PurpleConnection *gc;
-
- const char *msgA;
- const char *msgB;
- char *msg1;
-
- g_return_if_fail(buddy != NULL);
-
- acct = purple_buddy_get_account(buddy);
- g_return_if_fail(acct != NULL);
-
- gc = purple_account_get_connection(acct);
- g_return_if_fail(gc != NULL);
-
- fields = purple_request_fields_new();
-
- g = purple_request_field_group_new(NULL);
- purple_request_fields_add_group(fields, g);
-
- f = purple_request_field_string_new(CHAT_KEY_TOPIC, _("Topic"), NULL, FALSE);
- purple_request_field_group_add_field(g, f);
-
- f = purple_request_field_string_new(CHAT_KEY_INVITE, _("Message"), msg, FALSE);
- purple_request_field_group_add_field(g, f);
-
- msgA = _("Create conference with user");
- msgB = _("Please enter a topic for the new conference, and an invitation"
- " message to be sent to %s");
- msg1 = g_strdup_printf(msgB, purple_buddy_get_name(buddy));
-
- purple_request_fields(gc, _("New Conference"),
- msgA, msg1, fields,
- _("Create"), G_CALLBACK(conf_create_prompt_join),
- _("Cancel"), G_CALLBACK(conf_create_prompt_cancel),
- purple_request_cpar_from_account(acct),
- buddy);
- g_free(msg1);
-}
-
-
-static void conf_select_prompt_cancel(PurpleBuddy *buddy,
- PurpleRequestFields *fields) {
- ;
-}
-
-
-static void conf_select_prompt_invite(PurpleBuddy *buddy,
- PurpleRequestFields *fields) {
- PurpleRequestField *f;
- GList *l;
- const char *msg;
-
- f = purple_request_fields_get_field(fields, CHAT_KEY_INVITE);
- msg = purple_request_field_string_get_value(f);
-
- f = purple_request_fields_get_field(fields, "conf");
- l = purple_request_field_list_get_selected(f);
-
- if(l) {
- gpointer d = purple_request_field_list_get_data(f, l->data);
-
- if(GPOINTER_TO_INT(d) == 0x01) {
- blist_menu_conf_create(buddy, msg);
-
- } else {
- struct mwIdBlock idb = { (char *)purple_buddy_get_name(buddy), NULL };
- mwConference_invite(d, &idb, msg);
- }
- }
-}
-
-
-static void blist_menu_conf_list(PurpleBuddy *buddy,
- GList *confs) {
-
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
-
- PurpleAccount *acct;
- PurpleConnection *gc;
-
- const char *msgA;
- const char *msgB;
- char *msg;
-
- acct = purple_buddy_get_account(buddy);
- g_return_if_fail(acct != NULL);
-
- gc = purple_account_get_connection(acct);
- g_return_if_fail(gc != NULL);
-
- fields = purple_request_fields_new();
-
- g = purple_request_field_group_new(NULL);
- purple_request_fields_add_group(fields, g);
-
- f = purple_request_field_list_new("conf", _("Available Conferences"));
- purple_request_field_list_set_multi_select(f, FALSE);
- for(; confs; confs = confs->next) {
- struct mwConference *c = confs->data;
- purple_request_field_list_add_icon(f, mwConference_getTitle(c), NULL, c);
- }
- purple_request_field_list_add_icon(f, _("Create New Conference..."),
- NULL, GINT_TO_POINTER(0x01));
- purple_request_field_group_add_field(g, f);
-
- f = purple_request_field_string_new(CHAT_KEY_INVITE, "Message", NULL, FALSE);
- purple_request_field_group_add_field(g, f);
-
- msgA = _("Invite user to a conference");
- msgB = _("Select a conference from the list below to send an invite to"
- " user %s. Select \"Create New Conference\" if you'd like to"
- " create a new conference to invite this user to.");
- msg = g_strdup_printf(msgB, purple_buddy_get_name(buddy));
-
- purple_request_fields(gc, _("Invite to Conference"),
- msgA, msg, fields,
- _("Invite"), G_CALLBACK(conf_select_prompt_invite),
- _("Cancel"), G_CALLBACK(conf_select_prompt_cancel),
- purple_request_cpar_from_account(acct),
- buddy);
- g_free(msg);
-}
-
-
-static void blist_menu_conf(PurpleBlistNode *node, gpointer data) {
- PurpleBuddy *buddy = (PurpleBuddy *) node;
- PurpleAccount *acct;
- PurpleConnection *gc;
- struct mwPurpleProtocolData *pd;
- GList *l;
-
- g_return_if_fail(node != NULL);
- g_return_if_fail(PURPLE_IS_BUDDY(node));
-
- acct = purple_buddy_get_account(buddy);
- g_return_if_fail(acct != NULL);
-
- gc = purple_account_get_connection(acct);
- g_return_if_fail(gc != NULL);
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
-
- /*
- - get a list of all conferences on this session
- - if none, prompt to create one, and invite buddy to it
- - else, prompt to select a conference or create one
- */
-
- l = mwServiceConference_getConferences(pd->srvc_conf);
- if(l) {
- blist_menu_conf_list(buddy, l);
- g_list_free(l);
-
- } else {
- blist_menu_conf_create(buddy, NULL);
- }
-}
-
-
-static GList *
-mw_protocol_blist_node_menu(PurpleProtocolClient *client,
- PurpleBlistNode *node)
-{
- GList *l = NULL;
- PurpleActionMenu *act;
-
- if(! PURPLE_IS_BUDDY(node))
- return l;
-
- l = g_list_append(l, NULL);
-
- act = purple_action_menu_new(_("Invite to Conference..."),
- G_CALLBACK(blist_menu_conf), NULL, NULL);
- l = g_list_append(l, act);
-
- /** note: this never gets called for a PurpleGroup, have to use the
- blist-node-extended-menu signal for that. The function
- blist_node_menu_cb is assigned to this signal in the function
- services_starting */
-
- return l;
-}
-
-
-static GList *
-mw_protocol_chat_info(PurpleProtocolChat *protocol_chat,
- PurpleConnection *gc)
-{
- GList *l = NULL;
- PurpleProtocolChatEntry *pce;
-
- pce = g_new0(PurpleProtocolChatEntry, 1);
- pce->label = _("Topic:");
- pce->identifier = CHAT_KEY_TOPIC;
- l = g_list_append(l, pce);
-
- return l;
-}
-
-
-static GHashTable *
-mw_protocol_chat_info_defaults(PurpleProtocolChat *protocol_chat,
- PurpleConnection *gc,
- const char *name)
-{
- GHashTable *table;
-
- g_return_val_if_fail(gc != NULL, NULL);
-
- table = g_hash_table_new_full(g_str_hash, g_str_equal,
- NULL, g_free);
-
- g_hash_table_insert(table, CHAT_KEY_NAME, g_strdup(name));
- g_hash_table_insert(table, CHAT_KEY_INVITE, NULL);
-
- return table;
-}
-
-
-static void
-mw_protocol_login(G_GNUC_UNUSED PurpleProtocol *protocol,
- PurpleAccount *account)
-{
- PurpleConnection *gc;
- struct mwPurpleProtocolData *pd;
-
- char *user, *pass, *host;
- guint port;
-
- GError *error = NULL;
-
- gc = purple_account_get_connection(account);
- pd = mwPurpleProtocolData_new(gc);
-
- /* while we do support images, the default is to not offer it */
- purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
-
- user = g_strdup(purple_account_get_username(account));
-
- host = strrchr(user, ':');
- if(host) {
- /* annoying user split from 1.2.0, need to undo it */
- *host++ = '\0';
- purple_account_set_string(account, MW_KEY_HOST, host);
- purple_account_set_username(account, user);
-
- } else {
- host = (char *) purple_account_get_string(account, MW_KEY_HOST,
- MW_PLUGIN_DEFAULT_HOST);
- }
-
- if(! host || ! *host) {
- /* somehow, we don't have a host to connect to. Well, we need one
- to actually continue, so let's ask the user directly. */
- g_free(user);
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
- _("A server is required to connect this account"));
- return;
- }
-
- pass = g_strdup(purple_connection_get_password(gc));
- port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT);
-
- DEBUG_INFO("user: '%s'\n", user);
- DEBUG_INFO("host: '%s'\n", host);
- DEBUG_INFO("port: %u\n", port);
-
- mwSession_setProperty(pd->session, mwSession_NO_SECRET,
- (char *) no_secret, NULL);
- mwSession_setProperty(pd->session, mwSession_AUTH_USER_ID, user, g_free);
- mwSession_setProperty(pd->session, mwSession_AUTH_PASSWORD, pass, g_free);
-
- if(purple_account_get_bool(account, MW_KEY_FAKE_IT, FALSE)) {
- guint client, major, minor;
-
- /* if we're faking the login, let's also fake the version we're
- reporting. Let's also allow the actual values to be specified */
-
- client = purple_account_get_int(account, MW_KEY_CLIENT, mwLogin_BINARY);
- major = purple_account_get_int(account, MW_KEY_MAJOR, 0x001e);
- minor = purple_account_get_int(account, MW_KEY_MINOR, 0x196f);
-
- DEBUG_INFO("client id: 0x%04x\n", client);
- DEBUG_INFO("client major: 0x%04x\n", major);
- DEBUG_INFO("client minor: 0x%04x\n", minor);
-
- mwSession_setProperty(pd->session, mwSession_CLIENT_TYPE_ID,
- GUINT_TO_POINTER(client), NULL);
-
- mwSession_setProperty(pd->session, mwSession_CLIENT_VER_MAJOR,
- GUINT_TO_POINTER(major), NULL);
-
- mwSession_setProperty(pd->session, mwSession_CLIENT_VER_MINOR,
- GUINT_TO_POINTER(minor), NULL);
- }
-
- pd->client = purple_gio_socket_client_new(account, &error);
- if (pd->client == NULL) {
- purple_connection_take_error(gc, error);
- return;
- }
-
- g_socket_client_connect_to_host_async(
- pd->client, host,
- port, pd->cancellable,
- connect_cb, pd);
-}
-
-
-static void
-mw_protocol_close(G_GNUC_UNUSED PurpleProtocol *protocol,
- PurpleConnection *gc)
-{
- struct mwPurpleProtocolData *pd;
-
- g_return_if_fail(gc != NULL);
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
-
- /* get rid of the blist save timeout */
- if(pd->save_event) {
- g_source_remove(pd->save_event);
- pd->save_event = 0;
- blist_store(pd);
- }
-
- /* stop the session */
- mwSession_stop(pd->session, 0x00);
-
- /* no longer necessary */
- purple_connection_set_protocol_data(gc, NULL);
-
- /* stop watching the socket */
- if(pd->inpa) {
- g_source_remove(pd->inpa);
- pd->inpa = 0;
- }
-
- /* clean up the rest */
- mwPurpleProtocolData_free(pd);
-}
-
-
-static int mw_protocol_send_im(PurpleProtocolIM *im, PurpleConnection *gc,
- PurpleMessage *msg)
-{
- gchar name[1000];
- struct mwPurpleProtocolData *pd;
- struct mwIdBlock who = { name, NULL };
- struct mwConversation *conv;
- const gchar *message;
- PurpleMessageFlags flags;
-
- g_return_val_if_fail(gc != NULL, 0);
- pd = purple_connection_get_protocol_data(gc);
-
- g_return_val_if_fail(pd != NULL, 0);
-
- g_strlcpy(name, purple_message_get_recipient(msg), sizeof(name));
- message = purple_message_get_contents(msg);
- flags = purple_message_get_flags(msg);
-
- conv = mwServiceIm_getConversation(pd->srvc_im, &who);
-
- /* this detection of features to determine how to send the message
- (plain, html, or mime) is flawed because the other end of the
- conversation could close their channel at any time, rendering any
- existing formatting in an outgoing message innapropriate. The end
- result is that it may be possible that the other side of the
- conversation will receive a plaintext message with html contents,
- which is bad. I'm not sure how to fix this correctly. */
-
- if(strstr(message, "<img ") || strstr(message, "<IMG "))
- flags |= PURPLE_MESSAGE_IMAGES;
-
- if(mwConversation_isOpen(conv)) {
- char *tmp;
- int ret;
-
- if((flags & PURPLE_MESSAGE_IMAGES) &&
- mwConversation_supports(conv, mwImSend_MIME)) {
- /* send a MIME message */
-
- tmp = im_mime_generate(message);
- ret = mwConversation_send(conv, mwImSend_MIME, tmp);
- g_free(tmp);
-
- } else if(mwConversation_supports(conv, mwImSend_HTML)) {
- /* send an HTML message */
-
- char *ncr;
- ncr = purple_utf8_ncr_encode(message);
- tmp = purple_strdup_withhtml(ncr);
- g_free(ncr);
-
- ret = mwConversation_send(conv, mwImSend_HTML, tmp);
- g_free(tmp);
-
- } else {
- /* default to text */
- tmp = purple_markup_strip_html(message);
- ret = mwConversation_send(conv, mwImSend_PLAIN, tmp);
- g_free(tmp);
- }
-
- return !ret;
-
- } else {
-
- /* queue up the message safely as plain text */
- char *tmp = purple_markup_strip_html(message);
- convo_queue(conv, mwImSend_PLAIN, tmp);
- g_free(tmp);
-
- if(! mwConversation_isPending(conv))
- mwConversation_open(conv);
-
- return 1;
- }
-}
-
-
-static unsigned int mw_protocol_send_typing(PurpleProtocolIM *im,
- PurpleConnection *gc,
- const char *name,
- PurpleIMTypingState state) {
-
- struct mwPurpleProtocolData *pd;
- struct mwIdBlock who = { (char *) name, NULL };
- struct mwConversation *conv;
-
- gpointer t = GINT_TO_POINTER(!! state);
-
- g_return_val_if_fail(gc != NULL, 0);
- pd = purple_connection_get_protocol_data(gc);
-
- g_return_val_if_fail(pd != NULL, 0);
-
- conv = mwServiceIm_getConversation(pd->srvc_im, &who);
-
- if(mwConversation_isOpen(conv)) {
- mwConversation_send(conv, mwImSend_TYPING, t);
-
- } else if((state == PURPLE_IM_TYPING) || (state == PURPLE_IM_TYPED)) {
- /* only open a channel for sending typing notification, not for
- when typing has stopped. There's no point in re-opening a
- channel just to tell someone that this side isn't typing. */
-
- convo_queue(conv, mwImSend_TYPING, t);
-
- if(! mwConversation_isPending(conv)) {
- mwConversation_open(conv);
- }
- }
-
- return 0;
-}
-
-
-static const char *mw_client_name(guint16 type) {
- switch(type) {
- case mwLogin_LIB:
- return "Lotus Binary Library";
-
- case mwLogin_JAVA_WEB:
- return "Lotus Java Client Applet";
-
- case mwLogin_BINARY:
- return "Lotus Sametime Connect";
-
- case mwLogin_JAVA_APP:
- return "Lotus Java Client Application";
-
- case mwLogin_LINKS:
- return "Lotus Sametime Links";
-
- case mwLogin_NOTES_6_5:
- case mwLogin_NOTES_6_5_3:
- case mwLogin_NOTES_7_0_beta:
- case mwLogin_NOTES_7_0:
- return "Lotus Notes Client";
-
- case mwLogin_ICT:
- case mwLogin_ICT_1_7_8_2:
- case mwLogin_ICT_SIP:
- return "IBM Community Tools";
-
- case mwLogin_NOTESBUDDY_4_14:
- case mwLogin_NOTESBUDDY_4_15:
- case mwLogin_NOTESBUDDY_4_16:
- return "Alphaworks NotesBuddy";
-
- case 0x1305:
- case 0x1306:
- case 0x1307:
- return "Lotus Sametime Connect 7.5";
-
- case mwLogin_SANITY:
- return "Sanity";
-
- case mwLogin_ST_PERL:
- return "ST-Send-Message";
-
- case mwLogin_TRILLIAN:
- case mwLogin_TRILLIAN_IBM:
- return "Trillian";
-
- case mwLogin_MEANWHILE:
- return "Meanwhile";
-
- default:
- return NULL;
- }
-}
-
-
-static void
-mw_protocol_get_info(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, const gchar *who)
-{
- struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL };
-
- struct mwPurpleProtocolData *pd;
- PurpleAccount *acct;
- PurpleBuddy *b;
- PurpleNotifyUserInfo *user_info;
- char *tmp;
- const char *tmp2;
-
- g_return_if_fail(who != NULL);
- g_return_if_fail(*who != '\0');
-
- pd = purple_connection_get_protocol_data(gc);
-
- acct = purple_connection_get_account(gc);
- b = purple_blist_find_buddy(acct, who);
- user_info = purple_notify_user_info_new();
-
- if (g_str_has_prefix(who, "@E ")) {
- purple_notify_user_info_add_pair_html(user_info, _("External User"),
- NULL);
- }
-
- purple_notify_user_info_add_pair_plaintext(user_info, _("User ID"), who);
-
- if(b) {
- guint32 type;
-
- if(purple_buddy_get_server_alias(b)) {
- /* TODO: Check whether it's correct to call add_pair_html,
- or if we should be using add_pair_plaintext */
- purple_notify_user_info_add_pair_html(user_info, _("Full Name"), purple_buddy_get_server_alias(b));
- }
-
- type = purple_blist_node_get_int((PurpleBlistNode *) b, BUDDY_KEY_CLIENT);
- if(type) {
- tmp2 = mw_client_name(type);
- if (tmp2) {
- purple_notify_user_info_add_pair_plaintext(user_info, _("Last Known Client"), tmp2);
- } else {
- tmp = g_strdup_printf(_("Unknown (0x%04x)<br>"), type);
- purple_notify_user_info_add_pair_html(user_info, _("Last Known Client"), tmp);
- g_free(tmp);
- }
- }
- }
-
- tmp = user_supports_text(pd->srvc_aware, who);
- if(tmp) {
- purple_notify_user_info_add_pair_plaintext(user_info, _("Supports"), tmp);
- g_free(tmp);
- }
-
- if(b) {
- purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_text(b));
-
- /* XXX Is this adding a status message in its own section rather than with the "Status" label? */
- tmp2 = mwServiceAware_getText(pd->srvc_aware, &idb);
- if(tmp2 && g_utf8_validate(tmp2, -1, NULL)) {
- purple_notify_user_info_add_section_break(user_info);
- purple_notify_user_info_add_pair_plaintext(user_info, NULL, tmp2);
- }
- }
-
- /* @todo emit a signal to allow a plugin to override the display of
- this notification, so that it can create its own */
-
- purple_notify_userinfo(gc, who, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
-}
-
-
-static void
-mw_protocol_set_status(PurpleProtocolServer *protocol_server,
- PurpleAccount *acct, PurpleStatus *status)
-{
- PurpleConnection *gc;
- const char *state;
- char *message = NULL;
- struct mwSession *session;
- struct mwUserStatus stat;
-
- g_return_if_fail(acct != NULL);
- gc = purple_account_get_connection(acct);
-
- state = purple_status_get_id(status);
-
- DEBUG_INFO("Set status to %s\n", purple_status_get_name(status));
-
- g_return_if_fail(gc != NULL);
-
- session = gc_to_session(gc);
- g_return_if_fail(session != NULL);
-
- /* get a working copy of the current status */
- mwUserStatus_clone(&stat, mwSession_getUserStatus(session));
-
- /* determine the state */
- if(purple_strequal(state, MW_STATE_ACTIVE)) {
- stat.status = mwStatus_ACTIVE;
-
- } else if(purple_strequal(state, MW_STATE_AWAY)) {
- stat.status = mwStatus_AWAY;
-
- } else if(purple_strequal(state, MW_STATE_BUSY)) {
- stat.status = mwStatus_BUSY;
- }
-
- /* determine the message */
- message = (char *) purple_status_get_attr_string(status, MW_STATE_MESSAGE);
-
- if(message) {
- /* all the possible non-NULL values of message up to this point
- are const, so we don't need to free them */
- message = purple_markup_strip_html(message);
- }
-
- /* out with the old */
- g_free(stat.desc);
-
- /* in with the new */
- stat.desc = (char *) message;
-
- mwSession_setUserStatus(session, &stat);
- mwUserStatus_clear(&stat);
-}
-
-
-static void
-mw_protocol_set_idle(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, gint t)
-{
- struct mwSession *session;
- struct mwUserStatus stat;
-
-
- session = gc_to_session(gc);
- g_return_if_fail(session != NULL);
-
- mwUserStatus_clone(&stat, mwSession_getUserStatus(session));
-
- if(t) {
- time_t now = time(NULL);
- stat.time = now - t;
-
- } else {
- stat.time = 0;
- }
-
- if(t > 0 && stat.status == mwStatus_ACTIVE) {
- /* we were active and went idle, so change the status to IDLE. */
- stat.status = mwStatus_IDLE;
-
- } else if(t == 0 && stat.status == mwStatus_IDLE) {
- /* we only become idle automatically, so change back to ACTIVE */
- stat.status = mwStatus_ACTIVE;
- }
-
- mwSession_setUserStatus(session, &stat);
- mwUserStatus_clear(&stat);
-}
-
-
-static void notify_im(PurpleConnection *gc, GList *row, void *user_data) {
- PurpleAccount *acct;
- PurpleConversation *im;
- PurpleConversationManager *manager;
- char *id;
-
- acct = purple_connection_get_account(gc);
- id = g_list_nth_data(row, 1);
-
- manager = purple_conversation_manager_get_default();
- im = purple_conversation_manager_find_im(manager, acct, id);
- if(!PURPLE_IS_IM_CONVERSATION(im)) {
- im = purple_im_conversation_new(acct, id);
- }
- purple_conversation_present(im);
-}
-
-
-static void notify_add(PurpleConnection *gc, GList *row, void *user_data) {
- BuddyAddData *data = user_data;
- const char *group_name = NULL;
-
- if (data && data->group) {
- group_name = purple_group_get_name(data->group);
- }
-
- purple_blist_request_add_buddy(purple_connection_get_account(gc),
- g_list_nth_data(row, 1), group_name,
- g_list_nth_data(row, 0));
-}
-
-
-static void notify_close(gpointer data) {
- g_free(data);
-}
-
-
-static void multi_resolved_query(struct mwResolveResult *result,
- PurpleConnection *gc, gpointer data) {
- GList *l;
- const char *msgA;
- const char *msgB;
- char *msg;
-
- PurpleNotifySearchResults *sres;
- PurpleNotifySearchColumn *scol;
-
- sres = purple_notify_searchresults_new();
-
- scol = purple_notify_searchresults_column_new(_("User Name"));
- purple_notify_searchresults_column_add(sres, scol);
-
- scol = purple_notify_searchresults_column_new(_("Sametime ID"));
- purple_notify_searchresults_column_add(sres, scol);
-
- purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_IM,
- notify_im);
-
- purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_ADD,
- notify_add);
-
- for(l = result->matches; l; l = l->next) {
- struct mwResolveMatch *match = l->data;
- GList *row = NULL;
-
- DEBUG_INFO("multi resolve: %s, %s\n",
- match->id, match->name);
-
- if(!match->id || !match->name)
- continue;
-
- row = g_list_append(row, g_strdup(match->name));
- row = g_list_append(row, g_strdup(match->id));
- purple_notify_searchresults_row_add(sres, row);
- }
-
- msgA = _("An ambiguous user ID was entered");
- msgB = _("The identifier '%s' may possibly refer to any of the following"
- " users. Please select the correct user from the list below to"
- " add them to your buddy list.");
- msg = g_strdup_printf(msgB, result->name);
-
- purple_notify_searchresults(gc, _("Select User"),
- msgA, msg, sres, notify_close, data);
-
- g_free(msg);
-}
-
-
-static void add_buddy_resolved(struct mwServiceResolve *srvc,
- guint32 id, guint32 code, GList *results,
- gpointer b) {
-
- struct mwResolveResult *res = NULL;
- BuddyAddData *data = b;
- PurpleBuddy *buddy = NULL;
- PurpleConnection *gc;
- struct mwPurpleProtocolData *pd;
-
- g_return_if_fail(data != NULL);
-
- buddy = data->buddy;
-
- gc = purple_account_get_connection(purple_buddy_get_account(buddy));
- pd = purple_connection_get_protocol_data(gc);
-
- if(results)
- res = results->data;
-
- if(!code && res && res->matches) {
- if(!res->matches->next) {
- struct mwResolveMatch *match = res->matches->data;
-
- /* only one? that might be the right one! */
- if(!purple_strequal(res->name, match->id)) {
- /* uh oh, the single result isn't identical to the search
- term, better safe then sorry, so let's make sure it's who
- the user meant to add */
- purple_blist_remove_buddy(buddy);
- multi_resolved_query(res, gc, data);
-
- } else {
-
- /* same person, set the server alias */
- purple_buddy_set_server_alias(buddy, match->name);
- purple_blist_node_set_string((PurpleBlistNode *) buddy,
- BUDDY_KEY_NAME, match->name);
-
- /* subscribe to awareness */
- buddy_add(pd, buddy);
-
- blist_schedule(pd);
-
- g_free(data);
- }
-
- } else {
- /* prompt user if more than one match was returned */
- purple_blist_remove_buddy(buddy);
- multi_resolved_query(res, gc, data);
- }
-
- return;
- }
-}
-
-
-static void
-mw_protocol_add_buddy(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group, const gchar *message)
-{
- struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc);
- struct mwServiceResolve *srvc;
- GList *query;
- enum mwResolveFlag flags;
- guint32 req;
- BuddyAddData *data;
-
- /* catch external buddies. They won't be in the resolve service */
- if(buddy_is_external(buddy)) {
- buddy_add(pd, buddy);
- return;
- }
-
- data = g_new0(BuddyAddData, 1);
- data->buddy = buddy;
- data->group = group;
-
- srvc = pd->srvc_resolve;
-
- query = g_list_prepend(NULL, (char *)purple_buddy_get_name(buddy));
- flags = mwResolveFlag_FIRST | mwResolveFlag_USERS;
-
- req = mwServiceResolve_resolve(srvc, query, flags, add_buddy_resolved,
- data, NULL);
- g_list_free(query);
-
- if(req == SEARCH_ERROR) {
- purple_blist_remove_buddy(buddy);
- blist_schedule(pd);
- }
-}
-
-
-static void foreach_add_buddies(PurpleGroup *group, GList *buddies,
- struct mwPurpleProtocolData *pd) {
- struct mwAwareList *list;
-
- list = list_ensure(pd, group);
- mwAwareList_addAware(list, buddies);
- g_list_free(buddies);
-}
-
-
-static void
-mw_protocol_add_buddies(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc,
- GList *buddies,
- GList *groups,
- const char *message) {
-
- struct mwPurpleProtocolData *pd;
- GHashTable *group_sets;
- struct mwAwareIdBlock *idbs, *idb;
-
- pd = purple_connection_get_protocol_data(gc);
-
- /* map PurpleGroup:GList of mwAwareIdBlock */
- group_sets = g_hash_table_new(g_direct_hash, g_direct_equal);
-
- /* bunch of mwAwareIdBlock allocated at once, free'd at once */
- idb = idbs = g_new(struct mwAwareIdBlock, g_list_length(buddies));
-
- /* first pass collects mwAwareIdBlock lists for each group */
- for(; buddies; buddies = buddies->next) {
- PurpleBuddy *b = buddies->data;
- PurpleGroup *g;
- const char *fn;
- GList *l;
-
- /* nab the saved server alias and stick it on the buddy */
- fn = purple_blist_node_get_string((PurpleBlistNode *) b, BUDDY_KEY_NAME);
- purple_buddy_set_server_alias(b, fn);
-
- /* convert PurpleBuddy into a mwAwareIdBlock */
- idb->type = mwAware_USER;
- idb->user = (char *) purple_buddy_get_name(b);
- idb->community = NULL;
-
- /* put idb into the list associated with the buddy's group */
- g = purple_buddy_get_group(b);
- l = g_hash_table_lookup(group_sets, g);
- l = g_list_prepend(l, idb++);
- g_hash_table_insert(group_sets, g, l);
- }
-
- /* each group's buddies get added in one shot, and schedule the blist
- for saving */
- g_hash_table_foreach(group_sets, (GHFunc) foreach_add_buddies, pd);
- blist_schedule(pd);
-
- /* cleanup */
- g_hash_table_destroy(group_sets);
- g_free(idbs);
-}
-
-
-static void
-mw_protocol_remove_buddy(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, PurpleBuddy *buddy,
- PurpleGroup *group)
-{
- struct mwPurpleProtocolData *pd;
- struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(buddy), NULL };
- struct mwAwareList *list;
-
- GList *rem = g_list_prepend(NULL, &idb);
-
- pd = purple_connection_get_protocol_data(gc);
- group = purple_buddy_get_group(buddy);
- list = list_ensure(pd, group);
-
- mwAwareList_removeAware(list, rem);
- blist_schedule(pd);
-
- g_list_free(rem);
-}
-
-
-static void privacy_fill(struct mwPrivacyInfo *priv,
- GSList *members) {
-
- struct mwUserItem *u;
- guint count;
-
- count = g_slist_length(members);
- DEBUG_INFO("privacy_fill: %u members\n", count);
-
- priv->count = count;
- priv->users = g_new0(struct mwUserItem, count);
-
- while(count--) {
- u = priv->users + count;
- u->id = members->data;
- members = members->next;
- }
-}
-
-
-static void mw_protocol_set_permit_deny(PurpleProtocolPrivacy *priv,
- PurpleConnection *gc)
-{
- PurpleAccount *acct;
- struct mwPurpleProtocolData *pd;
- struct mwSession *session;
-
- struct mwPrivacyInfo privacy = {
- FALSE, /* deny */
- 0, /* count */
- NULL, /* users */
- };
-
- g_return_if_fail(gc != NULL);
-
- acct = purple_connection_get_account(gc);
- g_return_if_fail(acct != NULL);
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
-
- session = pd->session;
- g_return_if_fail(session != NULL);
-
- switch(purple_account_get_privacy_type(acct)) {
- case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
- DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_DENY_USERS\n");
- privacy_fill(&privacy, purple_account_privacy_get_denied(acct));
- privacy.deny = TRUE;
- break;
-
- case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
- DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL\n");
- privacy.deny = TRUE;
- break;
-
- case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
- DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS\n");
- privacy_fill(&privacy, purple_account_privacy_get_permitted(acct));
- privacy.deny = FALSE;
- break;
-
- case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
- DEBUG_INFO("PURPLE_ACCOUNT_PRIVACY_DENY_ALL\n");
- privacy.deny = FALSE;
- break;
-
- default:
- DEBUG_INFO("acct->perm_deny is 0x%x\n", purple_account_get_privacy_type(acct));
- return;
- }
-
- mwSession_setPrivacyInfo(session, &privacy);
- g_free(privacy.users);
-}
-
-
-static void mw_protocol_add_permit(PurpleProtocolPrivacy *privacy,
- PurpleConnection *gc, const char *name)
-{
- mw_protocol_set_permit_deny(privacy, gc);
-}
-
-
-static void mw_protocol_add_deny(PurpleProtocolPrivacy *privacy,
- PurpleConnection *gc, const char *name)
-{
- mw_protocol_set_permit_deny(privacy, gc);
-}
-
-
-static void mw_protocol_remove_permit(PurpleProtocolPrivacy *privacy,
- PurpleConnection *gc, const char *name)
-{
- mw_protocol_set_permit_deny(privacy, gc);
-}
-
-
-static void mw_protocol_remove_deny(PurpleProtocolPrivacy *privacy,
- PurpleConnection *gc, const char *name)
-{
- mw_protocol_set_permit_deny(privacy, gc);
-}
-
-
-static struct mwConference *conf_find(struct mwServiceConference *srvc,
- const char *name) {
- GList *l, *ll;
- struct mwConference *conf = NULL;
-
- ll = mwServiceConference_getConferences(srvc);
- for(l = ll; l; l = l->next) {
- struct mwConference *c = l->data;
- if(purple_strequal(name, mwConference_getName(c))) {
- conf = c;
- break;
- }
- }
- g_list_free(ll);
-
- return conf;
-}
-
-
-static void
-mw_protocol_join_chat(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
- GHashTable *components)
-{
-
- struct mwPurpleProtocolData *pd;
- char *c, *t;
-
- pd = purple_connection_get_protocol_data(gc);
-
- c = g_hash_table_lookup(components, CHAT_KEY_NAME);
- t = g_hash_table_lookup(components, CHAT_KEY_TOPIC);
-
- if(g_hash_table_lookup(components, CHAT_KEY_IS_PLACE)) {
- /* use place service */
- struct mwServicePlace *srvc;
- struct mwPlace *place = NULL;
-
- srvc = pd->srvc_place;
- place = mwPlace_new(srvc, c, t);
- mwPlace_open(place);
-
- } else {
- /* use conference service */
- struct mwServiceConference *srvc;
- struct mwConference *conf = NULL;
-
- srvc = pd->srvc_conf;
- if(c) conf = conf_find(srvc, c);
-
- if(conf) {
- DEBUG_INFO("accepting conference invitation\n");
- mwConference_accept(conf);
-
- } else {
- DEBUG_INFO("creating new conference\n");
- conf = mwConference_new(srvc, t);
- mwConference_open(conf);
- }
- }
-}
-
-
-static void
-mw_protocol_reject_chat(PurpleProtocolChat *protocol_chat,
- PurpleConnection *gc,
- GHashTable *components)
-{
- struct mwPurpleProtocolData *pd;
- struct mwServiceConference *srvc;
- char *c;
-
- pd = purple_connection_get_protocol_data(gc);
- srvc = pd->srvc_conf;
-
- if(g_hash_table_lookup(components, CHAT_KEY_IS_PLACE)) {
- ; /* nothing needs doing */
-
- } else {
- /* reject conference */
- c = g_hash_table_lookup(components, CHAT_KEY_NAME);
- if(c) {
- struct mwConference *conf = conf_find(srvc, c);
- if(conf) mwConference_reject(conf, ERR_SUCCESS, "Declined");
- }
- }
-}
-
-
-static char *
-mw_protocol_get_chat_name(PurpleProtocolChat *protocol_chat,
- GHashTable *components)
-{
- return g_hash_table_lookup(components, CHAT_KEY_NAME);
-}
-
-
-static void
-mw_protocol_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
- int id, const char *invitation, const char *who)
-{
- struct mwPurpleProtocolData *pd;
- struct mwConference *conf;
- struct mwPlace *place;
- struct mwIdBlock idb = { (char *) who, NULL };
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
-
- conf = ID_TO_CONF(pd, id);
-
- if(conf) {
- mwConference_invite(conf, &idb, invitation);
- return;
- }
-
- place = ID_TO_PLACE(pd, id);
- g_return_if_fail(place != NULL);
-
- /* @todo: use the IM service for invitation */
- mwPlace_legacyInvite(place, &idb, invitation);
-}
-
-
-static void
-mw_protocol_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
- int id)
-{
- struct mwPurpleProtocolData *pd;
- struct mwConference *conf;
-
- pd = purple_connection_get_protocol_data(gc);
-
- g_return_if_fail(pd != NULL);
- conf = ID_TO_CONF(pd, id);
-
- if(conf) {
- mwConference_destroy(conf, ERR_SUCCESS, "Leaving");
-
- } else {
- struct mwPlace *place = ID_TO_PLACE(pd, id);
- g_return_if_fail(place != NULL);
-
- mwPlace_destroy(place, ERR_SUCCESS);
- }
-}
-
-
-static int
-mw_protocol_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
- int id, PurpleMessage *pmsg)
-{
- struct mwPurpleProtocolData *pd;
- struct mwConference *conf;
- char *msg;
- int ret;
-
- pd = purple_connection_get_protocol_data(gc);
-
- g_return_val_if_fail(pd != NULL, 0);
- conf = ID_TO_CONF(pd, id);
-
- msg = purple_markup_strip_html(purple_message_get_contents(pmsg));
-
- if(conf) {
- ret = ! mwConference_sendText(conf, msg);
-
- } else {
- struct mwPlace *place = ID_TO_PLACE(pd, id);
- g_return_val_if_fail(place != NULL, 0);
-
- ret = ! mwPlace_sendText(place, msg);
- }
-
- g_free(msg);
- return ret;
-}
-
-
-static void
-mw_protocol_keepalive(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc)
-{
- struct mwSession *session;
-
- g_return_if_fail(gc != NULL);
-
- session = gc_to_session(gc);
- g_return_if_fail(session != NULL);
-
- mwSession_sendKeepalive(session);
-}
-
-
-static void
-mw_protocol_alias_buddy(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, const gchar *who,
- const gchar *alias)
-{
- struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
-
- /* it's a change to the buddy list, so we've gotta reflect that in
- the server copy */
-
- blist_schedule(pd);
-}
-
-
-static void
-mw_protocol_group_buddy(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, const gchar *who,
- const gchar *old_group, const gchar *new_group)
-{
- struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL };
- GList *gl = g_list_prepend(NULL, &idb);
-
- struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc);
- PurpleGroup *group;
- struct mwAwareList *list;
-
- /* add who to new_group's aware list */
- group = purple_blist_find_group(new_group);
- list = list_ensure(pd, group);
- mwAwareList_addAware(list, gl);
-
- /* remove who from old_group's aware list */
- group = purple_blist_find_group(old_group);
- list = list_ensure(pd, group);
- mwAwareList_removeAware(list, gl);
-
- g_list_free(gl);
-
- /* schedule the changes to be saved */
- blist_schedule(pd);
-}
-
-
-static void
-mw_protocol_rename_group(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, const gchar *old,
- PurpleGroup *group, GList *buddies)
-{
- struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
-
- /* it's a change in the buddy list, so we've gotta reflect that in
- the server copy. Also, having this function should prevent all
- those buddies from being removed and re-added. We don't really
- give a crap what the group is named in Purple other than to record
- that as the group name/alias */
-
- blist_schedule(pd);
-}
-
-
-static void
-mw_protocol_buddy_free(PurpleProtocolClient *client, PurpleBuddy *buddy) {
- /* I don't think we have any cleanup for buddies yet */
- ;
-}
-
-
-static void
-mw_protocol_convo_closed(PurpleProtocolClient *client, PurpleConnection *gc,
- const char *who)
-{
- struct mwPurpleProtocolData *pd = purple_connection_get_protocol_data(gc);
- struct mwServiceIm *srvc;
- struct mwConversation *conv;
- struct mwIdBlock idb = { (char *) who, NULL };
-
- g_return_if_fail(pd != NULL);
-
- srvc = pd->srvc_im;
- g_return_if_fail(srvc != NULL);
-
- conv = mwServiceIm_findConversation(srvc, &idb);
- if(! conv) return;
-
- if(mwConversation_isOpen(conv))
- mwConversation_free(conv);
-}
-
-
-static const gchar *
-mw_protocol_normalize(PurpleProtocolClient *client, PurpleAccount *account,
- const gchar *id)
-{
-
- /* code elsewhere assumes that the return value points to different
- memory than the passed value, but it won't free the normalized
- data. wtf? */
-
- static char buf[BUF_LEN];
- g_strlcpy(buf, id, sizeof(buf));
- return buf;
-}
-
-
-static void
-mw_protocol_remove_group(PurpleProtocolServer *protocol_server,
- PurpleConnection *gc, PurpleGroup *group)
-{
- struct mwPurpleProtocolData *pd;
- struct mwAwareList *list;
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_if_fail(pd != NULL);
- g_return_if_fail(pd->group_list_map != NULL);
-
- list = g_hash_table_lookup(pd->group_list_map, group);
-
- if(list) {
- g_hash_table_remove(pd->group_list_map, list);
- g_hash_table_remove(pd->group_list_map, group);
- mwAwareList_free(list);
-
- blist_schedule(pd);
- }
-}
-
-
-static gboolean mw_protocol_can_receive_file(PurpleProtocolXfer *prplxfer,
- PurpleConnection *gc,
- const char *who) {
- struct mwPurpleProtocolData *pd;
- struct mwServiceAware *srvc;
- PurpleAccount *acct;
-
- g_return_val_if_fail(gc != NULL, FALSE);
-
- pd = purple_connection_get_protocol_data(gc);
- g_return_val_if_fail(pd != NULL, FALSE);
-
- srvc = pd->srvc_aware;
- g_return_val_if_fail(srvc != NULL, FALSE);
-
- acct = purple_connection_get_account(gc);
- g_return_val_if_fail(acct != NULL, FALSE);
-
- return purple_blist_find_buddy(acct, who) &&
- user_supports(srvc, who, mwAttribute_FILE_TRANSFER);
-}
-
-
-static void mw_protocol_send_file(PurpleProtocolXfer *prplxfer,
- PurpleConnection *gc,
- const char *who, const char *file) {
-
- PurpleXfer *xfer = mw_protocol_new_xfer(prplxfer, gc, who);
-
- if(file) {
- DEBUG_INFO("file != NULL\n");
- purple_xfer_request_accepted(xfer, file);
-
- } else {
- DEBUG_INFO("file == NULL\n");
- purple_xfer_request(xfer);
- }
-}
-
-
-static void st_import_action_cb(PurpleConnection *gc, char *filename) {
- struct mwSametimeList *l;
-
- FILE *file;
- char buf[BUF_LEN];
- size_t len;
-
- GString *str;
-
- file = g_fopen(filename, "r");
- g_return_if_fail(file != NULL);
-
- str = g_string_new(NULL);
- while( (len = fread(buf, 1, BUF_LEN, file)) ) {
- g_string_append_len(str, buf, len);
- }
-
- fclose(file);
-
- l = mwSametimeList_load(str->str);
- g_string_free(str, TRUE);
-
- blist_merge(gc, l);
- mwSametimeList_free(l);
-}
-
-
-/** prompts for a file to import blist from */
-static void
-st_import_action(G_GNUC_UNUSED GSimpleAction *action,
- GVariant *parameter,
- G_GNUC_UNUSED gpointer data)
-{
- const gchar *account_id = NULL;
- PurpleAccountManager *manager = NULL;
- PurpleAccount *account = NULL;
- PurpleConnection *gc = NULL;
- char *title;
-
- if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
- g_critical("Sametime import action parameter is of incorrect type %s",
- g_variant_get_type_string(parameter));
- }
-
- account_id = g_variant_get_string(parameter, NULL);
- manager = purple_account_manager_get_default();
- account = purple_account_manager_find_by_id(manager, account_id);
- gc = purple_account_get_connection(account);
-
- title = g_strdup_printf(_("Import Sametime List for Account %s"),
- purple_account_get_username(account));
-
- purple_request_file(gc, title, NULL, FALSE,
- G_CALLBACK(st_import_action_cb), NULL,
- purple_request_cpar_from_connection(gc),
- gc);
-
- g_free(title);
-}
-
-
-static void st_export_action_cb(PurpleConnection *gc, char *filename) {
- struct mwSametimeList *l;
- char *str;
- FILE *file;
-
- file = g_fopen(filename, "w");
- g_return_if_fail(file != NULL);
-
- l = mwSametimeList_new();
- blist_export(gc, l);
- str = mwSametimeList_store(l);
- mwSametimeList_free(l);
-
- fprintf(file, "%s", str);
- fclose(file);
-
- g_free(str);
-}
-
-
-/** prompts for a file to export blist to */
-static void
-st_export_action(G_GNUC_UNUSED GSimpleAction *action,
- GVariant *parameter,
- G_GNUC_UNUSED gpointer data)
-{
- const gchar *account_id = NULL;
- PurpleAccountManager *manager = NULL;
- PurpleAccount *account = NULL;
- PurpleConnection *gc = NULL;
- char *title;
-
- if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
- g_critical("Sametime export action parameter is of incorrect type %s",
- g_variant_get_type_string(parameter));
- }
-
- account_id = g_variant_get_string(parameter, NULL);
- manager = purple_account_manager_get_default();
- account = purple_account_manager_find_by_id(manager, account_id);
- gc = purple_account_get_connection(account);
-
- title = g_strdup_printf(_("Export Sametime List for Account %s"),
- purple_account_get_username(account));
-
- purple_request_file(gc, title, NULL, TRUE,
- G_CALLBACK(st_export_action_cb), NULL,
- purple_request_cpar_from_connection(gc),
- gc);
-
- g_free(title);
-}
-
-
-static void remote_group_multi_cleanup(gpointer ignore,
- PurpleRequestFields *fields) {
-
- PurpleRequestField *f;
- GList *l;
-
- f = purple_request_fields_get_field(fields, "group");
- l = purple_request_field_list_get_items(f);
-
- for(; l; l = l->next) {
- const char *i = l->data;
- struct named_id *res;
-
- res = purple_request_field_list_get_data(f, i);
-
- g_free(res->id);
- g_free(res->name);
- g_free(res);
- }
-}
-
-
-static void remote_group_done(struct mwPurpleProtocolData *pd,
- const char *id, const char *name) {
- PurpleConnection *gc;
- PurpleAccount *acct;
- PurpleGroup *group;
- PurpleBlistNode *gn;
- const char *owner;
-
- g_return_if_fail(pd != NULL);
-
- gc = pd->gc;
- acct = purple_connection_get_account(gc);
-
- /* collision checking */
- group = purple_blist_find_group(name);
- if(group) {
- const char *msgA;
- const char *msgB;
- char *msg;
-
- msgA = _("Unable to add group: group exists");
- msgB = _("A group named '%s' already exists in your buddy list.");
- msg = g_strdup_printf(msgB, name);
-
- purple_notify_error(gc, _("Unable to add group"), msgA, msg,
- purple_request_cpar_from_connection(gc));
-
- g_free(msg);
- return;
- }
-
- group = purple_group_new(name);
- gn = (PurpleBlistNode *) group;
-
- owner = purple_account_get_username(acct);
-
- purple_blist_node_set_string(gn, GROUP_KEY_NAME, id);
- purple_blist_node_set_int(gn, GROUP_KEY_TYPE, mwSametimeGroup_DYNAMIC);
- purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner);
- purple_blist_add_group(group, NULL);
-
- group_add(pd, group);
- blist_schedule(pd);
-}
-
-
-static void remote_group_multi_cb(struct mwPurpleProtocolData *pd,
- PurpleRequestFields *fields) {
- PurpleRequestField *f;
- GList *l;
-
- f = purple_request_fields_get_field(fields, "group");
- l = purple_request_field_list_get_selected(f);
-
- if(l) {
- const char *i = l->data;
- struct named_id *res;
-
- res = purple_request_field_list_get_data(f, i);
- remote_group_done(pd, res->id, res->name);
- }
-
- remote_group_multi_cleanup(NULL, fields);
-}
-
-
-static void remote_group_multi(struct mwResolveResult *result,
- struct mwPurpleProtocolData *pd) {
-
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- GList *l;
- const char *msgA;
- const char *msgB;
- char *msg;
-
- PurpleConnection *gc = pd->gc;
-
- fields = purple_request_fields_new();
-
- g = purple_request_field_group_new(NULL);
- purple_request_fields_add_group(fields, g);
-
- f = purple_request_field_list_new("group", _("Possible Matches"));
- purple_request_field_list_set_multi_select(f, FALSE);
- purple_request_field_set_required(f, TRUE);
-
- for(l = result->matches; l; l = l->next) {
- struct mwResolveMatch *match = l->data;
- struct named_id *res = g_new0(struct named_id, 1);
-
- res->id = g_strdup(match->id);
- res->name = g_strdup(match->name);
-
- purple_request_field_list_add_icon(f, res->name, NULL, res);
- }
-
- purple_request_field_group_add_field(g, f);
-
- msgA = _("Notes Address Book group results");
- msgB = _("The identifier '%s' may possibly refer to any of the following"
- " Notes Address Book groups. Please select the correct group from"
- " the list below to add it to your buddy list.");
- msg = g_strdup_printf(msgB, result->name);
-
- purple_request_fields(gc, _("Select Notes Address Book"),
- msgA, msg, fields,
- _("Add Group"), G_CALLBACK(remote_group_multi_cb),
- _("Cancel"), G_CALLBACK(remote_group_multi_cleanup),
- purple_request_cpar_from_connection(gc),
- pd);
-
- g_free(msg);
-}
-
-
-static void remote_group_resolved(struct mwServiceResolve *srvc,
- guint32 id, guint32 code, GList *results,
- gpointer b) {
-
- struct mwResolveResult *res = NULL;
- struct mwSession *session;
- struct mwPurpleProtocolData *pd;
- PurpleConnection *gc;
-
- session = mwService_getSession(MW_SERVICE(srvc));
- g_return_if_fail(session != NULL);
-
- pd = mwSession_getClientData(session);
- g_return_if_fail(pd != NULL);
-
- gc = pd->gc;
- g_return_if_fail(gc != NULL);
-
- if(!code && results) {
- res = results->data;
-
- if(res->matches) {
- remote_group_multi(res, pd);
- return;
- }
- }
-
- if(res && res->name) {
- const char *msgA;
- const char *msgB;
- char *msg;
-
- msgA = _("Unable to add group: group not found");
-
- msgB = _("The identifier '%s' did not match any Notes Address Book"
- " groups in your Sametime community.");
- msg = g_strdup_printf(msgB, res->name);
-
- purple_notify_error(gc, _("Unable to add group"), msgA, msg,
- purple_request_cpar_from_connection(gc));
-
- g_free(msg);
- }
-}
-
-
-static void remote_group_action_cb(PurpleConnection *gc, const char *name) {
- struct mwPurpleProtocolData *pd;
- struct mwServiceResolve *srvc;
- GList *query;
- enum mwResolveFlag flags;
- guint32 req;
-
- pd = purple_connection_get_protocol_data(gc);
- srvc = pd->srvc_resolve;
-
- query = g_list_prepend(NULL, (char *) name);
- flags = mwResolveFlag_FIRST | mwResolveFlag_GROUPS;
-
- req = mwServiceResolve_resolve(srvc, query, flags, remote_group_resolved,
- NULL, NULL);
- g_list_free(query);
-
- if(req == SEARCH_ERROR) {
- /** @todo display error */
- }
-}
-
-
-static void
-remote_group_action(G_GNUC_UNUSED GSimpleAction *action,
- GVariant *parameter,
- G_GNUC_UNUSED gpointer data)
-{
- const gchar *account_id = NULL;
- PurpleAccountManager *manager = NULL;
- PurpleAccount *account = NULL;
- PurpleConnection *gc = NULL;
- const char *msgA;
- const char *msgB;
-
- if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
- g_critical("Sametime export action parameter is of incorrect type %s",
- g_variant_get_type_string(parameter));
- }
-
- account_id = g_variant_get_string(parameter, NULL);
- manager = purple_account_manager_get_default();
- account = purple_account_manager_find_by_id(manager, account_id);
- gc = purple_account_get_connection(account);
-
- msgA = _("Notes Address Book Group");
- msgB = _("Enter the name of a Notes Address Book group in the field below"
- " to add the group and its members to your buddy list.");
-
- purple_request_input(gc, _("Add Group"), msgA, msgB, NULL,
- FALSE, FALSE, NULL,
- _("Add"), G_CALLBACK(remote_group_action_cb),
- _("Cancel"), NULL,
- purple_request_cpar_from_connection(gc),
- gc);
-}
-
-
-static void search_notify(struct mwResolveResult *result,
- PurpleConnection *gc) {
- GList *l;
- const char *msgA;
- const char *msgB;
- char *msg1;
- char *msg2;
-
- PurpleNotifySearchResults *sres;
- PurpleNotifySearchColumn *scol;
-
- sres = purple_notify_searchresults_new();
-
- scol = purple_notify_searchresults_column_new(_("User Name"));
- purple_notify_searchresults_column_add(sres, scol);
-
- scol = purple_notify_searchresults_column_new(_("Sametime ID"));
- purple_notify_searchresults_column_add(sres, scol);
-
- purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_IM,
- notify_im);
-
- purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_ADD,
- notify_add);
-
- for(l = result->matches; l; l = l->next) {
- struct mwResolveMatch *match = l->data;
- GList *row = NULL;
-
- if(!match->id || !match->name)
- continue;
-
- row = g_list_append(row, g_strdup(match->name));
- row = g_list_append(row, g_strdup(match->id));
- purple_notify_searchresults_row_add(sres, row);
- }
-
- msgA = _("Search results for '%s'");
- msgB = _("The identifier '%s' may possibly refer to any of the following"
- " users. You may add these users to your buddy list or send them"
- " messages with the action buttons below.");
-
- msg1 = g_strdup_printf(msgA, result->name);
- msg2 = g_strdup_printf(msgB, result->name);
-
- purple_notify_searchresults(gc, _("Search Results"),
- msg1, msg2, sres, notify_close, NULL);
-
- g_free(msg1);
- g_free(msg2);
-}
-
-
-static void search_resolved(struct mwServiceResolve *srvc,
- guint32 id, guint32 code, GList *results,
- gpointer b) {
-
- PurpleConnection *gc = b;
- struct mwResolveResult *res = NULL;
-
- if(results) res = results->data;
-
- if(!code && res && res->matches) {
- search_notify(res, gc);
-
- } else {
- const char *msgA;
- const char *msgB;
- char *msg;
-
- msgA = _("No matches");
- msgB = _("The identifier '%s' did not match any users in your"
- " Sametime community.");
- msg = g_strdup_printf(msgB, (res && res->name) ? res->name : "");
-
- purple_notify_error(gc, _("No Matches"), msgA, msg,
- purple_request_cpar_from_connection(gc));
-
- g_free(msg);
- }
-}
-
-
-static void search_action_cb(PurpleConnection *gc, const char *name) {
- struct mwPurpleProtocolData *pd;
- struct mwServiceResolve *srvc;
- GList *query;
- enum mwResolveFlag flags;
- guint32 req;
-
- pd = purple_connection_get_protocol_data(gc);
- srvc = pd->srvc_resolve;
-
- query = g_list_prepend(NULL, (char *) name);
- flags = mwResolveFlag_FIRST | mwResolveFlag_USERS;
-
- req = mwServiceResolve_resolve(srvc, query, flags, search_resolved,
- gc, NULL);
- g_list_free(query);
-
- if(req == SEARCH_ERROR) {
- /** @todo display error */
- }
-}
-
-
-static void
-search_action(G_GNUC_UNUSED GSimpleAction *action,
- GVariant *parameter,
- G_GNUC_UNUSED gpointer data)
-{
- const gchar *account_id = NULL;
- PurpleAccountManager *manager = NULL;
- PurpleAccount *account = NULL;
- PurpleConnection *gc = NULL;
- const char *msgA;
- const char *msgB;
-
- if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
- g_critical("Sametime export action parameter is of incorrect type %s",
- g_variant_get_type_string(parameter));
- }
-
- account_id = g_variant_get_string(parameter, NULL);
- manager = purple_account_manager_get_default();
- account = purple_account_manager_find_by_id(manager, account_id);
- gc = purple_account_get_connection(account);
-
-
- msgA = _("Search for a user");
- msgB = _("Enter a name or partial ID in the field below to search"
- " for matching users in your Sametime community.");
-
- purple_request_input(gc, _("User Search"), msgA, msgB, NULL,
- FALSE, FALSE, NULL,
- _("Search"), G_CALLBACK(search_action_cb),
- _("Cancel"), NULL,
- purple_request_cpar_from_connection(gc),
- gc);
-}
-
-
-static const gchar *
-mw_protocol_actions_get_prefix(PurpleProtocolActions *actions) {
- return PLUGIN_ID;
-}
-
-static GActionGroup *
-mw_protocol_actions_get_action_group(PurpleProtocolActions *actions,
- PurpleConnection *connection)
-{
- GSimpleActionGroup *group = NULL;
- GActionEntry entries[] = {
- {
- .name = "import",
- .activate = st_import_action,
- .parameter_type = "s",
- },
- {
- .name = "export",
- .activate = st_export_action,
- .parameter_type = "s",
- },
- {
- .name = "notes",
- .activate = remote_group_action,
- .parameter_type = "s",
- },
- {
- .name = "search",
- .activate = search_action,
- .parameter_type = "s",
- },
- };
- gsize nentries = G_N_ELEMENTS(entries);
-
- group = g_simple_action_group_new();
- g_action_map_add_action_entries(G_ACTION_MAP(group), entries, nentries,
- NULL);
-
- return G_ACTION_GROUP(group);
-}
-
-static GMenu *
-mw_protocol_actions_get_menu(PurpleProtocolActions *actions) {
- GMenu *menu = NULL;
- GMenuItem *item = NULL;
-
- menu = g_menu_new();
-
- item = g_menu_item_new(_("Import Sametime List..."),
- PLUGIN_ID ".import");
- g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
- "account");
- g_menu_append_item(menu, item);
- g_object_unref(item);
-
- item = g_menu_item_new(_("Export Sametime List..."),
- PLUGIN_ID ".export");
- g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
- "account");
- g_menu_append_item(menu, item);
- g_object_unref(item);
-
- item = g_menu_item_new(_("Add Notes Address Book Group..."),
- PLUGIN_ID ".notes");
- g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
- "account");
- g_menu_append_item(menu, item);
- g_object_unref(item);
-
- item = g_menu_item_new(_("User Search..."), PLUGIN_ID ".search");
- g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
- "account");
- g_menu_append_item(menu, item);
- g_object_unref(item);
-
- return menu;
-}
-
-
-static void mw_log_handler(const gchar *domain, GLogLevelFlags flags,
- const gchar *msg, gpointer data) {
-
- if(! (msg && *msg)) return;
-
- /* handle g_log requests via purple's built-in debug logging */
- if(flags & G_LOG_LEVEL_ERROR) {
- purple_debug_error(domain, "%s\n", msg);
-
- } else if(flags & G_LOG_LEVEL_WARNING) {
- purple_debug_warning(domain, "%s\n", msg);
-
- } else {
- purple_debug_info(domain, "%s\n", msg);
- }
-}
-
-static void
-mw_protocol_init(mwProtocol *self)
-{
- /* set up the preferences */
- purple_prefs_add_none(MW_PROTOCOL_OPT_BASE);
- purple_prefs_add_int(MW_PROTOCOL_OPT_BLIST_ACTION,
- BLIST_CHOICE_DEFAULT);
-
- /* remove dead preferences */
- purple_prefs_remove(MW_PROTOCOL_OPT_PSYCHIC);
- purple_prefs_remove(MW_PROTOCOL_OPT_SAVE_DYNAMIC);
-}
-
-static void
-mw_protocol_class_init(mwProtocolClass *klass)
-{
- PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
-
- protocol_class->login = mw_protocol_login;
- protocol_class->close = mw_protocol_close;
- protocol_class->status_types = mw_protocol_status_types;
-
- protocol_class->get_account_options = mw_protocol_get_account_options;
- protocol_class->get_user_splits = mw_protocol_get_user_splits;
-}
-
-static void
-mw_protocol_class_finalize(G_GNUC_UNUSED mwProtocolClass *klass)
-{
-}
-
-
-static void
-mw_protocol_actions_iface_init(PurpleProtocolActionsInterface *iface)
-{
- iface->get_prefix = mw_protocol_actions_get_prefix;
- iface->get_action_group = mw_protocol_actions_get_action_group;
- iface->get_menu = mw_protocol_actions_get_menu;
-}
-
-static void
-mw_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
-{
- client_iface->list_emblem = mw_protocol_list_emblem;
- client_iface->status_text = mw_protocol_status_text;
- client_iface->tooltip_text = mw_protocol_tooltip_text;
- client_iface->blist_node_menu = mw_protocol_blist_node_menu;
- client_iface->buddy_free = mw_protocol_buddy_free;
- client_iface->convo_closed = mw_protocol_convo_closed;
- client_iface->normalize = mw_protocol_normalize;
-}
-
-
-static void
-mw_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
-{
- server_iface->get_info = mw_protocol_get_info;
- server_iface->set_status = mw_protocol_set_status;
- server_iface->set_idle = mw_protocol_set_idle;
- server_iface->add_buddy = mw_protocol_add_buddy;
- server_iface->add_buddies = mw_protocol_add_buddies;
- server_iface->remove_buddy = mw_protocol_remove_buddy;
- server_iface->keepalive = mw_protocol_keepalive;
- server_iface->alias_buddy = mw_protocol_alias_buddy;
- server_iface->group_buddy = mw_protocol_group_buddy;
- server_iface->rename_group = mw_protocol_rename_group;
- server_iface->remove_group = mw_protocol_remove_group;
-}
-
-
-static void
-mw_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
-{
- im_iface->send = mw_protocol_send_im;
- im_iface->send_typing = mw_protocol_send_typing;
-}
-
-
-static void
-mw_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
-{
- chat_iface->info = mw_protocol_chat_info;
- chat_iface->info_defaults = mw_protocol_chat_info_defaults;
- chat_iface->join = mw_protocol_join_chat;
- chat_iface->reject = mw_protocol_reject_chat;
- chat_iface->get_name = mw_protocol_get_chat_name;
- chat_iface->invite = mw_protocol_chat_invite;
- chat_iface->leave = mw_protocol_chat_leave;
- chat_iface->send = mw_protocol_chat_send;
-}
-
-
-static void
-mw_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
-{
- privacy_iface->add_permit = mw_protocol_add_permit;
- privacy_iface->add_deny = mw_protocol_add_deny;
- privacy_iface->remove_permit = mw_protocol_remove_permit;
- privacy_iface->remove_deny = mw_protocol_remove_deny;
- privacy_iface->set_permit_deny = mw_protocol_set_permit_deny;
-}
-
-
-static void
-mw_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
-{
- xfer_iface->can_receive = mw_protocol_can_receive_file;
- xfer_iface->send_file = mw_protocol_send_file;
- xfer_iface->new_xfer = mw_protocol_new_xfer;
-}
-
-G_DEFINE_DYNAMIC_TYPE_EXTENDED(
- mwProtocol,
- mw_protocol,
- PURPLE_TYPE_PROTOCOL,
- G_TYPE_FLAG_FINAL,
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ACTIONS,
- mw_protocol_actions_iface_init)
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
- mw_protocol_client_iface_init)
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
- mw_protocol_server_iface_init)
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
- mw_protocol_im_iface_init)
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
- mw_protocol_chat_iface_init)
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
- mw_protocol_privacy_iface_init)
- G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
- mw_protocol_xfer_iface_init))
-
-static PurpleProtocol *
-mw_protocol_new(void) {
- return PURPLE_PROTOCOL(g_object_new(
- MW_TYPE_PROTOCOL,
- "id", "prpl-meanwhile",
- "name", "Sametime",
- "description", _("HCL Sametime enterprise chat."),
- "icon-name", "im-meanwhile",
- "icon-resource-path", "/im/pidgin/libpurple/sametime/icons",
- NULL));
-}
-
-static GPluginPluginInfo *
-sametime_query(GError **error)
-{
- const gchar * const authors[] = PLUGIN_AUTHORS;
-
- return purple_plugin_info_new(
- "id", PLUGIN_ID,
- "name", PLUGIN_NAME,
- "version", DISPLAY_VERSION,
- "category", PLUGIN_CATEGORY,
- "summary", PLUGIN_SUMMARY,
- "description", PLUGIN_DESC,
- "authors", authors,
- "website", PLUGIN_HOMEPAGE,
- "abi-version", PURPLE_ABI_VERSION,
- "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
- PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
- NULL
- );
-}
-
-
-static gboolean
-sametime_load(GPluginPlugin *plugin, GError **error)
-{
- PurpleProtocolManager *manager = purple_protocol_manager_get_default();
- GLogLevelFlags logflags =
- G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION;
-
- mw_protocol_register_type(G_TYPE_MODULE(plugin));
-
- mw_xfer_register_type(G_TYPE_MODULE(plugin));
-
- my_protocol = mw_protocol_new();
- if(!purple_protocol_manager_register(manager, my_protocol, error)) {
- g_clear_object(&my_protocol);
-
- return FALSE;
- }
-
- /* forward all our g_log messages to purple. Generally all the logging
- calls are using purple_log directly, but the g_return macros will
- get caught here */
- log_handler[0] = g_log_set_handler(G_LOG_DOMAIN, logflags,
- mw_log_handler, NULL);
-
- /* redirect meanwhile's logging to purple's */
- log_handler[1] = g_log_set_handler("meanwhile", logflags,
- mw_log_handler, NULL);
-
- g_mime_init();
-
- return TRUE;
-}
-
-
-static gboolean
-sametime_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
-{
- PurpleProtocolManager *manager = purple_protocol_manager_get_default();
-
- if(!purple_protocol_manager_unregister(manager, my_protocol, error)) {
- return FALSE;
- }
-
- g_mime_shutdown();
-
- g_log_remove_handler(G_LOG_DOMAIN, log_handler[0]);
- g_log_remove_handler("meanwhile", log_handler[1]);
-
- g_clear_object(&my_protocol);
-
- return TRUE;
-}
-
-
-GPLUGIN_NATIVE_PLUGIN_DECLARE(sametime)
-/* The End. */
-
--- a/libpurple/protocols/sametime/sametime.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#ifndef PURPLE_SAMETIME_SAMETIME_H
-#define PURPLE_SAMETIME_SAMETIME_H
-
-#define MW_TYPE_PROTOCOL (mw_protocol_get_type())
-G_DECLARE_FINAL_TYPE(mwProtocol, mw_protocol, MW, PROTOCOL, PurpleProtocol)
-
-/* CFLAGS trumps configure values */
-
-
-/** default host for the purple plugin. You can specialize a build to
- default to your server by supplying this at compile time */
-#ifndef MW_PLUGIN_DEFAULT_HOST
-#define MW_PLUGIN_DEFAULT_HOST ""
-#endif
-/* "" */
-
-
-/** default port for the purple plugin. You can specialize a build to
- default to your server by supplying this at compile time */
-#ifndef MW_PLUGIN_DEFAULT_PORT
-#define MW_PLUGIN_DEFAULT_PORT 1533
-#endif
-/* 1533 */
-
-
-/** default encoding for the purple plugin.*/
-#ifndef MW_PLUGIN_DEFAULT_ENCODING
-#define MW_PLUGIN_DEFAULT_ENCODING "ISO-8859-1"
-#endif
-/* ISO-8859-1 */
-
-#endif /* PURPLE_SAMETIME_SAMETIME_H */
--- a/libpurple/protocols/sametime/tests/data/image-data.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-// generated via:
-// $ cat test-image.png | hexdump -v -e '1 1 "0x%02x," " "' | xargs -n 8 echo
-static const guint8 test_image_data[] = {
- 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
- 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
- 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
- 0x08, 0x02, 0x00, 0x00, 0x00, 0xfd, 0xd4, 0x9a,
- 0x73, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
- 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
- 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00,
- 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe0,
- 0x0a, 0x02, 0x16, 0x30, 0x22, 0x28, 0xa4, 0xc9,
- 0xdd, 0x00, 0x00, 0x00, 0x1d, 0x69, 0x54, 0x58,
- 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65,
- 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74,
- 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x64, 0x2e,
- 0x65, 0x07, 0x00, 0x00, 0x00, 0x16, 0x49, 0x44,
- 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff,
- 0x3f, 0x03, 0x03, 0x03, 0xe3, 0xb3, 0x4c, 0xb5,
- 0x9b, 0x4e, 0x0b, 0x00, 0x2f, 0xa9, 0x06, 0x2f,
- 0x8a, 0xd1, 0xc6, 0xb3, 0x00, 0x00, 0x00, 0x00,
- 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
-};
--- a/libpurple/protocols/sametime/tests/data/mime-basic.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-{
-/* Just an empty string round-trip. */
-.name = "empty",
-.html = "",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "\r\n"
- "--related_MWc7f_0aac--\r\n"
-},
-{
-/* A simple string. */
-.name = "simple",
-.html = "This is a test of the MIME encoding using a short message.",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "This is a test of the MIME encoding using a short message.\r\n"
- "--related_MWc7f_0aac--\r\n"
-},
--- a/libpurple/protocols/sametime/tests/data/mime-image.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-{
-/* A message with an image in it. */
-.name = "image",
-.html =
- "This is a test of the MIME encoding with an image: "
- "<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL "%u\">",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "This is a test of the MIME encoding with an image: "
- "<img src=\"cid:97c@4aa2fmeanwhile\">\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: image/png; "
- "name=c6e846b12072e3bdf2c8f768e776deaa94c970ad.png\r\n"
- "Content-Disposition: attachment;\r\n"
- "\tfilename=c6e846b12072e3bdf2c8f768e776deaa94c970ad.png\r\n"
- "Content-Transfer-Encoding: base64\r\n"
- "Content-Id: <97c@4aa2fmeanwhile>\r\n"
- "\r\n"
- "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA\r\n"
- "B3RJTUUH4AoCFjAiKKTJ3QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH\r\n"
- "AAAAFklEQVQI12P4//8/AwMD47NMtZtOCwAvqQYvitHGswAAAABJRU5ErkJggg==\r\n"
- "\r\n"
- "--related_MWc7f_0aac--\r\n",
-.with_image = TRUE
-},
--- a/libpurple/protocols/sametime/tests/data/mime-multiline.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-{
-/* A message with multiple lines. */
-.name = "multiline",
-.html =
- "This is a test of the MIME encoding using a multiline message.\n"
- "This is the second line of the message.",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "This is a test of the MIME encoding using a multiline message.\r\n"
- "This is the second line of the message.\r\n"
- "--related_MWc7f_0aac--\r\n"
-},
-{
-/* A message that ends with a newline. */
-.name = "trailing-newline",
-.html = "This is a test of the MIME encoding using a multiline message.\n",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "This is a test of the MIME encoding using a multiline message.\r\n"
- "\r\n"
- "--related_MWc7f_0aac--\r\n"
-},
--- a/libpurple/protocols/sametime/tests/data/mime-utf8.h Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-{
-/* Hello world in UTF-8. */
-.name = "utf8-hello",
-.html = "Hello world, Καλημέρα κόσμε, コンニチハ.",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "Hello world, "
- "&#922;&#945;&#955;&#951;&#956;&#8051;&#961;&#945; "
- "&#954;&#8057;&#963;&#956;&#949;, "
- "&#12467;&#12531;&#12491;&#12481;&#12495;.\r\n"
- "--related_MWc7f_0aac--\r\n"
-},
-{
-/* Hello world in UTF-8. */
-.name = "utf8-various",
-.html =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 "
- "abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ "
- "–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд "
- "∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა",
-.mime =
- "Content-Type: multipart/related; boundary=related_MWc7f_0aac\r\n"
- "Mime-Version: 1.0\r\n"
- "Content-Disposition: inline\r\n"
- "\r\n"
- "--related_MWc7f_0aac\r\n"
- "Content-Type: text/html; charset=us-ascii\r\n"
- "Content-Disposition: inline\r\n"
- "Content-Transfer-Encoding: 7bit\r\n"
- "\r\n"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 abcdefghijklmnopqrstuvwxyz "
- "&#163;&#169;&#181;&#192;&#198;&#214;&#222;&#223;&#233;&#246;&#255; "
- "&#8211;&#8212;&#8216;&#8220;&#8221;&#8222;&#8224;&#8226;&#8230;&#8240;&#8482;&#339;&#352;&#376;&#382;&#8364; "
- "&#913;&#914;&#915;&#916;&#937;&#945;&#946;&#947;&#948;&#969; "
- "&#1040;&#1041;&#1042;&#1043;&#1044;&#1072;&#1073;&#1074;&#1075;&#1076; "
- "&#8704;&#8706;&#8712;&#8477;&#8743;&#8746;&#8801;&#8734; "
- "&#8593;&#8599;&#8616;&#8635;&#8675; "
- "&#9488;&#9532;&#9556;&#9560;&#9617;&#9658;&#9786;&#9792; "
- "&#64257;&#65533;&#9280;&#8322;&#7968;&#7682;&#1253;&#7812;&#592;&#720;&#9038;&#1488;&#1329;&#4304;\r\n"
- "--related_MWc7f_0aac--\r\n"
-},
--- a/libpurple/protocols/sametime/tests/meson.build Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-foreach prog : ['im_mime']
- e = executable(
- 'test_sametime_' + prog, 'test_sametime_@0@.c'.format(prog),
- link_with : [sametime_prpl],
- dependencies : [libpurple_dep, gmime, glib])
-
- stenv = environment()
- stenv.set('G_TEST_SRCDIR', meson.current_source_dir())
- stenv.set('XDG_CONFIG_DIR', meson.current_build_dir() / 'config')
-
- test('sametime_' + prog, e,
- env : stenv)
-endforeach
--- a/libpurple/protocols/sametime/tests/test_sametime_im_mime.c Sat Nov 12 15:37:03 2022 -0600
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-/*
- * purple - Sametime Protocol Plugin Tests
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- */
-
-#include <glib.h>
-#include <gmime/gmime.h>
-#include <string.h>
-
-#include <purple.h>
-
-#include "protocols/sametime/im_mime.h"
-
-typedef struct _TestData {
- /* Name of the test case. */
- const gchar *name;
- /* HTML version of the message. */
- const gchar *html;
- /* MIME-encoded version of the message. */
- const gchar *mime;
- /* Whether to load an image and substitute %u for its imagestore id. */
- gboolean with_image;
-} TestData;
-
-const TestData TEST_CASES[] = {
-#include "data/mime-basic.h"
-#include "data/mime-multiline.h"
-#include "data/mime-image.h"
-#include "data/mime-utf8.h"
-};
-
-// Image data to use for tests:
-#include "data/image-data.h"
-
-
-static void
-test_sametime_im_mime_parse(gconstpointer _data)
-{
- const TestData *data = (const TestData *)_data;
- gchar *result, *ncr;
-
- /* This decoding is normally done in im_recv_html, but it's not used here. */
- ncr = im_mime_parse(data->mime);
- result = purple_utf8_ncr_decode(ncr);
- g_free(ncr);
-
- if (data->with_image) {
- gchar **expected;
- gchar **got;
- gchar *suffix_expected, *suffix_got;
-
- expected = g_strsplit(data->html, "<img src=\"", 2);
- g_assert_nonnull(expected);
-
- got = g_strsplit(result, "<img src=\"", 2);
- g_assert_nonnull(got);
-
- /* The image will be in the image store with some random id, so
- * assert it's starting with the same prefix. */
- g_assert_cmpstr(got[0], ==, expected[0]);
-
- /* Then assert it's ending with the same suffix. */
- g_assert_nonnull(suffix_expected = expected[1]);
- g_assert_nonnull(suffix_got = got[1]);
- if (suffix_expected && suffix_got) {
- /* First trim off the src attribute which will differ
- * between expected and the result. */
- g_assert_true(g_str_has_prefix(suffix_expected,
- PURPLE_IMAGE_STORE_PROTOCOL "%u\""));
- suffix_expected += sizeof(PURPLE_IMAGE_STORE_PROTOCOL "%u\"") - 1;
- suffix_got = strchr(suffix_got, '"');
- g_assert_nonnull(suffix_got);
- if (suffix_got) {
- /* Skip over any padding after the image store id. */
- suffix_got = strchr(suffix_got, '>');
- g_assert_nonnull(suffix_got);
- }
- g_assert_cmpstr(suffix_got, ==, suffix_expected);
- }
-
- g_strfreev(expected);
- g_strfreev(got);
- } else {
- g_assert_cmpstr(result, ==, data->html);
- }
- g_free(result);
-}
-
-
-static void
-test_sametime_im_mime_generate(gconstpointer _data)
-{
- const TestData *data = (const TestData *)_data;
- gchar *input = NULL;
- PurpleImage *image = NULL;
- guint imgid;
- gchar *result;
- gint i = 0;
- gchar **got = NULL;
- gchar **expected = NULL;
-
- g_random_set_seed(0);
- if (data->with_image) {
- image = purple_image_new_from_data(test_image_data,
- G_N_ELEMENTS(test_image_data));
- imgid = purple_image_store_add_weak(image);
- input = g_strdup_printf(data->html, imgid);
- } else {
- input = g_strdup(data->html);
- }
-
- result = im_mime_generate(input);
- got = g_strsplit(result, "\r\n", -1);
- expected = g_strsplit(data->mime, "\r\n", -1);
-
- /* Check lines are the same individually for more verbose errors. */
- g_assert_cmpuint(g_strv_length(got), ==, g_strv_length(expected));
- for (i = 0; got[i] && expected[i]; i++) {
- g_assert_cmpstr(got[i], ==, expected[i]);
- }
-
- g_strfreev(expected);
- g_strfreev(got);
- g_free(result);
- if (image) {
- g_object_unref(G_OBJECT(image));
- }
- g_free(input);
-}
-
-
-gint
-main(gint argc, gchar **argv)
-{
- gint i;
- gchar *name;
-
- g_test_init(&argc, &argv, NULL);
- g_mime_init();
- _purple_image_store_init();
-
- g_test_set_nonfatal_assertions();
-
- for (i = 0; i < G_N_ELEMENTS(TEST_CASES); i++) {
- name = g_strdup_printf("/sametime/im_mime/parse/%s",
- TEST_CASES[i].name);
- g_test_add_data_func(name, &TEST_CASES[i],
- test_sametime_im_mime_parse);
- g_free(name);
-
- name = g_strdup_printf("/sametime/im_mime/generate/%s",
- TEST_CASES[i].name);
- g_test_add_data_func(name, &TEST_CASES[i],
- test_sametime_im_mime_generate);
- g_free(name);
- }
-
- i = g_test_run();
-
- _purple_image_store_uninit();
- g_mime_shutdown();
- return i;
-}
--- a/meson.build Sat Nov 12 15:37:03 2022 -0600
+++ b/meson.build Sat Nov 12 15:38:17 2022 -0600
@@ -308,13 +308,6 @@
gstreamer_app = dependency('gstreamer-app-1.0')
#######################################################################
-# Check for Meanwhile headers (for Sametime)
-#######################################################################
-meanwhile = dependency('meanwhile', version : ['>= 1.0.0', '< 2.0.0'], required : get_option('meanwhile'))
-gmime = dependency('gmime-3.0', version : '>= 3.0.0', required : get_option('meanwhile'))
-enable_meanwhile = meanwhile.found() and gmime.found()
-
-#######################################################################
# Check for Native Avahi headers (for Bonjour)
#######################################################################
@@ -353,8 +346,7 @@
endif
-DEFAULT_PRPLS = ['bonjour', 'demo', 'facebook', 'gg', 'irc', 'ircv3', 'jabber',
- 'sametime']
+DEFAULT_PRPLS = ['bonjour', 'demo', 'facebook', 'gg', 'irc', 'ircv3', 'jabber']
dynamic_list = get_option('dynamic-prpls').split(',')
if dynamic_list == ['all']
@@ -364,8 +356,6 @@
foreach prpl : dynamic_list
if prpl == ''
# The list was empty; do nothing.
- elif prpl == 'sametime' and not enable_meanwhile
- # Do nothing.
elif prpl == 'bonjour' and not enable_avahi
# Do nothing.
elif prpl == 'gg' and not libgadu.found()
@@ -382,7 +372,6 @@
DYNAMIC_IRC = DYNAMIC_PRPLS.contains('irc')
DYNAMIC_IRCV3 = DYNAMIC_PRPLS.contains('ircv3')
DYNAMIC_JABBER = DYNAMIC_PRPLS.contains('jabber')
-DYNAMIC_SAMETIME = DYNAMIC_PRPLS.contains('sametime')
conf.set('HAVE_SYS_UTSNAME_H',
compiler.has_header('sys/utsname.h'))
--- a/meson_options.txt Sat Nov 12 15:37:03 2022 -0600
+++ b/meson_options.txt Sat Nov 12 15:38:17 2022 -0600
@@ -40,9 +40,6 @@
option('libgadu', type : 'feature',
description : 'compile with libgadu (required for GaduGadu support)')
-option('meanwhile', type : 'feature',
- description : 'compile with meanwhile')
-
##############################################################################
# Pidgin Options
--- a/packaging/Pidgin3.AppImageBuilder.yml Sat Nov 12 15:37:03 2022 -0600
+++ b/packaging/Pidgin3.AppImageBuilder.yml Sat Nov 12 15:38:17 2022 -0600
@@ -33,7 +33,6 @@
- libgirepository-1.0-1
- libglib-object-introspection-perl
- libglib-perl
- - libgmime-3.0-0
- libgspell-1-2
- libgstreamer-plugins-base1.0-0
- libgstreamer1.0-0
@@ -45,7 +44,6 @@
- libjson-glib-1.0-0
# - libkf5wallet5 # 57mb
- liblua5.3-0
- - libmeanwhile1
- libpython3.8
- libsecret-1-0
- libsoup2.4-1
--- a/pidgin/plugins/disco/xmppdisco.c Sat Nov 12 15:37:03 2022 -0600
+++ b/pidgin/plugins/disco/xmppdisco.c Sat Nov 12 15:38:17 2022 -0600
@@ -259,7 +259,6 @@
const char *to;
} disco_type_mappings[] = {
{ "gadu-gadu", "gadu-gadu" }, /* the protocol is gg, but list_icon returns "gadu-gadu" */
- { "sametime", "meanwhile" },
{ "xmpp", "jabber" }, /* jabber (mentioned in case the protocol is renamed so this line will match) */
{ NULL, NULL }
};
--- a/po/POTFILES.in Sat Nov 12 15:37:03 2022 -0600
+++ b/po/POTFILES.in Sat Nov 12 15:38:17 2022 -0600
@@ -160,9 +160,6 @@
libpurple/protocols/jabber/usertune.c
libpurple/protocols/jabber/xdata.c
libpurple/protocols/jabber/xmpp.c
-libpurple/protocols/sametime/im_mime.c
-libpurple/protocols/sametime/sametime.c
-libpurple/protocols/sametime/tests/test_sametime_im_mime.c
libpurple/proxy.c
libpurple/purpleaccountmanager.c
libpurple/purpleaccountoption.c