Mercurial > pidgin
comparison libpurple/protocols/jabber/si.c @ 25772:a34d6975c239
propagate from branch 'im.pidgin.pidgin' (head 3da4a61feea56ff40dc5fdba355a1057c675a32d)
to branch 'im.pidgin.cpw.malu.xmpp.ibb_ft' (head ad767a71b308c8f96d42a30bc59997f9bd395375)
author | Marcus Lundblad <ml@update.uu.se> |
---|---|
date | Fri, 06 Feb 2009 22:48:37 +0000 |
parents | e30e9779e7bf 5456120de480 |
children | da4e18e318e7 |
comparison
equal
deleted
inserted
replaced
25429:8f5a63aebdb6 | 25772:a34d6975c239 |
---|---|
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 |
1221 if((xfer = jabber_si_xfer_find(js, stream_id, from))) | 1623 if((xfer = jabber_si_xfer_find(js, stream_id, from))) |
1222 return; | 1624 return; |
1223 | 1625 |
1224 jsx = g_new0(JabberSIXfer, 1); | 1626 jsx = g_new0(JabberSIXfer, 1); |
1225 jsx->local_streamhost_fd = -1; | 1627 jsx->local_streamhost_fd = -1; |
1628 | |
1629 jsx->ibb_session = NULL; | |
1226 | 1630 |
1227 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | 1631 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { |
1228 const char *var = xmlnode_get_attrib(field, "var"); | 1632 const char *var = xmlnode_get_attrib(field, "var"); |
1229 if(var && !strcmp(var, "stream-method")) { | 1633 if(var && !strcmp(var, "stream-method")) { |
1230 for(option = xmlnode_get_child(field, "option"); option; | 1634 for(option = xmlnode_get_child(field, "option"); option; |
1232 if((value = xmlnode_get_child(option, "value"))) { | 1636 if((value = xmlnode_get_child(option, "value"))) { |
1233 char *val; | 1637 char *val; |
1234 if((val = xmlnode_get_data(value))) { | 1638 if((val = xmlnode_get_data(value))) { |
1235 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) { | 1639 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) { |
1236 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; | 1640 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; |
1237 /* | |
1238 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) { | 1641 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) { |
1239 jsx->stream_method |= STREAM_METHOD_IBB; | 1642 jsx->stream_method |= STREAM_METHOD_IBB; |
1240 */ | |
1241 } | 1643 } |
1242 g_free(val); | 1644 g_free(val); |
1243 } | 1645 } |
1244 } | 1646 } |
1245 } | 1647 } |
1273 | 1675 |
1274 purple_xfer_request(xfer); | 1676 purple_xfer_request(xfer); |
1275 } | 1677 } |
1276 } | 1678 } |
1277 | 1679 |
1278 | 1680 void |
1681 jabber_si_init(void) | |
1682 { | |
1683 jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb); | |
1684 } | |
1685 | |
1686 void | |
1687 jabber_si_uninit(void) | |
1688 { | |
1689 jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb); | |
1690 } | |
1691 |