1 /* This file is part of GNU Dico.
2 Copyright (C) 2008-2020 Sergey Poznyakoff
3
4 GNU Dico is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Dico is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <dicod.h>
18
19 char *msg_id;
20
21
22 struct capa_print {
23 size_t num;
24 dico_stream_t stream;
25 };
26
27 static int
print_capa(const char * name,int enabled,void * data)28 print_capa(const char *name, int enabled, void *data)
29 {
30 struct capa_print *cp = data;
31 if (enabled) {
32 if (cp->num++)
33 dico_stream_write(cp->stream, ".", 1);
34 stream_writez(cp->stream, (char*)name);
35 }
36 return 0;
37 }
38
39 static void
output_capabilities(dico_stream_t str)40 output_capabilities(dico_stream_t str)
41 {
42 struct capa_print cp;
43 cp.num = 0;
44 cp.stream = str;
45 dico_stream_write(str, "<", 1);
46 dicod_capa_iterate(print_capa, &cp);
47 dico_stream_write(str, ">", 1);
48 }
49
50 /*
51 3.1. Initial Connection
52
53 When a client initially connects to a DICT server, a code 220 is sent
54 if the client's IP is allowed to connect:
55
56 220 text capabilities msg-id
57 */
58 static void
initial_banner(dico_stream_t str)59 initial_banner(dico_stream_t str)
60 {
61 dico_stream_write(str, "220 ", 4);
62 stream_writez(str, hostname);
63 dico_stream_write(str, " ", 1);
64 if (initial_banner_text)
65 stream_writez(str, initial_banner_text);
66 else
67 stream_writez(str, (char*) program_version);
68 dico_stream_write(str, " ", 1);
69 output_capabilities(str);
70 dico_stream_write(str, " ", 1);
71 asprintf(&msg_id, "<%lu.%lu@%s>",
72 (unsigned long) getpid(),
73 (unsigned long) time(NULL),
74 hostname);
75 stream_writez(str, msg_id);
76 dico_stream_write(str, "\n", 1);
77 }
78
79 int
get_input_line(dico_stream_t str,char ** buf,size_t * size,size_t * rdbytes)80 get_input_line(dico_stream_t str, char **buf, size_t *size, size_t *rdbytes)
81 {
82 int rc;
83 alarm(inactivity_timeout);
84 rc = dico_stream_getline(str, buf, size, rdbytes);
85 alarm(0);
86 return rc;
87 }
88
89 RETSIGTYPE
sig_alarm(int sig)90 sig_alarm(int sig)
91 {
92 exit(EXIT_TIMEOUT);
93 }
94
95 static void
load_modules(void)96 load_modules(void)
97 {
98 dico_iterator_t itr = xdico_list_iterator(modinst_list);
99 dicod_module_instance_t *inst;
100
101 for (inst = dico_iterator_first(itr); inst;
102 inst = dico_iterator_next(itr)) {
103 if (dicod_load_module(inst)) {
104 database_remove_dependent(inst);
105 dico_iterator_remove_current(itr, NULL);
106 }
107 }
108 dico_iterator_destroy(&itr);
109 }
110
111 static void
init_databases(void)112 init_databases(void)
113 {
114 dico_iterator_t itr = xdico_list_iterator(database_list);
115 dicod_database_t *dp;
116
117 for (dp = dico_iterator_first(itr); dp; dp = dico_iterator_next(itr)) {
118 if (dicod_database_init(dp)) {
119 dico_log(L_NOTICE, 0, _("removing database %s"), dp->name);
120 dico_iterator_remove_current(itr, NULL);
121 dicod_database_free(dp);
122 }
123 }
124 dico_iterator_destroy(&itr);
125 }
126
127 static void
open_databases(void)128 open_databases(void)
129 {
130 dico_iterator_t itr = xdico_list_iterator(database_list);
131 dicod_database_t *dp;
132
133 for (dp = dico_iterator_first(itr); dp; dp = dico_iterator_next(itr)) {
134 if (dicod_database_open(dp) == 0) {
135 dp->flags |= dicod_database_flags(dp);
136 } else {
137 dico_log(L_NOTICE, 0, _("removing database %s"), dp->name);
138 dico_iterator_remove_current(itr, NULL);
139 dicod_database_free(dp);
140 }
141 }
142 dico_iterator_destroy(&itr);
143 }
144
145 static void
close_databases(void)146 close_databases(void)
147 {
148 dico_iterator_t itr = xdico_list_iterator(database_list);
149 dicod_database_t *dp;
150
151 for (dp = dico_iterator_first(itr); dp; dp = dico_iterator_next(itr)) {
152 if (dicod_database_close(dp))
153 dico_log(L_NOTICE, 0, _("error closing database %s"), dp->name);
154 }
155 dico_iterator_destroy(&itr);
156 }
157
158 /* Default selectors for some commonly used methods. Modules should
159 override these. */
160 int
exact_sel(int cmd,dico_key_t key,const char * dict_word)161 exact_sel(int cmd, dico_key_t key, const char *dict_word)
162 {
163 switch (cmd) {
164 case DICO_SELECT_BEGIN:
165 break;
166
167 case DICO_SELECT_RUN:
168 return utf8_strcasecmp(key->word, (char*)dict_word) == 0;
169
170 case DICO_SELECT_END:
171 break;
172 }
173 return 0;
174 }
175
176 int
prefix_sel(int cmd,dico_key_t key,const char * dict_word)177 prefix_sel(int cmd, dico_key_t key, const char *dict_word)
178 {
179 size_t wordlen, keylen;
180
181 switch (cmd) {
182 case DICO_SELECT_BEGIN:
183 key->call_data = (void*) utf8_strlen(key->word);
184 break;
185
186 case DICO_SELECT_RUN:
187 keylen = (size_t)key->call_data;
188 wordlen = utf8_strlen(dict_word);
189 return (wordlen >= keylen &&
190 utf8_strncasecmp((char*)dict_word, key->word, keylen) == 0);
191
192 case DICO_SELECT_END:
193 break;
194 }
195 return 0;
196 }
197
198 struct suffix {
199 size_t charlen;
200 size_t bytelen;
201 };
202
203 int
suffix_sel(int cmd,dico_key_t key,const char * dict_word)204 suffix_sel(int cmd, dico_key_t key, const char *dict_word)
205 {
206 size_t wordlen;
207 struct suffix *suf;
208
209 switch (cmd) {
210 case DICO_SELECT_BEGIN:
211 suf = malloc(sizeof(*suf));
212 if (!suf) {
213 dico_log(L_ERR, ENOMEM, "stdstrat_suffix");
214 return -1;
215 }
216 suf->bytelen = utf8_strlen(key->word);
217 suf->charlen = strlen(key->word);
218 key->call_data = suf;
219 break;
220
221 case DICO_SELECT_RUN:
222 suf = (struct suffix*)key->call_data;
223 wordlen = strlen(dict_word);
224 return (wordlen >= suf->bytelen &&
225 utf8_strncasecmp((char*)dict_word + (wordlen - suf->bytelen),
226 key->word, suf->bytelen) == 0);
227
228 case DICO_SELECT_END:
229 free(key->call_data);
230 break;
231 }
232 return 0;
233 }
234
235 /* Soundex selector */
236 int
soundex_sel(int cmd,dico_key_t key,const char * dict_word)237 soundex_sel(int cmd, dico_key_t key, const char *dict_word)
238 {
239 char dcode[DICO_SOUNDEX_SIZE];
240
241 switch (cmd) {
242 case DICO_SELECT_BEGIN:
243 key->call_data = malloc(DICO_SOUNDEX_SIZE);
244 if (!key->call_data)
245 return 1;
246 dico_soundex(key->word, key->call_data);
247 break;
248
249 case DICO_SELECT_RUN:
250 dico_soundex(dict_word, dcode);
251 return strcmp(dcode, key->call_data) == 0;
252
253 case DICO_SELECT_END:
254 free(key->call_data);
255 break;
256 }
257 return 0;
258 }
259
260 void
dicod_init_strategies(void)261 dicod_init_strategies(void)
262 {
263 static struct dico_strategy defstrat[] = {
264 { "exact", "Match words exactly", exact_sel },
265 { "prefix", "Match word prefixes", prefix_sel },
266 { "suffix", "Match word suffixes", suffix_sel },
267 { "soundex", "Match using SOUNDEX algorithm", soundex_sel, NULL },
268 };
269 int i;
270 for (i = 0; i < DICO_ARRAY_SIZE(defstrat); i++)
271 dico_strategy_add(defstrat + i);
272 }
273
274 void
dicod_server_init(void)275 dicod_server_init(void)
276 {
277 load_modules();
278 init_databases();
279 if (!dico_get_default_strategy())
280 dico_set_default_strategy(DICTD_DEFAULT_STRATEGY);
281 }
282
283 void
dicod_server_cleanup(void)284 dicod_server_cleanup(void)
285 {
286 dico_iterator_t itr = xdico_list_iterator(database_list);
287 dicod_database_t *dp;
288
289 for (dp = dico_iterator_first(itr); dp; dp = dico_iterator_next(itr)) {
290 if (dicod_database_deinit(dp))
291 dico_log(L_NOTICE, 0, _("error freeing database %s"), dp->name);
292 }
293 dico_iterator_destroy(&itr);
294 }
295
296 static void
log_connection(const char * msg)297 log_connection(const char *msg)
298 {
299 if (debug_level) {
300 char *p = sockaddr_to_astr((struct sockaddr*)&client_addr,
301 client_addrlen);
302 if (debug_source_info)
303 DICO_DEBUG_SINFO(debug_stream);
304 stream_writez(debug_stream, msg);
305 dico_stream_write(debug_stream, " ", 1);
306 if (identity_name)
307 stream_printf(debug_stream, "%s@", identity_name);
308 stream_writez(debug_stream, p);
309 dico_stream_write(debug_stream, "\n", 1);
310 free(p);
311 }
312 }
313
314 static dico_stream_t iostr;
315
316 void
replace_io_stream(dico_stream_t str)317 replace_io_stream(dico_stream_t str)
318 {
319 iostr = str;
320 }
321
322 int
dicod_loop(dico_stream_t str)323 dicod_loop(dico_stream_t str)
324 {
325 char *buf = NULL;
326 size_t size = 0;
327 size_t rdbytes;
328 struct dico_tokbuf tb;
329
330 begin_timing("dicod");
331 signal(SIGALRM, sig_alarm);
332
333 got_quit = 0;
334 if (transcript) {
335 dico_stream_t logstr = dico_log_stream_create(L_DEBUG);
336 if (!logstr)
337 xalloc_die();
338 str = xdico_transcript_stream_create(str, logstr, NULL);
339 }
340 replace_io_stream(str);
341
342 if (identity_check && server_addr.sa_family == AF_INET)
343 identity_name = query_ident_name((struct sockaddr_in *)&server_addr,
344 (struct sockaddr_in *)&client_addr);
345 log_connection(_("connection from"));
346
347 open_databases();
348 check_db_visibility();
349 initial_banner(iostr);
350
351 dico_tokenize_begin(&tb);
352 while (!got_quit && get_input_line(iostr, &buf, &size, &rdbytes) == 0) {
353 trimnl(buf, rdbytes);
354 xdico_tokenize_string(&tb, buf);
355 if (tb.tb_tokc == 0)
356 continue;
357 dicod_handle_command(iostr, tb.tb_tokc, tb.tb_tokv);
358 }
359 dico_tokenize_end(&tb);
360 close_databases();
361 init_auth_data();
362 access_log_free_cache();
363 /* FIXME: destroy stream here, or at least do it if transcript is set */
364 log_connection(_("session finished:"));
365 return 0;
366 }
367
368 int
dicod_inetd(void)369 dicod_inetd(void)
370 {
371 dico_stream_t str = dicod_iostream(0, 1);
372 if (!str)
373 return 1;
374
375 client_addrlen = sizeof(client_addr);
376 if (getsockname (0, (struct sockaddr*)&client_addr, &client_addrlen) == -1)
377 client_addrlen = 0;
378
379 server_addrlen = sizeof(server_addr);
380 if (getsockname (1, &server_addr, &server_addrlen) == -1)
381 server_addrlen = 0;
382
383 return dicod_loop(str);
384 }
385
386 dico_stream_t
dicod_iostream(int ifd,int ofd)387 dicod_iostream(int ifd, int ofd)
388 {
389 dico_stream_t str = dico_fd_io_stream_create(ifd, ofd);
390
391 if (!str)
392 return NULL;
393 dico_stream_set_buffer(str, dico_buffer_line, DICO_MAX_BUFFER);
394 if (!isatty(ifd)) {
395 dico_stream_t s = dico_crlf_stream(str,
396 DICO_STREAM_READ|DICO_STREAM_WRITE,
397 0);
398 if (!s) {
399 dico_stream_destroy(&str);
400 return NULL;
401 }
402 str = s;
403 dico_stream_set_buffer(str, dico_buffer_line, DICO_MAX_BUFFER);
404 }
405 return str;
406 }
407
408