1 /*
2  * server.c: interactions with servers, tcp code, and DNS resolver
3  *
4  * Copyright(c) 1997-2000 - All Rights Reserved
5  *
6  * See the COPYRIGHT file.
7  */
8 
9 #ifndef lint
10 static char rcsid[] = "@(#)$Id: server.c,v 1.83 2001/08/01 01:21:42 kalt Exp $";
11 #endif
12 
13 #include "os.h"
14 
15 #if HAVE_SYS_FILIO_H
16 # include <sys/filio.h>
17 #endif
18 
19 #include "struct.h"
20 #include "term.h"
21 #include "window.h"
22 #include "option.h"
23 #include "config.h"
24 #include "utils.h"
25 #include "server.h"
26 
27 extern char		need_collect;
28 extern struct window_	*current;
29 
30 extern void sic_format(char *, char *, char *, char *);
31 
32 static struct server_	*slist = NULL;
33 struct server_		*server = NULL;
34 
35 static int
sic_nextaddr(srv)36 sic_nextaddr(srv)
37 struct server_ *srv;
38 {
39 #if defined(HAVE_GETADDRINFO)
40   struct addrinfo *last;
41 
42   assert(srv->address);
43   assert(srv->ip);
44 
45   last = srv->addrip;
46   do
47     {
48       srv->addrip = srv->addrip->ai_next;
49       if (srv->addrip == NULL)
50 	  srv->addrip = srv->address;
51     }
52   while (srv->addrip->ai_family != PF_INET &&
53 	 srv->addrip->ai_family != PF_INET6);
54   free(srv->ip);
55   srv->ip = aitoip(srv->addrip);
56 
57   vsic_slog(LOG_DEBUG, "sic_nextaddr: srv->addrip %x, last %x",
58 	    srv->addrip, last);
59   if (srv->addrip != last)
60       return 1;
61 #endif
62   vsic_slog(LOG_DEBUG, "sic_nextaddr returns 0");
63   return 0;
64 }
65 
66 /* sic_connect: connect() to a server (given a struct server_ pointer) */
67 void
sic_connect(srv)68 sic_connect(srv)
69   struct server_ *srv;
70 {
71   struct  sockaddr_in	sock_sin;
72   int			fd, opt = 1, rc;
73   char			*vif;
74 
75   assert (srv->ip);	/* even if srv->addrip is defined */
76 
77   bzero((char *)&sock_sin, sizeof(sock_sin));
78   sock_sin.sin_family = AF_INET;
79 
80 #if defined(HAVE_GETADDRINFO)
81   if (srv->addrip)
82       fd = socket(srv->addrip->ai_family, SOCK_STREAM,
83 		  srv->addrip->ai_protocol);
84   else
85 #endif
86       fd = socket(AF_INET, SOCK_STREAM, 0);
87 
88   if (fd < 0)
89       vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno));
90   if (fd > 0 && (vif = getenv("SICIP")))
91     {
92       /* the following should be fixed rather than simply reporting an error */
93       sock_sin.sin_addr.s_addr = inet_addr(vif);
94       if (sock_sin.sin_addr.s_addr == INADDR_NONE)
95 	  vsic_slog(LOG_CLIENT, "--- Bad IP address: SICIP = %s", vif);
96       else
97 	  if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0)
98 	      vsic_slog(LOG_CLIENT, "--- Unable to bind to IP %s: %s", vif,
99 			strerror(errno));
100     }
101   if (fd > 0 && ioctl(fd, FIONBIO, &opt) < 0)
102       vsic_slog(LOG_CLIENT, "--- ioctl() for %s:%d failed: %s", srv->sname,
103 		srv->port, strerror(errno));
104 
105 #if defined(HAVE_GETADDRINFO)
106   if (srv->addrip)
107       rc = connect(fd, srv->addrip->ai_addr, srv->addrip->ai_addrlen);
108   else
109 #endif
110     {
111       sock_sin.sin_port = htons(srv->port);
112       sock_sin.sin_addr.s_addr = inet_addr(srv->ip);
113       rc = connect(fd, (struct sockaddr *)&sock_sin, sizeof(sock_sin));
114     }
115 
116   if (fd > 0 && rc < 0 && errno != EINPROGRESS)
117     {
118       rc = errno;
119       vsic_slog(LOG_CLIENT, "--- Connection to %s:%d failed: %s", srv->sname,
120 		srv->port, strerror(errno));
121       close(fd);
122       if (rc == EHOSTUNREACH && !option(srv->sopt, S_DCC))
123 	  fd = -2;
124       else
125 	  fd = -1;
126     }
127   unset_option(srv->sopt, S_RECONNECT);
128   unset_option(srv->sopt, S_QUIT);
129   switch (fd)
130     {
131     case -2: /* no route to host, may be there's another address to try? */
132 	unset_option(srv->sopt, S_CONNECTING);
133 	if (sic_nextaddr(srv))
134 	  {
135 	    set_option(srv->sopt, S_RECONNECT);
136 	    srv->ts = time(NULL) - 4;
137 	    break;
138 	  }
139 	srv->ts = time(NULL);
140 	break;
141     case -1: /* other type of failures */
142 	unset_option(srv->sopt, S_CONNECTING);
143 	if (!option(srv->sopt, S_DCC)) /* needed? */
144 	    srv->ts = time(NULL);
145 	break;
146     default: /* connection in progress */
147 	set_option(srv->sopt, S_CONNECTING);
148 
149 	if (!option(srv->sopt, S_CONFIG))
150 	    cfg_read(srv->sname);
151 	set_option(srv->sopt, S_CONFIG);
152 	break;
153     }
154   srv->fd = fd;
155   srv->lastr = 0;
156   srv->readbuf[0] = '\0';
157 }
158 
159 /* sic_listen: create a new socket, and bind it */
160 static void
sic_listen(dcc)161 sic_listen(dcc)
162   struct server_ *dcc;
163 {
164   struct  sockaddr_in	sock_sin;
165   int			fd, sz = sizeof(struct sockaddr_in);
166   char			*vif;
167 
168   if (getsockname(server->fd, (struct sockaddr *)&sock_sin, &sz) < 0)
169     {
170       vsic_slog(LOG_CLIENT, "--- Unable to get my own IP: %s",strerror(errno));
171       dcc->fd = -1;
172       return;
173     }
174   else
175       sz = sizeof(struct sockaddr_in);
176 
177   dcc->ip = strdup((char *)inet_ntoa(sock_sin.sin_addr));
178 
179   bzero((char *)&sock_sin, sizeof(sock_sin));
180   sock_sin.sin_family = AF_INET;
181 
182   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
183       vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno));
184   if (vif = getenv("SICIP"))
185       sock_sin.sin_addr.s_addr = inet_addr(vif);
186   else
187       sock_sin.sin_addr.s_addr = INADDR_ANY;
188   if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0)
189     {
190       vsic_slog(LOG_CLIENT, "--- bind() call failed: %s", strerror(errno));
191       close(fd);
192       fd = -1;
193     }
194   if (fd > 0 && listen(fd, 1) < 0)
195     {
196       vsic_slog(LOG_CLIENT, "--- listen() call failed: %s", strerror(errno));
197       close(fd);
198       fd = -1;
199     }
200   if (fd > 0)
201       if (getsockname(fd, (struct sockaddr *)&sock_sin, &sz) < 0)
202 	{
203 	  vsic_slog(LOG_CLIENT, "--- getsockname() call failed: %s",
204 		    strerror(errno));
205 	  free(dcc->ip);
206 	  close(fd);
207 	}
208       else
209 	{
210 	  dcc->port = sock_sin.sin_port;
211 	  dcc->sname = strdup(server->sname);
212 	  set_option(dcc->sopt, S_LISTEN);
213 	}
214   dcc->fd = fd;
215 }
216 
217 /* select_server: set a different server */
218 struct server_ *
select_server(name)219 select_server(name)
220   char *name;
221 {
222   struct server_ *stmp = slist;
223 
224   assert(name);
225 
226   while (stmp && (strcasecmp(stmp->sname, name)
227 		  || !option(stmp->sopt, S_CONNECTED)))
228       stmp = stmp->nexts;
229 
230   return server = stmp;
231 }
232 
233 /* sic_server: adds a server to the list, does NOT connect */
234 struct server_ *
sic_server(nick,name,port,pass)235 sic_server(nick, name, port, pass)
236   char *nick, *name, *pass;
237   int port;
238 {
239   struct server_ **stmp = &slist;
240 
241   while (*stmp)
242       stmp = &((*stmp)->nexts);
243 
244   server = (struct server_ *) malloc(sizeof(struct server_));
245   bzero(server, sizeof(struct server_));
246 
247   *stmp = server;
248 
249   server->nick = strdup(nick);
250   server->sname = strdup(name);
251   server->port = port;
252   if (pass)
253       server->pass = strdup(pass);
254 
255 #if defined(HAVE_GETADDRINFO)
256   {
257     struct addrinfo *address, hints;
258     char port_str[10];
259 
260     bzero((char *)&hints, sizeof(hints));
261     hints.ai_flags = AI_CANONNAME|AI_NUMERICHOST;
262     hints.ai_family = PF_UNSPEC;
263     hints.ai_socktype = SOCK_STREAM;
264     sprintf(port_str, "%d", port);
265     if (getaddrinfo(name, port_str, &hints, &address) == 0)
266       {
267 	server->ip = strdup(name);
268 	server->address = address;
269 	server->addrip = address;
270       }
271   }
272 #else
273   if (inet_addr(name) != INADDR_NONE)
274       server->ip = strdup(name);
275 #endif
276 
277   if (server->ip == NULL)
278     {
279       dns_lookup(name, server);
280       set_option(server->sopt, S_DNS);
281     }
282 
283   set_option(server->sopt, S_NOTCONNECTED);
284   set_option(server->sopt, S_QUIT);
285   server->fd = -1;
286   return server;
287 }
288 
289 /* sic_dserver: removes a server from the list */
290 void
sic_dserver(del)291 sic_dserver(del)
292   struct server_ *del;
293 {
294   struct server_ **stmp = &slist;
295 
296   assert(sic_scnt(del) == 0);
297   assert(option(del->sopt, S_CONNECTED) == 0);
298   assert(option(del->sopt, S_DNS) == 0);
299   while (*stmp != del)
300       stmp = &((*stmp)->nexts);
301   *stmp = del->nexts;
302   opt_free(&(del->custs));
303   free(del->sname);
304   if (del->ip)
305       free(del->ip);
306 #if defined(HAVE_GETADDRINFO)
307   if (del->address)
308       freeaddrinfo(del->address);
309 #endif
310   if (del->pass)
311       free(del->pass);
312   free(del->nick);
313   if (del->uname)
314       free(del->uname);
315   if (del->rname)
316       free(del->rname);
317   if (del->readf)
318       close(del->readf);
319   if (del->sendf)
320       close(del->sendf);
321   free(del);
322 }
323 
324 static void
sic_write(str)325 sic_write(str)
326      char *str;
327 {
328   if (server && server->fd >= 0)
329     {
330       if (write(server->fd, str, strlen(str)) == -1
331 	  && (errno == EAGAIN || errno == EWOULDBLOCK))
332 	  /* should be dealt with in a better way, not worth my time unless
333 	     it happens */
334 	  vsic_slog(LOG_CLIENT, "--- write() to server failed: %s",
335 		   strerror(errno));
336       str[strlen(str)-1] = '\0'; /* hmmpf */
337       sic_slog(LOG_OSNIF, str);
338     }
339   else
340       sic_slog(LOG_CLIENT, "--- No server");
341 }
342 
343 void
vsic_write(char * format,...)344 vsic_write(char *format, ...)
345 {
346   char	buffer[1024];
347   va_list va;
348 
349   va_start(va, format);
350   vsprintf(buffer, format, va);
351   va_end(va);
352   strcat(buffer, "\n");
353   sic_write(buffer);
354 }
355 
356 static int
readln(fd,buffer,length)357 readln(fd, buffer, length)
358      int fd, length;
359      char *buffer;
360 {
361   int status, ptr;
362 
363   ptr = -1;
364   while (buffer[++ptr]);
365 
366   while (((status = read(fd, &buffer[ptr], 1)) > 0) && (ptr < length - 1))
367     {
368       if (buffer[ptr++] == '\n')
369 	  break;
370     }
371 
372   if (status == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
373     {
374       /* incomplete line */
375       buffer[ptr] = '\0';
376       return -2;
377     }
378 
379   if (status > 0)
380     {
381       if (buffer[ptr-2] == '\r')
382 	  buffer[ptr-2] = '\0';
383       else
384 	  buffer[ptr-1] = '\0';
385       sic_slog(LOG_ISNIF, buffer);
386     }
387 
388   return (status);
389 }
390 
391 /* this function is totally buggy, but it's simple and works most of the time*/
392 static void
parse(str)393 parse(str)
394      char *str;
395 {
396   char		*sender, *cmd, *dest, *para;
397 
398   if (option(server->sopt, S_DCC))
399     {
400       char snder[267];
401 
402       sprintf(snder, "%s@%s", server->nick, server->sname);
403       sic_format(snder, NULL, NULL, str);
404       return;
405     }
406 
407   if (*str == ':')
408     {
409       sender = ++str;
410       while (*++str != ' ');
411       *str++ = '\0';
412     }
413   else
414       sender = server->sname;
415 
416   if (!strncmp(str, "ERROR ", 6))
417     {
418       cmd = "ERROR";
419       dest = server->nick;
420       para = str;
421     }
422   else
423     {
424       cmd = str;
425       while (*++str != ' ');
426       *str++ = '\0';
427       if (*str == ':')
428 	{
429 	  vsic_slog(LOG_DEBUG, "dest begins with : for `%s %s %s'",sender,cmd,str);
430 	  dest = str+1;
431 	  para = "";
432 	}
433       else
434 	{
435 	  dest = str;
436 	  while (*++str != ' ' && *str);
437 	  if (*str)
438 	    {
439 	      *str++ = '\0';
440 	      if (*str == ':')
441 		  para = str + 1;
442 	      else
443 		  para = str;
444 	    }
445 	  else
446 	      para = "";
447 	}
448     }
449 
450   if (strcmp(cmd, "PONG"))
451       sic_format(sender, cmd, dest, para);
452 }
453 
454 /* sic_select: big fat io loop */
455 int
sic_select()456 sic_select()
457 {
458   fd_set	rfd, wfd, efd;
459   int		n, maxfd = 0;
460   struct server_ *stmp = slist;
461   struct timeval timeout;
462   char		buf[10240];
463 
464   FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
465   timeout.tv_usec = 0; timeout.tv_sec = 5;
466 
467   FD_SET(0, &rfd);
468   while (stmp)
469     {
470       if (stmp->fd > maxfd)
471 	  maxfd = stmp->fd;
472       server = stmp;
473       if (option(stmp->sopt, S_NOTCONNECTED)
474 	  && !option(stmp->sopt, S_CONNECTING)
475 	  && !option(stmp->sopt, S_CONNECTED)
476 	  && !option(stmp->sopt, S_QUIT)
477 	  && !option(stmp->sopt, S_DCC)
478 	  && (time(NULL) - stmp->ts) >= 1800
479 	  && get_option(Z_RECONNECT, NULL))
480 	{
481 	  select_active(NULL, 0);
482 	  vsic_slog(LOG_CLIENT, "--- Attempting to reconnect to %s:%d",
483 		    stmp->sname, stmp->port);
484 	  set_option(stmp->sopt, S_RECONNECT);
485 	}
486       if (option(stmp->sopt, S_RECONNECT) && (time(NULL) - stmp->ts) >= 5)
487 	{
488 	  select_active(NULL, 0);
489 	  sic_connect(stmp);
490 	}
491       if (option(stmp->sopt, S_CONNECTED))
492 	{
493 	  if (!option(stmp->sopt, S_DCC))
494 	    {
495 	      server = stmp;
496 	      select_active(NULL, 0);
497 	      if (stmp->lastr && time(NULL) - stmp->lastr > 60)
498 		{
499 		  if ((time(NULL) - stmp->lastr) % 120 < 60)
500 		    {
501 		      if (!option(stmp->sopt, S_PING))
502 			{
503 			  vsic_write("PING .");
504 			  set_option(stmp->sopt, S_PING);
505 			}
506 		    }
507 		  else
508 		      unset_option(stmp->sopt, S_PING);
509 		}
510 	      if (need_collect)
511 		  collect_channel();
512 	    }
513 	  FD_SET(stmp->fd, &rfd);
514 	}
515       if (option(stmp->sopt, S_SEND) && stmp->sent < stmp->size)
516 	  FD_SET(stmp->fd, &wfd);
517       if (option(stmp->sopt, S_LISTEN))
518 	  FD_SET(stmp->fd, &rfd);
519       if (option(stmp->sopt, S_CONNECTING) && !option(stmp->sopt, S_DNS))
520 	  FD_SET(stmp->fd, &wfd);
521       stmp = stmp->nexts;
522     }
523 
524   display_status();
525   term_input(NULL, 0);
526   term_flush();
527 #if defined(GNU_PTH)
528   n = pth_select(maxfd + 1, &rfd, &wfd, NULL, &timeout);
529 #else
530   n = select(maxfd + 1, &rfd, &wfd, &efd, &timeout);
531 #endif
532 
533   if (n < 0)
534       return 0;
535 
536   stmp = slist;
537   while (stmp)
538     {
539       /* first deal with pending DCCs */
540       if (stmp->fd >= 0 && option(stmp->sopt, S_LISTEN))
541 	{
542 	  if (FD_ISSET(stmp->fd, &rfd))
543 	    {
544 	      struct sockaddr_in sock_sin;
545 	      int newfd, sz = sizeof(struct sockaddr_in);
546 
547 	      server = stmp;
548 	      unset_option(stmp->sopt, S_LISTEN);
549 	      set_option(stmp->sopt, S_DCC);
550 	      select_active(NULL, 0);
551 	      if ((newfd=accept(stmp->fd,(struct sockaddr *)&sock_sin,&sz))<0)
552 		{
553 		  vsic_slog(LOG_CLIENT, "DCC accept() failed for %s@%s: %s",
554 			    stmp->nick, inet_ntoa(sock_sin.sin_addr),
555 			    strerror(errno));
556 		  stmp = stmp->nexts;
557 		  sic_dserver(server);
558 		}
559 	      else
560 		{
561 		  close(stmp->fd);
562 		  stmp->fd = newfd;
563 		  free(stmp->sname);
564 		  stmp->sname = strdup((char *)inet_ntoa(sock_sin.sin_addr));
565 		  set_option(stmp->sopt, S_CONNECTED);
566 		  vsic_slog(LOG_CLIENT,
567 			    "--- DCC connection received from %s@%s",
568 			    stmp->nick, stmp->sname);
569 		  dns_lookup(stmp->sname, NULL);
570 		  sz = 1;
571 		  if (ioctl(stmp->fd, FIONBIO, &sz) < 0)
572 		      vsic_slog(LOG_CLIENT, "--- ioctl() failed: %s",
573 				strerror(errno));
574 		  if (stmp->pass)
575 		      set_option(stmp->sopt, S_SEND);
576 		  stmp->ts = time(NULL);
577 		}
578 	    }
579 	}
580       /* DCC sends */
581       if (option(stmp->sopt, S_SEND))
582 	{
583 	  server = stmp;
584           select_active(NULL, 0);
585 	  if (FD_ISSET(stmp->fd, &rfd))
586 	    {
587 	      u_32int_t total; /* DCC ack */
588 
589 	      if (recv(stmp->fd, (char *) &total, sizeof(u_32int_t),0) > 0)
590 		  stmp->read = ntohl(total);
591 	      else
592 		{
593 		  vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost: %s",
594 			    stmp->pass, stmp->nick, strerror(errno));
595 		  vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).",
596 			    stmp->read, stmp->size,
597 			    (int)(100*stmp->read/stmp->size));
598 		  close(stmp->fd);
599 		  stmp = stmp->nexts;
600 		  sic_dserver(server);
601 		  continue;
602 		}
603 	      if (stmp->read == stmp->size)
604 		{
605 		  vsic_slog(LOG_CLIENT,
606 			    "--- DCC \"%s\" to %s completed in %s (%ub/s)",
607 			    stmp->pass, stmp->nick,
608 			    sic_tdiff(time(NULL)-stmp->ts, 0),
609 			    stmp->size/(1+time(NULL)-stmp->ts));
610 		  shutdown(stmp->fd, 2);
611 		  close(stmp->fd);
612 		  stmp = stmp->nexts;
613 		  sic_dserver(server);
614 		  continue;
615 		}
616 	    }
617 	  if (FD_ISSET(stmp->fd, &wfd) && stmp->sent - stmp->read < 10240)
618 	    {
619 	      char error = 0;
620 	      unsigned long rd, st = 0;
621 
622 	      if (lseek(stmp->sendf, stmp->sent, SEEK_SET) < 0)
623 		  error = 1;
624 	      if ((rd = read(stmp->sendf, buf, 10240)) < 0)
625 		  error = 2;
626 	      if (rd > 0 && ((st = write(stmp->fd, buf, rd)) < 0))
627 		  error = 3;
628 	      if (error)
629 		{
630 		  if (error != 3)
631 		      vsic_slog(LOG_CLIENT, "--- %s() failed: %s!",
632 				(error == 1) ?"lseek":"read",strerror(errno));
633 		  vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost.",
634 			    stmp->pass, stmp->nick);
635 		  vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).",
636 			    stmp->read, stmp->size,
637 			    (int)(100*stmp->read/stmp->size));
638 		  close(stmp->fd);
639 		  stmp = stmp->nexts;
640 		  sic_dserver(server);
641 		  continue;
642 		}
643 	      stmp->sent += st;
644 	    }
645 	  stmp = stmp->nexts;
646 	  continue;
647 	}
648       /* DCC chat, DCC get, and normal server traffic */
649       if (stmp->fd >= 0 && !option(stmp->sopt, S_LISTEN))
650 	{
651 	  server = stmp;
652 	  select_active(NULL, 0);	/* default window for this server */
653 	  if (FD_ISSET(stmp->fd, &rfd))
654 	    {
655 	      int sz;
656 
657 	      if (option(stmp->sopt, S_DCC) && stmp->pass)
658 		  sz = read(stmp->fd, buf, 8192); /* big buffer for files */
659 	      else
660 		{
661 		  /*read up to \r\n otherwise*/
662 		  unset_option(stmp->sopt, S_PING);
663 		  sz = readln(stmp->fd, stmp->readbuf, 520);
664 		}
665 	      if (sz > 0)
666 		{
667 		  stmp->lastr = time(NULL);
668 		  if (option(stmp->sopt, S_NOTCONNECTED))
669 		    {
670 		      assert(!option(stmp->sopt, S_DCC));
671 		      unset_option(stmp->sopt, S_NOTCONNECTED);
672 		      sic_slog(LOG_CLIENT, "--- Connection established.");
673 		    }
674 		  if (option(stmp->sopt, S_DCC) && stmp->pass)
675 		    { /* file transfer */
676 		      if (stmp->readf == 0)
677 			  stmp->readf = open(stmp->pass,
678 					     O_WRONLY|O_TRUNC|O_CREAT, 0644);
679 		      if (stmp->readf == 0)
680 			{
681 			  vsic_slog(LOG_CLIENT,
682 				    "--- Unable to create file \"%s\"!",
683 				    stmp->pass);
684 			  close(stmp->fd);
685 			  stmp = stmp->nexts;
686 			  sic_dserver(server); /* "continue;" below */
687 			}
688 		      else
689 			{
690 			  u_32int_t total; /* DCC ack */
691 
692 			  write(stmp->readf, buf, sz); /* ugly! */
693 			  total = htonl(stmp->read += sz);
694 			  send(stmp->fd, (char *) &total, sizeof(u_32int_t),0);
695 			  if (stmp->read == stmp->size) /* got all of it? */
696 			    {
697 			      stmp->size /= 1024;
698 			      vsic_slog(LOG_CLIENT,
699 			"--- DCC \"%s\" from %s completed in %s (%ub/s)",
700 					stmp->pass, stmp->nick,
701 					sic_tdiff(time(NULL)-stmp->ts, 0),
702 					stmp->size/(1+time(NULL)-stmp->ts));
703 			      shutdown(stmp->fd, 2);
704 			      close(stmp->fd);
705 			      stmp = stmp->nexts;
706 			      sic_dserver(server); /* "continue;" below */
707 			    }
708 			  else
709 			      stmp = stmp->nexts;
710 			}
711 		      continue;
712 		    }
713 		  else
714 		    {
715 		      /* readln() */
716 		      if (!option(stmp->sopt, S_DCC)
717 			  && !strncmp("PING ", stmp->readbuf, 5))
718 			  vsic_write("PONG %s :%s", server->nick,
719 				     (stmp->readbuf[4] && stmp->readbuf[5]) ?
720 				     stmp->readbuf+6 : "");
721 		      else
722 			{
723 			  /* current window for this server */
724 			  select_active(NULL, 1);
725 			  parse(stmp->readbuf);
726 			}
727 		      stmp->readbuf[0] = '\0';
728 		    }
729 		}
730 	      else if (sz == -2)
731 		{
732 		  vsic_slog(LOG_DEBUG, "Incomplete line from %s", stmp->sname);
733 		}
734 	      else
735 		{
736 		  close(stmp->fd);
737 		  stmp->fd = -1;
738 		  if (option(stmp->sopt, S_NOTCONNECTED))
739 		    {
740 		      /* this can happen when connecting locally */
741 		      assert(!option(stmp->sopt, S_DCC));
742 		      vsic_slog(LOG_CLIENT,
743 				"--- Connection to %s %d [%s] failed.",
744 				stmp->sname, stmp->port, stmp->ip);
745 		    }
746 		  set_option(stmp->sopt, S_NOTCONNECTED);
747 		  unset_option(stmp->sopt, S_CONNECTED);
748 		  if ((time(NULL) - stmp->ts) > 60
749 		      && !option(stmp->sopt, S_DCC)
750 		      && !option(stmp->sopt, S_QUIT)
751 		      && get_option(Z_RECONNECT, NULL))
752 		      set_option(stmp->sopt, S_RECONNECT);
753 		  stmp->ts = time(NULL);
754 		  if (option(stmp->sopt, S_DCC))
755 		    {
756 		      vsic_slog(LOG_CLIENT, "--- DCC to %s@%s lost.",
757 				stmp->nick, stmp->sname);
758 		      if (stmp->pass)
759 			{
760 			  vsic_slog(LOG_CLIENT,
761 				    "--- Got %lu out of %lu (%u%% of %s).",
762 				    stmp->read, stmp->size,
763 				    (int)(100*stmp->read/stmp->size),
764 				    stmp->pass);
765 			  stmp = stmp->nexts;
766 			  sic_dserver(server);
767 			}
768 		    }
769 		  else
770 		      vsic_slog(LOG_CLIENT,
771 				"--- Lost connection to %s:%d : %s",
772 				stmp->sname, stmp->port,
773 				option(stmp->sopt, S_RECONNECT) ?
774 				"Reconnecting..." :
775 		option(stmp->sopt, S_QUIT) || !get_option(Z_RECONNECT, NULL) ?
776 				"No reconnect.":"Will attempt to reconnect.");
777 		  continue;
778 		}
779 	    }
780 	  /* connection establishment */
781 	  if (FD_ISSET(stmp->fd, &wfd))
782 	    {
783 	      char *wp, buffer[512];
784 
785 	      unset_option(stmp->sopt, S_CONNECTING);
786 
787 	      if (!option(stmp->sopt, S_DCC))
788 		{
789 		  server->umode[0] = '\0';
790 		  sprintf(buffer, "%s%s%sNICK %s\nUSER %s myhost 0 :%s\n",
791 			  (server->pass) ? "PASS " : "",
792 			  (server->pass) ? server->pass : "",
793 			  (server->pass) ? "\n" : "",
794 			  stmp->nick,
795 			  (stmp->uname) ? stmp->uname :
796 			  	(wp = getenv("SICUSER")) ? wp :
797 			  		(wp = getenv("USER")) ? wp : "sicuser",
798 			  (stmp->rname) ? stmp->rname :
799 			  	(wp = getenv("SICNAME")) ? wp :"wasting time");
800 		  if (write(stmp->fd, buffer, strlen(buffer)) == -1)
801 		    {
802 		      close(stmp->fd);
803 		      stmp->fd = -1;
804 		      stmp->ts = time(NULL);
805 		      set_option(stmp->sopt, S_NOTCONNECTED);
806 		      sic_slog(LOG_CLIENT, "--- Connection failed.");
807 		    }
808 		  else
809 		    {
810 		      del_member(NULL, NULL);
811 		      set_option(stmp->sopt, S_CONNECTED);
812 		    }
813 		}
814 	      else
815 		{
816 		  vsic_slog(LOG_CLIENT, "--- DCC to %s@%s established.",
817 			   stmp->nick, stmp->sname);
818 		  set_option(stmp->sopt, S_CONNECTED);
819 		  unset_option(stmp->sopt, S_NOTCONNECTED);
820 		}
821 	    }
822 	}
823       stmp = stmp->nexts;
824     }
825 
826   if (FD_ISSET(0, &rfd))
827     return 1;
828   else
829     return 0;
830 }
831 
832 int
cmd_server(p)833 cmd_server(p)
834   char *p;
835 {
836   struct server_ *stmp = slist;
837   int n = 0;
838 
839   if (p == NULL)
840     {
841       /* NULL argument, this is only used internally */
842       if (server == NULL)
843 	  return 0;
844       if (option(server->sopt, S_CONNECTING)
845 	  || option(server->sopt, S_CONNECTED))
846 	{
847 	  if (option(server->sopt, S_QUIT))
848 	    {
849 	      if (server->fd >= 0)
850 		  close(server->fd);
851 	      server->fd = -1;
852 	      set_option(server->sopt, S_NOTCONNECTED);
853 	      unset_option(server->sopt, S_CONNECTING);
854 	      unset_option(server->sopt, S_CONNECTED);
855 	      vsic_slog(LOG_CLIENT, "--- Closed connection to %s:%d",
856 			server->sname, server->port);
857 	    }
858 	}
859       unset_option(server->sopt, S_RECONNECT);
860       set_option(server->sopt, S_QUIT);
861       return 0;
862     }
863   /* real user input */
864   if (!*p || !strcasecmp(p, "-v"))
865     {
866       /* no argument or just `-v', user is simply requesting a list */
867       sic_slog(LOG_CLIENT, "--- Server list:");
868       while (stmp)
869 	{
870 	  if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN))
871 	      vsic_slog(LOG_CLIENT, "---   %2d: %s %d %s(%s%s)%s",
872 			n++, stmp->sname, stmp->port,
873 			(stmp->pass) ? "*password* " : "",
874 			(option(stmp->sopt, S_CONNECTING)) ? "will be " :
875 			(option(stmp->sopt, S_CONNECTED)) ? "" : "was ",
876 			stmp->nick,
877 			(stmp->ip) ? "" : option(stmp->sopt, S_DNS) ?
878 				" Looking up address" : " Unknown address");
879 	  if (*p && stmp->ip)
880 #if defined(HAVE_GETADDRINFO)
881 	    {
882 	      struct addrinfo *address = stmp->address;
883 
884 	      vsic_slog(LOG_CLIENT, "---       => %s[%s]",
885 			(stmp->address && stmp->address->ai_canonname) ?
886 			stmp->address->ai_canonname : "???", stmp->ip);
887 	      if (address && address->ai_next)
888 		  while (address)
889 		    {
890 		      if (address->ai_family == PF_INET ||
891 			  address->ai_family == PF_INET6)
892 			  vsic_slog(LOG_CLIENT, "---        > %s[%s]",
893 				    (address->ai_canonname) ?
894 				    address->ai_canonname : "???",
895 				    aitoip(address));
896 		      address = address->ai_next;
897 		    }
898 	    }
899 #else
900 	      vsic_slog(LOG_CLIENT, "---       => [%s]", stmp->ip);
901 #endif
902 	  stmp = stmp->nexts;
903 	}
904     }
905   else if (!strncasecmp(p, "-d", 2))
906     {
907       char *number = p;
908 
909       while (*number && !isdigit(*number))
910 	  number += 1;
911       if (*number == '\0')
912 	  return -4;
913 
914       n = 0;
915       while (stmp)
916 	{
917 	  if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)
918 	      && n++ == atoi(number))
919 	      break;
920 	  stmp = stmp->nexts;
921 	}
922       if (stmp == NULL)
923 	  return -4;
924 
925       if (option(stmp->sopt, S_DNS))
926 	{
927 	  sic_slog(LOG_CLIENT,
928 	   "--- DNS lookup in progress, cannot delete server. (try again)");
929 	  return 0;
930 	}
931       if (option(stmp->sopt, S_CONNECTED))
932 	{
933 	  sic_slog(LOG_CLIENT, "--- Close connection first.");
934 	  return 0;
935 	}
936       if (n = sic_scnt(stmp))
937 	{
938 	  vsic_slog(LOG_CLIENT,"--- %d window(s) are bound to this server.",n);
939 	  return 0;
940 	}
941       assert(server != stmp); /* this shouldn't be possible? */
942       vsic_slog(LOG_CLIENT,"--- Deleted server %d (%s).", atoi(number),
943 		stmp->sname);
944       sic_dserver(stmp);
945       return 0;
946     }
947   else
948     {
949       char *number = p;
950 
951       if (*p == '+')
952 	  number = p+1;
953       while (*number)
954 	  if (!isdigit(*number++))
955 	    {
956 	      number = NULL;
957 	      break;
958 	    }
959 
960       if (number)
961 	{
962 	  /* argument is a number, find selected server and try to connect */
963 	  if (current->via && (option(current->via->sopt, S_CONNECTED)
964 			       || option(current->via->sopt, S_CONNECTING)))
965 	    {
966 	      sic_slog(LOG_CLIENT, "--- This window is already active.");
967 	      return 0;
968 	    }
969 	  while (stmp)
970 	    {
971 	      if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)
972 		  && n++ == atoi(p))
973 		  break;
974 	      stmp = stmp->nexts;
975 	    }
976 	  if (stmp)
977 	    {
978 	      server = stmp;
979 	      if (stmp->ip || option(stmp->sopt, S_DNS))
980 		{
981 		  if (sic_swin(1))
982 		    {
983 		      if (!option(stmp->sopt, S_CONNECTED))
984 			{
985 			  set_option(stmp->sopt, S_CONNECTING);
986 			  if (option(stmp->sopt, S_DNS))
987 			    {
988 			      vsic_slog(LOG_CLIENT,
989 				"--- Connection to %s pending DNS lookup.",
990 					stmp->sname);
991 			    }
992 			  else
993 			    {
994 			      if (*p == '+')
995 				  sic_nextaddr(stmp);
996 			      vsic_slog(LOG_CLIENT,
997 					"--- Connecting to %s %d [%s]",
998 					stmp->sname, stmp->port, stmp->ip);
999 			      sic_connect(stmp);
1000 			    }
1001 			}
1002 		    }
1003 		}
1004 	      else
1005 		  vsic_slog(LOG_CLIENT, "--- Unknown IP for %s", stmp->sname);
1006 	    }
1007 	  else
1008 	      sic_slog(LOG_CLIENT, "--- No such server.");
1009 	}
1010       else
1011 	{
1012 	  /* format: /server name port pass */
1013 	  char *port, *pass = NULL;
1014 	  int portn = 6667;
1015 
1016 	  if (port = index(p, ' '))
1017 	    {
1018 	      *port++ = '\0';
1019 	      if (pass = index(port, ' '))
1020 		  *pass++ = '\0';
1021 	      portn = atoi(port);
1022 	    }
1023 	  sic_server((current->via) ? current->via->nick : "sic",
1024 		     p, portn, pass);
1025 	  cmd_server("");
1026 	}
1027     }
1028   return 0;
1029 }
1030 
1031 /*
1032  * DCC code
1033  */
1034 
1035 static void
sic_dwrite(nick,str)1036 sic_dwrite(nick, str)
1037      char *nick, *str;
1038 {
1039   struct server_ *dtmp = slist;
1040 
1041   while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, nick)))
1042       dtmp = dtmp->nexts;
1043   if (dtmp)
1044       if (dtmp->fd >= 0)
1045 	{
1046 	  if (write(dtmp->fd, str, strlen(str)) == -1
1047 	      && (errno == EAGAIN || errno == EWOULDBLOCK))
1048 	      /* should be dealt with in a better way, not worth my time unless
1049 		 it happens */
1050 	      vsic_slog(LOG_CLIENT, "--- write() to DCC failed: %s",
1051 		       strerror(errno));
1052 	  str[strlen(str)-1] = '\0'; /* hmmpf */
1053 	  sic_slog(LOG_OSNIF, str);
1054 	}
1055       else
1056 	  sic_slog(LOG_CLIENT, "--- DCC not established.");
1057   else
1058       sic_slog(LOG_CLIENT, "--- No such DCC.");
1059 }
1060 
1061 void
vsic_dwrite(char * nick,char * format,...)1062 vsic_dwrite(char *nick, char *format, ...)
1063 {
1064   char	buffer[1024];
1065   va_list va;
1066 
1067   va_start(va, format);
1068   vsprintf(buffer, format, va);
1069   va_end(va);
1070   strcat(buffer, "\n");
1071   sic_dwrite(nick, buffer);
1072 }
1073 
1074 /* sic_dcc: called to deal with DCC requests
1075  *	0 -> chat , 1 -> send
1076  */
1077 void
sic_dcc(type,nick,ip,port,file,size)1078 sic_dcc(type, nick, ip, port, file, size)
1079   int type;
1080   char *nick, *file;
1081   unsigned long ip, size;
1082   unsigned int port;
1083 {
1084   struct server_ *dtmp = slist;
1085   unsigned long	lip;
1086 
1087   assert(type == 0 || type == 1);
1088 
1089   while (dtmp)
1090     {
1091       if (option(dtmp->sopt, S_DCC)
1092 	  && !strcasecmp(dtmp->nick, nick)
1093 	  && (!dtmp->pass || !strcmp(dtmp->pass, file)))
1094 	  break;
1095       dtmp = dtmp->nexts;
1096     }
1097   if (dtmp)
1098     {
1099       /* should be dealt with more gracefully */
1100       if ((option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED))
1101 	  && (type == 0 || (dtmp->pass && !strcmp(dtmp->pass, file))))
1102 	{
1103 	  vsic_slog(LOG_CLIENT,
1104 		    "--- DCC chat request from %s ignored (duplicate).", nick);
1105 	  return;
1106 	}
1107       vsic_slog(LOG_CLIENT,
1108 		"--- old DCC chat request from %s deleted.", nick);
1109       sic_dserver(dtmp);
1110     }
1111   if (type == 1 && size == 0)
1112     {
1113       vsic_slog(LOG_CLIENT, "--- Invalid DCC request (file size = 0) from %s.",
1114 		nick);
1115       return;
1116     }
1117   dtmp = (struct server_ *) malloc(sizeof(struct server_));
1118   bzero(dtmp, sizeof(struct server_));
1119   dtmp->fd = -1;
1120   dtmp->ts = time(NULL);
1121   set_option(dtmp->sopt, S_DCC);
1122   dtmp->nick = strdup(nick);
1123   if (type == 1)
1124     {
1125       dtmp->pass = strdup(file);
1126       dtmp->size = size;
1127     }
1128   lip = ntohl(ip);
1129   dtmp->sname = strdup((char *)inet_ntoa(*(struct in_addr*)&lip));
1130   dtmp->ip = strdup((char *)inet_ntoa(*(struct in_addr*)&lip));
1131   set_option(dtmp->sopt, S_NOTCONNECTED);
1132   dns_lookup(dtmp->ip, NULL);
1133   switch (type)
1134     {
1135   case 0:
1136       vsic_slog(LOG_CLIENT, "--- DCC CHAT request from %s@%s.",
1137 		dtmp->nick, dtmp->ip);
1138       break;
1139   case 1:
1140       vsic_slog(LOG_CLIENT, "--- DCC SEND request from %s@%s: %s %lu",
1141 		dtmp->nick, dtmp->ip, file, size);
1142       break;
1143   default:
1144       abort(); /* never */
1145     }
1146   if ((dtmp->port = port) < 1024)
1147       vsic_slog(LOG_CLIENT, "--- DCC request from a privileged port %d!",port);
1148   dtmp->nexts = slist;
1149   slist = dtmp;
1150 }
1151 
1152 int
cmd_dcc(p)1153 cmd_dcc(p)
1154   char *p;
1155 {
1156   struct server_ *dtmp = slist;
1157 
1158   if (!*p)
1159     {
1160       sic_slog(LOG_CLIENT, "--- DCC list:");
1161       while (dtmp)
1162 	{
1163 	  if (option(dtmp->sopt, S_DCC))
1164 	      if (dtmp->pass)
1165 		  vsic_slog(LOG_CLIENT,
1166 			    "---   %s@%s:%d %s [%lu] %s %s: %d%% %s",
1167 			    dtmp->nick, dtmp->sname, dtmp->port, dtmp->pass,
1168 			    dtmp->size,
1169 			    (option(dtmp->sopt, S_CONNECTED)) ? "active" :
1170 			    (option(dtmp->sopt, S_CONNECTING)) ? "connecting" :
1171 			    "not connected",
1172 			    (option(dtmp->sopt,S_SEND)) ? "Sending":"Reading",
1173 			    (int)(100*dtmp->read/dtmp->size),
1174 			    sic_tdiff(time(NULL)-dtmp->ts, 0));
1175 	      else
1176 		  vsic_slog(LOG_CLIENT, "---   %s@%s:%d %s",
1177 			    dtmp->nick, dtmp->sname, dtmp->port,
1178 			    (option(dtmp->sopt, S_CONNECTED)) ? "active" :
1179 			    (option(dtmp->sopt, S_CONNECTING)) ? "connecting" :
1180 			    "not connected");
1181 	  if (option(dtmp->sopt, S_LISTEN))
1182 	      vsic_slog(LOG_CLIENT, "---   %s@%s pending %s%s", dtmp->nick,
1183 			dtmp->sname, (dtmp->pass) ? "Send: " : "chat",
1184 			(dtmp->pass) ? dtmp->pass : "");
1185 	  dtmp = dtmp->nexts;
1186 	}
1187       return 0;
1188     }
1189   if (server == NULL)
1190       return -5;
1191   if (!strncasecmp("CHAT ", p, 5))
1192     {
1193       p += 5;
1194       if (!*p)
1195 	  return -1;
1196       while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p)
1197 		      || dtmp->pass))
1198 	  dtmp = dtmp->nexts;
1199       if (dtmp)
1200 	{
1201 	  if (option(dtmp->sopt, S_CONNECTING)
1202 	      || option(dtmp->sopt, S_CONNECTED))
1203 	    {
1204 	      sic_slog(LOG_CLIENT, "--- Connection already initiated.");
1205 	      return 0;
1206 	    }
1207 	  sic_connect(dtmp);
1208 	}
1209       else
1210 	{
1211 	  dtmp = (struct server_ *) malloc(sizeof(struct server_));
1212 	  bzero(dtmp, sizeof(struct server_));
1213 	  sic_listen(dtmp);
1214 	  if (dtmp->fd < 0)
1215 	      free(dtmp);
1216 	  else
1217 	    {
1218 	      dtmp->nick = strdup(p);
1219 	      vsic_write("PRIVMSG %s :\001DCC CHAT chat %lu %u\001", p,
1220 			 htonl(inet_addr(dtmp->ip)), htons(dtmp->port));
1221 	      vsic_slog(LOG_CLIENT, "--- DCC chat offered to %s", p);
1222 	      dtmp->nexts = slist;
1223 	      slist = dtmp;
1224 	    }
1225 	}
1226       return 0;
1227     }
1228   if (!strncasecmp("SEND ", p, 5))
1229     {
1230       char *file = index(p+5, ' '), *basename;
1231 
1232       if (file == NULL)
1233           return -1;
1234       *file++ = '\0';
1235       if ((basename = rindex(file, '/')) == NULL)
1236 	  basename = file;
1237       else
1238 	  basename++;
1239       p += 5;
1240 
1241       dtmp = (struct server_ *) malloc(sizeof(struct server_));
1242       bzero(dtmp, sizeof(struct server_));
1243 
1244       if ((dtmp->sendf = open(file, O_RDONLY)) < 0)
1245 	{
1246 	  free(dtmp);
1247 	  vsic_slog(LOG_CLIENT, "--- Unable to open %s: %s", file,
1248 		    strerror(errno));
1249 	  return 0;
1250 	}
1251       dtmp->pass = strdup(file);
1252 
1253       sic_listen(dtmp);
1254       if (dtmp->fd < 0)
1255 	{
1256 	  close(dtmp->sendf);
1257 	  free(dtmp->pass);
1258 	  free(dtmp);
1259 	}
1260       else
1261 	{
1262 	  struct stat st;
1263 
1264 	  if (fstat(dtmp->sendf, &st) < 0)
1265 	    {
1266 	      vsic_slog(LOG_CLIENT, "--- Unable to find the file size: %s",
1267 			strerror(errno));
1268 	      close(dtmp->fd);
1269 	      close(dtmp->sendf);
1270 	      free(dtmp->pass);
1271 	      free(dtmp);
1272 	      return 0;
1273 	    }
1274 	  dtmp->size = st.st_size;
1275 	  dtmp->nick = strdup(p);
1276 	  vsic_write("PRIVMSG %s :\001DCC SEND %s %lu %u %u\001", p, basename,
1277 		     htonl(inet_addr(dtmp->ip)), htons(dtmp->port),st.st_size);
1278 	  vsic_slog(LOG_CLIENT, "--- \"%s\" offered to %s", file, p);
1279 	  dtmp->nexts = slist;
1280 	  slist = dtmp;
1281 	}
1282       return 0;
1283     }
1284   if (!strncasecmp("GET ", p, 4))
1285     {
1286       char *path = index(p+4, ' '), *file;
1287 
1288       if (path == NULL)
1289 	  return -1;
1290       p += 4;
1291       *path++ = '\0';
1292       if (file = rindex(path, '/'))
1293 	  file++;
1294       else
1295 	  file = path;
1296       while (dtmp
1297 	     && (dtmp->pass == NULL || !rmatch(file, dtmp->pass)
1298 		 || !option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p)))
1299 	  dtmp = dtmp->nexts;
1300       if (dtmp)
1301 	{
1302 	  if (option(dtmp->sopt, S_CONNECTING)
1303 	      || option(dtmp->sopt, S_CONNECTED))
1304 	    {
1305 	      sic_slog(LOG_CLIENT, "--- Connection already initiated.");
1306 	      return 0;
1307 	    }
1308 	  sic_connect(dtmp);
1309 	  dtmp->ts = time(NULL);
1310 	}
1311       else
1312 	return -4; /* invalid parameter */
1313       return 0;
1314     }
1315   if (!strncasecmp("delete ", p, 7))
1316     {
1317       char *file = index(p+7, ' ');
1318 
1319       p += 7;
1320       if (file)
1321 	  *file++ = '\0';
1322       while (dtmp
1323 	     && ((!option(dtmp->sopt, S_DCC) && !option(dtmp->sopt, S_LISTEN))
1324 		 || strcasecmp(dtmp->nick, p)
1325 		 || (file && !rmatch(file, dtmp->pass))))
1326 	  dtmp = dtmp->nexts;
1327       if (dtmp)
1328 	{
1329 	  if (option(dtmp->sopt, S_CONNECTING)
1330 	      || option(dtmp->sopt, S_CONNECTED))
1331 	    {
1332 	      sic_slog(LOG_CLIENT, "--- Closing DCC.");
1333 	      close(dtmp->fd);
1334 	    }
1335 	  sic_dserver(dtmp);
1336 	}
1337       else
1338 	  sic_slog(LOG_CLIENT, "--- No such DCC.");
1339       return 0;
1340     }
1341   return -1;
1342 }
1343