d0e6bb7e934af4ea2aaae120171c0e4516b14611
[silc.git] / apps / irssi / src / core / servers.c
1 /*
2  server.c : irssi
3
4     Copyright (C) 1999-2000 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 "signals.h"
23 #include "commands.h"
24 #include "net-disconnect.h"
25 #include "net-nonblock.h"
26 #include "net-sendbuffer.h"
27 #include "misc.h"
28 #include "rawlog.h"
29 #include "settings.h"
30
31 #include "chat-protocols.h"
32 #include "servers.h"
33 #include "servers-reconnect.h"
34 #include "servers-setup.h"
35 #include "channels.h"
36 #include "queries.h"
37
38 GSList *servers, *lookup_servers;
39
40 /* connection to server failed */
41 void server_connect_failed(SERVER_REC *server, const char *msg)
42 {
43         g_return_if_fail(IS_SERVER(server));
44
45         lookup_servers = g_slist_remove(lookup_servers, server);
46
47         signal_emit("server connect failed", 2, server, msg);
48
49         if (server->connect_tag != -1) {
50                 g_source_remove(server->connect_tag);
51                 server->connect_tag = -1;
52         }
53         if (server->handle != NULL) {
54                 net_sendbuffer_destroy(server->handle, TRUE);
55                 server->handle = NULL;
56         }
57
58         if (server->connect_pipe[0] != NULL) {
59                 g_io_channel_close(server->connect_pipe[0]);
60                 g_io_channel_unref(server->connect_pipe[0]);
61                 g_io_channel_close(server->connect_pipe[1]);
62                 g_io_channel_unref(server->connect_pipe[1]);
63                 server->connect_pipe[0] = NULL;
64                 server->connect_pipe[1] = NULL;
65         }
66
67         server_unref(server);
68 }
69
70 /* generate tag from server's address */
71 static char *server_create_address_tag(const char *address)
72 {
73         const char *start, *end;
74
75         g_return_val_if_fail(address != NULL, NULL);
76
77         /* try to generate a reasonable server tag */
78         if (strchr(address, '.') == NULL) {
79                 start = end = NULL;
80         } else if (g_ascii_strncasecmp(address, "irc", 3) == 0 ||
81             g_ascii_strncasecmp(address, "chat", 4) == 0) {
82                 /* irc-2.cs.hut.fi -> hut, chat.bt.net -> bt */
83                 end = strrchr(address, '.');
84                 start = end-1;
85                 while (start > address && *start != '.') start--;
86         } else {
87                 /* efnet.cs.hut.fi -> efnet */
88                 end = strchr(address, '.');
89                 start = end;
90         }
91
92         if (start == end) start = address; else start++;
93         if (end == NULL) end = address + strlen(address);
94
95         return g_strndup(start, (int) (end-start));
96 }
97
98 /* create unique tag for server. prefer ircnet's name or
99    generate it from server's address */
100 static char *server_create_tag(SERVER_CONNECT_REC *conn)
101 {
102         GString *str;
103         char *tag;
104         int num;
105
106         g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
107
108         tag = conn->chatnet != NULL && *conn->chatnet != '\0' ?
109                 g_strdup(conn->chatnet) :
110                 server_create_address_tag(conn->address);
111
112         if (conn->tag != NULL && server_find_tag(conn->tag) == NULL &&
113             server_find_lookup_tag(conn->tag) == NULL &&
114             strncmp(conn->tag, tag, strlen(tag)) == 0) {
115                 /* use the existing tag if it begins with the same ID -
116                    this is useful when you have several connections to
117                    same server and you want to keep the same tags with
118                    the servers (or it would cause problems when rejoining
119                    /LAYOUT SAVEd channels). */
120                 g_free(tag);
121                 return g_strdup(conn->tag);
122         }
123
124
125         /* then just append numbers after tag until unused is found.. */
126         str = g_string_new(tag);
127
128         num = 2;
129         while (server_find_tag(str->str) != NULL ||
130                server_find_lookup_tag(str->str) != NULL) {
131                 g_string_printf(str, "%s%d", tag, num);
132                 num++;
133         }
134         g_free(tag);
135
136         tag = str->str;
137         g_string_free(str, FALSE);
138         return tag;
139 }
140
141 /* Connection to server finished, fill the rest of the fields */
142 void server_connect_finished(SERVER_REC *server)
143 {
144         server->connect_time = time(NULL);
145
146         servers = g_slist_append(servers, server);
147         signal_emit("server connected", 1, server);
148 }
149
150 static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
151 {
152         int error;
153
154         g_return_if_fail(IS_SERVER(server));
155
156         error = net_geterror(handle);
157         if (error != 0) {
158                 server->connection_lost = TRUE;
159                 server_connect_failed(server, g_strerror(error));
160                 return;
161         }
162
163         lookup_servers = g_slist_remove(lookup_servers, server);
164         g_source_remove(server->connect_tag);
165         server->connect_tag = -1;
166
167         server_connect_finished(server);
168 }
169
170 #ifdef HAVE_OPENSSL
171 static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle)
172 {
173         int error;
174
175         g_return_if_fail(IS_SERVER(server));
176
177         error = irssi_ssl_handshake(handle);
178         if (error == -1) {
179                 server->connection_lost = TRUE;
180                 server_connect_failed(server, NULL);
181                 return;
182         }
183         if (error & 1) {
184                 if (server->connect_tag != -1)
185                         g_source_remove(server->connect_tag);
186                 server->connect_tag = g_input_add(handle, error == 1 ? G_INPUT_READ : G_INPUT_WRITE,
187                                                   (GInputFunction)
188                                                   server_connect_callback_init_ssl,
189                                                   server);
190                 return;
191         }
192
193         lookup_servers = g_slist_remove(lookup_servers, server);
194         if (server->connect_tag != -1) {
195                 g_source_remove(server->connect_tag);
196                 server->connect_tag = -1;
197         }
198
199         server_connect_finished(server);
200 }
201 #endif
202
203 static void server_real_connect(SERVER_REC *server, IPADDR *ip,
204                                 const char *unix_socket)
205 {
206         GIOChannel *handle;
207         IPADDR *own_ip = NULL;
208         const char *errmsg;
209         char *errmsg2;
210         char ipaddr[MAX_IP_LEN];
211         int port;
212
213         g_return_if_fail(ip != NULL || unix_socket != NULL);
214
215         signal_emit("server connecting", 2, server, ip);
216
217         if (server->connrec->no_connect)
218                 return;
219
220         if (ip != NULL) {
221                 own_ip = ip == NULL ? NULL :
222                         (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 :
223                          server->connrec->own_ip4);
224                 port = server->connrec->proxy != NULL ?
225                         server->connrec->proxy_port : server->connrec->port;
226                 handle = server->connrec->use_ssl ?
227                         net_connect_ip_ssl(ip, port, own_ip, server) : net_connect_ip(ip, port, own_ip);
228         } else {
229                 handle = net_connect_unix(unix_socket);
230         }
231
232         if (handle == NULL) {
233                 /* failed */
234                 errmsg = g_strerror(errno);
235                 errmsg2 = NULL;
236                 if (errno == EADDRNOTAVAIL) {
237                         if (own_ip != NULL) {
238                                 /* show the IP which is causing the error */
239                                 net_ip2host(own_ip, ipaddr);
240                                 errmsg2 = g_strconcat(errmsg, ": ", ipaddr, NULL);
241                         }
242                         server->no_reconnect = TRUE;
243                 }
244                 if (server->connrec->use_ssl && errno == ENOSYS)
245                         server->no_reconnect = TRUE;
246
247                 server->connection_lost = TRUE;
248                 server_connect_failed(server, errmsg2 ? errmsg2 : errmsg);
249                 g_free(errmsg2);
250         } else {
251                 server->handle = net_sendbuffer_create(handle, 0);
252 #ifdef HAVE_OPENSSL
253                 if (server->connrec->use_ssl)
254                         server_connect_callback_init_ssl(server, handle);
255                 else
256 #endif
257                 server->connect_tag =
258                         g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
259                                     (GInputFunction)
260                                     server_connect_callback_init,
261                                     server);
262         }
263 }
264
265 static void server_connect_callback_readpipe(SERVER_REC *server)
266 {
267         RESOLVED_IP_REC iprec;
268         IPADDR *ip;
269         const char *errormsg;
270         char *servername = NULL;
271
272         g_source_remove(server->connect_tag);
273         server->connect_tag = -1;
274
275         net_gethostbyname_return(server->connect_pipe[0], &iprec);
276
277         g_io_channel_close(server->connect_pipe[0]);
278         g_io_channel_unref(server->connect_pipe[0]);
279         g_io_channel_close(server->connect_pipe[1]);
280         g_io_channel_unref(server->connect_pipe[1]);
281
282         server->connect_pipe[0] = NULL;
283         server->connect_pipe[1] = NULL;
284
285         /* figure out if we should use IPv4 or v6 address */
286         if (iprec.error != 0) {
287                 /* error */
288                 ip = NULL;
289         } else if (server->connrec->family == AF_INET) {
290                 /* force IPv4 connection */
291                 ip = iprec.ip4.family == 0 ? NULL : &iprec.ip4;
292                 servername = iprec.host4;
293         } else if (server->connrec->family == AF_INET6) {
294                 /* force IPv6 connection */
295                 ip = iprec.ip6.family == 0 ? NULL : &iprec.ip6;
296                 servername = iprec.host6;
297         } else {
298                 /* pick the one that was found, or if both do it like
299                    /SET resolve_prefer_ipv6 says. */
300                 if (iprec.ip4.family == 0 ||
301                     (iprec.ip6.family != 0 &&
302                      settings_get_bool("resolve_prefer_ipv6"))) {
303                         ip = &iprec.ip6;
304                         servername = iprec.host6;
305                 } else {
306                         ip = &iprec.ip4;
307                         servername = iprec.host4;
308                 }
309         }
310
311         if (ip != NULL) {
312                 /* host lookup ok */
313                 if (servername) {
314                         g_free(server->connrec->address);
315                         server->connrec->address = g_strdup(servername);
316                 }
317                 server_real_connect(server, ip, NULL);
318                 errormsg = NULL;
319         } else {
320                 if (iprec.error == 0 || net_hosterror_notfound(iprec.error)) {
321                         /* IP wasn't found for the host, don't try to
322                            reconnect back to this server */
323                         server->dns_error = TRUE;
324                 }
325
326                 if (iprec.error == 0) {
327                         /* forced IPv4 or IPv6 address but it wasn't found */
328                         errormsg = server->connrec->family == AF_INET ?
329                                 "IPv4 address not found for host" :
330                                 "IPv6 address not found for host";
331                 } else {
332                         /* gethostbyname() failed */
333                         errormsg = iprec.errorstr != NULL ? iprec.errorstr :
334                                 "Host lookup failed";
335                 }
336
337                 server->connection_lost = TRUE;
338                 server_connect_failed(server, errormsg);
339         }
340
341         g_free(iprec.errorstr);
342         g_free(iprec.host4);
343         g_free(iprec.host6);
344 }
345
346 SERVER_REC *server_connect(SERVER_CONNECT_REC *conn)
347 {
348         CHAT_PROTOCOL_REC *proto;
349         SERVER_REC *server;
350
351         proto = CHAT_PROTOCOL(conn);
352         server = proto->server_init_connect(conn);
353         proto->server_connect(server);
354
355         return server;
356 }
357
358 /* initializes server record but doesn't start connecting */
359 void server_connect_init(SERVER_REC *server)
360 {
361         const char *str;
362
363         g_return_if_fail(server != NULL);
364
365         MODULE_DATA_INIT(server);
366         server->type = module_get_uniq_id("SERVER", 0);
367         server_ref(server);
368
369         server->nick = g_strdup(server->connrec->nick);
370         if (server->connrec->username == NULL || *server->connrec->username == '\0') {
371                 g_free_not_null(server->connrec->username);
372
373                 str = g_get_user_name();
374                 if (*str == '\0') str = "unknown";
375                 server->connrec->username = g_strdup(str);
376         }
377         if (server->connrec->realname == NULL || *server->connrec->realname == '\0') {
378                 g_free_not_null(server->connrec->realname);
379
380                 str = g_get_real_name();
381                 if (*str == '\0') str = server->connrec->username;
382                 server->connrec->realname = g_strdup(str);
383         }
384
385         server->tag = server_create_tag(server->connrec);
386         server->connect_tag = -1;
387 }
388
389 /* starts connecting to server */
390 int server_start_connect(SERVER_REC *server)
391 {
392         const char *connect_address;
393         int fd[2];
394
395         g_return_val_if_fail(server != NULL, FALSE);
396         if (!server->connrec->unix_socket && server->connrec->port <= 0)
397                 return FALSE;
398
399         server->rawlog = rawlog_create();
400
401         if (server->connrec->connect_handle != NULL) {
402                 /* already connected */
403                 GIOChannel *handle = server->connrec->connect_handle;
404
405                 server->connrec->connect_handle = NULL;
406                 server->handle = net_sendbuffer_create(handle, 0);
407                 server_connect_finished(server);
408         } else if (server->connrec->unix_socket) {
409                 /* connect with unix socket */
410                 server_real_connect(server, NULL, server->connrec->address);
411         } else {
412                 /* resolve host name */
413                 if (pipe(fd) != 0) {
414                         g_warning("server_connect(): pipe() failed.");
415                         g_free(server->tag);
416                         g_free(server->nick);
417                         return FALSE;
418                 }
419
420                 server->connect_pipe[0] = g_io_channel_new(fd[0]);
421                 server->connect_pipe[1] = g_io_channel_new(fd[1]);
422
423                 connect_address = server->connrec->proxy != NULL ?
424                         server->connrec->proxy : server->connrec->address;
425                 server->connect_pid =
426                         net_gethostbyname_nonblock(connect_address,
427                                                    server->connect_pipe[1],
428                                                    settings_get_bool("resolve_reverse_lookup"));
429                 server->connect_tag =
430                         g_input_add(server->connect_pipe[0], G_INPUT_READ,
431                                     (GInputFunction)
432                                     server_connect_callback_readpipe,
433                                     server);
434
435                 lookup_servers = g_slist_append(lookup_servers, server);
436
437                 signal_emit("server looking", 1, server);
438         }
439         return TRUE;
440 }
441
442 static int server_remove_channels(SERVER_REC *server)
443 {
444         GSList *tmp, *next;
445         int found;
446
447         g_return_val_if_fail(server != NULL, FALSE);
448
449         found = FALSE;
450         for (tmp = server->channels; tmp != NULL; tmp = next) {
451                 CHANNEL_REC *channel = tmp->data;
452
453                 next = tmp->next;
454                 channel_destroy(channel);
455                 found = TRUE;
456         }
457
458         while (server->queries != NULL)
459                 query_change_server(server->queries->data, NULL);
460
461         g_slist_free(server->channels);
462         g_slist_free(server->queries);
463
464         return found;
465 }
466
467 void server_disconnect(SERVER_REC *server)
468 {
469         int chans;
470
471         g_return_if_fail(IS_SERVER(server));
472
473         if (server->disconnected)
474                 return;
475
476         if (server->connect_tag != -1) {
477                 /* still connecting to server.. */
478                 if (server->connect_pid != -1)
479                         net_disconnect_nonblock(server->connect_pid);
480                 server_connect_failed(server, NULL);
481                 return;
482         }
483
484         servers = g_slist_remove(servers, server);
485
486         server->disconnected = TRUE;
487         signal_emit("server disconnected", 1, server);
488
489         /* close all channels */
490         chans = server_remove_channels(server);
491
492         if (server->handle != NULL) {
493                 if (!chans || server->connection_lost)
494                         net_sendbuffer_destroy(server->handle, TRUE);
495                 else {
496                         /* we were on some channels, try to let the server
497                            disconnect so that our quit message is guaranteed
498                            to get displayed */
499                         net_disconnect_later(net_sendbuffer_handle(server->handle));
500                         net_sendbuffer_destroy(server->handle, FALSE);
501                 }
502                 server->handle = NULL;
503         }
504
505         if (server->readtag > 0) {
506                 g_source_remove(server->readtag);
507                 server->readtag = -1;
508         }
509
510         server_unref(server);
511 }
512
513 void server_ref(SERVER_REC *server)
514 {
515         g_return_if_fail(IS_SERVER(server));
516
517         server->refcount++;
518 }
519
520 int server_unref(SERVER_REC *server)
521 {
522         g_return_val_if_fail(IS_SERVER(server), FALSE);
523
524         if (--server->refcount > 0)
525                 return TRUE;
526
527         if (g_slist_find(servers, server) != NULL) {
528                 g_warning("Non-referenced server wasn't disconnected");
529                 server_disconnect(server);
530                 return TRUE;
531         }
532
533         MODULE_DATA_DEINIT(server);
534         server_connect_unref(server->connrec);
535         if (server->rawlog != NULL) rawlog_destroy(server->rawlog);
536         g_free(server->version);
537         g_free(server->away_reason);
538         g_free(server->nick);
539         g_free(server->tag);
540
541         server->type = 0;
542         g_free(server);
543         return FALSE;
544 }
545
546 SERVER_REC *server_find_tag(const char *tag)
547 {
548         GSList *tmp;
549
550         g_return_val_if_fail(tag != NULL, NULL);
551         if (*tag == '\0') return NULL;
552
553         for (tmp = servers; tmp != NULL; tmp = tmp->next) {
554                 SERVER_REC *server = tmp->data;
555
556                 if (g_strcasecmp(server->tag, tag) == 0)
557                         return server;
558         }
559
560         return NULL;
561 }
562
563 SERVER_REC *server_find_lookup_tag(const char *tag)
564 {
565         GSList *tmp;
566
567         g_return_val_if_fail(tag != NULL, NULL);
568         if (*tag == '\0') return NULL;
569
570         for (tmp = lookup_servers; tmp != NULL; tmp = tmp->next) {
571                 SERVER_REC *server = tmp->data;
572
573                 if (g_strcasecmp(server->tag, tag) == 0)
574                         return server;
575         }
576
577         return NULL;
578 }
579
580 SERVER_REC *server_find_chatnet(const char *chatnet)
581 {
582         GSList *tmp;
583
584         g_return_val_if_fail(chatnet != NULL, NULL);
585         if (*chatnet == '\0') return NULL;
586
587         for (tmp = servers; tmp != NULL; tmp = tmp->next) {
588                 SERVER_REC *server = tmp->data;
589
590                 if (server->connrec->chatnet != NULL &&
591                     g_strcasecmp(server->connrec->chatnet, chatnet) == 0)
592                         return server;
593         }
594
595         return NULL;
596 }
597
598 void server_connect_ref(SERVER_CONNECT_REC *conn)
599 {
600         conn->refcount++;
601 }
602
603 void server_connect_unref(SERVER_CONNECT_REC *conn)
604 {
605         g_return_if_fail(IS_SERVER_CONNECT(conn));
606
607         if (--conn->refcount > 0)
608                 return;
609         if (conn->refcount < 0) {
610                 g_warning("Connection '%s' refcount = %d",
611                           conn->tag, conn->refcount);
612         }
613
614         CHAT_PROTOCOL(conn)->destroy_server_connect(conn);
615
616         if (conn->connect_handle != NULL)
617                 net_disconnect(conn->connect_handle);
618
619         g_free_not_null(conn->proxy);
620         g_free_not_null(conn->proxy_string);
621         g_free_not_null(conn->proxy_string_after);
622         g_free_not_null(conn->proxy_password);
623
624         g_free_not_null(conn->tag);
625         g_free_not_null(conn->address);
626         g_free_not_null(conn->chatnet);
627
628         g_free_not_null(conn->own_ip4);
629         g_free_not_null(conn->own_ip6);
630
631         g_free_not_null(conn->password);
632         g_free_not_null(conn->nick);
633         g_free_not_null(conn->username);
634         g_free_not_null(conn->realname);
635
636         g_free_not_null(conn->ssl_cert);
637         g_free_not_null(conn->ssl_pkey);
638         g_free_not_null(conn->ssl_cafile);
639         g_free_not_null(conn->ssl_capath);
640
641         g_free_not_null(conn->channels);
642         g_free_not_null(conn->away_reason);
643
644         conn->type = 0;
645         g_free(conn);
646 }
647
648 void server_change_nick(SERVER_REC *server, const char *nick)
649 {
650         g_free(server->nick);
651         server->nick = g_strdup(nick);
652
653         signal_emit("server nick changed", 1, server);
654 }
655
656 /* Update own IPv4 and IPv6 records */
657 void server_connect_own_ip_save(SERVER_CONNECT_REC *conn,
658                                 IPADDR *ip4, IPADDR *ip6)
659 {
660         if (ip4 == NULL || ip4->family == 0)
661                 g_free_and_null(conn->own_ip4);
662         if (ip6 == NULL || ip6->family == 0)
663                 g_free_and_null(conn->own_ip6);
664
665         if (ip4 != NULL && ip4->family != 0) {
666                 /* IPv4 address was found */
667                 if (conn->own_ip4 == NULL)
668                         conn->own_ip4 = g_new0(IPADDR, 1);
669                 memcpy(conn->own_ip4, ip4, sizeof(IPADDR));
670         }
671
672         if (ip6 != NULL && ip6->family != 0) {
673                 /* IPv6 address was found */
674                 if (conn->own_ip6 == NULL)
675                         conn->own_ip6 = g_new0(IPADDR, 1);
676                 memcpy(conn->own_ip6, ip6, sizeof(IPADDR));
677         }
678 }
679
680 /* `optlist' should contain only one unknown key - the server tag.
681    returns NULL if there was unknown -option */
682 SERVER_REC *cmd_options_get_server(const char *cmd,
683                                    GHashTable *optlist,
684                                    SERVER_REC *defserver)
685 {
686         SERVER_REC *server;
687         GSList *list, *tmp, *next;
688
689         /* get all the options, then remove the known ones. there should
690            be only one left - the server tag. */
691         list = hashtable_get_keys(optlist);
692         if (cmd != NULL) {
693                 for (tmp = list; tmp != NULL; tmp = next) {
694                         char *option = tmp->data;
695                         next = tmp->next;
696
697                         if (command_have_option(cmd, option))
698                                 list = g_slist_remove(list, option);
699                 }
700         }
701
702         if (list == NULL)
703                 return defserver;
704
705         server = server_find_tag(list->data);
706         if (server == NULL || list->next != NULL) {
707                 /* unknown option (not server tag) */
708                 signal_emit("error command", 2,
709                             GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN),
710                             server == NULL ? list->data : list->next->data);
711                 signal_stop();
712
713                 server = NULL;
714         }
715
716         g_slist_free(list);
717         return server;
718 }
719
720 static void disconnect_servers(GSList *servers, int chat_type)
721 {
722         GSList *tmp, *next;
723
724         for (tmp = servers; tmp != NULL; tmp = next) {
725                 SERVER_REC *rec = tmp->data;
726
727                 next = tmp->next;
728                 if (rec->chat_type == chat_type)
729                         server_disconnect(rec);
730         }
731 }
732
733 static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
734 {
735         disconnect_servers(servers, proto->id);
736         disconnect_servers(lookup_servers, proto->id);
737 }
738
739 void servers_init(void)
740 {
741         settings_add_bool("server", "resolve_prefer_ipv6", FALSE);
742         settings_add_bool("server", "resolve_reverse_lookup", FALSE);
743         lookup_servers = servers = NULL;
744
745         signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
746
747         servers_reconnect_init();
748         servers_setup_init();
749 }
750
751 void servers_deinit(void)
752 {
753         signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
754
755         servers_setup_deinit();
756         servers_reconnect_deinit();
757
758         module_uniq_destroy("SERVER");
759         module_uniq_destroy("SERVER CONNECT");
760 }