changeset 3044:e68e2ba82310

[gaim-migrate @ 3057] DirectIM Image sending. Not all the kinks are worked out, and it's really hackish at parts, but it works and I think it's stable. It's late and I have to be up early tommorow, and I want this to be somewhat tested before 0.54 is released so we don't have a repeat of 0.53 ;) Also, Luke Schierer wins the #gaim MVP award with an inclusion in CREDITS :) Thanks, Luke. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Thu, 14 Mar 2002 07:44:43 +0000
parents 5a459387755a
children 9f72aa1b1089
files doc/CREDITS src/conversation.c src/protocols/oscar/oscar.c src/protocols/oscar/txqueue.c src/prpl.h src/ui.h
diffstat 6 files changed, 175 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/doc/CREDITS	Thu Mar 14 03:29:03 2002 +0000
+++ b/doc/CREDITS	Thu Mar 14 07:44:43 2002 +0000
@@ -67,6 +67,9 @@
 Ryan C. Gordon - I still think you look like Silent Bob.
 Elliot Tobin <elliot@bha.udel.edu>
 
+Many, many thanks to Luke Schierer who logs many hours in #gaim (as LSchiere)
+providing technical support to users and testing patches for developers.
+
 A big thanks to the X-Chat developers, who were kind enough to
 license X-Chat under the GPL so that I could learn to be as cool
 as them. -EW
--- a/src/conversation.c	Thu Mar 14 03:29:03 2002 +0000
+++ b/src/conversation.c	Thu Mar 14 07:44:43 2002 +0000
@@ -53,6 +53,7 @@
 #include "pixmaps/wood.xpm"
 #include "pixmaps/save_small.xpm"
 #include "pixmaps/speaker.xpm"
+#include "pixmaps/image_icon.xpm"
 
 #include "pixmaps/luke03.xpm"
 #include "pixmaps/oneeye.xpm"
@@ -406,6 +407,68 @@
 	gtk_widget_show(window);
 }
 
