#include #include #include #include #include #include #include #include #include "proto.h" #include "chars.h" #include "client.h" #define MAX_CHANNEL_NAME 50 // from 2812 1.3 #define SPECIALS '[': case ']': case '\\': case '`': case '_': \ case '^': case '{'/*}*/: case '|': case /*{*/'}' typedef struct pline PLINE; typedef struct serv_priv_irc SERV_PRIV_IRC; typedef struct listchan LISTCHAN; typedef struct ohl_priv_irc OHL_PRIV_IRC; typedef struct delnick DELNICK; struct ohl_priv_irc { DELNICK *nick; } ; struct delnick { DELNICK *flink; DELNICK *blink; CSTR nick; char *nfree; unsigned int refs; unsigned int flags; #define DNF_DELETE 0x00000001 } ; struct listchan { LISTCHAN *link; WSTR chan; WSTR users; WSTR name; } ; struct serv_priv_irc { ES modes_set; ES modes_clr; ES il; LISTCHAN *lc; LISTCHAN **lctail; } ; #define PRIV(s) ((SERV_PRIV_IRC *)(s)->priv) struct pline { CSTR prefix; CSTR command; int npar; CSTR params[15]; } ; static DELNICK *delnicks = 0; static int n_deleted = 0; static int last_n_deleted = 0; static int acked_n_deleted = 0; static struct timeval last_del_change = { .tv_sec = 0, .tv_usec = 0 }; #define Cisspace(x) isspace((unsigned char)(x)) static char irclc(char c) { switch (c) { case 'A': return('a'); break; case 'B': return('b'); break; case 'C': return('c'); break; case 'D': return('d'); break; case 'E': return('e'); break; case 'F': return('f'); break; case 'G': return('g'); break; case 'H': return('h'); break; case 'I': return('i'); break; case 'J': return('j'); break; case 'K': return('k'); break; case 'L': return('l'); break; case 'M': return('m'); break; case 'N': return('n'); break; case 'O': return('o'); break; case 'P': return('p'); break; case 'Q': return('q'); break; case 'R': return('r'); break; case 'S': return('s'); break; case 'T': return('t'); break; case 'U': return('u'); break; case 'V': return('v'); break; case 'W': return('w'); break; case 'X': return('x'); break; case 'Y': return('y'); break; case 'Z': return('z'); break; case '[': return('{'/*}*/); break; case ']': return(/*{*/'}'); break; case '\\': return('|'); break; case '~': return('^'); break; } return(c); } static int nickmatch(CSTR n1, CSTR n2) { int i; if (n1.len != n2.len) return(0); for (i=n1.len-1;i>=0;i--) { if (irclc(n1.text[i]) != irclc(n2.text[i])) return(0); } return(1); } // 2812 1.3 static int ok_channel(const char *cn, int len) { int i; switch (*cn) { case '&': case '#': case '+': case '!': break; default: return(0); break; } if (len > MAX_CHANNEL_NAME) return(0); for (i=len-1;i>0;i--) { switch (cn[i]) { case ' ': case 7: case ',': return(0); break; } } return(1); } static int pfx_is_channel(char c) { switch (c) { case '&': case '#': case '+': case '!': return(1); break; } return(0); } static void send_line(SERVER *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void send_line(SERVER *s, const char *fmt, ...) { va_list ap; char *t; int l; va_start(ap,fmt); l = vasprintf(&t,fmt,ap); va_end(ap); if (debug) hlprintf_srv(s,">>> %.*s",l,t); aio_oq_queue_free(&s->oq,t,l); aio_oq_queue_point(&s->oq,"\r\n",2); } static int crack_prefix(CSTR pfx, CSTR *fw, CSTR *nw) { const char *bang; const char *at; if (pfx.len < 1) return(0); *fw = pfx; nw->text = pfx.text; bang = memchr(pfx.text,'!',pfx.len); at = memchr(pfx.text,'@',pfx.len); nw->len = bang ? at ? ((bang < at) ? bang : at) - pfx.text : bang - pfx.text : at ? at - pfx.text : pfx.len; return(1); } static void newmodes(ES *mset, ES *mclr, const char *t, int l) { ES *to; ES *from; int i; char *fb; int fl; char *at; to = 0; for (i=0;ilc)) { p->lc = lc->link; free(lc->chan.text); free(lc->users.text); free(lc->name.text); free(lc); } p->lctail = &p->lc; } static int server_op_ping(SERVER *s, const PLINE *pl) { if (! pl->prefix.text) { if (pl->npar == 1) { send_line(s,"PONG :%.*s",pl->params[0].len,pl->params[0].text); return(1); } } return(0); } static int server_op_pong(SERVER *s, const PLINE *pl __attribute__((__unused__))) { s->lastpong = ts_now(); //hlprintf_int("Got PONG from %s",s->stext); return(1); } static int server_op_invite(SERVER *s, const PLINE *pl) { if ((pl->npar == 2) && nickmatch(pl->params[0],wstr_as_cstr(s->nick))) { hlprintf_srv(s,"You have been invited to %.*s by %.*s", pl->params[1].len, pl->params[1].text, pl->prefix.len, pl->prefix.text); return(1); } return(0); } static int server_op__text_(SERVER *s, const PLINE *pl, int po) { int np; char *t; int l; int i; int o; np = pl->npar - po; if (np < 1) return(0); if (np == 1) { ohl_append_server(s,pl->prefix.text,pl->prefix.len,pl->params[po].text,pl->params[po].len); return(1); } l = np - 1; for (i=pl->npar-1;i>=po;i--) l += pl->params[i].len; t = malloc(l); o = 0; for (i=po;inpar;i++) { if (i > po) t[o++] = ' '; bcopy(pl->params[i].text,t+o,pl->params[i].len); o += pl->params[i].len; } if (o != l) abort(); ohl_append_server(s,pl->prefix.text,pl->prefix.len,t,l); free(t); return(1); } static int server_op__text(SERVER *s, const PLINE *pl) { return(server_op__text_(s,pl,0)); } static int server_op__textskip1(SERVER *s, const PLINE *pl) { return(server_op__text_(s,pl,1)); } static int server_op__textskip2(SERVER *s, const PLINE *pl) { return(server_op__text_(s,pl,2)); } static int server_op__textskip3(SERVER *s, const PLINE *pl) { return(server_op__text_(s,pl,3)); } static int server_op_004(SERVER *s, const PLINE *pl) { int i; if (pl->npar >= 2) { free(s->servname.text); s->servname = wstr_copy_cstr(pl->params[1]); hlprintf_srv(s,"Server name is %.*s",s->servname.len,s->servname.text); rerender_hist(); send_line(s,"MODE %.*s -i",s->nick.len,s->nick.text); for (i=0;inchan;i++) send_line(s,"JOIN %s",s->chans[i]); } return(server_op__textskip1(s,pl)); } static int server_op_mode(SERVER *s, const PLINE *pl) { int i; CSTR fw; CSTR nw; if (pl->npar < 1) return(0); crack_prefix(pl->prefix,&fw,&nw); if (nickmatch(pl->params[0],wstr_as_cstr(s->nick))) { for (i=1;inpar;i++) newmodes(&PRIV(s)->modes_set,&PRIV(s)->modes_clr,pl->params[i].text,pl->params[i].len); if (nickmatch(nw,wstr_as_cstr(s->nick))) { hlprintf_srv(s,"Now MODE +%.*s -%.*s", es_len(&PRIV(s)->modes_set), es_buf(&PRIV(s)->modes_set), es_len(&PRIV(s)->modes_clr), es_buf(&PRIV(s)->modes_clr) ); } else { hlprintf_srv(s,"%.*s set you MODE +%.*s -%.*s", fw.len, fw.text, es_len(&PRIV(s)->modes_set), es_buf(&PRIV(s)->modes_set), es_len(&PRIV(s)->modes_clr), es_buf(&PRIV(s)->modes_clr) ); } } else if (ok_channel(pl->params[0].text,pl->params[0].len)) { FILE *f; f = hline_open(s); fprintf(f,"%.*s set MODE",nw.len,nw.text); for (i=0;inpar;i++) fprintf(f," %.*s",pl->params[i].len,pl->params[i].text); fclose(f); } else { ES set; ES clr; es_init(&set); es_init(&clr); for (i=1;inpar;i++) newmodes(&set,&clr,pl->params[i].text,pl->params[i].len); if (nickmatch(nw,pl->params[0])) { hlprintf_srv(s,"%.*s set MODE +%.*s -%.*s",nw.len,nw.text,es_len(&set),es_buf(&set),es_len(&clr),es_buf(&clr)); } else { hlprintf_srv(s,"%.*s set %.*s MODE +%.*s -%.*s",fw.len,fw.text,pl->params[0].len,pl->params[0].text,es_len(&set),es_buf(&set),es_len(&clr),es_buf(&clr)); } es_done(&set); es_done(&clr); } return(1); } static void delnick_unlink(DELNICK *n) { if (n->flink) n->flink->blink = n->blink; if (n->blink) n->blink->flink = n->flink; else delnicks = n->flink; } static void delnick_link_head(DELNICK *n) { n->flink = delnicks; n->blink = 0; if (delnicks) delnicks->blink = n; delnicks = n; } static DELNICK *get_delnick(CSTR nick) { DELNICK *n; WSTR nw; for (n=delnicks;n;n=n->flink) { if (nickmatch(nick,n->nick)) { if (n != delnicks) { delnick_unlink(n); delnick_link_head(n); } return(n); } } n = malloc(sizeof(DELNICK)); nw = wstr_copy_cstr(nick); n->nick = wstr_as_cstr(nw); n->nfree = nw.text; n->refs = 0; n->flags = 0; delnick_link_head(n); return(n); } static void delnick_deref(DELNICK *n) { if (n->refs == 0) abort(); n->refs --; if (n->refs == 0) { free(n->nfree); free(n); } } static DELNICK *delnick_ref(DELNICK *n) { n->refs ++; if (n->refs == 0) abort(); return(n); } static void drop_ohl_nick(void *pv) { OHL_PRIV_IRC *p; p = pv; delnick_deref(p->nick); free(p); } static int ohline_set_del(OHLINE *l, void *arg __attribute__((__unused__))) { OHL_PRIV_IRC *p; p = ohl_get_proto_priv(l); if (p) ohl_set_deleted(l,(p->nick->flags&DNF_DELETE)?1:0); return(0); } static void set_del_time(void) { gettimeofday(&last_del_change,0); } static void scan_ohlines_del(void) { DELNICK *n; char *s; ohlines_scan(&ohline_set_del,0); n_deleted = 0; for (n=delnicks;n;n=n->flink) if (n->flags & DNF_DELETE) n_deleted ++; if (n_deleted != last_n_deleted) { if (n_deleted != acked_n_deleted) { asprintf(&s," %d ",n_deleted); set_modeline_note(s); free(s); } else { set_modeline_note(0); } last_n_deleted = n_deleted; } } static void set_ohl_nick(OHLINE *l, CSTR nick) { DELNICK *n; OHL_PRIV_IRC *p; n = get_delnick(nick); p = malloc(sizeof(OHL_PRIV_IRC)); p->nick = delnick_ref(n); ohl_set_proto_priv(l,p,&drop_ohl_nick); if (n->flags & DNF_DELETE) ohl_set_deleted(l,1); if (ohl_isset_hidden(l) && !(n->flags & DNF_DELETE)) { n->flags |= DNF_DELETE; scan_ohlines_del(); if (nickmatch(nick,n->nick)) { hlprintf_int("Deleting %.*s (generated hidden line)",nick.len,nick.text); } else { hlprintf_int("**** Deleting %.*s/%.*s (generated hidden line)",n->nick.len,n->nick.text,nick.len,nick.text); } set_del_time(); } } static int server_op_join(SERVER *s, const PLINE *pl) { int i; CSTR fw; CSTR nw; OHLINE *l; crack_prefix(pl->prefix,&fw,&nw); if (nickmatch(nw,wstr_as_cstr(s->nick))) { for (i=0;inpar;i++) { hlprintf_srv(s,"Joined %.*s",pl->params[i].len,pl->params[i].text); record_joined(s,pl->params[i].text,pl->params[i].len); } } else { for (i=0;inpar;i++) { l = hlprintf_srv(s,"%.*s has joined %.*s",nw.len,nw.text,pl->params[i].len,pl->params[i].text); if (l) set_ohl_nick(l,nw); } } return(1); } static int server_op_nick(SERVER *s, const PLINE *pl) { CSTR fw; CSTR nw; if (pl->npar != 1) return(0); crack_prefix(pl->prefix,&fw,&nw); if (nickmatch(nw,wstr_as_cstr(s->nick))) { hlprintf_srv(s,"Your nick is now %.*s",pl->params[0].len,pl->params[0].text); free(s->nick.text); s->nick = wstr_copy_cstr(pl->params[0]); } else { hlprintf_srv(s,"Nick change: %.*s is now %.*s",nw.len,nw.text,pl->params[0].len,pl->params[0].text); } return(1); } static int server_op_part(SERVER *s, const PLINE *pl) { int i; CSTR fw; CSTR nw; OHLINE *l; const char *U; OHL_PRIV_IRC *p; crack_prefix(pl->prefix,&fw,&nw); if (nickmatch(nw,wstr_as_cstr(s->nick))) { for (i=0;inpar;i++) { hlprintf_srv(s,"Left %.*s",pl->params[i].len,pl->params[i].text); record_parted(s,pl->params[i].text,pl->params[i].len); } } else { for (i=0;inpar;i++) { l = hlprintf_srv(s,"%.*s has left %.*s",nw.len,nw.text,pl->params[i].len,pl->params[i].text); if (l) { set_ohl_nick(l,nw); U = memchr(pl->params[i].text,'U',pl->params[i].len); if (U && ((U-pl->params[i].text)+21 < pl->params[i].len) && !bcmp(U,"User has been banned ",21)) { p = ohl_get_proto_priv(l); if (! (p->nick->flags & DNF_DELETE)) { p->nick->flags |= DNF_DELETE; scan_ohlines_del(); if (nickmatch(nw,p->nick->nick)) { hlprintf_int("Deleting %.*s (has-been-banned part)",nw.len,nw.text); } else { hlprintf_int("**** Deleting %.*s/%.*s (has-been-banned part)",p->nick->nick.len,p->nick->nick.text,nw.len,nw.text); } set_del_time(); } } } } } return(1); } static int server_op_privmsg(SERVER *s, const PLINE *pl) { CSTR fw; CSTR nw; CSTR b; unsigned int f; int i; OHLINE *l; if (pl->npar != 2) return(0); if (! crack_prefix(pl->prefix,&fw,&nw)) return(0); b = pl->params[1]; if ((b.len > 8) && (b.text[b.len-1] == 1) && !bcmp(b.text,"\1ACTION",7)) { f = OHLF_ACTION; for (i=7;(i= b.len) { b.len = 0; } else { b.len -= i + 1; b.text += i; } } else { f = 0; } if (nickmatch(pl->params[0],wstr_as_cstr(s->nick))) { l = ohl_append_private(s,fw.text,fw.len,nw.text,nw.len,b.text,b.len,f); } else if ((pl->params[0].len > 0) && pfx_is_channel(pl->params[0].text[0])) { l = ohl_append_channel(s,fw.text,fw.len,nw.text,nw.len,pl->params[0].text,pl->params[0].len,b.text,b.len,f); } else { return(0); } if (l) set_ohl_nick(l,nw); return(1); } static int server_op_quit(SERVER *s, const PLINE *pl) { CSTR fw; CSTR nw; if (pl->npar != 1) return(0); if (! crack_prefix(pl->prefix,&fw,&nw)) return(0); hlprintf_srv(s,"%.*s has quit: %.*s",nw.len,nw.text,pl->params[0].len,pl->params[0].text); return(1); } static int server_op_topic(SERVER *s, const PLINE *pl) { if (pl->npar != 2) return(0); hlprintf_srv(s,"Topic for %.*s: %.*s",pl->params[0].len,pl->params[0].text,pl->params[1].len,pl->params[1].text); return(1); } static int server_op_221(SERVER *s, const PLINE *pl) { int i; ES set; ES clr; if (pl->npar < 1) return(0); es_init(&set); es_init(&clr); for (i=1;inpar;i++) { newmodes(&set,&clr,pl->params[i].text,pl->params[i].len); newmodes(&PRIV(s)->modes_set,&PRIV(s)->modes_clr,pl->params[i].text,pl->params[i].len); } hlprintf_srv(s,"You are MODE +%.*s -%.*s (+%.*s -%.*s)", es_len(&set), es_buf(&set), es_len(&clr), es_buf(&clr), es_len(&PRIV(s)->modes_set), es_buf(&PRIV(s)->modes_set), es_len(&PRIV(s)->modes_clr), es_buf(&PRIV(s)->modes_clr) ); es_done(&set); es_done(&clr); return(1); } static int server_op_301(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"%.*s is away: %.*s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text); return(1); } static int server_op_302(SERVER *s, const PLINE *pl) { const char *equal; if (pl->npar != 2) return(0); equal = memchr(pl->params[1].text,'=',pl->params[1].len); if (! equal) { if (pl->params[1].len == 0) { hlprintf_srv(s,"Not found."); } else { hlprintf_srv(s,"Unexpected response: %.*s",pl->params[1].len,pl->params[1].text); } } else { hlprintf_srv(s,"%.*s is %.*s",(int)(equal-pl->params[1].text),pl->params[1].text,(int)((pl->params[1].text+pl->params[1].len)-(equal+1)),equal+1); } return(1); } static int server_op_305(SERVER *s, const PLINE *pl) { if (pl->npar != 2) return(0); hlprintf_srv(s,"[No longer away: %.*s]",pl->params[1].len,pl->params[1].text); return(1); } static int server_op_306(SERVER *s, const PLINE *pl) { if (pl->npar != 2) return(0); hlprintf_srv(s,"[Now away: %.*s]",pl->params[1].len,pl->params[1].text); return(1); } static int server_op_307(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"%.*s: %.*s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text); return(1); } static int server_op_311(SERVER *s, const PLINE *pl) { if ((pl->npar != 6) || (pl->params[4].len != 1) || (pl->params[4].text[0] != '*')) return(0); hlprintf_srv(s,"%.*s is %.*s@%.*s (%.*s)", pl->params[1].len, pl->params[1].text, pl->params[2].len, pl->params[2].text, pl->params[3].len, pl->params[3].text, pl->params[5].len, pl->params[5].text); return(1); } static int server_op_312(SERVER *s, const PLINE *pl) { if (pl->npar != 4) return(0); hlprintf_srv(s,"%.*s is connected to %.*s (%.*s)", pl->params[1].len, pl->params[1].text, pl->params[2].len, pl->params[2].text, pl->params[3].len, pl->params[3].text); return(1); } static int server_op_315(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"End of /WHO list for %.*s",pl->params[1].len,pl->params[1].text); return(1); } static int server_op_317(SERVER *s, const PLINE *pl) { FILE *f; switch (pl->npar) { case 4: f = hline_open(s); fprintf(f,"%.*s is idle ",pl->params[1].len,pl->params[1].text); print_timeunits(f,atoi(pl->params[2].text)); fclose(f); break; case 5: f = hline_open(s); fprintf(f,"%.*s is idle ",pl->params[1].len,pl->params[1].text); print_timeunits(f,atoi(pl->params[2].text)); fprintf(f,", on since "); print_unixtime(f,strtol(pl->params[3].text,0,0)); fclose(f); break; } return(1); } static int server_op_318(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"End of /WHOIS list for %.*s",pl->params[1].len,pl->params[1].text); return(1); } static int server_op_319(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"%.*s is on %.*s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text); return(1); } static int server_op_321(SERVER *s, const PLINE *pl) { SERV_PRIV_IRC *p; if (pl->npar != 3) return(0); p = PRIV(s); clear_listchan(p); return(1); } static int server_op_322(SERVER *s, const PLINE *pl) { SERV_PRIV_IRC *p; LISTCHAN *lc; if (pl->npar != 4) return(0); p = PRIV(s); lc = malloc(sizeof(LISTCHAN)); lc->chan = wstr_copy_cstr(pl->params[1]); lc->users = wstr_copy_cstr(pl->params[2]); lc->name = wstr_copy_cstr(pl->params[3]); lc->link = 0; *p->lctail = lc; p->lctail = &lc->link; return(1); } static int server_op_323(SERVER *s, const PLINE *pl) { SERV_PRIV_IRC *p; LISTCHAN *lc; int maxcl; int maxul; if (pl->npar != 2) return(0); p = PRIV(s); maxcl = 7; // strlen("Channel") maxul = 5; // strlen("Users") for (lc=PRIV(s)->lc;lc;lc=lc->link) { if (lc->chan.len > maxcl) maxcl = lc->chan.len; if (lc->users.len > maxul) maxul = lc->users.len; } hlprintf_srv(s,"%-*s %*s %s",maxcl,"Channel",maxul,"Users","Topic"); for (lc=PRIV(s)->lc;lc;lc=lc->link) { hlprintf_srv(s,"%-*.*s %*.*s %.*s",maxcl,lc->chan.len,lc->chan.text,maxul,lc->users.len,lc->users.text,lc->name.len,lc->name.text); } hlprintf_srv(s,"End of /LIST output"); clear_listchan(p); return(1); } static int server_op_331(SERVER *s, const PLINE *pl) { if (pl->npar < 2) return(0); hlprintf_srv(s,"No topic set for %.*s",pl->params[0].len,pl->params[0].text); return(1); } static int server_op_332(SERVER *s, const PLINE *pl) { if (pl->npar < 3) return(0); hlprintf_srv(s,"Topic for %.*s: %.*s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text); return(1); } static int server_op_333(SERVER *s, const PLINE *pl) { char *tbuf; time_t tt; struct tm *tm; char ttxt[64]; if (pl->npar < 4) return(0); tbuf = blk_to_nt(pl->params[3].text,pl->params[3].len); tt = strtol(tbuf,0,0); free(tbuf); tm = localtime(&tt); strftime(&ttxt[0],sizeof(ttxt),"%Y-%m-%d %H:%M:%S",tm); hlprintf_srv(s,"%.*s created by %.*s at %s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text,&ttxt[0]); return(1); } static int server_op_338(SERVER *s, const PLINE *pl) { if (pl->npar != 4) return(0); hlprintf_srv(s,"%.*s is on from %.*s (%.*s)", pl->params[1].len, pl->params[1].text, pl->params[2].len, pl->params[2].text, pl->params[3].len, pl->params[3].text); return(1); } static int server_op_352(SERVER *s, const PLINE *pl) { if (pl->npar != 8) return(0); hlprintf_srv(s,"%.*s on %.*s on %.*s from %.*s@%.*s [<%.*s> %.*s]", pl->params[5].len, pl->params[5].text, pl->params[1].len, pl->params[1].text, pl->params[4].len, pl->params[4].text, pl->params[2].len, pl->params[2].text, pl->params[3].len, pl->params[3].text, pl->params[6].len, pl->params[6].text, pl->params[7].len, pl->params[7].text); return(1); } static int server_op_353(SERVER *s, const PLINE *pl) { int i; const char *kind; FILE *f; if (pl->npar < 3) return(0); if (pl->params[1].len != 1) return(0); switch (pl->params[1].text[0]) { case '@': kind = "secret"; break; case '*': kind = "private"; break; case '=': kind = "public"; break; default: return(0); break; } f = hline_open(s); fprintf(f,"On %s channel %.*s:",kind,pl->params[2].len,pl->params[2].text); for (i=3;inpar;i++) fprintf(f," %.*s",pl->params[i].len,pl->params[i].text); fclose(f); return(1); } static int server_op_366(SERVER *s __attribute__((__unused__)), const PLINE *pl __attribute__((__unused__))) { return(1); } static int server_op_401(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"%.*s: No such nick/channel",pl->params[1].len,pl->params[1].text); return(1); } static int server_op_412(SERVER *s, const PLINE *pl) { if (pl->npar != 2) return(0); hlprintf_srv(s,"No text to send"); return(1); } static int server_op_421(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"%.*s: %.*s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text); return(1); } static int server_op_422(SERVER *s, const PLINE *pl) { if (pl->npar != 2) return(0); hlprintf_srv(s,"No MOTD: %.*s",pl->params[1].len,pl->params[1].text); return(1); } static int server_op_524(SERVER *s, const PLINE *pl) { if (pl->npar != 3) return(0); hlprintf_srv(s,"%.*s: %.*s",pl->params[1].len,pl->params[1].text,pl->params[2].len,pl->params[2].text); return(1); } #define server_op_notice server_op__text #define server_op_error server_op__text #define server_op_001 server_op__textskip1 #define server_op_002 server_op__textskip1 #define server_op_003 server_op__textskip1 #define server_op_005 server_op__textskip1 #define server_op_042 server_op__textskip1 #define server_op_250 server_op__textskip1 #define server_op_251 server_op__textskip1 #define server_op_252 server_op__textskip1 #define server_op_253 server_op__textskip1 #define server_op_254 server_op__textskip1 #define server_op_255 server_op__textskip1 #define server_op_256 server_op__textskip1 #define server_op_257 server_op__textskip1 #define server_op_258 server_op__textskip1 #define server_op_259 server_op__textskip1 #define server_op_265 server_op__textskip1 #define server_op_266 server_op__textskip1 #define server_op_290 server_op__textskip1 #define server_op_292 server_op__textskip1 #define server_op_371 server_op__textskip1 #define server_op_372 server_op__textskip1 #define server_op_374 server_op__textskip1 #define server_op_375 server_op__textskip1 #define server_op_376 server_op__textskip1 #define server_op_378 server_op__textskip1 #define server_op_396 server_op__textskip1 #define server_op_479 server_op__textskip1 #define server_op_481 server_op__textskip1 #define server_op_501 server_op__textskip1 #define server_op_671 server_op__textskip1 #define server_op_704 server_op__textskip2 #define server_op_705 server_op__textskip2 #define server_op_706 server_op__textskip2 #define server_op_900 server_op__textskip3 static int server_handle_op(SERVER *s, const PLINE *pl) { switch (pl->command.len) { case 3: switch (pl->command.text[0]) { case '0': switch (pl->command.text[1]) { case '0': switch (pl->command.text[2]) { case '1': if (server_op_001(s,pl)) return(1); break; case '2': if (server_op_002(s,pl)) return(1); break; case '3': if (server_op_003(s,pl)) return(1); break; case '4': if (server_op_004(s,pl)) return(1); break; case '5': if (server_op_005(s,pl)) return(1); break; } break; case '4': switch (pl->command.text[2]) { case '2': if (server_op_042(s,pl)) return(1); break; } break; } break; case '2': switch (pl->command.text[1]) { case '2': switch (pl->command.text[2]) { case '1': if (server_op_221(s,pl)) return(1); break; } break; case '5': switch (pl->command.text[2]) { case '0': if (server_op_250(s,pl)) return(1); break; case '1': if (server_op_251(s,pl)) return(1); break; case '2': if (server_op_252(s,pl)) return(1); break; case '3': if (server_op_253(s,pl)) return(1); break; case '4': if (server_op_254(s,pl)) return(1); break; case '5': if (server_op_255(s,pl)) return(1); break; case '6': if (server_op_256(s,pl)) return(1); break; case '7': if (server_op_257(s,pl)) return(1); break; case '8': if (server_op_258(s,pl)) return(1); break; case '9': if (server_op_259(s,pl)) return(1); break; } break; case '6': switch (pl->command.text[2]) { case '5': if (server_op_265(s,pl)) return(1); break; case '6': if (server_op_266(s,pl)) return(1); break; } break; case '9': switch (pl->command.text[2]) { case '0': if (server_op_290(s,pl)) return(1); break; case '2': if (server_op_292(s,pl)) return(1); break; } break; } break; case '3': switch (pl->command.text[1]) { case '0': switch (pl->command.text[2]) { case '1': if (server_op_301(s,pl)) return(1); break; case '2': if (server_op_302(s,pl)) return(1); break; case '5': if (server_op_305(s,pl)) return(1); break; case '6': if (server_op_306(s,pl)) return(1); break; case '7': if (server_op_307(s,pl)) return(1); break; } break; case '1': switch (pl->command.text[2]) { case '1': if (server_op_311(s,pl)) return(1); break; case '2': if (server_op_312(s,pl)) return(1); break; case '5': if (server_op_315(s,pl)) return(1); break; case '7': if (server_op_317(s,pl)) return(1); break; case '8': if (server_op_318(s,pl)) return(1); break; case '9': if (server_op_319(s,pl)) return(1); break; } break; case '2': switch (pl->command.text[2]) { case '1': if (server_op_321(s,pl)) return(1); break; case '2': if (server_op_322(s,pl)) return(1); break; case '3': if (server_op_323(s,pl)) return(1); break; } break; case '3': switch (pl->command.text[2]) { case '1': if (server_op_331(s,pl)) return(1); break; case '2': if (server_op_332(s,pl)) return(1); break; case '3': if (server_op_333(s,pl)) return(1); break; case '8': if (server_op_338(s,pl)) return(1); break; } break; case '5': switch (pl->command.text[2]) { case '2': if (server_op_352(s,pl)) return(1); break; case '3': if (server_op_353(s,pl)) return(1); break; } break; case '6': switch (pl->command.text[2]) { case '6': if (server_op_366(s,pl)) return(1); break; } break; case '7': switch (pl->command.text[2]) { case '1': if (server_op_371(s,pl)) return(1); break; case '2': if (server_op_372(s,pl)) return(1); break; case '4': if (server_op_374(s,pl)) return(1); break; case '5': if (server_op_375(s,pl)) return(1); break; case '6': if (server_op_376(s,pl)) return(1); break; case '8': if (server_op_378(s,pl)) return(1); break; } break; case '9': switch (pl->command.text[2]) { case '6': if (server_op_396(s,pl)) return(1); break; } break; } break; case '4': switch (pl->command.text[1]) { case '0': switch (pl->command.text[2]) { case '1': if (server_op_401(s,pl)) return(1); break; } break; case '1': switch (pl->command.text[2]) { case '2': if (server_op_412(s,pl)) return(1); break; } break; case '2': switch (pl->command.text[2]) { case '1': if (server_op_421(s,pl)) return(1); break; case '2': if (server_op_422(s,pl)) return(1); break; } break; case '7': switch (pl->command.text[2]) { case '9': if (server_op_479(s,pl)) return(1); break; } break; case '8': switch (pl->command.text[2]) { case '1': if (server_op_481(s,pl)) return(1); break; } break; } break; case '5': switch (pl->command.text[1]) { case '0': switch (pl->command.text[2]) { case '1': if (server_op_501(s,pl)) return(1); break; } break; case '2': switch (pl->command.text[2]) { case '4': if (server_op_524(s,pl)) return(1); break; } break; } break; case '6': switch (pl->command.text[1]) { case '7': switch (pl->command.text[2]) { case '1': if (server_op_671(s,pl)) return(1); break; } break; } break; case '7': switch (pl->command.text[1]) { case '0': switch (pl->command.text[2]) { case '4': if (server_op_704(s,pl)) return(1); break; case '5': if (server_op_705(s,pl)) return(1); break; case '6': if (server_op_706(s,pl)) return(1); break; } break; } break; case '9': switch (pl->command.text[1]) { case '0': switch (pl->command.text[2]) { case '0': if (server_op_900(s,pl)) return(1); break; } break; } break; } break; case 4: switch (pl->command.text[0]) { case 'J': if (! bcmp(pl->command.text,"JOIN",4)) if (server_op_join(s,pl)) return(1); break; case 'M': if (! bcmp(pl->command.text,"MODE",4)) if (server_op_mode(s,pl)) return(1); break; case 'N': if (! bcmp(pl->command.text,"NICK",4)) if (server_op_nick(s,pl)) return(1); break; case 'P': switch (pl->command.text[1]) { case 'A': if (! bcmp(pl->command.text,"PART",4)) if (server_op_part(s,pl)) return(1); break; case 'I': if (! bcmp(pl->command.text,"PING",4)) if (server_op_ping(s,pl)) return(1); break; case 'O': if (! bcmp(pl->command.text,"PONG",4)) if (server_op_pong(s,pl)) return(1); break; } break; case 'Q': if (! bcmp(pl->command.text,"QUIT",4)) if (server_op_quit(s,pl)) return(1); break; } break; case 5: switch (pl->command.text[0]) { case 'E': if (! bcmp(pl->command.text,"ERROR",5)) { if (server_op_error(s,pl)) return(1); } break; case 'T': if (! bcmp(pl->command.text,"TOPIC",5)) { if (server_op_topic(s,pl)) return(1); } break; } break; case 6: switch (pl->command.text[0]) { case 'I': break; if (! bcmp(pl->command.text,"INVITE",6)) { if (server_op_invite(s,pl)) return(1); } case 'N': if (! bcmp(pl->command.text,"NOTICE",6)) { if (server_op_notice(s,pl)) return(1); } break; } break; case 7: if (! bcmp(pl->command.text,"PRIVMSG",7)) { if (server_op_privmsg(s,pl)) return(1); } break; } return(0); } static void server_op(SERVER *s, const PLINE *pl) { int i; FILE *f; void print_s(FILE *f, const CSTR *s) { if (s->text) { fprintf(f,"»»%.*s««",s->len,s->text); } else { fprintf(f,"nil"); } } if (server_handle_op(s,pl)) { if (!debug && !debugn) return; if (debugn > 0) debugn --; hlprintf_srv(s,"Server output:"); } else { hlprintf_srv(s,"Unhandled server output:"); } f = hline_open(s); fprintf(f,"prefix = "); print_s(f,&pl->prefix); fclose(f); f = hline_open(s); fprintf(f,"command = "); print_s(f,&pl->command); fclose(f); hlprintf_srv(s,"npar = %d:",pl->npar); for (i=0;inpar;i++) { f = hline_open(s); fprintf(f," [%d] = ",i); print_s(f,&pl->params[i]); fclose(f); } } static void crack_proto_line(const char *body, int blen, PLINE *pl) { CSTR *p; int all; char *sp; if (blen < 1) { pl->prefix = NO_CSTR; pl->command = NO_CSTR; pl->npar = 0; return; } if (body[0] == ':') { pl->prefix.text = body + 1; sp = memchr(body+1,' ',blen-1); if (sp) { pl->prefix.len = sp - (body + 1); blen -= (sp + 1) - body; body = sp + 1; } else { pl->prefix.len = blen - 1; body += blen; blen = 0; } } else { pl->prefix = NO_CSTR; } if (blen > 0) { pl->command.text = body; sp = memchr(body,' ',blen); if (sp) { pl->command.len = sp - body; blen -= (sp + 1) - body; body = sp + 1; } else { pl->command.len = blen; body += blen; blen = 0; } } else { pl->command = NO_CSTR; } pl->npar = 0; while (blen > 0) { p = &pl->params[pl->npar++]; if ((blen > 0) && (*body == ':')) { all = 1; body ++; blen --; } else { all = (pl->npar >= 15); } p->text = body; if (all) { p->len = blen; break; } else { sp = memchr(body,' ',blen); if (sp) { p->len = sp - body; blen -= (sp + 1) - body; body = sp + 1; } else { p->len = blen; break; } } } } static void server_line(SERVER *s, const char *b, int l) { PLINE pl; if ((l > 0) && (b[l-1] == '\r')) l --; crack_proto_line(b,l,&pl); server_op(s,&pl); } static void slash_del(SERVER *s __attribute__((__unused__)), const char *cmd, int len) { int i; int a0; int onoff; int al; FILE *f; DELNICK *n; for (i=0;(i= len) { f = 0; for (n=delnicks;n;n=n->flink) { if (n->flags & DNF_DELETE) { if (! f) { f = hline_open(0); fprintf(f,"Currently under /del:"); } fprintf(f," %.*s",n->nick.len,n->nick.text); } } if (f) { fclose(f); } else { hlprintf_int("No nicks currently under /del"); } return; } if ((len == i+3) && !bcmp(cmd+i,"ack",3)) { struct timeval now; gettimeofday(&now,0); if (now.tv_sec < last_del_change.tv_sec+10) { hlprintf_int("/del ack: too-recent automated change "); return; } acked_n_deleted = n_deleted; set_modeline_note(0); return; } switch (cmd[i]) { case '+': onoff = 1; break; case '-': onoff = 0; break; default: hlprintf_int("/del: use +nick or -nick, or ack"); return; break; } for (i++;(i= len) { hlprintf_int("/del: missing nick after %c",onoff?'+':'-'); return; } a0 = i; for (i++;(iflink) { if (nickmatch(n->nick,(CSTR){.len=al,.text=cmd+a0})) { if (onoff) { if (n->flags & DNF_DELETE) { hlprintf_int("/del: %.*s is already deleted",al,cmd+a0); } else { delnick_ref(n)->flags |= DNF_DELETE; scan_ohlines_del(); // no set_del_time - this is user action } } else { if (n->flags & DNF_DELETE) { n->flags &= ~DNF_DELETE; delnick_deref(n); scan_ohlines_del(); // no set_del_time - this is user action } else { hlprintf_int("/del: %.*s is already not deleted",al,cmd+a0); } } break; } } if (! n) { if (onoff) { n = get_delnick((CSTR){.len=al,.text=cmd+a0}); delnick_ref(n)->flags |= DNF_DELETE; scan_ohlines_del(); // no set_del_time - this is user action } else { hlprintf_int("/del: %.*s is not known",al,cmd+a0); } } } static void slash_who(SERVER *s, const char *cmd, int len) { int i; int a0; for (i=0;(i= len) { hlprintf_int("/who: no arguments given"); return; } a0 = i; for (i=len-1;Cisspace(cmd[i]);i--) ; send_line(s,"WHO %.*s",i+1-a0,cmd+a0); } static void slash_help(SERVER *s, const char *rest, int len) { int i; for (i=0;(i= len) { send_line(s,"HELP"); } else { send_line(s,"HELP %.*s",len-i,rest+i); } } static void slash_list(SERVER *s, const char *rest, int len) { int i; for (i=0;(i= len) { send_line(s,"LIST"); } else { send_line(s,"LIST %.*s",len-i,rest+i); } } static void slash_mode(SERVER *s, const char *rest, int restlen) { ES toset; ES toclr; int i; ES *setin; ES *clrin; int l; char *b; char *p; es_init(&toset); es_init(&toclr); setin = 0; for (i=0;inick.len,s->nick.text,es_len(&toset),es_buf(&toset),es_len(&toclr),es_buf(&toclr)); } else { send_line(s,"MODE %.*s +%.*s",s->nick.len,s->nick.text,es_len(&toset),es_buf(&toset)); } } else { if (es_len(&toclr)) { send_line(s,"MODE %.*s -%.*s",s->nick.len,s->nick.text,es_len(&toclr),es_buf(&toclr)); } else { send_line(s,"MODE %.*s",s->nick.len,s->nick.text); } } es_done(&toset); es_done(&toclr); } static void slash_whois(SERVER *s, const char *cmd, int len) { int i; int s0; int sl; int n0; int nl; for (i=0;(i= len) { hlprintf_int("/whois: no arguments given"); return; } s0 = i; for (;(i= len) { send_line(s,"WHOIS %.*s",sl,cmd+s0); return; } n0 = i; for (i=len-1;Cisspace(cmd[i]);i--) ; nl = i + 1 - n0; send_line(s,"WHOIS %.*s :%.*s",sl,cmd+s0,nl,cmd+n0); } static void op_irc_init(SERVER *s) { SERV_PRIV_IRC *p; p = malloc(sizeof(SERV_PRIV_IRC)); es_init(&p->modes_set); es_init(&p->modes_clr); es_init(&p->il); p->lc = 0; p->lctail = &p->lc; s->priv = p; } static void op_irc_done(SERVER *s) { SERV_PRIV_IRC *p; p = s->priv; es_done(&p->modes_set); es_done(&p->modes_clr); es_done(&p->il); free(p); s->priv = 0; } static const char *op_irc_canuse(SERVER *s __attribute__((__unused__))) { return(0); } static void op_irc_connected(SERVER *s) { SERV_PRIV_IRC *p; p = s->priv; es_clear(&p->modes_set); es_clear(&p->modes_clr); es_clear(&p->il); send_line(s,"USER %s 8 - :%s",login,fullname); send_line(s,"NICK %.*s",s->nick.len,s->nick.text); } static void op_irc_disconnected(SERVER *s) { SERV_PRIV_IRC *p; p = s->priv; es_clear(&p->modes_set); es_clear(&p->modes_clr); es_clear(&p->il); } static void op_irc_input(SERVER *s, void *data, int len) { SERV_PRIV_IRC *p; int o; char *nl; char *dp; p = s->priv; dp = data; o = 0; while (o < len) { nl = memchr(&dp[o],'\n',len-o); if (nl) { if (es_len(&p->il) > 0) { es_append_n(&p->il,dp+o,nl-(dp+o)); server_line(s,es_buf(&p->il),es_len(&p->il)); es_clear(&p->il); } else { server_line(s,dp+o,nl-(dp+o)); } o = (nl + 1) - dp; } else { es_append_n(&p->il,dp+o,len-o); break; } } } static void op_irc_send(SERVER *s, const char *dest, int destlen, const char *body, int bodylen, unsigned int flags) { send_line(s,"PRIVMSG %.*s :%s%.*s%s", destlen, dest, (flags&SENDF_ACTION) ? "\1ACTION " : "", bodylen, body, (flags&SENDF_ACTION) ? "\1" : "" ); } static int op_irc_channeldest(SERVER *s __attribute__((__unused__)), const char *dest, int destlen) { return(destlen&&pfx_is_channel(dest[0])); } static int op_irc_okchan(SERVER *s __attribute__((__unused__)), const char *name, int namelen) { return(ok_channel(name,namelen)); } static void op_irc_sendjoin(SERVER *s, const char *name, int namelen) { send_line(s,"JOIN %.*s",namelen,name); } static void op_irc_sendpart(SERVER *s, const char *name, int namelen) { send_line(s,"PART %.*s",namelen,name); } static int op_irc_slashcmd(SERVER *s, const char *cmd, int len, int cx, int cl, int bx) { // " is special because it doesn't need a space after it if (cmd[cx] == '"') { send_line(s,"%.*s",len-(cx+1),cmd+(cx+1)); return(1); } switch (cl) { case 3: switch (cmd[cx]) { case 'd': if (! bcmp(cmd+cx,"del",3)) { slash_del(s,cmd+bx,len-bx); return(1); } break; case 'w': if (! bcmp(cmd+cx,"who",3)) { slash_who(s,cmd+bx,len-bx); return(1); } break; } break; case 4: switch (cmd[cx]) { case 'h': if (! bcmp(cmd+cx,"help",4)) { slash_help(s,cmd+bx,len-bx); return(1); } break; case 'l': if (! bcmp(cmd+cx,"list",4)) { slash_list(s,cmd+bx,len-bx); return(1); } break; case 'm': if (! bcmp(cmd+cx,"mode",4)) { slash_mode(s,cmd+bx,len-bx); return(1); } break; } break; case 5: if (! bcmp(cmd+cx,"whois",5)) { slash_whois(s,cmd+bx,len-bx); return(1); } break; } return(0); } // 2812 2.3.1 static int op_irc_oknick(SERVER *s __attribute__((__unused__)), const char *cn, int len) { int i; if (len < 1) return(0); switch (cn[0]) { case LETTERS: case SPECIALS: break; default: return(0); break; } for (i=1;i