用GTK+编写邮件客户端

效果:

正确发送邮件,状态栏显示OK。

用GTK+编写邮件客户端

发送邮件错误,状态栏显示相应的状态信息。

用GTK+编写邮件客户端

 

代码:

Mail-Client: Mail-Client.c
	gcc Mail-Client.c -DDEBUG=0 -o Mail-Client `pkg-config --cflags --libs gtk+-2.0`	

 

/* Mail-Client: fork+exec+pipe+redirection */
#include <gtk/gtk.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#define MAX_RECV_BUF_LEN 1024
#if DEBUG
#define PDEBUG(fmt, args...) printf(fmt, ##args)
#else
#define PDEBUG(fmt, args...)
#endif

struct EntryPack
{
	GtkWidget *mToEntry;
	GtkWidget *mSubjectEntry;
	GtkTextBuffer *mMessageBuffer;
	GtkStatusbar *mStatusBar;
};

/**
 * func: send_email
 * @return : 0 for success; -1 for failure;
 * @errmsg: if -1 is returned, errmsg contains the corresponding error msg
 * 
 * this function makes use of fork, exec, dup, pipe to create the following structure
 * Mail-Client -- pipe_to_mail --> mail command
 * mail command -- pipe_from_mail --> Mail-Client
 **/
int send_email(const char *to, const char *subject, const char *message, char *errmsg)
{
	int pipe_to_mail[2], pipe_from_mail[2];
	int pid;
	int ret;

	assert(errmsg != NULL);
	ret = pipe(pipe_to_mail);
	assert(ret == 0);
	ret = pipe(pipe_from_mail);
	assert(ret == 0);

	pid = fork();
	if (pid < 0)
	{
		fprintf(stderr, "fork failed: code=%d (%s) \n", errno, strerror(errno));
		exit(EXIT_FAILURE);
	}
	else if (pid == 0)	/* in child process */
	{
		/* receive message from pipe_to_mail, execute mail command, return result to parent using pipe_from_mail */
		PDEBUG("In Child Process\n");
		PDEBUG("to: %s \n", to);
		PDEBUG("subject: %s\n", subject);
		PDEBUG("message: %s\n", message);
		/* redirect its standard input from pipe[0] */
		close(0);
		dup(pipe_to_mail[0]);
		close(pipe_to_mail[1]);
		close(pipe_to_mail[0]);
		/* redirect its standard output to pipe[1] */
		close(2);
		dup(pipe_from_mail[1]);
		close(pipe_from_mail[0]);
		close(pipe_from_mail[1]);

		execlp("mail", "mail", "-s", subject, to, NULL);
		exit(EXIT_FAILURE);
	}
	else			/* in parent process */
	{
		/* send message to child process */
		close(pipe_to_mail[0]);
		int msg_len = strlen(message);
		int count = 0;
		while (count < msg_len)
		{
			ret = write(pipe_to_mail[1], message, msg_len-count);
			if (ret < 0)
			{
				fprintf(stderr, "unexpected error in write: code=%d (%s)\n", errno, strerror(errno));
				exit(EXIT_FAILURE);
			}
			else
			{
				PDEBUG("write %d bytes to pipe\n", ret);
				count += ret;
				PDEBUG("msg_len = %d, count = %d \n", msg_len, count);
			}
		}
		close(pipe_to_mail[1]); /* write finished */
		/* receive result from its child process: mail */
		close(pipe_from_mail[1]); /* close the write end */
		char buf[MAX_RECV_BUF_LEN] = {0};
		ret = read(pipe_from_mail[0], buf, sizeof(buf));
		close(pipe_from_mail[0]);
		/* wait for child process to exit */
		int status;
		wait(&status);
		PDEBUG("child process exited with status: %d\n", status);
		/* if status != 0, print out the contents from mail */
		if (strlen(buf) != 0)
		{
//			fprintf(stderr, "error in sending mail: %s\n", buf);
			sprintf(errmsg, "Error! %s", buf);
			return -1;
		}
		else
		{
			return 0;
		}
	}
}

void closeApp(GtkWidget *window, gpointer data)
{
	gtk_main_quit();
}

void button_send_clicked(GtkWidget *button, gpointer data)
{
	GtkTextBuffer *buffer = ((struct EntryPack*)data) -> mMessageBuffer;
	GtkWidget *toEntry = ((struct EntryPack*)data) -> mToEntry;
	GtkWidget *subjectEntry = ((struct EntryPack*)data) -> mSubjectEntry;
	GtkStatusbar *statusbar = ((struct EntryPack*)data) -> mStatusBar;
	/* get message */
	GtkTextIter start, end;
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	char *msg;
	msg = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
	PDEBUG("%s\n", msg);
	/* get recipient's address */
	const char *to = gtk_entry_get_text(GTK_ENTRY(toEntry));
	/* get subject */
	const char *subject = gtk_entry_get_text(GTK_ENTRY(subjectEntry));
	/* send email */
	gtk_statusbar_push(statusbar, 0, "Sending Mail ...");
	char errmsg[256] = {0};
	int ret = send_email(to, subject, msg, errmsg);
	if (ret == 0)
	{
		/* update statusbar: success */
		PDEBUG("OK\n");
		gtk_statusbar_push(statusbar, 0, "OK");
	}
	else
	{
		/* update statusbar: failed */
		PDEBUG("Error! %s\n", errmsg);
		gtk_statusbar_push(statusbar, 0, errmsg);
	}
	free(msg);
}