+static void do_insert_image(GtkObject *obj, GtkWidget *wid)
+{
+	struct conversation *c = gtk_object_get_user_data(obj);
+	char *name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(wid));
+	char *filename;
+	int pos;
+	char buf[512];
+	struct stat st;
+	int id = g_slist_length(c->images) + 1;
+	
+	if (file_is_dir(name, wid))
+		return;
+	if ((!c->is_chat && g_list_find(conversations, c)))
+		name = g_strdup(name);
+	else
+		name = NULL;
+	gtk_widget_destroy(wid);
+	if (!name)
+		return;
+
+	if (stat(name, &st) != 0) {
+		debug_printf("Could not stat %s\n", name);
+		return;
+	}
+	
+	filename = name;
+	while (strchr(filename, '/')) 
+		filename = strchr(filename, '/') + 1;
+		
+	g_snprintf(buf, sizeof(buf),
+		   "<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
+		   filename, id, (int)st.st_size);
+	
+	c->images = g_slist_append(c->images, g_strdup(name));
+
+	if (GTK_OLD_EDITABLE(c->entry)->has_selection) {
+		int finish = GTK_OLD_EDITABLE(c->entry)->selection_end_pos;
+		gtk_editable_insert_text(GTK_EDITABLE(c->entry),
+					 buf, strlen(buf), &finish);
+	} else {
+		pos = GTK_OLD_EDITABLE(c->entry)->current_pos;
+		gtk_editable_insert_text(GTK_EDITABLE(c->entry),
+					 buf, strlen(buf), &pos);
+	}
+	g_free(name);
+}
+
+void insert_image(GtkWidget *save, struct conversation *c)
+{
+	char buf[BUF_LONG];
+	GtkWidget *window = gtk_file_selection_new(_("Gaim - Insert Image"));
+	g_snprintf(buf, sizeof(buf), "%s/", g_get_home_dir());
+	gtk_file_selection_set_filename(GTK_FILE_SELECTION(window), buf);
+	gtk_object_set_user_data(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button), c);
+	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+			   "clicked", GTK_SIGNAL_FUNC(do_insert_image), window);
+	gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+				  "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)window);
+	gtk_widget_show(window);
+}
+
+
 void insert_smiley(GtkWidget *smiley, struct conversation *c)
 {
 	if (state_lock)
@@ -931,7 +994,8 @@
 void send_callback(GtkWidget *widget, struct conversation *c)
 {
 	char *buf, *buf2;
-	int limit, length=0;
+	int limit;
+	gulong length=0;
 	int err = 0;
 
 	if (!c->gc)
@@ -1045,52 +1109,79 @@
 				id = 1;
 				length = strlen(buffy) + strlen("<BINARY></BINARY>");
 				bigbuf = g_malloc(length);
-				g_snprintf(bigbuf, strlen(buffy)+strlen("<BINARY> "), "%s<BINARY>", buffy);
+				g_snprintf(bigbuf, strlen(buffy)+strlen("<BINARY> ") + 1, "%s<BINARY>", buffy);
 				offset = strlen(buffy) + strlen("<BINARY>");
 				while (tmplist) {
-				  FILE *imgfile;
-				  struct stat st;
+					FILE *imgfile;
+					char *filename;
+					struct stat st;
 					char imgtag[1024];
 					if (stat(tmplist->data, &st) != 0) {
 						debug_printf("Could not stat %s\n", tmplist->data);
 						break;
 					}
+								
+					/* Here we check to make sure the user still wants to send the
+					 * image.  He may have deleted the <img> tag in which case we
+					 * don't want to send the binary data. */
+					filename = tmplist->data;
+					while (strchr(filename, '/')) 
+						filename = strchr(filename, '/') + 1;
+					g_snprintf(imgtag, sizeof(imgtag),
+						   "<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
+						   filename, id, (int)st.st_size);
+				       	if (!strstr(buffy, imgtag)) {
+						tmplist = tmplist->next;
+						continue;
+					}
 					g_snprintf(imgtag, sizeof(imgtag),
 						   "<DATA ID=\"%d\" SIZE=\"%d\">",
-						   id, st.st_size);
-					length = length + strlen(imgtag) + st.st_size;
-					bigbuf = realloc(bigbuf, length);
-					if (!(imgfile = fopen(c->images->data, "r"))) {
+						   id, (int)st.st_size);
+					
+					length = length + strlen(imgtag) + st.st_size + strlen("</DATA>");;
+					bigbuf = g_realloc(bigbuf, length);
+					if (!(imgfile = fopen(tmplist->data, "r"))) {
 						debug_printf("Could not open %s\n", tmplist->data);
-						break;
+						continue;
 					}
 					g_snprintf(bigbuf + offset, strlen(imgtag) + 1, "%s", imgtag);
 					offset = offset + strlen(imgtag);
-					fread(bigbuf + offset, 1, st.st_size, imgfile);
-					offset = offset + st.st_size;
-					g_snprintf(bigbuf + offset, strlen("</DATA>"), "</DATA>");
+					offset = offset + fread(bigbuf + offset, 1, st.st_size, imgfile);
+					fclose(imgfile);
+					g_snprintf(bigbuf + offset, strlen("</DATA>") + 1, "</DATA>");
 					offset= offset + strlen("</DATA>");
 					id++;
 					tmplist = tmplist->next;
 				}
 				
-				g_snprintf(bigbuf + offset, strlen("</BINARY>"), "</BINARY>");   
-				if (serv_send_im(c->gc, c->name, bigbuf, length, imflags) > 0) {
+				g_snprintf(bigbuf + offset, strlen("</BINARY>") + 1, "</BINARY>"); 
+				err =serv_send_im(c->gc, c->name, bigbuf, length, imflags);
+				if (err > 0) {
+					GSList *tempy = c->images;
+					while (tempy) {
+						g_free(tempy->data);
+						tempy = tempy->next;
+					}
+					g_slist_free(tempy);
+					c->images = NULL;
 					write_to_conv(c, bigbuf, WFLAG_SEND, NULL, time(NULL), length);
 					if (c->makesound && (sound_options & OPT_SOUND_SEND))
 						play_sound(SEND);
 					if (im_options & OPT_IM_POPDOWN)
 						gtk_widget_hide(c->window);
 					
+					
 				}
 				g_free(bigbuf);
 			} else {
-				if (serv_send_im(c->gc, c->name, buffy, -1, imflags) > 0)
+				err =serv_send_im(c->gc, c->name, buffy, -1, imflags);
+				if (err > 0) { 
 					write_to_conv(c, buf, WFLAG_SEND, NULL, time(NULL), -1);
-				if (c->makesound && (sound_options & OPT_SOUND_SEND))
-					play_sound(SEND);
-				if (im_options & OPT_IM_POPDOWN)
-					gtk_widget_hide(c->window);
+					if (c->makesound && (sound_options & OPT_SOUND_SEND))
+						play_sound(SEND);
+					if (im_options & OPT_IM_POPDOWN)
+						gtk_widget_hide(c->window);
+				}
 			}
 			g_free(buffy);
 		}
@@ -1107,6 +1198,8 @@
 	if (err < 0) {
 		if (err == -E2BIG)
 			do_error_dialog(_("Unable to send message: too large"), _("Message Error"));
+		else if (err == -ENOTCONN)
+			debug_printf("Not yet connected\n");
 		else
 			do_error_dialog(_("Unable to send message: Unknown reason"), _("Message Error"));
 	} else {
@@ -1813,9 +1906,7 @@
 }
 
 void update_progress(struct conversation *c, float percent) {
-       while (gtk_events_pending())
-               gtk_main_iteration();
-       
+            
        if (percent >= 1 && !(c->progress))
 	       return;
 
@@ -1840,11 +1931,11 @@
 GtkWidget *build_conv_toolbar(struct conversation *c)
 {
 	GdkPixmap *strike_i, *small_i, *normal_i, *big_i, *bold_i, *italic_i, *underline_i, *speaker_i,
-	    *wood_i, *fgcolor_i, *bgcolor_i, *link_i, *font_i, *smiley_i, *save_i;
+	    *wood_i, *fgcolor_i, *bgcolor_i, *link_i, *font_i, *smiley_i, *save_i, *image_i;
 	GtkWidget *strike_p, *small_p, *normal_p, *big_p, *bold_p, *italic_p, *underline_p, *speaker_p,
-	    *wood_p, *fgcolor_p, *bgcolor_p, *link_p, *font_p, *smiley_p, *save_p;
+	    *wood_p, *fgcolor_p, *bgcolor_p, *link_p, *font_p, *smiley_p, *save_p, *image_p;
 	GtkWidget *strike, *small, *normal, *big, *bold, *italic, *underline, *speaker, *wood,
-	    *fgcolorbtn, *bgcolorbtn, *link, *font, *smiley, *save;
+	    *fgcolorbtn, *bgcolorbtn, *link, *font, *smiley, *save, *image;
 	GdkBitmap *mask;
 	GtkWidget *toolbar;
 	GtkWidget *win;
@@ -1915,6 +2006,11 @@
 	gtk_widget_show(smiley_p);
 	gdk_bitmap_unref(mask);
 
+	image_i = gdk_pixmap_create_from_xpm_d(win->window, &mask, &win->style->white, image_icon_xpm);
+	image_p = gtk_pixmap_new(image_i, mask);
+	gtk_widget_show(image_p);
+	gdk_bitmap_unref(mask);
+
 	wood_i = gdk_pixmap_create_from_xpm_d(win->window, &mask, &win->style->white, wood_xpm);
 	wood_p = gtk_pixmap_new(wood_i, mask);
 	gtk_widget_show(wood_p);
@@ -1987,7 +2083,10 @@
 					    GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
 					    NULL, NULL, _("Insert smiley face"), _("Smiley"),
 					    smiley_p, GTK_SIGNAL_FUNC(insert_smiley), c);
-
+	image = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
+					    NULL, _("Insert IM Image"), _("Image"),
+					    image_p, GTK_SIGNAL_FUNC(insert_image), c);
+	
 	gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
 
 	wood = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
@@ -2027,6 +2126,7 @@
 		gtk_button_set_relief(GTK_BUTTON(bgcolorbtn), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(link), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(smiley), GTK_RELIEF_NONE);
+		gtk_button_set_relief(GTK_BUTTON(image), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(wood), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(save), GTK_RELIEF_NONE);
 		gtk_button_set_relief(GTK_BUTTON(speaker), GTK_RELIEF_NONE);
@@ -2049,6 +2149,7 @@
 	gdk_pixmap_unref(wood_i);
 	gdk_pixmap_unref(save_i);
 	gdk_pixmap_unref(speaker_i);
+	gdk_pixmap_unref(image_i);
 
 	c->bold = bold;
 	c->strike = strike;
@@ -2061,9 +2162,9 @@
 	c->wood = wood;
 	c->font = font;
 	c->smiley = smiley;
+	c->imagebtn = image;
 
 	gtk_widget_set_sensitive(c->log_button, ((logging_options & OPT_LOG_ALL)) ? FALSE : TRUE);
-
 	gtk_widget_set_sensitive(c->bold, ((font_options & OPT_FONT_BOLD)) ? FALSE : TRUE);
 	gtk_widget_set_sensitive(c->italic, ((font_options & OPT_FONT_ITALIC)) ? FALSE : TRUE);
 	gtk_widget_set_sensitive(c->underline, ((font_options & OPT_FONT_UNDERLINE)) ? FALSE : TRUE);
@@ -2240,7 +2341,6 @@
 	c->gc = gc;
 
 	set_convo_title(c);
-		
 	update_buttons_by_protocol(c);
 
 	update_icon(c);
@@ -2264,7 +2364,6 @@
 			gtk_widget_set_sensitive(c->whisper, FALSE);
 		if (c->invite)
 			gtk_widget_set_sensitive(c->invite, FALSE);
-
 		return;
 	}
 
