Back to index

citadel  8.12
imap_metadata.c
Go to the documentation of this file.
00001 /*
00002  * IMAP METADATA extension
00003  *
00004  * This is an implementation of the Bynari variant of the METADATA extension.
00005  *
00006  * Copyright (c) 2007-2009 by the citadel.org team
00007  *
00008  *  This program is open source software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 3 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00023 
00024 #include "sysdep.h"
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 #include <stdio.h>
00028 #include <fcntl.h>
00029 #include <signal.h>
00030 #include <pwd.h>
00031 #include <errno.h>
00032 #include <sys/types.h>
00033 
00034 #if TIME_WITH_SYS_TIME
00035 # include <sys/time.h>
00036 # include <time.h>
00037 #else
00038 # if HAVE_SYS_TIME_H
00039 #  include <sys/time.h>
00040 # else
00041 #  include <time.h>
00042 # endif
00043 #endif
00044 
00045 #include <sys/wait.h>
00046 #include <ctype.h>
00047 #include <string.h>
00048 #include <limits.h>
00049 #include <libcitadel.h>
00050 #include "citadel.h"
00051 #include "server.h"
00052 #include "sysdep_decls.h"
00053 #include "citserver.h"
00054 #include "support.h"
00055 #include "config.h"
00056 #include "user_ops.h"
00057 #include "database.h"
00058 #include "msgbase.h"
00059 #include "internet_addressing.h"
00060 #include "serv_imap.h"
00061 #include "imap_tools.h"
00062 #include "imap_fetch.h"
00063 #include "imap_misc.h"
00064 #include "genstamp.h"
00065 
00066 #include "ctdl_module.h"
00067 
00068 /*
00069  * Implements the SETMETADATA command.
00070  *
00071  * Again, the only thing we're interested in setting here is the folder type.
00072  *
00073  * Attempting to set anything else calls a stub which fools the client into
00074  * thinking that there is no remaining space available to store annotations.
00075  */
00076 void imap_setmetadata(int num_parms, ConstStr *Params) {
00077        char roomname[ROOMNAMELEN];
00078        char savedroom[ROOMNAMELEN];
00079        int msgs, new;
00080        int ret;
00081        int setting_user_value = 0;
00082        char set_value[32];
00083        int set_view = VIEW_BBS;
00084        visit vbuf;
00085 
00086        if (num_parms != 6) {
00087               IReply("BAD usage error");
00088               return;
00089        }
00090 
00091        /*
00092         * Don't allow other types of metadata to be set
00093         */
00094        if (strcasecmp(Params[3].Key, "/vendor/kolab/folder-type")) {
00095               IReply("NO [METADATA TOOMANY] SETMETADATA failed");
00096               return;
00097        }
00098 
00099        if (!strcasecmp(Params[4].Key, "(value.shared")) {
00100               setting_user_value = 0;                          /* global view */
00101        }
00102        else if (!strcasecmp(Params[4].Key, "(value.priv")) {
00103               setting_user_value = 1;                          /* per-user view */
00104        }
00105        else {
00106               IReply("NO [METADATA TOOMANY] SETMETADATA failed");
00107               return;
00108        }
00109 
00110        /*
00111         * Extract the folder type without any parentheses.  Then learn
00112         * the Citadel view type based on the supplied folder type.
00113         */
00114        extract_token(set_value, Params[5].Key, 0, ')', sizeof set_value);
00115        if (!strncasecmp(set_value, "mail", 4)) {
00116               set_view = VIEW_MAILBOX;
00117        }
00118        else if (!strncasecmp(set_value, "event", 5)) {
00119               set_view = VIEW_CALENDAR;
00120        }
00121        else if (!strncasecmp(set_value, "contact", 7)) {
00122               set_view = VIEW_ADDRESSBOOK;
00123        }
00124        else if (!strncasecmp(set_value, "journal", 7)) {
00125               set_view = VIEW_JOURNAL;
00126        }
00127        else if (!strncasecmp(set_value, "note", 4)) {
00128               set_view = VIEW_NOTES;
00129        }
00130        else if (!strncasecmp(set_value, "task", 4)) {
00131               set_view = VIEW_TASKS;
00132        }
00133        else {
00134               set_view = VIEW_MAILBOX;
00135        }
00136 
00137        ret = imap_grabroom(roomname, Params[2].Key, 1);
00138        if (ret != 0) {
00139               IReply("NO Invalid mailbox name or access denied");
00140               return;
00141        }
00142 
00143        /*
00144         * CtdlUserGoto() formally takes us to the desired room.  (If another
00145         * folder is selected, save its name so we can return there!!!!!)
00146         */
00147        if (IMAP->selected) {
00148               strcpy(savedroom, CC->room.QRname);
00149        }
00150        CtdlUserGoto(roomname, 0, 0, &msgs, &new);
00151 
00152        /*
00153         * Always set the per-user view to the requested one.
00154         */
00155        CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
00156        vbuf.v_view = set_view;
00157        CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
00158 
00159        /* If this is a "value.priv" set operation, we're done. */
00160 
00161        if (setting_user_value)
00162        {
00163               IReply("OK SETANNOTATION complete");
00164        }
00165 
00166        /* If this is a "value.shared" set operation, we are allowed to perform it
00167         * under certain conditions.
00168         */
00169        else if (     (is_room_aide())                                 /* aide or room aide */
00170               ||     (      (CC->room.QRflags & QR_MAILBOX)
00171                      &&     (CC->user.usernum == atol(CC->room.QRname))      /* mailbox owner */
00172                      )
00173               ||     (msgs == 0)          /* hack: if room is empty, assume we just created it */
00174        ) {
00175               CtdlGetRoomLock(&CC->room, CC->room.QRname);
00176               CC->room.QRdefaultview = set_view;
00177               CtdlPutRoomLock(&CC->room);
00178               IReply("OK SETANNOTATION complete");
00179        }
00180 
00181        /* If we got to this point, we don't have permission to set the default view. */
00182        else {
00183               IReply("NO [METADATA TOOMANY] SETMETADATA failed");
00184        }
00185 
00186        /*
00187         * If a different folder was previously selected, return there now.
00188         */
00189        if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
00190               CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
00191        }
00192        return;
00193 }
00194 
00195 
00196 /*
00197  * Implements the GETMETADATA command.
00198  *
00199  * Regardless of what the client asked for, we are going to supply them with
00200  * the folder type.  It's the only metadata we have anyway.
00201  */
00202 void imap_getmetadata(int num_parms, ConstStr *Params) {
00203        char roomname[ROOMNAMELEN];
00204        char savedroom[ROOMNAMELEN];
00205        int msgs, new;
00206        int ret;
00207        int found = 0;
00208 
00209 /* this doesn't work if you have rooms/floors with spaces. 
00210    we need this for the bynari connector.
00211        if (num_parms > 5) {
00212               IReply("BAD usage error");
00213               return;
00214        }
00215 */
00216        ret = imap_grabroom(roomname, Params[2].Key, 1);
00217        if (ret != 0) {
00218               IReply("NO Invalid mailbox name or access denied");
00219               return;
00220        }
00221 
00222        /*
00223         * CtdlUserGoto() formally takes us to the desired room.  (If another
00224         * folder is selected, save its name so we can return there!!!!!)
00225         */
00226        if (IMAP->selected) {
00227               strcpy(savedroom, CC->room.QRname);
00228        }
00229        CtdlUserGoto(roomname, 0, 0, &msgs, &new);
00230 
00231        IAPuts("* METADATA ");
00232        IPutCParamStr(2);
00233        IAPuts(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
00234 
00235        /* If it's one of our hard-coded default rooms, we know what to do... */
00236 
00237        if (CC->room.QRname[10] == '.')
00238        {
00239               if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
00240                      found = 1;
00241                      IAPuts("mail.inbox");
00242               }
00243               else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
00244                      found = 1;
00245                      IAPuts("mail.sentitems");
00246               }
00247               else if (!strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) {
00248                      found = 1;
00249                      IAPuts("mail.drafts");
00250               }
00251               else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
00252                      found = 1;
00253                      IAPuts("event.default");
00254               }
00255               else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
00256                      found = 1;
00257                      IAPuts("contact.default");
00258               }
00259               else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
00260                      found = 1;
00261                      IAPuts("note.default");
00262               }
00263               else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
00264                      found = 1;
00265                      IAPuts("task.default");
00266               }
00267        }
00268 
00269        /* Otherwise, use the view for this room to determine the type of data.
00270         * We are going with the default view rather than the user's view, because
00271         * the default view almost always defines the actual contents, while the
00272         * user's view might only make changes to presentation.  It also saves us
00273         * an extra database access because we don't need to load the visit record.
00274         */
00275        if (!found)
00276        {
00277               if (CC->room.QRdefaultview == VIEW_CALENDAR) {
00278                      IAPuts("event");
00279               }
00280               else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
00281                      IAPuts("contact");
00282               }
00283               else if (CC->room.QRdefaultview == VIEW_TASKS) {
00284                      IAPuts("task");
00285               }
00286               else if (CC->room.QRdefaultview == VIEW_NOTES) {
00287                      IAPuts("note");
00288               }
00289               else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
00290                      IAPuts("journal");
00291               }
00292        }
00293        /* If none of the above conditions were met, consider it an ordinary mailbox. */
00294 
00295        if (!found) {
00296               IAPuts("mail");
00297        }
00298 
00299        /* "mail.outbox" and "junkemail" are not implemented. */
00300 
00301        IAPuts("\")\r\n");
00302 
00303        /*
00304         * If a different folder was previously selected, return there now.
00305         */
00306        if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
00307               CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
00308        }
00309 
00310        IReply("OK GETMETADATA complete");
00311        return;
00312 }
00313