/* compose.c */ #include #include #include #include #include #include "callbacks.h" #include "common.h" #include "compose.h" /* constants */ static struct _menu _menu_file[] = { { "_Close", G_CALLBACK(on_compose_file_close), GTK_STOCK_CLOSE }, { NULL, NULL, NULL } }; static struct _menu _menu_view[] = { /* FIXME CC and BCC should be toggle menu entries */ { "_CC field", G_CALLBACK(on_compose_view_cc), NULL }, { "_BCC field", G_CALLBACK(on_compose_view_bcc), NULL }, { NULL, NULL, NULL } }; static struct _menu _menu_help[] = { #if GTK_CHECK_VERSION(2, 6, 0) { "_About", G_CALLBACK(on_compose_help_about), GTK_STOCK_ABOUT }, #else { "_About", G_CALLBACK(on_compose_help_about), NULL }, #endif { NULL, NULL, NULL } }; static struct _menubar _compose_menubar[] = { { "_File", _menu_file }, { "_View", _menu_view }, { "_Help", _menu_help }, { NULL, NULL } }; /* compose_new */ Compose * compose_new(Mailer * mailer) { Compose * compose; GtkWidget * vbox; GtkWidget * toolbar; GtkToolItem * toolitem; GtkSizeGroup * group; GtkWidget * widget; PangoFontDescription * desc; if((compose = malloc(sizeof(*compose))) == NULL) { mailer_error(mailer, strerror(errno), 0); return NULL; } compose->mailer = mailer; compose->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(compose->window), "Mailer - Compose"); gtk_window_set_default_size(GTK_WINDOW(compose->window), 512, 384); g_signal_connect(G_OBJECT(compose->window), "delete_event", G_CALLBACK( on_compose_closex), compose); vbox = gtk_vbox_new(FALSE, 0); widget = common_new_menubar(_compose_menubar, compose); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0); /* toolbar */ toolbar = gtk_toolbar_new(); toolitem = gtk_tool_button_new(NULL, "Send"); g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK( on_compose_send), compose); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE); g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK( on_compose_save), compose); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0); /* from */ group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); toolbar = gtk_toolbar_new(); widget = gtk_label_new(" From: "); gtk_size_group_add_widget(group, widget); toolitem = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(toolitem), widget); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); compose->from = gtk_combo_box_entry_new_text(); toolitem = gtk_tool_item_new(); gtk_tool_item_set_expand(toolitem, TRUE); gtk_container_add(GTK_CONTAINER(toolitem), compose->from); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); /* to */ toolbar = gtk_toolbar_new(); widget = gtk_label_new(" To: "); gtk_size_group_add_widget(group, widget); toolitem = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(toolitem), widget); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); compose->to = gtk_entry_new(); toolitem = gtk_tool_item_new(); gtk_tool_item_set_expand(toolitem, TRUE); gtk_container_add(GTK_CONTAINER(toolitem), compose->to); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); /* cc */ compose->tb_cc = gtk_toolbar_new(); widget = gtk_label_new(" CC: "); gtk_size_group_add_widget(group, widget); toolitem = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(toolitem), widget); gtk_toolbar_insert(GTK_TOOLBAR(compose->tb_cc), toolitem, -1); compose->cc = gtk_entry_new(); toolitem = gtk_tool_item_new(); gtk_tool_item_set_expand(toolitem, TRUE); gtk_container_add(GTK_CONTAINER(toolitem), compose->cc); gtk_toolbar_insert(GTK_TOOLBAR(compose->tb_cc), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), compose->tb_cc, FALSE, FALSE, 0); /* bcc */ compose->tb_bcc = gtk_toolbar_new(); widget = gtk_label_new(" BCC: "); gtk_size_group_add_widget(group, widget); toolitem = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(toolitem), widget); gtk_toolbar_insert(GTK_TOOLBAR(compose->tb_bcc), toolitem, -1); compose->bcc = gtk_entry_new(); toolitem = gtk_tool_item_new(); gtk_tool_item_set_expand(toolitem, TRUE); gtk_container_add(GTK_CONTAINER(toolitem), compose->bcc); gtk_toolbar_insert(GTK_TOOLBAR(compose->tb_bcc), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), compose->tb_bcc, FALSE, FALSE, 0); /* subject */ toolbar = gtk_toolbar_new(); widget = gtk_label_new(" Subject: "); gtk_size_group_add_widget(group, widget); toolitem = gtk_tool_item_new(); gtk_container_add(GTK_CONTAINER(toolitem), widget); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); compose->subject = gtk_entry_new(); toolitem = gtk_tool_item_new(); gtk_tool_item_set_expand(toolitem, TRUE); gtk_container_add(GTK_CONTAINER(toolitem), compose->subject); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); compose->view = gtk_text_view_new(); desc = pango_font_description_new(); pango_font_description_set_family(desc, "monospace"); gtk_widget_modify_font(compose->view, desc); pango_font_description_free(desc); widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(widget), compose->view); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); compose->statusbar = gtk_statusbar_new(); compose->statusbar_id = 0; gtk_box_pack_start(GTK_BOX(vbox), compose->statusbar, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(compose->window), vbox); gtk_widget_show_all(compose->window); /* FIXME should not be showed in the first place */ gtk_widget_hide(compose->tb_cc); gtk_widget_hide(compose->tb_bcc); return compose; } /* compose_delete */ void compose_delete(Compose * compose) { gtk_widget_hide(compose->window); free(compose); } /* useful */ /* compose_save */ void compose_save(Compose * compose) { /* FIXME implement */ } /* compose_send */ static char * _send_headers(Compose * compose); static char * _send_body(GtkWidget * view); static int _send_mail(Compose * compose, char * msg, size_t msg_len); void compose_send(Compose * compose) { char * msg; size_t msg_len; char * body; size_t body_len; char * p; if((msg = _send_headers(compose)) == NULL) return; if((body = _send_body(compose->view)) == NULL) { free(msg); return; } msg_len = strlen(msg); body_len = strlen(body); if((p = realloc(msg, msg_len + body_len + 8)) == NULL) mailer_error(compose->mailer, strerror(errno), 0); else { msg = p; snprintf(&msg[msg_len], body_len + 8, "\r\n%s\r\n.\r\n", body); msg_len+=body_len+7; } g_free(body); _send_mail(compose, msg, msg_len); free(msg); } static int _mail_child(int fdin[2]); static int _send_mail(Compose * compose, char * msg, size_t msg_len) { int fdin[2]; pid_t pid; int status; int ret = 0; if(pipe(fdin) != 0 || (pid = fork()) == -1) return mailer_error(compose->mailer, strerror(errno), 1); if(pid == 0) return _mail_child(fdin); if(close(fdin[0]) != 0) mailer_error(compose->mailer, strerror(errno), 0); /* FIXME send mail progressively */ write(1, msg, msg_len); if(write(fdin[1], msg, msg_len) != msg_len) ret = mailer_error(compose->mailer, strerror(errno), 1); if(close(fdin[1]) != 0) mailer_error(compose->mailer, strerror(errno), 0); if(waitpid(pid, &status, 0) != pid) ret = mailer_error(compose->mailer, strerror(errno), 1); else if(WIFEXITED(status)) fprintf(stderr, "%s%s%d\n", "mailer: sendmail: ", "Exited with error ", WEXITSTATUS(status)); return ret; } static int _mail_child(int fdin[2]) { if(close(fdin[1]) != 0 || close(0) != 0 || dup2(fdin[0], 0) == -1) perror("mailer"); else { execl("/usr/sbin/sendmail", "sendmail", "-bm", "-t", NULL); perror("/usr/sbin/sendmail"); } exit(2); return 0; } static char * _send_headers(Compose * compose) { struct { char * hdr; GtkWidget * wgt; } widgets[] = { { "To: ", compose->to }, { "Cc: ", compose->cc }, { "Bcc: ", compose->bcc }, { "Subject: ", compose->subject }, { NULL, NULL } }; int i; char * msg = NULL; size_t msg_len = 0; char const * p; size_t len; size_t hdr_len; char * q; q = gtk_combo_box_get_active_text(GTK_COMBO_BOX(compose->from)); if(*q != '\0') { msg_len = strlen(q) + 8; if((msg = malloc(msg_len + 1)) == NULL) return NULL; snprintf(msg, msg_len+1, "%s%s\r\n", "From: ", q); } g_free(q); for(i = 0; widgets[i].hdr != NULL; i++) { p = gtk_entry_get_text(GTK_ENTRY(widgets[i].wgt)); if((len = strlen(p)) == 0) continue; hdr_len = strlen(widgets[i].hdr); if((q = realloc(msg, msg_len + hdr_len + len + 3)) == NULL) { free(msg); mailer_error(compose->mailer, strerror(errno), 0); return NULL; } msg = q; snprintf(&msg[msg_len], hdr_len + len + 3, "%s%s\r\n", widgets[i].hdr, p); msg_len+=hdr_len+len+2; } if(msg != NULL) return msg; if((msg = strdup("")) == NULL) mailer_error(compose->mailer, strerror(errno), 0); return msg; } static char * _send_body(GtkWidget * view) { GtkTextBuffer * tbuf; GtkTextIter start; GtkTextIter end; tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)); /* FIXME allocating the complete message is not optimal */ gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(tbuf), &start); gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(tbuf), &end); return gtk_text_buffer_get_text(GTK_TEXT_BUFFER(tbuf), &start, &end, FALSE); }