| 851 jabber_si_xfer_bytestreams_listen_cb(-1, xfer); |
922 jabber_si_xfer_bytestreams_listen_cb(-1, xfer); |
| 852 } |
923 } |
| 853 |
924 |
| 854 } |
925 } |
| 855 |
926 |
| |
927 /* forward declare some functions here... */ |
| |
928 static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer); |
| |
929 static void jabber_si_xfer_cancel_send(PurpleXfer *xfer); |
| |
930 static void jabber_si_xfer_free(PurpleXfer *xfer); |
| |
931 |
| |
932 static void |
| |
933 jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess) |
| |
934 { |
| |
935 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); |
| |
936 JabberStream *js = jabber_ibb_session_get_js(sess); |
| |
937 PurpleConnection *gc = js->gc; |
| |
938 PurpleAccount *account = purple_connection_get_account(gc); |
| |
939 |
| |
940 purple_debug_error("jabber", "an error occured during IBB file transfer\n"); |
| |
941 purple_xfer_error(purple_xfer_get_type(xfer), account, |
| |
942 jabber_ibb_session_get_who(sess), |
| |
943 _("An error occured on the in-band bytestream transfer\n")); |
| |
944 purple_xfer_end(xfer); |
| |
945 } |
| |
946 |
| |
947 static void |
| |
948 jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess) |
| |
949 { |
| |
950 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); |
| |
951 JabberStream *js = jabber_ibb_session_get_js(sess); |
| |
952 PurpleConnection *gc = js->gc; |
| |
953 PurpleAccount *account = purple_connection_get_account(gc); |
| |
954 |
| |
955 purple_debug_info("jabber", "the remote user closed the transfer\n"); |
| |
956 if (purple_xfer_get_bytes_remaining(xfer) > 0) { |
| |
957 purple_xfer_error(purple_xfer_get_type(xfer), account, |
| |
958 jabber_ibb_session_get_who(sess), _("Transfer was closed.")); |
| |
959 purple_xfer_end(xfer); |
| |
960 } else { |
| |
961 purple_xfer_set_completed(xfer, TRUE); |
| |
962 } |
| |
963 } |
| |
964 |
| |
965 static void |
| |
966 jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data, |
| |
967 gsize size) |
| |
968 { |
| |
969 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); |
| |
970 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; |
| |
971 |
| |
972 if (size <= purple_xfer_get_bytes_remaining(xfer)) { |
| |
973 fwrite(data, size, 1, jsx->fp); |
| |
974 purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + size); |
| |
975 purple_xfer_update_progress(xfer); |
| |
976 |
| |
977 if (purple_xfer_get_bytes_remaining(xfer) == 0) { |
| |
978 purple_xfer_set_completed(xfer, TRUE); |
| |
979 } |
| |
980 } else { |
| |
981 /* trying to write past size of file transfers negotiated size, |
| |
982 reject transfer to protect against malicious behaviour */ |
| |
983 purple_debug_error("jabber", |
| |
984 "IBB file transfer, trying to write past end of file\n"); |
| |
985 jabber_si_xfer_cancel_recv(xfer); |
| |
986 } |
| |
987 |
| |
988 } |
| |
989 |
| |
990 static gboolean |
| |
991 jabber_si_xfer_ibb_open_cb(JabberStream *js, xmlnode *packet) |
| |
992 { |
| |
993 const gchar *who = xmlnode_get_attrib(packet, "from"); |
| |
994 xmlnode *open = xmlnode_get_child(packet, "open"); |
| |
995 const gchar *sid = xmlnode_get_attrib(open, "sid"); |
| |
996 PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who); |
| |
997 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; |
| |
998 JabberIBBSession *sess = |
| |
999 jabber_ibb_session_create_from_xmlnode(js, packet, xfer); |
| |
1000 |
| |
1001 if (sess) { |
| |
1002 /* setup callbacks here...*/ |
| |
1003 jabber_ibb_session_set_data_received_callback(sess, |
| |
1004 jabber_si_xfer_ibb_recv_data_cb); |
| |
1005 jabber_ibb_session_set_closed_callback(sess, |
| |
1006 jabber_si_xfer_ibb_closed_cb); |
| |
1007 jabber_ibb_session_set_error_callback(sess, |
| |
1008 jabber_si_xfer_ibb_error_cb); |
| |
1009 |
| |
1010 /* open the file to write to */ |
| |
1011 jsx->fp = g_fopen(purple_xfer_get_local_filename(xfer), "w"); |
| |
1012 |
| |
1013 jsx->ibb_session = sess; |
| |
1014 |
| |
1015 /* start the transfer */ |
| |
1016 purple_xfer_start(xfer, 0, NULL, 0); |
| |
1017 return TRUE; |
| |
1018 } else { |
| |
1019 /* failed to create IBB session */ |
| |
1020 purple_debug_error("jabber", "failed to create IBB session\n"); |
| |
1021 jabber_si_xfer_cancel_recv(xfer); |
| |
1022 return FALSE; |
| |
1023 } |
| |
1024 } |
| |
1025 |
| |
1026 static void |
| |
1027 jabber_si_xfer_ibb_send_data(JabberIBBSession *sess) |
| |
1028 { |
| |
1029 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); |
| |
1030 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; |
| |
1031 gsize remaining = purple_xfer_get_bytes_remaining(xfer); |
| |
1032 gsize packet_size = remaining < jabber_ibb_session_get_block_size(sess) ? |
| |
1033 remaining : jabber_ibb_session_get_block_size(sess); |
| |
1034 gpointer data = g_malloc(packet_size); |
| |
1035 int res; |
| |
1036 |
| |
1037 purple_debug_info("jabber", "IBB: about to read %d bytes from file %lx\n", |
| |
1038 packet_size, jsx->fp); |
| |
1039 res = fread(data, packet_size, 1, jsx->fp); |
| |
1040 |
| |
1041 if (res == 1) { |
| |
1042 jabber_ibb_session_send_data(sess, data, packet_size); |
| |
1043 purple_xfer_set_bytes_sent(xfer, |
| |
1044 purple_xfer_get_bytes_sent(xfer) + packet_size); |
| |
1045 purple_xfer_update_progress(xfer); |
| |
1046 } else { |
| |
1047 purple_debug_error("jabber", |
| |
1048 "jabber_si_xfer_ibb_opened_cb: error reading from file\n"); |
| |
1049 jabber_si_xfer_cancel_send(xfer); |
| |
1050 } |
| |
1051 } |
| |
1052 |
| |
1053 static void |
| |
1054 jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess) |
| |
1055 { |
| |
1056 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); |
| |
1057 gsize remaining = purple_xfer_get_bytes_remaining(xfer); |
| |
1058 |
| |
1059 if (remaining == 0) { |
| |
1060 /* close the session */ |
| |
1061 jabber_ibb_session_close(sess); |
| |
1062 purple_xfer_set_completed(xfer, TRUE); |
| |
1063 } else { |
| |
1064 /* send more... */ |
| |
1065 jabber_si_xfer_ibb_send_data(sess); |
| |
1066 } |
| |
1067 } |
| |
1068 |
| |
1069 static void |
| |
1070 jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess) |
| |
1071 { |
| |
1072 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess); |
| |
1073 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; |
| |
1074 |
| |
1075 purple_xfer_start(xfer, 0, NULL, 0); |
| |
1076 purple_xfer_set_bytes_sent(xfer, 0); |
| |
1077 purple_xfer_update_progress(xfer); |
| |
1078 jsx->fp = g_fopen(purple_xfer_get_local_filename(xfer), "r"); |
| |
1079 jabber_si_xfer_ibb_send_data(sess); |
| |
1080 } |
| |
1081 |
| |
1082 static void |
| |
1083 jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer) |
| |
1084 { |
| |
1085 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; |
| |
1086 |
| |
1087 purple_xfer_ref(xfer); |
| |
1088 |
| |
1089 jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id, |
| |
1090 purple_xfer_get_remote_user(xfer), xfer); |
| |
1091 |
| |
1092 if (jsx->ibb_session) { |
| |
1093 /* should set callbacks here... */ |
| |
1094 jabber_ibb_session_set_opened_callback(jsx->ibb_session, |
| |
1095 jabber_si_xfer_ibb_opened_cb); |
| |
1096 jabber_ibb_session_set_data_sent_callback(jsx->ibb_session, |
| |
1097 jabber_si_xfer_ibb_sent_cb); |
| |
1098 jabber_ibb_session_set_closed_callback(jsx->ibb_session, |
| |
1099 jabber_si_xfer_ibb_closed_cb); |
| |
1100 jabber_ibb_session_set_error_callback(jsx->ibb_session, |
| |
1101 jabber_si_xfer_ibb_error_cb); |
| |
1102 |
| |
1103 /* open the IBB session */ |
| |
1104 jabber_ibb_session_open(jsx->ibb_session); |
| |
1105 |
| |
1106 } else { |
| |
1107 /* failed to create IBB session */ |
| |
1108 purple_xfer_unref(xfer); |
| |
1109 purple_debug_error("jabber", |
| |
1110 "failed to initiate IBB session for file transfer\n"); |
| |
1111 jabber_si_xfer_cancel_send(xfer); |
| |
1112 } |
| |
1113 } |
| |
1114 |
| 856 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, |
1115 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, |
| 857 gpointer data) |
1116 gpointer data) |
| 858 { |
1117 { |
| 859 PurpleXfer *xfer = data; |
1118 PurpleXfer *xfer = data; |
| 860 xmlnode *si, *feature, *x, *field, *value; |
1119 xmlnode *si, *feature, *x, *field, *value; |
| |
1120 gboolean found_method = FALSE; |
| 861 |
1121 |
| 862 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { |
1122 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { |
| 863 purple_xfer_cancel_remote(xfer); |
1123 purple_xfer_cancel_remote(xfer); |
| 864 return; |
1124 return; |
| 865 } |
1125 } |
| 874 return; |
1134 return; |
| 875 } |
1135 } |
| 876 |
1136 |
| 877 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { |
1137 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { |
| 878 const char *var = xmlnode_get_attrib(field, "var"); |
1138 const char *var = xmlnode_get_attrib(field, "var"); |
| 879 |
1139 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; |
| |
1140 |
| 880 if(var && !strcmp(var, "stream-method")) { |
1141 if(var && !strcmp(var, "stream-method")) { |
| 881 if((value = xmlnode_get_child(field, "value"))) { |
1142 if((value = xmlnode_get_child(field, "value"))) { |
| 882 char *val = xmlnode_get_data(value); |
1143 char *val = xmlnode_get_data(value); |
| 883 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) { |
1144 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) { |
| 884 jabber_si_xfer_bytestreams_send_init(xfer); |
1145 jabber_si_xfer_bytestreams_send_init(xfer); |
| 885 g_free(val); |
1146 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; |
| 886 return; |
1147 found_method = TRUE; |
| |
1148 } else if (val && !strcmp(val, XEP_0047_NAMESPACE)) { |
| |
1149 jsx->stream_method |= STREAM_METHOD_IBB; |
| |
1150 if (!found_method) { |
| |
1151 /* we haven't tried to init a bytestream session, yet |
| |
1152 start IBB right away... */ |
| |
1153 jabber_si_xfer_ibb_send_init(js, xfer); |
| |
1154 found_method = TRUE; |
| |
1155 } |
| 887 } |
1156 } |
| 888 g_free(val); |
1157 g_free(val); |
| 889 } |
1158 } |
| 890 } |
1159 } |
| 891 } |
1160 } |
| 892 purple_xfer_cancel_remote(xfer); |
1161 |
| |
1162 if (!found_method) { |
| |
1163 purple_xfer_cancel_remote(xfer); |
| |
1164 } |
| |
1165 |
| 893 } |
1166 } |
| 894 |
1167 |
| 895 static void jabber_si_xfer_send_request(PurpleXfer *xfer) |
1168 static void jabber_si_xfer_send_request(PurpleXfer *xfer) |
| 896 { |
1169 { |
| 897 JabberSIXfer *jsx = xfer->data; |
1170 JabberSIXfer *jsx = xfer->data; |
| 927 xmlnode_set_attrib(field, "var", "stream-method"); |
1200 xmlnode_set_attrib(field, "var", "stream-method"); |
| 928 xmlnode_set_attrib(field, "type", "list-single"); |
1201 xmlnode_set_attrib(field, "type", "list-single"); |
| 929 option = xmlnode_new_child(field, "option"); |
1202 option = xmlnode_new_child(field, "option"); |
| 930 value = xmlnode_new_child(option, "value"); |
1203 value = xmlnode_new_child(option, "value"); |
| 931 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); |
1204 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); |
| 932 /* |
|
| 933 option = xmlnode_new_child(field, "option"); |
1205 option = xmlnode_new_child(field, "option"); |
| 934 value = xmlnode_new_child(option, "value"); |
1206 value = xmlnode_new_child(option, "value"); |
| 935 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); |
1207 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); |
| 936 */ |
|
| 937 |
1208 |
| 938 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); |
1209 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); |
| 939 |
1210 |
| 940 /* Store the IQ id so that we can cancel the callback */ |
1211 /* Store the IQ id so that we can cancel the callback */ |
| 941 g_free(jsx->iq_id); |
1212 g_free(jsx->iq_id); |
| 1134 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); |
1433 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); |
| 1135 |
1434 |
| 1136 x = xmlnode_new_child(feature, "x"); |
1435 x = xmlnode_new_child(feature, "x"); |
| 1137 xmlnode_set_namespace(x, "jabber:x:data"); |
1436 xmlnode_set_namespace(x, "jabber:x:data"); |
| 1138 xmlnode_set_attrib(x, "type", "submit"); |
1437 xmlnode_set_attrib(x, "type", "submit"); |
| 1139 |
|
| 1140 field = xmlnode_new_child(x, "field"); |
1438 field = xmlnode_new_child(x, "field"); |
| 1141 xmlnode_set_attrib(field, "var", "stream-method"); |
1439 xmlnode_set_attrib(field, "var", "stream-method"); |
| 1142 |
1440 |
| 1143 value = xmlnode_new_child(field, "value"); |
1441 /* we should maybe "remember" if bytestreams has failed before (in the |
| 1144 if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) |
1442 same session) with this JID, and only present IBB as an option to |
| |
1443 avoid unnessesary timeout */ |
| |
1444 if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) { |
| |
1445 value = xmlnode_new_child(field, "value"); |
| 1145 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); |
1446 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); |
| 1146 /* |
1447 } else if(jsx->stream_method & STREAM_METHOD_IBB) { |
| 1147 else if(jsx->stream_method & STREAM_METHOD_IBB) |
1448 value = xmlnode_new_child(field, "value"); |
| 1148 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); |
1449 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); |
| 1149 */ |
1450 } |
| 1150 |
1451 |
| 1151 jabber_iq_send(iq); |
1452 jabber_iq_send(iq); |
| 1152 } |
1453 } |
| 1153 } |
1454 } |
| 1154 |
1455 |
| 1155 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who) |
1456 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who) |