/src/irssi/src/core/servers-setup.c
Line | Count | Source |
1 | | /* |
2 | | servers-setup.c : irssi |
3 | | |
4 | | Copyright (C) 1999-2001 Timo Sirainen |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 2 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License along |
17 | | with this program; if not, write to the Free Software Foundation, Inc., |
18 | | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | | */ |
20 | | |
21 | | #include "module.h" |
22 | | #include <irssi/src/core/signals.h> |
23 | | #include <irssi/src/core/network.h> |
24 | | #include <irssi/src/lib-config/iconfig.h> |
25 | | #include <irssi/src/core/settings.h> |
26 | | |
27 | | #include <irssi/src/core/chat-protocols.h> |
28 | | #include <irssi/src/core/chatnets.h> |
29 | | #include <irssi/src/core/servers.h> |
30 | | #include <irssi/src/core/servers-setup.h> |
31 | | |
32 | | GSList *setupservers; |
33 | | |
34 | | static char *old_source_host; |
35 | | int source_host_ok; /* Use source_host_ip .. */ |
36 | | IPADDR *source_host_ip4, *source_host_ip6; /* Resolved address */ |
37 | | |
38 | | static void save_ips(IPADDR *ip4, IPADDR *ip6, |
39 | | IPADDR **save_ip4, IPADDR **save_ip6) |
40 | 0 | { |
41 | 0 | if (ip4->family == 0) |
42 | 0 | g_free_and_null(*save_ip4); |
43 | 0 | else { |
44 | 0 | if (*save_ip4 == NULL) |
45 | 0 | *save_ip4 = g_new(IPADDR, 1); |
46 | 0 | memcpy(*save_ip4, ip4, sizeof(IPADDR)); |
47 | 0 | } |
48 | |
|
49 | 0 | if (ip6->family == 0) |
50 | 0 | g_free_and_null(*save_ip6); |
51 | 0 | else { |
52 | 0 | if (*save_ip6 == NULL) |
53 | 0 | *save_ip6 = g_new(IPADDR, 1); |
54 | 0 | memcpy(*save_ip6, ip6, sizeof(IPADDR)); |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | static void get_source_host_ip(void) |
59 | 8 | { |
60 | 8 | const char *hostname; |
61 | 8 | IPADDR ip4, ip6; |
62 | | |
63 | 8 | if (source_host_ok) |
64 | 0 | return; |
65 | | |
66 | | /* FIXME: This will block! */ |
67 | 8 | hostname = settings_get_str("hostname"); |
68 | 8 | source_host_ok = *hostname != '\0' && |
69 | 0 | net_gethostbyname(hostname, &ip4, &ip6) == 0; |
70 | | |
71 | 8 | if (source_host_ok) |
72 | 0 | save_ips(&ip4, &ip6, &source_host_ip4, &source_host_ip6); |
73 | 8 | else { |
74 | 8 | g_free_and_null(source_host_ip4); |
75 | 8 | g_free_and_null(source_host_ip6); |
76 | 8 | } |
77 | 8 | } |
78 | | |
79 | | static void conn_set_ip(SERVER_CONNECT_REC *conn, const char *own_host, |
80 | | IPADDR **own_ip4, IPADDR **own_ip6) |
81 | 0 | { |
82 | 0 | IPADDR ip4, ip6; |
83 | |
|
84 | 0 | if (*own_ip4 == NULL && *own_ip6 == NULL) { |
85 | | /* resolve the IP */ |
86 | 0 | if (net_gethostbyname(own_host, &ip4, &ip6) == 0) |
87 | 0 | save_ips(&ip4, &ip6, own_ip4, own_ip6); |
88 | 0 | } |
89 | |
|
90 | 0 | server_connect_own_ip_save(conn, *own_ip4, *own_ip6); |
91 | 0 | } |
92 | | |
93 | | /* Fill information to connection from server setup record */ |
94 | | void server_setup_fill_reconn(SERVER_CONNECT_REC *conn, |
95 | | SERVER_SETUP_REC *sserver) |
96 | 0 | { |
97 | 0 | g_return_if_fail(IS_SERVER_CONNECT(conn)); |
98 | 0 | g_return_if_fail(IS_SERVER_SETUP(sserver)); |
99 | | |
100 | 0 | if (sserver->own_host != NULL) { |
101 | 0 | conn_set_ip(conn, sserver->own_host, |
102 | 0 | &sserver->own_ip4, &sserver->own_ip6); |
103 | 0 | } |
104 | |
|
105 | 0 | if (sserver->chatnet != NULL && conn->chatnet == NULL) |
106 | 0 | conn->chatnet = g_strdup(sserver->chatnet); |
107 | |
|
108 | 0 | if (sserver->password != NULL && conn->password == NULL) |
109 | 0 | conn->password = g_strdup(sserver->password); |
110 | |
|
111 | 0 | if (sserver->no_proxy) |
112 | 0 | g_free_and_null(conn->proxy); |
113 | |
|
114 | 0 | if (sserver->family != 0 && conn->family == 0) |
115 | 0 | conn->family = sserver->family; |
116 | 0 | if (sserver->address && !conn->address) |
117 | 0 | conn->address = g_strdup(sserver->address); |
118 | 0 | if (sserver->port > 0 && conn->port <= 0) |
119 | 0 | conn->port = sserver->port; |
120 | |
|
121 | 0 | conn->use_tls = sserver->use_tls; |
122 | 0 | if (conn->tls_cert == NULL && sserver->tls_cert != NULL && sserver->tls_cert[0] != '\0') |
123 | 0 | conn->tls_cert = g_strdup(sserver->tls_cert); |
124 | 0 | if (conn->tls_pkey == NULL && sserver->tls_pkey != NULL && sserver->tls_pkey[0] != '\0') |
125 | 0 | conn->tls_pkey = g_strdup(sserver->tls_pkey); |
126 | 0 | if (conn->tls_pass == NULL && sserver->tls_pass != NULL && sserver->tls_pass[0] != '\0') |
127 | 0 | conn->tls_pass = g_strdup(sserver->tls_pass); |
128 | 0 | conn->tls_verify = sserver->tls_verify; |
129 | 0 | if (conn->tls_cafile == NULL && sserver->tls_cafile != NULL && sserver->tls_cafile[0] != '\0') |
130 | 0 | conn->tls_cafile = g_strdup(sserver->tls_cafile); |
131 | 0 | if (conn->tls_capath == NULL && sserver->tls_capath != NULL && sserver->tls_capath[0] != '\0') |
132 | 0 | conn->tls_capath = g_strdup(sserver->tls_capath); |
133 | 0 | if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0') |
134 | 0 | conn->tls_ciphers = g_strdup(sserver->tls_ciphers); |
135 | 0 | if (conn->tls_pinned_cert == NULL && sserver->tls_pinned_cert != NULL && sserver->tls_pinned_cert[0] != '\0') |
136 | 0 | conn->tls_pinned_cert = g_strdup(sserver->tls_pinned_cert); |
137 | 0 | if (conn->tls_pinned_pubkey == NULL && sserver->tls_pinned_pubkey != NULL && sserver->tls_pinned_pubkey[0] != '\0') |
138 | 0 | conn->tls_pinned_pubkey = g_strdup(sserver->tls_pinned_pubkey); |
139 | |
|
140 | 0 | signal_emit("server setup fill reconn", 2, conn, sserver); |
141 | 0 | } |
142 | | |
143 | | static void server_setup_fill(SERVER_CONNECT_REC *conn, const char *address, int port, |
144 | | GHashTable *optlist) |
145 | 6.59k | { |
146 | 6.59k | g_return_if_fail(conn != NULL); |
147 | 6.59k | g_return_if_fail(address != NULL); |
148 | | |
149 | 6.59k | conn->type = module_get_uniq_id("SERVER CONNECT", 0); |
150 | | |
151 | 6.59k | conn->address = g_strdup(address); |
152 | 6.59k | if (port > 0) conn->port = port; |
153 | | |
154 | 6.59k | if (strchr(address, '/') != NULL) |
155 | 0 | conn->unix_socket = TRUE; |
156 | | |
157 | 6.59k | if (!conn->nick) conn->nick = g_strdup(settings_get_str("nick")); |
158 | 6.59k | conn->username = g_strdup(settings_get_str("user_name")); |
159 | 6.59k | conn->realname = g_strdup(settings_get_str("real_name")); |
160 | | |
161 | | /* proxy settings */ |
162 | 6.59k | if (settings_get_bool("use_proxy")) { |
163 | 0 | conn->proxy = g_strdup(settings_get_str("proxy_address")); |
164 | 0 | conn->proxy_port = settings_get_int("proxy_port"); |
165 | 0 | conn->proxy_string = g_strdup(settings_get_str("proxy_string")); |
166 | 0 | conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after")); |
167 | 0 | conn->proxy_password = g_strdup(settings_get_str("proxy_password")); |
168 | 0 | } |
169 | | |
170 | | /* source IP */ |
171 | 6.59k | if (source_host_ip4 != NULL) { |
172 | 0 | conn->own_ip4 = g_new(IPADDR, 1); |
173 | 0 | memcpy(conn->own_ip4, source_host_ip4, sizeof(IPADDR)); |
174 | 0 | } |
175 | 6.59k | if (source_host_ip6 != NULL) { |
176 | 0 | conn->own_ip6 = g_new(IPADDR, 1); |
177 | 0 | memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR)); |
178 | 0 | } |
179 | | |
180 | 6.59k | signal_emit("server setup fill connect", 2, conn, optlist); |
181 | 6.59k | } |
182 | | |
183 | | static void server_setup_fill_optlist(SERVER_CONNECT_REC *conn, GHashTable *optlist) |
184 | 0 | { |
185 | 0 | char *tmp; |
186 | |
|
187 | 0 | if (g_hash_table_lookup(optlist, "6") != NULL) |
188 | 0 | conn->family = AF_INET6; |
189 | 0 | else if (g_hash_table_lookup(optlist, "4") != NULL) |
190 | 0 | conn->family = AF_INET; |
191 | | |
192 | | /* ad-hoc TLS settings from command optlist */ |
193 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || |
194 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL) { |
195 | 0 | conn->tls_cert = g_strdup(tmp); |
196 | 0 | conn->use_tls = TRUE; |
197 | 0 | } |
198 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || |
199 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL) |
200 | 0 | conn->tls_pkey = g_strdup(tmp); |
201 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || |
202 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL) |
203 | 0 | conn->tls_pass = g_strdup(tmp); |
204 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || |
205 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL) |
206 | 0 | conn->tls_cafile = g_strdup(tmp); |
207 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || |
208 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL) |
209 | 0 | conn->tls_capath = g_strdup(tmp); |
210 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || |
211 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL) |
212 | 0 | conn->tls_ciphers = g_strdup(tmp); |
213 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || |
214 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL) |
215 | 0 | conn->tls_pinned_cert = g_strdup(tmp); |
216 | 0 | if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || |
217 | 0 | (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL) |
218 | 0 | conn->tls_pinned_pubkey = g_strdup(tmp); |
219 | 0 | if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0') || |
220 | 0 | (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0')) |
221 | 0 | conn->tls_verify = TRUE; |
222 | 0 | if (g_hash_table_lookup(optlist, "notls_verify") != NULL) |
223 | 0 | conn->tls_verify = FALSE; |
224 | 0 | if (g_hash_table_lookup(optlist, "tls_verify") != NULL || |
225 | 0 | g_hash_table_lookup(optlist, "ssl_verify") != NULL) { |
226 | 0 | conn->tls_verify = TRUE; |
227 | 0 | conn->use_tls = TRUE; |
228 | 0 | } |
229 | 0 | if (g_hash_table_lookup(optlist, "notls") != NULL) |
230 | 0 | conn->use_tls = FALSE; |
231 | 0 | if (g_hash_table_lookup(optlist, "tls") != NULL || |
232 | 0 | g_hash_table_lookup(optlist, "ssl") != NULL) |
233 | 0 | conn->use_tls = TRUE; |
234 | |
|
235 | 0 | if (g_hash_table_lookup(optlist, "!") != NULL) |
236 | 0 | conn->no_autojoin_channels = TRUE; |
237 | |
|
238 | 0 | if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL) |
239 | 0 | conn->no_autosendcmd = TRUE; |
240 | |
|
241 | 0 | if (g_hash_table_lookup(optlist, "noproxy") != NULL) |
242 | 0 | g_free_and_null(conn->proxy); |
243 | |
|
244 | 0 | signal_emit("server setup fill optlist", 2, conn, optlist); |
245 | 0 | } |
246 | | |
247 | | static void server_setup_fill_server(SERVER_CONNECT_REC *conn, |
248 | | SERVER_SETUP_REC *sserver) |
249 | 0 | { |
250 | 0 | g_return_if_fail(IS_SERVER_CONNECT(conn)); |
251 | 0 | g_return_if_fail(IS_SERVER_SETUP(sserver)); |
252 | | |
253 | 0 | sserver->last_connect = time(NULL); |
254 | |
|
255 | 0 | server_setup_fill_reconn(conn, sserver); |
256 | |
|
257 | 0 | signal_emit("server setup fill server", 2, conn, sserver); |
258 | 0 | } |
259 | | |
260 | | static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn, |
261 | | CHATNET_REC *chatnet) |
262 | 0 | { |
263 | 0 | g_return_if_fail(IS_SERVER_CONNECT(conn)); |
264 | 0 | g_return_if_fail(IS_CHATNET(chatnet)); |
265 | | |
266 | 0 | if (chatnet->nick != NULL) { |
267 | 0 | g_free(conn->nick); |
268 | 0 | conn->nick = g_strdup(chatnet->nick);; |
269 | 0 | } |
270 | 0 | if (chatnet->username != NULL) { |
271 | 0 | g_free(conn->username); |
272 | 0 | conn->username = g_strdup(chatnet->username);; |
273 | 0 | } |
274 | 0 | if (chatnet->realname != NULL) { |
275 | 0 | g_free(conn->realname); |
276 | 0 | conn->realname = g_strdup(chatnet->realname);; |
277 | 0 | } |
278 | 0 | if (chatnet->own_host != NULL) { |
279 | 0 | conn_set_ip(conn, chatnet->own_host, |
280 | 0 | &chatnet->own_ip4, &chatnet->own_ip6); |
281 | 0 | } |
282 | |
|
283 | 0 | signal_emit("server setup fill chatnet", 2, conn, chatnet); |
284 | 0 | } |
285 | | |
286 | | static SERVER_CONNECT_REC *create_addr_conn(int chat_type, const char *address, int port, |
287 | | const char *chatnet, const char *password, |
288 | | const char *nick, GHashTable *optlist) |
289 | 6.59k | { |
290 | 6.59k | CHAT_PROTOCOL_REC *proto; |
291 | 6.59k | SERVER_CONNECT_REC *conn; |
292 | 6.59k | SERVER_SETUP_REC *sserver; |
293 | 6.59k | CHATNET_REC *chatnetrec; |
294 | | |
295 | 6.59k | g_return_val_if_fail(address != NULL, NULL); |
296 | | |
297 | 6.59k | sserver = server_setup_find(address, port, chatnet); |
298 | 6.59k | if (sserver != NULL) { |
299 | 0 | if (chat_type < 0) |
300 | 0 | chat_type = sserver->chat_type; |
301 | 0 | else if (chat_type != sserver->chat_type) |
302 | 0 | sserver = NULL; |
303 | 0 | } |
304 | | |
305 | 6.59k | proto = chat_type >= 0 ? chat_protocol_find_id(chat_type) : |
306 | 6.59k | chat_protocol_get_default(); |
307 | | |
308 | 6.59k | if (proto == NULL) { |
309 | 0 | signal_stop(); |
310 | 0 | return NULL; |
311 | 0 | } |
312 | | |
313 | 6.59k | g_return_val_if_fail(proto != NULL, NULL); |
314 | | |
315 | 6.59k | conn = proto->create_server_connect(); |
316 | 6.59k | server_connect_ref(conn); |
317 | | |
318 | 6.59k | conn->chat_type = proto->id; |
319 | 6.59k | if (chatnet != NULL && *chatnet != '\0') |
320 | 0 | conn->chatnet = g_strdup(chatnet); |
321 | | |
322 | | /* fill in the defaults */ |
323 | 6.59k | server_setup_fill(conn, address, port, optlist); |
324 | | |
325 | | /* fill the rest from chat network settings */ |
326 | 6.59k | chatnetrec = chatnet != NULL ? chatnet_find(chatnet) : |
327 | 6.59k | (sserver == NULL || sserver->chatnet == NULL ? NULL : |
328 | 0 | chatnet_find(sserver->chatnet)); |
329 | 6.59k | if (chatnetrec != NULL) |
330 | 0 | server_setup_fill_chatnet(conn, chatnetrec); |
331 | | |
332 | | /* fill the information from setup */ |
333 | 6.59k | if (sserver != NULL) |
334 | 0 | server_setup_fill_server(conn, sserver); |
335 | | |
336 | | /* fill the optlist overrides */ |
337 | 6.59k | if (g_hash_table_size(optlist)) |
338 | 0 | server_setup_fill_optlist(conn, optlist); |
339 | | |
340 | | /* nick / password given in command line overrides all settings */ |
341 | 6.59k | if (password && *password) { |
342 | 0 | g_free_not_null(conn->password); |
343 | 0 | conn->password = g_strdup(password); |
344 | 0 | } |
345 | 6.59k | if (nick && *nick) { |
346 | 6.59k | g_free_not_null(conn->nick); |
347 | 6.59k | conn->nick = g_strdup(nick); |
348 | 6.59k | } |
349 | | |
350 | 6.59k | return conn; |
351 | 6.59k | } |
352 | | |
353 | | /* Connect to server where last connect succeeded (or we haven't tried to |
354 | | connect yet). If there's no such server, connect to server where we |
355 | | haven't connected for the longest time */ |
356 | | static SERVER_CONNECT_REC *create_chatnet_conn(const char *dest, int port, const char *password, |
357 | | const char *nick, GHashTable *optlist) |
358 | 0 | { |
359 | 0 | SERVER_SETUP_REC *bestrec; |
360 | 0 | GSList *tmp; |
361 | 0 | time_t now, besttime; |
362 | |
|
363 | 0 | now = time(NULL); |
364 | 0 | bestrec = NULL; besttime = now; |
365 | 0 | for (tmp = setupservers; tmp != NULL; tmp = tmp->next) { |
366 | 0 | SERVER_SETUP_REC *rec = tmp->data; |
367 | |
|
368 | 0 | if (rec->chatnet == NULL || |
369 | 0 | g_ascii_strcasecmp(rec->chatnet, dest) != 0) |
370 | 0 | continue; |
371 | | |
372 | 0 | if (!rec->last_failed) { |
373 | 0 | bestrec = rec; |
374 | 0 | break; |
375 | 0 | } |
376 | | |
377 | 0 | if (bestrec == NULL || besttime > rec->last_connect) { |
378 | 0 | bestrec = rec; |
379 | 0 | besttime = rec->last_connect; |
380 | 0 | } |
381 | 0 | } |
382 | |
|
383 | 0 | return bestrec == NULL ? NULL : |
384 | 0 | create_addr_conn(bestrec->chat_type, bestrec->address, 0, dest, |
385 | 0 | NULL, nick, optlist); |
386 | 0 | } |
387 | | |
388 | | /* Create server connection record. `dest' is required, rest can be NULL. |
389 | | `dest' is either a server address or chat network */ |
390 | | SERVER_CONNECT_REC *server_create_conn_opt(int chat_type, const char *dest, int port, |
391 | | const char *chatnet, const char *password, |
392 | | const char *nick, GHashTable *optlist) |
393 | 6.59k | { |
394 | 6.59k | SERVER_CONNECT_REC *rec; |
395 | 6.59k | CHATNET_REC *chatrec; |
396 | | |
397 | 6.59k | g_return_val_if_fail(dest != NULL, NULL); |
398 | | |
399 | 6.59k | chatrec = chatnet_find(dest); |
400 | 6.59k | if (chatrec != NULL) { |
401 | 0 | rec = create_chatnet_conn(chatrec->name, port, password, nick, optlist); |
402 | | /* If rec is NULL the chatnet has no url to connect to */ |
403 | 0 | return rec; |
404 | 0 | } |
405 | | |
406 | 6.59k | chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet); |
407 | 6.59k | if (chatrec != NULL) |
408 | 0 | chatnet = chatrec->name; |
409 | | |
410 | 6.59k | return create_addr_conn(chat_type, dest, port, chatnet, password, nick, optlist); |
411 | 6.59k | } |
412 | | |
413 | | SERVER_CONNECT_REC *server_create_conn(int chat_type, const char *dest, int port, |
414 | | const char *chatnet, const char *password, const char *nick) |
415 | 6.59k | { |
416 | 6.59k | SERVER_CONNECT_REC *ret; |
417 | 6.59k | GHashTable *opt; |
418 | | |
419 | 6.59k | opt = g_hash_table_new(NULL, NULL); |
420 | 6.59k | ret = server_create_conn_opt(chat_type, dest, port, chatnet, password, nick, opt); |
421 | 6.59k | g_hash_table_destroy(opt); |
422 | | |
423 | 6.59k | return ret; |
424 | 6.59k | } |
425 | | |
426 | | /* Find matching server from setup. Try to find record with a same port, |
427 | | but fallback to any server with the same address. */ |
428 | | SERVER_SETUP_REC *server_setup_find(const char *address, int port, |
429 | | const char *chatnet) |
430 | 13.0k | { |
431 | 13.0k | SERVER_SETUP_REC *server; |
432 | 13.0k | GSList *tmp; |
433 | | |
434 | 13.0k | g_return_val_if_fail(address != NULL, NULL); |
435 | | |
436 | 13.0k | server = NULL; |
437 | 181k | for (tmp = setupservers; tmp != NULL; tmp = tmp->next) { |
438 | 168k | SERVER_SETUP_REC *rec = tmp->data; |
439 | | |
440 | 168k | if (g_ascii_strcasecmp(rec->address, address) == 0 && |
441 | 0 | (chatnet == NULL || rec->chatnet == NULL || |
442 | 0 | g_ascii_strcasecmp(rec->chatnet, chatnet) == 0)) { |
443 | 0 | server = rec; |
444 | 0 | if (rec->port == port) |
445 | 0 | break; |
446 | 0 | } |
447 | 168k | } |
448 | | |
449 | 13.0k | return server; |
450 | 13.0k | } |
451 | | |
452 | | static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) |
453 | 28 | { |
454 | 28 | SERVER_SETUP_REC *rec; |
455 | 28 | CHATNET_REC *chatnetrec; |
456 | 28 | CHAT_PROTOCOL_REC *proto; |
457 | 28 | char *server, *chatnet, *family; |
458 | 28 | int port; |
459 | 28 | char *value = NULL; |
460 | | |
461 | 28 | g_return_val_if_fail(node != NULL, NULL); |
462 | | |
463 | 28 | server = config_node_get_str(node, "address", NULL); |
464 | 28 | if (server == NULL) |
465 | 0 | return NULL; |
466 | | |
467 | 28 | port = config_node_get_int(node, "port", 0); |
468 | 28 | chatnet = config_node_get_str(node, "chatnet", NULL); |
469 | | |
470 | 28 | if ((rec = server_setup_find(server, port, chatnet)) != NULL && rec->port == port) { |
471 | | /* duplicate server setup */ |
472 | 0 | server_setup_remove(rec); |
473 | 0 | } |
474 | | |
475 | 28 | rec = NULL; |
476 | | |
477 | 28 | if (chatnet != NULL) { |
478 | 28 | chatnetrec = chatnet_find(chatnet); |
479 | 28 | if (chatnetrec != NULL) { |
480 | 26 | proto = CHAT_PROTOCOL(chatnetrec); |
481 | 26 | } else { |
482 | | /* chat network not found, create it. */ |
483 | 2 | if (chatnet_find_unavailable(chatnet)) { |
484 | | /* no protocols loaded, skip loading servers */ |
485 | 2 | return NULL; |
486 | 2 | } |
487 | 0 | proto = chat_protocol_get_default(); |
488 | 0 | chatnetrec = proto->create_chatnet(); |
489 | 0 | chatnetrec->chat_type = chat_protocol_get_default()->id; |
490 | 0 | chatnetrec->name = g_strdup(chatnet); |
491 | 0 | chatnet_create(chatnetrec); |
492 | 0 | } |
493 | 28 | } else { |
494 | 0 | chatnetrec = NULL; |
495 | 0 | proto = chat_protocol_get_default(); |
496 | 0 | if (proto == NULL) { |
497 | | /* no protocols loaded, skip loading servers */ |
498 | 0 | return NULL; |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | 26 | family = config_node_get_str(node, "family", ""); |
503 | | |
504 | 26 | rec = proto->create_server_setup(); |
505 | 26 | rec->type = module_get_uniq_id("SERVER SETUP", 0); |
506 | 26 | rec->chat_type = proto->id; |
507 | 26 | rec->chatnet = chatnetrec == NULL ? NULL : g_strdup(chatnetrec->name); |
508 | 26 | rec->family = g_ascii_strcasecmp(family, "inet6") == 0 ? |
509 | 26 | AF_INET6 : |
510 | 26 | (g_ascii_strcasecmp(family, "inet") == 0 ? AF_INET : 0); |
511 | 26 | rec->address = g_strdup(server); |
512 | 26 | rec->password = g_strdup(config_node_get_str(node, "password", NULL)); |
513 | | |
514 | 26 | rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE); |
515 | 26 | rec->tls_verify = config_node_find(node, "tls_verify") != NULL ? |
516 | 12 | config_node_get_bool(node, "tls_verify", TRUE) : |
517 | 26 | config_node_get_bool(node, "ssl_verify", TRUE); |
518 | | |
519 | 26 | value = config_node_get_str(node, "tls_cert", NULL); |
520 | 26 | if (value == NULL) |
521 | 26 | value = config_node_get_str(node, "ssl_cert", NULL); |
522 | 26 | rec->tls_cert = g_strdup(value); |
523 | | |
524 | 26 | value = config_node_get_str(node, "tls_pkey", NULL); |
525 | 26 | if (value == NULL) |
526 | 26 | value = config_node_get_str(node, "ssl_pkey", NULL); |
527 | 26 | rec->tls_pkey = g_strdup(value); |
528 | | |
529 | 26 | value = config_node_get_str(node, "tls_pass", NULL); |
530 | 26 | if (value == NULL) |
531 | 26 | value = config_node_get_str(node, "ssl_pass", NULL); |
532 | 26 | rec->tls_pass = g_strdup(value); |
533 | | |
534 | 26 | value = config_node_get_str(node, "tls_cafile", NULL); |
535 | 26 | if (value == NULL) |
536 | 26 | value = config_node_get_str(node, "ssl_cafile", NULL); |
537 | 26 | rec->tls_cafile = g_strdup(value); |
538 | | |
539 | 26 | value = config_node_get_str(node, "tls_capath", NULL); |
540 | 26 | if (value == NULL) |
541 | 26 | value = config_node_get_str(node, "ssl_capath", NULL); |
542 | 26 | rec->tls_capath = g_strdup(value); |
543 | | |
544 | 26 | value = config_node_get_str(node, "tls_ciphers", NULL); |
545 | 26 | if (value == NULL) |
546 | 26 | value = config_node_get_str(node, "ssl_ciphers", NULL); |
547 | 26 | rec->tls_ciphers = g_strdup(value); |
548 | | |
549 | 26 | value = config_node_get_str(node, "tls_pinned_cert", NULL); |
550 | 26 | if (value == NULL) |
551 | 26 | value = config_node_get_str(node, "ssl_pinned_cert", NULL); |
552 | 26 | rec->tls_pinned_cert = g_strdup(value); |
553 | | |
554 | 26 | value = config_node_get_str(node, "tls_pinned_pubkey", NULL); |
555 | 26 | if (value == NULL) |
556 | 26 | value = config_node_get_str(node, "ssl_pinned_pubkey", NULL); |
557 | 26 | rec->tls_pinned_pubkey = g_strdup(value); |
558 | | |
559 | 26 | rec->port = port; |
560 | 26 | rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE); |
561 | 26 | rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE); |
562 | 26 | rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL)); |
563 | | |
564 | 26 | signal_emit("server setup read", 2, rec, node); |
565 | | |
566 | 26 | setupservers = g_slist_append(setupservers, rec); |
567 | 26 | return rec; |
568 | 28 | } |
569 | | |
570 | | static int compare_server_setup (CONFIG_NODE *node, SERVER_SETUP_REC *server) |
571 | 0 | { |
572 | 0 | char *address, *chatnet; |
573 | 0 | int port; |
574 | | |
575 | | /* skip comment nodes */ |
576 | 0 | if (node->type == NODE_TYPE_COMMENT) |
577 | 0 | return -1; |
578 | | |
579 | 0 | address = config_node_get_str(node, "address", NULL); |
580 | 0 | chatnet = config_node_get_str(node, "chatnet", ""); |
581 | 0 | port = config_node_get_int(node, "port", 0); |
582 | |
|
583 | 0 | if (address == NULL || chatnet == NULL) { |
584 | 0 | return 0; |
585 | 0 | } |
586 | | |
587 | 0 | if (g_ascii_strcasecmp(address, server->address) != 0 || |
588 | 0 | g_ascii_strcasecmp(chatnet, server->chatnet != NULL ? server->chatnet : "") != 0 || |
589 | 0 | port != server->port) { |
590 | 0 | return 1; |
591 | 0 | } |
592 | | |
593 | 0 | return 0; |
594 | 0 | } |
595 | | |
596 | | static void server_setup_save(SERVER_SETUP_REC *rec, int old_port, const char *old_chatnet) |
597 | 0 | { |
598 | 0 | CONFIG_NODE *parent_node, *node; |
599 | 0 | SERVER_SETUP_REC search_rec = { 0 }; |
600 | 0 | GSList *config_node; |
601 | |
|
602 | 0 | parent_node = iconfig_node_traverse("(servers", TRUE); |
603 | | |
604 | | /* Try to find this channel in the configuration */ |
605 | 0 | search_rec.address = rec->address; |
606 | 0 | search_rec.chatnet = old_chatnet != NULL ? (char *) old_chatnet : rec->chatnet; |
607 | 0 | search_rec.port = old_port; |
608 | 0 | config_node = g_slist_find_custom(parent_node->value, &search_rec, |
609 | 0 | (GCompareFunc) compare_server_setup); |
610 | 0 | if (config_node != NULL) |
611 | | /* Let's update this server record */ |
612 | 0 | node = config_node->data; |
613 | 0 | else |
614 | | /* Create a brand-new server record */ |
615 | 0 | node = iconfig_node_section(parent_node, NULL, NODE_TYPE_BLOCK); |
616 | |
|
617 | 0 | iconfig_node_clear(node); |
618 | 0 | iconfig_node_set_str(node, "address", rec->address); |
619 | 0 | iconfig_node_set_str(node, "chatnet", rec->chatnet); |
620 | |
|
621 | 0 | iconfig_node_set_int(node, "port", rec->port); |
622 | 0 | iconfig_node_set_str(node, "password", rec->password); |
623 | |
|
624 | 0 | iconfig_node_set_bool(node, "use_tls", rec->use_tls); |
625 | 0 | iconfig_node_set_str(node, "tls_cert", rec->tls_cert); |
626 | 0 | iconfig_node_set_str(node, "tls_pkey", rec->tls_pkey); |
627 | 0 | iconfig_node_set_str(node, "tls_pass", rec->tls_pass); |
628 | 0 | iconfig_node_set_bool(node, "tls_verify", rec->tls_verify); |
629 | 0 | iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile); |
630 | 0 | iconfig_node_set_str(node, "tls_capath", rec->tls_capath); |
631 | 0 | iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers); |
632 | 0 | iconfig_node_set_str(node, "tls_pinned_cert", rec->tls_pinned_cert); |
633 | 0 | iconfig_node_set_str(node, "tls_pinned_pubkey", rec->tls_pinned_pubkey); |
634 | |
|
635 | 0 | iconfig_node_set_str(node, "own_host", rec->own_host); |
636 | |
|
637 | 0 | iconfig_node_set_str(node, "family", |
638 | 0 | rec->family == AF_INET6 ? "inet6" : |
639 | 0 | rec->family == AF_INET ? "inet" : NULL); |
640 | |
|
641 | 0 | if (rec->autoconnect) |
642 | 0 | iconfig_node_set_bool(node, "autoconnect", TRUE); |
643 | 0 | if (rec->no_proxy) |
644 | 0 | iconfig_node_set_bool(node, "no_proxy", TRUE); |
645 | |
|
646 | 0 | signal_emit("server setup saved", 2, rec, node); |
647 | 0 | } |
648 | | |
649 | | static void server_setup_remove_config(SERVER_SETUP_REC *rec) |
650 | 0 | { |
651 | 0 | CONFIG_NODE *parent_node; |
652 | 0 | GSList *config_node; |
653 | |
|
654 | 0 | parent_node = iconfig_node_traverse("servers", FALSE); |
655 | |
|
656 | 0 | if (parent_node == NULL) |
657 | 0 | return; |
658 | | |
659 | | /* Try to find this server in the configuration */ |
660 | 0 | config_node = g_slist_find_custom(parent_node->value, rec, |
661 | 0 | (GCompareFunc)compare_server_setup); |
662 | |
|
663 | 0 | if (config_node != NULL) |
664 | | /* Delete the server from the configuration */ |
665 | 0 | iconfig_node_remove(parent_node, config_node->data); |
666 | 0 | } |
667 | | |
668 | | static void server_setup_destroy(SERVER_SETUP_REC *rec) |
669 | 0 | { |
670 | 0 | setupservers = g_slist_remove(setupservers, rec); |
671 | 0 | signal_emit("server setup destroyed", 1, rec); |
672 | |
|
673 | 0 | g_free_not_null(rec->own_host); |
674 | 0 | g_free_not_null(rec->own_ip4); |
675 | 0 | g_free_not_null(rec->own_ip6); |
676 | 0 | g_free_not_null(rec->chatnet); |
677 | 0 | g_free_not_null(rec->password); |
678 | 0 | g_free_not_null(rec->tls_cert); |
679 | 0 | g_free_not_null(rec->tls_pkey); |
680 | 0 | g_free_not_null(rec->tls_pass); |
681 | 0 | g_free_not_null(rec->tls_cafile); |
682 | 0 | g_free_not_null(rec->tls_capath); |
683 | 0 | g_free_not_null(rec->tls_ciphers); |
684 | 0 | g_free_not_null(rec->tls_pinned_cert); |
685 | 0 | g_free_not_null(rec->tls_pinned_pubkey); |
686 | 0 | g_free(rec->address); |
687 | 0 | g_free(rec); |
688 | 0 | } |
689 | | |
690 | | void server_setup_modify(SERVER_SETUP_REC *rec, int old_port, const char *old_chatnet) |
691 | 0 | { |
692 | 0 | g_return_if_fail(g_slist_find(setupservers, rec) != NULL); |
693 | | |
694 | 0 | rec->type = module_get_uniq_id("SERVER SETUP", 0); |
695 | 0 | server_setup_save(rec, old_port, old_chatnet); |
696 | |
|
697 | 0 | signal_emit("server setup updated", 1, rec); |
698 | 0 | } |
699 | | |
700 | | void server_setup_add(SERVER_SETUP_REC *rec) |
701 | 0 | { |
702 | 0 | if (g_slist_find(setupservers, rec) == NULL) |
703 | 0 | setupservers = g_slist_append(setupservers, rec); |
704 | 0 | server_setup_modify(rec, -1, NULL); |
705 | 0 | } |
706 | | |
707 | | void server_setup_remove_chatnet(const char *chatnet) |
708 | 0 | { |
709 | 0 | GSList *tmp, *next; |
710 | |
|
711 | 0 | g_return_if_fail(chatnet != NULL); |
712 | | |
713 | 0 | for (tmp = setupservers; tmp != NULL; tmp = next) { |
714 | 0 | SERVER_SETUP_REC *rec = tmp->data; |
715 | |
|
716 | 0 | next = tmp->next; |
717 | 0 | if (g_ascii_strcasecmp(rec->chatnet, chatnet) == 0) |
718 | 0 | server_setup_remove(rec); |
719 | 0 | } |
720 | 0 | } |
721 | | |
722 | | void server_setup_remove(SERVER_SETUP_REC *rec) |
723 | 0 | { |
724 | 0 | server_setup_remove_config(rec); |
725 | 0 | server_setup_destroy(rec); |
726 | 0 | } |
727 | | |
728 | | static void read_servers(void) |
729 | 2 | { |
730 | 2 | CONFIG_NODE *node; |
731 | 2 | GSList *tmp; |
732 | | |
733 | 2 | while (setupservers != NULL) |
734 | 0 | server_setup_destroy(setupservers->data); |
735 | | |
736 | | /* Read servers */ |
737 | 2 | node = iconfig_node_traverse("servers", FALSE); |
738 | 2 | if (node != NULL) { |
739 | 2 | int i = 0; |
740 | 2 | tmp = config_node_first(node->value); |
741 | 30 | for (; tmp != NULL; tmp = config_node_next(tmp), i++) { |
742 | 28 | node = tmp->data; |
743 | 28 | if (node->type != NODE_TYPE_BLOCK) { |
744 | 0 | g_critical("Expected block node at `servers[%d]' was of %s type. " |
745 | 0 | "Corrupt config?", |
746 | 0 | i, node->type == NODE_TYPE_LIST ? "list" : "scalar"); |
747 | 28 | } else { |
748 | 28 | server_setup_read(node); |
749 | 28 | } |
750 | 28 | } |
751 | 2 | } |
752 | 2 | } |
753 | | |
754 | | static void read_settings(void) |
755 | 8 | { |
756 | 8 | if (old_source_host == NULL || |
757 | 8 | g_strcmp0(old_source_host, settings_get_str("hostname")) != 0) { |
758 | 8 | g_free_not_null(old_source_host); |
759 | 8 | old_source_host = g_strdup(settings_get_str("hostname")); |
760 | | |
761 | 8 | source_host_ok = FALSE; |
762 | 8 | get_source_host_ip(); |
763 | 8 | } |
764 | 8 | } |
765 | | |
766 | | void servers_setup_init(void) |
767 | 8 | { |
768 | 8 | settings_add_str("server", "hostname", ""); |
769 | | |
770 | 8 | settings_add_str("server", "nick", NULL); |
771 | 8 | settings_add_str("server", "user_name", NULL); |
772 | 8 | settings_add_str("server", "real_name", NULL); |
773 | | |
774 | 8 | settings_add_bool("proxy", "use_proxy", FALSE); |
775 | 8 | settings_add_str("proxy", "proxy_address", ""); |
776 | 8 | settings_add_int("proxy", "proxy_port", 6667); |
777 | 8 | settings_add_str("proxy", "proxy_string", "CONNECT %s %d"); |
778 | 8 | settings_add_str("proxy", "proxy_string_after", ""); |
779 | 8 | settings_add_str("proxy", "proxy_password", ""); |
780 | | |
781 | 8 | setupservers = NULL; |
782 | 8 | source_host_ip4 = source_host_ip6 = NULL; |
783 | 8 | old_source_host = NULL; |
784 | 8 | read_settings(); |
785 | | |
786 | 8 | signal_add("setup changed", (SIGNAL_FUNC) read_settings); |
787 | 8 | signal_add("setup reread servers", (SIGNAL_FUNC) read_servers); |
788 | 8 | } |
789 | | |
790 | | void servers_setup_deinit(void) |
791 | 0 | { |
792 | 0 | g_free_not_null(source_host_ip4); |
793 | 0 | g_free_not_null(source_host_ip6); |
794 | 0 | g_free_not_null(old_source_host); |
795 | |
|
796 | 0 | while (setupservers != NULL) |
797 | 0 | server_setup_destroy(setupservers->data); |
798 | |
|
799 | 0 | signal_remove("setup changed", (SIGNAL_FUNC) read_settings); |
800 | 0 | signal_remove("setup reread servers", (SIGNAL_FUNC) read_servers); |
801 | |
|
802 | 0 | module_uniq_destroy("SERVER SETUP"); |
803 | 0 | } |