Commit 435f1918 435f1918df505d58b8cd3fea9b3ab6bba296faa9 by Sergey Poznyakoff

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).
1 parent 6c615a02
...@@ -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
......