masqmail
diff src/spool.c @ 0:08114f7dcc23
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/spool.c Fri Sep 26 17:05:23 2008 +0200 1.3 @@ -0,0 +1,437 @@ 1.4 +/* MasqMail 1.5 + Copyright (C) 1999-2001 Oliver Kurth 1.6 + 1.7 + This program is free software; you can redistribute it and/or modify 1.8 + it under the terms of the GNU General Public License as published by 1.9 + the Free Software Foundation; either version 2 of the License, or 1.10 + (at your option) any later version. 1.11 + 1.12 + This program is distributed in the hope that it will be useful, 1.13 + but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.15 + GNU General Public License for more details. 1.16 + 1.17 + You should have received a copy of the GNU General Public License 1.18 + along with this program; if not, write to the Free Software 1.19 + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1.20 +*/ 1.21 + 1.22 +#include "masqmail.h" 1.23 +#include <sys/stat.h> 1.24 +#include "dotlock.h" 1.25 + 1.26 +static 1.27 +gint read_line(FILE *in, gchar *buf, gint buf_len) 1.28 +{ 1.29 + gint p = 0; 1.30 + gint c; 1.31 + 1.32 + while((c = getc(in)) != '\n' && (c != EOF)){ 1.33 + if(p >= buf_len-1) { return 0; } 1.34 + buf[p++] = c; 1.35 + } 1.36 + 1.37 + if(c == EOF){ 1.38 + return -1; 1.39 + } 1.40 + if((p > 0) && (buf[p-1] == '\r')) 1.41 + p--; 1.42 + buf[p++] = '\n'; 1.43 + buf[p] = 0; 1.44 + 1.45 + return p; 1.46 +} 1.47 + 1.48 +static 1.49 +void spool_write_rcpt(FILE *out, address *rcpt) 1.50 +{ 1.51 + gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' '); 1.52 + 1.53 + if(rcpt->local_part[0] != '|'){ 1.54 + /* this is a paranoid check, in case it slipped through: */ 1.55 + /* if this happens, it is a bug */ 1.56 + if(rcpt->domain == NULL){ 1.57 + logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n", 1.58 + rcpt->local_part, conf.host_name); 1.59 + logwrite(LOG_WARNING, "please report this bug.\n"); 1.60 + rcpt->domain = g_strdup(conf.host_name); 1.61 + } 1.62 + fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt)); 1.63 + }else{ 1.64 + fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part); 1.65 + } 1.66 +} 1.67 + 1.68 +static 1.69 +address *spool_scan_rcpt(gchar *line) 1.70 +{ 1.71 + address *rcpt = NULL; 1.72 + 1.73 + if(line[3] != 0){ 1.74 + if(line[4] != '|'){ 1.75 + rcpt = create_address(&(line[4]), TRUE); 1.76 + }else{ 1.77 + rcpt = create_address_pipe(&(line[4])); 1.78 + } 1.79 + if(line[3] == 'X'){ 1.80 + addr_mark_delivered(rcpt); 1.81 + }else if(line[3] == 'F'){ 1.82 + addr_mark_failed(rcpt); 1.83 + } 1.84 + } 1.85 + return rcpt; 1.86 +} 1.87 + 1.88 +gboolean spool_read_data(message *msg) 1.89 +{ 1.90 + FILE *in; 1.91 + gboolean ok = FALSE; 1.92 + gchar *spool_file; 1.93 + 1.94 + DEBUG(5) debugf("spool_read_data entered\n"); 1.95 + spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); 1.96 + DEBUG(5) debugf("reading data spool file '%s'\n", spool_file); 1.97 + if((in = fopen(spool_file, "r"))){ 1.98 + char buf[MAX_DATALINE]; 1.99 + int len; 1.100 + 1.101 + /* msg uid */ 1.102 + read_line(in, buf, MAX_DATALINE); 1.103 + 1.104 + /* data */ 1.105 + msg->data_list = NULL; 1.106 + while((len = read_line(in, buf, MAX_DATALINE)) > 0){ 1.107 + msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf)); 1.108 + } 1.109 + msg->data_list = g_list_reverse(msg->data_list); 1.110 + fclose(in); 1.111 + ok = TRUE; 1.112 + }else 1.113 + logwrite(LOG_ALERT, "could not open spool data file %s: %s\n", 1.114 + spool_file, strerror(errno)); 1.115 + return ok; 1.116 +} 1.117 + 1.118 +gboolean spool_read_header(message *msg) 1.119 +{ 1.120 + FILE *in; 1.121 + gboolean ok = FALSE; 1.122 + gchar *spool_file; 1.123 + 1.124 + /* header spool: */ 1.125 + spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); 1.126 + if((in = fopen(spool_file, "r"))){ 1.127 + header *hdr = NULL; 1.128 + char buf[MAX_DATALINE]; 1.129 + int len; 1.130 + 1.131 + /* msg uid */ 1.132 + read_line(in, buf, MAX_DATALINE); 1.133 + 1.134 + /* envelope header */ 1.135 + while((len = read_line(in, buf, MAX_DATALINE)) > 0){ 1.136 + if(buf[0] == '\n') 1.137 + break; 1.138 + else if(strncasecmp(buf, "MF:", 3) == 0){ 1.139 + msg->return_path = create_address(&(buf[3]), TRUE); 1.140 + DEBUG(3) debugf("spool_read: MAIL FROM: %s", 1.141 + msg->return_path->address); 1.142 + }else if(strncasecmp(buf, "RT:", 3) == 0){ 1.143 + address *addr; 1.144 + addr = spool_scan_rcpt(buf); 1.145 + if(!addr_is_delivered(addr) && !addr_is_failed(addr)){ 1.146 + msg->rcpt_list = g_list_append(msg->rcpt_list, addr); 1.147 + }else{ 1.148 + msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr); 1.149 + } 1.150 + }else if(strncasecmp(buf, "PR:", 3) == 0){ 1.151 + prot_id i; 1.152 + for(i = 0; i < PROT_NUM; i++){ 1.153 + if(strncasecmp(prot_names[i], &(buf[3]), 1.154 + strlen(prot_names[i])) == 0){ 1.155 + break; 1.156 + } 1.157 + } 1.158 + msg->received_prot = i; 1.159 + }else if(strncasecmp(buf, "RH:", 3) == 0){ 1.160 + g_strchomp(buf); 1.161 + msg->received_host = g_strdup(&(buf[3])); 1.162 + }else if(strncasecmp(buf, "ID:", 3) == 0){ 1.163 + g_strchomp(buf); 1.164 + msg->ident = g_strdup(&(buf[3])); 1.165 + }else if(strncasecmp(buf, "DS:", 3) == 0){ 1.166 + msg->data_size = atoi(&(buf[3])); 1.167 + }else if(strncasecmp(buf, "TR:", 3) == 0){ 1.168 + msg->received_time = (time_t)(atoi(&(buf[3]))); 1.169 + }else if(strncasecmp(buf, "TW:", 3) == 0){ 1.170 + msg->warned_time = (time_t)(atoi(&(buf[3]))); 1.171 + } 1.172 + /* so far ignore other tags */ 1.173 + } 1.174 + 1.175 + /* mail headers */ 1.176 + while((len = read_line(in, buf, MAX_DATALINE)) > 0){ 1.177 + if(strncasecmp(buf, "HD:", 3) == 0){ 1.178 + hdr = get_header(&(buf[3])); 1.179 + msg->hdr_list = g_list_append(msg->hdr_list, hdr); 1.180 + }else if((buf[0] == ' ' || buf[0] == '\t') && hdr){ 1.181 + char *tmp = hdr->header; 1.182 + /* header continuation */ 1.183 + hdr->header = g_strconcat(hdr->header, buf, NULL); 1.184 + hdr->value = hdr->header + (hdr->value - tmp); 1.185 + }else 1.186 + break; 1.187 + } 1.188 + fclose(in); 1.189 + ok = TRUE; 1.190 + }else 1.191 + logwrite(LOG_ALERT, "could not open spool header file %s: %s\n", 1.192 + spool_file, strerror(errno)); 1.193 + return ok; 1.194 +} 1.195 + 1.196 +message *msg_spool_read(gchar *uid, gboolean do_readdata) 1.197 +{ 1.198 + message *msg; 1.199 + gboolean ok = FALSE; 1.200 + 1.201 + msg = create_message(); 1.202 + msg->uid = g_strdup(uid); 1.203 + 1.204 + /* header spool: */ 1.205 + ok = spool_read_header(msg); 1.206 + if(ok && do_readdata){ 1.207 + /* data spool: */ 1.208 + ok = spool_read_data(msg); 1.209 + } 1.210 + return msg; 1.211 +} 1.212 + 1.213 +/* write header. uid and gid should already be set to the 1.214 + mail ids. Better call spool_write(msg, FALSE). 1.215 +*/ 1.216 +static 1.217 +gboolean spool_write_header(message *msg) 1.218 +{ 1.219 + GList *node; 1.220 + gchar *spool_file, *tmp_file; 1.221 + FILE *out; 1.222 + gboolean ok = TRUE; 1.223 + 1.224 + /* header spool: */ 1.225 + tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid()); 1.226 + DEBUG(4) debugf("tmp_file = %s\n", tmp_file); 1.227 + 1.228 + if((out = fopen(tmp_file, "w"))){ 1.229 + DEBUG(6) debugf("opened tmp_file %s\n", tmp_file); 1.230 + 1.231 + fprintf(out, "%s\n", msg->uid); 1.232 + fprintf(out, "MF:%s\n", addr_string(msg->return_path)); 1.233 + 1.234 + DEBUG(6) debugf("after MF\n"); 1.235 + foreach(msg->rcpt_list, node){ 1.236 + address *rcpt = (address *)(node->data); 1.237 + spool_write_rcpt(out, rcpt); 1.238 + } 1.239 + foreach(msg->non_rcpt_list, node){ 1.240 + address *rcpt = (address *)(node->data); 1.241 + spool_write_rcpt(out, rcpt); 1.242 + } 1.243 + DEBUG(6) debugf("after RT\n"); 1.244 + fprintf(out, "PR:%s\n", prot_names[msg->received_prot]); 1.245 + if(msg->received_host != NULL) 1.246 + fprintf(out, "RH:%s\n", msg->received_host); 1.247 + 1.248 + if(msg->ident != NULL) 1.249 + fprintf(out, "ID:%s\n", msg->ident); 1.250 + 1.251 + if(msg->data_size >= 0) 1.252 + fprintf(out, "DS: %d\n", msg->data_size); 1.253 + 1.254 + if(msg->received_time > 0) 1.255 + fprintf(out, "TR: %u\n", (int)(msg->received_time)); 1.256 + 1.257 + if(msg->warned_time > 0) 1.258 + fprintf(out, "TW: %u\n", (int)(msg->warned_time)); 1.259 + 1.260 + DEBUG(6) debugf("after RH\n"); 1.261 + fprintf(out, "\n"); 1.262 + 1.263 + foreach(msg->hdr_list, node){ 1.264 + header *hdr = (header *)(node->data); 1.265 + fprintf(out, "HD:%s", hdr->header); 1.266 + } 1.267 + if(fflush(out) == EOF) ok = FALSE; 1.268 + else if(fdatasync(fileno(out)) != 0){ 1.269 + if(errno != EINVAL) /* some fs do not support this.. 1.270 + I hope this also means that it is not necessary */ 1.271 + ok = FALSE; 1.272 + } 1.273 + fclose(out); 1.274 + if(ok){ 1.275 + spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); 1.276 + DEBUG(4) debugf("spool_file = %s\n", spool_file); 1.277 + ok = (rename(tmp_file, spool_file) != -1); 1.278 + g_free(spool_file); 1.279 + } 1.280 + }else{ 1.281 + logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno)); 1.282 + DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid()); 1.283 + ok = FALSE; 1.284 + } 1.285 + 1.286 + g_free(tmp_file); 1.287 + 1.288 + return ok; 1.289 +} 1.290 + 1.291 +gboolean spool_write(message *msg, gboolean do_write_data) 1.292 +{ 1.293 + GList *list; 1.294 + gchar *spool_file, *tmp_file; 1.295 + FILE *out; 1.296 + gboolean ok = TRUE; 1.297 + uid_t saved_uid, saved_gid; 1.298 + /* user can read/write, group can read, others cannot do anything: */ 1.299 + mode_t saved_mode = saved_mode = umask(026); 1.300 + 1.301 + /* set uid and gid to the mail ids */ 1.302 + if(!conf.run_as_user){ 1.303 + set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); 1.304 + } 1.305 + 1.306 + /* header spool: */ 1.307 + ok = spool_write_header(msg); 1.308 + 1.309 + if(ok){ 1.310 + 1.311 + if(do_write_data){ 1.312 + /* data spool: */ 1.313 + tmp_file = g_strdup_printf("%s/input/%d-D.tmp", 1.314 + conf.spool_dir, getpid()); 1.315 + DEBUG(4) debugf("tmp_file = %s\n", tmp_file); 1.316 + 1.317 + if((out = fopen(tmp_file, "w"))){ 1.318 + fprintf(out, "%s\n", msg->uid); 1.319 + for(list = g_list_first(msg->data_list); 1.320 + list != NULL; 1.321 + list = g_list_next(list)){ 1.322 + fprintf(out, "%s", (gchar *)(list->data)); 1.323 + } 1.324 + 1.325 + /* possibly paranoid ;-) */ 1.326 + if(fflush(out) == EOF) ok = FALSE; 1.327 + else if(fdatasync(fileno(out)) != 0){ 1.328 + if(errno != EINVAL) /* some fs do not support this.. 1.329 + I hope this also means that it is not necessary */ 1.330 + ok = FALSE; 1.331 + } 1.332 + fclose(out); 1.333 + if(ok){ 1.334 + spool_file = g_strdup_printf("%s/input/%s-D", 1.335 + conf.spool_dir, msg->uid); 1.336 + DEBUG(4) debugf("spool_file = %s\n", spool_file); 1.337 + ok = (rename(tmp_file, spool_file) != -1); 1.338 + g_free(spool_file); 1.339 + } 1.340 + }else{ 1.341 + logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n", 1.342 + strerror(errno)); 1.343 + ok = FALSE; 1.344 + } 1.345 + g_free(tmp_file); 1.346 + } 1.347 + } 1.348 + 1.349 + /* set uid and gid back */ 1.350 + if(!conf.run_as_user){ 1.351 + set_euidgid(saved_uid, saved_gid, NULL, NULL); 1.352 + } 1.353 + 1.354 + umask(saved_mode); 1.355 + 1.356 + return ok; 1.357 +} 1.358 + 1.359 +#define MAX_LOCKAGE 300 1.360 + 1.361 +gboolean spool_lock(gchar *uid) 1.362 +{ 1.363 + uid_t saved_uid, saved_gid; 1.364 + gchar *hitch_name; 1.365 + gchar *lock_name; 1.366 + gboolean ok = FALSE; 1.367 + 1.368 + hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid()); 1.369 + lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); 1.370 + 1.371 + /* set uid and gid to the mail ids */ 1.372 + if(!conf.run_as_user){ 1.373 + set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); 1.374 + } 1.375 + 1.376 + ok = dot_lock(lock_name, hitch_name); 1.377 + if(!ok) logwrite(LOG_WARNING, "spool file %s is locked\n", uid); 1.378 + 1.379 + /* set uid and gid back */ 1.380 + if(!conf.run_as_user){ 1.381 + set_euidgid(saved_uid, saved_gid, NULL, NULL); 1.382 + } 1.383 + 1.384 + g_free(lock_name); 1.385 + g_free(hitch_name); 1.386 + 1.387 + return ok; 1.388 +} 1.389 + 1.390 +gboolean spool_unlock(gchar *uid) 1.391 +{ 1.392 + uid_t saved_uid, saved_gid; 1.393 + gchar *lock_name; 1.394 + 1.395 + /* set uid and gid to the mail ids */ 1.396 + if(!conf.run_as_user){ 1.397 + set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); 1.398 + } 1.399 + 1.400 + lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid); 1.401 + dot_unlock(lock_name); 1.402 + g_free(lock_name); 1.403 + 1.404 + /* set uid and gid back */ 1.405 + if(!conf.run_as_user){ 1.406 + set_euidgid(saved_uid, saved_gid, NULL, NULL); 1.407 + } 1.408 + return TRUE; 1.409 +} 1.410 + 1.411 +gboolean spool_delete_all(message *msg) 1.412 +{ 1.413 + uid_t saved_uid, saved_gid; 1.414 + gchar *spool_file; 1.415 + 1.416 + /* set uid and gid to the mail ids */ 1.417 + if(!conf.run_as_user){ 1.418 + set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); 1.419 + } 1.420 + 1.421 + /* header spool: */ 1.422 + spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid); 1.423 + if(unlink(spool_file) != 0) 1.424 + logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", 1.425 + spool_file, strerror(errno)); 1.426 + g_free(spool_file); 1.427 + 1.428 + /* data spool: */ 1.429 + spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid); 1.430 + if(unlink(spool_file) != 0) 1.431 + logwrite(LOG_ALERT, "could not delete spool file %s: %s\n", 1.432 + spool_file, strerror(errno)); 1.433 + g_free(spool_file); 1.434 + 1.435 + /* set uid and gid back */ 1.436 + if(!conf.run_as_user){ 1.437 + set_euidgid(saved_uid, saved_gid, NULL, NULL); 1.438 + } 1.439 + return TRUE; 1.440 +}