Mercurial > pidgin
comparison libpurple/ft.c @ 27878:b7b25f580637
ft: Add infrastructure to allow a prpl to moderate when to send packets.
The file transfer subsystem should now allow a prpl to serialize packets
(the way XMPP IBB does, where we wait for an ACK before sending the next
packet.) I think this will also be usable for msn and any other prpls
that currently try to open the local file manually.
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Wed, 12 Aug 2009 04:31:17 +0000 |
parents | 7754717d574c |
children | dad4cb8f81df |
comparison
equal
deleted
inserted
replaced
27877:e502112a03e7 | 27878:b7b25f580637 |
---|---|
38 #define FT_MAX_BUFFER_SIZE 65535 | 38 #define FT_MAX_BUFFER_SIZE 65535 |
39 | 39 |
40 static PurpleXferUiOps *xfer_ui_ops = NULL; | 40 static PurpleXferUiOps *xfer_ui_ops = NULL; |
41 static GList *xfers; | 41 static GList *xfers; |
42 | 42 |
43 /* | |
44 * A hack to store more data since we can't extend the size of PurpleXfer | |
45 * easily. | |
46 */ | |
47 static GHashTable *xfers_data = NULL; | |
48 | |
49 typedef struct _PurpleXferPrivData { | |
50 /* | |
51 * Used to moderate the file transfer when either the read/write ui_ops are | |
52 * set or fd is not set. In those cases, the UI/prpl call the respective | |
53 * function, which is somewhat akin to a fd watch being triggered. | |
54 */ | |
55 enum { | |
56 PURPLE_XFER_READY_NONE = 0x0, | |
57 PURPLE_XFER_READY_UI = 0x1, | |
58 PURPLE_XFER_READY_PRPL = 0x2, | |
59 } ready; | |
60 } PurpleXferPrivData; | |
61 | |
43 static int purple_xfer_choose_file(PurpleXfer *xfer); | 62 static int purple_xfer_choose_file(PurpleXfer *xfer); |
63 | |
64 static void | |
65 purple_xfer_priv_data_destroy(gpointer data) | |
66 { | |
67 PurpleXferPrivData *priv = data; | |
68 | |
69 g_free(priv); | |
70 } | |
44 | 71 |
45 GList * | 72 GList * |
46 purple_xfers_get_all() | 73 purple_xfers_get_all() |
47 { | 74 { |
48 return xfers; | 75 return xfers; |
51 PurpleXfer * | 78 PurpleXfer * |
52 purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who) | 79 purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who) |
53 { | 80 { |
54 PurpleXfer *xfer; | 81 PurpleXfer *xfer; |
55 PurpleXferUiOps *ui_ops; | 82 PurpleXferUiOps *ui_ops; |
83 PurpleXferPrivData *priv; | |
56 | 84 |
57 g_return_val_if_fail(type != PURPLE_XFER_UNKNOWN, NULL); | 85 g_return_val_if_fail(type != PURPLE_XFER_UNKNOWN, NULL); |
58 g_return_val_if_fail(account != NULL, NULL); | 86 g_return_val_if_fail(account != NULL, NULL); |
59 g_return_val_if_fail(who != NULL, NULL); | 87 g_return_val_if_fail(who != NULL, NULL); |
60 | 88 |
68 xfer->ui_ops = purple_xfers_get_ui_ops(); | 96 xfer->ui_ops = purple_xfers_get_ui_ops(); |
69 xfer->message = NULL; | 97 xfer->message = NULL; |
70 xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE; | 98 xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE; |
71 xfer->fd = -1; | 99 xfer->fd = -1; |
72 | 100 |
101 priv = g_new0(PurpleXferPrivData, 1); | |
102 priv->ready = PURPLE_XFER_READY_NONE; | |
103 | |
104 g_hash_table_insert(xfers_data, xfer, priv); | |
105 | |
73 ui_ops = purple_xfer_get_ui_ops(xfer); | 106 ui_ops = purple_xfer_get_ui_ops(xfer); |
74 | 107 |
75 if (ui_ops != NULL && ui_ops->new_xfer != NULL) | 108 if (ui_ops != NULL && ui_ops->new_xfer != NULL) |
76 ui_ops->new_xfer(xfer); | 109 ui_ops->new_xfer(xfer); |
77 | 110 |
100 g_free(xfer->who); | 133 g_free(xfer->who); |
101 g_free(xfer->filename); | 134 g_free(xfer->filename); |
102 g_free(xfer->remote_ip); | 135 g_free(xfer->remote_ip); |
103 g_free(xfer->local_filename); | 136 g_free(xfer->local_filename); |
104 | 137 |
138 g_hash_table_remove(xfers_data, xfer); | |
139 | |
105 PURPLE_DBUS_UNREGISTER_POINTER(xfer); | 140 PURPLE_DBUS_UNREGISTER_POINTER(xfer); |
141 xfers = g_list_remove(xfers, xfer); | |
106 g_free(xfer); | 142 g_free(xfer); |
107 xfers = g_list_remove(xfers, xfer); | |
108 } | 143 } |
109 | 144 |
110 void | 145 void |
111 purple_xfer_ref(PurpleXfer *xfer) | 146 purple_xfer_ref(PurpleXfer *xfer) |
112 { | 147 { |
944 | 979 |
945 return r; | 980 return r; |
946 } | 981 } |
947 | 982 |
948 static void | 983 static void |
949 transfer_cb(gpointer data, gint source, PurpleInputCondition condition) | 984 do_transfer(PurpleXfer *xfer) |
950 { | 985 { |
951 PurpleXferUiOps *ui_ops; | 986 PurpleXferUiOps *ui_ops; |
952 PurpleXfer *xfer = (PurpleXfer *)data; | |
953 guchar *buffer = NULL; | 987 guchar *buffer = NULL; |
954 gssize r = 0; | 988 gssize r = 0; |
955 | 989 |
956 ui_ops = purple_xfer_get_ui_ops(xfer); | 990 ui_ops = purple_xfer_get_ui_ops(xfer); |
957 | 991 |
958 if (condition & PURPLE_INPUT_READ) { | 992 if (xfer->type == PURPLE_XFER_RECEIVE) { |
959 r = purple_xfer_read(xfer, &buffer); | 993 r = purple_xfer_read(xfer, &buffer); |
960 if (r > 0) { | 994 if (r > 0) { |
961 size_t wc; | 995 size_t wc; |
962 if (ui_ops && ui_ops->write) | 996 if (ui_ops && ui_ops->write) |
963 wc = ui_ops->write(xfer, buffer, r); | 997 wc = ui_ops->write(xfer, buffer, r); |
973 } else if(r < 0) { | 1007 } else if(r < 0) { |
974 purple_xfer_cancel_remote(xfer); | 1008 purple_xfer_cancel_remote(xfer); |
975 g_free(buffer); | 1009 g_free(buffer); |
976 return; | 1010 return; |
977 } | 1011 } |
978 } else if (condition & PURPLE_INPUT_WRITE) { | 1012 } else if (xfer->type == PURPLE_XFER_SEND) { |
979 size_t result; | 1013 size_t result; |
980 size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); | 1014 size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); |
981 | 1015 |
982 /* this is so the prpl can keep the connection open | 1016 /* this is so the prpl can keep the connection open |
983 if it needs to for some odd reason. */ | 1017 if it needs to for some odd reason. */ |
1065 if (purple_xfer_is_completed(xfer)) | 1099 if (purple_xfer_is_completed(xfer)) |
1066 purple_xfer_end(xfer); | 1100 purple_xfer_end(xfer); |
1067 } | 1101 } |
1068 | 1102 |
1069 static void | 1103 static void |
1104 transfer_cb(gpointer data, gint source, PurpleInputCondition condition) | |
1105 { | |
1106 PurpleXfer *xfer = data; | |
1107 | |
1108 if (xfer->dest_fp == NULL) { | |
1109 /* The UI is moderating its side manually */ | |
1110 PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer); | |
1111 if (0 == (priv->ready & PURPLE_XFER_READY_UI)) { | |
1112 priv->ready |= PURPLE_XFER_READY_PRPL; | |
1113 | |
1114 purple_input_remove(xfer->watcher); | |
1115 xfer->watcher = 0; | |
1116 return; | |
1117 } | |
1118 } | |
1119 | |
1120 do_transfer(xfer); | |
1121 } | |
1122 | |
1123 static void | |
1070 begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond) | 1124 begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond) |
1071 { | 1125 { |
1072 PurpleXferType type = purple_xfer_get_type(xfer); | 1126 PurpleXferType type = purple_xfer_get_type(xfer); |
1073 PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer); | 1127 PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer); |
1074 | 1128 |
1112 void | 1166 void |
1113 purple_xfer_ui_ready(PurpleXfer *xfer) | 1167 purple_xfer_ui_ready(PurpleXfer *xfer) |
1114 { | 1168 { |
1115 PurpleInputCondition cond; | 1169 PurpleInputCondition cond; |
1116 PurpleXferType type; | 1170 PurpleXferType type; |
1117 | 1171 PurpleXferPrivData *priv; |
1118 g_return_if_fail(xfer != NULL); | 1172 |
1173 g_return_if_fail(xfer != NULL); | |
1174 | |
1175 priv = g_hash_table_lookup(xfers_data, xfer); | |
1176 priv->ready |= PURPLE_XFER_READY_UI; | |
1177 | |
1178 if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) | |
1179 return; | |
1119 | 1180 |
1120 type = purple_xfer_get_type(xfer); | 1181 type = purple_xfer_get_type(xfer); |
1121 if (type == PURPLE_XFER_SEND) | 1182 if (type == PURPLE_XFER_SEND) |
1122 cond = PURPLE_INPUT_WRITE; | 1183 cond = PURPLE_INPUT_WRITE; |
1123 else /* if (type == PURPLE_XFER_RECEIVE) */ | 1184 else /* if (type == PURPLE_XFER_RECEIVE) */ |
1124 cond = PURPLE_INPUT_READ; | 1185 cond = PURPLE_INPUT_READ; |
1125 | 1186 |
1126 if (xfer->watcher == 0 && xfer->fd != -1) | 1187 if (xfer->watcher == 0 && xfer->fd != -1) |
1127 xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer); | 1188 xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer); |
1128 | 1189 |
1129 transfer_cb(xfer, 0, cond); | 1190 priv->ready = PURPLE_XFER_READY_NONE; |
1191 | |
1192 do_transfer(xfer); | |
1193 } | |
1194 | |
1195 void | |
1196 purple_xfer_prpl_ready(PurpleXfer *xfer) | |
1197 { | |
1198 PurpleXferPrivData *priv; | |
1199 | |
1200 g_return_if_fail(xfer != NULL); | |
1201 | |
1202 priv = g_hash_table_lookup(xfers_data, xfer); | |
1203 priv->ready |= PURPLE_XFER_READY_PRPL; | |
1204 | |
1205 /* I don't think fwrite/fread are ever *not* ready */ | |
1206 if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) | |
1207 return; | |
1208 | |
1209 priv->ready = PURPLE_XFER_READY_NONE; | |
1210 | |
1211 do_transfer(xfer); | |
1130 } | 1212 } |
1131 | 1213 |
1132 void | 1214 void |
1133 purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, | 1215 purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, |
1134 unsigned int port) | 1216 unsigned int port) |
1391 } | 1473 } |
1392 | 1474 |
1393 void | 1475 void |
1394 purple_xfers_init(void) { | 1476 purple_xfers_init(void) { |
1395 void *handle = purple_xfers_get_handle(); | 1477 void *handle = purple_xfers_get_handle(); |
1478 | |
1479 xfers_data = g_hash_table_new_full(g_direct_hash, g_direct_equal, | |
1480 NULL, purple_xfer_priv_data_destroy); | |
1396 | 1481 |
1397 /* register signals */ | 1482 /* register signals */ |
1398 purple_signal_register(handle, "file-recv-accept", | 1483 purple_signal_register(handle, "file-recv-accept", |
1399 purple_marshal_VOID__POINTER, NULL, 1, | 1484 purple_marshal_VOID__POINTER, NULL, 1, |
1400 purple_value_new(PURPLE_TYPE_SUBTYPE, | 1485 purple_value_new(PURPLE_TYPE_SUBTYPE, |
1438 { | 1523 { |
1439 void *handle = purple_xfers_get_handle(); | 1524 void *handle = purple_xfers_get_handle(); |
1440 | 1525 |
1441 purple_signals_disconnect_by_handle(handle); | 1526 purple_signals_disconnect_by_handle(handle); |
1442 purple_signals_unregister_by_instance(handle); | 1527 purple_signals_unregister_by_instance(handle); |
1528 | |
1529 g_hash_table_destroy(xfers_data); | |
1530 xfers_data = NULL; | |
1443 } | 1531 } |
1444 | 1532 |
1445 void | 1533 void |
1446 purple_xfers_set_ui_ops(PurpleXferUiOps *ops) { | 1534 purple_xfers_set_ui_ops(PurpleXferUiOps *ops) { |
1447 xfer_ui_ops = ops; | 1535 xfer_ui_ops = ops; |