Lines 64-91
Link Here
|
64 |
#include <mbox_open.h> |
64 |
#include <mbox_open.h> |
65 |
#include <dsn_util.h> |
65 |
#include <dsn_util.h> |
66 |
|
66 |
|
|
|
67 |
/* Patch library. */ |
68 |
|
69 |
#include <sys/types.h> /* opendir(3), stat(2) */ |
70 |
#include <sys/stat.h> /* stat(2) */ |
71 |
#include <dirent.h> /* opendir(3) */ |
72 |
#include <unistd.h> /* stat(2) */ |
73 |
#include <stdlib.h> /* atol(3) */ |
74 |
#include <string.h> /* strrchr(3) */ |
75 |
#include <vstring_vstream.h> |
76 |
#include <dict.h> |
77 |
#include <dict_regexp.h> |
78 |
#include <ctype.h> |
79 |
#include <stdio.h> |
80 |
#include <sys_defs.h> |
81 |
#include <mail_addr_find.h> |
82 |
|
67 |
/* Application-specific. */ |
83 |
/* Application-specific. */ |
68 |
|
84 |
|
69 |
#include "virtual.h" |
85 |
#include "virtual.h" |
70 |
|
86 |
|
71 |
/* deliver_maildir - delivery to maildir-style mailbox */ |
87 |
/* Maildirsize maximal size. */ |
|
|
88 |
|
89 |
#define SIZEFILE_MAX 5120 |
90 |
|
91 |
/* |
92 |
* Chris Stratford <chriss@pipex.net> |
93 |
* Read the maildirsize file to get quota info. |
94 |
* |
95 |
* Arguments: |
96 |
* dirname: the maildir |
97 |
* countptr: number of messages |
98 |
* |
99 |
* Returns the size of all mails as read from maildirsize, |
100 |
* zero if it couldn't read the file. |
101 |
*/ |
102 |
static long read_maildirsize(char *filename, long *sumptr, long *countptr) |
103 |
{ |
104 |
char *myname = "read_maildirsize"; |
105 |
struct stat statbuf; |
106 |
VSTREAM *sizefile; |
107 |
char *p; |
108 |
int len, first; |
109 |
long sum = 0, count = 0, ret_value = -1; |
110 |
|
111 |
if (msg_verbose) |
112 |
msg_info("%s: we will use sizefile = '%s'", myname, filename); |
113 |
|
114 |
sizefile = vstream_fopen(filename, O_RDONLY, 0); |
115 |
if (!sizefile) { |
116 |
if (msg_verbose) |
117 |
msg_info("%s: cannot open %s: %m (maybe file does not exist)", myname, filename); |
118 |
|
119 |
return -1; |
120 |
} else if (stat(filename, &statbuf) < 0 || statbuf.st_size > SIZEFILE_MAX) { |
121 |
if (sizefile) { |
122 |
vstream_fclose(sizefile); |
123 |
unlink(filename); |
124 |
} |
125 |
|
126 |
if (msg_verbose) |
127 |
msg_info("%s: stat() returned < 0 or filesize > SIZEFILE_MAX (filename = %s, filesize = %ld)", myname, filename, statbuf.st_size); |
128 |
|
129 |
return -1; |
130 |
} |
131 |
|
132 |
VSTRING *sizebuf = vstring_alloc(SIZEFILE_MAX); |
133 |
len = vstream_fread(sizefile, STR(sizebuf), SIZEFILE_MAX); |
134 |
|
135 |
p = STR(sizebuf); |
136 |
*(p + len) = '\0'; |
137 |
first = 1; |
138 |
|
139 |
while (*p) { |
140 |
long n = 0, c = 0; |
141 |
char *q = p; |
142 |
|
143 |
while (*p) { |
144 |
if (*p++ == '\n') { |
145 |
p[-1] = 0; |
146 |
break; |
147 |
} |
148 |
} |
149 |
|
150 |
if (first) { |
151 |
first = 0; |
152 |
continue; |
153 |
} |
154 |
|
155 |
if (sscanf(q, "%ld %ld", &n, &c) == 2) { |
156 |
sum += n; |
157 |
count += c; |
158 |
/* if (msg_verbose) |
159 |
msg_info("%s: we read line '%s', totals: sum = %ld, count = %ld", myname, q, sum, count); */ |
160 |
} |
161 |
else { |
162 |
vstream_fclose(sizefile); |
163 |
unlink(filename); |
164 |
msg_warn("%s: invalid line '%s' found in %s, removing maildirsize file", myname, q, filename); |
165 |
vstring_free(sizebuf); |
166 |
|
167 |
return -1; |
168 |
} |
169 |
} |
170 |
|
171 |
*countptr = count; |
172 |
*sumptr = sum; |
173 |
|
174 |
if (sum < 0 || count < 0 || (sum == 0 && count != 0) || (sum != 0 && count == 0)) { |
175 |
if (msg_verbose) { |
176 |
msg_info("%s: we will return -1 and unlink %s, because file count or sum is <= 0 (sum = %ld, count = %ld)", myname, filename, sum, count); |
177 |
} |
178 |
|
179 |
unlink(filename); |
180 |
ret_value = -1; |
181 |
} else { |
182 |
if (msg_verbose) |
183 |
msg_info("%s: we will return Maildir size = %ld, count = %ld", myname, *sumptr, *countptr); |
184 |
|
185 |
ret_value = sum; |
186 |
} |
187 |
|
188 |
vstream_fclose(sizefile); |
189 |
vstring_free(sizebuf); |
190 |
|
191 |
return ret_value; |
192 |
} |
193 |
|
194 |
/* |
195 |
* Gives the size of the file according to the Maildir++ extension |
196 |
* present in the filename (code taken from courier-imap). |
197 |
* |
198 |
* Arguments: |
199 |
* n: filename |
200 |
* |
201 |
* Returns the size given in ",S=<size>" in the filename, |
202 |
* zero if it cannot find ",S=<size>" in the filename. |
203 |
*/ |
204 |
static long maildir_parsequota(const char *n) |
205 |
{ |
206 |
const char *o; |
207 |
int yes = 0; |
208 |
|
209 |
if ((o = strrchr(n, '/')) == 0) |
210 |
o = n; |
211 |
|
212 |
for (; *o; o++) { |
213 |
if (*o == ':') |
214 |
break; |
215 |
} |
216 |
|
217 |
for (; o >= n; --o) { |
218 |
if (*o == '/') |
219 |
break; |
220 |
|
221 |
if (*o == ',' && o[1] == 'S' && o[2] == '=') { |
222 |
yes = 1; |
223 |
o += 3; |
224 |
break; |
225 |
} |
226 |
} |
227 |
|
228 |
if (yes) { |
229 |
long s = 0; |
230 |
|
231 |
while (*o >= '0' && *o <= '9') |
232 |
s = s*10 + (*o++ - '0'); |
233 |
|
234 |
return s; |
235 |
} |
236 |
|
237 |
return 0; |
238 |
} |
239 |
|
240 |
/* |
241 |
* Computes quota usage for a directory (taken from exim). |
242 |
* |
243 |
* This function is called to determine the exact quota usage of a virtual |
244 |
* maildir box. To achieve maximum possible speed while doing this, it takes |
245 |
* advantage of the maildirsize file and the Maildir++ extensions to filenames, |
246 |
* when applicable and configured to be used. In all other cases it simply |
247 |
* stats all the files as needed to get the size information. |
248 |
* |
249 |
* Arguments: |
250 |
* dirname: the name of the directory |
251 |
* countptr: where to add the file count (because this function recurses) |
252 |
* |
253 |
* Returns the sum of the sizes of all measurable files, |
254 |
* zero if the directory could not be opened. |
255 |
*/ |
256 |
static long check_dir_size(char *dirname, long *countptr) |
257 |
{ |
258 |
char *myname = "check_dir_size"; |
259 |
DIR *dir; |
260 |
long sum = 0; |
261 |
struct dirent *ent; |
262 |
struct stat statbuf; |
263 |
|
264 |
dir = opendir(dirname); |
265 |
if (dir == NULL) { |
266 |
if (make_dirs(dirname, 0700) == 0) { /* Try to create the dirs. */ |
267 |
dir = opendir(dirname); /* Reopen the dir. */ |
268 |
if (dir == NULL) { |
269 |
msg_warn("%s: cannot reopen directory: %s", myname, dirname); |
270 |
return 0; |
271 |
} |
272 |
} |
273 |
else { |
274 |
msg_warn("%s: cannot open directory: %s", myname, dirname); |
275 |
return 0; |
276 |
} |
277 |
} |
278 |
|
279 |
while ((ent = readdir(dir)) != NULL) { |
280 |
char *name = ent->d_name; |
281 |
long tmpsum = 0; |
282 |
VSTRING *buffer; |
283 |
|
284 |
/* do not count dot a double-dot dirs */ |
285 |
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) |
286 |
continue; |
287 |
/* do not count if this is the trash subdir and if we should NOT count it */ |
288 |
else if (var_virt_trash_count == 0 && strcmp(name, var_virt_trash_name) == 0) |
289 |
continue; |
290 |
|
291 |
/* |
292 |
* Here comes the real logic behind this function. |
293 |
* Optimized to be the most efficient possible, |
294 |
* depending on the settings given. |
295 |
* See above for a more detailed description. |
296 |
*/ |
297 |
if (var_virt_mailbox_limit_inbox) { |
298 |
if (var_virt_maildir_extended && (tmpsum = maildir_parsequota(name))) { |
299 |
sum += tmpsum; |
300 |
(*countptr)++; |
301 |
} |
302 |
else { |
303 |
buffer = vstring_alloc(1024); |
304 |
vstring_sprintf(buffer, "%s/%s", dirname, name); |
305 |
|
306 |
if (stat(STR(buffer), &statbuf) < 0) { |
307 |
vstring_free(buffer); |
308 |
continue; |
309 |
} |
310 |
if ((statbuf.st_mode & S_IFREG) != 0) { |
311 |
sum += (long) statbuf.st_size; |
312 |
(*countptr)++; |
313 |
} |
314 |
|
315 |
vstring_free(buffer); |
316 |
} |
317 |
} |
318 |
else { |
319 |
buffer = vstring_alloc(1024); |
320 |
vstring_sprintf(buffer, "%s/%s", dirname, name); |
321 |
|
322 |
if (stat(STR(buffer), &statbuf) < 0) { |
323 |
vstring_free(buffer); |
324 |
continue; |
325 |
} |
326 |
if ((statbuf.st_mode & S_IFREG) != 0) { |
327 |
if (strcmp(dirname + strlen(dirname) - 3, "new") == 0 || strcmp(dirname + strlen(dirname) - 3, "cur") == 0 || strcmp(dirname + strlen(dirname) - 3, "tmp") == 0) { |
328 |
sum += (long) statbuf.st_size; |
329 |
(*countptr)++; |
330 |
} |
331 |
} |
332 |
else if ((statbuf.st_mode & S_IFDIR) != 0) { |
333 |
sum += check_dir_size(STR(buffer), countptr); |
334 |
} |
335 |
|
336 |
vstring_free(buffer); |
337 |
} |
338 |
} |
339 |
closedir(dir); |
72 |
|
340 |
|
73 |
int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr) |
341 |
if (msg_verbose) |
|
|
342 |
msg_info("%s: full scan done: dir=%s sum=%ld count=%ld", myname, dirname, sum, *countptr); |
343 |
|
344 |
return sum; |
345 |
} |
346 |
|
347 |
/* Cut all occurrences of pattern from string. */ |
348 |
static char *strcut(char *str, const char *pat) |
349 |
{ |
350 |
char *ptr, *loc, *ret; |
351 |
ret = str; |
352 |
loc = str; |
353 |
|
354 |
/* No match, return original string. */ |
355 |
if (!strstr(loc, pat)) |
356 |
return(str); |
357 |
|
358 |
while (*loc && (ptr = strstr(loc, pat))) { |
359 |
while (loc < ptr) |
360 |
*str++ = *loc++; |
361 |
loc += strlen(pat); |
362 |
} |
363 |
|
364 |
while (*loc) |
365 |
*str++ = *loc++; |
366 |
|
367 |
*str = 0; |
368 |
|
369 |
return(ret); |
370 |
} |
371 |
|
372 |
/* Check if maildirfilter file is up-to-date compared to SQL, (re)write it if not. */ |
373 |
static long sql2file(char *filename, char *user) |
374 |
{ |
375 |
char *myname = "sql2file"; |
376 |
char *filter_sqlres; |
377 |
char filter_fileres[128]; |
378 |
long sqlmtime = 0, filemtime = 0, retval = 0; |
379 |
int filterfile, size_sqlres, i; |
380 |
struct stat statbuf; |
381 |
|
382 |
if (*var_virt_maildir_filter_maps != 0) { |
383 |
filter_sqlres = (char *) mymalloc(16000); |
384 |
filter_sqlres = (char *) mail_addr_find(virtual_maildir_filter_maps, user, (char **) 0); |
385 |
|
386 |
if (filter_sqlres) { |
387 |
strcut(filter_sqlres, "\r"); |
388 |
if (filter_sqlres[0] == '#' && filter_sqlres[1] == ' ' && filter_sqlres[2] == 'M') { |
389 |
size_sqlres = strlen(filter_sqlres); |
390 |
|
391 |
for (i = 4; i <= size_sqlres; i++) { |
392 |
if(filter_sqlres[i] == '/' && filter_sqlres[i+1] == '^') { |
393 |
filter_sqlres[i-1] = '\n'; |
394 |
} |
395 |
} |
396 |
|
397 |
filter_sqlres[(size_sqlres+1)] = '\0'; |
398 |
|
399 |
sqlmtime = atol(filter_sqlres+3); |
400 |
retval = sqlmtime; |
401 |
|
402 |
filterfile = open(filename, O_RDONLY, 0); |
403 |
if (filterfile) { |
404 |
read(filterfile, (void *) filter_fileres, 127); |
405 |
close(filterfile); |
406 |
|
407 |
filemtime = atol(filter_fileres+3); |
408 |
} |
409 |
|
410 |
if (msg_verbose) |
411 |
msg_info("%s: filter data: sql_size=%li sql_mtime=%ld file_mtime=%ld", myname, strlen(filter_sqlres), sqlmtime, filemtime); |
412 |
} |
413 |
if (sqlmtime != filemtime && sqlmtime != 0) { |
414 |
if ((filterfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0640))) { |
415 |
if (msg_verbose) |
416 |
msg_info("%s: updating filter file: %s", myname, filename); |
417 |
write(filterfile, filter_sqlres, strlen(filter_sqlres)); |
418 |
close(filterfile); |
419 |
} |
420 |
else { |
421 |
msg_warn("%s: can't create filter file: %s", myname, filename); |
422 |
retval = 0; |
423 |
} |
424 |
} |
425 |
} |
426 |
} |
427 |
else { |
428 |
if (stat(filename, &statbuf) == 0) |
429 |
retval = (long) statbuf.st_mtime; |
430 |
if (msg_verbose) |
431 |
msg_info("%s: processing filter file: file_mtime=%ld", myname, retval); |
432 |
} |
433 |
|
434 |
return retval; |
435 |
} |
436 |
|
437 |
/* deliver_maildir - delivery to maildir-style mailbox */ |
438 |
int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr) |
74 |
{ |
439 |
{ |
75 |
const char *myname = "deliver_maildir"; |
440 |
const char *myname = "deliver_maildir"; |
76 |
char *newdir; |
441 |
char *newdir; |
77 |
char *tmpdir; |
442 |
char *tmpdir; |
78 |
char *curdir; |
443 |
char *curdir; |
79 |
char *tmpfile; |
444 |
char *newfile; |
80 |
char *newfile; |
445 |
char *tmpfile; |
81 |
DSN_BUF *why = state.msg_attr.why; |
446 |
DSN_BUF *why = state.msg_attr.why; |
82 |
VSTRING *buf; |
447 |
VSTRING *buf; |
83 |
VSTREAM *dst; |
448 |
VSTREAM *dst; |
84 |
int mail_copy_status; |
449 |
int mail_copy_status; |
85 |
int deliver_status; |
450 |
int deliver_status; |
86 |
int copy_flags; |
451 |
int copy_flags; |
87 |
struct stat st; |
452 |
struct stat st; |
88 |
struct timeval starttime; |
453 |
struct timeval starttime; |
|
|
454 |
|
455 |
/* Maildir Quota. */ |
456 |
const char *limit_res; /* Limit from map. */ |
457 |
char *sizefilename = (char *) 0; /* Maildirsize file name. */ |
458 |
VSTRING *filequota; /* Quota setting from the maildirsize file. */ |
459 |
VSTREAM *sizefile; /* Maildirsize file handle. */ |
460 |
long n = 0; /* Limit in long integer format. */ |
461 |
long saved_count = 0; /* The total number of files. */ |
462 |
long saved_size = 0; /* The total quota of all files. */ |
463 |
struct stat mail_stat; /* To check the size of the mail to be written. */ |
464 |
struct stat sizefile_stat; /* To check the size of the maildirsize file. */ |
465 |
time_t tm; /* To check the age of the maildirsize file. */ |
466 |
|
467 |
/* Maildir Filters. */ |
468 |
const char *value, *cmd_text; /* Filter values. */ |
469 |
char *filtername; |
470 |
char *header; |
471 |
char *bkpnewfile; |
472 |
char *mdffilename = (char *) 0; /* Maildirfolder file name. */ |
473 |
VSTRING *fltstr; |
474 |
VSTREAM *tmpfilter; |
475 |
VSTREAM *mdffile; /* Maildirfolder file handle. */ |
476 |
DICT *FILTERS; |
477 |
long sqlmtime; /* Latest modification time from sql2file(). */ |
478 |
int cmd_len; |
479 |
int read_mds = -1; /* read_maildirsize() returned value */ |
480 |
struct stat mdffile_stat; /* To check if the maildirfolder file exists. */ |
89 |
|
481 |
|
90 |
GETTIMEOFDAY(&starttime); |
482 |
GETTIMEOFDAY(&starttime); |
91 |
|
483 |
|
Lines 94-108
Link Here
|
94 |
*/ |
486 |
*/ |
95 |
state.level++; |
487 |
state.level++; |
96 |
if (msg_verbose) |
488 |
if (msg_verbose) |
97 |
MSG_LOG_STATE(myname, state); |
489 |
MSG_LOG_STATE(myname, state); |
98 |
|
490 |
|
99 |
/* |
491 |
/* |
100 |
* Don't deliver trace-only requests. |
492 |
* Don't deliver trace-only requests. |
101 |
*/ |
493 |
*/ |
102 |
if (DEL_REQ_TRACE_ONLY(state.request->flags)) { |
494 |
if (DEL_REQ_TRACE_ONLY(state.request->flags)) { |
103 |
dsb_simple(why, "2.0.0", "delivers to maildir"); |
495 |
dsb_simple(why, "2.0.0", "delivers to maildir"); |
104 |
return (sent(BOUNCE_FLAGS(state.request), |
496 |
return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr))); |
105 |
SENT_ATTR(state.msg_attr))); |
|
|
106 |
} |
497 |
} |
107 |
|
498 |
|
108 |
/* |
499 |
/* |
Lines 110-127
Link Here
|
110 |
* attribute to reflect the final recipient. |
501 |
* attribute to reflect the final recipient. |
111 |
*/ |
502 |
*/ |
112 |
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) |
503 |
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) |
113 |
msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); |
504 |
msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); |
114 |
state.msg_attr.delivered = state.msg_attr.rcpt.address; |
505 |
state.msg_attr.delivered = state.msg_attr.rcpt.address; |
115 |
mail_copy_status = MAIL_COPY_STAT_WRITE; |
506 |
mail_copy_status = MAIL_COPY_STAT_WRITE; |
116 |
buf = vstring_alloc(100); |
507 |
buf = vstring_alloc(100); |
117 |
|
508 |
|
118 |
copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH |
509 |
copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT; |
119 |
| MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT; |
|
|
120 |
|
510 |
|
121 |
newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0); |
511 |
/* |
122 |
tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0); |
512 |
* Concatenate the maildir suffix (if set). |
123 |
curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0); |
513 |
*/ |
|
|
514 |
if (*var_virt_maildir_suffix == 0) { |
515 |
newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0); |
516 |
tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0); |
517 |
curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0); |
518 |
} |
519 |
else { |
520 |
newdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0); |
521 |
tmpdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0); |
522 |
curdir = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0); |
523 |
newdir = concatenate(newdir, "new/", (char *) 0); |
524 |
tmpdir = concatenate(tmpdir, "tmp/", (char *) 0); |
525 |
curdir = concatenate(curdir, "cur/", (char *) 0); |
526 |
} |
124 |
|
527 |
|
|
|
528 |
/* get the sizefilename, no matter if we use var_virt_maildir_extended */ |
529 |
if (*var_virt_maildir_suffix == 0) { |
530 |
sizefilename = concatenate(usr_attr.mailbox, "maildirsize", (char *) 0); |
531 |
} else { |
532 |
sizefilename = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0); |
533 |
sizefilename = concatenate(sizefilename, "maildirsize", (char *) 0); |
534 |
} |
535 |
|
536 |
/* |
537 |
* Look up the virtual maildir limit size for this user. |
538 |
* Fall back to virtual_mailbox_limit in case lookup failed. |
539 |
* If virtual maildir limit size is negative, fall back to virtual_mailbox_limit. |
540 |
* If it's 0, set the mailbox limit to 0, which means unlimited. |
541 |
* If it's more than 0 (positive int), check if the value is smaller than the maximum message size, |
542 |
* if it is and the virtual maildir limit can't be overridden, fall back to virtual_mailbox_limit and |
543 |
* warn the user, else use the value directly as the maildir limit. |
544 |
*/ |
545 |
if (*var_virt_mailbox_limit_maps != 0 && (limit_res = mail_addr_find(virtual_mailbox_limit_maps, state.msg_attr.user, (char **) NULL)) != 0) { |
546 |
n = atol(limit_res); |
547 |
if (n > 0) { |
548 |
if ((n < var_message_limit) && (!var_virt_mailbox_limit_override)) { |
549 |
n = var_virt_mailbox_limit; |
550 |
|
551 |
msg_warn("%s: recipient %s - virtual maildir limit is smaller than %s in %s - falling back to %s", |
552 |
myname, state.msg_attr.user, VAR_MESSAGE_LIMIT, virtual_mailbox_limit_maps->title, |
553 |
VAR_VIRT_MAILBOX_LIMIT); |
554 |
} |
555 |
else { |
556 |
if (msg_verbose) |
557 |
msg_info("%s: set virtual maildir limit size for %s to %ld", |
558 |
myname, usr_attr.mailbox, n); |
559 |
} |
560 |
} |
561 |
else if (n == 0) { |
562 |
if (msg_verbose) |
563 |
msg_info("%s: set virtual maildir limit size for %s to %ld", |
564 |
myname, usr_attr.mailbox, n); |
565 |
} |
566 |
else { |
567 |
if (msg_verbose) |
568 |
msg_info("%s: quota is negative (%ld), using default virtual_mailbox_limit (%ld)", |
569 |
myname, n, var_virt_mailbox_limit); |
570 |
/* Invalid limit size (negative). Use default virtual_mailbox_limit. */ |
571 |
n = var_virt_mailbox_limit; |
572 |
} |
573 |
} |
574 |
else { |
575 |
if (msg_verbose) |
576 |
msg_info("%s: no limit found in the maps, using default virtual_mailbox_limit (%ld)", |
577 |
myname, var_virt_mailbox_limit); |
578 |
/* There is no limit in the maps. Use default virtual_mailbox_limit. */ |
579 |
n = var_virt_mailbox_limit; |
580 |
} |
581 |
|
582 |
/* If there should is a quota on maildir generaly, check it before delivering the mail */ |
583 |
if (n != 0) { |
584 |
set_eugid(usr_attr.uid, usr_attr.gid); |
585 |
/* try to read the quota from maildirsize file. Returned values by read_maildirsize: |
586 |
x < 0 = something failed |
587 |
x >= 0 = reading successfully finished - sum si returned, so sum size of Maildir was 0 or more */ |
588 |
if (!var_virt_mailbox_limit_inbox && var_virt_maildir_extended && (read_mds = read_maildirsize(sizefilename, &saved_size, &saved_count)) >= 0) { |
589 |
if (msg_verbose) |
590 |
msg_info("%s: maildirsize used=%s sum=%ld count=%ld", myname, sizefilename, saved_size, saved_count); |
591 |
} else { |
592 |
if (msg_verbose) |
593 |
msg_info("%s: We will recount the quota (var_virt_mailbox_limit = %ld, var_virt_maildir_extended = %d, read_maildirsize = %d)", |
594 |
myname, var_virt_mailbox_limit, var_virt_maildir_extended, read_mds); |
595 |
|
596 |
/* sanity */ |
597 |
saved_size = 0; |
598 |
saved_count = 0; |
599 |
|
600 |
if (var_virt_mailbox_limit_inbox) { |
601 |
/* Check Inbox only (new, cur and tmp dirs). */ |
602 |
saved_size = check_dir_size(newdir, &saved_count); |
603 |
saved_size += check_dir_size(curdir, &saved_count); |
604 |
saved_size += check_dir_size(tmpdir, &saved_count); |
605 |
} else { |
606 |
/* Check all boxes. */ |
607 |
saved_size = check_dir_size(usr_attr.mailbox, &saved_count); |
608 |
} |
609 |
|
610 |
set_eugid(var_owner_uid, var_owner_gid); |
611 |
} |
612 |
} |
613 |
|
125 |
/* |
614 |
/* |
126 |
* Create and write the file as the recipient, so that file quota work. |
615 |
* Create and write the file as the recipient, so that file quota work. |
127 |
* Create any missing directories on the fly. The file name is chosen |
616 |
* Create any missing directories on the fly. The file name is chosen |
Lines 175-220
Link Here
|
175 |
* [...] |
664 |
* [...] |
176 |
*/ |
665 |
*/ |
177 |
set_eugid(usr_attr.uid, usr_attr.gid); |
666 |
set_eugid(usr_attr.uid, usr_attr.gid); |
178 |
vstring_sprintf(buf, "%lu.P%d.%s", |
667 |
vstring_sprintf(buf, "%lu.P%d.%s", (unsigned long) starttime.tv_sec, var_pid, get_hostname()); |
179 |
(unsigned long) starttime.tv_sec, var_pid, get_hostname()); |
|
|
180 |
tmpfile = concatenate(tmpdir, STR(buf), (char *) 0); |
668 |
tmpfile = concatenate(tmpdir, STR(buf), (char *) 0); |
181 |
newfile = 0; |
669 |
newfile = 0; |
|
|
670 |
bkpnewfile = 0; |
182 |
if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0 |
671 |
if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0 |
183 |
&& (errno != ENOENT |
672 |
&& (errno != ENOENT |
184 |
|| make_dirs(tmpdir, 0700) < 0 |
673 |
|| make_dirs(tmpdir, 0700) < 0 |
185 |
|| (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) { |
674 |
|| (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) { |
186 |
dsb_simple(why, mbox_dsn(errno, "4.2.0"), |
675 |
dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", tmpfile); |
187 |
"create maildir file %s: %m", tmpfile); |
676 |
} |
188 |
} else if (fstat(vstream_fileno(dst), &st) < 0) { |
677 |
else if (fstat(vstream_fileno(dst), &st) < 0) { |
189 |
|
678 |
/* |
190 |
/* |
679 |
* Coverity 200604: file descriptor leak in code that never executes. |
191 |
* Coverity 200604: file descriptor leak in code that never executes. |
680 |
* Code replaced by msg_fatal(), as it is not worthwhile to continue |
192 |
* Code replaced by msg_fatal(), as it is not worthwhile to continue |
681 |
* after an impossible error condition. |
193 |
* after an impossible error condition. |
682 |
*/ |
194 |
*/ |
683 |
msg_fatal("fstat %s: %m", tmpfile); |
195 |
msg_fatal("fstat %s: %m", tmpfile); |
684 |
} |
196 |
} else { |
685 |
else { |
197 |
vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s", |
686 |
vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s", |
198 |
(unsigned long) starttime.tv_sec, |
687 |
(unsigned long) starttime.tv_sec, |
199 |
(unsigned long) st.st_dev, |
688 |
(unsigned long) st.st_dev, |
200 |
(unsigned long) st.st_ino, |
689 |
(unsigned long) st.st_ino, |
201 |
(unsigned long) starttime.tv_usec, |
690 |
(unsigned long) starttime.tv_usec, |
202 |
get_hostname()); |
691 |
get_hostname()); |
203 |
newfile = concatenate(newdir, STR(buf), (char *) 0); |
692 |
newfile = concatenate(newdir, STR(buf), (char *) 0); |
204 |
if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), |
693 |
bkpnewfile = concatenate(STR(buf), (char *) 0); /* Will need it later, if we MOVE to other folders. */ |
205 |
dst, copy_flags, "\n", |
694 |
|
206 |
why)) == 0) { |
695 |
if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, "\n", why)) == 0) { |
207 |
if (sane_link(tmpfile, newfile) < 0 |
696 |
/* |
208 |
&& (errno != ENOENT |
697 |
* Add a ",S=<sizeoffile>" to the newly written file according to the |
209 |
|| (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0 |
698 |
* Maildir++ specifications: http://www.inter7.com/courierimap/README.maildirquota.html |
210 |
|| sane_link(tmpfile, newfile) < 0)) { |
699 |
* This needs a stat(2) of the tempfile and modification of the |
211 |
dsb_simple(why, mbox_dsn(errno, "4.2.0"), |
700 |
* name of the file. |
212 |
"create maildir file %s: %m", newfile); |
701 |
*/ |
213 |
mail_copy_status = MAIL_COPY_STAT_WRITE; |
702 |
if (stat(tmpfile, &mail_stat) == 0) { |
214 |
} |
703 |
if (n != 0) { |
215 |
} |
704 |
saved_size += (long) mail_stat.st_size; |
216 |
if (unlink(tmpfile) < 0) |
705 |
saved_count++; |
217 |
msg_warn("remove %s: %m", tmpfile); |
706 |
} |
|
|
707 |
if (var_virt_maildir_extended) { |
708 |
/* Append the size of the file to newfile. */ |
709 |
vstring_sprintf(buf, ",S=%ld", (long) mail_stat.st_size); |
710 |
newfile = concatenate(newfile, STR(buf), (char *) 0); |
711 |
bkpnewfile = concatenate(bkpnewfile, STR(buf), (char *) 0); |
712 |
} |
713 |
} |
714 |
|
715 |
/* |
716 |
* Now we have the maildir size in saved_size, compare it to the max |
717 |
* quota value and eventually issue a message that we've overdrawn it. |
718 |
*/ |
719 |
if (saved_size > n) { |
720 |
mail_copy_status = MAIL_COPY_STAT_WRITE; |
721 |
if (((long) mail_stat.st_size > n) || (var_virt_overquota_bounce)) |
722 |
errno = EFBIG; |
723 |
else |
724 |
errno = EDQUOT; |
725 |
} |
726 |
else { |
727 |
/* Maildirfilter code by rk@demiurg.net. */ |
728 |
if (var_virt_maildir_filter) { |
729 |
if (msg_verbose) |
730 |
msg_info("%s: loading DICT filters", myname); |
731 |
|
732 |
#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0) |
733 |
#define MAIL_COPY_STAT_REJECT (1<<3) |
734 |
#define MAIL_COPY_STAT_DISCARD (1<<4) |
735 |
|
736 |
/* Read filters. */ |
737 |
filtername = concatenate("regexp:", usr_attr.mailbox, "maildirfilter", (char *) 0); |
738 |
sqlmtime = sql2file(strchr(filtername, '/'), state.msg_attr.user); |
739 |
|
740 |
/* Check if this filter is already registered as dictionary. */ |
741 |
if (msg_verbose) |
742 |
msg_info("%s: checking DICT filters for %s", myname, filtername); |
743 |
|
744 |
if ((FILTERS = dict_handle(filtername))) { |
745 |
if (msg_verbose) |
746 |
msg_info("%s: DICT filter found", myname); |
747 |
|
748 |
/* |
749 |
* If we have mtime in our DICT structure, check it against sqlmtime |
750 |
* and reload the filters if they differ. |
751 |
*/ |
752 |
if (FILTERS->mtime > 0 && sqlmtime > 0 && FILTERS->mtime != sqlmtime) { |
753 |
if (msg_verbose) |
754 |
msg_info("%s: reloading DICT filters (dict_mtime=%ld != sql_mtime=%ld)", |
755 |
myname, FILTERS->mtime, sqlmtime); |
756 |
|
757 |
dict_unregister(filtername); |
758 |
FILTERS = dict_open(filtername, O_RDONLY, DICT_FLAG_LOCK); |
759 |
dict_register(filtername, FILTERS); |
760 |
FILTERS->mtime = sqlmtime; |
761 |
} |
762 |
} |
763 |
else { |
764 |
if (sqlmtime > 0) { |
765 |
/* Registering filter as new dictionary. */ |
766 |
if (msg_verbose) |
767 |
msg_info("%s: loading DICT filters from %s (mtime=%ld)", |
768 |
myname, filtername, sqlmtime); |
769 |
|
770 |
FILTERS = dict_open(filtername, O_RDONLY, DICT_FLAG_LOCK); |
771 |
dict_register(filtername, FILTERS); |
772 |
FILTERS->mtime = sqlmtime; |
773 |
} |
774 |
} |
775 |
|
776 |
if (FILTERS && (tmpfilter = vstream_fopen(tmpfile, O_RDONLY, 0))) { |
777 |
fltstr = vstring_alloc(1024); |
778 |
header = (char *) malloc(8192); /* !!!INSECURE!!! See 7168-hack below. */ |
779 |
header[0] = 0; |
780 |
vstring_get_nonl_bound(fltstr, tmpfilter, 1023); |
781 |
header = concatenate(header, STR(fltstr), (char *) 0); |
782 |
|
783 |
while(!vstream_feof(tmpfilter) && fltstr->vbuf.data[0] && strlen(header) < 7168 ) { |
784 |
vstring_get_nonl_bound(fltstr, tmpfilter, 1023); |
785 |
/* Glue multiline headers, replacing leading TAB with space. */ |
786 |
if (msg_verbose) |
787 |
msg_info("%s: fltstr value: %s", myname, STR(fltstr)); |
788 |
|
789 |
if (fltstr->vbuf.data[0] == ' ' || fltstr->vbuf.data[0] == '\t' ) { |
790 |
if (fltstr->vbuf.data[0] == '\t') |
791 |
fltstr->vbuf.data[0] = ' '; |
792 |
header = concatenate(header, STR(fltstr), (char *) 0); |
793 |
} |
794 |
else { |
795 |
header = concatenate(header, "\n", STR(fltstr), (char *) 0); |
796 |
} |
797 |
} |
798 |
|
799 |
if (msg_verbose) |
800 |
msg_info("%s: checking filter CMD for %s", myname, filtername); |
801 |
|
802 |
/* Check whole header part with regexp maps. */ |
803 |
if ((value = dict_get(FILTERS, lowercase(header))) != 0) { |
804 |
if (msg_verbose) |
805 |
msg_info("%s: preparing filter CMD", myname); |
806 |
|
807 |
cmd_text = value + strcspn(value, " \t"); |
808 |
cmd_len = cmd_text - value; |
809 |
while (*cmd_text && ISSPACE(*cmd_text)) |
810 |
cmd_text++; |
811 |
|
812 |
if (msg_verbose) |
813 |
msg_info("%s: executing filter CMD", myname); |
814 |
|
815 |
if (STREQUAL(value, "REJECT", cmd_len)) { |
816 |
if (msg_verbose) |
817 |
msg_info("%s: executing filter CMD REJECT", myname); |
818 |
|
819 |
mail_copy_status = MAIL_COPY_STAT_REJECT; |
820 |
vstring_sprintf(why->reason, "%s", cmd_text); |
821 |
dsb_simple(why, "5.0.0", "User filter - REJECT"); |
822 |
} |
823 |
|
824 |
if (STREQUAL(value, "DISCARD", cmd_len)) { |
825 |
if (msg_verbose) |
826 |
msg_info("%s: executing filter CMD DISCARD", myname); |
827 |
|
828 |
mail_copy_status = MAIL_COPY_STAT_DISCARD; |
829 |
vstring_sprintf(why->reason, "%s", cmd_text); |
830 |
dsb_simple(why, "5.0.0", "User filter - DISCARD"); |
831 |
} |
832 |
|
833 |
if (var_virt_maildir_extended) { |
834 |
if (STREQUAL(value, "MOVE", cmd_len)) { |
835 |
if (msg_verbose) |
836 |
msg_info("%s: executing filter CMD MOVE", myname); |
837 |
|
838 |
strcut((char *) cmd_text, " "); |
839 |
strcut((char *) cmd_text, "\t"); |
840 |
strcut((char *) cmd_text, "/"); |
841 |
strcut((char *) cmd_text, ".."); |
842 |
|
843 |
if (*var_virt_maildir_suffix == 0) { |
844 |
newfile = concatenate(usr_attr.mailbox, (char *) 0); |
845 |
} |
846 |
else { |
847 |
newfile = concatenate(usr_attr.mailbox, var_virt_maildir_suffix, (char *) 0); |
848 |
} |
849 |
|
850 |
if (cmd_text[0] != '.') { |
851 |
newfile = concatenate(newfile, ".", (char *) 0); |
852 |
} |
853 |
newdir = concatenate(newfile, cmd_text, "/", "new/", (char *) 0); |
854 |
tmpdir = concatenate(newfile, cmd_text, "/", "tmp/", (char *) 0); |
855 |
curdir = concatenate(newfile, cmd_text, "/", "cur/", (char *) 0); |
856 |
mdffilename = concatenate(newfile, cmd_text, "/", "maildirfolder", (char *) 0); |
857 |
newfile = concatenate(newfile, cmd_text, "/", "new/", bkpnewfile, (char *) 0); |
858 |
} |
859 |
} |
860 |
|
861 |
if (STREQUAL(value, "LOG", cmd_len) || STREQUAL(value, "WARN", cmd_len)) { |
862 |
msg_warn("%s: header check warning: %s", myname, cmd_text); |
863 |
} |
864 |
|
865 |
if (STREQUAL(value, "INFO", cmd_len)) { |
866 |
msg_info("%s: header check info: %s", myname, cmd_text); |
867 |
} |
868 |
|
869 |
if (msg_verbose) |
870 |
msg_info("%s: exiting filter CMD", myname); |
871 |
} /* End-Of-Check */ |
872 |
|
873 |
myfree(header); |
874 |
vstring_free(fltstr); |
875 |
vstream_fclose(tmpfilter); |
876 |
} |
877 |
|
878 |
myfree(filtername); |
879 |
} /* End-Of-Maildirfilter */ |
880 |
|
881 |
/* Deliver to curdir. */ |
882 |
if (mail_copy_status == 0) { |
883 |
if (sane_link(tmpfile, newfile) < 0 |
884 |
&& (errno != ENOENT |
885 |
|| (make_dirs(curdir, 0700), make_dirs(newdir, 0700), make_dirs(tmpdir, 0700)) < 0 |
886 |
|| sane_link(tmpfile, newfile) < 0)) { |
887 |
dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", newfile); |
888 |
mail_copy_status = MAIL_COPY_STAT_WRITE; |
889 |
} |
890 |
|
891 |
if (var_virt_maildir_extended) { |
892 |
time(&tm); |
893 |
|
894 |
/* Check if the quota in the file is the same as the current one, if not, delete the file. */ |
895 |
sizefile = vstream_fopen(sizefilename, O_RDONLY, 0); |
896 |
if (sizefile) { |
897 |
filequota = vstring_alloc(128); |
898 |
vstring_get_null_bound(filequota, sizefile, 127); |
899 |
vstream_fclose(sizefile); |
900 |
if (atol(vstring_export(filequota)) != n) |
901 |
unlink(sizefilename); |
902 |
} |
903 |
|
904 |
/* Open maildirsize file to append this transaction. */ |
905 |
sizefile = vstream_fopen(sizefilename, O_WRONLY | O_APPEND, 0640); |
906 |
|
907 |
/* If the open fails (maildirsize doesn't exist), or it's too large, or too old, overwrite it. */ |
908 |
if(!sizefile || (stat(sizefilename, &sizefile_stat) < 0) || (sizefile_stat.st_size > SIZEFILE_MAX) || (sizefile_stat.st_mtime + 15*60 < tm)) { |
909 |
/* If the file exists, sizefile has been opened above, so close it first. */ |
910 |
if (sizefile) { |
911 |
vstream_fclose(sizefile); |
912 |
sizefile = vstream_fopen(sizefilename, O_WRONLY | O_TRUNC, 0640); |
913 |
} |
914 |
else { |
915 |
sizefile = vstream_fopen(sizefilename, O_WRONLY | O_CREAT, 0640); |
916 |
} |
917 |
|
918 |
/* If the creation worked, write to the file, otherwise just give up. */ |
919 |
if (sizefile) { |
920 |
vstream_fprintf(sizefile, "%ldS\n%ld %ld\n", n, saved_size, saved_count); |
921 |
vstream_fclose(sizefile); |
922 |
} |
923 |
} |
924 |
else { |
925 |
/* We opened maildirsize, so let's just append this transaction and close it. */ |
926 |
vstream_fprintf(sizefile, "%ld 1\n", (long) mail_stat.st_size); |
927 |
vstream_fclose(sizefile); |
928 |
} |
929 |
|
930 |
/* |
931 |
* 1) mdffilename != 0, so the maildirfilter code went through the MOVE to subfolder rule. |
932 |
* 2) stat() failed, maybe the file does not exist? Try to create it. |
933 |
*/ |
934 |
if (mdffilename && (stat(mdffilename, &mdffile_stat) < 0)) { |
935 |
mdffile = vstream_fopen(mdffilename, O_WRONLY | O_CREAT, 0600); |
936 |
if (mdffile) { |
937 |
vstream_fclose(mdffile); |
938 |
} |
939 |
else { |
940 |
msg_warn("Cannot create maildirfolder file '%s': %s", mdffilename, strerror(errno)); |
941 |
} |
942 |
} |
943 |
} |
944 |
} |
945 |
} |
946 |
} |
947 |
if (unlink(tmpfile) < 0) |
948 |
msg_warn("remove %s: %m", tmpfile); |
218 |
} |
949 |
} |
219 |
set_eugid(var_owner_uid, var_owner_gid); |
950 |
set_eugid(var_owner_uid, var_owner_gid); |
220 |
|
951 |
|
Lines 224-254
Link Here
|
224 |
* location possibly under user control. |
955 |
* location possibly under user control. |
225 |
*/ |
956 |
*/ |
226 |
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { |
957 |
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { |
227 |
deliver_status = DEL_STAT_DEFER; |
958 |
deliver_status = DEL_STAT_DEFER; |
228 |
} else if (mail_copy_status != 0) { |
959 |
} |
229 |
if (errno == EACCES) { |
960 |
else if (mail_copy_status != 0) { |
230 |
msg_warn("maildir access problem for UID/GID=%lu/%lu: %s", |
961 |
if (errno == EACCES) { |
231 |
(long) usr_attr.uid, (long) usr_attr.gid, |
962 |
msg_warn("maildir access problem for UID/GID=%lu/%lu: %s", |
232 |
STR(why->reason)); |
963 |
(long) usr_attr.uid, (long) usr_attr.gid, STR(why->reason)); |
233 |
msg_warn("perhaps you need to create the maildirs in advance"); |
964 |
msg_warn("perhaps you need to create the maildirs in advance"); |
234 |
} |
965 |
} |
235 |
vstring_sprintf_prepend(why->reason, "maildir delivery failed: "); |
966 |
|
236 |
deliver_status = |
967 |
/* Support per-recipient bounce messages. */ |
237 |
(STR(why->status)[0] == '4' ? |
968 |
const char *limit_message; |
238 |
defer_append : bounce_append) |
969 |
int errnored = errno; /* Seems like mail_addr_find resets errno ... */ |
239 |
(BOUNCE_FLAGS(state.request), |
970 |
|
240 |
BOUNCE_ATTR(state.msg_attr)); |
971 |
if (*var_virt_maildir_limit_message_maps != 0 && (limit_message = mail_addr_find(virtual_maildir_limit_message_maps, state.msg_attr.user, (char **) NULL)) != 0) { |
241 |
} else { |
972 |
errno = errnored; |
242 |
dsb_simple(why, "2.0.0", "delivered to maildir"); |
973 |
if (errno == EFBIG) { |
243 |
deliver_status = sent(BOUNCE_FLAGS(state.request), |
974 |
dsb_simple(why, "5.2.2", limit_message, NULL); |
244 |
SENT_ATTR(state.msg_attr)); |
975 |
} |
|
|
976 |
if (errno == EDQUOT) { |
977 |
dsb_simple(why, "4.2.2", limit_message, NULL); |
978 |
} |
979 |
} |
980 |
else { |
981 |
errno = errnored; |
982 |
if (errno == EFBIG) { |
983 |
dsb_simple(why, "5.2.2", var_virt_maildir_limit_message, NULL); |
984 |
} |
985 |
if (errno == EDQUOT) { |
986 |
dsb_simple(why, "4.2.2", var_virt_maildir_limit_message, NULL); |
987 |
} |
988 |
} |
989 |
|
990 |
vstring_sprintf_prepend(why->reason, "maildir delivery failed: "); |
991 |
deliver_status = |
992 |
(STR(why->status)[0] == '4' ? defer_append : bounce_append) |
993 |
(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); |
245 |
} |
994 |
} |
|
|
995 |
else { |
996 |
dsb_simple(why, "2.0.0", "delivered to maildir"); |
997 |
deliver_status = sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)); |
998 |
} |
999 |
|
246 |
vstring_free(buf); |
1000 |
vstring_free(buf); |
|
|
1001 |
|
247 |
myfree(newdir); |
1002 |
myfree(newdir); |
248 |
myfree(tmpdir); |
1003 |
myfree(tmpdir); |
249 |
myfree(curdir); |
1004 |
myfree(curdir); |
|
|
1005 |
|
1006 |
if (sizefilename) |
1007 |
myfree(sizefilename); |
1008 |
if (mdffilename) |
1009 |
myfree(mdffilename); |
1010 |
|
250 |
myfree(tmpfile); |
1011 |
myfree(tmpfile); |
251 |
if (newfile) |
1012 |
if (newfile) |
252 |
myfree(newfile); |
1013 |
myfree(newfile); |
|
|
1014 |
if (bkpnewfile) |
1015 |
myfree(bkpnewfile); |
1016 |
|
253 |
return (deliver_status); |
1017 |
return (deliver_status); |
254 |
} |
1018 |
} |