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@0: static meillo@0: gint read_line(FILE *in, gchar *buf, gint buf_len) meillo@0: { meillo@0: gint p = 0; meillo@0: gint c; meillo@0: meillo@0: while((c = getc(in)) != '\n' && (c != EOF)){ meillo@0: if(p >= buf_len-1) { return 0; } meillo@0: buf[p++] = c; meillo@0: } meillo@0: meillo@0: if(c == EOF){ meillo@0: return -1; meillo@0: } meillo@0: if((p > 0) && (buf[p-1] == '\r')) meillo@0: p--; meillo@0: buf[p++] = '\n'; meillo@0: buf[p] = 0; meillo@0: meillo@0: return p; meillo@0: } meillo@0: meillo@0: static meillo@0: void spool_write_rcpt(FILE *out, address *rcpt) meillo@0: { meillo@0: gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' '); meillo@0: meillo@0: if(rcpt->local_part[0] != '|'){ meillo@0: /* this is a paranoid check, in case it slipped through: */ meillo@0: /* if this happens, it is a bug */ meillo@0: if(rcpt->domain == NULL){ meillo@0: logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", meillo@0: rcpt->local_part, conf.host_name); meillo@0: logwrite(LOG_WARNING, "please report this bug.\n"); meillo@0: rcpt->domain = g_strdup(conf.host_name); meillo@0: } meillo@0: fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt)); meillo@0: }else{ meillo@0: fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part); meillo@0: } meillo@0: } meillo@0: meillo@0: static meillo@0: address *spool_scan_rcpt(gchar *line) meillo@0: { meillo@0: address *rcpt = NULL; meillo@0: meillo@0: if(line[3] != 0){ meillo@0: if(line[4] != '|'){ meillo@0: rcpt = create_address(&(line[4]), TRUE); meillo@0: }else{ meillo@0: rcpt = create_address_pipe(&(line[4])); meillo@0: } meillo@0: if(line[3] == 'X'){ meillo@0: addr_mark_delivered(rcpt); meillo@0: }else if(line[3] == 'F'){ meillo@0: addr_mark_failed(rcpt); meillo@0: } meillo@0: } meillo@0: return rcpt; meillo@0: } meillo@0: meillo@0: gboolean spool_read_data(message *msg) meillo@0: { meillo@0: FILE *in; meillo@0: gboolean ok = FALSE; meillo@0: gchar *spool_file; meillo@0: meillo@0: DEBUG(5) debugf("spool_read_data entered\n"); meillo@0: spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); meillo@0: DEBUG(5) debugf("reading data spool file '%s'\n", spool_file); meillo@0: if((in = fopen(spool_file, "r"))){ meillo@0: char buf[MAX_DATALINE]; meillo@0: int len; meillo@0: meillo@0: /* msg uid */ meillo@0: read_line(in, buf, MAX_DATALINE); meillo@0: meillo@0: /* data */ meillo@0: msg->data_list = NULL; meillo@0: while((len = read_line(in, buf, MAX_DATALINE)) > 0){ meillo@0: msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf)); meillo@0: } meillo@0: msg->data_list = g_list_reverse(msg->data_list); meillo@0: fclose(in); meillo@0: ok = TRUE; meillo@0: }else meillo@0: logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", meillo@0: spool_file, strerror(errno)); meillo@0: return ok; meillo@0: } meillo@0: meillo@0: gboolean spool_read_header(message *msg) meillo@0: { meillo@0: FILE *in; meillo@0: gboolean ok = FALSE; meillo@0: gchar *spool_file; meillo@0: meillo@0: /* header spool: */ meillo@0: spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); meillo@0: if((in = fopen(spool_file, "r"))){ meillo@0: header *hdr = NULL; meillo@0: char buf[MAX_DATALINE]; meillo@0: int len; meillo@0: meillo@0: /* msg uid */ meillo@0: read_line(in, buf, MAX_DATALINE); meillo@0: meillo@0: /* envelope header */ meillo@0: while((len = read_line(in, buf, MAX_DATALINE)) > 0){ meillo@0: if(buf[0] == '\n') meillo@0: break; meillo@0: else if(strncasecmp(buf, "MF:", 3) == 0){ meillo@0: msg->return_path = create_address(&(buf[3]), TRUE); meillo@0: DEBUG(3) debugf("spool_read: MAIL FROM: %s", meillo@0: msg->return_path->address); meillo@0: }else if(strncasecmp(buf, "RT:", 3) == 0){ meillo@0: address *addr; meillo@0: addr = spool_scan_rcpt(buf); meillo@0: if(!addr_is_delivered(addr) && !addr_is_failed(addr)){ meillo@0: msg->rcpt_list = g_list_append(msg->rcpt_list, addr); meillo@0: }else{ meillo@0: msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr); meillo@0: } meillo@0: }else if(strncasecmp(buf, "PR:", 3) == 0){ meillo@0: prot_id i; meillo@0: for(i = 0; i < PROT_NUM; i++){ meillo@0: if(strncasecmp(prot_names[i], &(buf[3]), meillo@0: strlen(prot_names[i])) == 0){ meillo@0: break; meillo@0: } meillo@0: } meillo@0: msg->received_prot = i; meillo@0: }else if(strncasecmp(buf, "RH:", 3) == 0){ meillo@0: g_strchomp(buf); meillo@0: msg->received_host = g_strdup(&(buf[3])); meillo@0: }else if(strncasecmp(buf, "ID:", 3) == 0){ meillo@0: g_strchomp(buf); meillo@0: msg->ident = g_strdup(&(buf[3])); meillo@0: }else if(strncasecmp(buf, "DS:", 3) == 0){ meillo@0: msg->data_size = atoi(&(buf[3])); meillo@0: }else if(strncasecmp(buf, "TR:", 3) == 0){ meillo@0: msg->received_time = (time_t)(atoi(&(buf[3]))); meillo@0: }else if(strncasecmp(buf, "TW:", 3) == 0){ meillo@0: msg->warned_time = (time_t)(atoi(&(buf[3]))); meillo@0: } meillo@0: /* so far ignore other tags */ meillo@0: } meillo@0: meillo@0: /* mail headers */ meillo@0: while((len = read_line(in, buf, MAX_DATALINE)) > 0){ meillo@0: if(strncasecmp(buf, "HD:", 3) == 0){ meillo@0: hdr = get_header(&(buf[3])); meillo@0: msg->hdr_list = g_list_append(msg->hdr_list, hdr); meillo@0: }else if((buf[0] == ' ' || buf[0] == '\t') && hdr){ meillo@0: char *tmp = hdr->header; meillo@0: /* header continuation */ meillo@0: hdr->header = g_strconcat(hdr->header, buf, NULL); meillo@0: hdr->value = hdr->header + (hdr->value - tmp); meillo@0: }else meillo@0: break; meillo@0: } meillo@0: fclose(in); meillo@0: ok = TRUE; meillo@0: }else meillo@0: logwrite(LOG_ALERT, "could not open spool header file %s: %s\n", meillo@0: spool_file, strerror(errno)); meillo@0: return ok; meillo@0: } meillo@0: meillo@0: message *msg_spool_read(gchar *uid, gboolean do_readdata) meillo@0: { meillo@0: message *msg; meillo@0: gboolean ok = FALSE; meillo@0: meillo@0: msg = create_message(); meillo@0: msg->uid = g_strdup(uid); meillo@0: meillo@0: /* header spool: */ meillo@0: ok = spool_read_header(msg); meillo@0: if(ok && do_readdata){ meillo@0: /* data spool: */ meillo@0: ok = spool_read_data(msg); meillo@0: } meillo@0: 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@0: static meillo@0: gboolean spool_write_header(message *msg) meillo@0: { meillo@0: GList *node; meillo@0: gchar *spool_file, *tmp_file; meillo@0: FILE *out; meillo@0: gboolean ok = TRUE; meillo@0: meillo@0: /* header spool: */ meillo@0: tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid()); meillo@0: DEBUG(4) debugf("tmp_file = %s\n", tmp_file); meillo@0: meillo@0: if((out = fopen(tmp_file, "w"))){ meillo@0: DEBUG(6) debugf("opened tmp_file %s\n", tmp_file); meillo@0: meillo@0: fprintf(out, "%s\n", msg->uid); meillo@0: fprintf(out, "MF:%s\n", addr_string(msg->return_path)); meillo@0: meillo@0: DEBUG(6) debugf("after MF\n"); meillo@0: foreach(msg->rcpt_list, node){ meillo@0: address *rcpt = (address *)(node->data); meillo@0: spool_write_rcpt(out, rcpt); meillo@0: } meillo@0: foreach(msg->non_rcpt_list, node){ meillo@0: address *rcpt = (address *)(node->data); meillo@0: spool_write_rcpt(out, rcpt); meillo@0: } meillo@0: DEBUG(6) debugf("after RT\n"); meillo@0: fprintf(out, "PR:%s\n", prot_names[msg->received_prot]); meillo@0: if(msg->received_host != NULL) meillo@0: fprintf(out, "RH:%s\n", msg->received_host); meillo@0: meillo@0: if(msg->ident != NULL) meillo@0: fprintf(out, "ID:%s\n", msg->ident); meillo@0: meillo@0: if(msg->data_size >= 0) meillo@0: fprintf(out, "DS: %d\n", msg->data_size); meillo@0: meillo@0: if(msg->received_time > 0) meillo@0: fprintf(out, "TR: %u\n", (int)(msg->received_time)); meillo@0: meillo@0: if(msg->warned_time > 0) meillo@0: fprintf(out, "TW: %u\n", (int)(msg->warned_time)); meillo@0: meillo@0: DEBUG(6) debugf("after RH\n"); meillo@0: fprintf(out, "\n"); meillo@0: meillo@0: foreach(msg->hdr_list, node){ meillo@0: header *hdr = (header *)(node->data); meillo@0: fprintf(out, "HD:%s", hdr->header); meillo@0: } meillo@0: if(fflush(out) == EOF) ok = FALSE; meillo@0: else if(fdatasync(fileno(out)) != 0){ meillo@0: if(errno != EINVAL) /* some fs do not support this.. meillo@0: I hope this also means that it is not necessary */ meillo@0: ok = FALSE; meillo@0: } meillo@0: fclose(out); meillo@0: if(ok){ meillo@0: spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); meillo@0: DEBUG(4) debugf("spool_file = %s\n", spool_file); meillo@0: ok = (rename(tmp_file, spool_file) != -1); meillo@0: g_free(spool_file); meillo@0: } meillo@0: }else{ meillo@0: logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno)); meillo@0: DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid()); meillo@0: ok = FALSE; meillo@0: } meillo@0: meillo@0: g_free(tmp_file); meillo@0: meillo@0: return ok; meillo@0: } meillo@0: meillo@0: gboolean spool_write(message *msg, gboolean do_write_data) meillo@0: { meillo@0: GList *list; meillo@0: gchar *spool_file, *tmp_file; meillo@0: FILE *out; meillo@0: gboolean ok = TRUE; meillo@0: uid_t saved_uid, saved_gid; meillo@0: /* user can read/write, group can read, others cannot do anything: */ meillo@0: mode_t saved_mode = saved_mode = umask(026); meillo@0: meillo@0: /* set uid and gid to the mail ids */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@0: } meillo@0: meillo@0: /* header spool: */ meillo@0: ok = spool_write_header(msg); meillo@0: meillo@0: if(ok){ meillo@0: meillo@0: if(do_write_data){ meillo@0: /* data spool: */ meillo@0: tmp_file = g_strdup_printf("%s/input/%d-D.tmp", meillo@0: conf.spool_dir, getpid()); meillo@0: DEBUG(4) debugf("tmp_file = %s\n", tmp_file); meillo@0: meillo@0: if((out = fopen(tmp_file, "w"))){ meillo@0: fprintf(out, "%s\n", msg->uid); meillo@0: for(list = g_list_first(msg->data_list); meillo@0: list != NULL; meillo@0: list = g_list_next(list)){ meillo@0: fprintf(out, "%s", (gchar *)(list->data)); meillo@0: } meillo@0: meillo@0: /* possibly paranoid ;-) */ meillo@0: if(fflush(out) == EOF) ok = FALSE; meillo@0: else if(fdatasync(fileno(out)) != 0){ meillo@0: if(errno != EINVAL) /* some fs do not support this.. meillo@0: I hope this also means that it is not necessary */ meillo@0: ok = FALSE; meillo@0: } meillo@0: fclose(out); meillo@0: if(ok){ meillo@0: spool_file = g_strdup_printf("%s/input/%s-D", meillo@0: conf.spool_dir, msg->uid); meillo@0: DEBUG(4) debugf("spool_file = %s\n", spool_file); meillo@0: ok = (rename(tmp_file, spool_file) != -1); meillo@0: g_free(spool_file); meillo@0: } meillo@0: }else{ meillo@0: logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n", meillo@0: strerror(errno)); meillo@0: ok = FALSE; meillo@0: } meillo@0: g_free(tmp_file); meillo@0: } meillo@0: } meillo@0: meillo@0: /* set uid and gid back */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@0: } meillo@0: meillo@0: umask(saved_mode); meillo@0: meillo@0: return ok; meillo@0: } meillo@0: meillo@0: #define MAX_LOCKAGE 300 meillo@0: meillo@0: gboolean spool_lock(gchar *uid) meillo@0: { meillo@0: uid_t saved_uid, saved_gid; meillo@0: gchar *hitch_name; meillo@0: gchar *lock_name; meillo@0: gboolean ok = FALSE; meillo@0: meillo@0: hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid()); meillo@0: lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); meillo@0: meillo@0: /* set uid and gid to the mail ids */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@0: } meillo@0: meillo@0: ok = dot_lock(lock_name, hitch_name); meillo@0: if(!ok) logwrite(LOG_WARNING, "spool file %s is locked\n", uid); meillo@0: meillo@0: /* set uid and gid back */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@0: } meillo@0: meillo@0: g_free(lock_name); meillo@0: g_free(hitch_name); meillo@0: meillo@0: return ok; meillo@0: } meillo@0: meillo@0: gboolean spool_unlock(gchar *uid) meillo@0: { meillo@0: uid_t saved_uid, saved_gid; meillo@0: gchar *lock_name; meillo@0: meillo@0: /* set uid and gid to the mail ids */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@0: } meillo@0: meillo@0: lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); meillo@0: dot_unlock(lock_name); meillo@0: g_free(lock_name); meillo@0: meillo@0: /* set uid and gid back */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@0: } meillo@0: return TRUE; meillo@0: } meillo@0: meillo@0: gboolean spool_delete_all(message *msg) meillo@0: { meillo@0: uid_t saved_uid, saved_gid; meillo@0: gchar *spool_file; meillo@0: meillo@0: /* set uid and gid to the mail ids */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); meillo@0: } meillo@0: meillo@0: /* header spool: */ meillo@0: spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); meillo@0: if(unlink(spool_file) != 0) meillo@0: logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", meillo@0: spool_file, strerror(errno)); meillo@0: g_free(spool_file); meillo@0: meillo@0: /* data spool: */ meillo@0: spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); meillo@0: if(unlink(spool_file) != 0) meillo@0: logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", meillo@0: spool_file, strerror(errno)); meillo@0: g_free(spool_file); meillo@0: meillo@0: /* set uid and gid back */ meillo@0: if(!conf.run_as_user){ meillo@0: set_euidgid(saved_uid, saved_gid, NULL, NULL); meillo@0: } meillo@0: return TRUE; meillo@0: }