home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / yagirc-0.51.tar.gz / yagirc-0.51.tar / yagirc-0.51 / irc.c < prev    next >
C/C++ Source or Header  |  1998-05-11  |  32KB  |  1,271 lines

  1. /*
  2.  
  3.  irc.c : Main IRC related file - all kinds of stuff
  4.  
  5.     Copyright (C) 1998 Timo Sirainen
  6.  
  7.     This program is free software; you can redistribute it and/or modify
  8.     it under the terms of the GNU General Public License as published by
  9.     the Free Software Foundation; either version 2 of the License, or
  10.     (at your option) any later version.
  11.  
  12.     This program is distributed in the hope that it will be useful,
  13.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.     GNU General Public License for more details.
  16.  
  17.     You should have received a copy of the GNU General Public License
  18.     along with this program; if not, write to the Free Software
  19.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21.  
  22. #include <stdio.h>
  23. #include <stdarg.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <ctype.h>
  27.  
  28. #include <glib.h>
  29.  
  30. #include "os.h"
  31. #include "gui.h"
  32. #include "irc.h"
  33. #include "dcc.h"
  34. #include "network.h"
  35. #include "commands.h"
  36. #include "events.h"
  37. #include "txt.h"
  38. #include "script.h"
  39. #include "params.h"
  40. #include "misc.h"
  41.  
  42. char *ret_texts[] =
  43. {
  44.     "", /* error */
  45.     "", /* ok */
  46.     _("Not enough parameters given\n"),
  47.     _("Not connected to IRC server yet\n"),
  48.     _("Not joined to any channels yet\n"),
  49.     _("Error: getsockname() failed\n"),
  50.     _("Error: listen() failed\n"),
  51.     _("Multiple matches found, be more specific\n"),
  52. };
  53.  
  54. typedef int (*OUT_CALL_FUNC)(char *);
  55. typedef void (*IN_CALL_FUNC)(char *);
  56.  
  57. struct OUT_CMD_STRUCT
  58. {
  59.     char *name;
  60.     OUT_CALL_FUNC func;
  61. } OUT_CMD_STRUCT;
  62.  
  63. struct IN_CMD_STRUCT
  64. {
  65.     char *name;
  66.     IN_CALL_FUNC func;
  67. } IN_CMD_STRUCT;
  68.  
  69. static struct OUT_CMD_STRUCT out_cmds[] =
  70. {
  71.     { "SERVER", (OUT_CALL_FUNC) irccmd_server },
  72.     { "SERVERS", (OUT_CALL_FUNC) irccmd_servers },
  73.     { "DISCONNECT", (OUT_CALL_FUNC) irccmd_disconnect },
  74.     { "CTCP", (OUT_CALL_FUNC) irccmd_ctcp },
  75.     { "ME", (OUT_CALL_FUNC) irccmd_me },
  76.     { "DCC", (OUT_CALL_FUNC) irccmd_dcc },
  77.     { "WINDOW", (OUT_CALL_FUNC) irccmd_window },
  78.     { "LOAD", (OUT_CALL_FUNC) irccmd_load },
  79.     { "UNLOAD", (OUT_CALL_FUNC) irccmd_unload },
  80.     { "QUOTE", (OUT_CALL_FUNC) irccmd_quote },
  81.  
  82.     { "ISON", (OUT_CALL_FUNC) irccmd_ison },
  83.     { "MSG", (OUT_CALL_FUNC) irccmd_msg },
  84.     { "NOTICE", (OUT_CALL_FUNC) irccmd_notice },
  85.     { "QUIT", (OUT_CALL_FUNC) irccmd_quit },
  86.     { "JOIN", (OUT_CALL_FUNC) irccmd_join },
  87.     { "WHOIS", (OUT_CALL_FUNC) irccmd_whois },
  88.     { "QUERY", (OUT_CALL_FUNC) irccmd_query },
  89.     { "UNQUERY", (OUT_CALL_FUNC) irccmd_unquery },
  90.     { "PART", (OUT_CALL_FUNC) irccmd_part },
  91.     { "MODE", (OUT_CALL_FUNC) irccmd_mode },
  92.     { NULL, NULL }
  93. };
  94.  
  95. static struct IN_CMD_STRUCT in_cmds[] =
  96. {
  97.     { "PRIVMSG", (IN_CALL_FUNC) eirc_privmsg },
  98.     { "NOTICE", (IN_CALL_FUNC) eirc_notice },
  99.     { "NICK", (IN_CALL_FUNC) eirc_nick },
  100.     { "QUIT", (IN_CALL_FUNC) eirc_quit },
  101.     { "JOIN", (IN_CALL_FUNC) eirc_join },
  102.     { "PART", (IN_CALL_FUNC) eirc_part },
  103.     { "PING", (IN_CALL_FUNC) eirc_ping },
  104.     { "PONG", (IN_CALL_FUNC) eirc_pong },
  105.     { "MODE", (IN_CALL_FUNC) eirc_mode },
  106.     { "KICK", (IN_CALL_FUNC) eirc_kick },
  107.     { "INVITE", (IN_CALL_FUNC) eirc_invite },
  108.     { "TOPIC", (IN_CALL_FUNC) eirc_new_topic },
  109.     { "ERROR", (IN_CALL_FUNC) eirc_error },
  110.     { NULL, NULL }
  111. };
  112.  
  113. /* callback function list for numeric irc server replies */
  114. #define MAX_NUMCMDS 600
  115. static IN_CALL_FUNC in_numcmds[MAX_NUMCMDS];
  116.  
  117. static int ircwindows;
  118.  
  119. GList *winlist; /* List of windows */
  120. GList *servlist; /* List of servers */
  121. GList *scriptlist; /* List of scripts loaded */
  122. GList *aliases; /* List of command aliases */
  123. GList *ignores; /* Ignore list */
  124. GList *notifies; /* Notify list */
  125.  
  126. WINDOW_REC *curwin; /* current window */
  127. SERVER_REC *cserver; /* current server */
  128.  
  129. /* Write text to window - convert color codes */
  130. void drawtext(WINDOW_REC *win, int type, char *str, ...)
  131. {
  132.     va_list args;
  133.     char *out;
  134.     int pros, newln;
  135.  
  136.     g_return_if_fail(win != NULL);
  137.     g_return_if_fail(str != NULL);
  138.  
  139.     va_start(args, str);
  140.  
  141.     out = win->textbuf+win->textbuflen; pros = 0; newln = 0;
  142.     switch (type)
  143.     {
  144.         case TXT_TYPE_NOTICE:
  145.             out += sprintf(out, "\004%c - ", 11);
  146.             break;
  147.         case TXT_TYPE_ERROR:
  148.             out += sprintf(out, "\004%c - ", 12);
  149.             break;
  150.         case TXT_TYPE_SERVER_TEXT:
  151.             out += sprintf(out, "\004%c - ", 13);
  152.             break;
  153.         case TXT_TYPE_DCC:
  154.             out += sprintf(out, "\004%c", 14);
  155.             break;
  156.     }
  157.     for (; *str != '\0'; str++)
  158.     {
  159.         if (*str == '\n')
  160.         {
  161.             newln = 1;
  162.             break;
  163.         }
  164.  
  165.         if (*str != '%')
  166.         {
  167.             *out++ = *str;
  168.             continue;
  169.         }
  170.  
  171.         if (*++str == '\0') break;
  172.         switch (*str)
  173.         {
  174.             /* standard parameters */
  175.             case 's':
  176.                 {
  177.                     char *s = (char *) va_arg(args, char *);
  178.                     if (s)
  179.                         out += sprintf(out, "%s", s);
  180.                     break;
  181.                 }
  182.             case 'd':
  183.                 {
  184.                     int d = (int) va_arg(args, int);
  185.                     out += sprintf(out, "%d", d);
  186.                     break;
  187.                 }
  188.             case 'f':
  189.                 {
  190.                     double f = (double) va_arg(args, double);
  191.                     out += sprintf(out, "%0.2f", f);
  192.                     break;
  193.                 }
  194.             case 'c':
  195.                 {
  196.                     char c = (char )va_arg(args, int);
  197.                     *out++ = c;
  198.                     break;
  199.                 }
  200.             case 'u':
  201.                 {
  202.                     unsigned int d = (unsigned int) va_arg(args, unsigned int);
  203.                     out += sprintf(out, "%u", d);
  204.                     break;
  205.                 }
  206.             case 'l':
  207.                 {
  208.                     unsigned long d = (unsigned long) va_arg(args, unsigned long);
  209.                     if (*++str != 'd' && *str != 'u')
  210.                     {
  211.                         out += sprintf(out, "%ld", d);
  212.                         str--;
  213.                     }
  214.                     else
  215.                     {
  216.                         if (*str == 'd')
  217.                             out += sprintf(out, "%ld", d);
  218.                         else
  219.                             out += sprintf(out, "%lu", d);
  220.                     }
  221.                     break;
  222.                 }
  223.  
  224.             /* colors, to reset color back to default use %n */
  225.             case 'K':
  226.                 *out++ = 3;
  227.                 *out++ = BBLACK;
  228.                 break;
  229.             case 'k':
  230.                 *out++ = 3;
  231.                 *out++ = BLACK;
  232.                 break;
  233.             case 'B':
  234.                 *out++ = 3;
  235.                 *out++ = BBLUE;
  236.                 break;
  237.             case 'b':
  238.                 *out++ = 3;
  239.                 *out++ = BLUE;
  240.                 break;
  241.             case 'G':
  242.                 *out++ = 3;
  243.                 *out++ = BGREEN;
  244.                 break;
  245.             case 'g':
  246.                 *out++ = 3;
  247.                 *out++ = GREEN;
  248.                 break;
  249.             case 'A':
  250.                 *out++ = 3;
  251.                 *out++ = BCYAN;
  252.                 break;
  253.             case 'a':
  254.                 *out++ = 3;
  255.                 *out++ = CYAN;
  256.                 break;
  257.             case 'R':
  258.                 *out++ = 3;
  259.                 *out++ = BRED;
  260.                 break;
  261.             case 'r':
  262.                 *out++ = 3;
  263.                 *out++ = RED;
  264.                 break;
  265.             case 'M':
  266.                 *out++ = 3;
  267.                 *out++ = BMAGENTA;
  268.                 break;
  269.             case 'm':
  270.                 *out++ = 3;
  271.                 *out++ = MAGENTA;
  272.                 break;
  273.             case 'Y':
  274.                 *out++ = 3;
  275.                 *out++ = BYELLOW;
  276.                 break;
  277.             case 'y':
  278.                 *out++ = 3;
  279.                 *out++ = YELLOW;
  280.                 break;
  281.             case 'W':
  282.                 *out++ = 3;
  283.                 *out++ = BWHITE;
  284.                 break;
  285.             case 'w':
  286.                 *out++ = 3;
  287.                 *out++ = WHITE;
  288.                 break;
  289.             case 'n':
  290.                 *out++ = 4;
  291.                 *out++ = 15;
  292.                 break;
  293.  
  294.             /* others */
  295.             case '!':
  296.                 /* Italic on/off */
  297.                 *out++ = 31;
  298.                 break;
  299.  
  300.             case '_':
  301.                 /* BOLD on/off */
  302.                 *out++ = 2;
  303.                 break;
  304.             case '1':
  305.             case '2':
  306.             case '3':
  307.             case '4':
  308.             case '5':
  309.             case '6':
  310.             case '7':
  311.             case '8':
  312.             case '9':
  313.             case '0':
  314.                 *out++ = 4;
  315.                 *out++ = (*str-'0')+1;
  316.                 break;
  317.             case '%':
  318.                 *out++ = '%';
  319.                 break;
  320.  
  321.             default:
  322.                 *out++ = '%';
  323.                 *out++ = *str;
  324.                 break;
  325.         }
  326.     }
  327.     va_end(args);
  328.     *out = '\0';
  329.     win->textbuflen = (int) (out-win->textbuf);
  330.  
  331.     if (newln)
  332.     {
  333.         win->textbuflen = 0;
  334.         win->drawfunc(win->textbuf, win->drawfuncdata);
  335.     }
  336.  
  337.     if (curwin != win && win->new_data != 1)
  338.     {
  339.         win->new_data = 1;
  340.         gui_update_statusbar(NULL);
  341.     }
  342. }
  343.  
  344. /* clear buffer */
  345. void cleartextbuf(WINDOW_REC *win)
  346. {
  347.     win->textbuflen = 0;
  348. }
  349.  
  350. /* Destroy IRC channel record */
  351. void irc_chan_free(CHAN_REC *chan)
  352. {
  353.     g_return_if_fail(chan != NULL);
  354.  
  355.     g_free(chan->name);
  356.     if (chan->topic != NULL) g_free(chan->topic);
  357.     if (chan->nicks != NULL)
  358.     {
  359.         GList *tmp;
  360.  
  361.         for (tmp = g_list_first(chan->nicks); tmp != NULL; tmp = tmp->next)
  362.             g_free(tmp->data);
  363.         g_list_free(chan->nicks);
  364.     }
  365.     if (chan->window != NULL && chan->window->chanlist != NULL)
  366.         chan->window->chanlist = g_list_remove(chan->window->chanlist, chan);
  367.     g_free(chan);
  368. }
  369.  
  370. /* Find server record */
  371. SERVER_REC *irc_get_server(int handle)
  372. {
  373.     GList *tmp;
  374.  
  375.     for (tmp = g_list_first(servlist); tmp != NULL; tmp = tmp->next)
  376.     {
  377.         SERVER_REC *server;
  378.  
  379.         server = (SERVER_REC *) tmp->data;
  380.         if (server->handle == handle) return server;
  381.     }
  382.  
  383.     return NULL;
  384. }
  385.  
  386. /* Find channel record of chan in server serv, if serv==NULL, find from all servers */
  387. CHAN_REC *channel_joined(SERVER_REC *serv, char *chan)
  388. {
  389.     GList *link, *winl;
  390.     char *ptr;
  391.  
  392.     g_return_val_if_fail(chan != NULL, NULL);
  393.  
  394.     ptr = strchr(chan, ' ');
  395.     if (ptr != NULL)
  396.     {
  397.         int tag;
  398.  
  399.         *ptr = '\0';
  400.         if (sscanf(ptr+1, "%d", &tag) == 1)
  401.         {
  402.             serv = irc_get_server(tag);
  403.             if (serv == NULL) return NULL; /* no such server.. */
  404.         }
  405.     }
  406.  
  407.     if (*chan == '=')
  408.         serv = NULL; /* Trying to find DCC query - server doesn't matter */
  409.  
  410.     for (winl = g_list_first(winlist); winl != NULL; winl = winl->next)
  411.     {
  412.         WINDOW_REC *win;
  413.  
  414.         win = (WINDOW_REC *) winl->data;
  415.         for (link = g_list_first(win->chanlist); link != NULL; link = link->next)
  416.         {
  417.             CHAN_REC *ch;
  418.  
  419.             ch = (CHAN_REC *) link->data;
  420.             if ((serv == NULL || serv == ch->server) &&
  421.                 strcasecmp(chan, ch->name) == 0)
  422.             {
  423.                 /* found one! */
  424.                 if (ptr != NULL) *ptr = ' ';
  425.                 return ch;
  426.             }
  427.         }
  428.     }
  429.  
  430.     if (ptr != NULL) *ptr = ' ';
  431.     return NULL;
  432. }
  433.  
  434. /* IRC nick comparision for sort functions */
  435. int irc_nicks_compare(char *p1, char *p2)
  436. {
  437.     if (p1 == NULL) return -1;
  438.     if (p2 == NULL) return 1;
  439.  
  440.     if (*p1 == '@' && *p2 != '@') return -1;
  441.     if (*p2 == '@' && *p1 != '@') return 1;
  442.  
  443.     return strcasecmp(p1, p2);
  444. }
  445.  
  446. /* Parse command line sent by server */
  447. static int irc_parse_line(SERVER_REC *serv, char *str)
  448. {
  449.     char *cmd;
  450.     int cmdnum, n;
  451.     WINDOW_REC *win;
  452.  
  453.     g_return_val_if_fail(serv != NULL, 2);
  454.     g_return_val_if_fail(str != NULL, 2);
  455.  
  456.     if ((curwin->curchan != NULL && curwin->curchan->server == serv) ||
  457.         curwin->defserv == serv)
  458.     {
  459.         win = curwin;
  460.     }
  461.     else
  462.     {
  463.         if (serv->defwin != NULL)
  464.             win = serv->defwin;
  465.         else
  466.         {
  467.             /* server has no default window.. */
  468.             win = curwin;
  469.             drawtext(curwin, TXT_TYPE_DEFAULT, "[%s(%d)] ", serv->name, serv->handle);
  470.         }
  471.     }
  472.  
  473.     eserver = serv; edefwin = win;
  474.     esendnick = NULL;
  475.     esendaddr = NULL;
  476.  
  477.     if (*str == ':')
  478.     {
  479.         /* read prefix.. */
  480.         esendnick = ++str;
  481.         while (*str != '\0' && *str != ' ')
  482.         {
  483.             if (*str == '!')
  484.             {
  485.                 *str = '\0';
  486.                 esendaddr = str+1;
  487.             }
  488.             str++;
  489.         }
  490.         if (*str == ' ')
  491.         {
  492.             *str++ = '\0';
  493.             while (*str == ' ') str++;
  494.         }
  495.     }
  496.  
  497.     if (*str == '\0') return 1; /* empty line */
  498.  
  499. #ifdef USE_SCRIPT
  500.     script_event(eserver, NULL, NULL, "SERVERMSG", str);
  501. #endif
  502.  
  503.     /* get command.. */
  504.     cmd = str;
  505.     while (*str != '\0' && *str != ' ') str++;
  506.     if (*str == ' ') *str++ = '\0';
  507.     while (*str == ' ') str++;
  508.  
  509. #ifdef USE_SCRIPT
  510.     /* Call script events */
  511.     if (!script_event(eserver, esendnick == NULL ? "" : esendnick, esendaddr, cmd, str))
  512.     {
  513.         /* script handled this event all by itself, don't call build in events */
  514.         return 1;
  515.     }
  516. #endif
  517.     cmdnum = -1;
  518.     if (isdigit(*cmd))
  519.     {
  520.         /* numeric command */
  521.         if (sscanf(cmd, "%d", &cmdnum) != 1) cmdnum = -1;
  522.  
  523.         if (cmdnum < MAX_NUMCMDS && in_numcmds[cmdnum] != NULL)
  524.         {
  525.             in_numcmds[cmdnum](str);
  526.             cleartextbuf(curwin);
  527.             return 1;
  528.         }
  529.  
  530.         /* skip targets */
  531.         while (*str != '\0' && *str != ' ') str++;
  532.         while (*str == ' ') str++;
  533.  
  534.         /*drawtext(edefwin, "%s ", cmd);*/
  535.     }
  536.     else
  537.     {
  538.         /* named command.. */
  539.         for (n = 0; in_cmds[n].name != NULL; n++)
  540.         {
  541.             if (strcasecmp(in_cmds[n].name, cmd) == 0)
  542.             {
  543.                 in_cmds[n].func(str);
  544.                 cleartextbuf(curwin);
  545.                 return 1;
  546.             }
  547.         }
  548.         drawtext(win, TXT_TYPE_SERVER_TEXT, "(%s)", cmd);
  549.     }
  550.  
  551.     if (*str == ':')
  552.         str++;
  553.     else
  554.     {
  555.         char *ptr;
  556.  
  557.         ptr = strstr(str, " :");
  558.         if (ptr != NULL)
  559.         {
  560.             *ptr = '\0';
  561.  
  562.             drawtext(win, TXT_TYPE_SERVER_TEXT, "%s %s\n", str, ptr+2);
  563.             return 1;
  564.         }
  565.     }
  566.     drawtext(win, TXT_TYPE_SERVER_TEXT, "%s\n", str);
  567.  
  568.     return 1;
  569. }
  570.  
  571. /* Read line from server */
  572. static int irc_receive_line(SERVER_REC *serv, char *str)
  573. {
  574.     int ret;
  575.  
  576.     g_return_val_if_fail(serv != NULL, -1);
  577.     g_return_val_if_fail(str != NULL, -1);
  578.  
  579.     ret = read_line(1, serv->handle, str, serv->buf, sizeof(serv->buf), &serv->bufpos);
  580.     if (ret == -1)
  581.     {
  582.         /* connection lost */
  583.         drawtext(curwin->defserv == serv ? curwin : serv->defwin,
  584.                  TXT_TYPE_SERVER_TEXT, IRCTXT_CONNECTION_LOST, serv->name);
  585.         irc_server_disconnect(serv);
  586.     }
  587.     return ret;
  588. }
  589.  
  590. /* Send command to IRC server */
  591. int irc_send_cmd(SERVER_REC *serv, char *cmd)
  592. {
  593.     char str[512];
  594.     int len;
  595.  
  596.     g_return_val_if_fail(cmd != NULL, 2);
  597.  
  598.     if (serv == NULL || !serv->connected)
  599.     {
  600.         drawtext(curwin, TXT_TYPE_ERROR, IRCTXT_NOT_CONNECTED);
  601.         return 0;
  602.     }
  603.  
  604.     /* just check that we don't send any longer commands than 512 bytes.. */
  605.     len = 0;
  606.     while (*cmd != '\0' && len < 510)
  607.     {
  608.         str[len] = *cmd++;
  609.         len++;
  610.     }
  611.     str[len++] = 13; str[len] = 10;
  612.  
  613.     return net_transmit(serv->handle, str, len);
  614. }
  615.  
  616. /* Check if nick is in ignore list */
  617. int irc_is_ignored(char *nick, int type)
  618. {
  619.     GList *tmp;
  620.  
  621.     for (tmp = g_list_first(ignores); tmp != NULL; tmp = tmp->next)
  622.     {
  623.         char *data, *ptr;
  624.  
  625.         data = g_strdup((char *) tmp->data);
  626.         ptr = strchr(data, ' ');
  627.         if (ptr == NULL)
  628.         {
  629.             g_free(data);
  630.             continue; /* illegal ignore list entry */
  631.         }
  632.         *ptr++ = '\0';
  633.  
  634.         if (strcasecmp(data, nick) == 0)
  635.         {
  636.             strupr(ptr);
  637.             if (strstr(ptr, "ALL") != NULL)
  638.             {
  639.                 g_free(data);
  640.                 return 1;
  641.             }
  642.             switch (type)
  643.             {
  644.                 case IGNORE_PUBLIC:
  645.                     if (strstr(ptr, "PUBLIC") != NULL)
  646.                     {
  647.                         g_free(data);
  648.                         return 1;
  649.                     }
  650.                     break;
  651.                 case IGNORE_PRIVATE:
  652.                     if (strstr(ptr, "PRIVATE") != NULL)
  653.                     {
  654.                         g_free(data);
  655.                         return 1;
  656.                     }
  657.                     break;
  658.                 case IGNORE_CTCP:
  659.                     if (strstr(ptr, "CTCP") != NULL)
  660.                     {
  661.                         g_free(data);
  662.                         return 1;
  663.                     }
  664.                     break;
  665.             }
  666.             g_free(data);
  667.             return 0;
  668.         }
  669.         g_free(data);
  670.    }
  671.  
  672.     return 0;
  673. }
  674.  
  675. /* strip all extra characted from nick */
  676. static char *strip_nick(char *nick)
  677. {
  678.     char *ptr;
  679.     int n;
  680.  
  681.     g_return_val_if_fail(nick != NULL, NULL);
  682.  
  683.     for (n = 0, ptr = nick; *ptr != '\0'; ptr++)
  684.         if (isalnum(*ptr)) nick[n++] = toupper(*ptr);
  685.  
  686.     nick[n] = '\0';
  687.  
  688.     return nick;
  689. }
  690.  
  691. /* find best matching nick for nick completion */
  692. static char *find_nick(GList *list, char *pnick)
  693. {
  694.     char *tmp;
  695.     int pnicklen;
  696.  
  697.     char *best, *besttmp;
  698.     int bestlen;
  699.  
  700.     g_return_val_if_fail(pnick != NULL, NULL);
  701.     if (list == NULL) return NULL;
  702.  
  703.     besttmp = best = NULL; bestlen = 0;
  704.  
  705.     pnick = strip_nick(g_strdup(pnick));
  706.     if (*pnick == '\0')
  707.     {
  708.         g_free(pnick);
  709.         return NULL;
  710.     }
  711.     pnicklen = strlen(pnick);
  712.  
  713.     list = g_list_first(list);
  714.     while (list != NULL)
  715.     {
  716.         tmp = g_strdup((char *) list->data);
  717.         strip_nick(tmp);
  718.         if (strncmp(tmp, pnick, pnicklen) == 0)
  719.         {
  720.             if (((strlen(tmp) > bestlen ||
  721.                   (strlen(tmp) == bestlen && strstr(besttmp, "BOT") != NULL))) &&
  722.                 curwin->defserv != NULL && strcasecmp(list->data, curwin->defserv->nick) != 0)
  723.             {
  724.                 best = (char *) list->data;
  725.                 if (besttmp != NULL) g_free(besttmp);
  726.                 besttmp = tmp;
  727.                 bestlen = strlen(tmp);
  728.                 if (bestlen == pnicklen)
  729.                 {
  730.                     /* identical match */
  731.                     break;
  732.                 }
  733.             }
  734.         }
  735.         else
  736.         {
  737.             g_free(tmp);
  738.         }
  739.         list = list->next;
  740.     }
  741.     if (besttmp != NULL) g_free(besttmp);
  742.     g_free(pnick);
  743.     if (best != NULL && isircflag(*best)) best++; /* skip @ or + */
  744.     return best;
  745. }
  746.  
  747. /* Write text to channel */
  748. static int irc_write_text(CHAN_REC *chan, char *text)
  749. {
  750.     char str[512], *ptr;
  751.     int len;
  752.  
  753.     g_return_val_if_fail(text != NULL, 0);
  754.  
  755.     if (chan == NULL)
  756.     {
  757.         drawtext(curwin, TXT_TYPE_ERROR, IRCTXT_NOT_JOINED);
  758.         return 0;
  759.     }
  760.     if (*text == '\0') return 1;
  761.  
  762.     /* first parameter: channel name */
  763.     len = sprintf(str, "%s ", chan->name);
  764.  
  765.     /* check for nick completion */
  766.     for (ptr = text; *ptr != '\0' && *ptr != ' '; ptr++)
  767.     {
  768.         if (*ptr == ':')
  769.         {
  770.             char *nick;
  771.  
  772.             if (ptr == text) break;
  773.  
  774.             *ptr = '\0';
  775.             nick = find_nick(chan->nicks, text);
  776.             if (nick == NULL)
  777.             {
  778.                 /* No such nick.. */
  779.                 *ptr = ':';
  780.                 break;
  781.             }
  782.             text = ptr+1;
  783.             len += sprintf(str+len, "%s:", nick);
  784.             break;
  785.         }
  786.     }
  787.  
  788.     strncpy(str+len, text, 510-len); str[510] = '\0';
  789.     irccmd_msg(str);
  790.     return 1;
  791. }
  792.  
  793. /* Parse outgoing line */
  794. int irc_parse_outgoing(CHAN_REC *chan, char *line)
  795. {
  796.     SERVER_REC *serv;
  797.     char str[512], *cmd, *ptr;
  798.     int n, ret;
  799.     GList *tmp;
  800.  
  801.     g_return_val_if_fail(line != NULL, 0);
  802.  
  803.     if (*line != '/')
  804.     {
  805.         /* write text to channel */
  806.         irc_write_text(chan, line);
  807.         return 1;
  808.     }
  809.  
  810.     /* must be some command.. */
  811.     strncpy(str, line+1, sizeof(str)-1); str[sizeof(str)-1] = '\0';
  812.     ptr = strchr(str, ' ');
  813.     if (ptr != NULL) *ptr++ = '\0'; else ptr = "";
  814.     cmd = str;
  815.  
  816.     if (*cmd == '/')
  817.     {
  818.         /* //command overrides any aliases/scripts */
  819.         cmd++;
  820.     }
  821.     else
  822.     {
  823.         /* check if there's an alias for command */
  824.         for (tmp = g_list_first(aliases); tmp != NULL; tmp = tmp->next)
  825.         {
  826.             ALIAS_REC *al;
  827.  
  828.             al = (ALIAS_REC *) tmp->data;
  829.             if (strcasecmp(al->alias, str) == 0)
  830.             {
  831.                 cmd = al->cmd;
  832.                 break;
  833.             }
  834.         }
  835.  
  836. #ifdef USE_SCRIPT
  837.         if (!script_event(cserver, NULL, NULL, cmd, ptr)) return 1;
  838. #endif
  839.     }
  840.  
  841.     /* check if command is found */
  842.     ret = RET_ERROR;
  843.     for (n = 0; out_cmds[n].name != NULL; n++)
  844.     {
  845.         if (strcasecmp(out_cmds[n].name, cmd) == 0)
  846.         {
  847.             ret = out_cmds[n].func(ptr);
  848.             break;
  849.         }
  850.     }
  851.  
  852.     if (out_cmds[n].name == NULL)
  853.     {
  854.         /* command not found - just send it like it was typed */
  855.         serv = chan == NULL ? curwin->defserv : chan->server;
  856.         if (serv != NULL) irc_send_cmd(serv, line+1);
  857.         return 1;
  858.     }
  859.  
  860.  
  861.     if (ret == RET_ERR_PARAM)
  862.     {
  863.         /* internal error! */
  864.         drawtext(chan != NULL ? chan->window : curwin, TXT_TYPE_ERROR,
  865.                  "%_Internal error%_: g_return_val_if_fail() function failed!\n");
  866.     }
  867.     else if (ret != RET_OK && ret != RET_ERROR)
  868.     {
  869.         /* print error message */
  870.         drawtext(chan != NULL ? chan->window : curwin, TXT_TYPE_ERROR, ret_texts[ret]);
  871.     }
  872.  
  873.     return 1;
  874. }
  875.  
  876. /* Initialize new IRC window */
  877. WINDOW_REC *irc_window_new(SERVER_REC *server, WINDOW_REC *win)
  878. {
  879.     WINDOW_REC *w;
  880.  
  881.     w = (WINDOW_REC *) g_malloc(sizeof(WINDOW_REC));
  882.     memset(w, 0, sizeof(WINDOW_REC));
  883.  
  884.     w->num = ++ircwindows;
  885.     w->textbuflen = 0;
  886.     w->chanlist = NULL;
  887.     w->curchan = NULL;
  888.     w->defserv = NULL;
  889.     w->defserv = server;
  890.  
  891.     gui_window_init(w, win);
  892.  
  893.     winlist = g_list_append(winlist, w);
  894.     irc_window_focus(w);
  895.  
  896.     return w;
  897. }
  898.  
  899. /* Select new "default server window" for server messages */
  900. void irc_select_new_server_window(WINDOW_REC *win)
  901. {
  902.     GList *tmp;
  903.  
  904.     /* closing the default window for server .. need new one.. */
  905.     for (tmp = g_list_first(winlist); tmp != NULL; tmp = tmp->next)
  906.     {
  907.         WINDOW_REC *w;
  908.  
  909.         w = (WINDOW_REC *) tmp->data;
  910.         if (w->defserv == win->defserv && w != win)
  911.         {
  912.             /* found one! */
  913.             win->defserv->defwin = w;
  914.             break;
  915.         }
  916.     }
  917.     if (tmp == NULL)
  918.     {
  919.         /* none found.. */
  920.         win->defserv->defwin = NULL;
  921.     }
  922. }
  923.  
  924. /* Close IRC window, sends part messages for every channel in window */
  925. void irc_window_close(WINDOW_REC *win)
  926. {
  927.     GList *tmp;
  928.  
  929.     g_return_if_fail(win != NULL);
  930.  
  931.     if (win->defserv != NULL && win->defserv->defwin == win)
  932.         irc_select_new_server_window(win);
  933.  
  934.     /* Send PART command for every channel in this window */
  935.     while ((tmp = g_list_first(win->chanlist)) != NULL)
  936.     {
  937.         CHAN_REC *chan;
  938.  
  939.         chan = (CHAN_REC *) tmp->data;
  940.         if (chan->server != NULL && chan->server->connected)
  941.         {
  942.             if (*chan->name == '#' || *chan->name == '&')
  943.                 irccmd_part(chan->name);
  944.         }
  945.         gui_channel_part(chan, NULL);
  946.         irc_chan_free(chan);
  947.     }
  948.  
  949.     /* renumber windows */
  950.     for (tmp = g_list_nth(winlist, win->num); tmp != NULL; tmp = tmp->next)
  951.     {
  952.         WINDOW_REC *w;
  953.  
  954.         w = (WINDOW_REC *) tmp->data;
  955.         w->num--;
  956.     }
  957.     ircwindows--;
  958.  
  959.     winlist = g_list_remove(winlist, win);
  960.     gui_window_deinit(win);
  961.     g_free(win);
  962.  
  963.     if (winlist == NULL)
  964.     {
  965.         /* this was the last window.. */
  966.         curwin = NULL;
  967.     }
  968.     else
  969.     {
  970.         /* select new window */
  971.         tmp = g_list_first(winlist);
  972.         if (tmp->data == win) tmp = tmp->next;
  973.         if (tmp == NULL)
  974.         {
  975.             /* this was the last window anyway */
  976.             curwin = NULL;
  977.         }
  978.         else
  979.         {
  980.             /* new window found, great. */
  981.             curwin = (WINDOW_REC *) tmp->data;
  982.             gui_window_select(curwin);
  983.         }
  984.     }
  985. }
  986.  
  987. /* Window got focus */
  988. void irc_window_focus(WINDOW_REC *win)
  989. {
  990.     curwin = win;
  991.     if (win == NULL)
  992.         cserver = NULL;
  993.     else
  994.     {
  995.         cserver = win->curchan != NULL ? win->curchan->server : win->defserv;
  996.         gui_window_update(win);
  997.  
  998.         if (win->new_data)
  999.         {
  1000.             GList *tmp;
  1001.  
  1002.             /* remove hilight from all channels in this window */
  1003.             for (tmp = g_list_first(win->chanlist); tmp != NULL; tmp = tmp->next)
  1004.             {
  1005.                 CHAN_REC *chan;
  1006.  
  1007.                 chan = (CHAN_REC *) tmp->data;
  1008.                 if (chan->new_data)
  1009.                 {
  1010.                     chan->new_data = 0;
  1011.                     gui_channel_dehilight(chan);
  1012.                 }
  1013.             }
  1014.  
  1015.             win->new_data = 0;
  1016.             gui_update_statusbar(NULL);
  1017.         }
  1018.     }
  1019. }
  1020.  
  1021. void irc_init(void)
  1022. {
  1023.     winlist = NULL; servlist = NULL; scriptlist = NULL;
  1024.     ircwindows = 0;
  1025.  
  1026.     /* set numeric commands */
  1027.     memset(in_numcmds, 0, sizeof(in_numcmds));
  1028.     in_numcmds[1] = (IN_CALL_FUNC) eirc_welcome;
  1029.     in_numcmds[4] = (IN_CALL_FUNC) eirc_connected;
  1030.     in_numcmds[303] = (IN_CALL_FUNC) eirc_ison;
  1031.     in_numcmds[332] = (IN_CALL_FUNC) eirc_topic;
  1032.     in_numcmds[333] = (IN_CALL_FUNC) eirc_topic_info;
  1033.     in_numcmds[352] = (IN_CALL_FUNC) eirc_who;
  1034.     in_numcmds[353] = (IN_CALL_FUNC) eirc_names_list;
  1035.     in_numcmds[366] = (IN_CALL_FUNC) eirc_end_of_names;
  1036.     in_numcmds[367] = (IN_CALL_FUNC) eirc_ban_list;
  1037.     in_numcmds[368] = (IN_CALL_FUNC) eirc_end_of_banlist;
  1038.     in_numcmds[405] = (IN_CALL_FUNC) eirc_too_many_channels;
  1039.     in_numcmds[433] = (IN_CALL_FUNC) eirc_nick_in_use;
  1040.     in_numcmds[437] = (IN_CALL_FUNC) eirc_nick_unavailable;
  1041.     in_numcmds[471] = (IN_CALL_FUNC) eirc_channel_is_full;
  1042.     in_numcmds[473] = (IN_CALL_FUNC) eirc_invite_only;
  1043.     in_numcmds[474] = (IN_CALL_FUNC) eirc_banned;
  1044.     in_numcmds[475] = (IN_CALL_FUNC) eirc_bad_channel_key;
  1045.     in_numcmds[476] = (IN_CALL_FUNC) eirc_bad_channel_mask;
  1046.  
  1047.     events_init();
  1048.     dcc_init();
  1049. #ifdef USE_SCRIPT
  1050.     script_init();
  1051. #endif
  1052. }
  1053.  
  1054. void irc_deinit(void)
  1055. {
  1056.     GList *tmp;
  1057.  
  1058. #ifdef USE_SCRIPT
  1059.     script_deinit();
  1060. #endif
  1061.  
  1062.     while (servlist != NULL)
  1063.         irc_server_disconnect((SERVER_REC *) servlist->data);
  1064.  
  1065.     if (winlist != NULL)
  1066.     {
  1067.         /* close every unclosed window properly.. */
  1068.         while ((tmp = g_list_first(winlist)) != NULL)
  1069.             irc_window_close((WINDOW_REC *) tmp->data);
  1070.         winlist = NULL;
  1071.     }
  1072.  
  1073.     events_deinit();
  1074.     dcc_deinit();
  1075. }
  1076.  
  1077. /* Select any other channel in window except the one we are now */
  1078. int irc_select_new_channel(WINDOW_REC *window)
  1079. {
  1080.     GList *tmp;
  1081.  
  1082.     g_return_val_if_fail(window != NULL, 0);
  1083.  
  1084.     for (tmp = g_list_first(window->chanlist); tmp != NULL; tmp = tmp->next)
  1085.         if (tmp->data != window->curchan) break;
  1086.  
  1087.     window->curchan = tmp == NULL ? NULL : tmp->data;
  1088.     if (tmp == NULL)
  1089.     {
  1090.         /* no more channels */
  1091.         return 0;
  1092.     }
  1093.  
  1094.     if (*window->curchan->name == '#' || *window->curchan->name == '&')
  1095.         drawtext(window, TXT_TYPE_NOTICE, IRCTXT_TALKING_IN,
  1096.                  window->curchan->name);
  1097.     else
  1098.         drawtext(window, TXT_TYPE_NOTICE, IRCTXT_QUERYING,
  1099.                  window->curchan->name);
  1100.     irc_window_focus(window);
  1101.     return 1;
  1102. }
  1103.  
  1104. /* input function: handle incoming server messages */
  1105. static void irc_parse_incoming(SERVER_REC *server)
  1106. {
  1107.     char str[512];
  1108.  
  1109.     g_return_if_fail(server != NULL);
  1110.  
  1111.     while (irc_receive_line(server, str) > 0)
  1112.         irc_parse_line(server, str);
  1113. }
  1114.  
  1115. /* timeout function: send /ISON commands to server to check if someone in
  1116.    notify list is in IRC */
  1117. static int irc_timeout_func(SERVER_REC *serv)
  1118. {
  1119.     char tmp[512];
  1120.     int pos;
  1121.     GList *list;
  1122.  
  1123.     g_return_val_if_fail(serv != NULL, 0);
  1124.  
  1125.     if (serv->ison_reqs != 0)
  1126.     {
  1127.         /* still not received all replies to previous /ISON commands.. */
  1128.         return 1;
  1129.     }
  1130.  
  1131.     list = g_list_first(notifies);
  1132.     if (list == NULL) return 1; /* no-one in notify list */
  1133.  
  1134.     pos = sprintf(tmp, "ISON :");
  1135.     while (list != NULL)
  1136.     {
  1137.         int len;
  1138.  
  1139.         len = strlen((char *) list->data);
  1140.  
  1141.         if (pos+len+1 > 510)
  1142.         {
  1143.             irc_send_cmd(serv, tmp);
  1144.             serv->ison_reqs++;
  1145.             pos = sprintf(tmp, "ISON :");
  1146.         }
  1147.  
  1148.         if (tmp[pos-1] == ':')
  1149.             pos += sprintf(tmp+pos, "%s", (char *) list->data);
  1150.         else
  1151.             pos += sprintf(tmp+pos, " %s", (char *) list->data);
  1152.  
  1153.         list = list->next;
  1154.     }
  1155.  
  1156.     irc_send_cmd(serv, tmp);
  1157.     serv->ison_reqs++;
  1158.     return 1;
  1159. }
  1160.  
  1161. /* Connect to IRC server */
  1162. SERVER_REC *irc_server_connect(char *name, int port)
  1163. {
  1164.     int handle;
  1165.     SERVER_REC *serv;
  1166.     char tmp[512];
  1167.  
  1168.     g_return_val_if_fail(name != NULL, NULL);
  1169.  
  1170.     if (*name == '\0') return NULL;
  1171.  
  1172.     if (port == 0) port = 6667;
  1173.     handle = net_connect(name, port);
  1174.     if (handle == -1)
  1175.     {
  1176.         drawtext(curwin, TXT_TYPE_ERROR, IRCTXT_CANT_CONNECT, name, port);
  1177.         return NULL;
  1178.     }
  1179.  
  1180.     serv = (SERVER_REC *) g_malloc(sizeof(SERVER_REC));
  1181.     serv->name = g_strdup(name);
  1182.     serv->nick = g_strdup(default_nick);
  1183.     serv->handle = handle;
  1184.     serv->readtag = gui_input_add(handle, GUI_INPUT_READ, (GUI_INPUT_FUNC) irc_parse_incoming, serv);
  1185.     serv->timetag = gui_timeout_new(10000, (GUI_TIMEOUT_FUNC) irc_timeout_func, serv);
  1186.     serv->bufpos = 0;
  1187.  
  1188.     serv->connected = 1;
  1189.     sprintf(tmp, "NICK %s", serv->nick);
  1190.     irc_send_cmd(serv, tmp);
  1191.     sprintf(tmp, "USER %s - - :%s", user_name, real_name);
  1192.     irc_send_cmd(serv, tmp);
  1193.     serv->connected = 0;
  1194.  
  1195.     servlist = g_list_append(servlist, serv);
  1196.     return serv;
  1197. }
  1198.  
  1199. /* Disconnect from IRC server */
  1200. void irc_server_disconnect(SERVER_REC *server)
  1201. {
  1202.     GList *tmp, *nextwin;
  1203. #ifdef USE_SCRIPT
  1204.     char *str;
  1205. #endif
  1206.  
  1207.     g_return_if_fail(server != NULL);
  1208.  
  1209.     if (server->handle != -1) net_disconnect(server->handle);
  1210.     gui_input_remove(server->readtag);
  1211.     gui_timeout_remove(server->timetag);
  1212.     server->connected = 0;
  1213.  
  1214.     for (tmp = nextwin = g_list_first(winlist); nextwin != NULL; tmp = nextwin)
  1215.     {
  1216.         WINDOW_REC *win;
  1217.         GList *chan;
  1218.         int exists;
  1219.  
  1220.         nextwin = tmp->next;
  1221.         win = (WINDOW_REC *) tmp->data;
  1222.         if (win->defserv == server) win->defserv = NULL;
  1223.  
  1224.         exists = 0;
  1225.         for (chan = g_list_first(win->chanlist); chan != NULL;)
  1226.         {
  1227.             CHAN_REC *ch;
  1228.  
  1229.             ch = (CHAN_REC *) chan->data;
  1230.             if (ch->server == server && *ch->name != '=')
  1231.             {
  1232.                 chan = chan->next;
  1233.                 if (ch == win->curchan) win->curchan = NULL;
  1234.                 gui_channel_part(ch, NULL);
  1235.                 irc_chan_free(ch);
  1236.             }
  1237.             else
  1238.             {
  1239.                 chan = chan->next;
  1240.                 exists = 1;
  1241.             }
  1242.         }
  1243.  
  1244.         if (!exists)
  1245.         {
  1246.             /* no channels left in this window */
  1247.             if (g_list_first(winlist)->next != NULL) irc_window_close(win);
  1248.         }
  1249.         else
  1250.         {
  1251.             if (win->curchan == NULL) irc_select_new_channel(win);
  1252.         }
  1253.     }
  1254.  
  1255.     irc_window_focus(curwin);
  1256.     gui_connected(server, 0);
  1257. #ifdef USE_SCRIPT
  1258.     str = (char *) g_malloc(strlen(server->name)+20);
  1259.     sprintf(str, "%d %s", server->handle, server->name);
  1260.     script_event(server, "", "", "DISCONNECTED", str);
  1261.     g_free(str);
  1262. #endif
  1263.  
  1264.     servlist = g_list_remove(servlist, server);
  1265.  
  1266.     if (server->nick != NULL) g_free(server->nick);
  1267.     g_free(server->name);
  1268.     g_free(server);
  1269. }
  1270.  
  1271.