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