src/protocols/oscar/oscar.c

changeset 8971
a0adb943cbb4
parent 8962
5cd12e525d95
child 8978
38af990ff584
equal deleted inserted replaced
8970:7584822a5547 8971:a0adb943cbb4
133 GaimConversation *conv; /* bah. */ 133 GaimConversation *conv; /* bah. */
134 int maxlen; 134 int maxlen;
135 int maxvis; 135 int maxvis;
136 }; 136 };
137 137
138 struct direct_im { 138 struct oscar_direct_im {
139 GaimConnection *gc; 139 GaimConnection *gc;
140 char name[80]; 140 char name[80];
141 int watcher; 141 int watcher;
142 aim_conn_t *conn; 142 aim_conn_t *conn;
143 gboolean connected; 143 gboolean connected;
281 static int oscar_sendfile_ack (aim_session_t *, aim_frame_t *, ...); 281 static int oscar_sendfile_ack (aim_session_t *, aim_frame_t *, ...);
282 static int oscar_sendfile_done (aim_session_t *, aim_frame_t *, ...); 282 static int oscar_sendfile_done (aim_session_t *, aim_frame_t *, ...);
283 283
284 /* for icons */ 284 /* for icons */
285 static gboolean gaim_icon_timerfunc(gpointer data); 285 static gboolean gaim_icon_timerfunc(gpointer data);
286
287 /* just because */
288 static void oscar_callback(gpointer data, gint source, GaimInputCondition condition);
286 289
287 /* remove these at some point? */ 290 /* remove these at some point? */
288 /* Because I don't like forward declarations? I think that was why... */ 291 /* Because I don't like forward declarations? I think that was why... */
289 static void oscar_set_info(GaimConnection *gc, const char *text); 292 static void oscar_set_info(GaimConnection *gc, const char *text);
290 static void oscar_set_away(GaimConnection *gc, const char *state, const char *message); 293 static void oscar_set_away(GaimConnection *gc, const char *state, const char *message);
603 oscar_string_append(str, newline, _("Available"), tmp); 606 oscar_string_append(str, newline, _("Available"), tmp);
604 g_free(tmp); 607 g_free(tmp);
605 } 608 }
606 } 609 }
607 610
608 static struct direct_im *find_direct_im(OscarData *od, const char *who) {
609 GSList *d = od->direct_ims;
610 struct direct_im *m = NULL;
611
612 while (d) {
613 m = (struct direct_im *)d->data;
614 if (!aim_sncmp(who, m->name))
615 return m;
616 d = d->next;
617 }
618
619 return NULL;
620 }
621
622 static char *extract_name(const char *name) { 611 static char *extract_name(const char *name) {
623 char *tmp, *x; 612 char *tmp, *x;
624 int i, j; 613 int i, j;
625 614
626 if (!name) 615 if (!name)
693 } 682 }
694 683
695 return c; 684 return c;
696 } 685 }
697 686
698 static void gaim_odc_disconnect(aim_session_t *sess, aim_conn_t *conn) { 687 /*****************************************************************************
688 * Begin scary direct im stuff
689 *****************************************************************************/
690
691 static struct oscar_direct_im *oscar_direct_im_find(OscarData *od, const char *who) {
692 GSList *d = od->direct_ims;
693 struct oscar_direct_im *m = NULL;
694
695 while (d) {
696 m = (struct oscar_direct_im *)d->data;
697 if (!aim_sncmp(who, m->name))
698 return m;
699 d = d->next;
700 }
701
702 return NULL;
703 }
704
705 static void oscar_direct_im_destroy(OscarData *od, struct oscar_direct_im *dim)
706 {
707 gaim_debug_info("oscar",
708 "destroying Direct IM for %s.\n", dim->name);
709
710 od->direct_ims = g_slist_remove(od->direct_ims, dim);
711 if (dim->watcher)
712 gaim_input_remove(dim->watcher);
713 if (dim->conn) {
714 aim_conn_close(dim->conn);
715 aim_conn_kill(od->sess, &dim->conn);
716 }
717 g_free(dim);
718 }
719
720 /* the only difference between this and destroy is this writes a conv message */
721 static void oscar_direct_im_disconnect(OscarData *od, struct oscar_direct_im *dim)
722 {
723 GaimConversation *conv;
724 char buf[256];
725
726 gaim_debug_info("oscar",
727 "%s disconnected Direct IM.\n", dim->name);
728
729 if (dim->connected)
730 g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), dim->name);
731 else
732 g_snprintf(buf, sizeof buf, _("Direct IM with %s failed"), dim->name);
733
734 conv = gaim_find_conversation_with_account(dim->name, gaim_connection_get_account(dim->gc));
735 if (conv) {
736 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
737 gaim_conversation_update_progress(conv, 0);
738 } else {
739 gaim_notify_error(dim->gc, NULL, _("Direct Connect failed"), buf);
740 }
741
742 oscar_direct_im_destroy(od, dim);
743
744 return;
745 }
746
747 /* oops i made two of these. this one just calls the other one. */
748 static void gaim_odc_disconnect(aim_session_t *sess, aim_conn_t *conn)
749 {
750 GaimConnection *gc = sess->aux_data;
751 OscarData *od = (OscarData *)gc->proto_data;
752 struct oscar_direct_im *dim;
753 char *sn;
754
755 sn = g_strdup(aim_odc_getsn(conn));
756 dim = oscar_direct_im_find(od, sn);
757 oscar_direct_im_disconnect(od, dim);
758 g_free(sn);
759 }
760
761 static void destroy_direct_im_request(struct ask_direct *d) {
762 gaim_debug_info("oscar", "Freeing DirectIM prompts.\n");
763
764 g_free(d->sn);
765 g_free(d);
766 }
767
768 /* this is just a gaim_proxy_connect cb that sets up the rest of the cbs */
769 static void oscar_odc_callback(gpointer data, gint source, GaimInputCondition condition) {
770 struct oscar_direct_im *dim = data;
771 GaimConnection *gc = dim->gc;
772 OscarData *od = gc->proto_data;
773 GaimConversation *conv;
774 char buf[256];
775 struct sockaddr name;
776 socklen_t name_len = 1;
777
778 g_return_if_fail(gc != NULL);
779
780 if (!g_list_find(gaim_connections_get_all(), gc)) {
781 oscar_direct_im_destroy(od, dim);
782 return;
783 }
784
785 if (source < 0) {
786 oscar_direct_im_disconnect(od, dim);
787 return;
788 }
789
790 dim->conn->fd = source;
791 aim_conn_completeconnect(od->sess, dim->conn);
792 conv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, dim->name);
793
794 /* This is the best way to see if we're connected or not */
795 if (getpeername(source, &name, &name_len) == 0) {
796 g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
797 dim->connected = TRUE;
798 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
799 }
800
801 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
802 }
803
804 static void accept_direct_im_request(struct ask_direct *d) {
805 GaimConnection *gc = d->gc;
806 OscarData *od;
807 struct oscar_direct_im *dim;
808 char *host; int port = 5190;
809 int i, rc;
810
811 if (!g_list_find(gaim_connections_get_all(), gc)) {
812 destroy_direct_im_request(d);
813 return;
814 }
815
816 od = (OscarData *)gc->proto_data;
817 gaim_debug_info("oscar", "Accepted DirectIM.\n");
818
819 dim = oscar_direct_im_find(od, d->sn);
820 if (dim && dim->connected) {
821 destroy_direct_im_request(d); /* 40 */ /* what does that 40 mean? */
822 gaim_debug_info("oscar", "Wait, we're already connected, ignoring DirectIM.\n");
823 return;
824 }
825 dim = g_new0(struct oscar_direct_im, 1);
826 dim->gc = d->gc;
827 g_snprintf(dim->name, sizeof dim->name, "%s", d->sn);
828
829 dim->conn = aim_odc_connect(od->sess, d->sn, NULL, d->cookie);
830 od->direct_ims = g_slist_append(od->direct_ims, dim);
831 if (!dim->conn) {
832 oscar_direct_im_disconnect(od, dim);
833 destroy_direct_im_request(d);
834 return;
835 }
836
837 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
838 gaim_odc_incoming, 0);
839 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
840 gaim_odc_typing, 0);
841 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
842 gaim_odc_update_ui, 0);
843
844 gaim_debug_info("oscar", "ip is %s.\n", d->ip);
845 for (i = 0; i < (int)strlen(d->ip); i++) {
846 if (d->ip[i] == ':') {
847 port = atoi(&(d->ip[i+1]));
848 break;
849 }
850 }
851 host = g_strndup(d->ip, i);
852 dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
853 rc = gaim_proxy_connect(gc->account, host, port, oscar_odc_callback, dim);
854 g_free(host);
855 if (rc < 0) {
856 oscar_direct_im_disconnect(od, dim);
857 destroy_direct_im_request(d);
858 return;
859 }
860
861 destroy_direct_im_request(d);
862
863 return;
864 }
865
866 /*
867 * We have just established a socket with the other dude, so set up some handlers.
868 */
869 static int gaim_odc_initiate(aim_session_t *sess, aim_frame_t *fr, ...) {
699 GaimConnection *gc = sess->aux_data; 870 GaimConnection *gc = sess->aux_data;
700 OscarData *od = (OscarData *)gc->proto_data; 871 OscarData *od = (OscarData *)gc->proto_data;
701 GaimConversation *conv; 872 GaimConversation *conv;
702 struct direct_im *dim; 873 struct oscar_direct_im *dim;
874 char buf[256];
703 char *sn; 875 char *sn;
704 char buf[256]; 876 va_list ap;
705 877 aim_conn_t *newconn, *listenerconn;
706 sn = g_strdup(aim_odc_getsn(conn)); 878
879 va_start(ap, fr);
880 newconn = va_arg(ap, aim_conn_t *);
881 listenerconn = va_arg(ap, aim_conn_t *);
882 va_end(ap);
883
884 aim_conn_close(listenerconn);
885 aim_conn_kill(sess, &listenerconn);
886
887 sn = g_strdup(aim_odc_getsn(newconn));
707 888
708 gaim_debug_info("oscar", 889 gaim_debug_info("oscar",
709 "%s disconnected Direct IM.\n", sn); 890 "DirectIM: initiate success to %s\n", sn);
710 891 dim = oscar_direct_im_find(od, sn);
711 dim = find_direct_im(od, sn); 892
712 od->direct_ims = g_slist_remove(od->direct_ims, dim); 893 conv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, sn);
713 gaim_input_remove(dim->watcher); 894 gaim_input_remove(dim->watcher);
714 895 dim->conn = newconn;
715 if (dim->connected) 896 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
716 g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn); 897 dim->connected = TRUE;
717 else 898 g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
718 g_snprintf(buf, sizeof buf, _("Direct IM with %s failed"), sn);
719
720 conv = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
721 if (conv)
722 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
723
724 gaim_conversation_update_progress(conv, 0);
725
726 g_free(dim); /* I guess? I don't see it anywhere else... -- mid */
727 g_free(sn); 899 g_free(sn);
728 900 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
901
902 aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
903 aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
904 aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0);
905
906 return 1;
907 }
908
909 /*
910 * This is called when each chunk of an image is received. It can be used to
911 * update a progress bar, or to eat lots of dry cat food. Wet cat food is
912 * nasty, you sicko.
913 */
914 static int gaim_odc_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) {
915 va_list ap;
916 char *sn;
917 double percent;
918 GaimConnection *gc = sess->aux_data;
919 OscarData *od = (OscarData *)gc->proto_data;
920 GaimConversation *c;
921 struct oscar_direct_im *dim;
922
923 va_start(ap, fr);
924 sn = va_arg(ap, char *);
925 percent = va_arg(ap, double);
926 va_end(ap);
927
928 if (!sn || !(dim = oscar_direct_im_find(od, sn)))
929 return 1;
930 if (dim->watcher) {
931 gaim_input_remove(dim->watcher); /* Otherwise, the callback will callback */
932 /* The callback will callback? I don't get how that would happen here. */
933 dim->watcher = 0;
934 }
935
936 c = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
937 if (c != NULL)
938 gaim_conversation_update_progress(c, percent);
939 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
940 oscar_callback, dim->conn);
941
942 return 1;
943 }
944
945 /*
946 * This is called after a direct IM has been received in its entirety. This
947 * function is passed a long chunk of data which contains the IM with any
948 * data chunks (images) appended to it.
949 *
950 * This function rips out all the data chunks and creates an imgstore for
951 * each one. In order to do this, it first goes through the IM and takes
952 * out all the IMG tags. When doing so, it rewrites the original IMG tag
953 * with one compatible with the imgstore Gaim core code. For each one, we
954 * then read in chunks of data from the end of the message and actually
955 * create the img store using the given data.
956 *
957 * For somewhat easy reference, here's a sample message
958 * (without the whitespace and asterisks):
959 *
960 * <HTML><BODY BGCOLOR="#ffffff">
961 * <FONT LANG="0">
962 * This is a really stupid picture:<BR>
963 * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
964 * Yeah it is<BR>
965 * Here is another one:<BR>
966 * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">
967 * </FONT>
968 * </BODY></HTML>
969 * <BINARY>
970 * <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
971 * <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
972 * </BINARY>
973 */
974 static int gaim_odc_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
975 GaimConnection *gc = sess->aux_data;
976 GaimConvImFlags imflags = 0;
977 gchar *utf8;
978 GString *newmsg = g_string_new("");
979 GSList *images = NULL;
980 va_list ap;
981 const char *sn, *msg, *msgend, *binary;
982 size_t len;
983 int encoding, isawaymsg;
984
985 va_start(ap, fr);
986 sn = va_arg(ap, const char *);
987 msg = va_arg(ap, const char *);
988 len = va_arg(ap, size_t);
989 encoding = va_arg(ap, int);
990 isawaymsg = va_arg(ap, int);
991 va_end(ap);
992 msgend = msg + len;
993
994 gaim_debug_info("oscar",
995 "Got DirectIM message from %s\n", sn);
996
997 if (isawaymsg)
998 imflags |= GAIM_CONV_IM_AUTO_RESP;
999
1000 /* message has a binary trailer */
1001 if ((binary = gaim_strcasestr(msg, "<binary>"))) {
1002 GData *attribs;
1003 const char *tmp, *start, *end, *last = NULL;
1004
1005 tmp = msg;
1006
1007 /* for each valid image tag... */
1008 while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
1009 const char *id, *src, *datasize;
1010 const char *tag = NULL, *data = NULL;
1011 size_t size;
1012 int imgid = 0;
1013
1014 /* update the location of the last img tag */
1015 last = end;
1016
1017 /* grab attributes */
1018 id = g_datalist_get_data(&attribs, "id");
1019 src = g_datalist_get_data(&attribs, "src");
1020 datasize = g_datalist_get_data(&attribs, "datasize");
1021
1022 /* if we have id & datasize, build the data tag */
1023 if (id && datasize)
1024 tag = g_strdup_printf("<data id=\"%s\" size=\"%s\">", id, datasize);
1025
1026 /* if we have a tag, find the start of the data */
1027 if (tag && (data = gaim_strcasestr(binary, tag)))
1028 data += strlen(tag);
1029
1030 /* check the data is here and store it */
1031 if (data + (size = atoi(datasize)) <= msgend)
1032 imgid = gaim_imgstore_add(data, size, src);
1033
1034 /*
1035 * XXX - The code below contains some calls to oscar_encoding_to_utf8
1036 * The hardcoded "us-ascii" value REALLY needs to be removed.
1037 */
1038 /* if we have a stored image... */
1039 if (imgid) {
1040 /* append the message up to the tag */
1041 utf8 = oscar_encoding_to_utf8("us-ascii", tmp, start - tmp);
1042 if (utf8 != NULL) {
1043 newmsg = g_string_append(newmsg, utf8);
1044 g_free(utf8);
1045 }
1046
1047 /* write the new image tag */
1048 g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
1049
1050 /* and record the image number */
1051 images = g_slist_append(images, GINT_TO_POINTER(imgid));
1052 } else {
1053 /* otherwise, copy up to the end of the tag */
1054 utf8 = oscar_encoding_to_utf8("us-ascii", tmp, (end + 1) - tmp);
1055 if (utf8 != NULL) {
1056 newmsg = g_string_append(newmsg, utf8);
1057 g_free(utf8);
1058 }
1059 }
1060
1061 /* clear the attribute list */
1062 g_datalist_clear(&attribs);
1063
1064 /* continue from the end of the tag */
1065 tmp = end + 1;
1066 }
1067
1068 /* append any remaining message data (without the > :-) */
1069 if (last++ && (last < binary))
1070 newmsg = g_string_append_len(newmsg, last, binary - last);
1071
1072 /* set the flag if we caught any images */
1073 if (images)
1074 imflags |= GAIM_CONV_IM_IMAGES;
1075 } else {
1076 g_string_append_len(newmsg, msg, len);
1077 }
1078
1079 /* XXX - I imagine Paco-Paco will want to do some voodoo with the encoding here */
1080 serv_got_im(gc, sn, newmsg->str, imflags, time(NULL));
1081
1082 /* free up the message */
1083 g_string_free(newmsg, TRUE);
1084
1085 /* unref any images we allocated */
1086 if (images) {
1087 GSList *tmp;
1088 int id;
1089
1090 for (tmp = images; tmp != NULL; tmp = tmp->next) {
1091 id = GPOINTER_TO_INT(tmp->data);
1092 gaim_imgstore_unref(id);
1093 }
1094
1095 g_slist_free(images);
1096 }
1097
1098 return 1;
1099 }
1100
1101 static int gaim_odc_typing(aim_session_t *sess, aim_frame_t *fr, ...) {
1102 va_list ap;
1103 char *sn;
1104 int typing;
1105 GaimConnection *gc = sess->aux_data;
1106
1107 va_start(ap, fr);
1108 sn = va_arg(ap, char *);
1109 typing = va_arg(ap, int);
1110 va_end(ap);
1111
1112 if (typing == 0x0002) {
1113 /* I had to leave this. It's just too funny. It reminds me of my sister. */
1114 gaim_debug_info("oscar",
1115 "ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", sn);
1116 serv_got_typing(gc, sn, 0, GAIM_TYPING);
1117 } else if (typing == 0x0001)
1118 serv_got_typing(gc, sn, 0, GAIM_TYPED);
1119 else
1120 serv_got_typing_stopped(gc, sn);
1121 return 1;
1122 }
1123
1124 static int gaim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *message, GaimConvImFlags imflags) {
1125 char *buf;
1126 size_t len;
1127 int ret;
1128 GString *msg = g_string_new("<HTML><BODY>");
1129 GString *data = g_string_new("</BODY></HTML><BINARY>");
1130 GData *attribs;
1131 const char *start, *end, *last;
1132 int oscar_id = 0;
1133
1134 last = message;
1135
1136 /* for each valid IMG tag... */
1137 while (last && *last && gaim_markup_find_tag("img", last, &start, &end, &attribs)) {
1138 GaimStoredImage *image = NULL;
1139 const char *id;
1140
1141 if (start - last) {
1142 g_string_append_len(msg, last, start - last);
1143 }
1144
1145 id = g_datalist_get_data(&attribs, "id");
1146
1147 /* ... if it refers to a valid gaim image ... */
1148 if (id && (image = gaim_imgstore_get(atoi(id)))) {
1149 /* ... append the message from start to the tag ... */
1150 size_t size = gaim_imgstore_get_size(image);
1151 const char *filename = gaim_imgstore_get_filename(image);
1152 gpointer imgdata = gaim_imgstore_get_data(image);
1153
1154 oscar_id++;
1155
1156 /* ... insert a new img tag with the oscar id ... */
1157 if (filename)
1158 g_string_append_printf(msg,
1159 "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%zu\">",
1160 filename, oscar_id, size);
1161 else
1162 g_string_append_printf(msg,
1163 "<IMG ID=\"%d\" DATASIZE=\"%zu\">",
1164 oscar_id, size);
1165
1166 /* ... and append the data to the binary section ... */
1167 g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%zu\">",
1168 oscar_id, size);
1169 data = g_string_append_len(data, imgdata, size);
1170 data = g_string_append(data, "</DATA>");
1171 }
1172 /* If the tag is invalid, skip it, thus no else here */
1173
1174 g_datalist_clear(&attribs);
1175
1176 /* continue from the end of the tag */
1177 last = end + 1;
1178 }
1179
1180 /* append any remaining message data (without the > :-) */
1181 if (last && *last)
1182 msg = g_string_append(msg, last);
1183
1184 /* if we inserted any images in the binary section, append it */
1185 if (oscar_id) {
1186 msg = g_string_append_len(msg, data->str, data->len);
1187 msg = g_string_append(msg, "</BINARY>");
1188 }
1189
1190 len = msg->len;
1191 buf = msg->str;
1192 g_string_free(msg, FALSE);
1193 g_string_free(data, TRUE);
1194
1195
1196 /* XXX - The last parameter below is the encoding. Let Paco-Paco do something with it. */
1197 if (imflags & GAIM_CONV_IM_AUTO_RESP)
1198 ret = aim_odc_send_im(sess, conn, buf, len, 0, 1);
1199 else
1200 ret = aim_odc_send_im(sess, conn, buf, len, 0, 0);
1201
1202 g_free(buf);
1203
1204 return ret;
1205 }
1206
1207 struct ask_do_dir_im {
1208 char *who;
1209 GaimConnection *gc;
1210 };
1211
1212 static void oscar_cancel_direct_im(struct ask_do_dir_im *data) {
1213 g_free(data->who);
1214 g_free(data);
1215 }
1216
1217 static void oscar_direct_im(struct ask_do_dir_im *data) {
1218 GaimConnection *gc = data->gc;
1219 OscarData *od;
1220 struct oscar_direct_im *dim;
1221 int listenfd;
1222
1223 if (!g_list_find(gaim_connections_get_all(), gc)) {
1224 g_free(data->who);
1225 g_free(data);
1226 return;
1227 }
1228
1229 od = (OscarData *)gc->proto_data;
1230
1231 dim = oscar_direct_im_find(od, data->who);
1232 if (dim) {
1233 if (!(dim->connected)) { /* We'll free the old, unconnected dim, and start over */
1234 oscar_direct_im_disconnect(od, dim);
1235 gaim_debug_info("oscar",
1236 "Gave up on old direct IM, trying again\n");
1237 } else {
1238 gaim_notify_error(gc, NULL, "DirectIM already open.", NULL);
1239 g_free(data->who);
1240 g_free(data);
729 return; 1241 return;
730 } 1242 }
1243 }
1244 dim = g_new0(struct oscar_direct_im, 1);
1245 dim->gc = gc;
1246 g_snprintf(dim->name, sizeof dim->name, "%s", data->who);
1247
1248 listenfd = gaim_network_listen_range(5190, 5199);
1249 dim->conn = aim_odc_initiate(od->sess, data->who, listenfd, gaim_network_get_port_from_fd(listenfd));
1250 if (dim->conn != NULL) {
1251 od->direct_ims = g_slist_append(od->direct_ims, dim);
1252 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
1253 oscar_callback, dim->conn);
1254 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED,
1255 gaim_odc_initiate, 0);
1256 } else {
1257 gaim_notify_error(gc, NULL, _("Unable to open Direct IM"), NULL);
1258 oscar_direct_im_destroy(od, dim);
1259 }
1260
1261 g_free(data->who);
1262 g_free(data);
1263 }
1264
1265 /* this is the right click menu cb thingy */
1266 static void oscar_ask_direct_im(GaimConnection *gc, const char *who) {
1267 gchar *buf;
1268 struct ask_do_dir_im *data = g_new0(struct ask_do_dir_im, 1);
1269 data->who = g_strdup(who);
1270 data->gc = gc;
1271 buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."), who);
1272
1273 gaim_request_action(gc, NULL, buf,
1274 _("Because this reveals your IP address, it "
1275 "may be considered a privacy risk. Do you "
1276 "wish to continue?"),
1277 0, data, 2,
1278 _("Connect"), G_CALLBACK(oscar_direct_im),
1279 _("Cancel"), G_CALLBACK(oscar_cancel_direct_im));
1280 g_free(buf);
1281 }
1282
1283 /*****************************************************************************
1284 * End scary direct im stuff
1285 *****************************************************************************/
731 1286
732 static void oscar_callback(gpointer data, gint source, GaimInputCondition condition) { 1287 static void oscar_callback(gpointer data, gint source, GaimInputCondition condition) {
733 aim_conn_t *conn = (aim_conn_t *)data; 1288 aim_conn_t *conn = (aim_conn_t *)data;
734 aim_session_t *sess = aim_conn_getsess(conn); 1289 aim_session_t *sess = aim_conn_getsess(conn);
735 GaimConnection *gc = sess ? sess->aux_data : NULL; 1290 GaimConnection *gc = sess ? sess->aux_data : NULL;
956 g_free(n->show); 1511 g_free(n->show);
957 od->oscar_chats = g_slist_remove(od->oscar_chats, n); 1512 od->oscar_chats = g_slist_remove(od->oscar_chats, n);
958 g_free(n); 1513 g_free(n);
959 } 1514 }
960 while (od->direct_ims) { 1515 while (od->direct_ims) {
961 struct direct_im *n = od->direct_ims->data; 1516 struct oscar_direct_im *n = od->direct_ims->data;
962 if (n->watcher > 0) 1517 oscar_direct_im_destroy(od, n);
963 gaim_input_remove(n->watcher);
964 od->direct_ims = g_slist_remove(od->direct_ims, n);
965 g_free(n);
966 } 1518 }
967 /* BBB */ 1519 /* BBB */
968 while (od->file_transfers) { 1520 while (od->file_transfers) {
969 GaimXfer *xfer; 1521 GaimXfer *xfer;
970 xfer = (GaimXfer *)od->file_transfers->data; 1522 xfer = (GaimXfer *)od->file_transfers->data;
2158 g_hash_table_remove(od->buddyinfo, gaim_normalize(gc->account, info->sn)); 2710 g_hash_table_remove(od->buddyinfo, gaim_normalize(gc->account, info->sn));
2159 2711
2160 return 1; 2712 return 1;
2161 } 2713 }
2162 2714
2163 static void cancel_direct_im(struct ask_direct *d) {
2164 gaim_debug_info("oscar", "Freeing DirectIM prompts.\n");
2165
2166 g_free(d->sn);
2167 g_free(d);
2168 }
2169
2170 static void oscar_odc_callback(gpointer data, gint source, GaimInputCondition condition) {
2171 struct direct_im *dim = data;
2172 GaimConnection *gc = dim->gc;
2173 OscarData *od = gc->proto_data;
2174 GaimConversation *conv;
2175 char buf[256];
2176 struct sockaddr name;
2177 socklen_t name_len = 1;
2178
2179 if (!g_list_find(gaim_connections_get_all(), gc)) {
2180 g_free(dim);
2181 return;
2182 }
2183
2184 if (source < 0) {
2185 g_free(dim);
2186 return;
2187 }
2188
2189 dim->conn->fd = source;
2190 aim_conn_completeconnect(od->sess, dim->conn);
2191 conv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, dim->name);
2192
2193 /* This is the best way to see if we're connected or not */
2194 if (getpeername(source, &name, &name_len) == 0) {
2195 g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
2196 dim->connected = TRUE;
2197 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
2198 }
2199 od->direct_ims = g_slist_append(od->direct_ims, dim);
2200
2201 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
2202 }
2203
2204 /* BBB */ 2715 /* BBB */
2205 /* 2716 /*
2206 * This is called after a remote AIM user has connected to us. We 2717 * This is called after a remote AIM user has connected to us. We
2207 * want to do some voodoo with the socket file descriptors, add a 2718 * want to do some voodoo with the socket file descriptors, add a
2208 * callback or two, and then send the AIM_CB_OFT_PROMPT. 2719 * callback or two, and then send the AIM_CB_OFT_PROMPT.
2380 2891
2381 xfer->fd = conn->fd; 2892 xfer->fd = conn->fd;
2382 gaim_xfer_end(xfer); 2893 gaim_xfer_end(xfer);
2383 2894
2384 return 0; 2895 return 0;
2385 }
2386
2387 static void accept_direct_im(struct ask_direct *d) {
2388 GaimConnection *gc = d->gc;
2389 OscarData *od;
2390 struct direct_im *dim;
2391 char *host; int port = 5190;
2392 int i, rc;
2393
2394 if (!g_list_find(gaim_connections_get_all(), gc)) {
2395 cancel_direct_im(d);
2396 return;
2397 }
2398
2399 od = (OscarData *)gc->proto_data;
2400 gaim_debug_info("oscar", "Accepted DirectIM.\n");
2401
2402 dim = find_direct_im(od, d->sn);
2403 if (dim) {
2404 cancel_direct_im(d); /* 40 */
2405 return;
2406 }
2407 dim = g_new0(struct direct_im, 1);
2408 dim->gc = d->gc;
2409 g_snprintf(dim->name, sizeof dim->name, "%s", d->sn);
2410
2411 dim->conn = aim_odc_connect(od->sess, d->sn, NULL, d->cookie);
2412 if (!dim->conn) {
2413 g_free(dim);
2414 cancel_direct_im(d);
2415 return;
2416 }
2417
2418 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
2419 gaim_odc_incoming, 0);
2420 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
2421 gaim_odc_typing, 0);
2422 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
2423 gaim_odc_update_ui, 0);
2424 for (i = 0; i < (int)strlen(d->ip); i++) {
2425 if (d->ip[i] == ':') {
2426 port = atoi(&(d->ip[i+1]));
2427 break;
2428 }
2429 }
2430 host = g_strndup(d->ip, i);
2431 dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
2432 rc = gaim_proxy_connect(gc->account, host, port, oscar_odc_callback, dim);
2433 g_free(host);
2434 if (rc < 0) {
2435 aim_conn_kill(od->sess, &dim->conn);
2436 g_free(dim);
2437 cancel_direct_im(d);
2438 return;
2439 }
2440
2441 cancel_direct_im(d);
2442
2443 return;
2444 } 2896 }
2445 2897
2446 static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) { 2898 static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
2447 GaimConnection *gc = sess->aux_data; 2899 GaimConnection *gc = sess->aux_data;
2448 OscarData *od = gc->proto_data; 2900 OscarData *od = gc->proto_data;
2690 } else if (args->reqclass & AIM_CAPS_BUDDYICON) { 3142 } else if (args->reqclass & AIM_CAPS_BUDDYICON) {
2691 gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc), 3143 gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc),
2692 userinfo->sn, args->info.icon.icon, 3144 userinfo->sn, args->info.icon.icon,
2693 args->info.icon.length); 3145 args->info.icon.length);
2694 } else if (args->reqclass & AIM_CAPS_DIRECTIM) { 3146 } else if (args->reqclass & AIM_CAPS_DIRECTIM) {
3147 /* Consider moving all this into a helper func in the direct im block way up there */
2695 struct ask_direct *d = g_new0(struct ask_direct, 1); 3148 struct ask_direct *d = g_new0(struct ask_direct, 1);
3149 struct oscar_direct_im *dim = oscar_direct_im_find(od, userinfo->sn);
2696 char buf[256]; 3150 char buf[256];
2697 3151
2698 if (!args->verifiedip) { 3152 if (!args->verifiedip) {
2699 gaim_debug_info("oscar", 3153 gaim_debug_info("oscar",
2700 "directim kill blocked (%s)\n", userinfo->sn); 3154 "directim kill blocked (%s)\n", userinfo->sn);
2705 "%s received direct im request from %s (%s)\n", 3159 "%s received direct im request from %s (%s)\n",
2706 username, userinfo->sn, args->verifiedip); 3160 username, userinfo->sn, args->verifiedip);
2707 3161
2708 d->gc = gc; 3162 d->gc = gc;
2709 d->sn = g_strdup(userinfo->sn); 3163 d->sn = g_strdup(userinfo->sn);
2710 snprintf(d->ip, sizeof(d->ip), "%s:%d", args->verifiedip, args->port); 3164 snprintf(d->ip, sizeof(d->ip), "%s:%d", args->verifiedip, args->port?args->port:5190);
2711 memcpy(d->cookie, args->cookie, 8); 3165 memcpy(d->cookie, args->cookie, 8);
3166 if (dim && !dim->connected) {
3167 oscar_direct_im_destroy(od, dim);
3168 accept_direct_im_request(d);
3169 } else {
3170 if (dim)
3171 gaim_debug_warning("oscar", "DirectIM: received direct im request while "
3172 "already connected to that buddy!");
2712 g_snprintf(buf, sizeof buf, _("%s has just asked to directly connect to %s"), userinfo->sn, username); 3173 g_snprintf(buf, sizeof buf, _("%s has just asked to directly connect to %s"), userinfo->sn, username);
2713 3174
2714 gaim_request_action(gc, NULL, buf, 3175 gaim_request_action(gc, NULL, buf,
2715 _("This requires a direct connection between " 3176 _("This requires a direct connection between "
2716 "the two computers and is necessary for IM " 3177 "the two computers and is necessary for IM "
2717 "Images. Because your IP address will be " 3178 "Images. Because your IP address will be "
2718 "revealed, this may be considered a privacy " 3179 "revealed, this may be considered a privacy "
2719 "risk."), 0, d, 2, 3180 "risk."), 0, d, 2,
2720 _("Connect"), G_CALLBACK(accept_direct_im), 3181 _("Connect"), G_CALLBACK(accept_direct_im_request),
2721 _("Cancel"), G_CALLBACK(cancel_direct_im)); 3182 _("Cancel"), G_CALLBACK(destroy_direct_im_request));
3183 /* FIXME: we should actually send a packet on cancel */
3184 }
2722 } else if (args->reqclass & AIM_CAPS_ICQSERVERRELAY) { 3185 } else if (args->reqclass & AIM_CAPS_ICQSERVERRELAY) {
2723 gaim_debug_error("oscar", "Got an ICQ Server Relay message of type %d\n", args->info.rtfmsg.msgtype); 3186 gaim_debug_error("oscar", "Got an ICQ Server Relay message of type %d\n", args->info.rtfmsg.msgtype);
2724 } else { 3187 } else {
2725 gaim_debug_error("oscar", 3188 gaim_debug_error("oscar",
2726 "Unknown reqclass %hu\n", args->reqclass); 3189 "Unknown reqclass %hu\n", args->reqclass);
3155 3618
3156 /* BBB */ 3619 /* BBB */
3157 switch (reason) { 3620 switch (reason) {
3158 case 3: { /* Decline sendfile. */ 3621 case 3: { /* Decline sendfile. */
3159 GaimXfer *xfer; 3622 GaimXfer *xfer;
3623 struct oscar_direct_im *dim;
3624
3160 gaim_debug_info("oscar", 3625 gaim_debug_info("oscar",
3161 "AAA - Other user declined file transfer\n"); 3626 "AAA - Other user declined some sort of direct"
3627 "connect attempt (automaticly?)\n");
3162 if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie))) 3628 if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
3163 gaim_xfer_cancel_remote(xfer); 3629 gaim_xfer_cancel_remote(xfer);
3630 else if ((dim = oscar_direct_im_find(od, who))) {
3631 /* AAA should use find by cookie or something here */
3632 oscar_direct_im_disconnect(od, dim);
3633 }
3164 } break; 3634 } break;
3165 3635
3166 default: { 3636 default: {
3167 gaim_debug_warning("oscar", 3637 gaim_debug_warning("oscar",
3168 "Received an unknown rendezvous client auto-response " 3638 "Received an unknown rendezvous client auto-response "
4482 aim_flap_nop(od->sess, od->conn); 4952 aim_flap_nop(od->sess, od->conn);
4483 } 4953 }
4484 4954
4485 static int oscar_send_typing(GaimConnection *gc, const char *name, int typing) { 4955 static int oscar_send_typing(GaimConnection *gc, const char *name, int typing) {
4486 OscarData *od = (OscarData *)gc->proto_data; 4956 OscarData *od = (OscarData *)gc->proto_data;
4487 struct direct_im *dim = find_direct_im(od, name); 4957 struct oscar_direct_im *dim = oscar_direct_im_find(od, name);
4488 if (dim) 4958 if (dim && dim->connected)
4489 if (typing == GAIM_TYPING) 4959 if (typing == GAIM_TYPING)
4490 aim_odc_send_typing(od->sess, dim->conn, 0x0002); 4960 aim_odc_send_typing(od->sess, dim->conn, 0x0002);
4491 else if (typing == GAIM_TYPED) 4961 else if (typing == GAIM_TYPED)
4492 aim_odc_send_typing(od->sess, dim->conn, 0x0001); 4962 aim_odc_send_typing(od->sess, dim->conn, 0x0001);
4493 else 4963 else
4513 static void oscar_ask_direct_im(GaimConnection *gc, const char *name); 4983 static void oscar_ask_direct_im(GaimConnection *gc, const char *name);
4514 static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimConvImFlags); 4984 static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimConvImFlags);
4515 4985
4516 static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimConvImFlags imflags) { 4986 static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimConvImFlags imflags) {
4517 OscarData *od = (OscarData *)gc->proto_data; 4987 OscarData *od = (OscarData *)gc->proto_data;
4518 struct direct_im *dim = find_direct_im(od, name); 4988 struct oscar_direct_im *dim = oscar_direct_im_find(od, name);
4519 int ret = 0; 4989 int ret = 0;
4520 GError *err = NULL; 4990 GError *err = NULL;
4521 const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc)); 4991 const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc));
4522 char *tmpmsg = NULL, *tmpmsg2 = NULL; 4992 char *tmpmsg = NULL, *tmpmsg2 = NULL;
4523 4993
5892 va_end(ap); 6362 va_end(ap);
5893 6363
5894 return 0; 6364 return 0;
5895 } 6365 }
5896 6366
5897 /*
5898 * We have just established a socket with the other dude, so set up some handlers.
5899 */
5900 static int gaim_odc_initiate(aim_session_t *sess, aim_frame_t *fr, ...) {
5901 GaimConnection *gc = sess->aux_data;
5902 OscarData *od = (OscarData *)gc->proto_data;
5903 GaimConversation *conv;
5904 struct direct_im *dim;
5905 char buf[256];
5906 char *sn;
5907 va_list ap;
5908 aim_conn_t *newconn, *listenerconn;
5909
5910 va_start(ap, fr);
5911 newconn = va_arg(ap, aim_conn_t *);
5912 listenerconn = va_arg(ap, aim_conn_t *);
5913 va_end(ap);
5914
5915 aim_conn_close(listenerconn);
5916 aim_conn_kill(sess, &listenerconn);
5917
5918 sn = g_strdup(aim_odc_getsn(newconn));
5919
5920 gaim_debug_info("oscar",
5921 "DirectIM: initiate success to %s\n", sn);
5922 dim = find_direct_im(od, sn);
5923
5924 conv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, sn);
5925 gaim_input_remove(dim->watcher);
5926 dim->conn = newconn;
5927 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
5928 dim->connected = TRUE;
5929 g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
5930 g_free(sn);
5931 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
5932
5933 aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
5934 aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
5935 aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0);
5936
5937 return 1;
5938 }
5939
5940 /*
5941 * This is called when each chunk of an image is received. It can be used to
5942 * update a progress bar, or to eat lots of dry cat food. Wet cat food is
5943 * nasty, you sicko.
5944 */
5945 static int gaim_odc_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) {
5946 va_list ap;
5947 char *sn;
5948 double percent;
5949 GaimConnection *gc = sess->aux_data;
5950 OscarData *od = (OscarData *)gc->proto_data;
5951 GaimConversation *c;
5952 struct direct_im *dim;
5953
5954 va_start(ap, fr);
5955 sn = va_arg(ap, char *);
5956 percent = va_arg(ap, double);
5957 va_end(ap);
5958
5959 if (!sn || !(dim = find_direct_im(od, sn)))
5960 return 1;
5961 if (dim->watcher) {
5962 gaim_input_remove(dim->watcher); /* Otherwise, the callback will callback */
5963 dim->watcher = 0;
5964 }
5965
5966 c = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
5967 if (c != NULL)
5968 gaim_conversation_update_progress(c, percent);
5969 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
5970 oscar_callback, dim->conn);
5971
5972 return 1;
5973 }
5974
5975 /*
5976 * This is called after a direct IM has been received in its entirety. This
5977 * function is passed a long chunk of data which contains the IM with any
5978 * data chunks (images) appended to it.
5979 *
5980 * This function rips out all the data chunks and creates an imgstore for
5981 * each one. In order to do this, it first goes through the IM and takes
5982 * out all the IMG tags. When doing so, it rewrites the original IMG tag
5983 * with one compatible with the imgstore Gaim core code. For each one, we
5984 * then read in chunks of data from the end of the message and actually
5985 * create the img store using the given data.
5986 *
5987 * For somewhat easy reference, here's a sample message
5988 * (without the whitespace and asterisks):
5989 *
5990 * <HTML><BODY BGCOLOR="#ffffff">
5991 * <FONT LANG="0">
5992 * This is a really stupid picture:<BR>
5993 * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
5994 * Yeah it is<BR>
5995 * Here is another one:<BR>
5996 * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">
5997 * </FONT>
5998 * </BODY></HTML>
5999 * <BINARY>
6000 * <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
6001 * <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
6002 * </BINARY>
6003 */
6004 static int gaim_odc_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
6005 GaimConnection *gc = sess->aux_data;
6006 GaimConvImFlags imflags = 0;
6007 gchar *utf8;
6008 GString *newmsg = g_string_new("");
6009 GSList *images = NULL;
6010 va_list ap;
6011 const char *sn, *msg, *msgend, *binary;
6012 size_t len;
6013 int encoding, isawaymsg;
6014
6015 va_start(ap, fr);
6016 sn = va_arg(ap, const char *);
6017 msg = va_arg(ap, const char *);
6018 len = va_arg(ap, size_t);
6019 encoding = va_arg(ap, int);
6020 isawaymsg = va_arg(ap, int);
6021 va_end(ap);
6022 msgend = msg + len;
6023
6024 gaim_debug_info("oscar",
6025 "Got DirectIM message from %s\n", sn);
6026
6027 if (isawaymsg)
6028 imflags |= GAIM_CONV_IM_AUTO_RESP;
6029
6030 /* message has a binary trailer */
6031 if ((binary = gaim_strcasestr(msg, "<binary>"))) {
6032 GData *attribs;
6033 const char *tmp, *start, *end, *last = NULL;
6034
6035 tmp = msg;
6036
6037 /* for each valid image tag... */
6038 while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
6039 const char *id, *src, *datasize;
6040 const char *tag = NULL, *data = NULL;
6041 size_t size;
6042 int imgid = 0;
6043
6044 /* update the location of the last img tag */
6045 last = end;
6046
6047 /* grab attributes */
6048 id = g_datalist_get_data(&attribs, "id");
6049 src = g_datalist_get_data(&attribs, "src");
6050 datasize = g_datalist_get_data(&attribs, "datasize");
6051
6052 /* if we have id & datasize, build the data tag */
6053 if (id && datasize)
6054 tag = g_strdup_printf("<data id=\"%s\" size=\"%s\">", id, datasize);
6055
6056 /* if we have a tag, find the start of the data */
6057 if (tag && (data = gaim_strcasestr(binary, tag)))
6058 data += strlen(tag);
6059
6060 /* check the data is here and store it */
6061 if (data + (size = atoi(datasize)) <= msgend)
6062 imgid = gaim_imgstore_add(data, size, src);
6063
6064 /*
6065 * XXX - The code below contains some calls to oscar_encoding_to_utf8
6066 * The hardcoded "us-ascii" value REALLY needs to be removed.
6067 */
6068 /* if we have a stored image... */
6069 if (imgid) {
6070 /* append the message up to the tag */
6071 utf8 = oscar_encoding_to_utf8("us-ascii", tmp, start - tmp);
6072 if (utf8 != NULL) {
6073 newmsg = g_string_append(newmsg, utf8);
6074 g_free(utf8);
6075 }
6076
6077 /* write the new image tag */
6078 g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
6079
6080 /* and record the image number */
6081 images = g_slist_append(images, GINT_TO_POINTER(imgid));
6082 } else {
6083 /* otherwise, copy up to the end of the tag */
6084 utf8 = oscar_encoding_to_utf8("us-ascii", tmp, (end + 1) - tmp);
6085 if (utf8 != NULL) {
6086 newmsg = g_string_append(newmsg, utf8);
6087 g_free(utf8);
6088 }
6089 }
6090
6091 /* clear the attribute list */
6092 g_datalist_clear(&attribs);
6093
6094 /* continue from the end of the tag */
6095 tmp = end + 1;
6096 }
6097
6098 /* append any remaining message data (without the > :-) */
6099 if (last++ && (last < binary))
6100 newmsg = g_string_append_len(newmsg, last, binary - last);
6101
6102 /* set the flag if we caught any images */
6103 if (images)
6104 imflags |= GAIM_CONV_IM_IMAGES;
6105 } else {
6106 g_string_append_len(newmsg, msg, len);
6107 }
6108
6109 /* XXX - I imagine Paco-Paco will want to do some voodoo with the encoding here */
6110 serv_got_im(gc, sn, newmsg->str, imflags, time(NULL));
6111
6112 /* free up the message */
6113 g_string_free(newmsg, TRUE);
6114
6115 /* unref any images we allocated */
6116 if (images) {
6117 GSList *tmp;
6118 int id;
6119
6120 for (tmp = images; tmp != NULL; tmp = tmp->next) {
6121 id = GPOINTER_TO_INT(tmp->data);
6122 gaim_imgstore_unref(id);
6123 }
6124
6125 g_slist_free(images);
6126 }
6127
6128 return 1;
6129 }
6130
6131 static int gaim_odc_typing(aim_session_t *sess, aim_frame_t *fr, ...) {
6132 va_list ap;
6133 char *sn;
6134 int typing;
6135 GaimConnection *gc = sess->aux_data;
6136
6137 va_start(ap, fr);
6138 sn = va_arg(ap, char *);
6139 typing = va_arg(ap, int);
6140 va_end(ap);
6141
6142 if (typing == 0x0002) {
6143 /* I had to leave this. It's just too funny. It reminds me of my sister. */
6144 gaim_debug_info("oscar",
6145 "ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", sn);
6146 serv_got_typing(gc, sn, 0, GAIM_TYPING);
6147 } else if (typing == 0x0001)
6148 serv_got_typing(gc, sn, 0, GAIM_TYPED);
6149 else
6150 serv_got_typing_stopped(gc, sn);
6151 return 1;
6152 }
6153
6154 static int gaim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *message, GaimConvImFlags imflags) {
6155 char *buf;
6156 size_t len;
6157 int ret;
6158 GString *msg = g_string_new("");
6159 GString *data = g_string_new("<BINARY>");
6160 GData *attribs;
6161 const char *start, *end, *last;
6162 int oscar_id = 0;
6163
6164 last = message;
6165
6166 /* for each valid IMG tag... */
6167 while (last && *last && gaim_markup_find_tag("img", last, &start, &end, &attribs)) {
6168 GaimStoredImage *image = NULL;
6169 const char *id;
6170
6171 if (start - last) {
6172 g_string_append_len(msg, last, start - last);
6173 }
6174
6175 id = g_datalist_get_data(&attribs, "id");
6176
6177 /* ... if it refers to a valid gaim image ... */
6178 if (id && (image = gaim_imgstore_get(atoi(id)))) {
6179 /* ... append the message from start to the tag ... */
6180 size_t size = gaim_imgstore_get_size(image);
6181 const char *filename = gaim_imgstore_get_filename(image);
6182 gpointer imgdata = gaim_imgstore_get_data(image);
6183
6184 oscar_id++;
6185
6186 /* ... insert a new img tag with the oscar id ... */
6187 if (filename)
6188 g_string_append_printf(msg,
6189 "<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%zu\">",
6190 filename, oscar_id, size);
6191 else
6192 g_string_append_printf(msg,
6193 "<IMG ID=\"%d\" DATASIZE=\"%zu\">",
6194 oscar_id, size);
6195
6196 /* ... and append the data to the binary section ... */
6197 g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%zu\">",
6198 oscar_id, size);
6199 data = g_string_append_len(data, imgdata, size);
6200 data = g_string_append(data, "</DATA>");
6201 }
6202 /* If the tag is invalid, skip it, thus no else here */
6203
6204 g_datalist_clear(&attribs);
6205
6206 /* continue from the end of the tag */
6207 last = end + 1;
6208 }
6209
6210 /* append any remaining message data (without the > :-) */
6211 if (last && *last)
6212 msg = g_string_append(msg, last);
6213
6214 /* if we inserted any images in the binary section, append it */
6215 if (oscar_id) {
6216 msg = g_string_append_len(msg, data->str, data->len);
6217 msg = g_string_append(msg, "</BINARY>");
6218 }
6219
6220 len = msg->len;
6221 buf = msg->str;
6222 g_string_free(msg, FALSE);
6223 g_string_free(data, TRUE);
6224
6225
6226 /* XXX - The last parameter below is the encoding. Let Paco-Paco do something with it. */
6227 if (imflags & GAIM_CONV_IM_AUTO_RESP)
6228 ret = aim_odc_send_im(sess, conn, buf, len, 0, 1);
6229 else
6230 ret = aim_odc_send_im(sess, conn, buf, len, 0, 0);
6231
6232 g_free(buf);
6233
6234 return ret;
6235 }
6236
6237 struct ask_do_dir_im {
6238 char *who;
6239 GaimConnection *gc;
6240 };
6241
6242 static void oscar_cancel_direct_im(struct ask_do_dir_im *data) {
6243 g_free(data->who);
6244 g_free(data);
6245 }
6246
6247 static void oscar_direct_im(struct ask_do_dir_im *data) {
6248 GaimConnection *gc = data->gc;
6249 OscarData *od;
6250 struct direct_im *dim;
6251 int listenfd;
6252
6253 if (!g_list_find(gaim_connections_get_all(), gc)) {
6254 g_free(data->who);
6255 g_free(data);
6256 return;
6257 }
6258
6259 od = (OscarData *)gc->proto_data;
6260
6261 dim = find_direct_im(od, data->who);
6262 if (dim) {
6263 if (!(dim->connected)) { /* We'll free the old, unconnected dim, and start over */
6264 od->direct_ims = g_slist_remove(od->direct_ims, dim);
6265 gaim_input_remove(dim->watcher);
6266 g_free(dim);
6267 gaim_debug_info("oscar",
6268 "Gave up on old direct IM, trying again\n");
6269 } else {
6270 gaim_notify_error(gc, NULL, "DirectIM already open.", NULL);
6271 g_free(data->who);
6272 g_free(data);
6273 return;
6274 }
6275 }
6276 dim = g_new0(struct direct_im, 1);
6277 dim->gc = gc;
6278 g_snprintf(dim->name, sizeof dim->name, "%s", data->who);
6279
6280 listenfd = gaim_network_listen_range(5190, 5199);
6281 dim->conn = aim_odc_initiate(od->sess, data->who, listenfd, gaim_network_get_port_from_fd(listenfd));
6282 if (dim->conn != NULL) {
6283 od->direct_ims = g_slist_append(od->direct_ims, dim);
6284 dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
6285 oscar_callback, dim->conn);
6286 aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED,
6287 gaim_odc_initiate, 0);
6288 } else {
6289 gaim_notify_error(gc, NULL, _("Unable to open Direct IM"), NULL);
6290 g_free(dim);
6291 }
6292
6293 g_free(data->who);
6294 g_free(data);
6295 }
6296
6297 static void oscar_ask_direct_im(GaimConnection *gc, const char *who) {
6298 gchar *buf;
6299 struct ask_do_dir_im *data = g_new0(struct ask_do_dir_im, 1);
6300 data->who = g_strdup(who);
6301 data->gc = gc;
6302 buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."), who);
6303
6304 gaim_request_action(gc, NULL, buf,
6305 _("Because this reveals your IP address, it "
6306 "may be considered a privacy risk. Do you "
6307 "wish to continue?"),
6308 0, data, 2,
6309 _("Connect"), G_CALLBACK(oscar_direct_im),
6310 _("Cancel"), G_CALLBACK(oscar_cancel_direct_im));
6311 g_free(buf);
6312 }
6313
6314 static void oscar_set_permit_deny(GaimConnection *gc) { 6367 static void oscar_set_permit_deny(GaimConnection *gc) {
6315 GaimAccount *account = gaim_connection_get_account(gc); 6368 GaimAccount *account = gaim_connection_get_account(gc);
6316 OscarData *od = (OscarData *)gc->proto_data; 6369 OscarData *od = (OscarData *)gc->proto_data;
6317 #ifdef NOSSI 6370 #ifdef NOSSI
6318 GSList *list; 6371 GSList *list;
6911 } 6964 }
6912 6965
6913 static void oscar_convo_closed(GaimConnection *gc, const char *who) 6966 static void oscar_convo_closed(GaimConnection *gc, const char *who)
6914 { 6967 {
6915 OscarData *od = gc->proto_data; 6968 OscarData *od = gc->proto_data;
6916 struct direct_im *dim = find_direct_im(od, who); 6969 struct oscar_direct_im *dim = oscar_direct_im_find(od, who);
6917 6970
6918 if (!dim) 6971 if (!dim)
6919 return; 6972 return;
6920 6973
6921 od->direct_ims = g_slist_remove(od->direct_ims, dim); 6974 oscar_direct_im_destroy(od, dim);
6922 gaim_input_remove(dim->watcher);
6923 aim_conn_kill(od->sess, &dim->conn);
6924 g_free(dim);
6925 } 6975 }
6926 6976
6927 static GaimPluginProtocolInfo prpl_info = 6977 static GaimPluginProtocolInfo prpl_info =
6928 { 6978 {
6929 GAIM_PRPL_API_VERSION, 6979 GAIM_PRPL_API_VERSION,

mercurial