diff --git a/Makefile.linux b/Makefile.linux index 1f06219..603003c 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -5,7 +5,7 @@ JAMLIB = jamlib/jamlib.a ZMODEM = Xmodem/libzmodem.a LUA = lua/liblua.a -OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o files.o settings.o lua_glue.o strings.o +OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o files.o settings.o lua_glue.o strings.o bluewave.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/Makefile.linux.WWW b/Makefile.linux.WWW index ac304c3..484294b 100644 --- a/Makefile.linux.WWW +++ b/Makefile.linux.WWW @@ -6,7 +6,7 @@ ZMODEM = Xmodem/libzmodem.a LUA = lua/liblua.a MICROHTTPD=-lmicrohttpd -lb64 -OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o files.o settings.o lua_glue.o strings.o www.o www_email.o www_msgs.o www_last10.o +OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o files.o settings.o lua_glue.o strings.o www.o www_email.o www_msgs.o www_last10.o bluewave.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/ansis_default/mailmenu.ans b/ansis_default/mailmenu.ans index 74f6339..e902d75 100644 Binary files a/ansis_default/mailmenu.ans and b/ansis_default/mailmenu.ans differ diff --git a/bbs.c b/bbs.c index 3ea4a28..1fc04fd 100644 --- a/bbs.c +++ b/bbs.c @@ -70,6 +70,9 @@ void dolog(char *fmt, ...) { } struct fido_addr *parse_fido_addr(const char *str) { + if (str == NULL) { + return NULL; + } struct fido_addr *ret = (struct fido_addr *)malloc(sizeof(struct fido_addr)); int c; int state = 0; diff --git a/bbs.h b/bbs.h index 2951fed..15f74e8 100644 --- a/bbs.h +++ b/bbs.h @@ -2,6 +2,8 @@ #define __BBS_H__ #include +#include + #if defined(ENABLE_WWW) #include #endif @@ -10,7 +12,7 @@ #include "jamlib/jam.h" #define VERSION_MAJOR 0 -#define VERSION_MINOR 4 +#define VERSION_MINOR 5 #define VERSION_STR "alpha" #define NETWORK_FIDO 1 @@ -86,6 +88,7 @@ struct file_directory { struct bbs_config { char *bbs_name; + char *bwave_name; char *sysop_name; char *pid_file; char *ansi_path; @@ -107,6 +110,10 @@ struct bbs_config { char *irc_server; int irc_port; char *irc_channel; + char *zip_cmd; + char *unzip_cmd; + int bwave_max_msgs; + struct fido_addr *main_aka; char *external_editor_cmd; int external_editor_stdio; @@ -147,6 +154,7 @@ struct user_record { int cur_file_dir; int cur_file_sub; int timeson; + int bwavepktno; }; struct jam_msg { @@ -219,11 +227,16 @@ extern void send_email(struct user_record *user); extern void list_emails(struct user_record *user); extern int file_menu(struct user_record *user); - +extern void download_zmodem(struct user_record *user, char *filename); extern void settings_menu(struct user_record *user); +extern void upload_zmodem(struct user_record *user, char *upload_p); +extern int ttySetRaw(int fd, struct termios *prevTermios); extern void lua_push_cfunctions(lua_State *L); +extern void bwave_create_packet(); +extern void bwave_upload_reply(); + extern void load_strings(); extern char *get_string(int offset); extern void chomp(char *string); diff --git a/bluewave.c b/bluewave.c new file mode 100644 index 0000000..fcf1f70 --- /dev/null +++ b/bluewave.c @@ -0,0 +1,728 @@ +#include +#include +#include +#include +#include +#include +#include "bluewave.h" +#include "jamlib/jam.h" +#include "bbs.h" + +extern struct bbs_config conf; +extern struct user_record *gUser; +extern int mynode; +extern char upload_filename[1024]; +extern int sshBBS; + +tLONG convertl(tLONG l) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + unsigned char result_bytes[4]; + unsigned int result; + result_bytes[0] = (unsigned char) ((l >> 24) & 0xFF); + result_bytes[1] = (unsigned char) ((l >> 16) & 0xFF); + result_bytes[2] = (unsigned char) ((l >> 8) & 0xFF); + result_bytes[3] = (unsigned char) (l & 0xFF); + memcpy(&result, result_bytes, 4); + return result; +#else + return l; +#endif +} + +tWORD converts(tWORD s) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + unsigned char result_bytes[2]; + unsigned short result; + result_bytes[0] = (unsigned char) ((s >> 8) & 0xFF); + result_bytes[1] = (unsigned char) (s & 0xFF); + memcpy(&result, result_bytes, 4); + return result; +#else + return s; +#endif +} + + +int bwave_scan_area(int confr, int area, int areano, int totmsgs, FILE *fti_file, FILE *mix_file, FILE *dat_file) { + struct msg_headers *msghs = read_message_headers(confr, area, gUser); + int all_unread = 1; + s_JamBase *jb; + s_JamLastRead jlr; + int i; + int k; + MIX_REC mix; + int area_msgs; + int personal_msgs; + long mixptr; + FTI_REC fti; + struct fido_addr *fido; + char *body; + struct tm timeStruct; + char *month_name[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (msghs == NULL) { + return totmsgs; + } + + jb = open_jam_base(conf.mail_conferences[confr]->mail_areas[area]->path); + if (!jb) { + dolog("Error opening JAM base.. %s", conf.mail_conferences[confr]->mail_areas[area]->path); + free_message_headers(msghs); + return totmsgs; + } else { + all_unread = 0; + if (JAM_ReadLastRead(jb, gUser->id, &jlr) == JAM_NO_USER) { + jlr.LastReadMsg = 0; + jlr.HighReadMsg = 0; + all_unread = 1; + } + + } + + if (all_unread == 0) { + k = jlr.HighReadMsg; + for (i=0;imsg_count;i++) { + if (msghs->msgs[i]->msg_h->MsgNum == k) { + break; + } + } + i+=1; + } else { + i = 0; + } + + mixptr = ftell(fti_file); + area_msgs = 0; + personal_msgs = 0; + + for (k=i;kmsg_count;k++) { + + if (totmsgs == conf.bwave_max_msgs) { + break; + } + + if (strcasecmp(msghs->msgs[k]->to, gUser->loginname) == 0) { + personal_msgs++; + } + + memset(&fti, 0, sizeof(FTI_REC)); + + strncpy(fti.from, msghs->msgs[k]->from, 35); + strncpy(fti.to, msghs->msgs[k]->to, 35); + strncpy(fti.subject, msghs->msgs[k]->subject, 71); + + localtime_r((time_t *)&msghs->msgs[k]->msg_h->DateWritten, &timeStruct); + + sprintf(fti.date, "%02d-%s-%04d %02d:%02d", timeStruct.tm_mday, month_name[timeStruct.tm_mon], timeStruct.tm_year + 1900, timeStruct.tm_hour, timeStruct.tm_min); + fti.msgnum = converts((tWORD)msghs->msgs[k]->msg_h->MsgNum); + fti.replyto = 0; + fti.replyat = 0; + fti.msgptr = convertl(ftell(dat_file)); + fti.msglength = convertl(msghs->msgs[k]->msg_h->TxtLen); + + + if (msghs->msgs[k]->msg_h->Attribute & MSG_LOCAL) { + fti.flags |= FTI_MSGLOCAL; + } + + fti.flags = converts(fti.flags); + + fido = parse_fido_addr(msghs->msgs[k]->oaddress); + if (fido != NULL) { + fti.orig_zone = converts(fido->zone); + fti.orig_net = converts(fido->net); + fti.orig_node = converts(fido->node); + free(fido); + } else { + fti.orig_zone = 0; + fti.orig_net = 0; + fti.orig_node = 0; + } + // write msg data + body = (char *)malloc(msghs->msgs[k]->msg_h->TxtLen); + JAM_ReadMsgText(jb, msghs->msgs[k]->msg_h->TxtOffset, msghs->msgs[k]->msg_h->TxtLen, (char *)body); + + fwrite(body, 1, msghs->msgs[k]->msg_h->TxtLen, dat_file); + fwrite(&fti, sizeof(FTI_REC), 1, fti_file); + + free(body); + jlr.LastReadMsg = msghs->msgs[k]->msg_h->MsgNum; + if (jlr.HighReadMsg < msghs->msgs[k]->msg_h->MsgNum) { + jlr.HighReadMsg = msghs->msgs[k]->msg_h->MsgNum; + } + + JAM_WriteLastRead(jb, gUser->id, &jlr); + + area_msgs++; + totmsgs++; + } + + if (area_msgs) { + + memset(&mix, 0, sizeof(MIX_REC)); + + snprintf(mix.areanum, 6, "%d", areano); + mix.totmsgs = converts(area_msgs); + mix.numpers = converts(personal_msgs); + mix.msghptr = convertl(mixptr); + fwrite(&mix, sizeof(MIX_REC), 1, mix_file); + } + JAM_CloseMB(jb); + free_message_headers(msghs); + return totmsgs; +} + +void bwave_create_packet() { + char buffer[1024]; + char archive[1024]; + INF_HEADER hdr; + INF_AREA_INFO **areas = NULL; + int i; + int j; + int area_count; + tWORD flags; + int lasttot; + int bpos; + + struct termios oldit; + struct termios oldot; + + FILE *mix_file; + FILE *fti_file; + FILE *dat_file; + FILE *inf_file; + + int totmsgs = 0; + + area_count = 0; + + memset(&hdr, 0, sizeof(INF_HEADER)); + + hdr.ver = PACKET_LEVEL; + strncpy(hdr.loginname, gUser->loginname, 42); + strncpy(hdr.aliasname, gUser->loginname, 42); + hdr.zone = converts(conf.main_aka->zone); + hdr.node = converts(conf.main_aka->node); + hdr.net = converts(conf.main_aka->net); + hdr.point = converts(conf.main_aka->point); + strncpy(hdr.sysop, conf.sysop_name, 40); + + strncpy(hdr.systemname, conf.bbs_name, 64); + hdr.inf_header_len = converts(sizeof(INF_HEADER)); + hdr.inf_areainfo_len = converts(sizeof(INF_AREA_INFO)); + hdr.mix_structlen = converts(sizeof(MIX_REC)); + hdr.fti_structlen = converts(sizeof(FTI_REC)); + hdr.uses_upl_file = 1; + hdr.from_to_len = 35; + hdr.subject_len = 71; + memcpy(hdr.packet_id, conf.bwave_name, strlen(conf.bwave_name)); + + snprintf(buffer, 1024, "%s/node%d/%s.FTI", conf.bbs_path, mynode, conf.bwave_name); + + fti_file = fopen(buffer, "w"); + + snprintf(buffer, 1024, "%s/node%d/%s.MIX", conf.bbs_path, mynode, conf.bwave_name); + mix_file = fopen(buffer, "w"); + + snprintf(buffer, 1024, "%s/node%d/%s.DAT", conf.bbs_path, mynode, conf.bwave_name); + dat_file = fopen(buffer, "w"); + + s_printf("\r\n"); + + for (i=0;imail_area_count;j++) { + if (conf.mail_conferences[i]->mail_areas[j]->read_sec_level <= gUser->sec_level && conf.mail_conferences[i]->mail_areas[j]->qwkname != NULL) { + lasttot = totmsgs; + totmsgs = bwave_scan_area(i, j, area_count+1, totmsgs, fti_file, mix_file, dat_file); + s_printf(get_string(195), conf.mail_conferences[i]->name, conf.mail_conferences[i]->mail_areas[j]->name, totmsgs - lasttot); + if (lasttot == totmsgs) { + continue; + } + + if (area_count == 0) { + areas = (INF_AREA_INFO **)malloc(sizeof(INF_AREA_INFO *)); + } else { + areas = (INF_AREA_INFO **)realloc(areas, sizeof(INF_AREA_INFO *) * (area_count + 1)); + } + flags = 0; + areas[area_count] = (INF_AREA_INFO *)malloc(sizeof(INF_AREA_INFO)); + + memset(areas[area_count], 0, sizeof(INF_AREA_INFO)); + + snprintf(areas[area_count]->areanum, 6, "%d", area_count + 1); + + memcpy(areas[area_count]->echotag, conf.mail_conferences[i]->mail_areas[j]->qwkname, strlen(conf.mail_conferences[i]->mail_areas[j]->qwkname)); + + strncpy(areas[area_count]->title, conf.mail_conferences[i]->mail_areas[j]->name, 49); + + if (conf.mail_conferences[i]->mail_areas[j]->write_sec_level <= gUser->sec_level) { + flags |= INF_POST; + } + + if (conf.mail_conferences[i]->mail_areas[j]->type == TYPE_NETMAIL_AREA) { + flags |= INF_NO_PUBLIC; + flags |= INF_NETMAIL; + flags |= INF_ECHO; + } + + if (conf.mail_conferences[i]->mail_areas[j]->type == TYPE_ECHOMAIL_AREA) { + flags |= INF_NO_PRIVATE; + flags |= INF_ECHO; + } + + if (conf.mail_conferences[i]->mail_areas[j]->type == TYPE_LOCAL_AREA) { + flags |= INF_NO_PRIVATE; + } + + flags |= INF_SCANNING; + + areas[area_count]->area_flags = converts(flags); + areas[area_count]->network_type = INF_NET_FIDONET; + + area_count++; + if (totmsgs == conf.bwave_max_msgs) { + break; + } + + } + } + if (totmsgs == conf.bwave_max_msgs) { + break; + } + } + + fclose(dat_file); + fclose(mix_file); + fclose(fti_file); + + snprintf(buffer, 1024, "%s/node%d/%s.INF", conf.bbs_path, mynode, conf.bwave_name); + + inf_file = fopen(buffer, "w"); + fwrite(&hdr, sizeof(INF_HEADER), 1, inf_file); + + for (i=0;i 0) { + // create archive + bpos = 0; + snprintf(archive, 1024, "%s/node%d/%s.%03d", conf.bbs_path, mynode, conf.bwave_name, gUser->bwavepktno); + + for (i=0;ibwavepktno++; + save_user(gUser); + } + + s_printf(get_string(6)); + s_getc(); +} + + +int bwave_add_message(int confr, int area, unsigned int dwritten, char *to, char *subject, struct fido_addr *destaddr, char *msg) { + s_JamBase *jb; + s_JamMsgHeader jmh; + s_JamSubPacket* jsp; + s_JamSubfield jsf; + int z; + char buffer[256]; + + jb = open_jam_base(conf.mail_conferences[confr]->mail_areas[area]->path); + if (!jb) { + dolog("Error opening JAM base.. %s", conf.mail_conferences[confr]->mail_areas[area]->path); + return 1; + } + + JAM_ClearMsgHeader( &jmh ); + jmh.DateWritten = dwritten; + jmh.Attribute |= MSG_LOCAL; + if (conf.mail_conferences[confr]->realnames == 0) { + strcpy(buffer, gUser->loginname); + } else { + sprintf(buffer, "%s %s", gUser->firstname, gUser->lastname); + } + + + jsp = JAM_NewSubPacket(); + + jsf.LoID = JAMSFLD_SENDERNAME; + jsf.HiID = 0; + jsf.DatLen = strlen(buffer); + jsf.Buffer = (char *)buffer; + JAM_PutSubfield(jsp, &jsf); + + jsf.LoID = JAMSFLD_RECVRNAME; + jsf.HiID = 0; + jsf.DatLen = strlen(to); + jsf.Buffer = (char *)to; + JAM_PutSubfield(jsp, &jsf); + + jsf.LoID = JAMSFLD_SUBJECT; + jsf.HiID = 0; + jsf.DatLen = strlen(subject); + jsf.Buffer = (char *)subject; + JAM_PutSubfield(jsp, &jsf); + + if (conf.mail_conferences[confr]->mail_areas[area]->type == TYPE_ECHOMAIL_AREA) { + jmh.Attribute |= MSG_TYPEECHO; + + if (conf.mail_conferences[confr]->fidoaddr->point) { + sprintf(buffer, "%d:%d/%d.%d", conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node, + conf.mail_conferences[confr]->fidoaddr->point); + } else { + sprintf(buffer, "%d:%d/%d", conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node); + } + jsf.LoID = JAMSFLD_OADDRESS; + jsf.HiID = 0; + jsf.DatLen = strlen(buffer); + jsf.Buffer = (char *)buffer; + JAM_PutSubfield(jsp, &jsf); + + sprintf(buffer, "%d:%d/%d.%d %08lx", conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node, + conf.mail_conferences[confr]->fidoaddr->point, + generate_msgid()); + + jsf.LoID = JAMSFLD_MSGID; + jsf.HiID = 0; + jsf.DatLen = strlen(buffer); + jsf.Buffer = (char *)buffer; + JAM_PutSubfield(jsp, &jsf); + jmh.MsgIdCRC = JAM_Crc32(buffer, strlen(buffer)); + + } else if (conf.mail_conferences[confr]->mail_areas[confr]->type == TYPE_NETMAIL_AREA) { + jmh.Attribute |= MSG_TYPENET; + jmh.Attribute |= MSG_PRIVATE; + + if (conf.mail_conferences[confr]->fidoaddr->point) { + sprintf(buffer, "%d:%d/%d.%d", conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node, + conf.mail_conferences[confr]->fidoaddr->point); + } else { + sprintf(buffer, "%d:%d/%d", conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node); + } + jsf.LoID = JAMSFLD_OADDRESS; + jsf.HiID = 0; + jsf.DatLen = strlen(buffer); + jsf.Buffer = (char *)buffer; + JAM_PutSubfield(jsp, &jsf); + + if (destaddr != NULL) { + if (destaddr->point) { + sprintf(buffer, "%d:%d/%d.%d", destaddr->zone, + destaddr->net, + destaddr->node, + destaddr->point); + } else { + sprintf(buffer, "%d:%d/%d", destaddr->zone, + destaddr->net, + destaddr->node); + } + jsf.LoID = JAMSFLD_DADDRESS; + jsf.HiID = 0; + jsf.DatLen = strlen(buffer); + jsf.Buffer = (char *)buffer; + JAM_PutSubfield(jsp, &jsf); + } + + sprintf(buffer, "%d:%d/%d.%d %08lx", conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node, + conf.mail_conferences[confr]->fidoaddr->point, + generate_msgid()); + + jsf.LoID = JAMSFLD_MSGID; + jsf.HiID = 0; + jsf.DatLen = strlen(buffer); + jsf.Buffer = (char *)buffer; + JAM_PutSubfield(jsp, &jsf); + jmh.MsgIdCRC = JAM_Crc32(buffer, strlen(buffer)); + } + + while (1) { + z = JAM_LockMB(jb, 100); + if (z == 0) { + break; + } else if (z == JAM_LOCK_FAILED) { + sleep(1); + } else { + dolog("Failed to lock msg base!"); + JAM_CloseMB(jb); + return 1; + } + } + if (JAM_AddMessage(jb, &jmh, jsp, (char *)msg, strlen(msg))) { + dolog("Failed to add message"); + JAM_UnlockMB(jb); + + JAM_DelSubPacket(jsp); + JAM_CloseMB(jb); + return -1; + } else { + JAM_UnlockMB(jb); + + JAM_DelSubPacket(jsp); + JAM_CloseMB(jb); + } + return 0; +} + +void bwave_upload_reply() { + char buffer[1024]; + char msgbuffer[1024]; + char originlinebuffer[256]; + int i; + int bpos; + UPL_HEADER upl_hdr; + UPL_REC upl_rec; + int j; + int confr; + int area; + tWORD msg_attr; + struct fido_addr addr; + char *body; + struct stat s; + char *tagline; + int echomail = 0; + int netmail = 0; + FILE *upl_file; + FILE *msg_file; + int sem_fd; + + snprintf(buffer, 1024, "%s/node%d/", conf.bbs_path, mynode); + + upload_zmodem(gUser, buffer); + + bpos = 0; + for (i=0;imail_area_count;j++) { + if (strcmp(conf.mail_conferences[i]->mail_areas[j]->qwkname, upl_rec.echotag) == 0) { + confr = i; + area = j; + break; + } + } + if (confr != -1) { + break; + } + } + + if (confr != -1 && area != -1) { + // import message + if (conf.mail_conferences[confr]->mail_areas[area]->write_sec_level <= gUser->sec_level) { + msg_attr = converts(upl_rec.msg_attr); + + if (msg_attr & UPL_INACTIVE) { + continue; + } + + if (strcasecmp(upl_rec.from, gUser->loginname) != 0) { + continue; + } + + addr.zone = 0; + addr.net = 0; + addr.node = 0; + addr.zone = 0; + + if (conf.mail_conferences[confr]->mail_areas[area]->type == TYPE_NETMAIL_AREA) { + if (!(msg_attr & UPL_NETMAIL)) { + continue; + } + addr.zone = converts(upl_rec.destzone); + addr.net = converts(upl_rec.destnet); + addr.node = converts(upl_rec.destnode); + addr.zone = converts(upl_rec.destpoint); + } else if (conf.mail_conferences[confr]->mail_areas[area]->type == TYPE_ECHOMAIL_AREA) { + if (msg_attr & UPL_PRIVATE) { + continue; + } + + } else { // Local area + if (msg_attr & UPL_PRIVATE) { + continue; + } + } + + snprintf(msgbuffer, 1024, "%s/node%d/%s", conf.bbs_path, mynode, upl_rec.filename); + + if (conf.mail_conferences[confr]->tagline != NULL) { + tagline = conf.mail_conferences[confr]->tagline; + } else { + tagline = conf.default_tagline; + } + + if (conf.mail_conferences[confr]->nettype == NETWORK_FIDO) { + if (conf.mail_conferences[confr]->fidoaddr->point == 0) { + snprintf(originlinebuffer, 256, "\r--- %s\r * Origin: %s (%d:%d/%d)\r", upl_hdr.reader_tear, tagline, conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node); + } else { + + snprintf(originlinebuffer, 256, "\r--- %s\r * Origin: %s (%d:%d/%d.%d)\r", upl_hdr.reader_tear, tagline, conf.mail_conferences[confr]->fidoaddr->zone, + conf.mail_conferences[confr]->fidoaddr->net, + conf.mail_conferences[confr]->fidoaddr->node, + conf.mail_conferences[confr]->fidoaddr->point); + } + } else { + snprintf(originlinebuffer, 256, "\r--- %s\r * Origin: %s \r", upl_hdr.reader_tear, tagline); + } + + if (stat(msgbuffer, &s) != 0) { + continue; + } + body = (char *)malloc(s.st_size + 1 + strlen(originlinebuffer)); + msg_file = fopen(msgbuffer, "r"); + if (!msg_file) { + free(body); + continue; + } + + fread(body, 1, s.st_size, msg_file); + fclose(msg_file); + + unlink(msgbuffer); + + body[s.st_size] = '\0'; + + strcat(body, originlinebuffer); + + bpos = 0; + for (i=0;imail_areas[area]->type == TYPE_NETMAIL_AREA) { + if (conf.netmail_sem != NULL) { + sem_fd = open(conf.netmail_sem, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); + close(sem_fd); + } + } else if (conf.mail_conferences[confr]->mail_areas[area]->type == TYPE_ECHOMAIL_AREA) { + if (conf.echomail_sem != NULL) { + sem_fd = open(conf.echomail_sem, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); + close(sem_fd); + } + } + } + + free(body); + } + } + } + + fclose(upl_file); + unlink(buffer); + + s_printf("\r\n"); + s_printf(get_string(6)); + s_getc(); +} diff --git a/bluewave.h b/bluewave.h new file mode 100644 index 0000000..a4d5e63 --- /dev/null +++ b/bluewave.h @@ -0,0 +1,1152 @@ +/*****************************************************************************/ +/* */ +/* The Blue Wave Offline Mail System Packet Structures */ +/* Copyright 1990-1995 by Cutting Edge Computing. All rights reserved. */ +/* Created by George Hatchew */ +/* */ +/* Version 3 - November 30, 1995 */ +/* */ +/* --------------------------------------------------------- */ +/* DISTRIBUTION OF THIS FILE IS LIMITED BY THE TERMS */ +/* SPECIFIED IN THE BLUE WAVE STRUCTURE DOCUMENTATION! */ +/* --------------------------------------------------------- */ +/* */ +/* These data structures should be usable with any C compiler that */ +/* supports the 1989 ANSI/ISO C language standard. They are NOT */ +/* guaranteed to be usable with older compilers, which largely relied */ +/* on the definition of the language as specified in Kernighan & Ritchie's */ +/* _The C Programming Language (1st Edition)_. */ +/* */ +/*****************************************************************************/ + +#ifndef __BLUEWAVE_H /* An extra safeguard to prevent this header from */ +#define __BLUEWAVE_H /* being included twice in the same source file */ + + +#define PACKET_LEVEL 3 /* The current mail packet revision level, */ + /* used in the "ver" field of the *.INF */ + /* file header. */ + + +/* +** This header defines the data structures for the following files in the +** official Blue Wave offline mail specification: +** +** Door: *.INF BBS and message area information +** *.MIX Quick index to *.FTI records +** *.FTI Information for all packet messages +** *.DAT Packet message text +** +** Reader: *.NET NetMail reply message information +** *.UPI Information for all other reply messages +** *.UPL Reply message information +** (alternative to *.NET and *.UPI) +** *.REQ List of files to download from BBS +** *.PDQ Offline door configuration information +** (packet version 2 and earlier) +** *.OLC Offline door configuration information +** (packet version 3 and later) +** +** Misc: *.MSG Fido-style message header +** (used *only* in the *.NET structure) +** *.XTI Extended message packet information +** (not an official part of the Blue Wave +** packet specification; is used by the Blue +** Wave reader only) +** +** The door files (plus individual files for BBS bulletins) comprise a Blue +** Wave message packet, and the reader files (plus individual files for each +** message) comprise a Blue Wave reply packet. +** +** In order to cover ALL BASES, and to be able to say that you were warned, +** *ALL* unused fields should be set to ASCII NUL (0). Any future +** implementation of reserved fields will rely on the premise that the field +** will be 0 if not implemented! The same warning follows for BITMAPPED +** fields. If a bit is not implemented or is not used, TURN IT OFF (0). +** (Clearing an entire structure can be easily accomplished via the memset() +** function. Example: "memset(&ftirec, 0, sizeof(FTI_REC))".) +*/ + + +/*****************************************************************************/ +/* >>>>>>>>>>>>>>>>>>>>>>> DATA TYPE DEFINITIONS <<<<<<<<<<<<<<<<<<<<<<<<< */ +/*****************************************************************************/ + + +/* +** The data type definitions below help make these structures a little more +** universal between environments. The 8-bit, 16-bit, and 32-bit data types +** defined below can be used as-is with virtually all MS-DOS and OS/2 C/C++ +** compilers, but can be changed if necessary should your compiler define +** data types in a different fashion. (Note that the tCHAR and tINT types +** are currently not used; they are included simply for completeness.) +** +** If you are programming for a system that employs a CPU which stores multi- +** byte integers in a manner other than in Intel format (LSB-MSB, or "little +** endian"), simply #define BIG_ENDIAN before #including this header. As +** shown below, this will define the data types as arrays of bytes; the +** drawback is that *YOU* will have to write functions to convert the data, +** since the Blue Wave packet specification requires the data to be in Intel- +** style little-endian format. +** +** IMPORTANT NOTE ABOUT COMPILERS AND STRUCTURES: +** All structures *must* be "packed" (i.e., the compiler MUST NOT insert +** padding bytes between structure elements in order to force elements onto +** word boundaries). The Blue Wave products expect them to be packed; if +** they aren't, you're bound to get some *very* interesting results. +*/ + + + + +typedef signed char tCHAR; /* 8 bit signed values */ +typedef unsigned char tBYTE; /* 8 bit unsigned values */ +typedef signed short tINT; /* 16 bit signed values */ +typedef unsigned short tWORD; /* 16 bit unsigned values */ +typedef int tLONG; /* 32 bit signed values */ +typedef unsigned int tDWORD; /* 32 bit unsigned values */ + + +/*****************************************************************************/ +/* >>>>>>>>>>>>>>>>>>>>> DOOR DATA FILE STRUCTURES <<<<<<<<<<<<<<<<<<<<<<< */ +/*****************************************************************************/ + + +/* +** Name of file: *.INF +** +** Description: The *.INF file is the source of information for just about +** everything from the host BBS, as well as definitions for +** all of the message areas that are available to the user +** and their status (Local, EchoMail, NetMail, Read Only, +** etc.). +** +** File format: INF_HEADER { only included one time! } +** INF_AREA_INFO { repeated for as many msg bases } +** INF_AREA_INFO { as are available to the user } +** ... +*/ + +/* Bit-masks for INF_HEADER.UFLAGS field */ + +#define INF_HOTKEYS 0x0001 /* User uses "hotkeys" in door prompts */ +#define INF_XPERT 0x0002 /* Short menus displayed in door */ +#define INF_RES1 0x0004 /* RESERVED -- DO NOT USE! */ +#define INF_GRAPHICS 0x0008 /* Enable ANSI control sequences in door */ +#define INF_NOT_MY_MAIL 0x0010 /* Do not bundle mail from user */ +#define INF_EXT_INFO 0x0020 /* Download extended info with messages */ + /* (* VERSION 3 AND LATER ONLY *) */ +#define INF_NUMERIC_EXT 0x0040 /* Use numeric extensions on packets */ + /* (* VERSION 3 AND LATER ONLY *) */ + +/* Bit-masks for INF_HEADER.NETMAIL_FLAGS field */ + +#define INF_CAN_CRASH 0x0002 /* Allow Crash status */ +#define INF_CAN_ATTACH 0x0010 /* Allow File Attach messages */ +#define INF_CAN_KSENT 0x0080 /* Allow Kill/Sent status */ +#define INF_CAN_HOLD 0x0200 /* Allow Hold status */ +#define INF_CAN_IMM 0x0400 /* Allow Immediate status */ +#define INF_CAN_FREQ 0x0800 /* Allow File Request messages */ +#define INF_CAN_DIRECT 0x1000 /* Allow Direct status */ + +/* Bit-masks for INF_HEADER.CTRL_FLAGS field */ + +#define INF_NO_CONFIG 0x0001 /* Do not allow offline configuration */ +#define INF_NO_FREQ 0x0002 /* Do not allow file requesting */ + +/* Values for INF_HEADER.FILE_LIST_TYPE field */ + +#define INF_FLIST_NONE 0 /* Door does not generate a list file */ +#define INF_FLIST_TEXT 1 /* Door generates plain text list file */ +#define INF_FLIST_ANSI 2 /* Door generates ANSI list file */ + +typedef struct /* INF_HEADER */ +{ + tBYTE ver; /* Packet version type (currently 2) */ + tBYTE readerfiles[5][13]; /* Files to be displayed by reader */ + tBYTE regnum[9]; /* User's registration number */ + tBYTE mashtype; /* Currently unused (door fills with 0) */ + /* Reserved for Blue Wave reader to store */ + /* the compression type the packet uses. */ + tBYTE loginname[43]; /* Name user types at BBS login */ + tBYTE aliasname[43]; /* User's "other" name */ + tBYTE password[21]; /* Password */ + /* All bytes should be the actually ASCII */ + /* value plus 10. Lame security, yes, */ + /* but it does prevent "TYPE *.INF" from */ + /* showing the password. */ + tBYTE passtype; /* Password type */ + /* 0=none 1=door 2=reader 3=both */ + tWORD zone; /* Main network address of host BBS */ + tWORD net; /* (zone:net/node.point) */ + tWORD node; + tWORD point; + tBYTE sysop[41]; /* Name of SysOp of host BBS */ + tWORD ctrl_flags; /* Flags to control reader capabilities */ + /* (* VERSION 3 AND LATER ONLY *) */ + tBYTE systemname[65]; /* Name of host BBS */ + tBYTE maxfreqs; /* Max number of file requests allowed */ + tWORD is_QWK; /* Whether *.INF belongs to a QWK packet */ + tBYTE obsolete2[4]; /* OBSOLETE -- DO NOT USE! */ + tWORD uflags; /* Bit-mapped door options/toggles */ + tBYTE keywords[10][21]; /* User's entire set of door keywords */ + tBYTE filters[10][21]; /* User's entire set of door filters */ + tBYTE macros[3][80]; /* User's door bundling command macros */ + tWORD netmail_flags; /* Bit-mapped NetMail options */ + tWORD credits; /* NetMail credits */ + tWORD debits; /* NetMail debits */ + tBYTE can_forward; /* 0=Message forwarding not allowed */ + tWORD inf_header_len; /* Size of INF_HEADER structure */ + tWORD inf_areainfo_len; /* Size of INF_AREA_INFO structure */ + tWORD mix_structlen; /* Size of MIX_REC structure */ + tWORD fti_structlen; /* Size of FTI_REC structure */ + tBYTE uses_upl_file; /* If this field is not zero, the door that */ + /* created this packet can receive reply */ + /* packets in the new *.UPL file format. */ + /* Otherwise, the old *.UPI and *.NET */ + /* files must be used. */ + tBYTE from_to_len; /* The maximum length of the FROM: and TO: */ + /* fields that the host BBS can support. */ + /* If this value is 0 or is greater than */ + /* 35, then 35 must be used (the upload */ + /* file formats only allow for a maximum */ + /* of 35 characters). */ + tBYTE subject_len; /* The maximum length of the SUBJECT: field */ + /* that the host BBS can support. If */ + /* this value is 0 or is greater than 71, */ + /* then 71 must be used (the upload file */ + /* formats only allow for a maximum of 71 */ + /* characters). */ + tBYTE packet_id[9]; /* Original root name of the mail packet, */ + /* as specified by the mail door. All */ + /* files in the packet that are created */ + /* by the mail door will use this root */ + /* name, as will the reader when creating */ + /* the upload files. Thus, even if the */ + /* packets themselves are renamed to */ + /* something completely different, the */ + /* mail doors and readers will still be */ + /* able to work with the proper files. */ + tBYTE file_list_type; /* New file listing type */ + /* (* VERSION 3 AND LATER ONLY *) */ + /* Specifies the type of new file list */ + /* that is generated by the door (see */ + /* INF_FLIST_xxx, above). This field is */ + /* intended for use with offline config. */ + tBYTE auto_macro[3]; /* Auto-macro indicator flags */ + /* (* VERSION 3 AND LATER ONLY *) */ + /* Specifies which macros are auto macros */ + /* (i.e. execute automatically after mail */ + /* is scanned). */ + tINT max_packet_size; /* Maximum size of uncompressed packet */ + /* (* VERSION 3 AND LATER ONLY *) */ + /* Specifies, in K, the maximum size of */ + /* an uncompressed mail packet. A value */ + /* of 0 indicates no maximum length. */ + /* This field is intended for use with */ + /* offline config. */ + tBYTE reserved[228]; /* RESERVED FOR FUTURE USE */ + /* This field MUST be filled with ASCII */ + /* NUL (0x00) characters in order for */ + /* future additional features to work */ + /* properly! */ +}__attribute__((packed)) +INF_HEADER; + +/* +** Notes about the INF_HEADER.XXXXX_LEN fields, above: +** +** Door authors should take the few extra lines of code to fill in the +** structure lengths defined above. Doing so will make the Blue Wave data +** structures extensible and adaptable to almost any kind of file change that +** may be required in the future. The readers that use this mail packet +** format should contain code to handle a structure length that is longer or +** shorter than they expect. +** +** Reader authors need to take the time to code for possible extensions to +** this file format. If the data fields are LONGER than expected, simply do +** a seek to move to the next record, and ignore the extra information. If +** the data fields are SHORTER than expected, a simple "Please upgrade your +** reader" should suffice. However, you should never encounter a +** record size smaller than the ones defined here. Any extra information +** that is sent in the packets probably would not be crucial, and you may be +** able to continue with reading the packet anyway. +** +** It should be noted that all current Blue Wave doors set these fields to 0, +** as this extensibility was not added until recently. If the structure +** sizes are 0, the reader will assume that all records are of the sizes +** defined here. (Blue Wave readers below version 2.10 do NOT allow for +** extensible data files. Version 2.10, and all subsequent versions, WILL +** handle them properly.) DO NOT EXTEND THESE STRUCTURE FORMATS WITHOUT +** NOTIFYING CUTTING EDGE COMPUTING FIRST! If the extended information will +** benefit programs/users, it will be officially added to the packet format. +** +** The original values for the INF_HEADER.XXXXX_LEN structures are as below, +** defined as macros which you can use in your programs. Remember, if the +** value in INF_HEADER.XXXXX_LEN is 0, you must use these values instead! +*/ + +#define ORIGINAL_INF_HEADER_LEN 1230 /* Original *.INF header len */ +#define ORIGINAL_INF_AREA_LEN 80 /* Original *.INF area rec len */ +#define ORIGINAL_MIX_STRUCT_LEN 14 /* Original *.MIX record len */ +#define ORIGINAL_FTI_STRUCT_LEN 186 /* Original *.FTI record len */ + +/* +** Below is some sample C code for reading in the variable length *.INF +** structure, which is the most "difficult" one to do. Note the sections of +** code which use the ORIGINAL_XXXXX_LEN macros; these are the sections that +** determine the proper structure length. (Comments are preceeded by "#" +** signs, since using C comment symbols would make most compilers think that +** nested comments are in use, a practice which normally is not allowed.) +** +** int read_inf_file(void) +** { +** INF_HEADER inf_header; +** INF_AREA_INFO inf_info; +** FILE *inf_file=NULL; +** tWORD record_num=0u; +** tWORD inf_header_slen, inf_area_slen; +** tLONG seek_pos=0L; +** +** inf_file = fopen("WILDBLUE.INF", "rb"); +** if (inf_file == NULL) +** return 0; +** +** fread(&inf_header, sizeof(INF_HEADER), 1, inf_file); +** puts(inf_header.loginname); +** puts(inf_header.aliasname); +** +** # Test and verify the validity of the structure lengths. +** +** if (inf_header.inf_header_len < ORIGINAL_INF_HEADER_LEN) +** inf_header_slen = ORIGINAL_INF_HEADER_LEN; +** else +** inf_header_slen = inf_header.inf_header_len; +** +** if (inf_header.inf_areainfo_len < ORIGINAL_INF_AREA_LEN) +** inf_area_slen = ORIGINAL_INF_AREA_LEN; +** else +** inf_area_slen = inf_header.inf_areainfo_len; +** +** # now, move to the END of the header, since it may be longer +** # than we expect it to be. Use fseek()... +** +** fseek(inf_file, (long)inf_header_slen, SEEK_SET); +** +** record_num = 0U; +** while(fread(&inf_info, sizeof(INF_AREA_INFO), 1, inf_file)) +** { +** puts(inf_info.title); +** record_num++; +** +** # we need to seek past the header, and then [record_num] +** # number of recs. +** +** seek_pos = (long)(inf_header_slen+(record_num*inf_area_slen)); +** fseek(inf_file, seek_pos, SEEK_SET); +** } +** +** fclose(inf_file); +** return 1; +** } +*/ + +/* Bit-masks for INF_AREA_INFO.AREA_FLAGS field */ + +#define INF_SCANNING 0x0001 /* On=User is active for area */ +#define INF_ALIAS_NAME 0x0002 /* On=Alias name, Off=Login name */ + /* If ON, use INF_HEADER.ALIASNAME when */ + /* addressing new mail or replies for the */ + /* message area. If OFF, the reader uses */ + /* the INF_HEADER.LOGINNAME for this */ + /* purpose. */ +#define INF_ANY_NAME 0x0004 /* On=Allow any name to be entered */ + /* If ON, any name can be entered in the */ + /* From: field when addressing new mail */ + /* or replies for the message area. If */ + /* OFF, the normal rules apply. */ +#define INF_ECHO 0x0008 /* On=Network mail, Off=Local mail */ +#define INF_NETMAIL 0x0010 /* On=E-mail, Off=Conference mail */ + /* Refer to the chart below (the values */ + /* for the NETWORK_TYPE field) for info */ + /* on how these two flags should be set */ + /* for message areas. */ +#define INF_POST 0x0020 /* On=User can post, Off=User CANNOT post */ +#define INF_NO_PRIVATE 0x0040 /* On=Private messages are NOT allowed */ +#define INF_NO_PUBLIC 0x0080 /* On=Public messages are NOT allowed */ +#define INF_NO_TAGLINE 0x0100 /* On=Taglines are not allowed */ +#define INF_NO_HIGHBIT 0x0200 /* On=ASCII 1-127 only, Off=ASCII 1-255 */ + /* If ON, only ASCII values 1 to 127 are */ + /* allowed in messages. If OFF, all */ + /* values from 1 to 255 are allowed. Due */ + /* to the fact that ASCII value 0 is used */ + /* in C as a string terminator, the value */ + /* 0 should not be allowed in messages at */ + /* all. */ +#define INF_NOECHO 0x0400 /* On=User can prevent messages from being */ + /* sent through the network */ +#define INF_HASFILE 0x0800 /* On=User can attach files to messages */ +#define INF_PERSONAL 0x1000 /* On=User is downloading only personal */ + /* msgs in this message area. The flag */ + /* INF_SCANNING also needs to be ON. */ + /* (* VERSION 3 AND LATER ONLY *) */ +#define INF_TO_ALL 0x2000 /* On=User is downloading messages to "All" */ + /* and personal messages only in this */ + /* area. The flag INF_SCANNING also */ + /* needs to be ON. INF_PERSONAL should */ + /* *not* be set, as this flag implies the */ + /* downloading of personal messages also. */ + /* (* VERSION 3 AND LATER ONLY *) */ + +/* Values for INF_AREA_INFO.NETWORK_TYPE field */ + +#define INF_NET_FIDONET 0 /* FidoNet-style E-mail and conferences */ + /* Local = INF_ECHO=off, NETMAIL=off */ + /* EchoMail = INF_ECHO=on, NETMAIL=off */ + /* GroupMail = INF_ECHO=on, NETMAIL=off */ + /* NetMail = INF_ECHO=on, NETMAIL=on */ +#define INF_NET_INTERNET 1 /* Internet E-mail and Usenet newsgroups */ + /* Local = INF_ECHO=off, NETMAIL=off */ + /* Newsgroup = INF_ECHO=on, NETMAIL=off */ + /* E-mail = INF_ECHO=on, NETMAIL=on */ + +typedef struct /* INF_AREA_INFO */ +{ + tBYTE areanum[6]; /* Area number this record corresponds to */ + tBYTE echotag[21]; /* Area tag name (*.BRD name for Telegard) */ + tBYTE title[50]; /* Area description/title */ + tWORD area_flags; /* Bit-mapped area options */ + tBYTE network_type; /* Network mail type (see above) */ +}__attribute__((packed)) +INF_AREA_INFO; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.MIX +** +** Description: The *.MIX file is a very small file, with one record for +** every message area that was scanned. It contains the +** information to get into the *.FTI file. +** +** File format: MIX_REC { repeated for each message area scanned } +** MIX_REC +** ... +*/ + +typedef struct /* MIX_REC */ +{ + tBYTE areanum[6]; /* Area number this record corresponds to */ + /* This is the ASCII representation of the */ + /* actual area number shown on the host BBS. */ + tWORD totmsgs; /* Total number of messages for this area */ + tWORD numpers; /* Total number of personal messages in this area */ + tLONG msghptr; /* Pointer to first message header in *.FTI file */ +}__attribute__((packed)) +MIX_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.FTI +** +** Description: The *.FTI file contains the information for each message +** in the packet. Each record includes all of the +** information about the message, including the pointer to +** the actual message text in the *.DAT file. +** +** NOTE: Messages in the *.FTI file will ALWAYS be in area +** number order. That is to say, if the MIX_REC +** indicates there are 100 messages for this area, +** all 100 messages will follow in sequential order. +** +** File format: FTI_REC { repeated for as many messages } +** FTI_REC { as obtained from the host BBS } +** ... +*/ + +/* Bit-masks for FTI_REC.FLAGS field */ + +#define FTI_MSGPRIVATE 0x0001 /* Private = For addressee ONLY */ +#define FTI_MSGCRASH 0x0002 /* Crash = High priority mail */ +#define FTI_MSGREAD 0x0004 /* Read = Message read by addressee */ +#define FTI_MSGSENT 0x0008 /* Sent = Message sent */ +#define FTI_MSGFILE 0x0010 /* File Attach = Send file(s) */ +#define FTI_MSGFWD 0x0020 /* Forward = Message to/from others */ +#define FTI_MSGORPHAN 0x0040 /* Orphan = Message destination unknown */ +#define FTI_MSGKILL 0x0080 /* Kill/Sent = Delete after sending */ +#define FTI_MSGLOCAL 0x0100 /* Local = Message originated here */ +#define FTI_MSGHOLD 0x0200 /* Hold = Hold for pickup, don't send */ +#define FTI_MSGIMMEDIATE 0x0400 /* Immediate = Send message NOW */ +#define FTI_MSGFRQ 0x0800 /* File Request = Request file(s) */ +#define FTI_MSGDIRECT 0x1000 /* Direct = Send direct, no routing */ +#define FTI_MSGUNUSED1 0x2000 /* */ +#define FTI_MSGUNUSED2 0x4000 /* */ +#define FTI_MSGURQ 0x8000 /* Update Request = Req updated file(s) */ + +typedef struct /* FTI_REC */ +{ + tBYTE from[36]; /* Person message is from */ + tBYTE to[36]; /* Person message is to */ + tBYTE subject[72]; /* Subject/title of message */ + tBYTE date[20]; /* Origin date of message */ + /* Depending on the host BBS's date storage */ + /* format, the EXACT format of this field */ + /* will change. Some will take all 19 bytes, */ + /* others may take only 10. */ + tWORD msgnum; /* Number of THIS message on BBS */ + tWORD replyto; /* "This is a reply to #xx" */ + /* Not used for every message. When non- */ + /* zero, there is a previous message in */ + /* the thread. */ + tWORD replyat; /* "There is a reply at #xx" */ + /* Not used for every message. When non- */ + /* zero, there is a reply to this message. */ + tLONG msgptr; /* Offset to start of message in *.DAT file */ + /* Seek to this exact offset in the *.DAT */ + /* file, then read "msglength" bytes from */ + /* the file to load the entire message text. */ + tLONG msglength; /* Length of message text (in bytes) */ + tWORD flags; /* Bit-mapped message status flags */ + tWORD orig_zone; /* Origin address of message */ + /* These three fields will most likely be 0, */ + /* unless the current message belongs to a */ + /* NetMail message base. */ + tWORD orig_net; + tWORD orig_node; +}__attribute__((packed)) +FTI_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.DAT +** +** Description: The *.DAT file is an unstructured file which contains the +** text of every message obtained from the host BBS. +** Valid messages begin with an ASCII space (0x20) character +** (which is NOT to be considered part of the message!) +** followed by zero or more bytes which constitute the +** message text. The pointer to the text for each message is +** stored in FTI_REC.MSGPTR, and the length of the text for +** each message is stored in FTI_REC.MSGLENGTH. +** +** File format: Unstructured +*/ + + +/*****************************************************************************/ +/* >>>>>>>>>>>>>>>>> MISCELLANEOUS DATA FILE STRUCTURES <<<<<<<<<<<<<<<<<< */ +/*****************************************************************************/ + + +/* +** Name of file: *.MSG +** +** Description: The Fido *.MSG message (named for the BBS program on which +** it originated) has become a de-facto standard among BBS +** implementations, due to the sheer number of utilities +** available that operate with *.MSG messages. It is as +** close to a universal message format as one can get in +** FidoNet (and FidoNet-style networks), and is the reason +** why it is used here (well, the *.MSG header, anyway). +** +** NOTE: Most of the fields in the FTI_REC structure (shown +** above) correspond to similar fields in MSG_REC. +** This was done deliberately, in order to make +** *.FTI file processing a little more intuitive for +** programmers. Also note that MSG_REC is only used +** by the NET_REC structure, which will soon become +** obsolete (replaced by UPL_REC). +** +** File format: MSG_REC { only included one time! } +** message text { text can be terminated by an ASCII NUL } +** { character (0x00), or by an ASCII CR, } +** { LF, NUL (0x0D 0x0A 0x00) sequence } +*/ + +/* Bit-masks for MSG_REC.ATTR field */ + +#define MSG_NET_PRIVATE 0x0001 /* Private */ +#define MSG_NET_CRASH 0x0002 /* Crash mail */ +#define MSG_NET_RECEIVED 0x0004 /* Received */ +#define MSG_NET_SENT 0x0008 /* Sent */ +#define MSG_NET_FATTACH 0x0010 /* File attached */ +#define MSG_NET_INTRANSIT 0x0020 /* In-transit */ +#define MSG_NET_ORPHAN 0x0040 /* Orphaned */ +#define MSG_NET_KILL 0x0080 /* Kill after sending */ +#define MSG_NET_LOCAL 0x0100 /* Local message */ +#define MSG_NET_HOLD 0x0200 /* Hold for pickup */ +#define MSG_NET_RESERVED 0x0400 /* RESERVED */ +#define MSG_NET_FREQ 0x0800 /* File request */ +#define MSG_NET_RREQ 0x1000 /* Return receipt request */ +#define MSG_NET_RECEIPT 0x2000 /* Return receipt message */ +#define MSG_NET_AREQ 0x4000 /* Audit request */ +#define MSG_NET_FUREQ 0x8000 /* File update request */ + +typedef struct /* MSG_REC (will soon be obsolete) */ +{ + tBYTE from[36]; /* Person message is from */ + tBYTE to[36]; /* Person message is to */ + tBYTE subj[72]; /* Subject/title of message */ + tBYTE date[20]; /* Creation date/time */ + /* This date/time is usually in either of the */ + /* Fido-sanctioned formats "DD MMM YY HH:MM:SS" */ + /* or "WWW DD MMM YY HH:MM", but due to the */ + /* chaotic nature of FidoNet-compatible software, */ + /* this CANNOT be relied upon! */ + tWORD times; /* Number of times read (fairly obsolete) */ + tWORD dest; /* Destination node (of net/node) */ + tWORD orig; /* Origin node (of net/node) */ + tWORD cost; /* Cost of sending message (usually in US cents) */ + tWORD orig_net; /* Origin net (of net/node) */ + tWORD destnet; /* Destination net (of net/node) */ + tLONG unused1; /* Undefined */ + tLONG unused2; /* Some software (Opus and Maximus, for example) */ + /* uses these fields to store the sent/received */ + /* date/time as bit-packed fields, using the same */ + /* format used in MS-DOS directory entries. */ + tWORD reply; /* Message # that this message replies to */ + tWORD attr; /* Message attributes and behavior flags */ + tWORD up; /* Message # that replies to this message */ +}__attribute__((packed)) +MSG_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.XTI +** +** Description: The *.XTI file contains extended information for each +** message in the packet. The number of records in the *.XTI +** file will always equal the number of messages in the +** packet, with each record corresponding to a record in the +** *.FTI file (i.e. record #1 in the *.XTI file corresponds +** to record #1 in the *.FTI file, and so on). +** +** NOTE: This file is currently created ONLY by the Blue +** Wave reader, and is not a part of the official +** Blue Wave packet specification; it is merely +** documented here for third party programmers to use +** if they so desire. How other readers store which +** messages have been read/replied-to/marked is left +** as an option to be implemented by the individual +** reader authors. You may use this method if you so +** desire; however, PLEASE do not name any external +** files not conforming to this specification as +** .XTI, due to the fact that the Blue +** Wave reader will expect the file to be in the +** format described. If it's not in the expected +** format, things will get interesting. :-) +** +** File format: XTI_REC { repeated for as many messages } +** XTI_REC { as obtained from the host BBS } +** ... +*/ + +/* Bit-masks for XTI_REC.FLAGS field */ + +#define XTI_HAS_READ 0x01 /* Message has been read */ +#define XTI_HAS_REPLIED 0x02 /* Message has been replied to */ +#define XTI_IS_PERSONAL 0x04 /* Message is personal */ +#define XTI_IS_TAGGED 0x08 /* Message has been 'tagged' */ +#define XTI_HAS_SAVED 0x10 /* Message has been saved */ +#define XTI_HAS_PRINTED 0x20 /* Message has been printed */ + +/* Bit-masks for XTI_REC.MARKS field */ + +#define XTI_MARK_SAVE 0x01 /* Message marked for saving */ +#define XTI_MARK_REPLY 0x02 /* Message marked for replying */ +#define XTI_MARK_PRINT 0x04 /* Message marked for printing */ +#define XTI_MARK_DELETE 0x08 /* Message marked for deletion */ + +typedef struct /* XTI_REC */ +{ + tBYTE flags; /* Bit-mapped message flags */ + tBYTE marks; /* Bit-mapped message markers */ +}__attribute__((packed)) +XTI_REC; + + +/*****************************************************************************/ +/* >>>>>>>>>>>>>>>>>>>> READER DATA FILE STRUCTURES <<<<<<<<<<<<<<<<<<<<<< */ +/*****************************************************************************/ + + +/* +** Name of file: *.NET +** +** Description: The *.NET file is created ONLY when there is NetMail to be +** sent. It contains the FULL header of the Fido-style *.MSG +** structure plus the fields defined below (which aren't part +** of the standard *.MSG structure yet required by the door). +** +** NOTE: Readers should only generate a *.NET file if +** INF_HEADER.USES_UPL_FILE is not set *AND* the +** mail packet format is version 2 or earlier. +** Doors should process *.NET files *ONLY* in cases +** where a *.UPL file is not present. +** +** File format: NET_REC { repeated for as many NetMail } +** NET_REC { messages as exist in the packet } +** ... +*/ + +typedef struct /* NET_REC */ +{ + MSG_REC msg; /* The Fido-style *.MSG header */ + tBYTE fname[13]; /* Filename the message text is in */ + tBYTE echotag[21]; /* NetMail area tag (*.BRD name for Telegard) */ + tWORD zone; /* Destination zone (of zone:net/node.point) */ + tWORD point; /* Destination point (of zone:net/node.point) */ + tLONG unix_date; /* Date/time of message */ + /* This Unix-style date/time value (number */ + /* of seconds since 01/01/70) is converted */ + /* to the date/time storage method used by */ + /* the host BBS. */ +}__attribute__((packed)) +NET_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.UPI +** +** Description: The *.UPI file contains the information for each message +** in the reply packet, as well as information on the reader +** version and registration numbers. Each record includes +** all of the information about the message. +** +** NOTE: Readers should only generate a *.UPI file if +** INF_HEADER.USES_UPL_FILE is not set *AND* the +** mail packet format is version 2 or earlier. +** Doors should process *.UPI files *ONLY* in cases +** where a *.UPL file is not present. +** +** File format: UPI_HEADER { only included one time! } +** UPI_REC { repeated for as many msg bases } +** UPI_REC { as are available to the user } +** ... +*/ + +typedef struct /* UPI_HEADER */ +{ + tBYTE regnum[9]; /* Reader registration number */ + tBYTE vernum[13]; /* Reader version number */ + /* All bytes should be the actually ASCII */ + /* value plus 10. Lame security, yes, but it */ + /* does prevent "TYPE *.UPI" from showing the */ + /* version number. */ + tBYTE future[33]; /* RESERVED FOR FUTURE USE */ +#ifdef PAD_SIZES_EVEN + tBYTE evenpad; /* If your compiler pads structures out to even */ + /* numbered sizes, define PAD_SIZES_EVEN */ + /* before including this header. When the */ + /* *.UPI file is written, be sure to write */ + /* sizeof(UPI_HEADER) - 1 bytes, otherwise */ + /* your compiler may insert an extra byte not */ + /* explicitly specified here. */ +#endif +}__attribute__((packed)) +UPI_HEADER; + +/* Bit-masks for UPI_REC.FLAGS field */ + +#define UPI_RES1 0x01 /* RESERVED FOR FUTURE USE */ +#define UPI_RES2 0x02 /* RESERVED FOR FUTURE USE */ +#define UPI_RES3 0x04 /* RESERVED FOR FUTURE USE */ +#define UPI_RES4 0x08 /* RESERVED FOR FUTURE USE */ +#define UPI_RES5 0x10 /* RESERVED FOR FUTURE USE */ +#define UPI_RES6 0x20 /* RESERVED FOR FUTURE USE */ +#define UPI_PRIVATE 0x40 /* Message is PRIVATE */ +#define UPI_NO_ECHO 0x80 /* Message is NOT to be echoed */ + /* This feature is not yet implemented in */ + /* the Blue Wave reader or doors, as none */ + /* of the currently supported BBS software */ + /* has support for this feature. */ + +typedef struct /* UPI_REC */ +{ + tBYTE from[36]; /* Person message is from */ + tBYTE to[36]; /* Person message is to */ + tBYTE subj[72]; /* Subject/title of message */ + tLONG unix_date; /* Date/time of message */ + /* This Unix-style date/time value (number */ + /* of seconds since 01/01/70) is converted */ + /* to the date/time storage method used by */ + /* the host BBS. */ + tBYTE fname[13]; /* Filename the message text is in */ + tBYTE echotag[21]; /* Area tag name (*.BRD name for Telegard) */ + tBYTE flags; /* Bit-mapped flags */ + tBYTE reedit; /* INTERNAL USE ONLY! */ + /* This flag is used internally by the Blue */ + /* Wave reader. Doors should ignore this */ + /* field during reply packet processing. */ +}__attribute__((packed)) +UPI_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.UPL +** +** Description: The *.UPL file contains the information for each message +** in the reply packet, as well as information on the reader +** version and registration numbers. Each record includes +** all of the information about the message. +** +** NOTE: Readers should only generate a *.UPL file if +** INF_HEADER.USES_UPL_FILE is set *AND/OR* the mail +** packet format is version 3 or later. Doors should +** process *.UPL files in all cases where one is +** present. +** +** File format: UPL_HEADER { only included one time! } +** UPL_REC { repeated for as many messages } +** UPL_REC { as are included in the packet } +** ... +*/ + +typedef struct /* UPL_HEADER */ +{ + tBYTE regnum[10]; /* Reader registration number (if desired) */ + tBYTE vernum[20]; /* Reader version number as a string. */ + /* All bytes should be the actually ASCII */ + /* value plus 10. Lame security, yes, but it */ + /* does prevent "TYPE *.UPL" from showing the */ + /* version number. */ + /* Examples: "2.10a Beta" */ + /* "2.11" */ + tBYTE reader_major; /* Major version of the reader (number to the */ + /* left of the decimal point) */ + tBYTE reader_minor; /* Minor version of the reader (number to the */ + /* right of the decimal point) */ + tBYTE reader_name[80]; /* String containing name of the reader, such */ + /* as "The Blue Wave Offline Mail Reader". */ + /* This is provided for door programmers that */ + /* wish to display the name of the reader */ + /* that created the reply packet. (Filling */ + /* it is mandatory but using it is optional.) */ + tWORD upl_header_len; /* Size of UPL_HEADER structure */ + tWORD upl_rec_len; /* Size of UPL_REC structure */ + /* NOTE: Refer to the INF_HEADER section for */ + /* more information on using the size */ + /* fields. */ + tBYTE loginname[44]; /* Name found in INF_HEADER.LOGINNAME. This is */ + /* provided for door authors as a security */ + /* measure to implement as they wish. */ + tBYTE aliasname[44]; /* Name found in INF_HEADER.ALIASNAME */ + tBYTE reader_tear[16]; /* String containing abbreviated name of the */ + /* reader, such as "Blue Wave", "Q-Blue", */ + /* "Wave Rider", etc. This is provided for */ + /* doors programmers that wish to add to the */ + /* tear line the name of the reader that */ + /* created the reply packet. (Filling it is */ + /* mandatory but using it is optional.) If */ + /* this field is blank, the tear line to be */ + /* generated is left to the discretion of the */ + /* door author. */ + tBYTE compress_type; /* Compression type required for mail packet */ + /* The Blue Wave reader uses this internally */ + /* to store the compression type required for */ + /* this particular mail packet. */ + tBYTE flags; /* Reader processing flags */ + /* The Blue Wave reader uses this internally */ + /* to store flags required for later */ + /* processing. */ + /* 0x01 = Was a .QWK packet. */ + /* 0x02 = Host requires a *.UPI file */ + tBYTE not_registered; /* Reader is not registered to user */ + /* If this byte is set to a non-zero value, */ + /* the Blue Wave doors will assume that the */ + /* user's reader was not registered, and will */ + /* place "[NR]" at the end of the tear line. */ + /* Third-party doors may use this flag for */ + /* the same purpose; its use is optional by */ + /* mail readers (especially if you don't care */ + /* whether or not "[NR]" shows up on the tear */ + /* line ). */ + tBYTE pad[33]; /* RESERVED FOR FUTURE USE, and to pad struct */ + /* out to a 'nice' 256 bytes */ +}__attribute__((packed)) +UPL_HEADER; + +/* Bit-masks for UPL_REC.MSG_ATTR field */ + +#define UPL_INACTIVE 0x0001 /* Message is INACTIVE */ + /* Doors should NOT attempt to import this */ + /* message. */ +#define UPL_PRIVATE 0x0002 /* Message is PRIVATE */ +#define UPL_NO_ECHO 0x0004 /* Message is NOT to be echoed */ + /* This feature is not yet implemented in */ + /* the Blue Wave reader or doors, as none */ + /* of the currently supported BBS software */ + /* has support for this feature. */ +#define UPL_HAS_FILE 0x0008 /* Message has file "attached" to it */ + /* It is up to the door to check the */ + /* validity of this flag. If the file is */ + /* contained in the mail packet, great. */ + /* If not, the door should probably prompt */ + /* the user to begin uploading the file */ + /* after importing the messages. (Not yet */ + /* implemented in the Blue Wave reader.) */ +#define UPL_NETMAIL 0x0010 /* Message is network mail */ + /* Indicates NetMail/E-mail message. The */ + /* NETWORK_TYPE field (see below) will */ + /* indicate which fields should be used */ + /* for addressing the message. */ +#define UPL_IS_REPLY 0x0020 /* Indicates that the message is a reply to */ + /* an existing message, rather than being */ + /* a completely new message. */ +#define UPL_MRES7 0x0040 /* RESERVED FOR FUTURE USE */ +#define UPL_MRES8 0x0080 /* RESERVED FOR FUTURE USE */ + /* All of the other 8 bits of this field are */ + /* also reserved for future use. This */ + /* should provide for plenty of expansion */ + /* for future development. */ + +/* Bit-masks for UPL_REC.NETMAIL_ATTR field */ + +#define UPL_NRES1 0x0001 /* RESERVED FOR FUTURE USE */ +#define UPL_NETCRASH 0x0002 /* Crash = High priority mail */ +#define UPL_NRES2 0x0004 /* RESERVED FOR FUTURE USE */ +#define UPL_NRES3 0x0008 /* RESERVED FOR FUTURE USE */ +#define UPL_NETFILE 0x0010 /* File Attach = Send file(s) listed */ + /* in Subject field */ +#define UPL_NRES4 0x0020 /* RESERVED FOR FUTURE USE */ +#define UPL_NRES5 0x0040 /* RESERVED FOR FUTURE USE */ +#define UPL_NETKILL 0x0080 /* Kill/Sent = Delete after sending */ +#define UPL_NETLOCAL 0x0100 /* Local = Message originated here */ +#define UPL_NETHOLD 0x0200 /* Hold = Hold for pickup, do not send */ +#define UPL_NETIMMEDIATE 0x0400 /* Immediate = Send message NOW */ +#define UPL_NETFRQ 0x0800 /* File Request = Request file(s) */ + /* listed in Subject field */ +#define UPL_NETDIRECT 0x1000 /* Direct = Send direct, no routing */ +#define UPL_NRES6 0x2000 /* RESERVED FOR FUTURE USE */ +#define UPL_NRES7 0x4000 /* RESERVED FOR FUTURE USE */ +#define UPL_NETURQ 0x8000 /* Update Request = Request updated */ + /* file(s) listed in Subject field */ + +/* Values for UPL_REC.NETWORK_TYPE field */ + +#define UPL_NET_FIDONET 0 /* FidoNet-style E-mail and conferences */ + /* UPL_NETMAIL=off - Local, Echo, Group */ + /* UPL_NETMAIL=on - NetMail */ +#define UPL_NET_INTERNET 1 /* Internet E-mail and Usenet newsgroups */ + /* UPL_NETMAIL=off - Local, Newsgroup */ + /* UPL_NETMAIL=on - E-mail */ + +typedef struct /* UPL_REC */ +{ + tBYTE from[36]; /* Person message is from */ + /* NOTE: Doors should validate this field! */ + tBYTE to[36]; /* Person message is to (non-Internet) */ + /* For Internet E-mail, the NET_DEST field */ + /* should be used to store the destination */ + /* name/address, leaving this field blank. */ + /* For Usenet newsgroups, this field should be */ + /* left blank, as newsgroups don't use a "To:" */ + /* field. */ + tBYTE subj[72]; /* Subject/Title of message */ + tWORD destzone; /* Destination address (FidoNet only) */ + /* If the message is not a FidoNet NetMail */ + /* message, this field (and the subsequent */ + /* three fields as well) should be set to */ + /* zero. */ + tWORD destnet; + tWORD destnode; + tWORD destpoint; + tWORD msg_attr; /* Bit-mapped message attributes */ + tWORD netmail_attr; /* Bit-mapped NetMail attributes (FidoNet only) */ + /* If the message is not a FidoNet NetMail */ + /* message, this field should not be used. */ + tLONG unix_date; /* Date/time of message */ + /* This Unix-style date/time value (number */ + /* of seconds since 01/01/70) is converted to */ + /* the date/time storage method used by the */ + /* host BBS. */ + tDWORD replyto; /* This unsigned long word stores the message # */ + /* that this message is a reply to. This */ + /* should be the same as FTI.MSGNUM. Note, */ + /* however, that FTI.MSGNUM is a word. C */ + /* programmers especially will need to */ + /* properly typecast the value (i.e. */ + /* upl.replyto=(tDWORD)fti.msgnum). As */ + /* messaging/BBS systems become more complex, */ + /* FTI.MSGNUM may become obsolete, and a */ + /* tDWORD variable may be used in its place. */ + tBYTE filename[13]; /* Filename the message text is in */ + /* If this file does not exist in the upload */ + /* packet then doors should consider this an */ + /* invalid record. */ + tBYTE echotag[21]; /* Area tag the message goes in */ + /* This must correspond exactly to the */ + /* INF_AREA_INFO.ECHOTAG field for the message */ + /* area this message belongs to. Simple area */ + /* number matching has proven not to work */ + /* simply because sysops are finicky people, */ + /* and seem to constantly renumber/change the */ + /* message area numbers on the host BBS. */ + /* Using an echotag helps to alleviate this */ + /* problem. C_ECHO will be C_ECHO on the BBS, */ + /* whether it is msg area 17 on the host BBS */ + /* or whether it is area 207. Doors should do */ + /* a case-INSENSITIVE compare on this field to */ + /* find where the message belongs. */ + tWORD area_flags; /* The Blue Wave Offline Mail Reader uses this */ + /* word internally to store the same value as */ + /* in INF_AREA_INFO.AREA_FLAGS. The purpose */ + /* of this word is to hold the original */ + /* information about the message area so that */ + /* later message editing processes can be */ + /* controlled properly. For example, if a */ + /* user later wanted to edit this message, the */ + /* reader would know instantly whether this is */ + /* a NETMAIL area, whether PVT messages are */ + /* allowed, etc. This allows re-editing of */ + /* the message, even when there is not a */ + /* corresponding *.INF file laying around, or */ + /* the area is not listed in the *.INF file */ + /* you currently have to work with. DOOR */ + /* AUTHORS SHOULD IGNORE THIS FIELD WHEN */ + /* IMPORTING MESSAGES! */ + tBYTE f_attach[13]; /* If the UPL_HAS_FILE flag is set, this field */ + /* will contain the file name that is attached */ + /* to the message. */ + tBYTE user_area[6]; /* User-defined storage. Doors should ignore */ + /* this field, and reader authors should feel */ + /* free to utilize this field for their own */ + /* internal use, if necessary. */ + tBYTE network_type; /* Indicates the network type. This field must */ + /* hold the same value as the NETWORK_TYPE */ + /* field in INF_AREA_INFO, allowing doors and */ + /* readers to properly handle the message. */ + /* (Values duplicated as UPL_NET_xxx, above.) */ + /* For FidoNet NetMail and Internet E-mail, it */ + /* also indicates which fields should be used */ + /* for addressing and status information (as */ + /* indicated in comments above and below). */ + tBYTE net_dest[100]; /* Network destination address (non-FidoNet) */ + /* Internet E-mail messages should use this */ + /* field to store the destination address. */ +}__attribute__((packed)) +UPL_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.REQ +** +** Description: The *.REQ file is simply a list of filenames the user +** wants to request from the host BBS. Wildcard characters +** ("*" and "?" under MS-DOS) are allowed, but are not +** guaranteed to produce accurate results on all door +** implementations. +** +** NOTE: Current Blue Wave doors do not accept wildcard +** characters in filenames, and will consider any +** filenames which contain them as being invalid. +** Additionally, if there are more than 10 entries in +** the *.REQ file, current Blue Wave doors will read +** the first 10 and discard the rest. These are +** limitations of the Blue Wave doors, not of the +** Blue Wave format itself. +** +** File format: REQ_REC { repeated for as many files as } +** REQ_REC { requested from the host BBS } +** ... +*/ + +typedef struct /* REQ_REC */ +{ + tBYTE filename[13]; /* Name of file to request */ +}__attribute__((packed)) +REQ_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.PDQ +** +** Description: The *.PDQ file contains the information used for the +** offline configuration feature of the mail door. After the +** header is a series of records which indicate the message +** areas to enable for scanning the next time a mail packet +** is requested. +** +** NOTE: Readers should generate a *.PDQ file *ONLY* if +** the mail packet format is version 2 or earlier; +** otherwise, a *.OLC file should be generated. +** Doors should process *.PDQ files *ONLY* in cases +** where a *.OLC file is not present. +** +** If the AREA_CHANGES flag in PDQ_HEADER.FLAGS is +** set, the door should process the offline +** configuration as well as changes to the list of +** areas the user wants to download. In the Blue +** Wave door, this is done by first turning OFF all +** message areas that were active, then turning ON +** the ones specified in the *.PDQ file. This seems +** to be the simplest, most straight-forward method +** of accomplishing this task, though other, more +** complex schemes could easily have been devised. +** +** File format: PDQ_HEADER { only included one time! +** PDQ_REC { repeated for as many message areas } +** PDQ_REC { as the user wishes to enable } +** ... +*/ + +/* Bit-masks for PDQ_HEADER.FLAGS field */ + +#define PDQ_HOTKEYS 0x0001 /* Toggle "hotkeys" in prompts */ +#define PDQ_XPERT 0x0002 /* Toggle expert mode (menu displays) */ +#define PDQ_AREA_CHANGES 0x0004 /* Change active message areas */ +#define PDQ_GRAPHICS 0x0008 /* Toggle IBM 8-bit ASCII characters */ +#define PDQ_NOT_MY_MAIL 0x0010 /* Toggle bundling mail from user */ + +typedef struct /* PDQ_HEADER */ +{ + tBYTE keywords[10][21]; /* User's entire set of door keywords */ + tBYTE filters[10][21]; /* User's entire set of door filters */ + tBYTE macros[3][78]; /* User's door bundling command macros */ + tBYTE password[21]; /* Password */ + tBYTE passtype; /* Password type */ + /* 0=none 1=door 2=reader 3=both */ + tWORD flags; /* Bit-mapped flags */ +}__attribute__((packed)) +PDQ_HEADER; + +typedef struct /* PDQ_REC */ +{ + tBYTE echotag[21]; /* Echo tag of message area to activate */ + /* With Telegard systems, this should */ + /* be the name of the *.BRD file, rather */ + /* than the actual echo tag. */ +}__attribute__((packed)) +PDQ_REC; + +/*---------------------------------------------------------------------------*/ + +/* +** Name of file: *.OLC +** +** Description: The *.OLC file contains the information used for the +** offline configuration feature of the mail door. +** +** NOTE: Readers should generate a *.OLC file *ONLY* if +** the mail packet format is version 3 or later; +** otherwise, a *.PDQ file should be generated. +** Doors should process *.OLC files in all cases +** where one is present. +** +** File format: ASCII text (lines terminated with CRLF) +** +** Comments: Refer to the Blue Wave Developer's Kit documentation +** for details on the exact format of the *.OLC file. +*/ + +/*---------------------------------------------------------------------------*/ + +#endif /* __BLUEWAVE_H */ + diff --git a/config_default/bbs.ini b/config_default/bbs.ini index 2d0a972..f70202f 100644 --- a/config_default/bbs.ini +++ b/config_default/bbs.ini @@ -18,6 +18,11 @@ Enable SSH = false SSH Port = 2024 SSH DSA Key = /home/andrew/MagickaBBS/keys/ssh_host_dsa_key SSH RSA Key = /home/andrew/MagickaBBS/keys/ssh_host_rsa_key +Main AKA = 1:2/3.4 +QWK Name = MAGICKA +QWK Max Messages = 5000; +ZIP Command = zip -j *a *f +UNZIP Command = unzip -j *a -d *d [paths] WWW Path = /home/andrew/MagickaBBS/www diff --git a/files.c b/files.c index 27201d7..ede50d6 100644 --- a/files.c +++ b/files.c @@ -181,11 +181,16 @@ int doIO(ZModem *zm) { return done; } -void upload_zmodem(struct user_record *user) { +void upload_zmodem(struct user_record *user, char *upload_p) { ZModem zm; + struct termios oldit; + struct termios oldot; + if (sshBBS) { + ttySetRaw(STDIN_FILENO, &oldit); + ttySetRaw(STDOUT_FILENO, &oldot); + } - - upload_path = conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->upload_path; + upload_path = upload_p; zm.attn = NULL; zm.windowsize = 0; @@ -206,6 +211,10 @@ void upload_zmodem(struct user_record *user) { ZmodemRInit(&zm); doIO(&zm); + if (sshBBS) { + tcsetattr(STDIN_FILENO, TCSANOW, &oldit); + tcsetattr(STDOUT_FILENO, TCSANOW, &oldot); + } } void upload(struct user_record *user) { @@ -226,17 +235,9 @@ void upload(struct user_record *user) { int rc; struct stat s; char *err_msg = NULL; - struct termios oldit; - struct termios oldot; - if (sshBBS) { - ttySetRaw(STDIN_FILENO, &oldit); - ttySetRaw(STDOUT_FILENO, &oldot); - } - upload_zmodem(user); - if (sshBBS) { - tcsetattr(STDIN_FILENO, TCSANOW, &oldit); - tcsetattr(STDOUT_FILENO, TCSANOW, &oldot); - } + + upload_zmodem(user, conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->upload_path); + s_printf("\r\nPlease enter a description:\r\n"); buffer[0] = '\0'; diff --git a/magicka.strings b/magicka.strings index 56f179e..fdca3ae 100644 --- a/magicka.strings +++ b/magicka.strings @@ -193,3 +193,6 @@ Is this Correct? (Y/N) \e[1;30m[\e[1;34;44m%4d\e[1;30;40m]\e[1;32m*\e[1;37m%-39.39s \e[1;32m%-16.16s \e[1;35m%02d:%02d %02d-%02d-%02d\e[K\e[0m\r\n \e[1;30m[\e[1;34;44m%4d\e[1;30;40m] \e[1;37m%-39.39s \e[1;32m%-16.16s \e[1;35m%02d:%02d %02d-%02d-%02d\e[K\e[0m\r\n \e[1;31mYou have no email!\e[0m +\e[1;32mScanned \e[1;37m%s\e[1;32m->\e[1;37m%s\e[1;32m got \e[1;37m%d\e[1;32m msgs...\e[0m\r\n +\e[1;31mInvalid Reply Packet!\e[0m +\r\n\e[1;31mFailed to add message\e[0m\r\n diff --git a/mail_menu.c b/mail_menu.c index ba54cbe..59362d7 100644 --- a/mail_menu.c +++ b/mail_menu.c @@ -2542,6 +2542,16 @@ int mail_menu(struct user_record *user) { } } break; + case 'b': + { + bwave_create_packet(); + } + break; + case 'u': + { + bwave_upload_reply(); + } + break; } } if (do_internal_menu == 0) { diff --git a/main.c b/main.c index 8fd07a4..eff1a12 100644 --- a/main.c +++ b/main.c @@ -224,6 +224,9 @@ static int mail_area_handler(void* user, const char* section, const char* name, } } else if (strcasecmp(name, "qwk name") == 0) { mc->mail_areas[i]->qwkname = strdup(value); + if (strlen(mc->mail_areas[i]->qwkname) > 8) { + mc->mail_areas[i]->qwkname[8] = '\0'; + } } return 1; } @@ -255,6 +258,9 @@ static int mail_area_handler(void* user, const char* section, const char* name, } } else if (strcasecmp(name, "qwk name") == 0) { mc->mail_areas[mc->mail_area_count]->qwkname = strdup(value); + if (strlen(mc->mail_areas[mc->mail_area_count]->qwkname) > 8) { + mc->mail_areas[mc->mail_area_count]->qwkname[8] = '\0'; + } } mc->mail_area_count++; } @@ -321,6 +327,19 @@ static int handler(void* user, const char* section, const char* name, } else { conf->fork = 0; } + } else if (strcasecmp(name, "qwk name") == 0) { + conf->bwave_name = strdup(value); + if (strlen(conf->bwave_name) > 8) { + conf->bwave_name[8] = '\0'; + } + } else if (strcasecmp(name, "main aka") == 0) { + conf->main_aka = parse_fido_addr(value); + } else if (strcasecmp(name, "qwk max messages") == 0) { + conf->bwave_max_msgs = atoi(value); + } else if (strcasecmp(name, "zip command") == 0) { + conf->zip_cmd = strdup(value); + } else if (strcasecmp(name, "unzip command") == 0) { + conf->unzip_cmd = strdup(value); } } else if (strcasecmp(section, "paths") == 0){ if (strcasecmp(name, "ansi path") == 0) { diff --git a/users.c b/users.c index 78d5c53..676384e 100644 --- a/users.c +++ b/users.c @@ -81,7 +81,7 @@ int save_user(struct user_record *user) { int rc; char *update_sql = "UPDATE users SET password=?, salt=?, firstname=?," - "lastname=?, email=?, location=?, sec_level=?, last_on=?, time_left=?, cur_mail_conf=?, cur_mail_area=?, cur_file_dir=?, cur_file_sub=?, times_on=? where loginname LIKE ?"; + "lastname=?, email=?, location=?, sec_level=?, last_on=?, time_left=?, cur_mail_conf=?, cur_mail_area=?, cur_file_dir=?, cur_file_sub=?, times_on=?, bwavepktno=? where loginname LIKE ?"; sprintf(buffer, "%s/users.sq3", conf.bbs_path); @@ -111,7 +111,8 @@ int save_user(struct user_record *user) { sqlite3_bind_int(res, 12, user->cur_file_dir); sqlite3_bind_int(res, 13, user->cur_file_sub); sqlite3_bind_int(res, 14, user->timeson); - sqlite3_bind_text(res, 15, user->loginname, -1, 0); + sqlite3_bind_int(res, 15, user->bwavepktno); + sqlite3_bind_text(res, 16, user->loginname, -1, 0); } else { dolog("Failed to execute statement: %s", sqlite3_errmsg(db)); } @@ -151,10 +152,11 @@ int inst_user(struct user_record *user) { "cur_mail_area INTEGER," "cur_file_sub INTEGER," "cur_file_dir INTEGER," - "times_on INTEGER);"; + "times_on INTEGER," + "bwavepktno INTEGER);"; char *insert_sql = "INSERT INTO users (loginname, password, salt, firstname," - "lastname, email, location, sec_level, last_on, time_left, cur_mail_conf, cur_mail_area, cur_file_dir, cur_file_sub, times_on) VALUES(?,?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + "lastname, email, location, sec_level, last_on, time_left, cur_mail_conf, cur_mail_area, cur_file_dir, cur_file_sub, times_on, bwavepktno) VALUES(?,?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; char *err_msg = 0; sprintf(buffer, "%s/users.sq3", conf.bbs_path); @@ -197,7 +199,7 @@ int inst_user(struct user_record *user) { sqlite3_bind_int(res, 13, user->cur_file_dir); sqlite3_bind_int(res, 14, user->cur_file_sub); sqlite3_bind_int(res, 15, user->timeson); - + sqlite3_bind_int(res, 16, user->bwavepktno); } else { dolog("Failed to execute statement: %s", sqlite3_errmsg(db)); } @@ -225,7 +227,7 @@ struct user_record *check_user_pass(char *loginname, char *password) { sqlite3_stmt *res; int rc; char *sql = "SELECT Id, loginname, password, salt, firstname," - "lastname, email, location, sec_level, last_on, time_left, cur_mail_conf, cur_mail_area, cur_file_dir, cur_file_sub, times_on FROM users WHERE loginname LIKE ?"; + "lastname, email, location, sec_level, last_on, time_left, cur_mail_conf, cur_mail_area, cur_file_dir, cur_file_sub, times_on, bwavepktno FROM users WHERE loginname LIKE ?"; char *pass_hash; sprintf(buffer, "%s/users.sq3", conf.bbs_path); @@ -268,7 +270,8 @@ struct user_record *check_user_pass(char *loginname, char *password) { user->cur_file_dir = sqlite3_column_int(res, 13); user->cur_file_sub = sqlite3_column_int(res, 14); user->timeson = sqlite3_column_int(res, 15); - + user->bwavepktno = sqlite3_column_int(res, 16); + pass_hash = hash_sha256(password, user->salt); if (strcmp(pass_hash, user->password) != 0) { @@ -523,7 +526,7 @@ struct user_record *new_user() { } } while (!done); user->sec_level = conf.newuserlvl; - + user->bwavepktno = 0; user->sec_info = (struct sec_level_t *)malloc(sizeof(struct sec_level_t)); sprintf(buffer, "%s/config/s%d.ini", conf.bbs_path, user->sec_level);