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;