/* * display.c: routines that proceeds the data from a server and format it. * * Copyright(c) 1997-2000 - All Rights Reserved * * See the COPYRIGHT file. */ #ifndef lint static char rcsid[] = "@(#)$Id: display.c,v 1.59 2001/03/14 19:50:20 kalt Exp $"; #endif #include "os.h" #include "struct.h" #include "term.h" #include "window.h" #include "format.h" #include "option.h" #include "server.h" #include "utils.h" extern struct server_ *server; extern char need_collect; extern char tab_add(char *, char *); extern char space_add(char *, char *); extern char sic_ctcp(char *, char, char *); extern void url_find(char *, char *, char *); extern void yow_back(char *, char *); static char outp[1024]; static u_char outa[1024]; static char *s, *c, *d, *p; static int n; /* locals is currently unused */ static char toself, locals, froms, fromv, toch, showid; static char nick[10], user[11], host[64]; /* * "RAW" options * %d recipient * %m command * %o origin (nick!user@host or server.name) * %p parameters * * blah * %B bold on/off * %R reverse video on/off * %U underline on/off * * "processed" options * %I tabkey identity (%u@%h) * %n!%u@%h * %N numeric (alias for command??) * %s server name the message originated from * %S server name you are connected to */ static int sic_sprintf(buffer, attr, format, channel, flags) char *buffer, *format, *channel; u_char *attr; unsigned int *flags; { char *wp = buffer; u_char video = 0, vplus = 0, anyattr = 0, checknow = 0, dobeep = 0; int align; *wp = '\0'; while (*format) { while (*format && *format != '%') { *wp++ = *format++; *attr++ = video; if (video) anyattr = 1; } if(*format++) { if (align = atoi(format)) { if (align < 0) format++; while (isdigit((int) *format)) format++; } *wp = '\0'; switch (*format++) { case '%': *wp++ = '%'; *attr++ = video; *wp = '\0'; align = 0; break; case 'B': video ^= TERM_BOLD; align = 0; break; case 'c': strcat(buffer, d); break; case 'd': strcat(buffer, d); break; case 'h': strcat(buffer, host); break; case 'I': if (showid) { strcat(buffer, user); strcat(buffer, "@"); strcat(buffer, host); } break; case 'm': strcat(buffer, c); break; case 'n': strcat(buffer, nick); get_ignore(s, channel, flags); if (*flags & LOG_HIGHLIGHT && !(video & TERM_STANDOUT)) video |= (vplus = TERM_STANDOUT); break; case 'N': sprintf(wp, "%3.3d", n); break; case 'p': if (*format != '{') strcat(buffer, p); else { strcat(buffer, sic_split(p, format)); if (index(format, '}')) format = index(format, '}') + 1; } if (channel && (*flags & LOG_PUBLIC) && (*flags & (LOG_MSG|LOG_NOTICE))) checknow = 1; break; case 'o': strcat(buffer, s); get_ignore(s, channel, flags); if (*flags & LOG_HIGHLIGHT && !(video & TERM_STANDOUT)) video |= (vplus = TERM_STANDOUT); break; case 'R': video ^= TERM_STANDOUT; align = 0; break; case 's': if (froms) strcat(buffer, s); else strcat(buffer, "żżż"); break; case 'S': if (server) strcat(buffer, server->sname); else strcat(buffer, ""); break; case 'u': strcat(buffer, user); break; case 'U': video ^= TERM_UNDERLINE; align = 0; break; default: *wp++ = (char) 191; *attr++ = TERM_STANDOUT; anyattr = 1; *wp = '\0'; align = 0; term_beep(); break; } if (align && *wp != '\0') { if (align > 0) if (strlen(wp) < align) { char *where = wp; align -= strlen(wp); where += strlen(wp); while (align--) *where++ = ' '; *where = '\0'; } else wp[align] = '\0'; else if (strlen(wp) < -align) { char *where = wp - align; char *end = wp + strlen(wp); while (end >= wp) *where-- = *end--; while (where >= wp) *where-- = ' '; } else wp[-align] = '\0'; } if (checknow) { char *where = wp, how = 0; u_char extra; int len; checknow = 0; len = get_keyword(channel, &where, &how, flags); if (how & K_BEEP) dobeep = 1; if (how & K_SPACE && *flags & LOG_PUBLIC) space_add(nick, ""); if (len || video) anyattr = 1; while (*wp != '\0') { extra = 0; /* check for ircii rendering junk */ if (*wp == ctrl('_')) extra = TERM_EUNDERLINE; else if (*wp == ctrl('V')) extra = TERM_ESTANDOUT; else if (*wp == ctrl('B')) extra = TERM_EBOLD; if (extra) { video ^= extra; extra = TERM_EHIDE; anyattr = 1; } /* keywords */ *attr = video | extra; if (where) { if (wp >= where && (wp - where) < len) *attr |= (how & 0x0F); if ((wp - where) == len) { where = wp; if (len = get_keyword(channel, &where, &how, flags)) anyattr = 1; if (how & K_BEEP) dobeep = 1; if (how & K_SPACE && *flags & LOG_PUBLIC) space_add(nick, ""); } } wp++; attr++; } } else { while (*wp != '\0') { wp++; *attr++ = video; } if (video) anyattr = 1; } if (vplus) { video ^= vplus; vplus = 0; } } else break; } *wp = '\0'; if (dobeep && !(*flags & LOG_IGNORE)) term_beep(); return anyattr; } /* sic_splitorig: splits n!u@h */ static void sic_splitorig(origin) char *origin; { strcpy(nick, ""); strcpy(user, ""); strcpy(host, ""); if (s && index(s, '!')) { char *ch = s, *ptr; ptr = nick; while (*ch != '!') *ptr++ = *ch++; *ptr = '\0'; ch++; ptr = user; while (*ch != '@') *ptr++ = *ch++; *ptr = '\0'; ch++; ptr = host; while (*ch) *ptr++ = *ch++; *ptr = '\0'; } } void sic_format(sender, cmd, dest, para) char *sender, *cmd, *dest, *para; { int fmt = -1; unsigned int flags = 0; int tocurrent = 1, ctcp = 0, yow = 0; char tochannel[512]; assert(server); locals = toself = froms = fromv = toch = showid = 0; s = sender; if (option(server->sopt, S_DCC)) { char name[11]; strcpy(user, ""); strcpy(host, ""); strcpy(nick, server->nick); flags = LOG_USER|LOG_PRIVATE|LOG_DCC; fmt = F_DCC; p = para; sprintf(name, "=%s", server->nick); tab_add(name, ""); } else { sic_splitorig(s); c = cmd; d = (*dest == ':') ? dest+1 : dest; /* hmmpf */ if (p = index(d, '\007')) /* special case, 2.9 join with ^G */ { assert (*para == '\0'); *p = '\0'; para = p+1; } p = para; n = atoi(cmd); if (n != 1) { /* * simple analysis of origin and destination * It will be used to define the message appearance, * as well as the window where it is to be displayed. */ if (!strcasecmp(s, server->sname)) locals = 1; /* from local server */ if (!strcasecmp(d, server->nick)) { toself = 1; /* to the user */ flags |= LOG_PRIVATE; } else if (toch = map_c2w(d)) /* to a channel? */ { flags |= LOG_PUBLIC; strcpy(tochannel, d); } else flags |= LOG_BROADCAST; if (index(s, '!')) flags |= LOG_USER; else if (!index(s, '@')) { froms = 1; /* from a server */ flags |= LOG_SERVER; } else { fromv = 1; /* from a service */ flags |= LOG_SERVICE; } } /* * ugly huge statement, but everything that has to be parsed is here, * ... */ if (!strcasecmp("NOTICE", cmd)) { time_t ts; flags |= LOG_NOTICE; fmt = fromv ? F_VNOTICE : froms ? F_SNOTICE : toch ? F_CNOTICE : F_UNOTICE; if (fromv) tab_add(sender, NULL); tocurrent = 0; if (!fromv && !froms && sscanf(p, "\001PING %lu\001", &ts) == 1) { flags |= LOG_HIDE; vsic_slog(LOG_CLIENT, "--- CTCP ping reply from %s: %s", nick, sic_tdiff(time(NULL)-ts, 0)); } } else if (!strcasecmp("PRIVMSG", cmd)) { flags |= LOG_MSG; fmt = froms ? F_SMSG : toch ? F_CMSG : F_UMSG; tocurrent = 0; if (strncmp("\001DCC ", p, 5) && index(p, '\001')) { ctcp = 1; if (strncmp("\001ACTION ", p, 8) && !get_option(Z_VCTCP, (toch) ? tochannel : NULL)) flags |= LOG_HIDE; } if (get_option(Z_URL, (toch) ? tochannel : NULL)) url_find(sender, (toch) ? tochannel : NULL, p); if (toch && !froms && !fromv && !get_option(Z_NOYOW, tochannel)) yow = 1; if (!froms && !toch) if (strncmp("\001DCC ", p, 5)) { char uh[256]; sprintf(uh, "%s@%s", user, host); if (strncmp("\001ACTION ", p, 8) && get_option(Z_VCTCP, (toch) ? tochannel : NULL)) showid = tab_add(nick, uh); } else { char *file, *ip, *size; signed char type = -1; unsigned long ipaddr, sz; unsigned int portnb; if (!strncmp("CHAT ", p+5, 5)) type = 0; else if (!strncmp("SEND ", p+5, 5)) type = 1; else vsic_slog(LOG_CLIENT, "--- Unknown DCC request from %s:", nick); if (type >= 0) if (file = index(p+5, ' ')) if (ip = index(file+1, ' ')) if (size = index(ip+1, ' ')) { *ip = '\0'; flags |= LOG_HIDE; sscanf(ip+1, "%lu %u", &ipaddr, &portnb); if (size = index(size+1, ' ')) sscanf(size, "%lu", &sz); sic_dcc(type, nick, ipaddr, portnb, file+1, sz); *ip = ' '; } } } else { flags |= LOG_CRAP; if (n) { char *ch1, *ch2; fmt = F_NUM; switch (n) { case 1: if (server->sname) free(server->sname); server->sname = strdup(s); if (server->nick) free(server->nick); server->nick = strdup(d); if (!strcasecmp(s, server->sname)) locals = 1; if (!strcasecmp(d, server->nick)) toself = 1; unset_option(server->sopt, S_MOTD); break; case 2: ch1 = strstr(p, "version"); if (ch1 == NULL) { sic_slog(LOG_CLIENT, "--- Unable to determine protocol type."); server->protocol = P_IRC; } else { unsigned char proto; ch1 += 8; if (*ch1 == 'u') { proto = P_U; ch2 = "Undernet"; } else if (!strncmp(ch1, "dal", 3)) { proto = P_D; ch2 = "Dalnet"; } else if (strstr(ch1, "+CSr") || strstr(ch1, "/hybrid")) { proto = P_E; ch2 = "EFnet"; } else { proto = P_IRC; ch2 = "IRC"; } if (server->protocol == 0) vsic_slog(LOG_CLIENT, "--- Protocol: %s", ch2); else if (proto != server->protocol) vsic_slog(LOG_CLIENT,"--- Protocol changed: %s",ch2); server->protocol = proto; } break; case 221: /* umodes */ flags |= LOG_HIDE; /* see cheap trick comment below */ strcpy(server->umode, p); break; case 324: /* channel mode */ break; case 353: /* names */ ch2 = p + 2; /* channel name */ *(ch1 = index(ch2, ' ')) = '\0'; strcpy(tochannel, ch2); *ch1 = ' '; ch1 +=2; if (map_c2w(tochannel)) { toch = 1; if (*ch1 == '@') set_chop(tochannel, 2); /* inexact, caught by set_chop() */ while (ch1 && *ch1) { while (*ch1 == '@' || *ch1 == '+') ch1++; if (ch2 = index(ch1, ' ')) *ch2 = '\0'; add_member(tochannel, ch1, 1); if (ch2) { *ch2 = ' '; ch1 = ch2 + 1; } else ch1 = NULL; } } break; case 366: /* end of names */ ch1 = index(p, ' '); *ch1 = '\0'; strcpy(tochannel, p); *ch1 = ' '; if (map_c2w(tochannel)) { toch = 1; add_member(tochannel, NULL, 0); } break; case 421: /* Unknown command */ if (!strncmp("LAME:", p, 5)) { ch1 = index(p, ' '); *ch1 = '\0'; join_channel(p+5, 1); flags |= LOG_IGNORE; *ch1 = ' '; } break; case 422: /* MOTD file is missing */ case 376: /* end of MOTD */ if (!strcasecmp(s, server->sname) && !option(server->sopt, S_MOTD)) { if (need_collect) collect_channel(); rejoin_channel(NULL); set_option(server->sopt, S_MOTD); } break; case 381: /* you're now such a lamer (ircop) */ vsic_write("MODE %s", server->nick); break; case 403: /* no such channel */ case 442: /* not on this channel */ ch1 = index(p, ' '); *ch1 = '\0'; if (map_c2w(p)) del_channel(p); *ch1 = ' '; break; case 437: /* temporarily unavailable */ case 471: /* cannot join +l */ case 473: /* cannot join +i */ case 474: /* cannot join +b */ case 475: /* cannot join +k */ ch1 = index(p, ' '); *ch1 = '\0'; if (map_c2w(p)) { if (n == 475) { struct channel_ *ctmp; ctmp = get_channel(p); if (ctmp->chkey) { vsic_slog(LOG_CLIENT, "--- Forgetting key \"%s\" for channel %s.", ctmp->chkey, p); free(ctmp->chkey); ctmp->chkey = NULL; } } if (get_option(Z_REJOIN, p)) rejoin_channel(p); else { del_channel(p); collect_channel(); } } *ch1 = ' '; break; } } else if (!strcasecmp("KILL", cmd)) fmt = F_KILL; else if (!strcasecmp("ERROR", cmd)) { fmt = F_ERROR; del_channel(NULL); } else if (!strcasecmp("INVITE", cmd)) { struct channel_ *ctmp; fmt = F_INVITE; if ((ctmp = get_channel(p)) && option(ctmp->copt, C_REJOIN)) { vsic_slog(LOG_CLIENT, "--- Answering invitation to %s.", p); join_channel(p, 0); } } else if (!strcasecmp("JOIN", cmd)) { fmt = F_JOIN; assert(toch || *d == '!'); if (!toch) { chg_channel(d); strcpy(tochannel, d); flags = (flags ^ LOG_BROADCAST) | LOG_PUBLIC; toch = 1; } if (!strcasecmp(nick, server->nick)) del_member(d, NULL); else add_member(d, nick, 0); } else if (!strcasecmp("PART", cmd)) { fmt = F_PART; del_member(d, nick); if (!strcasecmp(nick, server->nick)) del_channel(d); } else if (!strcasecmp("KICK", cmd)) { char *comment = index(p, ' '); assert(comment); fmt = F_KICK; *comment = '\0'; del_member(d, p); if (!strcasecmp(p, server->nick)) { del_channel(d); if (get_option(Z_REJOIN, d)) rejoin_channel(d); } *comment = ' '; } else if (!strcasecmp("TOPIC", cmd)) fmt = F_TOPIC; else if (!strcasecmp("QUIT", cmd)) { int split = 0; char *wp; fmt = F_QUIT; flags |= LOG_MULTICAST; del_member(NULL, nick); toch = 0; d = ""; p = dest; wp = p; while (*wp && *wp != ' ') if (*wp++ == '.') split = 1; if (*wp++ && split) { split = 0; while (*wp && *wp != ' ') if (*wp++ == '.') split = 1; if (!split || *wp) flags |= LOG_UQUIT; else flags |= LOG_SQUIT; } else flags |= LOG_UQUIT; } else if (!strcasecmp("NICK", cmd)) { chg_member(nick, d); if (!strcmp(nick, server->nick)) { free(server->nick); server->nick = strdup(d); } else flags |= LOG_MULTICAST; fmt = F_NICK; } else if (!strcasecmp("MODE", cmd)) if (!strcmp(sender, server->nick)) { strcpy(nick, sender); /* eww */ fmt = F_UMODE; if (!index(p, '-') && server->umode[0] == '\0') strcpy(server->umode, p); else vsic_write("MODE %s", server->nick); /* cheap trick */ } else if (froms) fmt = F_SMODE; else fmt = F_CMODE; else fmt = F_UNKNOWN; } } select_active((toch) ? tochannel : NULL, tocurrent); if (toch == 0 && (flags & (LOG_MSG|LOG_NOTICE|LOG_DCC))) if (fromv || froms) select_query(sender); else { char tmpq[11]; sprintf(tmpq, "%s%s", (flags & LOG_DCC) ? "=" : "", nick); select_query(tmpq); } if (fmt != -1) { char *fmt_str; int rendering; if (fmt == F_NUM) fmt_str = get_nformat(n, (toch) ? tochannel : NULL); else fmt_str = get_format(fmt, (toch) ? tochannel : NULL); if (*fmt_str == '\0') { flags |= LOG_HIDE; fmt_str++; } rendering = sic_sprintf(outp, outa, fmt_str, (toch) ? tochannel : NULL, &flags); #if defined(HAVE_REGEXP) rendering += get_rewrite(fmt, outp, outa, &flags, (toch) ? tochannel : NULL); #endif sic_log(flags, 0, s, c, (toself) ? NULL : d, p, fmt, outp, (rendering) ? outa : NULL); } else vsic_slog(LOG_CLIENT, "Wooops: %s", cmd); if (ctcp) sic_ctcp(nick, toself, p); if (yow) yow_back(tochannel, p); } void sic_reformat(log) struct log_ *log; { char *fmt_str; /* locals is unused, toself and toch hace no use here, showid isn't reusable :/ */ locals = toself = froms = fromv = toch = showid = 0; n = 0; if (log->fmt == -1 || log->fmt == F_SPLIT) return; s = log->origin; c = log->cmd; d = log->dest; p = log->para; sic_splitorig(log->origin); if (log->flags & LOG_DCC && log->origin) { char *at; strncpy(nick, log->origin, 10); at = index(nick, '@'); if (at) *at = '\0'; else nick[9] = '\0'; } if (log->flags & LOG_SERVICE) fromv = 1; if (log->flags & LOG_SERVER) froms = 1; log->flags &= ~(LOG_HIGHLIGHT|LOG_KEYWORD | LOG_HIDE|LOG_IGNORE); if (log->fmt == F_NUM) fmt_str = get_nformat(n = atoi(log->cmd), log->dest); else fmt_str = get_format(log->fmt, log->dest); if (*fmt_str == '\0') { fmt_str++; log->flags |= LOG_HIDE; } free(log->prefix); if (log->prefix = get_format(F_PREFIX, log->dest)) { char pbuf[80]; /* should be more than enough */ my_cftime(pbuf, 80, log->prefix, log->ts); log->prefix = strdup(pbuf); } log->plen = strlen(log->prefix); free(log->formatted); free(log->attributes); log->attributes = NULL; if (sic_sprintf(outp, outa, fmt_str, log->dest, &(log->flags))) { log->attributes = (u_char *) malloc(log->len+1); bcopy(outa, log->attributes, log->len+1); } log->formatted = strdup(outp); log->len = strlen(log->formatted); }