comparison libpurple/protocols/jabber/si.c @ 25773:da4e18e318e7

propagate from branch 'im.pidgin.pidgin' (head b79100551ea19cf35ee8952a34a44b97204e75f3) to branch 'im.pidgin.cpw.malu.xmpp.ibb_ft' (head ff75b10d74fa79731602410b5122236fdef7502b)
author Marcus Lundblad <ml@update.uu.se>
date Thu, 12 Feb 2009 21:48:48 +0000
parents a34d6975c239
children 5f9a24d1c25e
comparison
equal deleted inserted replaced
25449:fa5987472e00 25773:da4e18e318e7
30 #include "notify.h" 30 #include "notify.h"
31 31
32 #include "buddy.h" 32 #include "buddy.h"
33 #include "disco.h" 33 #include "disco.h"
34 #include "jabber.h" 34 #include "jabber.h"
35 #include "ibb.h"
35 #include "iq.h" 36 #include "iq.h"
36 #include "si.h" 37 #include "si.h"
37 38
38 #define STREAMHOST_CONNECT_TIMEOUT 15 39 #define STREAMHOST_CONNECT_TIMEOUT 15
39 40
61 62
62 char *rxqueue; 63 char *rxqueue;
63 size_t rxlen; 64 size_t rxlen;
64 gsize rxmaxlen; 65 gsize rxmaxlen;
65 int local_streamhost_fd; 66 int local_streamhost_fd;
67
68 JabberIBBSession *ibb_session;
69 guint ibb_timeout_handle;
70 FILE *fp;
66 } JabberSIXfer; 71 } JabberSIXfer;
72
73 /* some forward declarations */
74 static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
67 75
68 static PurpleXfer* 76 static PurpleXfer*
69 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from) 77 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
70 { 78 {
71 GList *xfers; 79 GList *xfers;
176 jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded."); 184 jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded.");
177 185
178 return FALSE; 186 return FALSE;
179 } 187 }
180 188
189 static void
190 jabber_si_bytestreams_ibb_timeout_remove(JabberSIXfer *jsx)
191 {
192 if (jsx->ibb_timeout_handle) {
193 purple_timeout_remove(jsx->ibb_timeout_handle);
194 jsx->ibb_timeout_handle = 0;
195 }
196 }
197
198 static gboolean
199 jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
200 {
201 PurpleXfer *xfer = (PurpleXfer *) data;
202 JabberSIXfer *jsx = xfer->data;
203
204 if (jsx && !jsx->ibb_session) {
205 purple_debug_info("jabber",
206 "jabber_si_bytestreams_ibb_timeout called and IBB session not set "
207 " up yet, cancel transfer");
208 jabber_si_bytestreams_ibb_timeout_remove(jsx);
209 purple_xfer_cancel_local(xfer);
210 }
211
212 return FALSE;
213 }
214
181 static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer) 215 static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
182 { 216 {
183 JabberSIXfer *jsx = xfer->data; 217 JabberSIXfer *jsx = xfer->data;
184 JabberBytestreamsStreamhost *streamhost; 218 JabberBytestreamsStreamhost *streamhost;
185 JabberID *dstjid; 219 JabberID *dstjid;
198 inf = xmlnode_new_child(error, "item-not-found"); 232 inf = xmlnode_new_child(error, "item-not-found");
199 xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas"); 233 xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas");
200 234
201 jabber_iq_send(iq); 235 jabber_iq_send(iq);
202 236
203 purple_xfer_cancel_local(xfer); 237 /* if IBB is available, revert to that before giving up... */
204 238 if (jsx->stream_method & STREAM_METHOD_IBB) {
239 /* if we are the initializer, init IBB */
240 purple_debug_info("jabber",
241 "jabber_si_bytestreams_attempt_connect: "
242 "no streamhosts found, trying IBB\n");
243 /* if we are the sender, open an IBB session, but not if we already
244 did it, since we could have received the error <iq/> from the
245 receiver already... */
246 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
247 && !jsx->ibb_session) {
248 jabber_si_xfer_ibb_send_init(jsx->js, xfer);
249 } else {
250 /* setup a timeout to cancel waiting for IBB open */
251 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
252 jabber_si_bytestreams_ibb_timeout_cb, xfer);
253 }
254 /* if we are the receiver, just wait for IBB open, callback is
255 already set up... */
256 } else {
257 purple_xfer_cancel_local(xfer);
258 }
259
205 return; 260 return;
206 } 261 }
207 262
208 streamhost = jsx->streamhosts->data; 263 streamhost = jsx->streamhosts->data;
209 264
652 return; 707 return;
653 708
654 jsx = xfer->data; 709 jsx = xfer->data;
655 710
656 if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) { 711 if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
657 if (type && !strcmp(type, "error")) 712 purple_debug_info("jabber",
658 purple_xfer_cancel_remote(xfer); 713 "jabber_si_xfer_connect_proxy_cb: type = %s\n",
714 type);
715 if (type && !strcmp(type, "error")) {
716 /* if IBB is available, open IBB session */
717 purple_debug_info("jabber",
718 "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
719 jsx->stream_method);
720 if (jsx->stream_method & STREAM_METHOD_IBB) {
721 purple_debug_info("jabber", "IBB is possible, try it\n");
722 /* if we are the sender and haven't already opened an IBB
723 session, do so now (we might already have failed to open
724 the bytestream proxy ourselves when receiving this <iq/> */
725 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
726 && !jsx->ibb_session) {
727 jabber_si_xfer_ibb_send_init(js, xfer);
728 } else {
729 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
730 jabber_si_bytestreams_ibb_timeout_cb, xfer);
731 }
732 /* if we are receiver, just wait for IBB open stanza, callback
733 is already set up */
734 } else {
735 purple_xfer_cancel_remote(xfer);
736 }
737 }
659 return; 738 return;
660 } 739 }
661 740
662 if(!(from = xmlnode_get_attrib(packet, "from"))) 741 if(!(from = xmlnode_get_attrib(packet, "from")))
663 return; 742 return;
680 jsx->js->user->domain, jsx->js->user->resource); 759 jsx->js->user->domain, jsx->js->user->resource);
681 if (!strcmp(jid, my_jid)) { 760 if (!strcmp(jid, my_jid)) {
682 purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n"); 761 purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
683 purple_xfer_start(xfer, xfer->fd, NULL, -1); 762 purple_xfer_start(xfer, xfer->fd, NULL, -1);
684 } else { 763 } else {
685 purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n"); 764 /* if available, try to revert to IBB... */
686 purple_xfer_cancel_local(xfer); 765 if (jsx->stream_method & STREAM_METHOD_IBB) {
766 purple_debug_info("jabber",
767 "jabber_si_connect_proxy_cb: trying to revert to IBB\n");
768 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
769 jabber_si_xfer_ibb_send_init(jsx->js, xfer);
770 } else {
771 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
772 jabber_si_bytestreams_ibb_timeout_cb, xfer);
773 }
774 /* if we are the receiver, we are already set up...*/
775 } else {
776 purple_debug_info("jabber",
777 "streamhost-used does not match any proxy that was offered to target\n");
778 purple_xfer_cancel_local(xfer);
779 }
687 } 780 }
688 g_free(my_jid); 781 g_free(my_jid);
689 return; 782 return;
690 } 783 }
691 784
808 } 901 }
809 902
810 /* We have no way of transferring, cancel the transfer */ 903 /* We have no way of transferring, cancel the transfer */
811 if (streamhost_count == 0) { 904 if (streamhost_count == 0) {
812 jabber_iq_free(iq); 905 jabber_iq_free(iq);
813 /* We should probably notify the target, but this really shouldn't ever happen */ 906
814 purple_xfer_cancel_local(xfer); 907 /* if available, revert to IBB */
908 if (jsx->stream_method & STREAM_METHOD_IBB) {
909 purple_debug_info("jabber",
910 "jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
911 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
912 /* if we are the sender, init the IBB session... */
913 jabber_si_xfer_ibb_send_init(jsx->js, xfer);
914 } else {
915 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
916 jabber_si_bytestreams_ibb_timeout_cb, xfer);
917 }
918 /* if we are the receiver, we should just wait... the IBB open
919 handler has already been set up... */
920 } else {
921 /* We should probably notify the target,
922 but this really shouldn't ever happen */
923 purple_xfer_cancel_local(xfer);
924 }
925
815 return; 926 return;
816 } 927 }
817 928
818 jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer); 929 jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
819 930
839 jabber_si_xfer_bytestreams_listen_cb(-1, xfer); 950 jabber_si_xfer_bytestreams_listen_cb(-1, xfer);
840 } 951 }
841 952
842 } 953 }
843 954
955 static void
956 jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess)
957 {
958 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
959 JabberStream *js = jabber_ibb_session_get_js(sess);
960 PurpleConnection *gc = js->gc;
961 PurpleAccount *account = purple_connection_get_account(gc);
962
963 purple_debug_error("jabber", "an error occured during IBB file transfer\n");
964 purple_xfer_error(purple_xfer_get_type(xfer), account,
965 jabber_ibb_session_get_who(sess),
966 _("An error occured on the in-band bytestream transfer\n"));
967 purple_xfer_cancel_remote(xfer);
968 }
969
970 static void
971 jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess)
972 {
973 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
974 JabberStream *js = jabber_ibb_session_get_js(sess);
975 PurpleConnection *gc = js->gc;
976 PurpleAccount *account = purple_connection_get_account(gc);
977
978 purple_debug_info("jabber", "the remote user closed the transfer\n");
979 if (purple_xfer_get_bytes_remaining(xfer) > 0) {
980 purple_xfer_error(purple_xfer_get_type(xfer), account,
981 jabber_ibb_session_get_who(sess), _("Transfer was closed."));
982 purple_xfer_cancel_remote(xfer);
983 } else {
984 purple_xfer_set_completed(xfer, TRUE);
985 purple_xfer_end(xfer);
986 }
987 }
988
989 static void
990 jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
991 gsize size)
992 {
993 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
994 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
995
996 if (size <= purple_xfer_get_bytes_remaining(xfer)) {
997 purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n",
998 size);
999 if(!fwrite(data, size, 1, jsx->fp)) {
1000 purple_debug_error("jabber", "error writing to file\n");
1001 purple_xfer_cancel_remote(xfer);
1002 return;
1003 }
1004 purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + size);
1005 purple_xfer_update_progress(xfer);
1006
1007 if (purple_xfer_get_bytes_remaining(xfer) == 0) {
1008 purple_xfer_set_completed(xfer, TRUE);
1009 purple_xfer_end(xfer);
1010 }
1011 } else {
1012 /* trying to write past size of file transfers negotiated size,
1013 reject transfer to protect against malicious behaviour */
1014 purple_debug_error("jabber",
1015 "IBB file transfer send more data than expected\n");
1016 purple_xfer_cancel_remote(xfer);
1017 }
1018
1019 }
1020
1021 static gboolean
1022 jabber_si_xfer_ibb_open_cb(JabberStream *js, xmlnode *packet)
1023 {
1024 const gchar *who = xmlnode_get_attrib(packet, "from");
1025 xmlnode *open = xmlnode_get_child(packet, "open");
1026 const gchar *sid = xmlnode_get_attrib(open, "sid");
1027 PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
1028 if (xfer) {
1029 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1030 JabberIBBSession *sess =
1031 jabber_ibb_session_create_from_xmlnode(js, packet, xfer);
1032 const char *filename;
1033
1034 jabber_si_bytestreams_ibb_timeout_remove(jsx);
1035
1036 if (sess) {
1037 /* open the file to write to */
1038 filename = purple_xfer_get_local_filename(xfer);
1039 jsx->fp = g_fopen(filename, "wb");
1040 if (jsx->fp == NULL) {
1041 purple_debug_error("jabber", "failed to open file %s for writing: %s\n",
1042 filename, g_strerror(errno));
1043 purple_xfer_cancel_remote(xfer);
1044 return FALSE;
1045 }
1046
1047 /* setup callbacks here...*/
1048 jabber_ibb_session_set_data_received_callback(sess,
1049 jabber_si_xfer_ibb_recv_data_cb);
1050 jabber_ibb_session_set_closed_callback(sess,
1051 jabber_si_xfer_ibb_closed_cb);
1052 jabber_ibb_session_set_error_callback(sess,
1053 jabber_si_xfer_ibb_error_cb);
1054
1055 jsx->ibb_session = sess;
1056
1057 /* start the transfer */
1058 purple_xfer_start(xfer, 0, NULL, 0);
1059 return TRUE;
1060 } else {
1061 /* failed to create IBB session */
1062 purple_debug_error("jabber", "failed to create IBB session\n");
1063 purple_xfer_cancel_remote(xfer);
1064 return FALSE;
1065 }
1066 } else {
1067 /* we got an IBB <open/> for an unknown file transfer, pass along... */
1068 purple_debug_info("jabber",
1069 "IBB open did not match any SI file transfer\n");
1070 return FALSE;
1071 }
1072 }
1073
1074 static void
1075 jabber_si_xfer_ibb_send_data(JabberIBBSession *sess)
1076 {
1077 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1078 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1079 gsize remaining = purple_xfer_get_bytes_remaining(xfer);
1080 gsize packet_size = remaining < jabber_ibb_session_get_block_size(sess) ?
1081 remaining : jabber_ibb_session_get_block_size(sess);
1082 gpointer data = g_malloc(packet_size);
1083 int res;
1084
1085 purple_debug_info("jabber", "IBB: about to read %" G_GSIZE_FORMAT " bytes from file %p\n",
1086 packet_size, jsx->fp);
1087 res = fread(data, packet_size, 1, jsx->fp);
1088
1089 if (res == 1) {
1090 jabber_ibb_session_send_data(sess, data, packet_size);
1091 purple_xfer_set_bytes_sent(xfer,
1092 purple_xfer_get_bytes_sent(xfer) + packet_size);
1093 purple_xfer_update_progress(xfer);
1094 } else {
1095 purple_debug_error("jabber",
1096 "jabber_si_xfer_ibb_send_data: error reading from file\n");
1097 purple_xfer_cancel_local(xfer);
1098 }
1099 }
1100
1101 static void
1102 jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
1103 {
1104 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1105 gsize remaining = purple_xfer_get_bytes_remaining(xfer);
1106
1107 if (remaining == 0) {
1108 /* close the session */
1109 jabber_ibb_session_close(sess);
1110 purple_xfer_set_completed(xfer, TRUE);
1111 purple_xfer_end(xfer);
1112 } else {
1113 /* send more... */
1114 jabber_si_xfer_ibb_send_data(sess);
1115 }
1116 }
1117
1118 static void
1119 jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
1120 {
1121 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1122 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1123 JabberStream *js = jabber_ibb_session_get_js(sess);
1124 PurpleConnection *gc = js->gc;
1125 PurpleAccount *account = purple_connection_get_account(gc);
1126
1127 if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
1128 const char *filename = purple_xfer_get_local_filename(xfer);
1129 jsx->fp = g_fopen(filename, "rb");
1130 if (jsx->fp == NULL) {
1131 purple_debug_error("jabber", "Failed to open file %s for reading: %s\n",
1132 filename, g_strerror(errno));
1133 purple_xfer_error(purple_xfer_get_type(xfer), account,
1134 jabber_ibb_session_get_who(sess),
1135 _("Failed to open the file"));
1136 purple_xfer_cancel_local(xfer);
1137 return;
1138 }
1139
1140 purple_xfer_start(xfer, 0, NULL, 0);
1141 purple_xfer_set_bytes_sent(xfer, 0);
1142 purple_xfer_update_progress(xfer);
1143 jabber_si_xfer_ibb_send_data(sess);
1144 } else {
1145 /* error */
1146 purple_xfer_error(purple_xfer_get_type(xfer), account,
1147 jabber_ibb_session_get_who(sess),
1148 _("Failed to open in-band bytestream"));
1149 purple_xfer_end(xfer);
1150 }
1151 }
1152
1153 static void
1154 jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
1155 {
1156 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1157
1158 purple_xfer_ref(xfer);
1159
1160 jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
1161 purple_xfer_get_remote_user(xfer), xfer);
1162
1163 if (jsx->ibb_session) {
1164 /* should set callbacks here... */
1165 jabber_ibb_session_set_opened_callback(jsx->ibb_session,
1166 jabber_si_xfer_ibb_opened_cb);
1167 jabber_ibb_session_set_data_sent_callback(jsx->ibb_session,
1168 jabber_si_xfer_ibb_sent_cb);
1169 jabber_ibb_session_set_closed_callback(jsx->ibb_session,
1170 jabber_si_xfer_ibb_closed_cb);
1171 jabber_ibb_session_set_error_callback(jsx->ibb_session,
1172 jabber_si_xfer_ibb_error_cb);
1173
1174 /* open the IBB session */
1175 jabber_ibb_session_open(jsx->ibb_session);
1176
1177 } else {
1178 /* failed to create IBB session */
1179 purple_debug_error("jabber",
1180 "failed to initiate IBB session for file transfer\n");
1181 purple_xfer_cancel_local(xfer);
1182 }
1183 }
1184
844 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, 1185 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
845 gpointer data) 1186 gpointer data)
846 { 1187 {
847 PurpleXfer *xfer = data; 1188 PurpleXfer *xfer = data;
848 xmlnode *si, *feature, *x, *field, *value; 1189 xmlnode *si, *feature, *x, *field, *value;
1190 gboolean found_method = FALSE;
849 1191
850 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { 1192 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
851 purple_xfer_cancel_remote(xfer); 1193 purple_xfer_cancel_remote(xfer);
852 return; 1194 return;
853 } 1195 }
862 return; 1204 return;
863 } 1205 }
864 1206
865 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { 1207 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
866 const char *var = xmlnode_get_attrib(field, "var"); 1208 const char *var = xmlnode_get_attrib(field, "var");
867 1209 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1210
868 if(var && !strcmp(var, "stream-method")) { 1211 if(var && !strcmp(var, "stream-method")) {
869 if((value = xmlnode_get_child(field, "value"))) { 1212 if((value = xmlnode_get_child(field, "value"))) {
870 char *val = xmlnode_get_data(value); 1213 char *val = xmlnode_get_data(value);
871 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) { 1214 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
872 jabber_si_xfer_bytestreams_send_init(xfer); 1215 jabber_si_xfer_bytestreams_send_init(xfer);
873 g_free(val); 1216 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
874 return; 1217 found_method = TRUE;
1218 } else if (val && !strcmp(val, XEP_0047_NAMESPACE)) {
1219 jsx->stream_method |= STREAM_METHOD_IBB;
1220 if (!found_method) {
1221 /* we haven't tried to init a bytestream session, yet
1222 start IBB right away... */
1223 jabber_si_xfer_ibb_send_init(js, xfer);
1224 found_method = TRUE;
1225 }
875 } 1226 }
876 g_free(val); 1227 g_free(val);
877 } 1228 }
878 } 1229 }
879 } 1230 }
880 purple_xfer_cancel_remote(xfer); 1231
1232 if (!found_method) {
1233 purple_xfer_cancel_remote(xfer);
1234 }
1235
881 } 1236 }
882 1237
883 static void jabber_si_xfer_send_request(PurpleXfer *xfer) 1238 static void jabber_si_xfer_send_request(PurpleXfer *xfer)
884 { 1239 {
885 JabberSIXfer *jsx = xfer->data; 1240 JabberSIXfer *jsx = xfer->data;
912 xmlnode_set_namespace(x, "jabber:x:data"); 1267 xmlnode_set_namespace(x, "jabber:x:data");
913 xmlnode_set_attrib(x, "type", "form"); 1268 xmlnode_set_attrib(x, "type", "form");
914 field = xmlnode_new_child(x, "field"); 1269 field = xmlnode_new_child(x, "field");
915 xmlnode_set_attrib(field, "var", "stream-method"); 1270 xmlnode_set_attrib(field, "var", "stream-method");
916 xmlnode_set_attrib(field, "type", "list-single"); 1271 xmlnode_set_attrib(field, "type", "list-single");
1272 /* maybe we should add an option to always skip bytestreams for people
1273 behind troublesome firewalls */
917 option = xmlnode_new_child(field, "option"); 1274 option = xmlnode_new_child(field, "option");
918 value = xmlnode_new_child(option, "value"); 1275 value = xmlnode_new_child(option, "value");
919 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); 1276 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
920 /*
921 option = xmlnode_new_child(field, "option"); 1277 option = xmlnode_new_child(field, "option");
922 value = xmlnode_new_child(option, "value"); 1278 value = xmlnode_new_child(option, "value");
923 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); 1279 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
924 */
925 1280
926 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); 1281 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
927 1282
928 /* Store the IQ id so that we can cancel the callback */ 1283 /* Store the IQ id so that we can cancel the callback */
929 g_free(jsx->iq_id); 1284 g_free(jsx->iq_id);
933 } 1288 }
934 1289
935 static void jabber_si_xfer_free(PurpleXfer *xfer) 1290 static void jabber_si_xfer_free(PurpleXfer *xfer)
936 { 1291 {
937 JabberSIXfer *jsx = xfer->data; 1292 JabberSIXfer *jsx = xfer->data;
938 JabberStream *js = jsx->js; 1293
939 1294 if (jsx) {
940 js->file_transfers = g_list_remove(js->file_transfers, xfer); 1295 JabberStream *js = jsx->js;
941 1296
942 if (jsx->connect_data != NULL) 1297 js->file_transfers = g_list_remove(js->file_transfers, xfer);
943 purple_proxy_connect_cancel(jsx->connect_data); 1298
944 if (jsx->listen_data != NULL) 1299 if (jsx->connect_data != NULL)
945 purple_network_listen_cancel(jsx->listen_data); 1300 purple_proxy_connect_cancel(jsx->connect_data);
946 if (jsx->iq_id != NULL) 1301 if (jsx->listen_data != NULL)
947 jabber_iq_remove_callback_by_id(js, jsx->iq_id); 1302 purple_network_listen_cancel(jsx->listen_data);
948 if (jsx->local_streamhost_fd >= 0) 1303 if (jsx->iq_id != NULL)
949 close(jsx->local_streamhost_fd); 1304 jabber_iq_remove_callback_by_id(js, jsx->iq_id);
950 if (jsx->connect_timeout > 0) 1305 if (jsx->local_streamhost_fd >= 0)
951 purple_timeout_remove(jsx->connect_timeout); 1306 close(jsx->local_streamhost_fd);
952 1307 if (jsx->connect_timeout > 0)
953 if (jsx->streamhosts) { 1308 purple_timeout_remove(jsx->connect_timeout);
954 g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL); 1309 if (jsx->ibb_timeout_handle > 0)
955 g_list_free(jsx->streamhosts); 1310 purple_timeout_remove(jsx->ibb_timeout_handle);
956 } 1311
957 1312 if (jsx->streamhosts) {
958 g_free(jsx->stream_id); 1313 g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
959 g_free(jsx->iq_id); 1314 g_list_free(jsx->streamhosts);
960 /* XXX: free other stuff */ 1315 }
961 g_free(jsx->rxqueue); 1316
962 g_free(jsx); 1317 if (jsx->ibb_session) {
963 xfer->data = NULL; 1318 purple_debug_info("jabber",
964 1319 "jabber_si_xfer_free: destroying IBB session\n");
965 purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p", jsx); 1320 jabber_ibb_session_destroy(jsx->ibb_session);
966 } 1321 }
967 1322
1323 if (jsx->fp) {
1324 purple_debug_info("jabber",
1325 "jabber_si_xfer_free: closing file for IBB transfer\n");
1326 fclose(jsx->fp);
1327 }
1328
1329 g_free(jsx->stream_id);
1330 g_free(jsx->iq_id);
1331 /* XXX: free other stuff */
1332 g_free(jsx->rxqueue);
1333 g_free(jsx);
1334 xfer->data = NULL;
1335
1336 purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
1337 }
1338 }
1339
1340 /*
1341 * These four functions should only be called from the PurpleXfer functions
1342 * (typically purple_xfer_cancel_(remote|local), purple_xfer_end, or
1343 * purple_xfer_request_denied.
1344 */
968 static void jabber_si_xfer_cancel_send(PurpleXfer *xfer) 1345 static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
969 { 1346 {
1347 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1348
1349 /* if there is an IBB session active, send close on that */
1350 if (jsx->ibb_session) {
1351 jabber_ibb_session_close(jsx->ibb_session);
1352 }
970 jabber_si_xfer_free(xfer); 1353 jabber_si_xfer_free(xfer);
971 purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n"); 1354 purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n");
972 } 1355 }
973 1356
974 1357
979 } 1362 }
980 1363
981 1364
982 static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer) 1365 static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
983 { 1366 {
1367 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1368 /* if there is an IBB session active, send close */
1369 if (jsx->ibb_session) {
1370 jabber_ibb_session_close(jsx->ibb_session);
1371 }
984 jabber_si_xfer_free(xfer); 1372 jabber_si_xfer_free(xfer);
985 purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n"); 1373 purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n");
986 } 1374 }
987 1375
988 1376
993 1381
994 1382
995 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who, 1383 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
996 JabberCapabilities capabilities, gpointer data) 1384 JabberCapabilities capabilities, gpointer data)
997 { 1385 {
998 PurpleXfer *xfer = data; 1386 PurpleXfer *xfer = (PurpleXfer *) data;
999 1387 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1000 if(capabilities & JABBER_CAP_SI_FILE_XFER) { 1388
1389 if (capabilities & JABBER_CAP_IBB) {
1390 purple_debug_info("jabber",
1391 "jabber_si_xfer_send_disco_cb: remote JID supports IBB\n");
1392 jsx->stream_method |= STREAM_METHOD_IBB;
1393 }
1394
1395 if (capabilities & JABBER_CAP_SI_FILE_XFER) {
1001 jabber_si_xfer_send_request(xfer); 1396 jabber_si_xfer_send_request(xfer);
1002 } else { 1397 } else {
1003 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); 1398 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
1004 purple_notify_error(js->gc, _("File Send Failed"), 1399 purple_notify_error(js->gc, _("File Send Failed"),
1005 _("File Send Failed"), msg); 1400 _("File Send Failed"), msg);
1122 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); 1517 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
1123 1518
1124 x = xmlnode_new_child(feature, "x"); 1519 x = xmlnode_new_child(feature, "x");
1125 xmlnode_set_namespace(x, "jabber:x:data"); 1520 xmlnode_set_namespace(x, "jabber:x:data");
1126 xmlnode_set_attrib(x, "type", "submit"); 1521 xmlnode_set_attrib(x, "type", "submit");
1127
1128 field = xmlnode_new_child(x, "field"); 1522 field = xmlnode_new_child(x, "field");
1129 xmlnode_set_attrib(field, "var", "stream-method"); 1523 xmlnode_set_attrib(field, "var", "stream-method");
1130 1524
1131 value = xmlnode_new_child(field, "value"); 1525 /* we should maybe "remember" if bytestreams has failed before (in the
1132 if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) 1526 same session) with this JID, and only present IBB as an option to
1527 avoid unnessesary timeout */
1528 /* maybe we should have an account option to always just try IBB
1529 for people who know their firewalls are very restrictive */
1530 if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
1531 value = xmlnode_new_child(field, "value");
1133 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); 1532 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
1134 /* 1533 } else if(jsx->stream_method & STREAM_METHOD_IBB) {
1135 else if(jsx->stream_method & STREAM_METHOD_IBB) 1534 value = xmlnode_new_child(field, "value");
1136 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); 1535 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
1137 */ 1536 }
1138 1537
1139 jabber_iq_send(iq); 1538 jabber_iq_send(iq);
1140 } 1539 }
1141 } 1540 }
1142 1541
1143 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who) 1542 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who)
1153 if (xfer) 1552 if (xfer)
1154 { 1553 {
1155 xfer->data = jsx = g_new0(JabberSIXfer, 1); 1554 xfer->data = jsx = g_new0(JabberSIXfer, 1);
1156 jsx->js = js; 1555 jsx->js = js;
1157 jsx->local_streamhost_fd = -1; 1556 jsx->local_streamhost_fd = -1;
1557
1558 jsx->ibb_session = NULL;
1559 jsx->fp = NULL;
1158 1560
1159 purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init); 1561 purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
1160 purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send); 1562 purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
1161 purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end); 1563 purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
1162 1564
1224 if((xfer = jabber_si_xfer_find(js, stream_id, from))) 1626 if((xfer = jabber_si_xfer_find(js, stream_id, from)))
1225 return; 1627 return;
1226 1628
1227 jsx = g_new0(JabberSIXfer, 1); 1629 jsx = g_new0(JabberSIXfer, 1);
1228 jsx->local_streamhost_fd = -1; 1630 jsx->local_streamhost_fd = -1;
1631
1632 jsx->ibb_session = NULL;
1229 1633
1230 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { 1634 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
1231 const char *var = xmlnode_get_attrib(field, "var"); 1635 const char *var = xmlnode_get_attrib(field, "var");
1232 if(var && !strcmp(var, "stream-method")) { 1636 if(var && !strcmp(var, "stream-method")) {
1233 for(option = xmlnode_get_child(field, "option"); option; 1637 for(option = xmlnode_get_child(field, "option"); option;
1235 if((value = xmlnode_get_child(option, "value"))) { 1639 if((value = xmlnode_get_child(option, "value"))) {
1236 char *val; 1640 char *val;
1237 if((val = xmlnode_get_data(value))) { 1641 if((val = xmlnode_get_data(value))) {
1238 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) { 1642 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
1239 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; 1643 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
1240 /*
1241 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) { 1644 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
1242 jsx->stream_method |= STREAM_METHOD_IBB; 1645 jsx->stream_method |= STREAM_METHOD_IBB;
1243 */
1244 } 1646 }
1245 g_free(val); 1647 g_free(val);
1246 } 1648 }
1247 } 1649 }
1248 } 1650 }
1276 1678
1277 purple_xfer_request(xfer); 1679 purple_xfer_request(xfer);
1278 } 1680 }
1279 } 1681 }
1280 1682
1281 1683 void
1684 jabber_si_init(void)
1685 {
1686 jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
1687 }
1688
1689 void
1690 jabber_si_uninit(void)
1691 {
1692 jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
1693 }
1694