imap4d: various fixes.
* imap4d/lsub.c (imap4d_lsub): Fix untagged responses. * imap4d/imap4d.h (make_interdir): New proto. * imap4d/create.c (mkdir_p): Remove function. (MKDIR_PERMISSIONS): Move macro definition to imap4d.h (imap4d_create): Use make_interdir instead of mkdir_p. * imap4d/rename.c (make_interdir): New function. (imap4d_rename): Create intermediate directories as required by RFC 3501. Issue additional diagnostics if rename fails. Fix OK response text. * imap4d/imap4d.c (imap4d_mainloop): Implement idle timeout. * imap4d/bye.c (imap4d_bye0): Reset the SIGPIPE before sending the OK response in order to avoid recursion. Unrelated changes: * maidag/tests/forward.at: Fix the expected reply (see f5374bc2).
Showing
8 changed files
with
116 additions
and
53 deletions
... | @@ -23,6 +23,14 @@ imap4d_bye (int reason) | ... | @@ -23,6 +23,14 @@ imap4d_bye (int reason) |
23 | return imap4d_bye0 (reason, NULL); | 23 | return imap4d_bye0 (reason, NULL); |
24 | } | 24 | } |
25 | 25 | ||
26 | static jmp_buf pipejmp; | ||
27 | |||
28 | static RETSIGTYPE | ||
29 | sigpipe (int sig) | ||
30 | { | ||
31 | longjmp (pipejmp, 1); | ||
32 | } | ||
33 | |||
26 | int | 34 | int |
27 | imap4d_bye0 (int reason, struct imap4d_command *command) | 35 | imap4d_bye0 (int reason, struct imap4d_command *command) |
28 | { | 36 | { |
... | @@ -97,7 +105,14 @@ imap4d_bye0 (int reason, struct imap4d_command *command) | ... | @@ -97,7 +105,14 @@ imap4d_bye0 (int reason, struct imap4d_command *command) |
97 | } | 105 | } |
98 | 106 | ||
99 | if (status == EX_OK && command) | 107 | if (status == EX_OK && command) |
108 | { | ||
109 | /* Some clients may close the connection immediately after sending | ||
110 | LOGOUT. Do not treat this as error (RFC 2683). */ | ||
111 | static int sigtab[] = { SIGPIPE }; | ||
112 | mu_set_signals (sigpipe, sigtab, MU_ARRAY_SIZE (sigtab)); | ||
113 | if (setjmp (pipejmp) == 0) | ||
100 | io_completion_response (command, RESP_OK, "Completed"); | 114 | io_completion_response (command, RESP_OK, "Completed"); |
115 | } | ||
101 | 116 | ||
102 | util_bye (); | 117 | util_bye (); |
103 | 118 | ... | ... |
... | @@ -18,51 +18,6 @@ | ... | @@ -18,51 +18,6 @@ |
18 | #include "imap4d.h" | 18 | #include "imap4d.h" |
19 | #include <unistd.h> | 19 | #include <unistd.h> |
20 | 20 | ||
21 | #define MKDIR_PERMISSIONS 0700 | ||
22 | |||
23 | /* FIXME: take permissions as argument */ | ||
24 | static int | ||
25 | mkdir_p (char *name, int delim) | ||
26 | { | ||
27 | char *p, *dir; | ||
28 | int rc = 0; | ||
29 | |||
30 | dir = name; | ||
31 | if (dir[0] == delim) | ||
32 | dir++; | ||
33 | for (; rc == 0 && (p = strchr (dir, delim)); dir = p) | ||
34 | { | ||
35 | struct stat st; | ||
36 | |||
37 | *p++ = 0; | ||
38 | if (stat (name, &st) == 0) | ||
39 | { | ||
40 | if (!S_ISDIR (st.st_mode)) | ||
41 | { | ||
42 | mu_diag_output (MU_DIAG_ERR, | ||
43 | _("component %s is not a directory"), name); | ||
44 | rc = 1; | ||
45 | } | ||
46 | } | ||
47 | else if (errno != ENOENT) | ||
48 | { | ||
49 | mu_diag_output (MU_DIAG_ERR, | ||
50 | _("cannot stat file %s: %s"), | ||
51 | name, mu_strerror (errno)); | ||
52 | rc = 1; | ||
53 | } | ||
54 | else if (mkdir (name, MKDIR_PERMISSIONS)) | ||
55 | { | ||
56 | mu_diag_output (MU_DIAG_ERR, | ||
57 | _("cannot create directory %s: %s"), name, | ||
58 | mu_strerror (errno)); | ||
59 | rc = 1; | ||
60 | } | ||
61 | p[-1] = delim; | ||
62 | } | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* | 21 | /* |
67 | 6.3.3. CREATE Command | 22 | 6.3.3. CREATE Command |
68 | 23 | ||
... | @@ -120,7 +75,7 @@ imap4d_create (struct imap4d_command *command, imap4d_tokbuf_t tok) | ... | @@ -120,7 +75,7 @@ imap4d_create (struct imap4d_command *command, imap4d_tokbuf_t tok) |
120 | /* It will fail if the mailbox already exists. */ | 75 | /* It will fail if the mailbox already exists. */ |
121 | if (access (name, F_OK) != 0) | 76 | if (access (name, F_OK) != 0) |
122 | { | 77 | { |
123 | if (mkdir_p (name, delim[0])) | 78 | if (make_interdir (name, delim[0], MKDIR_PERMISSIONS)) |
124 | { | 79 | { |
125 | rc = RESP_NO; | 80 | rc = RESP_NO; |
126 | msg = "Cannot create mailbox"; | 81 | msg = "Cannot create mailbox"; | ... | ... |
... | @@ -64,7 +64,7 @@ static struct argp_option options[] = { | ... | @@ -64,7 +64,7 @@ static struct argp_option options[] = { |
64 | { "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL, | 64 | { "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL, |
65 | N_("runs in daemon mode with a maximum of NUMBER children"), 0 }, | 65 | N_("runs in daemon mode with a maximum of NUMBER children"), 0 }, |
66 | 66 | ||
67 | {"preauth", OPT_PREAUTH, NULL, 0, | 67 | { "preauth", OPT_PREAUTH, NULL, 0, |
68 | N_("start in preauth mode") }, | 68 | N_("start in preauth mode") }, |
69 | 69 | ||
70 | {NULL, 0, NULL, 0, NULL, 0} | 70 | {NULL, 0, NULL, 0, NULL, 0} |
... | @@ -436,6 +436,8 @@ imap4d_mainloop (int ifd, int ofd) | ... | @@ -436,6 +436,8 @@ imap4d_mainloop (int ifd, int ofd) |
436 | tokp = imap4d_tokbuf_init (); | 436 | tokp = imap4d_tokbuf_init (); |
437 | while (1) | 437 | while (1) |
438 | { | 438 | { |
439 | if (idle_timeout && io_wait_input (idle_timeout) != 1) | ||
440 | imap4d_bye (ERR_TIMEOUT); | ||
439 | imap4d_readline (tokp); | 441 | imap4d_readline (tokp); |
440 | /* check for updates */ | 442 | /* check for updates */ |
441 | imap4d_sync (); | 443 | imap4d_sync (); | ... | ... |
... | @@ -319,6 +319,11 @@ extern int imap4d_check_home_dir (const char *dir, uid_t uid, gid_t gid); | ... | @@ -319,6 +319,11 @@ extern int imap4d_check_home_dir (const char *dir, uid_t uid, gid_t gid); |
319 | /* Shared between fetch and store */ | 319 | /* Shared between fetch and store */ |
320 | extern void fetch_flags0 (const char *prefix, mu_message_t msg, int isuid); | 320 | extern void fetch_flags0 (const char *prefix, mu_message_t msg, int isuid); |
321 | 321 | ||
322 | /* Permissions for creating intermediate directories. | ||
323 | FIXME: These should better be configurable. */ | ||
324 | #define MKDIR_PERMISSIONS 0700 | ||
325 | int make_interdir (const char *name, int delim, int perms); | ||
326 | |||
322 | /* Synchronisation on simultaneous access. */ | 327 | /* Synchronisation on simultaneous access. */ |
323 | extern int imap4d_sync (void); | 328 | extern int imap4d_sync (void); |
324 | extern int imap4d_sync_flags (size_t); | 329 | extern int imap4d_sync_flags (size_t); | ... | ... |
... | @@ -65,7 +65,8 @@ imap4d_lsub (struct imap4d_command *command, imap4d_tokbuf_t tok) | ... | @@ -65,7 +65,8 @@ imap4d_lsub (struct imap4d_command *command, imap4d_tokbuf_t tok) |
65 | mu_iterator_current_kv (itr, (const void **)&name, (void**)&val); | 65 | mu_iterator_current_kv (itr, (const void **)&name, (void**)&val); |
66 | 66 | ||
67 | if (util_wcard_match (name, pattern, delim) == 0) | 67 | if (util_wcard_match (name, pattern, delim) == 0) |
68 | io_untagged_response (RESP_NONE, "LIST () \"%s\" %s", delim, name); | 68 | io_untagged_response (RESP_NONE, "LSUB () \"%s\" \"%s\"", |
69 | delim, name); | ||
69 | } | 70 | } |
70 | } | 71 | } |
71 | else | 72 | else | ... | ... |
... | @@ -17,6 +17,74 @@ | ... | @@ -17,6 +17,74 @@ |
17 | 17 | ||
18 | #include "imap4d.h" | 18 | #include "imap4d.h" |
19 | 19 | ||
20 | int | ||
21 | make_interdir (const char *name, int delim, int perms) | ||
22 | { | ||
23 | int rc; | ||
24 | size_t i; | ||
25 | struct mu_wordsplit ws; | ||
26 | char *namebuf; | ||
27 | size_t namelen = 0; | ||
28 | char delimbuf[2]; | ||
29 | |||
30 | namebuf = malloc (strlen (name) + 1); | ||
31 | if (!namebuf) | ||
32 | imap4d_bye (ERR_NO_MEM); | ||
33 | if (name[0] == '/') | ||
34 | namebuf[namelen++] = name[0]; | ||
35 | |||
36 | delimbuf[0] = delim; | ||
37 | delimbuf[1] = 0; | ||
38 | ws.ws_delim = delimbuf; | ||
39 | if (mu_wordsplit (name, &ws, | ||
40 | MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS| | ||
41 | MU_WRDSF_NOVAR|MU_WRDSF_NOCMD)) | ||
42 | { | ||
43 | mu_error (_("cannot split line `%s': %s"), name, | ||
44 | mu_wordsplit_strerror (&ws)); | ||
45 | free (namebuf); | ||
46 | return 1; | ||
47 | } | ||
48 | |||
49 | rc = 0; | ||
50 | for (i = 0; rc == 0 && i < ws.ws_wordc - 1; i++) | ||
51 | { | ||
52 | struct stat st; | ||
53 | |||
54 | strcpy (namebuf + namelen, ws.ws_wordv[i]); | ||
55 | namelen += strlen (ws.ws_wordv[i]); | ||
56 | |||
57 | if (stat (namebuf, &st)) | ||
58 | { | ||
59 | if (errno == ENOENT) | ||
60 | { | ||
61 | if (mkdir (namebuf, perms)) | ||
62 | { | ||
63 | mu_error (_("cannot create directory %s: %s"), namebuf, | ||
64 | mu_strerror (errno)); | ||
65 | rc = 1; | ||
66 | } | ||
67 | } | ||
68 | else | ||
69 | { | ||
70 | mu_error (_("cannot stat file %s: %s"), | ||
71 | namebuf, mu_strerror (errno)); | ||
72 | rc = 1; | ||
73 | } | ||
74 | } | ||
75 | else if (!S_ISDIR (st.st_mode)) | ||
76 | { | ||
77 | mu_error (_("component %s is not a directory"), namebuf); | ||
78 | rc = 1; | ||
79 | } | ||
80 | namebuf[namelen++] = '/'; | ||
81 | } | ||
82 | |||
83 | mu_wordsplit_free (&ws); | ||
84 | free (namebuf); | ||
85 | return rc; | ||
86 | } | ||
87 | |||
20 | /* | 88 | /* |
21 | 6.3.5. RENAME Command | 89 | 6.3.5. RENAME Command |
22 | 90 | ||
... | @@ -71,6 +139,12 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok) | ... | @@ -71,6 +139,12 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok) |
71 | } | 139 | } |
72 | } | 140 | } |
73 | 141 | ||
142 | if (make_interdir (newname, delim[0], MKDIR_PERMISSIONS)) | ||
143 | { | ||
144 | free (newname); | ||
145 | return io_completion_response (command, RESP_NO, "Cannot rename"); | ||
146 | } | ||
147 | |||
74 | /* Renaming INBOX is permitted, and has special behavior. It moves | 148 | /* Renaming INBOX is permitted, and has special behavior. It moves |
75 | all messages in INBOX to a new mailbox with the given name, | 149 | all messages in INBOX to a new mailbox with the given name, |
76 | leaving INBOX empty. */ | 150 | leaving INBOX empty. */ |
... | @@ -119,7 +193,7 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok) | ... | @@ -119,7 +193,7 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok) |
119 | } | 193 | } |
120 | mu_mailbox_close (newmbox); | 194 | mu_mailbox_close (newmbox); |
121 | mu_mailbox_destroy (&newmbox); | 195 | mu_mailbox_destroy (&newmbox); |
122 | return io_completion_response (command, RESP_OK, "Already exist"); | 196 | return io_completion_response (command, RESP_OK, "Rename successful"); |
123 | } | 197 | } |
124 | 198 | ||
125 | oldname = namespace_getfullpath (oldname, delim, NULL); | 199 | oldname = namespace_getfullpath (oldname, delim, NULL); |
... | @@ -127,14 +201,25 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok) | ... | @@ -127,14 +201,25 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok) |
127 | /* It must exist. */ | 201 | /* It must exist. */ |
128 | /* FIXME: 1. What if odlname or newname is a remote mailbox? | 202 | /* FIXME: 1. What if odlname or newname is a remote mailbox? |
129 | 2. If newname is local and is in another namespace, its | 203 | 2. If newname is local and is in another namespace, its |
130 | permissions must be fixed */ | 204 | permissions must be fixed. |
131 | if (!oldname || rename (oldname, newname) != 0) | 205 | 3. All in all, it would perhaps be better to use the same |
206 | algorithm as for INBOX, and delete source mailbox afterwards. | ||
207 | */ | ||
208 | if (!oldname) | ||
209 | { | ||
210 | rc = RESP_NO; | ||
211 | msg = "Failed"; | ||
212 | } | ||
213 | else | ||
132 | { | 214 | { |
215 | if (rename (oldname, newname) != 0) | ||
216 | { | ||
217 | mu_diag_funcall (MU_DIAG_ERROR, "rename", oldname, errno); | ||
133 | rc = RESP_NO; | 218 | rc = RESP_NO; |
134 | msg = "Failed"; | 219 | msg = "Failed"; |
135 | } | 220 | } |
136 | if (oldname) | ||
137 | free (oldname); | 221 | free (oldname); |
222 | } | ||
138 | free (newname); | 223 | free (newname); |
139 | return io_completion_response (command, rc, msg); | 224 | return io_completion_response (command, rc, msg); |
140 | } | 225 | } | ... | ... |
... | @@ -44,7 +44,7 @@ echo "" >> expout.0 | ... | @@ -44,7 +44,7 @@ echo "" >> expout.0 |
44 | 44 | ||
45 | echo "ENVELOPE FROM: gulliver@example.net" > expout.1 | 45 | echo "ENVELOPE FROM: gulliver@example.net" > expout.1 |
46 | echo "ENVELOPE TO: <foo@bar.baz>" >> expout.1 | 46 | echo "ENVELOPE TO: <foo@bar.baz>" >> expout.1 |
47 | awk '{printf("% 4d: %s\n", NR-1,$0)}' $abs_top_srcdir/maidag/tests/input.msg >> expout.1 | 47 | awk '{printf("% 4d:",NR-1); if (NF!=0) printf(" %s",$0); print ""}' $abs_top_srcdir/maidag/tests/input.msg >> expout.1 |
48 | echo "END OF MESSAGE" >> expout.1 | 48 | echo "END OF MESSAGE" >> expout.1 |
49 | 49 | ||
50 | MTA_DIAG=mta.diag | 50 | MTA_DIAG=mta.diag | ... | ... |
-
Please register or sign in to post a comment