效果:
正确发送邮件,状态栏显示OK。
发送邮件错误,状态栏显示相应的状态信息。
代码:
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)