Sun, 04 Jan 2004 23:07:41 +0000
[gaim-migrate @ 8676]
this fixes the add/remove button showing up incorrectly for jabber chat rooms
when you add or remove a chat, the button doesn't automagically update in the
chat window. i'm too hungry to figure that out right now, so someone else
should do it.
| 7395 | 1 | /* |
| 2 | * gaim - Jabber Protocol Plugin | |
| 3 | * | |
| 4 | * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> | |
| 5 | * | |
| 6 | * This program is free software; you can redistribute it and/or modify | |
| 7 | * it under the terms of the GNU General Public License as published by | |
| 8 | * the Free Software Foundation; either version 2 of the License, or | |
| 9 | * (at your option) any later version. | |
| 10 | * | |
| 11 | * This program is distributed in the hope that it will be useful, | |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | * GNU General Public License for more details. | |
| 15 | * | |
| 16 | * You should have received a copy of the GNU General Public License | |
| 17 | * along with this program; if not, write to the Free Software | |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 19 | * | |
| 20 | */ | |
| 21 | #include "internal.h" | |
| 22 | #include "debug.h" | |
| 23 | #include "ft.h" | |
| 24 | #include "notify.h" | |
| 25 | #include "util.h" | |
| 26 | ||
| 27 | #include "buddy.h" | |
| 28 | #include "jabber.h" | |
| 29 | #include "iq.h" | |
| 30 | #include "si.h" | |
| 31 | ||
| 32 | #include "si.h" | |
| 33 | ||
| 34 | static GaimXfer *jabber_si_xfer_find_by_id(JabberStream *js, const char *id) | |
| 35 | { | |
| 36 | GList *xfers; | |
| 37 | ||
| 38 | if(!id) | |
| 39 | return NULL; | |
| 40 | ||
| 41 | for(xfers = js->file_transfers; xfers; xfers = xfers->next) { | |
| 42 | GaimXfer *xfer = xfers->data; | |
| 43 | JabberSIXfer *jsx = xfer->data; | |
| 44 | ||
| 45 | if(!strcmp(jsx->id, id)) | |
| 46 | return xfer; | |
| 47 | } | |
| 48 | ||
| 49 | return NULL; | |
| 50 | } | |
| 51 | ||
| 52 | static void | |
| 53 | jabber_si_xfer_ibb_start(JabberStream *js, xmlnode *packet, gpointer data) { | |
| 54 | GaimXfer *xfer = data; | |
| 55 | JabberSIXfer *jsx = xfer->data; | |
| 56 | ||
| 57 | /* Make sure we didn't get an error back */ | |
| 58 | ||
| 59 | /* XXX: OK, here we need to set up a g_idle thing to send messages | |
| 60 | * until our eyes bleed, but do it without interfering with normal | |
| 61 | * gaim operations. When we're done, we have to send a <close> like | |
| 62 | * we sent the <open> to start this damn thing. If we're really | |
| 63 | * fortunate, Exodus or someone else will implement something to test | |
| 64 | * against soon */ | |
| 65 | } | |
| 66 | ||
| 67 | void jabber_si_parse(JabberStream *js, xmlnode *packet) | |
| 68 | { | |
| 69 | GaimXfer *xfer; | |
| 70 | JabberSIXfer *jsx; | |
| 71 | xmlnode *si, *feature, *x, *field, *value; | |
| 72 | ||
| 73 | si = xmlnode_get_child(packet, "si"); | |
| 74 | ||
| 75 | xfer = jabber_si_xfer_find_by_id(js, xmlnode_get_attrib(si, "id")); | |
| 76 | ||
| 77 | if(!xfer) | |
| 78 | return; | |
| 79 | ||
| 80 | jsx = xfer->data; | |
| 81 | ||
| 82 | if(!(feature = xmlnode_get_child(si, "feature"))) | |
| 83 | return; | |
| 84 | ||
| 85 | for(x = feature->child; x; x = x->next) { | |
| 86 | const char *xmlns; | |
| 87 | if(x->type != NODE_TYPE_TAG) | |
| 88 | continue; | |
| 89 | if(!(xmlns = xmlnode_get_attrib(x, "xmlns"))) | |
| 90 | continue; | |
| 91 | if(strcmp(xmlns, "jabber:x:data")) | |
| 92 | continue; | |
| 93 | for(field = x->child; field; field = field->next) { | |
| 94 | const char *var; | |
| 95 | if(field->type != NODE_TYPE_TAG) | |
| 96 | continue; | |
| 97 | if(!(var = xmlnode_get_attrib(field, "var"))) | |
| 98 | continue; | |
| 99 | if(!strcmp(var, "stream-method")) { | |
| 100 | if((value = xmlnode_get_child(field, "value"))) { | |
| 101 | char *val_data = xmlnode_get_data(value); | |
| 102 | if(!val_data) | |
| 103 | jsx->stream_method = STREAM_METHOD_UNKNOWN; | |
| 104 | else if(!strcmp(val_data, | |
| 105 | "http://jabber.org/protocol/bytestreams")) | |
| 106 | jsx->stream_method = STREAM_METHOD_BYTESTREAMS; | |
| 107 | else if(!strcmp(val_data, "http://jabber.org/protocol/ibb")) | |
| 108 | jsx->stream_method = STREAM_METHOD_IBB; | |
| 109 | else | |
| 110 | jsx->stream_method = STREAM_METHOD_UNSUPPORTED; | |
| 111 | g_free(val_data); | |
| 112 | } | |
| 113 | } | |
| 114 | } | |
| 115 | } | |
| 116 | if(jsx->stream_method == STREAM_METHOD_UNKNOWN) { | |
| 117 | /* XXX */ | |
| 118 | } else if(jsx->stream_method == STREAM_METHOD_UNSUPPORTED) { | |
| 119 | /* XXX */ | |
| 120 | } else if(jsx->stream_method == STREAM_METHOD_BYTESTREAMS) { | |
| 121 | /* XXX: open the port and stuff */ | |
| 122 | char *buf; | |
| 123 | xmlnode *query, *streamhost; | |
| 124 | JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_SET, | |
| 125 | "http://jabber.org/protocol/bytestreams"); | |
| 126 | ||
| 127 | buf = g_strdup_printf("%s/%s", xfer->who, jsx->resource); | |
| 128 | xmlnode_set_attrib(iq->node, "to", buf); | |
| 129 | g_free(buf); | |
| 130 | ||
| 131 | query = xmlnode_get_child(iq->node, "query"); | |
| 132 | xmlnode_set_attrib(query, "sid", jsx->id); | |
| 133 | streamhost = xmlnode_new_child(query, "streamhost"); | |
| 134 | xmlnode_set_attrib(streamhost, "jid", | |
| 135 | gaim_account_get_username(js->gc->account)); | |
| 136 | xmlnode_set_attrib(streamhost, "host", xfer->local_ip); | |
| 137 | buf = g_strdup_printf("%d", xfer->local_port); | |
| 138 | xmlnode_set_attrib(streamhost, "port", buf); | |
| 139 | g_free(buf); | |
| 140 | jabber_iq_send(iq); | |
| 141 | } else if(jsx->stream_method == STREAM_METHOD_IBB) { | |
| 142 | char *buf; | |
| 143 | xmlnode *open; | |
| 144 | JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); | |
| 145 | buf = g_strdup_printf("%s/%s", xfer->who, jsx->resource); | |
| 146 | xmlnode_set_attrib(iq->node, "to", buf); | |
| 147 | g_free(buf); | |
| 148 | ||
| 149 | open = xmlnode_new_child(iq->node, "open"); | |
| 150 | xmlnode_set_attrib(open, "xmlns", "http://jabber.org/protocol/ibb"); | |
| 151 | xmlnode_set_attrib(open, "sid", jsx->id); | |
| 152 | ||
| 153 | jabber_iq_set_callback(iq, jabber_si_xfer_ibb_start, xfer); | |
| 154 | ||
| 155 | jabber_iq_send(iq); | |
| 156 | ||
| 157 | } | |
| 158 | } | |
| 159 | ||
| 160 | static void jabber_si_xfer_send_request(GaimXfer *xfer) | |
| 161 | { | |
| 162 | JabberSIXfer *jsx = xfer->data; | |
| 163 | JabberIq *iq; | |
| 164 | xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
| 165 | char buf[32]; | |
| 166 | char *to; | |
| 167 | ||
| 168 | xfer->filename = g_path_get_basename(xfer->local_filename); | |
| 169 | ||
| 170 | iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); | |
| 171 | to = g_strdup_printf("%s/%s", xfer->who, jsx->resource); | |
| 172 | xmlnode_set_attrib(iq->node, "to", to); | |
| 173 | g_free(to); | |
| 174 | si = xmlnode_new_child(iq->node, "si"); | |
| 175 | xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si"); | |
| 176 | jsx->id = jabber_get_next_id(jsx->js); | |
| 177 | xmlnode_set_attrib(si, "id", jsx->id); | |
| 178 | xmlnode_set_attrib(si, "profile", | |
| 179 | "http://jabber.org/protocol/si/profile/file-transfer"); | |
| 180 | ||
| 181 | file = xmlnode_new_child(si, "file"); | |
| 182 | xmlnode_set_attrib(file, "xmlns", | |
| 183 | "http://jabber.org/protocol/si/profile/file-transfer"); | |
| 184 | xmlnode_set_attrib(file, "name", xfer->filename); | |
| 185 | g_snprintf(buf, sizeof(buf), "%d", xfer->size); | |
| 186 | xmlnode_set_attrib(file, "size", buf); | |
| 187 | /* maybe later we'll do hash and date attribs */ | |
| 188 | ||
| 189 | feature = xmlnode_new_child(si, "feature"); | |
| 190 | xmlnode_set_attrib(feature, "xmlns", | |
| 191 | "http://jabber.org/protocol/feature-neg"); | |
| 192 | x = xmlnode_new_child(feature, "x"); | |
| 193 | xmlnode_set_attrib(x, "xmlns", "jabber:x:data"); | |
| 194 | xmlnode_set_attrib(x, "type", "form"); | |
| 195 | field = xmlnode_new_child(x, "field"); | |
| 196 | xmlnode_set_attrib(field, "var", "stream-method"); | |
| 197 | xmlnode_set_attrib(field, "type", "list-single"); | |
| 198 | option = xmlnode_new_child(field, "option"); | |
| 199 | value = xmlnode_new_child(option, "value"); | |
| 200 | xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", | |
| 201 | -1); | |
| 202 | option = xmlnode_new_child(field, "option"); | |
| 203 | value = xmlnode_new_child(option, "value"); | |
| 204 | xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); | |
| 205 | ||
| 206 | jabber_iq_send(iq); | |
| 207 | } | |
| 208 | ||
| 209 | void jabber_si_xfer_init(GaimXfer *xfer) | |
| 210 | { | |
| 211 | JabberSIXfer *jsx = xfer->data; | |
| 212 | if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { | |
| 213 | JabberBuddy *jb; | |
| 214 | JabberBuddyResource *jbr = NULL; | |
| 215 | GList *resources; | |
| 216 | GList *xfer_resources = NULL; | |
| 217 | ||
| 218 | jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); | |
| 219 | if(!jb) | |
| 220 | return; | |
| 221 | ||
| 222 | for(resources = jb->resources; resources; resources = resources->next) { | |
| 223 | jbr = resources->data; | |
| 224 | if(jbr->capabilities & JABBER_CAP_SI_FILE_XFER) | |
| 225 | xfer_resources = g_list_append(xfer_resources, jbr); | |
| 226 | } | |
| 227 | ||
| 228 | if(g_list_length(xfer_resources) == 1) { | |
| 229 | jbr = xfer_resources->data; | |
| 230 | jsx->resource = g_strdup(jbr->name); | |
| 231 | jabber_si_xfer_send_request(xfer); | |
| 232 | } else if(g_list_length(xfer_resources) == 0) { | |
| 233 | char *buf = g_strdup_printf(_("Could not send %s to %s, protocol not supported."), xfer->filename, xfer->who); | |
| 234 | gaim_notify_error(jsx->js->gc, _("File Send Failed"), | |
| 235 | _("File Send Failed"), buf); | |
| 236 | g_free(buf); | |
| 237 | } else { | |
| 238 | /* XXX: ask which resource to send to! */ | |
| 239 | } | |
| 240 | g_list_free(xfer_resources); | |
| 241 | } | |
| 242 | } | |
| 243 | ||
| 244 | void jabber_si_xfer_start(GaimXfer *xfer) | |
| 245 | { | |
| 246 | gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_start\n"); | |
| 247 | } | |
| 248 | ||
| 249 | void jabber_si_xfer_end(GaimXfer *xfer) | |
| 250 | { | |
| 251 | gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_end\n"); | |
| 252 | } | |
| 253 | ||
| 254 | void jabber_si_xfer_cancel_send(GaimXfer *xfer) | |
| 255 | { | |
| 256 | gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n"); | |
| 257 | } | |
| 258 | ||
| 259 | ||
| 260 | void jabber_si_xfer_cancel_recv(GaimXfer *xfer) | |
| 261 | { | |
| 262 | gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n"); | |
| 263 | } | |
| 264 | ||
| 265 | ||
| 266 | void jabber_si_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size) | |
| 267 | { | |
| 268 | gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_ack\n"); | |
| 269 | } | |
| 270 |