Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/imap/client.c @ 410:1f0e7229ee58 HEAD
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
file OBuffer.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 14 Oct 2002 02:49:11 +0300 |
parents | ea958a5b9de1 |
children | 3b53dd1280c6 |
rev | line source |
---|---|
0 | 1 /* Copyright (C) 2002 Timo Sirainen */ |
2 | |
3 #include "common.h" | |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
4 #include "network.h" |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
5 #include "ibuffer.h" |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
6 #include "obuffer.h" |
0 | 7 #include "commands.h" |
8 | |
9 #include <stdlib.h> | |
10 | |
11 /* max. size of one parameter in line */ | |
12 #define MAX_INBUF_SIZE 8192 | |
13 | |
14 /* If we can't send a buffer in a minute, disconnect the client */ | |
15 #define CLIENT_OUTPUT_TIMEOUT (60*1000) | |
16 | |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
17 /* If we don't soon receive expected data from client while processing |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
18 a command, disconnect the client */ |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
19 #define CLIENT_CMDINPUT_TIMEOUT CLIENT_OUTPUT_TIMEOUT |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
20 |
0 | 21 /* Disconnect client when it sends too many bad commands */ |
22 #define CLIENT_MAX_BAD_COMMANDS 20 | |
23 | |
24 /* Disconnect client after idling this many seconds */ | |
25 #define CLIENT_IDLE_TIMEOUT (60*30) | |
26 | |
27 static Client *my_client; /* we don't need more than one currently */ | |
28 static Timeout to_idle; | |
29 | |
30 static void client_input(Client *client); | |
31 | |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
32 static void client_output_timeout(void *context, |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
33 Timeout timeout __attr_unused__) |
0 | 34 { |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
35 Client *client = context; |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
36 |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
37 i_buffer_close(client->inbuf); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
38 o_buffer_close(client->outbuf); |
0 | 39 } |
40 | |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
41 static void client_input_timeout(void *context, |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
42 Timeout timeout __attr_unused__) |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
43 { |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
44 Client *client = context; |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
45 |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
46 client_send_line(my_client, "* BYE Disconnected for inactivity " |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
47 "while waiting for command data."); |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
48 o_buffer_close(client->outbuf); |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
49 } |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
50 |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
51 Client *client_create(int hin, int hout, MailStorage *storage) |
0 | 52 { |
53 Client *client; | |
54 | |
55 client = i_new(Client, 1); | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
56 client->inbuf = i_buffer_create_file(hin, default_pool, |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
57 MAX_INBUF_SIZE, FALSE); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
58 client->outbuf = o_buffer_create_file(hout, default_pool, 4096, |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
59 IO_PRIORITY_DEFAULT, FALSE); |
0 | 60 |
164
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
61 /* always use nonblocking I/O */ |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
62 net_set_nonblock(hin, TRUE); |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
63 net_set_nonblock(hout, TRUE); |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
64 |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
65 /* set timeout for reading expected data (eg. APPEND). This is |
2660a5684515
Added io_buffer_read_blocking() which can be used to read data blockingly,
Timo Sirainen <tss@iki.fi>
parents:
158
diff
changeset
|
66 different from the actual idle time. */ |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
67 i_buffer_set_blocking(client->inbuf, CLIENT_CMDINPUT_TIMEOUT, |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
68 client_input_timeout, client); |
0 | 69 |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
70 /* set timeout for sending data */ |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
71 o_buffer_set_blocking(client->outbuf, CLIENT_OUTPUT_TIMEOUT, |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
72 client_output_timeout, client); |
0 | 73 |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
74 client->io = io_add(hin, IO_READ, (IOFunc) client_input, client); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
75 client->parser = imap_parser_create(client->inbuf, client->outbuf, |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
76 MAX_INBUF_SIZE); |
0 | 77 client->last_input = ioloop_time; |
78 | |
79 client->storage = storage; | |
80 | |
81 i_assert(my_client == NULL); | |
82 my_client = client; | |
83 return client; | |
84 } | |
85 | |
86 void client_destroy(Client *client) | |
87 { | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
88 o_buffer_flush(client->outbuf); |
158
a1204e882bc7
Flush output buffer to client at exit, and send a nice "BYE Server shutting
Timo Sirainen <tss@iki.fi>
parents:
10
diff
changeset
|
89 |
0 | 90 if (client->mailbox != NULL) |
91 client->mailbox->close(client->mailbox); | |
92 mail_storage_destroy(client->storage); | |
93 | |
94 imap_parser_destroy(client->parser); | |
95 io_remove(client->io); | |
96 | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
97 i_buffer_unref(client->inbuf); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
98 o_buffer_unref(client->outbuf); |
0 | 99 |
100 i_free(client); | |
101 | |
102 /* quit the program */ | |
103 my_client = NULL; | |
104 io_loop_stop(ioloop); | |
105 } | |
106 | |
107 void client_disconnect(Client *client) | |
108 { | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
109 o_buffer_flush(client->outbuf); |
222
cf4d065f2f85
lots of cleanups. also index/datafile is now capable of staying in memory,
Timo Sirainen <tss@iki.fi>
parents:
184
diff
changeset
|
110 |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
111 i_buffer_close(client->inbuf); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
112 o_buffer_close(client->outbuf); |
0 | 113 } |
114 | |
115 void client_send_line(Client *client, const char *data) | |
116 { | |
117 if (client->outbuf->closed) | |
118 return; | |
119 | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
120 (void)o_buffer_send(client->outbuf, data, strlen(data)); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
121 (void)o_buffer_send(client->outbuf, "\r\n", 2); |
0 | 122 } |
123 | |
124 void client_send_tagline(Client *client, const char *data) | |
125 { | |
126 const char *tag = client->cmd_tag; | |
127 | |
128 if (client->outbuf->closed) | |
129 return; | |
130 | |
131 if (tag == NULL || *tag == '\0') | |
132 tag = "*"; | |
133 | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
134 (void)o_buffer_send(client->outbuf, tag, strlen(tag)); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
135 (void)o_buffer_send(client->outbuf, " ", 1); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
136 (void)o_buffer_send(client->outbuf, data, strlen(data)); |
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
137 (void)o_buffer_send(client->outbuf, "\r\n", 2); |
0 | 138 } |
139 | |
140 void client_send_command_error(Client *client, const char *msg) | |
141 { | |
142 const char *error; | |
143 | |
144 if (msg == NULL) | |
145 error = "BAD Error in IMAP command."; | |
146 else | |
147 error = t_strconcat("BAD Error in IMAP command: ", msg, NULL); | |
148 | |
149 client->cmd_error = TRUE; | |
150 client_send_tagline(client, error); | |
151 | |
152 if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { | |
153 client_send_line(client, | |
154 "* BYE Too many invalid IMAP commands."); | |
155 client_disconnect(client); | |
156 } | |
157 } | |
158 | |
159 int client_read_args(Client *client, unsigned int count, unsigned int flags, | |
160 ImapArg **args) | |
161 { | |
162 int ret; | |
163 | |
164 ret = imap_parser_read_args(client->parser, count, flags, args); | |
165 if ((unsigned int) ret == count) { | |
166 /* all parameters read successfully */ | |
167 return TRUE; | |
168 } else if (ret == -2) { | |
169 /* need more data */ | |
170 return FALSE; | |
171 } else { | |
172 /* error, or missing arguments */ | |
173 client_send_command_error(client, | |
174 "Missing or invalid arguments."); | |
175 return FALSE; | |
176 } | |
177 } | |
178 | |
179 int client_read_string_args(Client *client, unsigned int count, ...) | |
180 { | |
181 ImapArg *imap_args; | |
182 va_list va; | |
183 const char *str; | |
184 unsigned int i; | |
185 | |
186 if (!client_read_args(client, count, 0, &imap_args)) | |
187 return FALSE; | |
188 | |
189 va_start(va, count); | |
190 for (i = 0; i < count; i++) { | |
191 const char **ret = va_arg(va, const char **); | |
192 | |
193 str = imap_arg_string(&imap_args[i]); | |
194 if (str == NULL) { | |
195 client_send_command_error(client, "Missing arguments."); | |
196 va_end(va); | |
197 return FALSE; | |
198 } | |
199 | |
200 if (ret != NULL) | |
201 *ret = str; | |
202 } | |
203 va_end(va); | |
204 | |
205 return TRUE; | |
206 } | |
207 | |
208 static void client_reset_command(Client *client) | |
209 { | |
210 client->cmd_tag = NULL; | |
211 client->cmd_name = NULL; | |
212 client->cmd_func = NULL; | |
213 client->cmd_error = FALSE; | |
214 client->cmd_uid = FALSE; | |
215 | |
216 imap_parser_reset(client->parser); | |
217 } | |
218 | |
219 static void client_command_finished(Client *client) | |
220 { | |
221 client->inbuf_skip_line = TRUE; | |
222 client_reset_command(client); | |
223 } | |
224 | |
225 /* Skip incoming data until newline is found, | |
226 returns TRUE if newline was found. */ | |
227 static int client_skip_line(Client *client) | |
228 { | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
229 const unsigned char *data; |
184 | 230 size_t i, data_size; |
0 | 231 |
232 /* get the beginning of data in input buffer */ | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
233 data = i_buffer_get_data(client->inbuf, &data_size); |
0 | 234 |
235 for (i = 0; i < data_size; i++) { | |
236 if (data[i] == '\n') { | |
237 client->inbuf_skip_line = FALSE; | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
238 i_buffer_skip(client->inbuf, i+1); |
0 | 239 break; |
240 } | |
241 } | |
242 | |
243 return !client->inbuf_skip_line; | |
244 } | |
245 | |
246 static int client_handle_input(Client *client) | |
247 { | |
248 if (client->cmd_func != NULL) { | |
249 /* command is being executed - continue it */ | |
250 if (client->cmd_func(client)) { | |
251 /* command is finished */ | |
252 client_command_finished(client); | |
253 return TRUE; | |
254 } | |
255 return FALSE; | |
256 } | |
257 | |
258 if (client->inbuf_skip_line) { | |
259 /* we're just waiting for new line.. */ | |
260 if (!client_skip_line(client)) | |
261 return FALSE; | |
262 | |
263 /* got the newline */ | |
264 client_reset_command(client); | |
265 | |
266 /* pass through to parse next command */ | |
267 } | |
268 | |
269 if (client->cmd_tag == NULL) { | |
270 client->cmd_tag = imap_parser_read_word(client->parser); | |
271 if (client->cmd_tag == NULL) | |
272 return FALSE; /* need more data */ | |
273 } | |
274 | |
275 if (client->cmd_name == NULL) { | |
276 client->cmd_name = imap_parser_read_word(client->parser); | |
277 if (client->cmd_name == NULL) | |
278 return FALSE; /* need more data */ | |
279 } | |
280 | |
281 if (client->cmd_name == '\0') { | |
282 /* command not given - cmd_func is already NULL. */ | |
283 } else { | |
284 /* find the command function */ | |
285 client->cmd_func = client_command_find(client->cmd_name); | |
286 } | |
287 | |
288 if (client->cmd_func == NULL) { | |
289 /* unknown command */ | |
290 client_send_command_error(client, t_strconcat( | |
291 "Unknown command '", client->cmd_name, "'", NULL)); | |
292 client_command_finished(client); | |
293 } else { | |
294 if (client->cmd_func(client) || client->cmd_error) { | |
295 /* command execution was finished */ | |
296 client_command_finished(client); | |
297 } | |
298 } | |
299 | |
300 return TRUE; | |
301 } | |
302 | |
303 static void client_input(Client *client) | |
304 { | |
305 client->last_input = ioloop_time; | |
306 | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
307 switch (i_buffer_read(client->inbuf)) { |
0 | 308 case -1: |
309 /* disconnected */ | |
310 client_destroy(client); | |
311 return; | |
312 case -2: | |
313 /* parameter word is longer than max. input buffer size. | |
314 this is most likely an error, so skip the new data | |
315 until newline is found. */ | |
316 client->inbuf_skip_line = TRUE; | |
317 | |
318 client_send_command_error(client, "Too long argument."); | |
319 client_command_finished(client); | |
320 break; | |
321 } | |
322 | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
323 o_buffer_cork(client->outbuf); |
0 | 324 while (client_handle_input(client)) |
325 ; | |
410
1f0e7229ee58
Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and
Timo Sirainen <tss@iki.fi>
parents:
364
diff
changeset
|
326 o_buffer_flush(client->outbuf); |
0 | 327 |
328 if (client->outbuf->closed) | |
329 client_destroy(client); | |
330 } | |
331 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
332 static void idle_timeout(void *context __attr_unused__, |
0 | 333 Timeout timeout __attr_unused__) |
334 { | |
335 if (my_client == NULL) | |
336 return; | |
337 | |
338 if (ioloop_time - my_client->last_input >= CLIENT_IDLE_TIMEOUT) { | |
339 client_send_line(my_client, | |
340 "* BYE Disconnected for inactivity."); | |
341 client_destroy(my_client); | |
342 } | |
343 } | |
344 | |
345 void clients_init(void) | |
346 { | |
347 my_client = NULL; | |
348 to_idle = timeout_add(10000, idle_timeout, NULL); | |
349 } | |
350 | |
351 void clients_deinit(void) | |
352 { | |
158
a1204e882bc7
Flush output buffer to client at exit, and send a nice "BYE Server shutting
Timo Sirainen <tss@iki.fi>
parents:
10
diff
changeset
|
353 if (my_client != NULL) { |
a1204e882bc7
Flush output buffer to client at exit, and send a nice "BYE Server shutting
Timo Sirainen <tss@iki.fi>
parents:
10
diff
changeset
|
354 client_send_line(my_client, "* BYE Server shutting down."); |
0 | 355 client_destroy(my_client); |
158
a1204e882bc7
Flush output buffer to client at exit, and send a nice "BYE Server shutting
Timo Sirainen <tss@iki.fi>
parents:
10
diff
changeset
|
356 } |
0 | 357 |
358 timeout_remove(to_idle); | |
359 } |