#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAXZSIZE 16384 static int port = 0; /* what port to use locally */ static int init = 0; static int fd = 0; static int do_exit = 0; static char *my_realm; static char *exposure; static int printpings = 0; static int showdots = 0; static int striprealm = 0; static int trimsig = 0; static void szc_shutdown(void); static void szc_catchsig(int sig); static int szc_UIDACK(ZNotice_t *notice, void *arg); static void szc_init(void) { int ret; ZSubscription_t sub; signal(SIGINT, szc_catchsig); signal(SIGTERM, szc_catchsig); atexit(szc_shutdown); ret = ZInitialize(); if (ret != ZERR_NONE) { com_err("szc", ret, "from ZInitialize"); exit(EX_SOFTWARE); } ret = ZOpenPort((unsigned short *) &port); if (ret != ZERR_NONE) { com_err("szc", ret, "from ZOpenPort"); exit(EX_SOFTWARE); } sub.zsub_class = "MESSAGE"; sub.zsub_classinst = "PERSONAL"; sub.zsub_recipient = ZGetSender(); ret = ZSubscribeTo(&sub,1,port); if (ret != ZERR_NONE) { com_err("szc", ret, "from initial ZSubscribeTo"); exit(EX_SOFTWARE); } fd = ZGetFD(); my_realm = ZGetRealm(); exposure = ZGetVariable("exposure"); exposure = (exposure) ? exposure : EXPOSE_REALMANN; ret = ZSetLocation(exposure); if (ret != ZERR_NONE) { com_err("szc", ret, "from ZSetLocation"); } /* setvbuf(stdout, NULL, _IONBF, 0); */ ret = fcntl(0, F_SETFL, O_NONBLOCK); if (ret == -1) { com_err("szc", errno, "setting stdin non-blocking."); exit(EX_OSERR); } init = 1; } static void szc_catchsig(int x) { if (x == SIGINT) printf("Shut 'er down, she's a sucking mud.\n"); do_exit = 1; } void szc_shutdown(void) { if (init == 0) return; ZUnsetLocation(); ZCancelSubscriptions(0); ZClosePort(); init = 0; } void szc_togglepings(void) { printpings = !printpings; } int szc_wait(void) { int ret; fd_set fds; if (ZPending()) return 0; FD_ZERO(&fds); FD_SET(fd, &fds); /* Zephyr */ FD_SET(0, &fds); /* stdin */ ret = select(fd+1, &fds, NULL, NULL, NULL); if (ret == -1 && errno != EINTR) { com_err("szc", errno, "while waiting for input."); exit(EX_IOERR); } return 0; } void szc_readzephyrs(void) { ZNotice_t znote; struct sockaddr_in from; int readycnt; char zbuf[MAXZSIZE]; char zbody[MAXZSIZE]; char c, *p, *q; int l,ret; struct tm *tm; while ((readycnt = ZPending()) > 0) { ret = ZReceiveNotice(&znote, &from); if (ret) com_err("szc", ret, "from ZReceiveNotice."); /* Process headers */ p = strchr(znote.z_sender, '@'); if (p && !strcmp(p+1, my_realm) || striprealm) *p = 0; p = strchr(znote.z_recipient, '@'); if (p && !strcmp(p+1, my_realm) || striprealm) *p = 0; for (p = znote.z_class; *p != '\0'; p++) { (*p) = tolower(*p); } #ifdef DAR_CLIENT if (strcmp(znote.z_class, "message") == 0) strcpy(znote.z_class,"msg"); #endif for (p = znote.z_class_inst; *p != '\0'; p++) { (*p) = tolower(*p); } #ifdef DAR_CLIENT if (strcmp(znote.z_class_inst, "share&enjoy") == 0) strcpy(znote.z_class_inst,"s&e"); #endif for (p = znote.z_class_inst; *p != '\0'; p++) { (*p) = tolower(*p); } if (!strcmp(znote.z_opcode,"PING")) { if (printpings) printf("ping from %s\n",znote.z_sender); fflush(stdout); ZFreeNotice(&znote); continue; } /* Process the Body By convention, signature is the first NULL terminated field, followed by the body. */ /* Force termination */ if (znote.z_message[znote.z_message_len] != '\0') znote.z_message[znote.z_message_len] = '\0'; p = zbody; /* skip the signature */ q = strchr(znote.z_message,'\0'); q++; strcpy(p,q); if (trimsig) if (strlen(znote.z_message) > trimsig) znote.z_message[trimsig] = '\0'; memset(zbuf,0x00,MAXZSIZE); tm = localtime(&znote.z_time.tv_sec); ret = snprintf(zbuf, MAXZSIZE, "%02d:%02d:%02d|<%s,%s,%s>|%s <%s>\n", tm->tm_hour, tm->tm_min, tm->tm_sec, znote.z_class, znote.z_class_inst, znote.z_recipient, znote.z_message, znote.z_sender); printf("%s",zbuf); if (showdots) ret = snprintf(zbuf, MAXZSIZE, "%s\n.\n", zbody); else ret = snprintf(zbuf, MAXZSIZE, "%s\n\n", zbody); printf("%s",zbuf); fflush(stdout); ZFreeNotice(&znote); } if (readycnt < 0) { com_err("szc", errno, "from ZPending."); exit(EX_SOFTWARE); } } /* Take the string and copies the parts into the provided buffers. Warning: this modifies the original triplet string Return 0 if failure occurs (bad triplet) Return 1 if success */ int szc_parsetriplet(char *triplet, char *class, char *inst, char *recip) { char *end; char *segment; if (*triplet != '<') return 0; segment = triplet + 1; end = strchr(segment,','); if (!end) return 0; strncpy(class,segment,end-segment); class[end-segment] = '\0'; segment = end + 1; end = strchr(segment,','); if (!end) return 0; strncpy(inst,segment,end-segment); inst[end-segment] = '\0'; segment = end + 1; end = strchr(segment,'>'); if (!end) return 0; *end = 0; strcpy(recip,segment); return 1; } /* If we don't do this dance and just use ZCompareUID in the arg to ZIfNotice, then we can't send to ourselves, because we get the message before the ACK and it has the same UID */ int szc_UIDACK(ZNotice_t *notice, void *arg) { if (notice->z_kind != SERVACK && notice->z_kind != HMACK) return 0; if (ZCompareUID(¬ice->z_uid, (ZUnique_Id_t *) arg)) return 1; return 0; } void szc_initzephyr(ZNotice_t *note, int auth, int port, char *sig, char *class, char *inst, char *op, char *sender) { char *zsig, *zclass, *zinst, *zop; memset(note, 0, sizeof(ZNotice_t)); zsig = ZGetVariable("zwrite-signature"); strcpy(sig, (zsig) ? zsig : getenv("USER")); zclass = ZGetVariable("zwrite-class"); strcpy(class, (zclass) ? zclass : "MESSAGE"); zinst = ZGetVariable("zwrite-inst"); strcpy(inst, (zinst) ? zinst : "PERSONAL"); zop = ZGetVariable("zwrite-opcode"); strcpy(op, (zop) ? zop : ""); note->z_kind = ACKED; note->z_port = port; note->z_class = class; note->z_class_inst = inst; note->z_opcode = op; note->z_sender = sender; note->z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold($1) <$sender>\n\n$2"; note->z_message = 0; note->z_message_len = 0; note->z_recipient = 0; return; } void szc_dosend(ZNotice_t *note) { int ret; ZNotice_t zret; ret = ZSendNotice(note, ZAUTH); if (ret != ZERR_NONE) { com_err("szc", ret, "sending message."); return; } ret = ZIfNotice(&zret, (struct sockaddr_in *)0, szc_UIDACK, (void *) &(note->z_uid)); if (ret != ZERR_NONE) { com_err("szc", ret, "waiting for ack."); return; } if (zret.z_kind == SERVNAK) { printf("Auth failure on message send.\n"); } else if (zret.z_message_len == 0) { printf("Server failure on message send.\n"); } else if (!strcmp(zret.z_message, ZSRVACK_SENT)) { printf("message sent\n"); } else if (!strcmp(zret.z_message, ZSRVACK_NOTSENT)) { printf("%s\n", "No one listening"); } else { printf("Illegal server reply on message ack.\n"); } fflush(stdout); ZFreeNotice(&zret); return; } void szc_send(char *cmd) { ZNotice_t znote; char sig[BUFSIZ], cl[BUFSIZ], ins[BUFSIZ], op[BUFSIZ]; char rc[BUFSIZ], mbuf[BUFSIZ]; char *p, *msg, *recp; recp = cmd; msg = strchr(cmd,'|'); if (!msg) goto brokeit; *msg++ = '\0'; szc_initzephyr(&znote, ACKED, 0, sig, cl, ins, op, 0); if (*recp == '<') { if(!szc_parsetriplet(recp, cl, ins, rc)) goto brokeit; znote.z_class = cl; znote.z_class_inst = ins; } else { strcpy(rc,recp); } znote.z_recipient = rc; znote.z_message = mbuf; znote.z_message_len = strlen(msg) + strlen(sig) + 2; strcpy(mbuf,sig); p = strchr(mbuf,'\0'); p++; strcpy(p,msg); szc_dosend(&znote); return; brokeit: printf("Illegally formatted send request.\n"); fflush(stdout); return; } static ZNotice_t mznote; void szc_msend(char *cmd) { static char sig[BUFSIZ], cl[BUFSIZ], ins[BUFSIZ], op[BUFSIZ]; static char rc[BUFSIZ]; char *mzp, *end; szc_initzephyr(&mznote, ACKED, 0, sig, cl, ins, op, 0); if (*cmd == '<') { if(!szc_parsetriplet(cmd, cl, ins, rc)) { printf("Illegally formatted send request.\n"); fflush(stdout); return; } mznote.z_class = cl; mznote.z_class_inst = ins; } else { strcpy(rc,cmd); } mznote.z_recipient = rc; mzp = mznote.z_message = (char *) malloc(BUFSIZ); strcpy(mzp, sig); end = strchr(mzp,'\0'); mznote.z_message_len = end-mzp + 1; /* 1 for null */ return; } void szc_addline(char *line) { int l; l = strlen(line) + 1; mznote.z_message = realloc(mznote.z_message, mznote.z_message_len + l); strcpy(mznote.z_message + mznote.z_message_len, line); mznote.z_message_len += l; mznote.z_message[mznote.z_message_len - 1] = '\n'; } void szc_msendfinalize(int sendit) { if (sendit) szc_dosend(&mznote); free(mznote.z_message); } void szc_unsub(char *cmd) { char c[BUFSIZ], i[BUFSIZ], r[BUFSIZ]; ZSubscription_t zsub; int ret; if (!szc_parsetriplet(cmd, c, i, r)) { printf("Bad unsubscription request."); fflush(stdout); return; } zsub.zsub_class = c; zsub.zsub_classinst = i; zsub.zsub_recipient = r; ret = ZUnsubscribeTo(&zsub, 1, 0); if (ret == ZERR_SERVNAK) printf("SERVNAK attempting to unsubscribe.\n"); fflush(stdout); return; } void szc_sub(char *cmd) { char c[BUFSIZ], i[BUFSIZ], r[BUFSIZ]; ZSubscription_t zsub; int ret; if (!szc_parsetriplet(cmd, c, i, r)) { printf("Bad subscription request."); fflush(stdout); return; } zsub.zsub_class = c; zsub.zsub_classinst = i; zsub.zsub_recipient = r; ret = ZSubscribeTo(&zsub, 1, 0); if (ret == ZERR_SERVNAK) printf("SERVNAK attempting to subscribe.\n"); fflush(stdout); return; } void szc_exposure(char *cmd) { switch (*cmd) { case 'o': if (!strncmp("opstaff",cmd,7)) { ZSetLocation(EXPOSE_OPSTAFF); break; } break; case 'n': if (!strncmp("net-announced",cmd,12)) { ZSetLocation(EXPOSE_NETANN); break; } if (!strncmp("net-visible",cmd,10)) { ZSetLocation(EXPOSE_NETVIS); break; } if (!strncmp("none",cmd,4)) { ZSetLocation(EXPOSE_NONE); break; } break; case 'r': if (!strncmp("realm-visible",cmd,13)) { ZSetLocation(EXPOSE_REALMVIS); break; } if (!strncmp("realm-announced",cmd,15)) { ZSetLocation(EXPOSE_REALMANN); break; } break; default: printf("Bad exposure level.\n"); fflush(stdout); } return; } void szc_dispatch(char*buf); static int mline = 0; void szc_load(char *filename) { FILE *f; char cmdbuf[MAXZSIZE]; char *p; f = fopen(filename, "r"); if (f == NULL) return; while (!feof(f)) { if (fgets(cmdbuf, MAXZSIZE, f) == NULL) break; p = strchr(cmdbuf, '\n'); if (p != NULL) *p = '\0'; szc_dispatch(cmdbuf); } fclose(f); } void szc_echo(char *cmdbuf) { printf("%s\n", cmdbuf); fflush(stdout); } void szc_dispatch(char *cmdbuf) { switch (*cmdbuf) { case 's': if (!strncmp(cmdbuf, "send:", 5)) { szc_send(cmdbuf+5); break; } if (!strncmp(cmdbuf, "sub:", 4)) { szc_sub(cmdbuf+4); break; } if (!strncmp(cmdbuf, "striprealm", 10)) { striprealm = !striprealm; break; } break; case 'u': if (!strncmp(cmdbuf, "unsub:", 6)) { szc_unsub(cmdbuf+6); break; } break; case 'e': if (!strncmp(cmdbuf, "exposure:", 9)) { szc_exposure(cmdbuf+9); break; } if (!strncmp(cmdbuf, "echo:", 5)) { szc_echo(cmdbuf+5); break; } break; case 'q': if (!strncmp(cmdbuf, "quit", 4)) do_exit = 1; break; case 'm': if (!strncmp(cmdbuf, "msend:", 6)) { mline = 1; szc_msend(cmdbuf+6); break; } break; case 'l': if (!strncmp(cmdbuf, "load:", 5)) { szc_load(cmdbuf+5); break; } break; case 'p': if (!strncmp(cmdbuf, "pingshow", 8)) { printpings = !printpings; break; } break; case 'd': if (!strncmp(cmdbuf, "dotshow", 7)) { printpings = !showdots; break; } break; case 'c': if (!strncmp(cmdbuf, "cls", 3)) { system("clear"); break; } break; case 't': if (!strncmp(cmdbuf, "trimsig:", 8)) { trimsig = atoi(cmdbuf + 8); break; } break; case '\0': break; default: printf("szc error: unknown command.\n"); fflush(stdout); break; } } void szc_readcommands(void) { static char cmdbuf[MAXZSIZE]; static char *p = cmdbuf; static int l = MAXZSIZE; int r; char *q; do { r = read(0, p, l); } while (r < 0 && errno == EINTR); if (r == -1 && errno == EAGAIN) return; *(p + r + 1) = '\0'; /* Be sure */ l -= r; if (l == 0) goto cleanup; /* Drop it, too long */ if ((q = strchr(cmdbuf,'\n')) == NULL) return; *q = '\0'; if (mline && !strcmp(cmdbuf, "!")) { szc_msendfinalize(0); mline = 0; return; } if (mline && !strcmp(cmdbuf, ".")) { szc_msendfinalize(1); mline = 0; return; } if (mline) { szc_addline(cmdbuf); return; } szc_dispatch(cmdbuf); cleanup: memset(cmdbuf, 0x0, MAXZSIZE); p = cmdbuf; l = MAXZSIZE; return; } void szc_mainloop(void) { while (!do_exit) { szc_wait(); if (do_exit) return; szc_readzephyrs(); szc_readcommands(); } } int main(int ac, char **av) { char path[5000]; szc_init(); strcpy(path, getenv("HOME")); strcat(path, "/.szccmd"); szc_load(path); szc_mainloop(); exit(EX_OK); }