2 * IMAP METADATA extension
4 * This is an implementation of the Bynari variant of the METADATA extension.
6 * Copyright (c) 2007-2009 by the citadel.org team
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <sys/types.h>
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
39 # include <sys/time.h>
49 #include <libcitadel.h>
52 #include "sysdep_decls.h"
53 #include "citserver.h"
59 #include "internet_addressing.h"
60 #include "imap_tools.h"
61 #include "serv_imap.h"
62 #include "imap_fetch.h"
63 #include "imap_misc.h"
66 #include "ctdl_module.h"
69 * Implements the SETMETADATA command.
71 * Again, the only thing we're interested in setting here is the folder type.
73 * Attempting to set anything else calls a stub which fools the client into
74 * thinking that there is no remaining space available to store annotations.
76 void imap_setmetadata(int num_parms, ConstStr *Params) {
77 char roomname[ROOMNAMELEN];
78 char savedroom[ROOMNAMELEN];
81 int setting_user_value = 0;
83 int set_view = VIEW_BBS;
87 cprintf("%s BAD usage error\r\n", Params[0].Key);
92 * Don't allow other types of metadata to be set
94 if (strcasecmp(Params[3].Key, "/vendor/kolab/folder-type")) {
95 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", Params[0].Key);
99 if (!strcasecmp(Params[4].Key, "(value.shared")) {
100 setting_user_value = 0; /* global view */
102 else if (!strcasecmp(Params[4].Key, "(value.priv")) {
103 setting_user_value = 1; /* per-user view */
106 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", Params[0].Key);
111 * Extract the folder type without any parentheses. Then learn
112 * the Citadel view type based on the supplied folder type.
114 extract_token(set_value, Params[5].Key, 0, ')', sizeof set_value);
115 if (!strncasecmp(set_value, "mail", 4)) {
116 set_view = VIEW_MAILBOX;
118 else if (!strncasecmp(set_value, "event", 5)) {
119 set_view = VIEW_CALENDAR;
121 else if (!strncasecmp(set_value, "contact", 7)) {
122 set_view = VIEW_ADDRESSBOOK;
124 else if (!strncasecmp(set_value, "journal", 7)) {
125 set_view = VIEW_JOURNAL;
127 else if (!strncasecmp(set_value, "note", 4)) {
128 set_view = VIEW_NOTES;
130 else if (!strncasecmp(set_value, "task", 4)) {
131 set_view = VIEW_TASKS;
134 set_view = VIEW_MAILBOX;
137 ret = imap_grabroom(roomname, Params[2].Key, 1);
139 cprintf("%s NO Invalid mailbox name or access denied\r\n",
145 * CtdlUserGoto() formally takes us to the desired room. (If another
146 * folder is selected, save its name so we can return there!!!!!)
148 if (IMAP->selected) {
149 strcpy(savedroom, CC->room.QRname);
151 CtdlUserGoto(roomname, 0, 0, &msgs, &new);
154 * Always set the per-user view to the requested one.
156 CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
157 vbuf.v_view = set_view;
158 CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
160 /* If this is a "value.priv" set operation, we're done. */
162 if (setting_user_value)
164 cprintf("%s OK SETANNOTATION complete\r\n", Params[0].Key);
167 /* If this is a "value.shared" set operation, we are allowed to perform it
168 * under certain conditions.
170 else if ( (is_room_aide()) /* aide or room aide */
171 || ( (CC->room.QRflags & QR_MAILBOX)
172 && (CC->user.usernum == atol(CC->room.QRname)) /* mailbox owner */
174 || (msgs == 0) /* hack: if room is empty, assume we just created it */
176 CtdlGetRoomLock(&CC->room, CC->room.QRname);
177 CC->room.QRdefaultview = set_view;
178 CtdlPutRoomLock(&CC->room);
179 cprintf("%s OK SETANNOTATION complete\r\n", Params[0].Key);
182 /* If we got to this point, we don't have permission to set the default view. */
184 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", Params[0].Key);
188 * If a different folder was previously selected, return there now.
190 if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
191 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
198 * Implements the GETMETADATA command.
200 * Regardless of what the client asked for, we are going to supply them with
201 * the folder type. It's the only metadata we have anyway.
203 void imap_getmetadata(int num_parms, ConstStr *Params) {
204 char roomname[ROOMNAMELEN];
205 char savedroom[ROOMNAMELEN];
210 cprintf("%s BAD usage error\r\n", Params[0].Key);
214 ret = imap_grabroom(roomname, Params[2].Key, 1);
216 cprintf("%s NO Invalid mailbox name or access denied\r\n",
222 * CtdlUserGoto() formally takes us to the desired room. (If another
223 * folder is selected, save its name so we can return there!!!!!)
225 if (IMAP->selected) {
226 strcpy(savedroom, CC->room.QRname);
228 CtdlUserGoto(roomname, 0, 0, &msgs, &new);
230 cprintf("* METADATA ");
231 imap_strout(&Params[2]);
232 cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
234 /* If it's one of our hard-coded default rooms, we know what to do... */
236 if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
237 cprintf("mail.inbox");
239 else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
240 cprintf("mail.sentitems");
242 else if (!strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) {
243 cprintf("mail.drafts");
245 else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
246 cprintf("event.default");
248 else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
249 cprintf("contact.default");
251 else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
252 cprintf("note.default");
254 else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
255 cprintf("task.default");
258 /* Otherwise, use the view for this room to determine the type of data.
259 * We are going with the default view rather than the user's view, because
260 * the default view almost always defines the actual contents, while the
261 * user's view might only make changes to presentation. It also saves us
262 * an extra database access because we don't need to load the visit record.
265 else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
268 else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
271 else if (CC->room.QRdefaultview == VIEW_TASKS) {
274 else if (CC->room.QRdefaultview == VIEW_NOTES) {
277 else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
281 /* If none of the above conditions were met, consider it an ordinary mailbox. */
286 /* "mail.outbox" and "junkemail" are not implemented. */
291 * If a different folder was previously selected, return there now.
293 if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
294 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
297 cprintf("%s OK GETMETADATA complete\r\n", Params[0].Key);