masqmail-0.2

annotate src/spool.c @ 3:8c55886cacd8

man pages will be maintained in troff now
author meillo@marmaro.de
date Fri, 26 Sep 2008 21:40:10 +0200
parents
children 26e34ae9a3e3
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@0 3
meillo@0 4 This program is free software; you can redistribute it and/or modify
meillo@0 5 it under the terms of the GNU General Public License as published by
meillo@0 6 the Free Software Foundation; either version 2 of the License, or
meillo@0 7 (at your option) any later version.
meillo@0 8
meillo@0 9 This program is distributed in the hope that it will be useful,
meillo@0 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 12 GNU General Public License for more details.
meillo@0 13
meillo@0 14 You should have received a copy of the GNU General Public License
meillo@0 15 along with this program; if not, write to the Free Software
meillo@0 16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 17 */
meillo@0 18
meillo@0 19 #include "masqmail.h"
meillo@0 20 #include <sys/stat.h>
meillo@0 21 #include "dotlock.h"
meillo@0 22
meillo@0 23 static
meillo@0 24 gint read_line(FILE *in, gchar *buf, gint buf_len)
meillo@0 25 {
meillo@0 26 gint p = 0;
meillo@0 27 gint c;
meillo@0 28
meillo@0 29 while((c = getc(in)) != '\n' && (c != EOF)){
meillo@0 30 if(p >= buf_len-1) { return 0; }
meillo@0 31 buf[p++] = c;
meillo@0 32 }
meillo@0 33
meillo@0 34 if(c == EOF){
meillo@0 35 return -1;
meillo@0 36 }
meillo@0 37 if((p > 0) && (buf[p-1] == '\r'))
meillo@0 38 p--;
meillo@0 39 buf[p++] = '\n';
meillo@0 40 buf[p] = 0;
meillo@0 41
meillo@0 42 return p;
meillo@0 43 }
meillo@0 44
meillo@0 45 static
meillo@0 46 void spool_write_rcpt(FILE *out, address *rcpt)
meillo@0 47 {
meillo@0 48 gchar dlvrd_char = addr_is_delivered(rcpt) ? 'X' : (addr_is_failed(rcpt) ? 'F' : ' ');
meillo@0 49
meillo@0 50 if(rcpt->local_part[0] != '|'){
meillo@0 51 /* this is a paranoid check, in case it slipped through: */
meillo@0 52 /* if this happens, it is a bug */
meillo@0 53 if(rcpt->domain == NULL){
meillo@0 54 logwrite(LOG_WARNING, "BUG: null domain for address %s, setting to %s\n",
meillo@0 55 rcpt->local_part, conf.host_name);
meillo@0 56 logwrite(LOG_WARNING, "please report this bug.\n");
meillo@0 57 rcpt->domain = g_strdup(conf.host_name);
meillo@0 58 }
meillo@0 59 fprintf(out, "RT:%c%s\n", dlvrd_char, addr_string(rcpt));
meillo@0 60 }else{
meillo@0 61 fprintf(out, "RT:%c%s\n", dlvrd_char, rcpt->local_part);
meillo@0 62 }
meillo@0 63 }
meillo@0 64
meillo@0 65 static
meillo@0 66 address *spool_scan_rcpt(gchar *line)
meillo@0 67 {
meillo@0 68 address *rcpt = NULL;
meillo@0 69
meillo@0 70 if(line[3] != 0){
meillo@0 71 if(line[4] != '|'){
meillo@0 72 rcpt = create_address(&(line[4]), TRUE);
meillo@0 73 }else{
meillo@0 74 rcpt = create_address_pipe(&(line[4]));
meillo@0 75 }
meillo@0 76 if(line[3] == 'X'){
meillo@0 77 addr_mark_delivered(rcpt);
meillo@0 78 }else if(line[3] == 'F'){
meillo@0 79 addr_mark_failed(rcpt);
meillo@0 80 }
meillo@0 81 }
meillo@0 82 return rcpt;
meillo@0 83 }
meillo@0 84
meillo@0 85 gboolean spool_read_data(message *msg)
meillo@0 86 {
meillo@0 87 FILE *in;
meillo@0 88 gboolean ok = FALSE;
meillo@0 89 gchar *spool_file;
meillo@0 90
meillo@0 91 DEBUG(5) debugf("spool_read_data entered\n");
meillo@0 92 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
meillo@0 93 DEBUG(5) debugf("reading data spool file '%s'\n", spool_file);
meillo@0 94 if((in = fopen(spool_file, "r"))){
meillo@0 95 char buf[MAX_DATALINE];
meillo@0 96 int len;
meillo@0 97
meillo@0 98 /* msg uid */
meillo@0 99 read_line(in, buf, MAX_DATALINE);
meillo@0 100
meillo@0 101 /* data */
meillo@0 102 msg->data_list = NULL;
meillo@0 103 while((len = read_line(in, buf, MAX_DATALINE)) > 0){
meillo@0 104 msg->data_list = g_list_prepend(msg->data_list, g_strdup(buf));
meillo@0 105 }
meillo@0 106 msg->data_list = g_list_reverse(msg->data_list);
meillo@0 107 fclose(in);
meillo@0 108 ok = TRUE;
meillo@0 109 }else
meillo@0 110 logwrite(LOG_ALERT, "could not open spool data file %s: %s\n",
meillo@0 111 spool_file, strerror(errno));
meillo@0 112 return ok;
meillo@0 113 }
meillo@0 114
meillo@0 115 gboolean spool_read_header(message *msg)
meillo@0 116 {
meillo@0 117 FILE *in;
meillo@0 118 gboolean ok = FALSE;
meillo@0 119 gchar *spool_file;
meillo@0 120
meillo@0 121 /* header spool: */
meillo@0 122 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
meillo@0 123 if((in = fopen(spool_file, "r"))){
meillo@0 124 header *hdr = NULL;
meillo@0 125 char buf[MAX_DATALINE];
meillo@0 126 int len;
meillo@0 127
meillo@0 128 /* msg uid */
meillo@0 129 read_line(in, buf, MAX_DATALINE);
meillo@0 130
meillo@0 131 /* envelope header */
meillo@0 132 while((len = read_line(in, buf, MAX_DATALINE)) > 0){
meillo@0 133 if(buf[0] == '\n')
meillo@0 134 break;
meillo@0 135 else if(strncasecmp(buf, "MF:", 3) == 0){
meillo@0 136 msg->return_path = create_address(&(buf[3]), TRUE);
meillo@0 137 DEBUG(3) debugf("spool_read: MAIL FROM: %s",
meillo@0 138 msg->return_path->address);
meillo@0 139 }else if(strncasecmp(buf, "RT:", 3) == 0){
meillo@0 140 address *addr;
meillo@0 141 addr = spool_scan_rcpt(buf);
meillo@0 142 if(!addr_is_delivered(addr) && !addr_is_failed(addr)){
meillo@0 143 msg->rcpt_list = g_list_append(msg->rcpt_list, addr);
meillo@0 144 }else{
meillo@0 145 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, addr);
meillo@0 146 }
meillo@0 147 }else if(strncasecmp(buf, "PR:", 3) == 0){
meillo@0 148 prot_id i;
meillo@0 149 for(i = 0; i < PROT_NUM; i++){
meillo@0 150 if(strncasecmp(prot_names[i], &(buf[3]),
meillo@0 151 strlen(prot_names[i])) == 0){
meillo@0 152 break;
meillo@0 153 }
meillo@0 154 }
meillo@0 155 msg->received_prot = i;
meillo@0 156 }else if(strncasecmp(buf, "RH:", 3) == 0){
meillo@0 157 g_strchomp(buf);
meillo@0 158 msg->received_host = g_strdup(&(buf[3]));
meillo@0 159 }else if(strncasecmp(buf, "ID:", 3) == 0){
meillo@0 160 g_strchomp(buf);
meillo@0 161 msg->ident = g_strdup(&(buf[3]));
meillo@0 162 }else if(strncasecmp(buf, "DS:", 3) == 0){
meillo@0 163 msg->data_size = atoi(&(buf[3]));
meillo@0 164 }else if(strncasecmp(buf, "TR:", 3) == 0){
meillo@0 165 msg->received_time = (time_t)(atoi(&(buf[3])));
meillo@0 166 }else if(strncasecmp(buf, "TW:", 3) == 0){
meillo@0 167 msg->warned_time = (time_t)(atoi(&(buf[3])));
meillo@0 168 }
meillo@0 169 /* so far ignore other tags */
meillo@0 170 }
meillo@0 171
meillo@0 172 /* mail headers */
meillo@0 173 while((len = read_line(in, buf, MAX_DATALINE)) > 0){
meillo@0 174 if(strncasecmp(buf, "HD:", 3) == 0){
meillo@0 175 hdr = get_header(&(buf[3]));
meillo@0 176 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
meillo@0 177 }else if((buf[0] == ' ' || buf[0] == '\t') && hdr){
meillo@0 178 char *tmp = hdr->header;
meillo@0 179 /* header continuation */
meillo@0 180 hdr->header = g_strconcat(hdr->header, buf, NULL);
meillo@0 181 hdr->value = hdr->header + (hdr->value - tmp);
meillo@0 182 }else
meillo@0 183 break;
meillo@0 184 }
meillo@0 185 fclose(in);
meillo@0 186 ok = TRUE;
meillo@0 187 }else
meillo@0 188 logwrite(LOG_ALERT, "could not open spool header file %s: %s\n",
meillo@0 189 spool_file, strerror(errno));
meillo@0 190 return ok;
meillo@0 191 }
meillo@0 192
meillo@0 193 message *msg_spool_read(gchar *uid, gboolean do_readdata)
meillo@0 194 {
meillo@0 195 message *msg;
meillo@0 196 gboolean ok = FALSE;
meillo@0 197
meillo@0 198 msg = create_message();
meillo@0 199 msg->uid = g_strdup(uid);
meillo@0 200
meillo@0 201 /* header spool: */
meillo@0 202 ok = spool_read_header(msg);
meillo@0 203 if(ok && do_readdata){
meillo@0 204 /* data spool: */
meillo@0 205 ok = spool_read_data(msg);
meillo@0 206 }
meillo@0 207 return msg;
meillo@0 208 }
meillo@0 209
meillo@0 210 /* write header. uid and gid should already be set to the
meillo@0 211 mail ids. Better call spool_write(msg, FALSE).
meillo@0 212 */
meillo@0 213 static
meillo@0 214 gboolean spool_write_header(message *msg)
meillo@0 215 {
meillo@0 216 GList *node;
meillo@0 217 gchar *spool_file, *tmp_file;
meillo@0 218 FILE *out;
meillo@0 219 gboolean ok = TRUE;
meillo@0 220
meillo@0 221 /* header spool: */
meillo@0 222 tmp_file = g_strdup_printf("%s/input/%d-H.tmp", conf.spool_dir, getpid());
meillo@0 223 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
meillo@0 224
meillo@0 225 if((out = fopen(tmp_file, "w"))){
meillo@0 226 DEBUG(6) debugf("opened tmp_file %s\n", tmp_file);
meillo@0 227
meillo@0 228 fprintf(out, "%s\n", msg->uid);
meillo@0 229 fprintf(out, "MF:%s\n", addr_string(msg->return_path));
meillo@0 230
meillo@0 231 DEBUG(6) debugf("after MF\n");
meillo@0 232 foreach(msg->rcpt_list, node){
meillo@0 233 address *rcpt = (address *)(node->data);
meillo@0 234 spool_write_rcpt(out, rcpt);
meillo@0 235 }
meillo@0 236 foreach(msg->non_rcpt_list, node){
meillo@0 237 address *rcpt = (address *)(node->data);
meillo@0 238 spool_write_rcpt(out, rcpt);
meillo@0 239 }
meillo@0 240 DEBUG(6) debugf("after RT\n");
meillo@0 241 fprintf(out, "PR:%s\n", prot_names[msg->received_prot]);
meillo@0 242 if(msg->received_host != NULL)
meillo@0 243 fprintf(out, "RH:%s\n", msg->received_host);
meillo@0 244
meillo@0 245 if(msg->ident != NULL)
meillo@0 246 fprintf(out, "ID:%s\n", msg->ident);
meillo@0 247
meillo@0 248 if(msg->data_size >= 0)
meillo@0 249 fprintf(out, "DS: %d\n", msg->data_size);
meillo@0 250
meillo@0 251 if(msg->received_time > 0)
meillo@0 252 fprintf(out, "TR: %u\n", (int)(msg->received_time));
meillo@0 253
meillo@0 254 if(msg->warned_time > 0)
meillo@0 255 fprintf(out, "TW: %u\n", (int)(msg->warned_time));
meillo@0 256
meillo@0 257 DEBUG(6) debugf("after RH\n");
meillo@0 258 fprintf(out, "\n");
meillo@0 259
meillo@0 260 foreach(msg->hdr_list, node){
meillo@0 261 header *hdr = (header *)(node->data);
meillo@0 262 fprintf(out, "HD:%s", hdr->header);
meillo@0 263 }
meillo@0 264 if(fflush(out) == EOF) ok = FALSE;
meillo@0 265 else if(fdatasync(fileno(out)) != 0){
meillo@0 266 if(errno != EINVAL) /* some fs do not support this..
meillo@0 267 I hope this also means that it is not necessary */
meillo@0 268 ok = FALSE;
meillo@0 269 }
meillo@0 270 fclose(out);
meillo@0 271 if(ok){
meillo@0 272 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
meillo@0 273 DEBUG(4) debugf("spool_file = %s\n", spool_file);
meillo@0 274 ok = (rename(tmp_file, spool_file) != -1);
meillo@0 275 g_free(spool_file);
meillo@0 276 }
meillo@0 277 }else{
meillo@0 278 logwrite(LOG_ALERT, "could not open temporary header spool file '%s': %s\n", tmp_file, strerror(errno));
meillo@0 279 DEBUG(1) debugf("euid = %d, egid = %d\n", geteuid(), getegid());
meillo@0 280 ok = FALSE;
meillo@0 281 }
meillo@0 282
meillo@0 283 g_free(tmp_file);
meillo@0 284
meillo@0 285 return ok;
meillo@0 286 }
meillo@0 287
meillo@0 288 gboolean spool_write(message *msg, gboolean do_write_data)
meillo@0 289 {
meillo@0 290 GList *list;
meillo@0 291 gchar *spool_file, *tmp_file;
meillo@0 292 FILE *out;
meillo@0 293 gboolean ok = TRUE;
meillo@0 294 uid_t saved_uid, saved_gid;
meillo@0 295 /* user can read/write, group can read, others cannot do anything: */
meillo@0 296 mode_t saved_mode = saved_mode = umask(026);
meillo@0 297
meillo@0 298 /* set uid and gid to the mail ids */
meillo@0 299 if(!conf.run_as_user){
meillo@0 300 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
meillo@0 301 }
meillo@0 302
meillo@0 303 /* header spool: */
meillo@0 304 ok = spool_write_header(msg);
meillo@0 305
meillo@0 306 if(ok){
meillo@0 307
meillo@0 308 if(do_write_data){
meillo@0 309 /* data spool: */
meillo@0 310 tmp_file = g_strdup_printf("%s/input/%d-D.tmp",
meillo@0 311 conf.spool_dir, getpid());
meillo@0 312 DEBUG(4) debugf("tmp_file = %s\n", tmp_file);
meillo@0 313
meillo@0 314 if((out = fopen(tmp_file, "w"))){
meillo@0 315 fprintf(out, "%s\n", msg->uid);
meillo@0 316 for(list = g_list_first(msg->data_list);
meillo@0 317 list != NULL;
meillo@0 318 list = g_list_next(list)){
meillo@0 319 fprintf(out, "%s", (gchar *)(list->data));
meillo@0 320 }
meillo@0 321
meillo@0 322 /* possibly paranoid ;-) */
meillo@0 323 if(fflush(out) == EOF) ok = FALSE;
meillo@0 324 else if(fdatasync(fileno(out)) != 0){
meillo@0 325 if(errno != EINVAL) /* some fs do not support this..
meillo@0 326 I hope this also means that it is not necessary */
meillo@0 327 ok = FALSE;
meillo@0 328 }
meillo@0 329 fclose(out);
meillo@0 330 if(ok){
meillo@0 331 spool_file = g_strdup_printf("%s/input/%s-D",
meillo@0 332 conf.spool_dir, msg->uid);
meillo@0 333 DEBUG(4) debugf("spool_file = %s\n", spool_file);
meillo@0 334 ok = (rename(tmp_file, spool_file) != -1);
meillo@0 335 g_free(spool_file);
meillo@0 336 }
meillo@0 337 }else{
meillo@0 338 logwrite(LOG_ALERT, "could not open temporary data spool file: %s\n",
meillo@0 339 strerror(errno));
meillo@0 340 ok = FALSE;
meillo@0 341 }
meillo@0 342 g_free(tmp_file);
meillo@0 343 }
meillo@0 344 }
meillo@0 345
meillo@0 346 /* set uid and gid back */
meillo@0 347 if(!conf.run_as_user){
meillo@0 348 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@0 349 }
meillo@0 350
meillo@0 351 umask(saved_mode);
meillo@0 352
meillo@0 353 return ok;
meillo@0 354 }
meillo@0 355
meillo@0 356 #define MAX_LOCKAGE 300
meillo@0 357
meillo@0 358 gboolean spool_lock(gchar *uid)
meillo@0 359 {
meillo@0 360 uid_t saved_uid, saved_gid;
meillo@0 361 gchar *hitch_name;
meillo@0 362 gchar *lock_name;
meillo@0 363 gboolean ok = FALSE;
meillo@0 364
meillo@0 365 hitch_name = g_strdup_printf("%s/%s-%d.lock", conf.lock_dir, uid, getpid());
meillo@0 366 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
meillo@0 367
meillo@0 368 /* set uid and gid to the mail ids */
meillo@0 369 if(!conf.run_as_user){
meillo@0 370 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
meillo@0 371 }
meillo@0 372
meillo@0 373 ok = dot_lock(lock_name, hitch_name);
meillo@0 374 if(!ok) logwrite(LOG_WARNING, "spool file %s is locked\n", uid);
meillo@0 375
meillo@0 376 /* set uid and gid back */
meillo@0 377 if(!conf.run_as_user){
meillo@0 378 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@0 379 }
meillo@0 380
meillo@0 381 g_free(lock_name);
meillo@0 382 g_free(hitch_name);
meillo@0 383
meillo@0 384 return ok;
meillo@0 385 }
meillo@0 386
meillo@0 387 gboolean spool_unlock(gchar *uid)
meillo@0 388 {
meillo@0 389 uid_t saved_uid, saved_gid;
meillo@0 390 gchar *lock_name;
meillo@0 391
meillo@0 392 /* set uid and gid to the mail ids */
meillo@0 393 if(!conf.run_as_user){
meillo@0 394 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
meillo@0 395 }
meillo@0 396
meillo@0 397 lock_name = g_strdup_printf("%s/%s.lock", conf.lock_dir, uid);
meillo@0 398 dot_unlock(lock_name);
meillo@0 399 g_free(lock_name);
meillo@0 400
meillo@0 401 /* set uid and gid back */
meillo@0 402 if(!conf.run_as_user){
meillo@0 403 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@0 404 }
meillo@0 405 return TRUE;
meillo@0 406 }
meillo@0 407
meillo@0 408 gboolean spool_delete_all(message *msg)
meillo@0 409 {
meillo@0 410 uid_t saved_uid, saved_gid;
meillo@0 411 gchar *spool_file;
meillo@0 412
meillo@0 413 /* set uid and gid to the mail ids */
meillo@0 414 if(!conf.run_as_user){
meillo@0 415 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
meillo@0 416 }
meillo@0 417
meillo@0 418 /* header spool: */
meillo@0 419 spool_file = g_strdup_printf("%s/input/%s-H", conf.spool_dir, msg->uid);
meillo@0 420 if(unlink(spool_file) != 0)
meillo@0 421 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n",
meillo@0 422 spool_file, strerror(errno));
meillo@0 423 g_free(spool_file);
meillo@0 424
meillo@0 425 /* data spool: */
meillo@0 426 spool_file = g_strdup_printf("%s/input/%s-D", conf.spool_dir, msg->uid);
meillo@0 427 if(unlink(spool_file) != 0)
meillo@0 428 logwrite(LOG_ALERT, "could not delete spool file %s: %s\n",
meillo@0 429 spool_file, strerror(errno));
meillo@0 430 g_free(spool_file);
meillo@0 431
meillo@0 432 /* set uid and gid back */
meillo@0 433 if(!conf.run_as_user){
meillo@0 434 set_euidgid(saved_uid, saved_gid, NULL, NULL);
meillo@0 435 }
meillo@0 436 return TRUE;
meillo@0 437 }