mbox: check the message number in mbox_get()
[neatmail.git] / mbox.c
blobce9e265ad4910a7df9899bfe0af3fe01d985ac92
1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <time.h>
10 #include <unistd.h>
11 #include <utime.h>
12 #include "mail.h"
14 struct mbox {
15 char *path;
16 char **msg; /* messages */
17 long *msglen; /* message lengths */
18 char **mod; /* modified messages */
19 long *modlen; /* modified message lengths */
20 char *buf; /* mbox buffer */
21 long len; /* buf len */
22 int n; /* number of messages */
25 static void set_atime(char *filename)
27 struct stat st;
28 struct utimbuf times;
29 stat(filename, &st);
30 times.actime = time(NULL);
31 times.modtime = st.st_mtime;
32 utime(filename, &times);
35 static char *mbox_from_(char *s, char *e)
37 char *r;
38 while (s && s + 6 < e) {
39 if (s[0] == 'F' && s[1] == 'r' && s[2] == 'o' &&
40 s[3] == 'm' && s[4] == ' ')
41 return s;
42 r = memchr(s, '\n', e - s);
43 s = r && r + 7 < e ? r + 1 : NULL;
45 return NULL;
48 static int mbox_count(char *s, char *e)
50 int n = 0;
51 while ((s = mbox_from_(s, e)))
52 n++, s++;
53 return n;
56 int mbox_get(struct mbox *mbox, int i, char **msg, long *msglen)
58 if (i < 0 || i >= mbox->n)
59 return 1;
60 if (mbox->mod[i]) {
61 *msg = mbox->mod[i];
62 *msglen = mbox->modlen[i];
63 } else {
64 *msg = mbox->msg[i];
65 *msglen = mbox->msglen[i];
67 return 0;
70 void mbox_set(struct mbox *mbox, int i, char *msg, long msz)
72 free(mbox->mod[i]);
73 mbox->mod[i] = malloc(msz + 1);
74 if (mbox->mod[i]) {
75 mbox->modlen[i] = msz;
76 memcpy(mbox->mod[i], msg, msz);
77 mbox->mod[i][msz] = '\0';
81 int mbox_len(struct mbox *mbox)
83 return mbox->n;
86 static int mbox_mails(struct mbox *mbox, char *s, char *e)
88 int i;
89 s = mbox_from_(s, e);
90 for (i = 0; s && s < e; i++) {
91 char *r = s;
92 mbox->msg[i] = s;
93 s = mbox_from_(s + 6, e);
94 mbox->msglen[i] = s ? s - r : mbox->buf + mbox->len - r;
96 mbox->n = i;
97 return 0;
100 static int filesize(int fd)
102 struct stat stat;
103 fstat(fd, &stat);
104 return stat.st_size;
107 static int mbox_read(struct mbox *mbox)
109 int fd = open(mbox->path, O_RDONLY);
110 if (fd < 0)
111 return 1;
112 mbox->len = filesize(fd);
113 mbox->buf = malloc(mbox->len + 1);
114 if (!mbox->buf)
115 return 1;
116 xread(fd, mbox->buf, mbox->len);
117 mbox->buf[mbox->len] = '\0';
118 close(fd);
119 set_atime(mbox->path); /* update mbox access time */
120 mbox->n = mbox_count(mbox->buf, mbox->buf + mbox->len);
121 mbox->msg = malloc(mbox->n * sizeof(mbox->msg[0]));
122 mbox->msglen = malloc(mbox->n * sizeof(mbox->msglen[0]));
123 mbox->mod = malloc(mbox->n * sizeof(mbox->mod[0]));
124 mbox->modlen = malloc(mbox->n * sizeof(mbox->modlen[0]));
125 memset(mbox->mod, 0, mbox->n * sizeof(mbox->mod[0]));
126 if (!mbox->msg || !mbox->msglen || !mbox->mod || !mbox->modlen)
127 return 1;
128 if (mbox_mails(mbox, mbox->buf, mbox->buf + mbox->len))
129 return 1;
130 return 0;
133 static char *sdup(char *s)
135 int n = strlen(s) + 1;
136 char *r = malloc(n);
137 if (r)
138 memcpy(r, s, n);
139 return r;
142 struct mbox *mbox_open(char *path)
144 struct mbox *mbox;
145 mbox = malloc(sizeof(*mbox));
146 if (!mbox)
147 return NULL;
148 memset(mbox, 0, sizeof(*mbox));
149 mbox->path = sdup(path);
150 if (!mbox->path || mbox_read(mbox)) {
151 mbox_free(mbox);
152 return NULL;
154 return mbox;
157 void mbox_free(struct mbox *mbox)
159 int i;
160 for (i = 0; i < mbox->n; i++)
161 free(mbox->mod[i]);
162 free(mbox->msg);
163 free(mbox->msglen);
164 free(mbox->mod);
165 free(mbox->modlen);
166 free(mbox->path);
167 free(mbox->buf);
168 free(mbox);
171 static int mbox_modified(struct mbox *mbox)
173 int i;
174 for (i = 0; i < mbox->n; i++)
175 if (mbox->mod[i])
176 return 1;
177 return 0;
180 int mbox_copy(struct mbox *mbox, char *path)
182 int fd;
183 int i;
184 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
185 if (fd < 0)
186 return -1;
187 for (i = 0; i < mbox->n; i++) {
188 char *msg = mbox->mod[i] ? mbox->mod[i] : mbox->msg[i];
189 long len = mbox->mod[i] ? mbox->modlen[i] : mbox->msglen[i];
190 xwrite(fd, msg, len);
192 close(fd);
193 return 0;
196 int mbox_save(struct mbox *mbox)
198 int fd;
199 int i = 0;
200 char *newbuf;
201 long off = 0;
202 long newlen;
203 if (!mbox_modified(mbox))
204 return 0;
205 fd = open(mbox->path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
206 newlen = filesize(fd) - mbox->len;
207 if (newlen > 0) {
208 newbuf = malloc(newlen);
209 lseek(fd, mbox->len, 0);
210 xread(fd, newbuf, newlen);
212 while (i < mbox->n && !mbox->mod[i])
213 off += mbox->msglen[i++];
214 lseek(fd, off, 0);
215 for (; i < mbox->n; i++) {
216 char *msg = mbox->mod[i] ? mbox->mod[i] : mbox->msg[i];
217 long len = mbox->mod[i] ? mbox->modlen[i] : mbox->msglen[i];
218 xwrite(fd, msg, len);
220 ftruncate(fd, lseek(fd, 0, 1));
221 close(fd);
222 return newlen;
225 int mbox_ith(char *path, int n, char **msg, long *msz)
227 int fd = open(path, O_RDONLY);
228 char *s, *e, *r;
229 char *buf;
230 int len;
231 int i;
232 if (fd < 0)
233 return 1;
234 len = filesize(fd);
235 buf = malloc(len + 1);
236 if (!buf)
237 return 1;
238 xread(fd, buf, len);
239 buf[len] = '\0';
240 close(fd);
241 e = buf + len;
242 s = mbox_from_(buf, e);
243 for (i = 0; s && i < n; i++)
244 s = mbox_from_(s + 1, e);
245 if (!s)
246 return 1;
247 r = mbox_from_(s + 1, e);
248 if (!r)
249 r = buf + len;
250 *msg = malloc(r - s + 1);
251 if (!*msg)
252 return 1;
253 *msz = r - s;
254 memcpy(*msg, s, *msz);
255 (*msg)[*msz] = '\0';
256 free(buf);
257 return 0;