Bincimap 2.0.16
Easy Imapping
Loading...
Searching...
No Matches
operator-idle.cc
Go to the documentation of this file.
1
7#include <unistd.h>
8
9#include <string>
10#include <iostream>
11
12#include "iodevice.h"
13#include "iofactory.h"
14#include "convert.h"
15#include "depot.h"
16#include "globals.h"
17#include "mailbox.h"
18#include "operators.h"
19#include "pendingupdates.h"
20#include "recursivedescent.h"
21#include "session.h"
22
23static bool directoryChangeNotification = false;
24
25#ifdef HAVE_FNOTIFY // GNU dependencies removed
26#include <sys/types.h>
27#include <sys/select.h>
28#include <stdio.h>
29#include <signal.h>
30#include <fcntl.h>
31
32void fnotifyEventHandler(int sig)
33{
34 directoryChangeNotification = true;
35}
36#endif
37
38using namespace ::std;
39using namespace Binc;
40
41// Seconds between each poll. With FNOTIFY support, we can idle for 30
42// minutes before timing out.
43#ifdef HAVE_FNOTIFY
44static const int POLLTIMEOUT = 30 * 60;
45#else
46static const int POLLTIMEOUT = 30;
47#endif
48
49//----------------------------------------------------------------------
51{
52}
53
54//----------------------------------------------------------------------
56{
57}
58
59//----------------------------------------------------------------------
60const string IdleOperator::getName(void) const
61{
62 return "IDLE";
63}
64
65//----------------------------------------------------------------------
67{
68 return Session::SELECTED;
69}
70
71//----------------------------------------------------------------------
73 Request &command)
74{
75 Mailbox *mailbox = depot.getSelected();
76 string mailboxDir = depot.mailboxToFilename(mailbox->getName());
77
78#ifdef HAVE_FNOTIFY
79 // Check for FNOTIFY support.
80 bool waitForNotification = false;
81 int newfd = open((mailboxDir + "/new").c_str(), O_RDONLY);
82 int curfd = open((mailboxDir + "/cur").c_str(), O_RDONLY);
83
84 // Watch for notifications for renames, deletes or creates.
85 if (newfd && curfd
86 && !fcntl(newfd, F_NOTIFY, DN_RENAME|DN_DELETE|DN_CREATE)
87 && !fcntl(curfd, F_NOTIFY, DN_RENAME|DN_DELETE|DN_CREATE)) {
88 struct sigaction fnotifyAction;
89 fnotifyAction.sa_handler = fnotifyEventHandler;
90 sigemptyset(&fnotifyAction.sa_mask);
91 fnotifyAction.sa_flags = SA_RESTART;
92 sigaction(SIGUSR1, &fnotifyAction, 0);
93 fcntl(newfd, F_SETSIG, SIGUSR1);
94 fcntl(curfd, F_SETSIG, SIGUSR1);
95 waitForNotification = true;
96 }
97#endif
98
99 // when not using FNOTIFY, we need to check the session timeout.
100 time_t startTime = time(0);
101#ifdef HAVE_FNOTIFY
102 (void)startTime; // removes a compile warning
103#endif
104
105 bincClient << "+ idling" << endl;
106 bincClient.flush();
107
108 // loop until the session times out or the client submits DONE.
109 for (;;) {
110 int maxfd = 0;
111 fd_set readfds;
112 FD_ZERO(&readfds);
113 FD_SET(0, &readfds);
114
115 // check for data from stdin. with FNOTIFY enabled, this select
116 // will be interrupted by the notification signal.
117 string input;
118 struct timeval tv = {POLLTIMEOUT, 0};
119 int ret = select(maxfd + 1, &readfds, 0, 0, &tv);
120
121 // check if the select timed out.
122 if (ret == 0) {
123 Session &session = Session::getInstance();
124#ifdef HAVE_FNOTIFY
125 if (waitForNotification) {
126 bincClient << "* BYE Timeout after " << session.timeout()
127 << " seconds of inactivity." << endl;
128 session.setState(Session::LOGOUT);
129 close(newfd);
130 close(curfd);
131 return NOTHING;
132 } else
133#endif
134 if (time(0) > startTime + IDLE_TIMEOUT) {
135 bincClient << "* BYE Timeout after " << IDLE_TIMEOUT
136 << " seconds of inactivity." << endl;
137 session.setState(Session::LOGOUT);
138 return NOTHING;
139 }
140 }
141
142 // unless the select failed, attempt to read client input.
143 if (ret > 0 && FD_ISSET(0, &readfds)) {
144 if (bincClient.readStr(&input) == 0) {
145 break;
146 } else {
147 uppercase(input);
148 trim(input);
149 if (input == "DONE") {
150 break;
151 } else {
152 bincClient << "* BAD Syntax error: \"" << input << "\"" << endl;
153 bincClient.flush();
154 continue;
155 }
156 }
157 }
158
159 // at this point, we either got a directory change notification,
160 // or the select simply timed out, in which case we poll.
161 bool scanForChanges = false;
162#ifdef HAVE_FNOTIFY
163 if (directoryChangeNotification)
164 scanForChanges = true;
165 else if (!waitForNotification)
166#endif
167 scanForChanges = true;
168
169 if (scanForChanges) {
170 if (directoryChangeNotification) {
171 // sleep the magic 1 second to ensure that anything that
172 // arrived in new/ the last second isn't skipped by
173 // pendingUpdates' scan.
174 sleep(1);
175 }
176
177 // scan for changes in the mailbox and report to the client.
181 | PendingUpdates::FLAGS, true) == false) {
182 Session &session = Session::getInstance();
183 bincClient << "* NO " << session.getLastError() << endl;
184 bincWarning << "when scanning mailbox: "
185 << session.getLastError() << endl;
186
187#ifdef HAVE_FNOTIFY
188 close(newfd);
189 close(curfd);
190#endif
191 return NO;
192 }
193
194#ifdef HAVE_FNOTIFY
195 // if FNOTIFY is enabled, set it up again.
196 if (waitForNotification) {
197 directoryChangeNotification = false;
198
199 // set up F_NOTIFY again.
200 if (!fcntl(newfd, F_NOTIFY, DN_MODIFY|DN_RENAME|DN_DELETE|DN_CREATE)
201 && !fcntl(curfd, F_NOTIFY, DN_MODIFY|DN_RENAME|DN_DELETE|DN_CREATE)) {
202 struct sigaction fnotifyAction;
203 fnotifyAction.sa_handler = fnotifyEventHandler;
204 sigemptyset(&fnotifyAction.sa_mask);
205 fnotifyAction.sa_flags = SA_RESTART;
206 sigaction(SIGUSR1, &fnotifyAction, 0);
207 } else {
208 waitForNotification = false;
209 }
210 }
211#endif
212 bincClient.flush();
213 }
214 }
215
216#ifdef HAVE_FNOTIFY
217 close(newfd);
218 close(curfd);
219#endif
220 return OK;
221}
222
223//----------------------------------------------------------------------
225{
226 Session &session = Session::getInstance();
227
228 if (c_in.getUidMode())
229 return REJECT;
230
232 if ((res = expectCRLF()) != ACCEPT) {
233 session.setLastError("Expected CRLF after IDLE");
234 return res;
235 }
236
237 c_in.setName("IDLE");
238 return ACCEPT;
239}
virtual std::string mailboxToFilename(const std::string &m) const =0
virtual Mailbox * getSelected(void) const
Definition: depot.cc:183
virtual ParseResult parse(Request &) const
int getState(void) const
ProcessResult process(Depot &, Request &)
const std::string getName(void) const
const std::string getName(void) const
Definition: mailbox.cc:97
void setName(const std::string &s_in)
Definition: imapparser.cc:70
bool getUidMode(void) const
Definition: imapparser.cc:40
void setLastError(const std::string &error) const
Definition: session.cc:185
void setState(int n)
Definition: session.cc:46
const std::string & getLastError(void) const
Definition: session.cc:179
int timeout() const
Definition: session.cc:218
static Session & getInstance(void)
Definition: session.cc:33
Declaration of miscellaneous convertion functions.
Global constants.
Declaration of the IODevice class.
Declaration of the IOFactory class.
#define bincWarning
Definition: iofactory.h:44
#define bincClient
Definition: iofactory.h:31
Declaration of the Mailbox class (Mailbox is logical container)
Definition: bincimapd.cc:9
bool pendingUpdates(Mailbox *, int type, bool rescan, bool showAll=false, bool forceScan=false, bool uidfetchflags=false)
void uppercase(std::string &input)
Definition: convert.h:115
void trim(std::string &s_in, const std::string &chars=" \t\r\n")
Definition: convert.h:137
Operator::ParseResult expectCRLF(void)
Declaration of all operators.
Declaration of a recursive descent IMAP command parser.