void button_clear_clicked(GtkWidget *button, gpointer data)
{
	GtkTextBuffer *buffer = ((struct EntryPack*)data) -> mMessageBuffer;
	GtkWidget *toEntry = ((struct EntryPack*)data) -> mToEntry;
	GtkWidget *subjectEntry = ((struct EntryPack*)data) -> mSubjectEntry;
	GtkStatusbar *statusbar = ((struct EntryPack*)data) -> mStatusBar;
	GtkTextIter start, end;
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	gtk_text_buffer_delete(buffer, &start, &end);
	gtk_entry_set_text(GTK_ENTRY(toEntry), "");
	gtk_entry_set_text(GTK_ENTRY(subjectEntry), "");
	gtk_statusbar_push(statusbar, 0, "");
}

void button_quit_clicked(GtkWidget *button, gpointer data)
{
	gtk_main_quit();
}

int main(int argc, char *argv[])
{
	GtkWidget *window;
	GtkWidget *label_to, *label_subject, *label_message;
	GtkWidget *entry_to, *entry_subject;
	GtkTextBuffer *messageBuffer;
	GtkWidget *messageView;
	GtkWidget *button_send, *button_clear, *button_quit;
	GtkWidget *statusbar;
	GtkWidget *hbox1, *hbox2, *hbox3;
	GtkWidget *vbox;
	struct EntryPack pack;

	gtk_init(&argc, &argv);

	/* create main window */
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(window), "Mail Client");
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
	g_signal_connect(GTK_OBJECT(window), "destroy", 
			 GTK_SIGNAL_FUNC(closeApp), NULL);

	/* create label */
	label_to = gtk_label_new("To:");
	label_subject = gtk_label_new("Subject:");
	label_message = gtk_label_new("Message:");
	/* create entry */
	entry_to = gtk_entry_new();
	entry_subject = gtk_entry_new();
	/* create message view and message buffer */
	messageView = gtk_text_view_new();
	messageBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(messageView));
	/* pack entry */
	pack.mToEntry = entry_to;
	pack.mSubjectEntry = entry_subject;
	pack.mMessageBuffer = messageBuffer;
	/* create button */
	button_send = gtk_button_new_with_label("Send");
	button_clear = gtk_button_new_with_label("Clear");
	button_quit = gtk_button_new_with_label("Quit");
	/* associate button callbacks */
	g_signal_connect(GTK_OBJECT(button_send), "clicked",
			 GTK_SIGNAL_FUNC(button_send_clicked), &pack);
	g_signal_connect(GTK_OBJECT(button_clear), "clicked",
			 GTK_SIGNAL_FUNC(button_clear_clicked), &pack);
	g_signal_connect(GTK_OBJECT(button_quit), "clicked",
			 GTK_SIGNAL_FUNC(button_quit_clicked), NULL);
	/* create status bar */
	statusbar = gtk_statusbar_new();
	/* pack statusbar */
	pack.mStatusBar = GTK_STATUSBAR(statusbar);
	/* organize layout */
	hbox1 = gtk_hbox_new(FALSE, 5);
	hbox2 = gtk_hbox_new(FALSE, 5);
	hbox3 = gtk_hbox_new(FALSE, 5);
	vbox = gtk_vbox_new(FALSE, 10);

	gtk_box_pack_start(GTK_BOX(hbox1), label_to, FALSE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(hbox1), entry_to, TRUE, TRUE, 5);

	gtk_box_pack_start(GTK_BOX(hbox2), label_subject, FALSE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(hbox2), entry_subject, TRUE, TRUE, 5);

	gtk_box_pack_start(GTK_BOX(hbox3), button_send, TRUE, TRUE, 5);
	gtk_box_pack_start(GTK_BOX(hbox3), button_clear, TRUE, TRUE, 5);
	gtk_box_pack_start(GTK_BOX(hbox3), button_quit, TRUE, TRUE, 5);

	gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 1);
	gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 1);
//	gtk_box_pack_start(GTK_BOX(vbox), label_message, FALSE, FALSE, 1);
	gtk_box_pack_start(GTK_BOX(vbox), messageView, TRUE, TRUE, 1);
	gtk_box_pack_start(GTK_BOX(vbox), hbox3, FALSE, FALSE, 1);
	gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 1);

	gtk_container_add(GTK_CONTAINER(window), vbox);

	gtk_widget_show_all(window);
	gtk_main();

	return 0;

}

 

后记:

这是第一个版本,实现了邮件的正确发送。

目前版本中,使用了mail这个命令处理后台实际发送邮件的任务。以后要手动进行SMTP协议的处理。

 

参考资料:

gtk-demo (这个命令很nb,有示例,有源码)

gtk的devhelp(这个应用很强大,类似微软的MSDN)

 

 

你可能感兴趣的:(fork,pipe,gtk+,redirection,Mail_Client)