1 // Implements the STORE command in IMAP.
3 // Copyright (c) 1987-2022 by the citadel.org team
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.
8 #include "../../ctdl_module.h"
17 #include <sys/types.h>
23 #include <libcitadel.h>
24 #include "../../citadel.h"
25 #include "../../server.h"
26 #include "../../sysdep_decls.h"
27 #include "../../citserver.h"
28 #include "../../support.h"
29 #include "../../config.h"
30 #include "../../user_ops.h"
31 #include "../../database.h"
32 #include "../../room_ops.h"
33 #include "../../msgbase.h"
34 #include "../../internet_addressing.h"
35 #include "serv_imap.h"
36 #include "imap_tools.h"
37 #include "imap_fetch.h"
38 #include "imap_store.h"
39 #include "../../genstamp.h"
42 // imap_do_store() calls imap_do_store_msg() to tweak the settings of
43 // an individual message.
45 // We also implement the ".SILENT" protocol option here. :(
46 void imap_do_store_msg(int seq, const char *oper, unsigned int bits_to_twiddle) {
49 if (!strncasecmp(oper, "FLAGS", 5)) {
50 Imap->flags[seq] &= IMAP_MASK_SYSTEM;
51 Imap->flags[seq] |= bits_to_twiddle;
53 else if (!strncasecmp(oper, "+FLAGS", 6)) {
54 Imap->flags[seq] |= bits_to_twiddle;
56 else if (!strncasecmp(oper, "-FLAGS", 6)) {
57 Imap->flags[seq] &= (~bits_to_twiddle);
62 // imap_store() calls imap_do_store() to perform the actual bit twiddling
64 void imap_do_store(citimap_command *Cmd) {
66 unsigned int bits_to_twiddle = 0;
74 int last_item_twiddled = (-1);
77 if (Cmd->num_parms < 2) return;
78 oper = Cmd->Params[0].Key;
79 if (cbmstrcasestr(oper, ".SILENT")) {
83 // ss_msglist is an array of message numbers to manipulate. We are going to supply this array to CtdlSetSeen() later.
84 ss_msglist = malloc(Imap->num_msgs * sizeof(long));
85 if (ss_msglist == NULL) return;
87 // Ok, go ahead and parse the flags.
88 for (i=1; i<Cmd->num_parms; ++i) {
89 strcpy(whichflags, Cmd->Params[i].Key);
90 if (whichflags[0]=='(') {
91 safestrncpy(whichflags, &whichflags[1],
94 if (whichflags[strlen(whichflags)-1]==')') {
95 whichflags[strlen(whichflags)-1]=0;
97 string_trim(whichflags);
99 // A client might twiddle more than one bit at a time.
100 // Note that we check for the flag names without the leading
101 // backslash because imap_parameterize() strips them out.
102 num_flags = num_tokens(whichflags, ' ');
103 for (j=0; j<num_flags; ++j) {
104 extract_token(flag, whichflags, j, ' ', sizeof flag);
106 if ((!strcasecmp(flag, "\\Deleted"))
107 || (!strcasecmp(flag, "Deleted"))) {
108 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
109 bits_to_twiddle |= IMAP_DELETED;
112 if ((!strcasecmp(flag, "\\Seen"))
113 || (!strcasecmp(flag, "Seen"))) {
114 bits_to_twiddle |= IMAP_SEEN;
116 if ((!strcasecmp(flag, "\\Answered"))
117 || (!strcasecmp(flag, "Answered"))) {
118 bits_to_twiddle |= IMAP_ANSWERED;
123 if (Imap->num_msgs > 0) {
124 for (i = 0; i < Imap->num_msgs; ++i) {
125 if (Imap->flags[i] & IMAP_SELECTED) {
126 last_item_twiddled = i;
128 ss_msglist[num_ss++] = Imap->msgids[i];
129 imap_do_store_msg(i, oper, bits_to_twiddle);
132 IAPrintf("* %d FETCH (", i+1);
141 // Now manipulate the database -- all in one shot.
142 if ( (last_item_twiddled >= 0) && (num_ss > 0) ) {
144 if (bits_to_twiddle & IMAP_SEEN) {
145 CtdlSetSeen(ss_msglist, num_ss,
146 ((Imap->flags[last_item_twiddled] & IMAP_SEEN) ? 1 : 0),
152 if (bits_to_twiddle & IMAP_ANSWERED) {
153 CtdlSetSeen(ss_msglist, num_ss,
154 ((Imap->flags[last_item_twiddled] & IMAP_ANSWERED) ? 1 : 0),
155 ctdlsetseen_answered,
163 imap_do_expunge(); // Citadel always expunges immediately.
164 imap_rescan_msgids();
168 // This function is called by the main command loop.
169 void imap_store(int num_parms, ConstStr *Params) {
174 IReply("BAD invalid parameters");
178 if (imap_is_message_set(Params[2].Key)) {
179 imap_pick_range(Params[2].Key, 0);
182 IReply("BAD invalid parameters");
186 memset(&Cmd, 0, sizeof(citimap_command));
187 Cmd.CmdBuf = NewStrBufPlain(NULL, StrLength(IMAP->Cmd.CmdBuf));
188 MakeStringOf(Cmd.CmdBuf, 3);
190 num_items = imap_extract_data_items(&Cmd);
192 IReply("BAD invalid data item list");
193 FreeStrBuf(&Cmd.CmdBuf);
199 IReply("OK STORE completed");
200 FreeStrBuf(&Cmd.CmdBuf);
204 // This function is called by the main command loop.
205 void imap_uidstore(int num_parms, ConstStr *Params) {
210 IReply("BAD invalid parameters");
214 if (imap_is_message_set(Params[3].Key)) {
215 imap_pick_range(Params[3].Key, 1);
218 IReply("BAD invalid parameters");
222 memset(&Cmd, 0, sizeof(citimap_command));
223 Cmd.CmdBuf = NewStrBufPlain(NULL, StrLength(IMAP->Cmd.CmdBuf));
224 MakeStringOf(Cmd.CmdBuf, 4);
226 num_items = imap_extract_data_items(&Cmd);
228 IReply("BAD invalid data item list");
229 FreeStrBuf(&Cmd.CmdBuf);
235 IReply("OK UID STORE completed");
236 FreeStrBuf(&Cmd.CmdBuf);