meillo@0: /* MasqMail meillo@0: Copyright (C) 1999-2001 Oliver Kurth meillo@0: meillo@0: This program is free software; you can redistribute it and/or modify meillo@0: it under the terms of the GNU General Public License as published by meillo@0: the Free Software Foundation; either version 2 of the License, or meillo@0: (at your option) any later version. meillo@0: meillo@0: This program is distributed in the hope that it will be useful, meillo@0: but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@0: GNU General Public License for more details. meillo@0: meillo@0: You should have received a copy of the GNU General Public License meillo@0: along with this program; if not, write to the Free Software meillo@0: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. meillo@0: */ meillo@0: meillo@0: #include "masqmail.h" meillo@0: #include meillo@0: #include "dotlock.h" meillo@0: meillo@10: static gint meillo@10: read_line(FILE * in, gchar * buf, gint buf_len) meillo@0: { meillo@10: gint p = 0; meillo@10: gint c; meillo@0: meillo@10: while ((c = getc(in)) != '\n' && (c != EOF)) { meillo@10: if (p >= buf_len - 1) { meillo@10: return 0; meillo@10: } meillo@10: buf[p++] = c; meillo@10: } meillo@0: meillo@10: if (c == EOF) { meillo@10: return -1; meillo@10: } meillo@10: if ((p > 0) && (buf[p - 1] == '\r')) meillo@10: p--; meillo@10: buf[p++] = '\n'; meillo@10: buf[p] = 0; meillo@0: meillo@10: return p; meillo@0: } meillo@0: meillo@10: static void meillo@10: spool_write_rcpt(FILE * out, address * rcpt) meillo@0: { meillo@10: gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' '); meillo@0: meillo@10: if (rcpt->local_part[0] != '|') { meillo@10: /* this is a paranoid check, in case it slipped through: */ meillo@10: /* if this happens, it is a bug */ meillo@10: if (rcpt->domain == NULL) { meillo@10: logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", rcpt->local_part, conf.host_name); meillo@10: logwrite(LOG_WARNING, "please report this bug.\n"); meillo@10: rcpt->domain = g_strdup(conf.host_name); meillo@10: } meillo@10: fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt)); meillo@10: } else { meillo@10: fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part); meillo@10: } meillo@0: } meillo@0: meillo@10: static address* meillo@10: spool_scan_rcpt(gchar * line) meillo@0: { meillo@10: address *rcpt = NULL; meillo@0: meillo@10: if (line[3] != 0) { meillo@10: if (line[4] != '|') { meillo@10: rcpt = create_address(&(line[4]), TRUE); meillo@10: } else { meillo@10: rcpt = create_address_pipe(&(line[4])); meillo@10: } meillo@10: if (line[3] == 'X') { meillo@10: addr_mark_delivered(rcpt); meillo@10: } else if (line[3] == 'F') { meillo@10: addr_mark_failed(rcpt); meillo@10: } meillo@10: } meillo@10: return rcpt; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: spool_read_data(message * msg) meillo@0: { meillo@10: FILE *in; meillo@10: gboolean ok = FALSE; meillo@10: gchar *spool_file; meillo@0: meillo@10: DEBUG(5) debugf("spool_read_data entered\n"); meillo@10: spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); meillo@10: DEBUG(5) debugf("reading data spool file '%s'\n", spool_file); meillo@10: if ((in = fopen(spool_file, "r"))) { meillo@10: char buf[MAX_DATALINE]; meillo@10: int len; meillo@10: meillo@10: /* msg uid */ meillo@10: read_line(in, buf, MAX_DATALINE); meillo@10: meillo@10: /* data */ meillo@10: msg->data_list = NULL; meillo@10: while ((len = read_line(in, buf, MAX_DATALINE)) > 0) { meillo@10: msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf)); meillo@10: } meillo@10: msg->data_list = g_list_reverse(msg->data_list); meillo@10: fclose(in); meillo@10: ok = TRUE; meillo@10: } else meillo@10: logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", spool_file, strerror(errno)); meillo@10: return ok; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: spool_read_header(message * msg) meillo@0: { meillo@10: FILE *in; meillo@10: gboolean ok = FALSE; meillo@10: gchar *spool_file; meillo@0: meillo@10: /* header spool: */ meillo@10: spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); meillo@10: if ((in = fopen(spool_file, "r"))) { meillo@10: header *hdr = NULL; meillo@10: char buf[MAX_DATALINE]; meillo@10: int len; meillo@0: meillo@10: /* msg uid */ meillo@10: read_line(in, buf, MAX_DATALINE); meillo@10: meillo@10: /* envelope header */ meillo@10: while ((len = read_line(in, buf, MAX_DATALINE)) > 0) { meillo@10: if (buf[0] == '\n') meillo@10: break; meillo@10: else if (strncasecmp(buf, "MF:", 3) == 0) { meillo@10: msg->return_path = create_address(&(buf[3]), TRUE); meillo@10: DEBUG(3) debugf("spool_read: MAIL FROM: %s", msg->return_path->address); meillo@10: } else if (strncasecmp(buf, "RT:", 3) == 0) { meillo@10: address *addr; meillo@10: addr = spool_scan_rcpt(buf); meillo@10: if (!addr_is_delivered(addr) && !addr_is_failed(addr)) { meillo@10: msg->rcpt_list = g_list_append(msg->rcpt_list, addr); meillo@10: } else { meillo@10: msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr); meillo@10: } meillo@10: } else if (strncasecmp(buf, "PR:", 3) == 0) { meillo@10: prot_id i; meillo@10: for (i = 0; i < PROT_NUM; i++) { meillo@10: if (strncasecmp(prot_names[i], &(buf[3]), strlen(prot_names[i])) == 0) { meillo@10: break; meillo@10: } meillo@10: } meillo@10: msg->received_prot = i; meillo@10: } else if (strncasecmp(buf, "RH:", 3) == 0) { meillo@10: g_strchomp(buf); meillo@10: msg->received_host = g_strdup(&(buf[3])); meillo@10: } else if (strncasecmp(buf, "ID:", 3) == 0) { meillo@10: g_strchomp(buf); meillo@10: msg->ident = g_strdup(&(buf[3])); meillo@10: } else if (strncasecmp(buf, "DS:", 3) == 0) { meillo@10: msg->data_size = atoi(&(buf[3])); meillo@10: } else if (strncasecmp(buf, "TR:", 3) == 0) { meillo@10: msg->received_time = (time_t) (atoi(&(buf[3]))); meillo@10: } else if (strncasecmp(buf, "TW:", 3) == 0) { meillo@10: msg->warned_time = (time_t) (atoi(&(buf[3]))); meillo@10: } meillo@10: /* so far ignore other tags */ meillo@10: } meillo@10: meillo@10: /* mail headers */ meillo@10: while ((len = read_line(in, buf, MAX_DATALINE)) > 0) { meillo@10: if (strncasecmp(buf, "HD:", 3) == 0) { meillo@10: hdr = get_header(&(buf[3])); meillo@10: msg->hdr_list = g_list_append(msg->hdr_list, hdr); meillo@10: } else if ((buf[0] == ' ' || buf[0] == '\t') && hdr) { meillo@10: char *tmp = hdr->header; meillo@10: /* header continuation */ meillo@10: hdr->header = g_strconcat(hdr->header, buf, NULL); meillo@10: hdr->value = hdr->header + (hdr->value - tmp); meillo@10: } else meillo@10: break; meillo@10: } meillo@10: fclose(in); meillo@10: ok = TRUE; meillo@10: } else meillo@10: logwrite(LOG_ALERT, "could not open spool header file %s: %s\n", spool_file, strerror(errno)); meillo@10: return ok; meillo@0: } meillo@0: meillo@10: message* meillo@10: msg_spool_read(gchar * uid, gboolean do_readdata) meillo@0: { meillo@10: message *msg; meillo@10: gboolean ok = FALSE; meillo@0: meillo@10: msg = create_message(); meillo@10: msg->uid = g_strdup(uid); meillo@10: meillo@10: /* header spool: */ meillo@10: ok = spool_read_header(msg); meillo@10: if (ok && do_readdata) { meillo@10: /* data spool: */ meillo@10: ok = spool_read_data(msg); meillo@10: } meillo@10: return msg; meillo@0: } meillo@0: meillo@0: /* write header. uid and gid should already be set to the meillo@0: mail ids. Better call spool_write(msg, FALSE). meillo@0: */ meillo@10: static gboolean meillo@10: spool_write_header(message * msg) meillo@0: { meillo@10: GList *node; meillo@10: gchar *spool_file, *tmp_file; meillo@10: FILE *out; meillo@10: gboolean ok = TRUE; meillo@0: meillo@10: /* header spool: */ meillo@10: tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid()); meillo@10: DEBUG(4) debugf("tmp_file = %s\n", tmp_file); meillo@0: meillo@10: if ((out = fopen(tmp_file, "w"))) { meillo@10: DEBUG(6) debugf("opened tmp_file %s\n", tmp_file); meillo@0: meillo@10: fprintf(out, "%s\n", msg->uid); meillo@10: fprintf(out, "MF:%s\n", addr_string(msg->return_path)); meillo@0: meillo@10: DEBUG(6) debugf("after MF\n"); meillo@10: foreach(msg->rcpt_list, node) { meillo@10: address *rcpt = (address *) (node->data); meillo@10: spool_write_rcpt(out, rcpt); meillo@10: } meillo@10: foreach(msg->non_rcpt_list, node) { meillo@10: address *rcpt = (address *) (node->data); meillo@10: spool_write_rcpt(out, rcpt); meillo@10: } meillo@10: DEBUG(6) debugf("after RT\n"); meillo@10: fprintf(out, "PR:%s\n", prot_names[msg->received_prot]); meillo@10: if (msg->received_host != NULL) meillo@10: fprintf(out, "RH:%s\n", msg->received_host); meillo@0: meillo@10: if (msg->ident != NULL) meillo@10: fprintf(out, "ID:%s\n", msg->ident); meillo@0: meillo@10: if (msg->data_size >= 0) meillo@10: fprintf(out, "DS: %d\n", msg->data_size); meillo@0: meillo@10: if (msg->received_time > 0) meillo@10: fprintf(out, "TR: %u\n", (int) (msg->received_time)); meillo@0: meillo@10: if (msg->warned_time > 0) meillo@10: fprintf(out, "TW: %u\n", (int) (msg->warned_time)); meillo@0: meillo@10: DEBUG(6) debugf("after RH\n"); meillo@10: fprintf(out, "\n"); meillo@0: meillo@10: foreach(msg->hdr_list, node) { meillo@10: header *hdr = (header *) (node->data); meillo@10: fprintf(out, "HD:%s", hdr->header); meillo@10: } meillo@10: if (fflush(out) == EOF) meillo@10: ok = FALSE; meillo@10: else if (fdatasync(fileno(out)) != 0) { meillo@10: if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */ meillo@10: ok = FALSE; meillo@10: } meillo@10: fclose(out); meillo@10: if (ok) { meillo@10: spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); meillo@10: DEBUG(4) debugf("spool_file = %s\n", spool_file); meillo@10: ok = (rename(tmp_file, spool_file) != -1); meillo@10: g_free(spool_file); meillo@10: } meillo@10: } else { meillo@10: logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno)); meillo@10: DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid()); meillo@10: ok = FALSE; meillo@10: } meillo@0: meillo@10: g_free(tmp_file); meillo@0: meillo@10: return ok; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: spool_write(message * msg, gboolean do_write_data) meillo@0: { meillo@10: GList *list; meillo@10: gchar *spool_file, *tmp_file; meillo@10: FILE *out; meillo@10: gboolean ok = TRUE; meillo@10: uid_t saved_uid, saved_gid; meillo@10: /* user can read/write, group can read, others cannot do anything: */ meillo@10: mode_t saved_mode = saved_mode = umask(026); meillo@0: meillo@10: /* set uid and gid to the mail ids */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@0: } meillo@0: meillo@10: /* header spool: */ meillo@10: ok = spool_write_header(msg); meillo@10: meillo@10: if (ok) { meillo@10: meillo@10: if (do_write_data) { meillo@10: /* data spool: */ meillo@10: tmp_file = g_strdup_printf("%s/input/%d-D.tmp", conf.spool_dir, getpid()); meillo@10: DEBUG(4) debugf("tmp_file = %s\n", tmp_file); meillo@10: meillo@10: if ((out = fopen(tmp_file, "w"))) { meillo@10: fprintf(out, "%s\n", msg->uid); meillo@10: for (list = g_list_first(msg->data_list); list != NULL; list = g_list_next(list)) { meillo@10: fprintf(out, "%s", (gchar *) (list->data)); meillo@10: } meillo@10: meillo@10: /* possibly paranoid ;-) */ meillo@10: if (fflush(out) == EOF) meillo@10: ok = FALSE; meillo@10: else if (fdatasync(fileno(out)) != 0) { meillo@10: if (errno != EINVAL) /* some fs do not support this.. I hope this also means that it is not necessary */ meillo@10: ok = FALSE; meillo@10: } meillo@10: fclose(out); meillo@10: if (ok) { meillo@10: spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); meillo@10: DEBUG(4) debugf("spool_file = %s\n", spool_file); meillo@10: ok = (rename(tmp_file, spool_file) != -1); meillo@10: g_free(spool_file); meillo@10: } meillo@10: } else { meillo@10: logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n", strerror(errno)); meillo@10: ok = FALSE; meillo@10: } meillo@10: g_free(tmp_file); meillo@10: } meillo@0: } meillo@10: meillo@10: /* set uid and gid back */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@0: } meillo@0: meillo@10: umask(saved_mode); meillo@0: meillo@10: return ok; meillo@0: } meillo@0: meillo@0: #define MAX_LOCKAGE 300 meillo@0: meillo@10: gboolean meillo@10: spool_lock(gchar * uid) meillo@0: { meillo@10: uid_t saved_uid, saved_gid; meillo@10: gchar *hitch_name; meillo@10: gchar *lock_name; meillo@10: gboolean ok = FALSE; meillo@0: meillo@10: hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid()); meillo@10: lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); meillo@0: meillo@10: /* set uid and gid to the mail ids */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@10: } meillo@0: meillo@10: ok = dot_lock(lock_name, hitch_name); meillo@10: if (!ok) meillo@10: logwrite(LOG_WARNING, "spool file %s is locked\n", uid); meillo@0: meillo@10: /* set uid and gid back */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@10: } meillo@0: meillo@10: g_free(lock_name); meillo@10: g_free(hitch_name); meillo@0: meillo@10: return ok; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: spool_unlock(gchar * uid) meillo@0: { meillo@10: uid_t saved_uid, saved_gid; meillo@10: gchar *lock_name; meillo@0: meillo@10: /* set uid and gid to the mail ids */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@10: } meillo@0: meillo@10: lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); meillo@10: dot_unlock(lock_name); meillo@10: g_free(lock_name); meillo@0: meillo@10: /* set uid and gid back */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@10: } meillo@10: return TRUE; meillo@0: } meillo@0: meillo@10: gboolean meillo@10: spool_delete_all(message * msg) meillo@0: { meillo@10: uid_t saved_uid, saved_gid; meillo@10: gchar *spool_file; meillo@0: meillo@10: /* set uid and gid to the mail ids */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@10: } meillo@0: meillo@10: /* header spool: */ meillo@10: spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); meillo@10: if (unlink(spool_file) != 0) meillo@10: logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno)); meillo@10: g_free(spool_file); meillo@0: meillo@10: /* data spool: */ meillo@10: spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); meillo@10: if (unlink(spool_file) != 0) meillo@10: logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", spool_file, strerror(errno)); meillo@10: g_free(spool_file); meillo@0: meillo@10: /* set uid and gid back */ meillo@10: if (!conf.run_as_user) { meillo@10: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@10: } meillo@10: return TRUE; meillo@0: }