comparison src/protocols/oscar/oscar.c @ 3752:b32474e522fa

[gaim-migrate @ 3890] From: "William T. Mahan" <wtm2@duke.edu> This patch, against CVS HEAD, fixes three bugs in Oscar File Transfer support. I can split it up further if desired. * Send a null checksum when initiating a file transfer, which fixes "files don't match" warnings produced by some versions of WinAIM; add a compile-time option to actually compute the checksum, which is slow but necessary when sending to some Mac clients. * Don't allow sending files to oneself, because it causes all kinds of subtle problems and it's not useful. * Don't crash when there is an error writing to the output file when receiving. From: "William T. Mahan" <wtm2@duke.edu> This patch 2 of 3, which applies on top of the first, adds support for reverse connections for Oscar File Transfer, the lack of which has been the biggest complaint so far. Reverse connections are used by newer AIM clients when there is difficulty verifying the IP of the sender. From: "William T. Mahan" <wtm2@duke.edu> This patch 3 of 3, which applies on top of the first 2, removes the alarm() and sigaction() calls that were added by my original FT patch to detect transfer timeouts. Besides apparently not working on Windows, they involved a lot of ugly code to handle a special case. My new approach is to add destructors that can called when SNACs are freed; a timeout is detected when a request SNAC is cleaned up before the transfer is accepted. Although this touches several files, it is more generic than the old method. I tried to implement this in an unintrusive manner, so that there is little preformance penalty for SNACs that do not use destructors. My first two patches should work fine without this. If there are any objections to the third patch, I ask that the first two patches be applied, in which case I will set up a SourceForge page for this one. committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Sat, 19 Oct 2002 05:22:30 +0000
parents a20bf3d247ff
children f53370197bb9
comparison
equal deleted inserted replaced
3751:e25577506dec 3752:b32474e522fa
186 int totsize; 186 int totsize;
187 int filesdone; 187 int filesdone;
188 int totfiles; 188 int totfiles;
189 int watcher; 189 int watcher;
190 }; 190 };
191
192 static struct oscar_file_transfer *oft_listening;
193 191
194 struct icon_req { 192 struct icon_req {
195 char *user; 193 char *user;
196 time_t timestamp; 194 time_t timestamp;
197 unsigned long length; 195 unsigned long length;
381 aim_conn_t *); 379 aim_conn_t *);
382 static void oscar_cancel_transfer(struct gaim_connection *, 380 static void oscar_cancel_transfer(struct gaim_connection *,
383 struct file_transfer *); 381 struct file_transfer *);
384 static int oscar_sendfile_request(aim_session_t *sess, 382 static int oscar_sendfile_request(aim_session_t *sess,
385 struct oscar_file_transfer *oft); 383 struct oscar_file_transfer *oft);
384 static int oscar_sendfile_timeout(aim_session_t *sess, aim_frame_t *fr, ...);
386 385
387 static char *msgerrreason[] = { 386 static char *msgerrreason[] = {
388 "Invalid error", 387 "Invalid error",
389 "Invalid SNAC", 388 "Invalid SNAC",
390 "Rate to host", 389 "Rate to host",
759 set_login_progress(gc, 4, _("Connection established, cookie sent")); 758 set_login_progress(gc, 4, _("Connection established, cookie sent"));
760 } 759 }
761 760
762 static void oscar_ask_send_file(struct gaim_connection *gc, char *destsn) { 761 static void oscar_ask_send_file(struct gaim_connection *gc, char *destsn) {
763 struct oscar_data *od = (struct oscar_data *)gc->proto_data; 762 struct oscar_data *od = (struct oscar_data *)gc->proto_data;
764 struct oscar_file_transfer *oft = oft_listening; 763
765 764 struct oscar_file_transfer *oft = g_new0(struct oscar_file_transfer,
766 /* Kludge: if we try to send a file to a client that doesn't 765 1);
767 * support it, the BOS server sends us back an error without 766
768 * any information identifying which transfer was aborted. So 767 oft->type = OFT_SENDFILE_OUT;
769 * we only allow one sendfile request at a time, to ensure that 768 oft->sn = g_strdup(destsn);
770 * the transfer referenced by an error is unambiguous. It's ugly, 769
771 * but what else can we do? -- wtm 770 od->file_transfers = g_slist_append(od->file_transfers, oft);
772 */ 771
773 if (oft) { 772 oft->xfer = transfer_out_add(gc, oft->sn);
774 do_error_dialog(_("Sorry, you already have an outgoing transfer pending. Due to limitations of the Oscar protocol, only one outgoing transfer request is permitted at a time."), NULL, GAIM_ERROR);
775 }
776 else {
777 struct oscar_file_transfer *oft = g_new0(struct oscar_file_transfer,
778 1);
779
780 oft->type = OFT_SENDFILE_OUT;
781 oft->sn = g_strdup(destsn);
782
783 od->file_transfers = g_slist_append(od->file_transfers, oft);
784
785 oft->xfer = transfer_out_add(gc, oft->sn);
786 }
787 } 773 }
788 774
789 static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { 775 static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
790 va_list ap; 776 va_list ap;
791 struct aim_authresp_info *info; 777 struct aim_authresp_info *info;
890 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0); 876 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0);
891 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_SIMPLEINFO, gaim_simpleinfo, 0); 877 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_SIMPLEINFO, gaim_simpleinfo, 0);
892 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0); 878 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
893 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0); 879 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
894 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0); 880 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0);
881 aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_MSGTIMEOUT, oscar_sendfile_timeout, 0);
895 882
896 ((struct oscar_data *)gc->proto_data)->conn = bosconn; 883 ((struct oscar_data *)gc->proto_data)->conn = bosconn;
897 for (i = 0; i < (int)strlen(info->bosip); i++) { 884 for (i = 0; i < (int)strlen(info->bosip); i++) {
898 if (info->bosip[i] == ':') { 885 if (info->bosip[i] == ':') {
899 port = atoi(&(info->bosip[i+1])); 886 port = atoi(&(info->bosip[i+1]));
1538 struct gaim_connection *gc = sess->aux_data; 1525 struct gaim_connection *gc = sess->aux_data;
1539 struct oscar_data *od = (struct oscar_data *)gc->proto_data; 1526 struct oscar_data *od = (struct oscar_data *)gc->proto_data;
1540 struct oscar_file_transfer *oft; 1527 struct oscar_file_transfer *oft;
1541 va_list ap; 1528 va_list ap;
1542 aim_conn_t *conn, *listenerconn; 1529 aim_conn_t *conn, *listenerconn;
1543 #ifndef NOSIGALARM 1530
1544 alarm(0); /* reset timeout alarm */
1545 #endif
1546 va_start(ap, fr); 1531 va_start(ap, fr);
1547 conn = va_arg(ap, aim_conn_t *); 1532 conn = va_arg(ap, aim_conn_t *);
1548 listenerconn = va_arg(ap, aim_conn_t *); 1533 listenerconn = va_arg(ap, aim_conn_t *);
1549 va_end(ap); 1534 va_end(ap);
1550 1535
1551 oft = find_oft_by_conn(gc, listenerconn); 1536 oft = find_oft_by_conn(gc, listenerconn);
1552 oft->conn = conn; 1537 oft->conn = conn;
1553 /* Stop watching listener conn; watch transfer conn instead */ 1538 /* Stop watching listener conn; watch transfer conn instead */
1554 gaim_input_remove(oft->watcher); 1539 gaim_input_remove(oft->watcher);
1555 aim_conn_kill(sess, &listenerconn); 1540 aim_conn_kill(sess, &listenerconn);
1556 /* We no longer need to block other outgoing transfers. */
1557 oft_listening = NULL;
1558 1541
1559 aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT, 1542 aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT,
1560 AIM_CB_OFT_GETFILEFILESEND, 1543 AIM_CB_OFT_GETFILEFILESEND,
1561 oscar_file_transfer_do, 1544 oscar_file_transfer_do,
1562 0); 1545 0);
1571 oscar_sendfile_request(sess, oft); 1554 oscar_sendfile_request(sess, oft);
1572 1555
1573 return 0; 1556 return 0;
1574 } 1557 }
1575 1558
1576 void oscar_sendfile_timeout(int sig) 1559 static int oscar_sendfile_timeout(aim_session_t *sess, aim_frame_t *fr, ...) {
1577 { 1560 struct gaim_connection *gc = sess->aux_data;
1578 struct oscar_file_transfer *oft = oft_listening; 1561 va_list ap;
1579 1562 struct oscar_file_transfer *oft;
1580 if (oft) { 1563 char *cookie;
1581 aim_session_t *sess = aim_conn_getsess(oft->conn); 1564 aim_conn_t *bosconn;
1582 aim_conn_t *bosconn; 1565
1583 { 1566 va_start(ap, fr);
1584 /* XXX is this valid? is there a better way? -- wtm */ 1567 bosconn = va_arg(ap, aim_conn_t *);
1585 struct gaim_connection *gc = sess->aux_data; 1568 cookie = va_arg(ap, char *);
1586 struct oscar_data *odata = (struct oscar_data *)gc->proto_data; 1569 va_end(ap);
1587 bosconn = odata->conn; 1570
1588 } 1571 if ((oft = find_oft_by_cookie(gc, cookie))) {
1589
1590 oft_listening = NULL;
1591 aim_canceltransfer(sess, bosconn, oft->cookie, 1572 aim_canceltransfer(sess, bosconn, oft->cookie,
1592 oft->sn, AIM_CAPS_SENDFILE); 1573 oft->sn, AIM_CAPS_SENDFILE);
1593 1574
1594 transfer_abort(oft->xfer, _("Transfer timed out")); 1575 transfer_abort(oft->xfer, _("Transfer timed out"));
1595 oscar_file_transfer_disconnect(sess, oft->conn); 1576 oscar_file_transfer_disconnect(sess, oft->conn);
1596 } 1577 }
1578
1579 return 1; /* success */
1597 } 1580 }
1598 1581
1599 /* Called once at the beginning of an outgoing transfer session. */ 1582 /* Called once at the beginning of an outgoing transfer session. */
1600 static void oscar_start_transfer_out(struct gaim_connection *gc, 1583 static void oscar_start_transfer_out(struct gaim_connection *gc,
1601 struct file_transfer *xfer, const char *name, int totfiles, 1584 struct file_transfer *xfer, const char *name, int totfiles,
1605 1588
1606 oft->xfer = xfer; 1589 oft->xfer = xfer;
1607 oft->totsize = totsize; 1590 oft->totsize = totsize;
1608 oft->totfiles = totfiles; 1591 oft->totfiles = totfiles;
1609 oft->filesdone = 0; 1592 oft->filesdone = 0;
1610 oft_listening = oft;
1611 1593
1612 oft->conn = aim_sendfile_initiate(od->sess, oft->sn, 1594 oft->conn = aim_sendfile_initiate(od->sess, oft->sn,
1613 name, totfiles, oft->totsize, oft->cookie); 1595 name, totfiles, oft->totsize, oft->cookie);
1614 if (!oft->conn) { 1596 if (!oft->conn) {
1615 do_error_dialog(_("Couldn't open listener to send file"), 1597 do_error_dialog(_("Couldn't open listener to send file"),
1616 _("File transfer aborted"), 1598 _("File transfer aborted"),
1617 GAIM_ERROR); 1599 GAIM_ERROR);
1618 return; 1600 return;
1619 } 1601 }
1620 #ifndef NOSIGALARM 1602
1621 {
1622 /* XXX is there a good glib-oriented way of doing this?
1623 * -- wtm */
1624 struct sigaction act;
1625 act.sa_handler = oscar_sendfile_timeout;
1626 act.sa_flags = SA_RESETHAND;
1627 sigemptyset (&act.sa_mask);
1628 sigaction(SIGALRM, &act, NULL);
1629 alarm(OFT_TIMEOUT);
1630 }
1631 #endif
1632 aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT, 1603 aim_conn_addhandler(od->sess, oft->conn, AIM_CB_FAM_OFT,
1633 AIM_CB_OFT_GETFILEINITIATE, 1604 AIM_CB_OFT_GETFILEINITIATE,
1634 oscar_sendfile_accepted, 1605 oscar_sendfile_accepted,
1635 0); 1606 0);
1636 oft->watcher = gaim_input_add(oft->conn->fd, GAIM_INPUT_READ, 1607 oft->watcher = gaim_input_add(oft->conn->fd, GAIM_INPUT_READ,
1858 * -- wtm 1829 * -- wtm
1859 */ 1830 */
1860 return 0; 1831 return 0;
1861 } 1832 }
1862 else if (args->status != AIM_RENDEZVOUS_PROPOSE) { 1833 else if (args->status != AIM_RENDEZVOUS_PROPOSE) {
1834 debug_printf("unknown rendezvous status\n");
1863 return 1; 1835 return 1;
1864 } 1836 }
1865 1837
1866 if (args->reqclass & AIM_CAPS_CHAT) { 1838 if (args->reqclass & AIM_CAPS_CHAT) {
1867 char *name = extract_name(args->info.chat.roominfo.name); 1839 char *name = extract_name(args->info.chat.roominfo.name);
1877 m); 1849 m);
1878 if (name) 1850 if (name)
1879 g_free(name); 1851 g_free(name);
1880 } else if (args->reqclass & AIM_CAPS_SENDFILE) { 1852 } else if (args->reqclass & AIM_CAPS_SENDFILE) {
1881 struct oscar_file_transfer *oft; 1853 struct oscar_file_transfer *oft;
1882 1854 struct oscar_data *od = gc->proto_data;
1883 if (!args->verifiedip) { 1855
1884 /* It seems that Trillian sends a message 1856 if ((oft = find_oft_by_cookie(sess->aux_data, args->cookie)))
1885 * with no file data during multiple-file 1857 {
1886 * transfers. 1858 /* This is a request for a reverse connection,
1859 * which is used by newer clients when for some
1860 * reason they are unable to connect to our listener
1861 * (e.g. they are behind a firewall).
1887 */ 1862 */
1888 debug_printf("sendfile: didn't get any data\n"); 1863 if (oft->type != OFT_SENDFILE_OUT)
1889 return -1; 1864 return -1;
1890 } 1865
1891 1866 /* It seems that Trillian sends some weird
1892 oft = g_new0(struct oscar_file_transfer, 1); 1867 * packets. Sanity check.
1868 */
1869 if (!args->verifiedip)
1870 return -1;
1871
1872 /* This connection isn't used for anything, since
1873 * we're using a reverse connection instead.
1874 */
1875 gaim_input_remove(oft->watcher);
1876 aim_conn_kill(sess, &oft->conn);
1877
1878 debug_printf("sendfile: doing reverse connection to %s:%d\n", args->verifiedip, args->port);
1879
1880 oft->conn = aim_accepttransfer(sess, od->conn,
1881 userinfo->sn,
1882 args->cookie, args->verifiedip,
1883 args->port,
1884 AIM_CAPS_SENDFILE);
1885
1886 /* XXX: this is a bit of a hack: ideally
1887 * we should wait on GAIM_INPUT_WRITE. -- wtm
1888 */
1889 aim_conn_completeconnect(sess, oft->conn);
1890
1891 oscar_sendfile_request(sess, oft);
1892
1893 aim_conn_addhandler(sess, oft->conn,
1894 AIM_CB_FAM_OFT,
1895 AIM_CB_OFT_GETFILECOMPLETE,
1896 oscar_sendfile_out_done,
1897 0);
1898 aim_conn_addhandler(sess, oft->conn,
1899 AIM_CB_FAM_OFT,
1900 AIM_CB_OFT_GETFILEFILESEND,
1901 oscar_file_transfer_do,
1902 0);
1903 oft->watcher = gaim_input_add(oft->conn->fd,
1904 GAIM_INPUT_READ, oscar_callback,
1905 oft->conn);
1906 return 0;
1907 }
1893 1908
1894 debug_printf("%s (%s) requests to send a file to %s\n", 1909 debug_printf("%s (%s) requests to send a file to %s\n",
1895 userinfo->sn, args->verifiedip, gc->username); 1910 userinfo->sn, args->verifiedip, gc->username);
1911
1912 oft = g_new0(struct oscar_file_transfer, 1);
1896 1913
1897 oft->type = OFT_SENDFILE_IN; 1914 oft->type = OFT_SENDFILE_IN;
1898 oft->sn = g_strdup(userinfo->sn); 1915 oft->sn = g_strdup(userinfo->sn);
1899 strncpy(oft->ip, args->verifiedip, sizeof(oft->ip)); 1916 strncpy(oft->ip, args->verifiedip, sizeof(oft->ip));
1900 oft->port = args->port; 1917 oft->port = args->port;
1901 memcpy(oft->cookie, args->cookie, 8); 1918 memcpy(oft->cookie, args->cookie, 8);
1902 1919
1903 { /* XXX ugly... */ 1920 od->file_transfers = g_slist_append(od->file_transfers, oft);
1904 struct gaim_connection *gc = sess->aux_data;
1905 struct oscar_data *od = gc->proto_data;
1906 od->file_transfers = g_slist_append(od->file_transfers, oft);
1907 }
1908 1921
1909 oft->xfer = transfer_in_add(gc, userinfo->sn, 1922 oft->xfer = transfer_in_add(gc, userinfo->sn,
1910 args->info.sendfile.filename, 1923 args->info.sendfile.filename,
1911 args->info.sendfile.totsize, 1924 args->info.sendfile.totsize,
1912 args->info.sendfile.totfiles, 1925 args->info.sendfile.totfiles,
2222 oft = find_oft_by_cookie(gc, cookie); 2235 oft = find_oft_by_cookie(gc, cookie);
2223 2236
2224 if (oft) { 2237 if (oft) {
2225 buf = g_strdup_printf(_("%s has declined to receive a file from %s.\n"), 2238 buf = g_strdup_printf(_("%s has declined to receive a file from %s.\n"),
2226 who, gc->username); 2239 who, gc->username);
2227 #ifndef NOSIGALARM
2228 alarm(0); /* reset timeout alarm */
2229 #endif
2230 oft_listening = NULL;
2231 transfer_abort(oft->xfer, buf); 2240 transfer_abort(oft->xfer, buf);
2232 g_free(buf); 2241 g_free(buf);
2233 oscar_file_transfer_disconnect(sess, oft->conn); 2242 oscar_file_transfer_disconnect(sess, oft->conn);
2234 } 2243 }
2235 break; 2244 break;
2326 return 1; 2335 return 1;
2327 } 2336 }
2328 2337
2329 static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) { 2338 static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
2330 va_list ap; 2339 va_list ap;
2331 char *destn; 2340 char *data;
2332 fu16_t reason; 2341 fu16_t reason;
2333 char buf[1024]; 2342 char buf[1024];
2334 struct oscar_file_transfer *oft = oft_listening; 2343 struct gaim_connection *gc = sess->aux_data;
2335 2344 struct oscar_file_transfer *oft;
2345
2336 va_start(ap, fr); 2346 va_start(ap, fr);
2337 reason = (fu16_t)va_arg(ap, unsigned int); 2347 reason = (fu16_t)va_arg(ap, unsigned int);
2338 destn = va_arg(ap, char *); 2348 data = va_arg(ap, char *);
2339 va_end(ap); 2349 va_end(ap);
2340 2350
2341 if (oft) { 2351 /* If this was a file transfer request, data is a cookie. */
2342 /* If we try to send a file but it isn't supported, then 2352 if ((oft = find_oft_by_cookie(gc, data))) {
2343 * we get this error without any information identifying
2344 * the failed connection. So we can only have one outgoing
2345 * transfer at a time. Ugh. -- wtm
2346 */
2347 oft_listening = NULL;
2348 transfer_abort(oft->xfer, 2353 transfer_abort(oft->xfer,
2349 (reason < msgerrreasonlen) ? msgerrreason[reason] : _("No reason was given.")); 2354 (reason < msgerrreasonlen) ? msgerrreason[reason] : _("No reason was given."));
2350 2355
2351 oscar_file_transfer_disconnect(sess, oft->conn); 2356 oscar_file_transfer_disconnect(sess, oft->conn);
2352 return 1; 2357 return 1;
2353 } 2358 }
2354 2359
2355 snprintf(buf, sizeof(buf), _("Your message to %s did not get sent:"), destn); 2360 /* Data is assumed to be the destination sn. */
2361 snprintf(buf, sizeof(buf), _("Your message to %s did not get sent:"), data);
2356 do_error_dialog(buf, (reason < msgerrreasonlen) ? msgerrreason[reason] : _("No reason was given."), GAIM_ERROR); 2362 do_error_dialog(buf, (reason < msgerrreasonlen) ? msgerrreason[reason] : _("No reason was given."), GAIM_ERROR);
2357 2363
2358 return 1; 2364 return 1;
2359 } 2365 }
2360 2366
4163 if (oft->type == OFT_SENDFILE_IN) { 4169 if (oft->type == OFT_SENDFILE_IN) {
4164 aim_oft_end(sess, conn); 4170 aim_oft_end(sess, conn);
4165 oscar_file_transfer_disconnect(sess, conn); 4171 oscar_file_transfer_disconnect(sess, conn);
4166 } 4172 }
4167 else if (oft->type == OFT_SENDFILE_OUT) { 4173 else if (oft->type == OFT_SENDFILE_OUT) {
4168 #if 0
4169 /* Wait for response before closing connection. */ 4174 /* Wait for response before closing connection. */
4170 oft->watcher = gaim_input_add(conn->fd, GAIM_INPUT_READ, 4175 oft->watcher = gaim_input_add(conn->fd, GAIM_INPUT_READ,
4171 oscar_callback, conn); 4176 oscar_callback, conn);
4172 #else
4173 oscar_file_transfer_disconnect(sess, conn);
4174 #endif
4175 } 4177 }
4176 } 4178 }
4177 4179
4178 static int oscar_file_transfer_do(aim_session_t *sess, aim_frame_t *fr, ...) { 4180 static int oscar_file_transfer_do(aim_session_t *sess, aim_frame_t *fr, ...) {
4179 struct gaim_connection *gc = sess->aux_data; 4181 struct gaim_connection *gc = sess->aux_data;
4180 va_list ap; 4182 va_list ap;
4181 aim_conn_t *conn; 4183 aim_conn_t *conn;
4182 struct oscar_file_transfer *oft; 4184 struct oscar_file_transfer *oft;
4185 int err;
4183 4186
4184 va_start(ap, fr); 4187 va_start(ap, fr);
4185 conn = va_arg(ap, aim_conn_t *); 4188 conn = va_arg(ap, aim_conn_t *);
4186 4189
4187 oft = find_oft_by_conn(gc, conn); 4190 oft = find_oft_by_conn(gc, conn);
4191 oft->watcher = 0; 4194 oft->watcher = 0;
4192 4195
4193 if (oft->type == OFT_SENDFILE_IN) { 4196 if (oft->type == OFT_SENDFILE_IN) {
4194 const char *name = va_arg(ap, const char *); 4197 const char *name = va_arg(ap, const char *);
4195 int size = va_arg(ap, int); 4198 int size = va_arg(ap, int);
4196 if (transfer_in_do(oft->xfer, conn->fd, name, size)) 4199 err = transfer_in_do(oft->xfer, conn->fd, name, size);
4197 oscar_file_transfer_disconnect(sess, oft->conn);
4198 } 4200 }
4199 else { 4201 else {
4200 int offset = va_arg(ap, int); 4202 int offset = va_arg(ap, int);
4201 if (transfer_out_do(oft->xfer, conn->fd, offset)) 4203 err = transfer_out_do(oft->xfer, conn->fd, offset);
4202 oscar_file_transfer_disconnect(sess, oft->conn);
4203 } 4204 }
4204 va_end(ap); 4205 va_end(ap);
4206
4207 if (err) {
4208 /* There was an error; cancel the transfer. */
4209 struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4210 aim_conn_t *bosconn = od->conn;
4211 aim_canceltransfer(sess, bosconn, oft->cookie,
4212 oft->sn, AIM_CAPS_SENDFILE);
4213 oscar_file_transfer_disconnect(sess, oft->conn);
4214 }
4205 4215
4206 return 0; 4216 return 0;
4207 } 4217 }
4208 4218
4209 static int gaim_directim_initiate(aim_session_t *sess, aim_frame_t *fr, ...) { 4219 static int gaim_directim_initiate(aim_session_t *sess, aim_frame_t *fr, ...) {
4429 pbm = g_new0(struct proto_buddy_menu, 1); 4439 pbm = g_new0(struct proto_buddy_menu, 1);
4430 pbm->label = _("Direct IM"); 4440 pbm->label = _("Direct IM");
4431 pbm->callback = oscar_ask_direct_im; 4441 pbm->callback = oscar_ask_direct_im;
4432 pbm->gc = gc; 4442 pbm->gc = gc;
4433 m = g_list_append(m, pbm); 4443 m = g_list_append(m, pbm);
4434 } 4444
4435 4445 pbm = g_new0(struct proto_buddy_menu, 1);
4436 pbm = g_new0(struct proto_buddy_menu, 1); 4446 pbm->label = _("Send File");
4437 pbm->label = _("Send File"); 4447 pbm->callback = oscar_ask_send_file;
4438 pbm->callback = oscar_ask_send_file; 4448 pbm->gc = gc;
4439 pbm->gc = gc; 4449 m = g_list_append(m, pbm);
4440 m = g_list_append(m, pbm); 4450 }
4441 } 4451 }
4442 4452
4443 pbm = g_new0(struct proto_buddy_menu, 1); 4453 pbm = g_new0(struct proto_buddy_menu, 1);
4444 pbm->label = _("Get Capabilities"); 4454 pbm->label = _("Get Capabilities");
4445 pbm->callback = oscar_get_caps; 4455 pbm->callback = oscar_get_caps;