7965322e0b103ae3919aabaef58abda38ef8600b
[citadel.git] / citadel / server / modules / imap / imap_acl.c
1 // Functions which implement RFC2086 (and maybe RFC4314) (IMAP ACL extension)
2 //
3 // Copyright (c) 2007-2023 by the citadel.org team
4 //
5 //  This program is open source software.  Use, duplication or disclosure
6 //  is subject to the terms of the GNU General Public License, version 3.
7
8 #include "../../sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <pwd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <time.h>
18 #include <sys/wait.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <limits.h>
22 #include <libcitadel.h>
23 #include "../../citadel_defs.h"
24 #include "../../server.h"
25 #include "../../sysdep_decls.h"
26 #include "../../citserver.h"
27 #include "../../support.h"
28 #include "../../config.h"
29 #include "../../user_ops.h"
30 #include "../../database.h"
31 #include "../../msgbase.h"
32 #include "../../internet_addressing.h"
33 #include "serv_imap.h"
34 #include "imap_tools.h"
35 #include "imap_fetch.h"
36 #include "imap_misc.h"
37 #include "../../genstamp.h"
38 #include "../../ctdl_module.h"
39
40
41 // Implements the SETACL command.
42 void imap_setacl(int num_parms, ConstStr *Params) {
43         IReply("BAD not yet implemented FIXME");
44         return;
45 }
46
47
48 // Implements the DELETEACL command.
49 void imap_deleteacl(int num_parms, ConstStr *Params) {
50         IReply("BAD not yet implemented FIXME");
51         return;
52 }
53
54
55 // Given the bits returned by CtdlRoomAccess(), populate a string buffer
56 // with IMAP ACL format flags.   This code is common to GETACL and MYRIGHTS.
57 void imap_acl_flags(StrBuf *rights, int ra) {
58         FlushStrBuf(rights);
59
60         // l - lookup (mailbox is visible to LIST/LSUB commands)
61         // r - read (SELECT the mailbox, perform STATUS et al)
62         // s - keep seen/unseen information across sessions (STORE SEEN flag)
63         if (    (ra & UA_KNOWN)                                 // known rooms
64            ||   ((ra & UA_GOTOALLOWED) && (ra & UA_ZAPPED))     // zapped rooms
65         ) {
66                 StrBufAppendBufPlain(rights, HKEY("l"), 0);
67                 StrBufAppendBufPlain(rights, HKEY("r"), 0);
68                 StrBufAppendBufPlain(rights, HKEY("s"), 0);
69
70                 // Only output the remaining flags if the room is known
71
72                 // w - write (set or clear flags other than SEEN or DELETED, not supported in Citadel
73
74                 // i - insert (perform APPEND, COPY into mailbox)
75                 // p - post (send mail to submission address for mailbox - not enforced)
76                 // c - create (CREATE new sub-mailboxes)
77                 if (ra & UA_POSTALLOWED) {
78                         StrBufAppendBufPlain(rights, HKEY("i"), 0);
79                         StrBufAppendBufPlain(rights, HKEY("p"), 0);
80                         StrBufAppendBufPlain(rights, HKEY("c"), 0);
81                 }
82
83                 // d - delete messages (STORE DELETED flag, perform EXPUNGE)
84                 if (ra & UA_DELETEALLOWED) {
85                         StrBufAppendBufPlain(rights, HKEY("d"), 0);
86                 }
87
88                 // a - administer (perform SETACL/DELETEACL/GETACL/LISTRIGHTS)
89                 if (ra & UA_ADMINALLOWED) {
90                         // This is the correct place to put the "a" flag.  We are leaving
91                         // it commented out for now, because it implies that we could
92                         // perform any of SETACL/DELETEACL/GETACL/LISTRIGHTS.  Since these
93                         // commands are not yet implemented, omitting the flag should
94                         // theoretically prevent compliant clients from attempting to
95                         // perform them.
96                         //
97                         // StrBufAppendBufPlain(rights, HKEY("a"), 0);
98                 }
99         }
100 }
101
102
103 // Implements the GETACL command.
104 void imap_getacl(int num_parms, ConstStr *Params) {
105         char roomname[ROOMNAMELEN];
106         char savedroom[ROOMNAMELEN];
107         int msgs, new;
108         int ret;
109         struct ctdluser temp;
110         struct cdbkeyval cdbus;
111         int ra;
112         StrBuf *rights;
113
114         if (num_parms != 3) {
115                 IReply("BAD usage error");
116                 return;
117         }
118
119         // Search for the specified room or folder
120         ret = imap_grabroom(roomname, Params[2].Key, 1);
121         if (ret != 0) {
122                 IReply("NO Invalid mailbox name or access denied");
123                 return;
124         }
125
126         // CtdlUserGoto() formally takes us to the desired room.  (If another
127         // folder is selected, save its name so we can return there!!!!!)
128         if (IMAP->selected) {
129                 strcpy(savedroom, CC->room.QRname);
130         }
131         CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
132
133         IAPuts("* ACL ");
134         IPutCParamStr(2);
135
136         // Traverse the userlist
137         rights = NewStrBuf();
138         cdb_rewind(CDB_USERS);
139         while (cdbus = cdb_next_item(CDB_USERS), cdbus.val.ptr!=NULL) {         // always read through to the end
140                 memset(&temp, 0, sizeof temp);
141                 memcpy(&temp, cdbus.val.ptr, sizeof temp);
142
143                 CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
144                 if (!IsEmptyStr(temp.fullname)) {
145                         imap_acl_flags(rights, ra);
146                         if (StrLength(rights) > 0) {
147                                 IAPuts(" ");
148                                 IPutStr(temp.fullname, strlen(temp.fullname));
149                                 IAPuts(" ");
150                                 iaputs(SKEY( rights));
151                         }
152                 }
153         }
154         FreeStrBuf(&rights);
155         IAPuts("\r\n");
156
157         // If another folder is selected, go back to that room so we can resume
158         // our happy day without violent explosions.
159         if (IMAP->selected) {
160                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
161         }
162
163         IReply("OK GETACL completed");
164 }
165
166
167 // Implements the LISTRIGHTS command.
168 void imap_listrights(int num_parms, ConstStr *Params) {
169         char roomname[ROOMNAMELEN];
170         char savedroom[ROOMNAMELEN];
171         int msgs, new;
172         int ret;
173         struct recptypes *valid;
174         struct ctdluser temp;
175
176         if (num_parms != 4) {
177                 IReply("BAD usage error");
178                 return;
179         }
180
181         // Search for the specified room/folder
182         ret = imap_grabroom(roomname, Params[2].Key, 1);
183         if (ret != 0) {
184                 IReply("NO Invalid mailbox name or access denied");
185                 return;
186         }
187
188         // Search for the specified user
189         ret = (-1);
190         valid = validate_recipients(Params[3].Key, NULL, 0);
191         if (valid != NULL) {
192                 if (valid->num_local == 1) {
193                         ret = CtdlGetUser(&temp, valid->recp_local);
194                 }
195                 free_recipients(valid);
196         }
197         if (ret != 0) {
198                 IReply("NO Invalid user name or access denied");
199                 return;
200         }
201
202         // CtdlUserGoto() formally takes us to the desired room.  (If another
203         // folder is selected, save its name so we can return there!!!!!)
204         if (IMAP->selected) {
205                 strcpy(savedroom, CC->room.QRname);
206         }
207         CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
208
209         // Now output the list of rights
210         IAPuts("* LISTRIGHTS ");
211         IPutCParamStr(2);
212         IAPuts(" ");
213         IPutCParamStr(3);
214         IAPuts(" ");
215         IPutStr(HKEY(""));              // FIXME ... do something here
216         IAPuts("\r\n");
217
218         // If another folder is selected, go back to that room so we can resume
219         // our happy day without violent explosions.
220         if (IMAP->selected) {
221                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
222         }
223
224         IReply("OK LISTRIGHTS completed");
225         return;
226 }
227
228
229 // Implements the MYRIGHTS command.
230 void imap_myrights(int num_parms, ConstStr *Params) {
231         char roomname[ROOMNAMELEN];
232         char savedroom[ROOMNAMELEN];
233         int msgs, new;
234         int ret;
235         int ra;
236         StrBuf *rights;
237
238         if (num_parms != 3) {
239                 IReply("BAD usage error");
240                 return;
241         }
242
243         ret = imap_grabroom(roomname, Params[2].Key, 1);
244         if (ret != 0) {
245                 IReply("NO Invalid mailbox name or access denied");
246                 return;
247         }
248
249         // CtdlUserGoto() formally takes us to the desired room.  (If another
250         // folder is selected, save its name so we can return there!!!!!)
251         if (IMAP->selected) {
252                 strcpy(savedroom, CC->room.QRname);
253         }
254         CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
255
256         CtdlRoomAccess(&CC->room, &CC->user, &ra, NULL);
257         rights = NewStrBuf();
258         imap_acl_flags(rights, ra);
259
260         IAPuts("* MYRIGHTS ");
261         IPutCParamStr(2);
262         IAPuts(" ");
263         IPutStr(SKEY(rights));
264         IAPuts("\r\n");
265
266         FreeStrBuf(&rights);
267
268         // If a different folder was previously selected, return there now.
269         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
270                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
271         }
272
273         IReply("OK MYRIGHTS completed");
274         return;
275 }