maildir.c
changeset 0 9e2cb1ed20b1
child 9 52527e90ed80
equal deleted inserted replaced
-1:000000000000 0:9e2cb1ed20b1
       
     1 /*
       
     2  * Copyright (c) 2014 Sunil Nimmagadda <sunil@nimmagadda.net>
       
     3  *
       
     4  * Permission to use, copy, modify, and distribute this software for any
       
     5  * purpose with or without fee is hereby granted, provided that the above
       
     6  * copyright notice and this permission notice appear in all copies.
       
     7  *
       
     8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       
     9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       
    10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       
    11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       
    12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       
    13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       
    14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       
    15  */
       
    16 
       
    17 #include <sys/param.h>
       
    18 #include <sys/types.h>
       
    19 #include <sys/tree.h>
       
    20 #include <sys/stat.h>
       
    21 #include <sys/socket.h>
       
    22 
       
    23 #include <fcntl.h>
       
    24 #include <dirent.h>
       
    25 #include <stdio.h>
       
    26 #include <string.h>
       
    27 #include <syslog.h>
       
    28 #include <unistd.h>
       
    29 
       
    30 #include "pop3d.h"
       
    31 
       
    32 static int init(struct mdrop *, size_t *, size_t *);
       
    33 static int retr(struct mdrop *, unsigned int, size_t *, size_t *);
       
    34 static int update(struct mdrop *);
       
    35 static int new_to_cur(struct mdrop *);
       
    36 static int msgcmp(struct msg *, struct msg *);
       
    37 RB_PROTOTYPE(msgtree, msg, e.t_entry, msgcmp);
       
    38 
       
    39 struct m_backend m_backend_maildir = {
       
    40 	init,
       
    41 	retr,
       
    42 	update
       
    43 };
       
    44 
       
    45 /*
       
    46  * No resource management on error path as the process is
       
    47  * killed if an error occurs.
       
    48  */
       
    49 static int
       
    50 init(struct mdrop *m, size_t *nmsgs, size_t *sz)
       
    51 {
       
    52 	SHA1_CTX	ctx;
       
    53 	struct stat	sb;
       
    54 	char		buf[MAXBSIZE];
       
    55 	DIR		*dirp;
       
    56 	struct dirent	*dp;
       
    57 	struct msg	*msg;
       
    58 	u_char		*C;
       
    59 	size_t		i;
       
    60 	ssize_t		len;
       
    61 	int		cur_fd, msg_fd;
       
    62 
       
    63 	*nmsgs = 0;
       
    64 	*sz = 0;
       
    65 	if (new_to_cur(m) == -1) {
       
    66 		logit(LOG_WARNING, "maildir: move msgs from new to cur failed");
       
    67 		return (-1);
       
    68 	}
       
    69 
       
    70 	if ((cur_fd = openat(m->fd, "cur", O_RDONLY)) == -1) {
       
    71 		logit(LOG_CRIT, "maildir: unable to open \"cur\" dir");
       
    72 		return (-1);
       
    73 	}
       
    74 
       
    75 	if ((dirp = fdopendir(cur_fd)) == NULL)
       
    76 		return (-1);
       
    77 
       
    78 	while ((dp = readdir(dirp))) {
       
    79 		if (dp->d_type != DT_REG)
       
    80 			continue;
       
    81 
       
    82 		if (strcmp(dp->d_name, ".") == 0 ||
       
    83 		    strcmp(dp->d_name, "..") == 0)
       
    84 			continue;
       
    85 
       
    86 		msg = xcalloc(1, sizeof(*msg), "init");
       
    87 		if ((msg->u.fname = strdup(dp->d_name)) == NULL)
       
    88 			fatalx("init: strdup");
       
    89 
       
    90 		if (fstatat(cur_fd, dp->d_name, &sb, 0) == -1) {
       
    91 			logit(LOG_CRIT, "%s fstatat failed", dp->d_name);
       
    92 			return (-1);
       
    93 		}
       
    94 
       
    95 		msg->sz = sb.st_size;
       
    96 		if ((msg_fd = openat(cur_fd, dp->d_name, O_RDONLY)) == -1) {
       
    97 			logit(LOG_CRIT, "%s openat failed", dp->d_name);
       
    98 			return (-1);
       
    99 		}
       
   100 
       
   101 		SHA1Init(&ctx);
       
   102 		while (( len = read(msg_fd, buf, sizeof(buf))) > 0) {
       
   103 			SHA1Update(&ctx, (u_int8_t *)buf, len);
       
   104 			for (C = buf;len--; ++C)
       
   105 				if (*C == '\n')
       
   106 					msg->nlines += 1;
       
   107 		}
       
   108 
       
   109 		SHA1End(&ctx, msg->hash);
       
   110 		close(msg_fd);
       
   111 		RB_INSERT(msgtree, &m->e.t_msgs, msg);
       
   112 		m->nmsgs += 1;
       
   113 	}
       
   114 
       
   115 	/* allocate space for nmsgs of struct msg pointers */
       
   116 	m->msgs_index = xcalloc(m->nmsgs, sizeof(msg), "init");
       
   117 	*nmsgs = m->nmsgs;
       
   118 	i = 0;
       
   119 	*sz = 0;
       
   120 	RB_FOREACH(msg, msgtree, &m->e.t_msgs) {
       
   121 		m->msgs_index[i++] = msg;
       
   122 		/* calculate maildir size by counting newline as 2 (CRLF) */
       
   123 		*sz += msg->sz + msg->nlines;
       
   124 	}
       
   125 
       
   126 	closedir(dirp);
       
   127 	close(cur_fd);
       
   128 	return (0);
       
   129 }
       
   130 
       
   131 static int
       
   132 new_to_cur(struct mdrop *m)
       
   133 {
       
   134 	DIR		*dirp;
       
   135 	struct dirent	*dp;
       
   136 	int		cur_fd, new_fd;
       
   137 
       
   138 
       
   139 	if ((cur_fd = openat(m->fd, "cur", O_RDONLY)) == -1) {
       
   140 		logit(LOG_CRIT, "maildir: unable to open \"cur\" dir");
       
   141 		return (-1);
       
   142 	}
       
   143 
       
   144 	if ((new_fd = openat(m->fd, "new", O_RDONLY)) == -1) {
       
   145 		logit(LOG_CRIT, "maildir: unable to open \"new\" dir");
       
   146 		return (-1);
       
   147 	}
       
   148 
       
   149 	if ((dirp = fdopendir(new_fd)) == NULL)
       
   150 		return (-1);
       
   151 
       
   152 	while ((dp = readdir(dirp))) {
       
   153 		if (dp->d_type != DT_REG)
       
   154 			continue;
       
   155 
       
   156 		if (strcmp(dp->d_name, ".") == 0 ||
       
   157 		    strcmp(dp->d_name, "..") == 0)
       
   158 			continue;
       
   159 
       
   160 		if(renameat(new_fd, dp->d_name, cur_fd, dp->d_name) == -1) {
       
   161 			logit(LOG_CRIT, "maildir: renameat failed");
       
   162 			return (-1);
       
   163 		}
       
   164 	}
       
   165 
       
   166 	closedir(dirp);
       
   167 	close(cur_fd);
       
   168 	close(new_fd);
       
   169 	return (0);
       
   170 }
       
   171 
       
   172 static int
       
   173 retr(struct mdrop *m, unsigned int idx, size_t *nlines, size_t *offset)
       
   174 {
       
   175 	char	buf[MAXPATHLEN];
       
   176 	int	fd, r;
       
   177 
       
   178 	*offset = 0;
       
   179 	r = snprintf(buf, sizeof(buf), "cur/%s", m->msgs_index[idx]->u.fname);
       
   180 	if ((u_int)r >= sizeof(buf)) {
       
   181 		logit(LOG_WARNING, "path too long");
       
   182 		return (-1);
       
   183 	}
       
   184 
       
   185 	fd = openat(m->fd, buf, O_RDONLY);
       
   186 	return (fd);
       
   187 }
       
   188 
       
   189 static int
       
   190 update(struct mdrop *m)
       
   191 {
       
   192 	char	buf[MAXPATHLEN];
       
   193 	size_t	i, j = 0;
       
   194 	int	r;
       
   195 
       
   196 	for (i = 0; i < m->nmsgs; i++)
       
   197 		if (m->msgs_index[i]->flags & F_DELE)
       
   198 			j += 1;
       
   199 
       
   200 	if (j == 0) /* nothing to update */
       
   201 		return (0);
       
   202 
       
   203 	for (i = 0; i < m->nmsgs; i++) {
       
   204 		if (!(m->msgs_index[i]->flags & F_DELE))
       
   205 			continue;
       
   206 
       
   207 		r = snprintf(buf, sizeof(buf), "cur/%s",
       
   208 		    m->msgs_index[i]->u.fname);
       
   209 		if ((u_int)r >= sizeof(buf)) {
       
   210 			logit(LOG_WARNING, "path too long");
       
   211 			return (1);
       
   212 		}
       
   213 
       
   214 		if (unlinkat(m->fd, buf, 0) == -1) {
       
   215 			logit(LOG_CRIT, "%s unlink failed", buf);
       
   216 			return (1);
       
   217 		}
       
   218 	}
       
   219 
       
   220 	return (0);
       
   221 }
       
   222 
       
   223 static int
       
   224 msgcmp(struct msg *m1, struct msg *m2)
       
   225 {
       
   226 	return strcmp(m1->u.fname, m2->u.fname);
       
   227 }
       
   228 
       
   229 RB_GENERATE(msgtree, msg, e.t_entry, msgcmp);