Back to index

citadel  8.12
imap_store.c
Go to the documentation of this file.
00001 /*
00002  * Implements the STORE command in IMAP.
00003  *
00004  * Copyright (c) 2001-2009 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 #include "ctdl_module.h"
00022 
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <stdio.h>
00026 #include <fcntl.h>
00027 #include <signal.h>
00028 #include <pwd.h>
00029 #include <errno.h>
00030 #include <sys/types.h>
00031 
00032 #if TIME_WITH_SYS_TIME
00033 # include <sys/time.h>
00034 # include <time.h>
00035 #else
00036 # if HAVE_SYS_TIME_H
00037 #  include <sys/time.h>
00038 # else
00039 #  include <time.h>
00040 # endif
00041 #endif
00042 
00043 #include <sys/wait.h>
00044 #include <ctype.h>
00045 #include <string.h>
00046 #include <limits.h>
00047 #include <libcitadel.h>
00048 #include "citadel.h"
00049 #include "server.h"
00050 #include "sysdep_decls.h"
00051 #include "citserver.h"
00052 #include "support.h"
00053 #include "config.h"
00054 #include "user_ops.h"
00055 #include "database.h"
00056 #include "msgbase.h"
00057 #include "internet_addressing.h"
00058 #include "serv_imap.h"
00059 #include "imap_tools.h"
00060 #include "imap_fetch.h"
00061 #include "imap_store.h"
00062 #include "genstamp.h"
00063 
00064 
00065 /*
00066  * imap_do_store() calls imap_do_store_msg() to tweak the settings of
00067  * an individual message.
00068  *
00069  * We also implement the ".SILENT" protocol option here.  :(
00070  */
00071 void imap_do_store_msg(int seq, const char *oper, unsigned int bits_to_twiddle) {
00072        citimap *Imap = IMAP;
00073 
00074        if (!strncasecmp(oper, "FLAGS", 5)) {
00075               Imap->flags[seq] &= IMAP_MASK_SYSTEM;
00076               Imap->flags[seq] |= bits_to_twiddle;
00077        }
00078        else if (!strncasecmp(oper, "+FLAGS", 6)) {
00079               Imap->flags[seq] |= bits_to_twiddle;
00080        }
00081        else if (!strncasecmp(oper, "-FLAGS", 6)) {
00082               Imap->flags[seq] &= (~bits_to_twiddle);
00083        }
00084 }
00085 
00086 
00087 /*
00088  * imap_store() calls imap_do_store() to perform the actual bit twiddling
00089  * on the flags.
00090  */
00091 void imap_do_store(citimap_command *Cmd) {
00092        int i, j;
00093        unsigned int bits_to_twiddle = 0;
00094        const char *oper;
00095        char flag[32];
00096        char whichflags[256];
00097        char num_flags;
00098        int silent = 0;
00099        long *ss_msglist;
00100        int num_ss = 0;
00101        int last_item_twiddled = (-1);
00102        citimap *Imap = IMAP;
00103 
00104        if (Cmd->num_parms < 2) return;
00105        oper = Cmd->Params[0].Key;
00106        if (cbmstrcasestr(oper, ".SILENT")) {
00107               silent = 1;
00108        }
00109 
00110        /*
00111         * ss_msglist is an array of message numbers to manipulate.  We
00112         * are going to supply this array to CtdlSetSeen() later.
00113         */
00114        ss_msglist = malloc(Imap->num_msgs * sizeof(long));
00115        if (ss_msglist == NULL) return;
00116 
00117        /*
00118         * Ok, go ahead and parse the flags.
00119         */
00120        for (i=1; i<Cmd->num_parms; ++i) {
00121               strcpy(whichflags, Cmd->Params[i].Key);
00122               if (whichflags[0]=='(') {
00123                      safestrncpy(whichflags, &whichflags[1], 
00124                             sizeof whichflags);
00125               }
00126               if (whichflags[strlen(whichflags)-1]==')') {
00127                      whichflags[strlen(whichflags)-1]=0;
00128               }
00129               striplt(whichflags);
00130 
00131               /* A client might twiddle more than one bit at a time.
00132                * Note that we check for the flag names without the leading
00133                * backslash because imap_parameterize() strips them out.
00134                */
00135               num_flags = num_tokens(whichflags, ' ');
00136               for (j=0; j<num_flags; ++j) {
00137                      extract_token(flag, whichflags, j, ' ', sizeof flag);
00138 
00139                      if ((!strcasecmp(flag, "\\Deleted"))
00140                         || (!strcasecmp(flag, "Deleted"))) {
00141                             if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
00142                                    bits_to_twiddle |= IMAP_DELETED;
00143                             }
00144                      }
00145                      if ((!strcasecmp(flag, "\\Seen"))
00146                         || (!strcasecmp(flag, "Seen"))) {
00147                             bits_to_twiddle |= IMAP_SEEN;
00148                      }
00149                      if ((!strcasecmp(flag, "\\Answered")) 
00150                         || (!strcasecmp(flag, "Answered"))) {
00151                             bits_to_twiddle |= IMAP_ANSWERED;
00152                      }
00153               }
00154        }
00155 
00156        if (Imap->num_msgs > 0) {
00157               for (i = 0; i < Imap->num_msgs; ++i) {
00158                      if (Imap->flags[i] & IMAP_SELECTED) {
00159                             last_item_twiddled = i;
00160 
00161                             ss_msglist[num_ss++] = Imap->msgids[i];
00162                             imap_do_store_msg(i, oper, bits_to_twiddle);
00163 
00164                             if (!silent) {
00165                                    IAPrintf("* %d FETCH (", i+1);
00166                                    imap_fetch_flags(i);
00167                                    IAPuts(")\r\n");
00168                             }
00169 
00170                      }
00171               }
00172        }
00173 
00174        /*
00175         * Now manipulate the database -- all in one shot.
00176         */
00177        if ( (last_item_twiddled >= 0) && (num_ss > 0) ) {
00178 
00179               if (bits_to_twiddle & IMAP_SEEN) {
00180                      CtdlSetSeen(ss_msglist, num_ss,
00181                             ((Imap->flags[last_item_twiddled] & IMAP_SEEN) ? 1 : 0),
00182                             ctdlsetseen_seen,
00183                             NULL, NULL
00184                      );
00185               }
00186 
00187               if (bits_to_twiddle & IMAP_ANSWERED) {
00188                      CtdlSetSeen(ss_msglist, num_ss,
00189                             ((Imap->flags[last_item_twiddled] & IMAP_ANSWERED) ? 1 : 0),
00190                             ctdlsetseen_answered,
00191                             NULL, NULL
00192                      );
00193               }
00194 
00195        }
00196 
00197        free(ss_msglist);
00198 
00199        /*
00200         * The following two commands implement "instant expunge" if enabled.
00201         */
00202        if (config.c_instant_expunge) {
00203               imap_do_expunge();
00204               imap_rescan_msgids();
00205        }
00206 }
00207 
00208 
00209 /*
00210  * This function is called by the main command loop.
00211  */
00212 void imap_store(int num_parms, ConstStr *Params) {
00213        citimap_command Cmd;
00214        int num_items;
00215 
00216        if (num_parms < 3) {
00217               IReply("BAD invalid parameters");
00218               return;
00219        }
00220 
00221        if (imap_is_message_set(Params[2].Key)) {
00222               imap_pick_range(Params[2].Key, 0);
00223        }
00224        else {
00225               IReply("BAD invalid parameters");
00226               return;
00227        }
00228 
00229        memset(&Cmd, 0, sizeof(citimap_command));
00230        Cmd.CmdBuf = NewStrBufPlain(NULL, StrLength(IMAP->Cmd.CmdBuf));
00231        MakeStringOf(Cmd.CmdBuf, 3);
00232 
00233        num_items = imap_extract_data_items(&Cmd);
00234        if (num_items < 1) {
00235               IReply("BAD invalid data item list");
00236               FreeStrBuf(&Cmd.CmdBuf);
00237               free(Cmd.Params);
00238               return;
00239        }
00240 
00241        imap_do_store(&Cmd);
00242        IReply("OK STORE completed");
00243        FreeStrBuf(&Cmd.CmdBuf);
00244        free(Cmd.Params);
00245 }
00246 
00247 /*
00248  * This function is called by the main command loop.
00249  */
00250 void imap_uidstore(int num_parms, ConstStr *Params) {
00251        citimap_command Cmd;
00252        int num_items;
00253 
00254        if (num_parms < 4) {
00255               IReply("BAD invalid parameters");
00256               return;
00257        }
00258 
00259        if (imap_is_message_set(Params[3].Key)) {
00260               imap_pick_range(Params[3].Key, 1);
00261        }
00262        else {
00263               IReply("BAD invalid parameters");
00264               return;
00265        }
00266 
00267        memset(&Cmd, 0, sizeof(citimap_command));
00268        Cmd.CmdBuf = NewStrBufPlain(NULL, StrLength(IMAP->Cmd.CmdBuf));
00269        MakeStringOf(Cmd.CmdBuf, 4);
00270 
00271        num_items = imap_extract_data_items(&Cmd);
00272        if (num_items < 1) {
00273               IReply("BAD invalid data item list");
00274               FreeStrBuf(&Cmd.CmdBuf);
00275               free(Cmd.Params);
00276               return;
00277        }
00278 
00279        imap_do_store(&Cmd);
00280        IReply("OK UID STORE completed");
00281        FreeStrBuf(&Cmd.CmdBuf);
00282        free(Cmd.Params);
00283 }
00284 
00285