@@ -2278,11 +2377,16 @@
 			gtk_widget_set_sensitive(c->send, FALSE);
 		else
 			gtk_widget_set_sensitive(c->send, TRUE);
+		gtk_widget_set_sensitive(c->imagebtn, FALSE);
 	} else {
 		if (c->gc->prpl->send_im == NULL && c->send)
 			gtk_widget_set_sensitive(c->send, FALSE);
 		else
 			gtk_widget_set_sensitive(c->send, TRUE);
+		if (c->gc->prpl->options & OPT_PROTO_IM_IMAGE)
+			gtk_widget_set_sensitive(c->imagebtn, TRUE);
+		else
+			gtk_widget_set_sensitive(c->imagebtn, FALSE);
 	}
 
 	if (c->gc->prpl->warn == NULL && c->warn)
--- a/src/protocols/oscar/oscar.c	Thu Mar 14 03:29:03 2002 +0000
+++ b/src/protocols/oscar/oscar.c	Thu Mar 14 07:44:43 2002 +0000
@@ -2436,7 +2436,11 @@
 			else return ret;
 		}
 		debug_printf("Direct IM pending, but not connected; sending through server\n");
-		}
+	} else if (len != -1) {
+		/* Trying to send an IM image outside of a direct connection. */
+		oscar_ask_direct_im(gc, name);
+		return -ENOTCONN;
+	}
 	if (imflags & IM_FLAG_AWAY) {
 		ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
 	} else {
@@ -3168,6 +3172,9 @@
 	if (!(dim = find_direct_im(od, sn)))
 		return 1;
 	gaim_input_remove(dim->watcher);   /* Otherwise, the callback will callback */
+	while (gtk_events_pending())
+		gtk_main_iteration();
+	
 	if ((c = find_conversation(sn)))
 		update_progress(c, percent);
 	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
@@ -3554,7 +3561,7 @@
 
 void oscar_init(struct prpl *ret) {
 	ret->protocol = PROTO_OSCAR;
-	ret->options = OPT_PROTO_BUDDY_ICON;
+	ret->options = OPT_PROTO_BUDDY_ICON | OPT_PROTO_IM_IMAGE;
 	ret->name = oscar_name;
 	ret->list_icon = oscar_list_icon;
 	ret->away_states = oscar_away_states;
--- a/src/protocols/oscar/txqueue.c	Thu Mar 14 03:29:03 2002 +0000
+++ b/src/protocols/oscar/txqueue.c	Thu Mar 14 07:44:43 2002 +0000
@@ -230,15 +230,34 @@
 static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
 {
 	int wrote = 0;
-
 	if (!bs || !conn || (count < 0))
 		return -EINVAL;
 
 	if (count > aim_bstream_empty(bs))
 		count = aim_bstream_empty(bs); /* truncate to remaining space */
 
-	if (count)
-		wrote = aim_send(conn->fd, bs->data + bs->offset, count);
+	if (count) {
+		if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) && 
+		    (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
+			/* I strongly suspect that this is a horrible thing to do
+			 * and I feel really guilty doing it. */
+			char *sn = aim_directim_getsn(conn);
+			aim_rxcallback_t userfunc;
+			while (count - wrote > 1024) {
+				wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024);
+				if ((userfunc=aim_callhandler(conn->sessv, conn, 
+							      AIM_CB_FAM_SPECIAL, 
+							      AIM_CB_SPECIAL_IMAGETRANSFER)))
+				  userfunc(conn->sessv, NULL, sn, 
+					   count-wrote>1024 ? ((double)wrote / count) : 1);
+			}
+		}
+		if (count - wrote) {
+			wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
+		}
+		
+	}
+	
 
 	if (((aim_session_t *)conn->sessv)->debug >= 2) {
 		int i;
@@ -284,7 +303,6 @@
 
 	obslen = aim_bstream_curpos(&obs);
 	aim_bstream_rewind(&obs);
-
 	if (aim_bstream_send(&obs, fr->conn, obslen) != obslen)
 		err = -errno;
 	
@@ -302,9 +320,8 @@
 	fu8_t *hbs_raw;
 	int hbslen;
 	int err = 0;
-
+	
 	hbslen = 8 + fr->hdr.oft.hdr2len;
-
 	if (!(hbs_raw = malloc(hbslen)))
 		return -1;
 
@@ -317,6 +334,7 @@
 
 	aim_bstream_rewind(&hbs);
 	
+	
 	if (aim_bstream_send(&hbs, fr->conn, hbslen) != hbslen) {
 
 		err = -errno;
@@ -338,6 +356,8 @@
 
 
 	return err;
+
+
 }
 
 faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
--- a/src/prpl.h	Thu Mar 14 03:29:03 2002 +0000
+++ b/src/prpl.h	Thu Mar 14 07:44:43 2002 +0000
@@ -41,7 +41,7 @@
 #define PROTO_NAPSTER	9
 #define PROTO_ZEPHYR   10
 #define PROTO_GADUGADU 11
-/* DON'T TAKE AN UNASSIGNED NUMBER! Talk to Eric or Rob if you'd like
+/* DON'T TAKE AN UNASSIGNED NUMBER! Talk to Rob or Sean if you'd like
  * to create a new PRPL. */
 
 #define PRPL_DESC(x)	"Allows gaim to use the " x " protocol.\n\n" \
@@ -70,6 +70,8 @@
 #define OPT_PROTO_MAIL_CHECK      0x00000020
 /* Oscar and Jabber have buddy icons */
 #define OPT_PROTO_BUDDY_ICON      0x00000040
+/* Oscar lets you send images in direct IMs */
+#define OPT_PROTO_IM_IMAGE        0x00000080
 
 #define GAIM_AWAY_CUSTOM "Custom"
 
--- a/src/ui.h	Thu Mar 14 03:29:03 2002 +0000
+++ b/src/ui.h	Thu Mar 14 07:44:43 2002 +0000
@@ -122,6 +122,7 @@
 	GtkWidget *strike;
 	GtkWidget *font;
 	GtkWidget *smiley;
+	GtkWidget *imagebtn;
 	GtkWidget *fg_color_dialog;
 	GtkWidget *bg_color_dialog;
 	GtkWidget *font_dialog;