/* Copyright (C), 2007 by Stephen Hurd */ #include #include #include #include #include #include #include "filepick.h" #include "syncterm.h" #include "fonts.h" #include "bbslist.h" #include "uifcinit.h" #include "comio.h" #include "conn.h" #include "ciolib.h" #include "cterm.h" #include "window.h" #include "term.h" #include "menu.h" #include "vidmodes.h" struct sort_order_info { char *name; int flags; size_t offset; int length; }; #define SORT_ORDER_REVERSED (1<<0) #define SORT_ORDER_STRING (1<<1) static struct sort_order_info sort_order[] = { { NULL ,0 ,0 ,0 } ,{ "Entry Name" ,SORT_ORDER_STRING ,offsetof(struct bbslist, name) ,sizeof(((struct bbslist *)NULL)->name) } ,{ "Date Added" ,SORT_ORDER_REVERSED ,offsetof(struct bbslist, added) ,sizeof(((struct bbslist *)NULL)->added) } ,{ "Date Last Connected" ,SORT_ORDER_REVERSED ,offsetof(struct bbslist, connected) ,sizeof(((struct bbslist *)NULL)->connected) } ,{ "Total Calls" ,SORT_ORDER_REVERSED ,offsetof(struct bbslist, calls) ,sizeof(((struct bbslist *)NULL)->calls) } ,{ "Dialing List" ,0 ,offsetof(struct bbslist, type) ,sizeof(((struct bbslist *)NULL)->type) } ,{ "Address" ,SORT_ORDER_STRING ,offsetof(struct bbslist, addr) ,sizeof(((struct bbslist *)NULL)->addr) } ,{ "Port" ,0 ,offsetof(struct bbslist, port) ,sizeof(((struct bbslist *)NULL)->port) } ,{ "Username" ,SORT_ORDER_STRING ,offsetof(struct bbslist, user) ,sizeof(((struct bbslist *)NULL)->user) } ,{ "Password" ,SORT_ORDER_STRING ,offsetof(struct bbslist, password) ,sizeof(((struct bbslist *)NULL)->password) } ,{ "System Password" ,SORT_ORDER_STRING ,offsetof(struct bbslist, syspass) ,sizeof(((struct bbslist *)NULL)->syspass) } ,{ "Connection Type" ,0 ,offsetof(struct bbslist, conn_type) ,sizeof(((struct bbslist *)NULL)->conn_type) } ,{ "Screen Mode" ,0 ,offsetof(struct bbslist, screen_mode) ,sizeof(((struct bbslist *)NULL)->screen_mode) } ,{ "Status Line Visibility" ,0 ,offsetof(struct bbslist, nostatus) ,sizeof(((struct bbslist *)NULL)->nostatus) } ,{ "Download Directory" ,SORT_ORDER_STRING ,offsetof(struct bbslist, dldir) ,sizeof(((struct bbslist *)NULL)->dldir) } ,{ "Upload Directory" ,SORT_ORDER_STRING ,offsetof(struct bbslist, uldir) ,sizeof(((struct bbslist *)NULL)->uldir) } ,{ "Log File" ,SORT_ORDER_STRING ,offsetof(struct bbslist, logfile) ,sizeof(((struct bbslist *)NULL)->logfile) } ,{ "Transfer Log Level" ,0 ,offsetof(struct bbslist, xfer_loglevel) ,sizeof(((struct bbslist *)NULL)->xfer_loglevel) } ,{ "BPS Rate" ,0 ,offsetof(struct bbslist, bpsrate) ,sizeof(((struct bbslist *)NULL)->bpsrate) } ,{ "ANSI Music" ,0 ,offsetof(struct bbslist, music) ,sizeof(((struct bbslist *)NULL)->music) } ,{ "Address Family" ,0 ,offsetof(struct bbslist, address_family) ,sizeof(((struct bbslist *)NULL)->address_family) } ,{ "Font" ,SORT_ORDER_STRING ,offsetof(struct bbslist, font) ,sizeof(((struct bbslist *)NULL)->font) } ,{ "Hide Popups" ,0 ,offsetof(struct bbslist, hidepopups) ,sizeof(((struct bbslist *)NULL)->hidepopups) } ,{ "RIP" ,0 ,offsetof(struct bbslist, rip) ,sizeof(((struct bbslist *)NULL)->rip) } ,{ NULL ,0 ,0 ,0 } }; int sortorder[sizeof(sort_order)/sizeof(struct sort_order_info)]; char *screen_modes[]={ "Current", "80x25", "80x28", "80x30", "80x43", "80x50", "80x60", "132x37 (16:9)", "132x52 (5:4)", "132x25", "132x28", "132x30", "132x34", "132x43", "132x50", "132x60", "C64", "C128 (40col)", "C128 (80col)", "Atari", "Atari XEP80", "Custom", "EGA 80x25", NULL}; static char *screen_modes_enum[]={"Current", "80x25", "80x28", "80x30", "80x43", "80x50", "80x60", "132x37", "132x52", "132x25", "132x28", "132x30", "132x34", "132x43", "132x50", "132x60", "C64", "C128-40col", "C128-80col", "Atari", "Atari-XEP80", "Custom", "EGA80x25", NULL}; char *log_levels[]={"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Info", "Debug", NULL}; static char *log_level_desc[]={"None", "Alerts", "Critical Errors", "Errors", "Warnings", "Notices", "Normal", "All (Debug)", NULL}; char *rate_names[]={"300", "600", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "76800", "115200", "Current", NULL}; int rates[]={300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 0}; static char *rip_versions[] = {"Off", "RIPv1", "RIPv3"}; static char *fc_names[] = {"RTS/CTS", "XON/XOFF", "RTS/CTS and XON/XOFF", "None", NULL}; static char *fc_enum[] = {"RTSCTS", "XONXOFF", "RTSCTS_XONXOFF", "None", NULL}; char *music_names[]={"ESC [ | only", "BANSI Style", "All ANSI Music enabled", NULL}; char music_helpbuf[] = "`ANSI Music Setup`\n\n" "~ ESC [ | only ~ Only CSI | (SyncTERM) ANSI music is supported.\n" " Enables Delete Line\n" "~ BANSI Style ~ Also enables BANSI-Style (CSI N)\n" " Enables Delete Line\n" "~ All ANSI Music Enabled ~ Enables both CSI M and CSI N ANSI music.\n" " Delete Line is disabled.\n" "\n" "So-Called ANSI Music has a long and troubled history. Although the\n" "original ANSI standard has well defined ways to provide private\n" "extensions to the spec, none of these methods were used. Instead,\n" "so-called ANSI music replaced the Delete Line ANSI sequence. Many\n" "full-screen editors use DL, and to this day, some programs (Such as\n" "BitchX) require it to run.\n\n" "To deal with this, BananaCom decided to use what *they* thought was an\n" "unspecified escape code ESC[N for ANSI music. Unfortunately, this is\n" "broken also. Although rarely implemented in BBS clients, ESC[N is\n" "the erase field sequence.\n\n" "SyncTERM has now defined a third ANSI music sequence which *IS* legal\n" "according to the ANSI spec. Specifically ESC[|."; static char *address_families[]={"PerDNS", "IPv4", "IPv6", NULL}; static char *address_family_names[]={"As per DNS", "IPv4 only", "IPv6 only", NULL}; static char *address_family_help = "`Address Family`\n\n" "Select the address family to resolve\n\n" "`As per DNS`..: Uses what is in the DNS system\n" "`IPv4 only`...: Only uses IPv4 addresses.\n" "`IPv6 only`...: Only uses IPv6 addresses.\n"; static char *address_help= "`Address`, `Phone Number`, `Serial Port`, or `Command`\n\n" "Enter the hostname, IP address, phone number, or serial port device of\n" "the system to connect to. Example: `nix.synchro.net`\n\n" "In the case of the Shell type, enter the command to run.\n" "Shell types are only functional under *nix\n"; static char *conn_type_help= "`Connection Type`\n\n" "Select the type of connection you wish to make:\n\n" "`RLogin`...........: Auto-login with RLogin protocol\n" "`RLogin Reversed`..: RLogin using reversed username/password parameters\n" "`Telnet`...........: Use more common Telnet protocol\n" "`Raw`..............: Make a raw TCP socket connection\n" "`SSH`..............: Connect using the Secure Shell (SSH-2) protocol\n" "`Modem`............: Connect using a dial-up modem\n" "`Serial`...........: Connect directly to a serial communications port\n" "`Shell`............: Connect to a local PTY (*nix only)\n" "`MBBS GHost`.......: Communicate using the Major BBS 'GHost' protocol\n"; ; static char *YesNo[3]={"Yes","No",""}; ini_style_t ini_style = { /* key_len */ 15, /* key_prefix */ "\t", /* section_separator */ "\n", /* value_separarator */NULL, /* bit_separator */ NULL }; static int fc_to_enum(int fc) { switch (fc & (COM_FLOW_CONTROL_RTS_CTS | COM_FLOW_CONTROL_XON_OFF)) { case COM_FLOW_CONTROL_RTS_CTS: return 0; case COM_FLOW_CONTROL_XON_OFF: return 1; case (COM_FLOW_CONTROL_RTS_CTS | COM_FLOW_CONTROL_XON_OFF): return 2; default: return 3; } } static int fc_from_enum(int fc_enum) { switch(fc_enum) { case 0: return COM_FLOW_CONTROL_RTS_CTS; case 1: return COM_FLOW_CONTROL_XON_OFF; case 2: return (COM_FLOW_CONTROL_RTS_CTS | COM_FLOW_CONTROL_XON_OFF); default: return 0; } } void viewofflinescroll(void) { int top; int key; int i; struct text_info txtinfo; struct text_info sbtxtinfo; struct mouse_event mevent; if(scrollback_buf==NULL) return; uifcbail(); gettextinfo(&txtinfo); textmode(scrollback_mode); switch(ciolib_to_screen(scrollback_mode)) { case SCREEN_MODE_C64: setfont(33,TRUE,1); break; case SCREEN_MODE_C128_40: case SCREEN_MODE_C128_80: setfont(35,TRUE,1); break; case SCREEN_MODE_ATARI: case SCREEN_MODE_ATARI_XEP80: setfont(36,TRUE,1); break; } /* Set up a shadow palette */ if (cio_api.options & CONIO_OPT_EXTENDED_PALETTE) { for (i=0; i < sizeof(dac_default)/sizeof(struct dac_colors); i++) setpalette(i + 16, dac_default[i].red << 8 | dac_default[i].red, dac_default[i].green << 8 | dac_default[i].green, dac_default[i].blue << 8 | dac_default[i].blue); } setfont(0, FALSE, 1); setfont(0, FALSE, 2); setfont(0, FALSE, 3); setfont(0, FALSE, 4); drawwin(); set_modepalette(palettes[COLOUR_PALETTE]); top=scrollback_lines; gotoxy(1,1); textattr(uifc.hclr|(uifc.bclr<<4)|BLINK); gettextinfo(&sbtxtinfo); ciomouse_addevent(CIOLIB_BUTTON_4_PRESS); ciomouse_addevent(CIOLIB_BUTTON_5_PRESS); showmouse(); for(i=0;!i && !quitting;) { if(top<1) top=1; if(top>(int)scrollback_lines) top=scrollback_lines; vmem_puttext(((sbtxtinfo.screenwidth-scrollback_cols)/2)+1,1 ,(sbtxtinfo.screenwidth-scrollback_cols)/2+scrollback_cols ,sbtxtinfo.screenheight ,scrollback_buf+(scrollback_cols*top)); cputs("Scrollback"); gotoxy(scrollback_cols-9,1); cputs("Scrollback"); gotoxy(1,1); key=getch(); switch(key) { case 0xe0: case 0: switch(key|getch()<<8) { case CIO_KEY_QUIT: check_exit(TRUE); if (quitting) i=1; break; case CIO_KEY_MOUSE: getmouse(&mevent); switch(mevent.event) { case CIOLIB_BUTTON_1_DRAG_START: mousedrag(scrollback_buf); break; case CIOLIB_BUTTON_4_PRESS: top--; break; case CIOLIB_BUTTON_5_PRESS: top++; break; } break; case CIO_KEY_UP: top--; break; case CIO_KEY_DOWN: top++; break; case CIO_KEY_PPAGE: top-=sbtxtinfo.screenheight; break; case CIO_KEY_NPAGE: top+=sbtxtinfo.screenheight; break; case CIO_KEY_F(1): init_uifc(FALSE, FALSE); uifc.helpbuf= "`Scrollback Buffer`\n\n" "~ J ~ or ~ Up Arrow ~ Scrolls up one line\n" "~ K ~ or ~ Down Arrow ~ Scrolls down one line\n" "~ H ~ or ~ Page Up ~ Scrolls up one screen\n" "~ L ~ or ~ Page Down ~ Scrolls down one screen\n"; uifc.showhelp(); uifcbail(); drawwin(); break; } break; case 'j': case 'J': top--; break; case 'k': case 'K': top++; break; case 'h': case 'H': top-=term.height; break; case 'l': case 'L': top+=term.height; break; case ESC: i=1; break; } } textmode(txtinfo.currmode); init_uifc(TRUE,TRUE); return; } int get_rate_num(int rate) { int i; for(i=0; rates[i] && (!rate || rate > rates[i]); i++); return(i); } int get_next_rate(int curr_rate) { int i; if(curr_rate == 0) i=0; else i=get_rate_num(curr_rate)+1; return(rates[i]); } int is_sorting(int chk) { int i; for(i=0; i=0; i--) { if(ac[i]!=bc[i]) return(ac[i]-bc[i]); } return(0); #endif } int listcmp(const void *aptr, const void *bptr) { const char *a=*(void **)(aptr); const char *b=*(void **)(bptr); int i; int item; int reverse; int ret=0; for(i=0; iname,current)==0) { *cur=i; if(bar) *bar=i; break; } } } } void write_sortorder(void) { char inipath[MAX_PATH+1]; FILE *inifile; str_list_t inicontents; str_list_t sortorders; char str[64]; int i; sortorders=strListInit(); for(i=0;sort_order[abs(sortorder[i])].name!=NULL;i++) { sprintf(str,"%d",sortorder[i]); strListPush(&sortorders, str); } get_syncterm_filename(inipath, sizeof(inipath), SYNCTERM_PATH_INI, FALSE); if((inifile=fopen(inipath,"r"))!=NULL) { inicontents=iniReadFile(inifile); fclose(inifile); } else { inicontents=strListInit(); } iniSetStringList(&inicontents, "SyncTERM", "SortOrder", ",", sortorders, &ini_style); if((inifile=fopen(inipath,"w"))!=NULL) { iniWriteFile(inifile,inicontents); fclose(inifile); } strListFree(&sortorders); strListFree(&inicontents); } void edit_sorting(struct bbslist **list, int *listcount, int *ocur, int *obar, char *current) { char opt[sizeof(sort_order)/sizeof(struct sort_order_info)][80]; char *opts[sizeof(sort_order)/sizeof(struct sort_order_info)+1]; char sopt[sizeof(sort_order)/sizeof(struct sort_order_info)][80]; char *sopts[sizeof(sort_order)/sizeof(struct sort_order_info)+1]; int curr=0,bar=0; int scurr=0,sbar=0; int ret,sret; int i,j; for(i=0;i=0) { /* Insert into array */ memmove(&(sortorder[ret&MSK_OFF])+1,&(sortorder[(ret&MSK_OFF)]),sizeof(sortorder)-sizeof(sortorder[0])*((ret&MSK_OFF)+1)); j=0; for(i=0; iname,bbsname); break; case SYSTEM_BBSLIST: sprintf(entry->name,"[%.*s]",sizeof(entry->name)-3,bbsname); break; } #else SAFECOPY(entry->name,bbsname); #endif } section=iniGetSection(listfile,bbsname); iniGetString(section,NULL,"Address","",entry->addr); entry->conn_type=iniGetEnum(section,NULL,"ConnectionType",conn_types_enum,CONN_TYPE_SSH); entry->flow_control = fc_from_enum(iniGetEnum(section, NULL, "FlowControl", fc_enum, 0)); entry->port=iniGetShortInt(section,NULL,"Port",conn_ports[entry->conn_type]); entry->added=iniGetDateTime(section,NULL,"Added",0); entry->connected=iniGetDateTime(section,NULL,"LastConnected",0); entry->calls=iniGetInteger(section,NULL,"TotalCalls",0); iniGetString(section,NULL,"UserName","",entry->user); iniGetString(section,NULL,"Password","",entry->password); iniGetString(section,NULL,"SystemPassword","",entry->syspass); if(iniGetBool(section,NULL,"BeDumb",FALSE)) /* Legacy */ entry->conn_type=CONN_TYPE_RAW; entry->screen_mode=iniGetEnum(section,NULL,"ScreenMode",screen_modes_enum,SCREEN_MODE_CURRENT); entry->nostatus=iniGetBool(section,NULL,"NoStatus",FALSE); entry->hidepopups=iniGetBool(section,NULL,"HidePopups",FALSE); entry->rip=iniGetEnum(section,NULL,"RIP",rip_versions,RIP_VERSION_NONE); iniGetString(section,NULL,"DownloadPath",home,entry->dldir); iniGetString(section,NULL,"UploadPath",home,entry->uldir); /* Log Stuff */ iniGetString(section,NULL,"LogFile","",entry->logfile); entry->append_logfile=iniGetBool(section,NULL,"AppendLogFile",TRUE); entry->xfer_loglevel=iniGetEnum(section,NULL,"TransferLogLevel",log_levels,LOG_INFO); entry->telnet_loglevel=iniGetEnum(section,NULL,"TelnetLogLevel",log_levels,LOG_INFO); entry->bpsrate=iniGetInteger(section,NULL,"BPSRate",0); entry->music=iniGetInteger(section,NULL,"ANSIMusic",CTERM_MUSIC_BANSI); entry->address_family=iniGetEnum(section,NULL,"AddressFamily",address_families, ADDRESS_FAMILY_UNSPEC); iniGetString(section,NULL,"Font","Codepage 437 English",entry->font); iniGetString(section,NULL,"Comment","",entry->comment); entry->type=type; entry->id=id; strListFree(§ion); } /* * Checks if bbsname already is listed in list * setting *pos to the position if not NULL. * optionally only if the entry is a user list * entry */ int list_name_check(struct bbslist **list, char *bbsname, int *pos, int useronly) { int i; if(list==NULL) { FILE *listfile; str_list_t inifile; if((listfile=fopen(settings.list_path,"r"))!=NULL) { inifile=iniReadFile(listfile); i=iniSectionExists(inifile, bbsname); strListFree(&inifile); fclose(listfile); return(i); } return(0); } for(i=0; list[i]!=NULL; i++) { if(useronly && list[i]->type != USER_BBSLIST) continue; if(stricmp(list[i]->name,bbsname)==0) { if(pos) *pos=i; return(1); } } return(0); } /* * Reads in a BBS list from listpath using *i as the counter into bbslist * first BBS read goes into list[i] */ void read_list(char *listpath, struct bbslist **list, struct bbslist *defaults, int *i, int type) { FILE *listfile; char *bbsname; str_list_t bbses; str_list_t inilines; if((listfile=fopen(listpath,"r"))!=NULL) { inilines=iniReadFile(listfile); fclose(listfile); if(defaults != NULL && type==USER_BBSLIST) read_item(inilines,defaults,NULL,-1,type); bbses=iniGetSectionList(inilines,NULL); while((bbsname=strListRemove(&bbses,0))!=NULL) { if(!list_name_check(list, bbsname, NULL, FALSE)) { if((list[*i]=(struct bbslist *)malloc(sizeof(struct bbslist)))==NULL) { free(bbsname); break; } read_item(inilines,list[*i],bbsname,*i,type); (*i)++; } free(bbsname); } strListFree(&bbses); strListFree(&inilines); } else { if(defaults != NULL && type==USER_BBSLIST) read_item(NULL,defaults,NULL,-1,type); } #if 0 /* This isn't necessary (NULL is a sufficient) */ /* Add terminator */ list[*i]=(struct bbslist *)""; #endif } static void fc_str(char *str, int fc) { sprintf(str, "Flow Control %s", fc_names[fc_to_enum(fc)]); } /* * Terminates a path with "..." if it's too long. * Format must contain only a single '%s'. */ void printf_trunc(char *dst, size_t dstsz, char *fmt, char *path) { char *mangled; size_t fmt_len = strlen(fmt) - 2; size_t remain_len = dstsz - fmt_len; size_t full_len = fmt_len + strlen(path); if (full_len >= dstsz) { mangled = strdup(path); if (mangled) { mangled[remain_len - 1] = '\0'; mangled[remain_len - 2] = '.'; mangled[remain_len - 3] = '.'; mangled[remain_len - 4] = '.'; sprintf(dst, fmt, mangled); } else sprintf(dst, fmt, ""); } else { sprintf(dst, fmt, path); } } void configure_log(struct bbslist *item, const char *itemname, str_list_t inifile, int *changed) { char opt[4][69]; char *opts[(sizeof(opt)/sizeof(opt[0]))+1]; int o; int i; int copt = 0; for (o = 0; o < sizeof(opt) / sizeof(opt[0]); o++) opts[o] = opt[o]; opts[o] = NULL; for (;;) { o = 0; printf_trunc(opt[o], sizeof(opt[o]), "Log Filename %s", item->logfile); o++; sprintf(opt[o++], "File Transfer Log Level %s", log_level_desc[item->xfer_loglevel]); sprintf(opt[o++], "Telnet Command Log Level %s", log_level_desc[item->telnet_loglevel]); sprintf(opt[o++], "Append Log File %s", item->append_logfile ? "Yes" : "No"); uifc.helpbuf = "`Log Configuration`\n\n" "~ Log File ~\n" " Log file name when logging is enabled\n\n" "~ File Transfer Log Level ~\n" " Selects the transfer log level.\n\n" "~ Telnet Command Log Level ~\n" " Selects the telnet command log level.\n\n" "~ Append Log File ~\n" " Append log file (instead of overwrite) on each connection\n\n"; switch (uifc.list(WIN_SAV|WIN_ACT, 0, 0, 0, &copt, NULL, "Log Configuration", opts)) { case -1: return; case 0: uifc.helpbuf= "`Log Filename`\n\n" "Enter the path to the optional log file."; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Log File",item->logfile,MAX_PATH,K_EDIT)>=0) { iniSetString(&inifile,itemname,"LogFile",item->logfile,&ini_style); *changed = 1; } else check_exit(FALSE); break; case 1: uifc.helpbuf= "`File Transfer Log Level`\n\n" "Select the varbosity level for logging.\n" "The lower in the list the item, the more berbose the log.\n" "Each level includes all messages in levels above it."; i = item->xfer_loglevel; switch(uifc.list(WIN_SAV|WIN_BOT|WIN_RHT, 0, 0, 0, &(item->xfer_loglevel), NULL, "File Transfer Log Level", log_level_desc)) { case -1: item->xfer_loglevel = i; check_exit(FALSE); break; default: if (item->xfer_loglevel != i) { iniSetEnum(&inifile,itemname,"TransferLogLevel",log_levels,item->xfer_loglevel,&ini_style); *changed=1; } } break; case 2: uifc.helpbuf= "`Telnet Command Log Level`\n\n" "Select the varbosity level for logging.\n" "The lower in the list the item, the more berbose the log.\n" "Each level includes all messages in levels above it."; i = item->telnet_loglevel; switch(uifc.list(WIN_SAV|WIN_BOT|WIN_RHT, 0, 0, 0, &(item->telnet_loglevel), NULL, "Telnet Command Log Level", log_level_desc)) { case -1: item->telnet_loglevel = i; check_exit(FALSE); break; default: if (item->telnet_loglevel != i) { iniSetEnum(&inifile,itemname,"TransferLogLevel",log_levels,item->telnet_loglevel,&ini_style); *changed=1; } break; } break; case 3: item->append_logfile=!item->append_logfile; *changed=1; iniSetBool(&inifile,itemname,"AppendLogFile",item->append_logfile,&ini_style); break; } } } static int get_rip_version(int oldver, int *changed) { int cur = oldver; int bar = 0; uifc.helpbuf= "`RIP Version`\n\n" "RIP v1 requires EGA mode while RIP v3\n" "works in any screen mode."; switch(uifc.list(WIN_SAV,0,0,0,&cur,NULL,"RIP Mode",rip_versions)) { case -1: check_exit(FALSE); break; case RIP_VERSION_NONE: case RIP_VERSION_1: case RIP_VERSION_3: if (cur != oldver) *changed = 1; } return cur; } int edit_list(struct bbslist **list, struct bbslist *item,char *listpath,int isdefault) { char opt[19][69]; /* 21=Holds number of menu items, 80=Number of columns */ char *opts[(sizeof(opt)/sizeof(opt[0]))+1]; int changed=0; int copt=0,i,j; int bar=0; char str[64]; FILE *listfile; str_list_t inifile; char tmp[LIST_NAME_MAX+1]; char *itemname; for(i=0;itype==SYSTEM_BBSLIST) { uifc.helpbuf= "`Copy from system directory`\n\n" "This entry was loaded from the system directory. In order to edit it, it\n" "must be copied into your personal directory.\n"; i=0; if(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Copy from system directory?",YesNo)!=0) { check_exit(FALSE); return(0); } item->type=USER_BBSLIST; add_bbs(listpath, item); } if((listfile=fopen(listpath,"r"))!=NULL) { inifile=iniReadFile(listfile); fclose(listfile); } else { inifile=strListInit(); } if(isdefault) itemname=NULL; else itemname=item->name; for(;!quitting;) { i=0; if(!isdefault) { sprintf(opt[i++], "Name %s",itemname); if(item->conn_type==CONN_TYPE_MODEM) sprintf(opt[i++], "Phone Number %s",item->addr); else if(item->conn_type==CONN_TYPE_SERIAL) sprintf(opt[i++], "Device Name %s",item->addr); else if(item->conn_type==CONN_TYPE_SHELL) sprintf(opt[i++], "Command %s",item->addr); else sprintf(opt[i++], "Address %s",item->addr); } sprintf(opt[i++], "Connection Type %s",conn_types[item->conn_type]); if (item->conn_type == CONN_TYPE_MODEM || item->conn_type ==CONN_TYPE_SERIAL) fc_str(opt[i++], item->flow_control); else if (item->conn_type != CONN_TYPE_SHELL) sprintf(opt[i++], "TCP Port %hu",item->port); if (item->conn_type == CONN_TYPE_SSHNA) { printf_trunc(opt[i], sizeof(opt[i]), "SSH Username %s",item->user); i++; sprintf(opt[i++], "BBS Username %s",item->password); sprintf(opt[i++], "BBS Password %s",item->syspass[0]?"********":""); } else { printf_trunc(opt[i], sizeof(opt[i]), "Username %s",item->user); i++; sprintf(opt[i++], "Password %s",item->password[0]?"********":""); sprintf(opt[i++], "System Password %s",item->syspass[0]?"********":""); } sprintf(opt[i++], "Screen Mode %s",screen_modes[item->screen_mode]); sprintf(opt[i++], "Hide Status Line %s",item->nostatus?"Yes":"No"); printf_trunc(opt[i], sizeof(opt[i]), "Download Path %s", item->dldir); i++; printf_trunc(opt[i], sizeof(opt[i]), "Upload Path %s", item->uldir); i++; strcpy(opt[i++], "Log Configuration"); if(item->bpsrate) sprintf(str,"%ubps", item->bpsrate); else strcpy(str,"Current"); sprintf(opt[i++], "Comm Rate %s",str); sprintf(opt[i++], "ANSI Music %s",music_names[item->music]); sprintf(opt[i++], "Address Family %s",address_family_names[item->address_family]); sprintf(opt[i++], "Font %s",item->font); sprintf(opt[i++], "Hide Popups %s",item->hidepopups?"Yes":"No"); sprintf(opt[i++], "RIP %s",rip_versions[item->rip]); opt[i][0]=0; uifc.changes=0; if (isdefault) { uifc.helpbuf= "`Edit Default Connection`\n\n" "Select item to edit.\n\n" "~ Conection Type ~\n" " Type of connection\n\n" "~ TCP Port ~ (if applicable)\n" " TCP port to connect to (applicable types only)\n\n" "~ Username ~\n" " Username sent by the auto-login command\n\n" "~ Password ~\n" " Password sent by auto-login command (not securely stored)\n\n" "~ System Password ~\n" " System Password sent by auto-login command (not securely stored)\n\n" "~ Screen Mode ~\n" " Display mode to use\n\n" "~ Hide Status Line ~\n" " Selects if the status line should be hidden, giving an extra\n" " display row\n\n" "~ Download Path ~\n" " Default path to store downloaded files\n\n" "~ Upload Path ~\n" " Default path for uploads\n\n" "~ Log Configuration ~\n" " Configure logging settings\n\n" "~ Comm Rate ~\n" " Display speed\n\n" "~ ANSI Music ~\n" " ANSI music type selection\n\n" "~ Address Family ~\n" " IPv4 or IPv6\n\n" "~ Font ~\n" " Select font to use for the entry\n\n" "~ Hide Popups ~\n" " Hide all popup dialogs (i.e., Connecting, Disconnected, etc.)\n\n" ; } else { uifc.helpbuf= "`Edit Directory Entry`\n\n" "Select item to edit.\n\n" "~ Name ~\n" " The name of the BBS entry\n\n" "~ Phone Number / Device Name / Command / Address ~\n" " Required information to establish the connection (type specific)\n\n" "~ Conection Type ~\n" " Type of connection\n\n" "~ TCP Port ~ (if applicable)\n" " TCP port to connect to (applicable types only)\n\n" "~ Username ~\n" " Username sent by the auto-login command\n\n" "~ Password ~\n" " Password sent by auto-login command (not securely stored)\n\n" "~ System Password ~\n" " System Password sent by auto-login command (not securely stored)\n\n" "~ Screen Mode ~\n" " Display mode to use\n\n" "~ Hide Status Line ~\n" " Selects if the status line should be hidden, giving an extra\n" " display row\n\n" "~ Download Path ~\n" " Default path to store downloaded files\n\n" "~ Upload Path ~\n" " Default path for uploads\n\n" "~ Log Configuration ~\n" " Configure logging settings\n\n" "~ Comm Rate ~\n" " Display speed\n\n" "~ ANSI Music ~\n" " ANSI music type selection\n\n" "~ Address Family ~\n" " IPv4 or IPv6\n\n" "~ Font ~\n" " Select font to use for the entry\n\n" "~ Hide Popups ~\n" " Hide all popup dialogs (i.e., Connecting, Disconnected, etc.)\n\n" ; } i=uifc.list(WIN_MID|WIN_SAV|WIN_ACT,0,0,0,&copt,&bar ,isdefault ? "Edit Default Connection":"Edit Directory Entry" ,opts); if(i>=0 && isdefault) i+=2; if(i>=3 && item->conn_type==CONN_TYPE_SHELL) i++; /* no port number */ switch(i) { case -1: check_exit(FALSE); if((!isdefault) && (itemname!=NULL) && (itemname[0]==0)) { uifc.helpbuf= "`Cancel Save`\n\n" "This entry has no name and can therefore not be saved.\n" "Selecting `No` will return to editing mode.\n"; i=0; if(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Cancel Save?",YesNo)!=0) { quitting=FALSE; check_exit(FALSE); break; } strListFree(&inifile); return(0); } if(!safe_mode) { if((listfile=fopen(listpath,"w"))!=NULL) { iniWriteFile(listfile,inifile); fclose(listfile); } } strListFree(&inifile); return(changed); case 0: do { uifc.helpbuf= "`Directory Entry Name`\n\n" "Enter the name of the entry as it is to appear in the directory."; strcpy(tmp,itemname); uifc.input(WIN_MID|WIN_SAV,0,0,"Name",tmp,LIST_NAME_MAX,K_EDIT); check_exit(FALSE); if(quitting) break; if(stricmp(tmp,itemname) && list_name_check(list, tmp, NULL, FALSE)) { uifc.helpbuf= "`Entry Name Already Exists`\n\n" "An entry with that name already exists in the directory.\n" "Please choose a unique name.\n"; uifc.msg("Entry Name Already Exists!"); check_exit(FALSE); } else { if(tmp[0]==0) { uifc.helpbuf= "`Can Not Use and Empty Name`\n\n" "Entry names can not be empty. Please enter an entry name.\n"; uifc.msg("Can not use an empty name"); check_exit(FALSE); } else { iniRenameSection(&inifile,itemname,tmp); strcpy(itemname, tmp); } } } while(tmp[0]==0 && !quitting); break; case 1: uifc.helpbuf=address_help; uifc.input(WIN_MID|WIN_SAV,0,0 ,item->conn_type==CONN_TYPE_MODEM ? "Phone Number" :item->conn_type==CONN_TYPE_SERIAL ? "Device Name" :item->conn_type==CONN_TYPE_SHELL ? "Command" : "Address" ,item->addr,LIST_ADDR_MAX,K_EDIT); check_exit(FALSE); iniSetString(&inifile,itemname,"Address",item->addr,&ini_style); break; case 3: if (item->conn_type == CONN_TYPE_MODEM || item->conn_type == CONN_TYPE_SERIAL) { uifc.helpbuf = "`Flow Control`\n\n" "Select the desired flow control type.\n" "This should usually be left as \"RTS/CTS\".\n"; i = fc_to_enum(item->flow_control); j = i; switch (uifc.list(WIN_SAV, 0, 0, 0, &j, NULL, "Flow Control", fc_names)) { case -1: check_exit(FALSE); break; default: item->flow_control = fc_from_enum(j); if (j != i) { iniSetEnum(&inifile, itemname, "FlowControl", fc_enum, j, &ini_style); uifc.changes = 1; } break; } } else { i=item->port; sprintf(str,"%hu",item->port); uifc.helpbuf= "`TCP Port`\n\n" "Enter the TCP port number that the server is listening to on the remote system.\n" "Telnet is generally port 23, RLogin is generally 513 and SSH is\n" "generally 22\n"; uifc.input(WIN_MID|WIN_SAV,0,0,"TCP Port",str,5,K_EDIT|K_NUMBER); check_exit(FALSE); j=atoi(str); if(j<1 || j>65535) j=conn_ports[item->conn_type]; item->port=j; iniSetShortInt(&inifile,itemname,"Port",item->port,&ini_style); if(i!=j) uifc.changes=1; else uifc.changes=0; } break; case 4: if (item->conn_type == CONN_TYPE_SSHNA) { uifc.helpbuf= "`SSH Username`\n\n" "Enter the username for passwordless SSH authentication."; } else { uifc.helpbuf= "`Username`\n\n" "Enter the username to attempt auto-login to the remote with.\n" "For SSH, this must be the SSH user name."; } uifc.input(WIN_MID|WIN_SAV,0,0,"Username",item->user,MAX_USER_LEN,K_EDIT); check_exit(FALSE); iniSetString(&inifile,itemname,"UserName",item->user,&ini_style); break; case 5: if (item->conn_type == CONN_TYPE_SSHNA) { uifc.helpbuf= "`BBS Username`\n\n" "Enter the username to be sent for auto-login (ALT-L)."; } else { uifc.helpbuf= "`Password`\n\n" "Enter your password for auto-login.\n" "For SSH, this must be the SSH password if it exists.\n"; } uifc.input(WIN_MID|WIN_SAV,0,0,"Password",item->password,MAX_PASSWD_LEN,K_EDIT); check_exit(FALSE); iniSetString(&inifile,itemname,"Password",item->password,&ini_style); break; case 6: if (item->conn_type == CONN_TYPE_SSHNA) { uifc.helpbuf= "`BBS Password`\n\n" "Enter your password for auto-login. (ALT-L)\n"; } else { uifc.helpbuf= "`System Password`\n\n" "Enter your System password for auto-login.\n" "This password is sent after the username and password, so for non-\n" "Synchronet, or non-sysop accounts, this can be used for simple\n" "scripting."; } uifc.input(WIN_MID|WIN_SAV,0,0,"System Password",item->syspass,MAX_SYSPASS_LEN,K_EDIT); check_exit(FALSE); iniSetString(&inifile,itemname,"SystemPassword",item->syspass,&ini_style); break; case 2: i=item->conn_type; item->conn_type--; uifc.helpbuf=conn_type_help; switch(uifc.list(WIN_SAV,0,0,0,&(item->conn_type),NULL,"Connection Type",&(conn_types[1]))) { case -1: check_exit(FALSE); item->conn_type=i; break; default: item->conn_type++; iniSetEnum(&inifile,itemname,"ConnectionType",conn_types_enum,item->conn_type,&ini_style); // TODO: NOTE: This is destructive! Beware! Ooooooo.... if (i == CONN_TYPE_SSHNA && item->conn_type != CONN_TYPE_SSHNA) { SAFECOPY(item->user, item->password); SAFECOPY(item->password, item->syspass); item->syspass[0] = 0; } if (i != CONN_TYPE_SSHNA && item->conn_type == CONN_TYPE_SSHNA) { SAFECOPY(item->syspass, item->password); SAFECOPY(item->password, item->user); item->user[0] = 0; } if(item->conn_type!=CONN_TYPE_MODEM && item->conn_type!=CONN_TYPE_SERIAL && item->conn_type!=CONN_TYPE_SHELL ) { /* Set the port too */ j=conn_ports[item->conn_type]; if(j<1 || j>65535) j=item->port; item->port=j; iniSetShortInt(&inifile,itemname,"Port",item->port,&ini_style); } changed=1; break; } break; case 7: i=item->screen_mode; uifc.helpbuf= "`Screen Mode`\n\n" "Select the screen size for this connection\n"; j = i; switch(uifc.list(WIN_SAV,0,0,0,&(item->screen_mode),&j,"Screen Mode",screen_modes)) { case -1: check_exit(FALSE); item->screen_mode=i; break; default: iniSetEnum(&inifile,itemname,"ScreenMode",screen_modes_enum,item->screen_mode,&ini_style); if (item->rip == RIP_VERSION_1 && item->screen_mode != SCREEN_MODE_EGA_80X25 && item->screen_mode != SCREEN_MODE_80X43) { item->rip = RIP_VERSION_3; iniSetEnum(&inifile,itemname,"RIP",rip_versions,item->rip,&ini_style); } if(item->screen_mode == SCREEN_MODE_C64) { SAFECOPY(item->font,font_names[33]); iniSetString(&inifile,itemname,"Font",item->font,&ini_style); item->nostatus = 1; iniSetBool(&inifile,itemname,"NoStatus",item->nostatus,&ini_style); } else if(item->screen_mode == SCREEN_MODE_C128_40 || item->screen_mode == SCREEN_MODE_C128_80) { SAFECOPY(item->font,font_names[35]); iniSetString(&inifile,itemname,"Font",item->font,&ini_style); item->nostatus = 1; iniSetBool(&inifile,itemname,"NoStatus",item->nostatus,&ini_style); } else if(item->screen_mode == SCREEN_MODE_ATARI) { SAFECOPY(item->font,font_names[36]); iniSetString(&inifile,itemname,"Font",item->font,&ini_style); item->nostatus = 1; iniSetBool(&inifile,itemname,"NoStatus",item->nostatus,&ini_style); } else if(item->screen_mode == SCREEN_MODE_ATARI_XEP80) { SAFECOPY(item->font,font_names[36]); iniSetString(&inifile,itemname,"Font",item->font,&ini_style); item->nostatus = 1; iniSetBool(&inifile,itemname,"NoStatus",item->nostatus,&ini_style); } else if (i == SCREEN_MODE_C64 || i == SCREEN_MODE_C128_40 || i == SCREEN_MODE_C128_80 || i == SCREEN_MODE_ATARI || i == SCREEN_MODE_ATARI_XEP80) { SAFECOPY(item->font,font_names[0]); iniSetString(&inifile,itemname,"Font",item->font,&ini_style); item->nostatus = 0; iniSetBool(&inifile,itemname,"NoStatus",item->nostatus,&ini_style); } changed=1; break; } break; case 8: item->nostatus=!item->nostatus; changed=1; iniSetBool(&inifile,itemname,"NoStatus",item->nostatus,&ini_style); break; case 9: uifc.helpbuf= "`Download Path`\n\n" "Enter the path where downloads will be placed."; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Download Path",item->dldir,MAX_PATH,K_EDIT)>=0) iniSetString(&inifile,itemname,"DownloadPath",item->dldir,&ini_style); else check_exit(FALSE); break; case 10: uifc.helpbuf= "`Upload Path`\n\n" "Enter the path where uploads will be browsed from."; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Upload Path",item->uldir,MAX_PATH,K_EDIT)>=0) iniSetString(&inifile,itemname,"UploadPath",item->uldir,&ini_style); else check_exit(FALSE); break; case 11: configure_log(item, itemname, inifile, &changed); break; case 12: uifc.helpbuf= "`Comm Rate (in bits-per-second)`\n\n" "`For TCP connections:`\n" "Select the rate which received characters will be displayed.\n\n" "This allows animated ANSI and some games to work as intended.\n\n" "`For Modem/Direct COM port connections:`\n" "Select the `DTE Rate` to use." ; i=get_rate_num(item->bpsrate); switch(uifc.list(WIN_SAV,0,0,0,&i,NULL,"Comm Rate (BPS)",rate_names)) { case -1: check_exit(FALSE); break; default: item->bpsrate=rates[i]; iniSetInteger(&inifile,itemname,"BPSRate",item->bpsrate,&ini_style); changed=1; } break; case 13: uifc.helpbuf=music_helpbuf; i=item->music; if(uifc.list(WIN_SAV,0,0,0,&i,NULL,"ANSI Music Setup",music_names)!=-1) { item->music=i; iniSetInteger(&inifile,itemname,"ANSIMusic",item->music,&ini_style); changed=1; } else check_exit(FALSE); break; case 14: uifc.helpbuf=address_family_help; i=item->address_family; if(uifc.list(WIN_SAV, 0, 0, 0, &i, NULL, "Address Family", address_family_names)!=-1) { item->address_family=i; iniSetEnum(&inifile, itemname, "AddressFamily", address_families, item->address_family, &ini_style); changed=1; } else check_exit(FALSE); break; case 15: uifc.helpbuf= "`Font`\n\n" "Select the desired font for this connection.\n\n" "Some fonts do not allow some modes. When this is the case, an\n" "appropriate mode will be forced.\n"; i=j=find_font_id(item->font); switch(uifc.list(WIN_SAV,0,0,0,&i,&j,"Font",font_names)) { case -1: check_exit(FALSE); break; default: if(i!=find_font_id(item->font)) { SAFECOPY(item->font,font_names[i]); iniSetString(&inifile,itemname,"Font",item->font,&ini_style); changed=1; } } break; case 16: item->hidepopups=!item->hidepopups; changed=1; iniSetBool(&inifile,itemname,"HidePopups",item->hidepopups,&ini_style); break; case 17: item->rip = get_rip_version(item->rip, &changed); if (item->rip == RIP_VERSION_1) { item->screen_mode = 4; iniSetEnum(&inifile,itemname,"ScreenMode",screen_modes_enum,item->screen_mode,&ini_style); } iniSetEnum(&inifile,itemname,"RIP",rip_versions,item->rip,&ini_style); } if(uifc.changes) changed=1; } strListFree(&inifile); return (changed); } void add_bbs(char *listpath, struct bbslist *bbs) { FILE *listfile; str_list_t inifile; if(safe_mode) return; if((listfile=fopen(listpath,"r"))!=NULL) { inifile=iniReadFile(listfile); fclose(listfile); } else { inifile=strListInit(); } /* * Redundant: * iniAddSection(&inifile,bbs->name,NULL); */ iniSetString(&inifile,bbs->name,"Address",bbs->addr,&ini_style); iniSetShortInt(&inifile,bbs->name,"Port",bbs->port,&ini_style); iniSetDateTime(&inifile,bbs->name,"Added",/* include time */TRUE,time(NULL),&ini_style); iniSetDateTime(&inifile,bbs->name,"LastConnected",/* include time */TRUE,bbs->connected,&ini_style); iniSetInteger(&inifile,bbs->name,"TotalCalls",bbs->calls,&ini_style); iniSetString(&inifile,bbs->name,"UserName",bbs->user,&ini_style); iniSetString(&inifile,bbs->name,"Password",bbs->password,&ini_style); iniSetString(&inifile,bbs->name,"SystemPassword",bbs->syspass,&ini_style); iniSetEnum(&inifile,bbs->name,"ConnectionType",conn_types_enum,bbs->conn_type,&ini_style); iniSetEnum(&inifile, bbs->name, "FlowControl", fc_enum, fc_to_enum(bbs->flow_control), &ini_style); iniSetEnum(&inifile,bbs->name,"ScreenMode",screen_modes_enum,bbs->screen_mode,&ini_style); iniSetBool(&inifile,bbs->name,"NoStatus",bbs->nostatus,&ini_style); iniSetString(&inifile,bbs->name,"DownloadPath",bbs->dldir,&ini_style); iniSetString(&inifile,bbs->name,"UploadPath",bbs->uldir,&ini_style); iniSetString(&inifile,bbs->name,"LogFile",bbs->logfile,&ini_style); iniSetEnum(&inifile,bbs->name,"TransferLogLevel",log_levels,bbs->xfer_loglevel,&ini_style); iniSetEnum(&inifile,bbs->name,"TelnetLogLevel",log_levels,bbs->telnet_loglevel,&ini_style); iniSetBool(&inifile,bbs->name,"AppendLogFile",bbs->append_logfile,&ini_style); iniSetInteger(&inifile,bbs->name,"BPSRate",bbs->bpsrate,&ini_style); iniSetInteger(&inifile,bbs->name,"ANSIMusic",bbs->music,&ini_style); iniSetEnum(&inifile, bbs->name, "AddressFamily", address_families, bbs->address_family, &ini_style); iniSetString(&inifile,bbs->name,"Font",bbs->font,&ini_style); iniSetBool(&inifile,bbs->name,"HidePopups",bbs->hidepopups,&ini_style); iniSetEnum(&inifile,bbs->name,"RIP",rip_versions,bbs->rip,&ini_style); iniSetString(&inifile,bbs->name,"Comment",bbs->comment,&ini_style); if((listfile=fopen(listpath,"w"))!=NULL) { iniWriteFile(listfile,inifile); fclose(listfile); } strListFree(&inifile); } void del_bbs(char *listpath, struct bbslist *bbs) { FILE *listfile; str_list_t inifile; if(safe_mode) return; if((listfile=fopen(listpath,"r"))!=NULL) { inifile=iniReadFile(listfile); fclose(listfile); iniRemoveSection(&inifile,bbs->name); if((listfile=fopen(listpath,"w"))!=NULL) { iniWriteFile(listfile,inifile); fclose(listfile); } strListFree(&inifile); } } /* * This is pretty sketchy... * These are pointers to automatic variables in show_bbslist() which are * used to redraw the entire menu set if the custom mode is current * and is changed. */ static int *glob_sopt; static int *glob_sbar; static char **glob_settings_menu; static int *glob_listcount; static int *glob_opt; static int *glob_bar; static char *glob_list_title; static struct bbslist ***glob_list; /* * This uses the above variables and therefore *must* be called from * show_bbslist(). * * If show_bbslist() is not on the stack, this will do insane things. */ static void custom_mode_adjusted(int *cur, char **opt) { struct text_info ti; int cvmode; gettextinfo(&ti); if (ti.currmode != CIOLIB_MODE_CUSTOM) { cvmode = find_vmode(ti.currmode); if (cvmode >= 0) { vparams[cvmode].cols = settings.custom_cols; vparams[cvmode].rows = settings.custom_rows; vparams[cvmode].charheight = settings.custom_fontheight; } return; } uifcbail(); textmode(0); cvmode = find_vmode(ti.currmode); if (cvmode >= 0) { vparams[cvmode].cols = settings.custom_cols; vparams[cvmode].rows = settings.custom_rows; vparams[cvmode].charheight = settings.custom_fontheight; textmode(ti.currmode); } init_uifc(TRUE, TRUE); // Draw BBS List uifc.list((*glob_listcount (unsigned)j) scrollback_lines=j; settings.backlines=j; iniSetInteger(&inicontents,"SyncTERM","ScrollBackLines",settings.backlines,&ini_style); } } } else check_exit(FALSE); break; case 5: uifc.helpbuf= "`Modem/Comm Device`\n\n" "Enter the name of the device used to communicate with the modem.\n\n" "Example: \"`" DEFAULT_MODEM_DEV "`\""; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Modem/Comm Device",settings.mdm.device_name,INI_MAX_VALUE_LEN,K_EDIT)>=0) iniSetString(&inicontents,"SyncTERM","ModemDevice",settings.mdm.device_name,&ini_style); else check_exit(FALSE); break; case 6: uifc.helpbuf= "`Modem/Comm Rate`\n\n" "Enter the rate (in `bits-per-second`) used to communicate with the modem.\n" "Use the highest `DTE Rate` supported by your communication port and modem.\n\n" "Examples: `38400`, `57600`, `115200`\n\n" "This rate is sometimes (incorrectly) referred to as the `baud rate`.\n\n" "Enter `0` to use the current or default rate of the communication port"; sprintf(str,"%lu",settings.mdm.com_rate); if(uifc.input(WIN_MID|WIN_SAV,0,0,"Modem/Comm Rate",str,12,K_EDIT)>=0) { settings.mdm.com_rate=strtol(str,NULL,10); iniSetLongInt(&inicontents,"SyncTERM","ModemComRate",settings.mdm.com_rate,&ini_style); } else check_exit(FALSE); break; case 7: uifc.helpbuf= "`Modem Init String`\n\n" "Your modem initialization string goes here.\n\n" "Example:\n" "\"`AT&F`\" will load a Hayes compatible modem's factory default settings.\n\n" "~For reference, here are the expected Hayes-compatible settings:~\n\n" "State Command\n" "----------------------------------\n" "Echo on E1\n" "Verbal result codes Q0V1\n" "Normal CD Handling &C1\n" "Normal DTR &D2\n" "\n\n" "~For reference, here are the expected USRobotics-compatible settings:~\n\n" "State Command\n" "----------------------------------\n" "Include connection speed &X4\n" "Locked speed &B1\n" "CTS/RTS Flow Control &H1&R2\n" "Disable Software Flow &I0\n"; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Modem Init String",settings.mdm.init_string,INI_MAX_VALUE_LEN-1,K_EDIT)>=0) iniSetString(&inicontents,"SyncTERM","ModemInit",settings.mdm.init_string,&ini_style); else check_exit(FALSE); break; case 8: uifc.helpbuf= "`Modem Dial String`\n\n" "The command string to dial the modem goes here.\n\n" "Example: \"`ATDT`\" will dial a Hayes-compatible modem in touch-tone mode."; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Modem Dial String",settings.mdm.dial_string,INI_MAX_VALUE_LEN-1,K_EDIT)>=0) iniSetString(&inicontents,"SyncTERM","ModemDial",settings.mdm.dial_string,&ini_style); else check_exit(FALSE); break; case 9: uifc.helpbuf= "`List Path`\n\n" "The complete path to the BBS list goes here.\n"; if(uifc.input(WIN_MID|WIN_SAV,0,0,"List Path",settings.list_path,MAX_PATH,K_EDIT)>=0) iniSetString(&inicontents,"SyncTERM","ListPath",settings.list_path,&ini_style); else check_exit(FALSE); break; case 10: uifc.helpbuf= "`TERM For Shell`\n\n" "The value to set the TERM envirnonment variable to goes here.\n\n" "Example: \"`ansi`\" will select a dumb ANSI mode."; if(uifc.input(WIN_MID|WIN_SAV,0,0,"TERM",settings.TERM,LIST_NAME_MAX,K_EDIT)>=0) iniSetString(&inicontents,"SyncTERM","TERM",settings.TERM,&ini_style); else check_exit(FALSE); break; case 11: settings.blocky = !settings.blocky; iniSetBool(&inicontents,"SyncTERM","BlockyScaling",settings.blocky,&ini_style); if (settings.blocky) cio_api.options |= CONIO_OPT_BLOCKY_SCALING; else cio_api.options &= ~CONIO_OPT_BLOCKY_SCALING; break; case 12: uifc.helpbuf= "`Custom Screen Mode`\n\n" "~ Rows ~\n" " Sets the number of rows in the custom screen mode\n" "~ Columns ~\n" " Sets the number of columns in the custom screen mode\n" "~ Font Size ~\n" " Chooses the font size used by the custom screen mode\n"; j = 0; for (k=0; k==0;) { // Beware case 2 below if adding things asprintf(&subopts[0], "Rows (%d)", settings.custom_rows); asprintf(&subopts[1], "Columns (%d)", settings.custom_cols); asprintf(&subopts[2], "Font Size (%s)", settings.custom_fontheight == 8 ? "8x8" : settings.custom_fontheight == 14 ? "8x14" : "8x16"); subopts[3] = NULL; switch (uifc.list(WIN_SAV,0,0,0,&j,NULL,"Video Output Mode",subopts)) { case -1: check_exit(FALSE); k = 1; break; case 0: uifc.helpbuf= "`Rows`\n\n" "Number of rows on the custom screen. Must be between 14 and 255"; sprintf(str,"%d",settings.custom_rows); if (uifc.input(WIN_SAV|WIN_MID, 0, 0, "Custom Rows", str, 3, K_NUMBER|K_EDIT)!=-1) { l = atoi(str); if (l < 14 || l > 255) { uifc.msg("Rows must be between 14 and 255."); check_exit(FALSE); } else { settings.custom_rows = l; iniSetInteger(&inicontents, "SyncTERM", "CustomRows", settings.custom_rows, &ini_style); custom_mode_adjusted(&cur, opt); } } break; case 1: uifc.helpbuf= "`Columns`\n\n" "Number of columns on the custom screen. Must be between 40 and 255\n" "Note that values other than 40, 80, and 132 are not supported."; sprintf(str,"%d",settings.custom_cols); if (uifc.input(WIN_SAV|WIN_MID, 0, 0, "Custom Columns", str, 3, K_NUMBER|K_EDIT)!=-1) { l = atoi(str); if (l < 40 || l > 255) { uifc.msg("Columns must be between 40 and 255."); check_exit(FALSE); } else { settings.custom_cols = l; iniSetInteger(&inicontents, "SyncTERM", "CustomColumns", settings.custom_cols, &ini_style); custom_mode_adjusted(&cur, opt); } } break; case 2: uifc.helpbuf= "`Font Size`\n\n" "Choose the font size for the custom mode."; subopts[4] = "8x8"; subopts[5] = "8x14"; subopts[6] = "8x16"; subopts[7] = NULL; switch(settings.custom_fontheight) { case 8: l = 0; break; case 14: l = 1; break; default: l = 2; break; } switch (uifc.list(WIN_SAV, 0, 0, 0, &l, NULL, "Font Size", &subopts[4])) { case -1: check_exit(FALSE); break; case 0: settings.custom_fontheight = 8; iniSetInteger(&inicontents, "SyncTERM", "CustomFontHeight", settings.custom_fontheight, &ini_style); custom_mode_adjusted(&cur, opt); break; case 1: settings.custom_fontheight = 14; iniSetInteger(&inicontents, "SyncTERM", "CustomFontHeight", settings.custom_fontheight, &ini_style); custom_mode_adjusted(&cur, opt); break; case 2: settings.custom_fontheight = 16; iniSetInteger(&inicontents, "SyncTERM", "CustomFontHeight", settings.custom_fontheight, &ini_style); custom_mode_adjusted(&cur, opt); break; } } free(subopts[0]); free(subopts[1]); free(subopts[2]); } } } write_ini: if(!safe_mode) { if((inifile=fopen(inipath,"w"))!=NULL) { iniWriteFile(inifile,inicontents); fclose(inifile); } } strListFree(&inicontents); } void load_bbslist(struct bbslist **list, size_t listsize, struct bbslist *defaults, char *listpath, size_t listpathsize, char *shared_list, size_t shared_listsize, int *listcount, int *cur, int *bar, char *current) { free_list(&list[0],*listcount); *listcount=0; memset(list,0,listsize); memset(defaults,0,sizeof(struct bbslist)); read_list(listpath, list, defaults, listcount, USER_BBSLIST); /* System BBS List */ if(stricmp(shared_list, listpath)) /* don't read the same list twice */ read_list(shared_list, list, defaults, listcount, SYSTEM_BBSLIST); sort_list(list, listcount, cur, bar, current); if(current) free(current); } /* * Note that any time it's drawn, it's inactive... */ static void draw_comment(struct bbslist *list) { int lpad; int rpad; int clen; int remain; char *comment; if (list == NULL) comment = ""; else comment = list->comment; gotoxy(1, uifc.scrn_len); textattr(uifc.lclr|(uifc.cclr<<4)); // Calculator how to centre. clen = strlen(comment); if (clen > uifc.scrn_width - 4) { lpad = 0; rpad = 0; } else { remain = uifc.scrn_width - 4 - clen; rpad = remain / 2 + (remain % 2); lpad = remain - rpad; } cprintf(" %*s%-.*s%*s ", lpad, "", uifc.scrn_width - 4, comment, rpad, ""); } /* * Return value indicates if focus should return to list (true) or move * to settings (false) * * TODO: ESC in edit box doesn't exit program... good or bad? */ static bool edit_comment(struct bbslist *list, char *listpath) { FILE *listfile; str_list_t inifile = NULL; int ch; bool ret = false; char *old; int i; if (list == NULL) goto done; if (safe_mode) goto done; // Open with write permissions so it fails if you can't edit. if ((listfile = fopen(listpath,"r+")) != NULL) { inifile = iniReadFile(listfile); fclose(listfile); } else { goto done; } old = strdup(list->comment); if (!old) goto done; textattr(uifc.lclr|(uifc.bclr<<4)); gotoxy(1, uifc.scrn_len); clreol(); uifc.getstrxy(3, uifc.scrn_len, uifc.scrn_width - 4, list->comment, sizeof(list->comment), K_LINE|K_EDIT|K_NOCRLF|K_TABEXIT|K_MOUSEEXIT|K_TABEXIT, &ch); switch(ch) { case '\x1b': strcpy(list->comment, old); ret = true; goto done; case '\t': ret = false; break; default: ret = true; break; } if (strcmp(old, list->comment)) { iniSetString(&inifile, list->name, "Comment", list->comment, &ini_style); if (list->type==SYSTEM_BBSLIST) { uifc.helpbuf = "`Copy from system directory`\n\n" "This entry was loaded from the system directory. In order to edit it, it\n" "must be copied into your personal directory.\n"; i = 0; if (uifc.list(WIN_MID|WIN_SAV, 0, 0, 0, &i, NULL, "Copy from system directory?", YesNo) != 0) goto done; list->type = USER_BBSLIST; add_bbs(listpath, list); } } done: if (inifile != NULL) { if ((listfile = fopen(listpath,"w")) != NULL) { iniWriteFile(listfile, inifile); fclose(listfile); } strListFree(&inifile); } draw_comment(list); return ret; } /* * Displays the BBS list and allows edits to user BBS list * Mode is one of BBSLIST_SELECT or BBSLIST_EDIT */ struct bbslist *show_bbslist(char *current, int connected) { #define BBSLIST_SIZE ((MAX_OPTS+1)*sizeof(struct bbslist *)) struct bbslist **list; int i,j; static int opt=0,bar=0; int oldopt=-1; int sopt=0,sbar=0; static struct bbslist retlist; int val; int listcount=0; char str[128]; char title[1024]; char *p; char addy[LIST_ADDR_MAX+1]; char *settings_menu[]= { "Default Connection Settings" ,"Current Screen Mode" ,"Font Management" ,"Program Settings" ,NULL }; char *connected_settings_menu[]= { "Default Connection Settings" ,"Font Management" ,"Program Settings" ,NULL }; int at_settings=0; struct mouse_event mevent; struct bbslist defaults; char shared_list[MAX_PATH+1]; char list_title[30]; int redraw = 0; glob_sbar = &sbar; glob_sopt = &sopt; glob_settings_menu = settings_menu; glob_listcount = &listcount; glob_opt = &opt; glob_bar = &bar; glob_list_title = list_title; glob_list = &list; if(init_uifc(connected?FALSE:TRUE, TRUE)) return(NULL); get_syncterm_filename(shared_list, sizeof(shared_list), SYNCTERM_PATH_LIST, TRUE); list = malloc(BBSLIST_SIZE); if (list == NULL) return (NULL); load_bbslist(list, BBSLIST_SIZE, &defaults, settings.list_path, sizeof(settings.list_path), shared_list, sizeof(shared_list), &listcount, &opt, &bar, current?strdup(current):NULL); uifc.helpbuf="Help Button Hack"; uifc.list(WIN_T2B|WIN_RHT|WIN_EXTKEYS|WIN_DYN|WIN_ACT|WIN_INACT ,0,0,0,&sopt,&sbar,"SyncTERM Settings",connected?connected_settings_menu:settings_menu); for(;;) { if (quitting) { free(list); return NULL; } if (!at_settings) { for(;!at_settings;) { sprintf(list_title, "Directory (%d items)", listcount); if (quitting) { free(list); return NULL; } if(connected) uifc.helpbuf= "`SyncTERM Directory`\n\n" "Commands:\n\n" "~ CTRL-E ~ to edit the selected entry\n" "~ CTRL-S ~ to modify the sort order\n" "~ ENTER ~ to connect to the selected entry"; else { uifc.helpbuf= "`SyncTERM Directory`\n\n" "Commands:\n\n" "~ CTRL-D ~ Quick-connect to a URL\n" "~ CTRL-E ~ to edit the selected entry\n" "~ CTRL-S ~ to modify the sort order\n" "~ " ALT_KEY_NAMEP "-B ~ View scrollback of last session\n" "~ ENTER ~ to connect to the selected entry\n\n" "`Conio Keys` (may not work in some modes)\n\n" "~ " ALT_KEY_NAMEP "-Left ~ Snap window size to next smaller horizontal size\n" "~ " ALT_KEY_NAMEP "-Right ~ Snap window size to next larger horizontal size\n" "~ " ALT_KEY_NAMEP "-Up ~ Snap window size to next smaller vertical size\n" "~ " ALT_KEY_NAMEP "-Down ~ Snap window size to next larger vertical size\n" "~ " ALT_KEY_NAMEP "-Enter ~ Toggle fullscreen mode when available\n\n" "`UIFC List Keys`\n\n" "~ CTRL-F ~ find text in current menu options\n" "~ CTRL-G ~ repeat last search\n"; } if(opt != oldopt) { if(list[opt]!=NULL && list[opt]->name[0]) { sprintf(title, "%s - %s (%d calls / Last: %s", syncterm_version, (char *)(list[opt]), list[opt]->calls, list[opt]->connected?ctime(&list[opt]->connected):"Never\n"); p=strrchr(title, '\n'); if(p!=NULL) *p=')'; } else SAFECOPY(title, syncterm_version); settitle(title); } oldopt=opt; uifc.list_height = listcount + 5; if (uifc.list_height > (uifc.scrn_len - 4)) uifc.list_height = uifc.scrn_len - 4; val=uifc.list((listcountname:NULL); break; case -2-0x3000: /* ALT-B - Scrollback */ if(!connected) { viewofflinescroll(); uifc.list(WIN_T2B|WIN_RHT|WIN_EXTKEYS|WIN_DYN|WIN_ACT|WIN_INACT ,0,0,0,&sopt,&sbar,"SyncTERM Settings",settings_menu); } break; case -11: /* TAB */ if (val == -11) { uifc.list((listcount=MAX_OPTS) { uifc.helpbuf= "`Max List size reached`\n\n" "The total combined size of loaded entries is currently the highest\n" "supported size. You must delete entries before adding more."; uifc.msg("Max List size reached!"); check_exit(FALSE); break; } if(safe_mode) { uifc.helpbuf= "`Cannot edit list in safe mode`\n\n" "SyncTERM is currently running in safe mode. This means you cannot add to the\n" "directory."; uifc.msg("Cannot edit list in safe mode"); check_exit(FALSE); break; } tmp[0]=0; uifc.changes=0; uifc.helpbuf= "`Name`\n\n" "Enter the name of the entry as it is to appear in the directory."; if(uifc.input(WIN_MID|WIN_SAV,0,0,"Name",tmp,LIST_NAME_MAX,K_EDIT)==-1) { if (check_exit(FALSE)) break; } if(!uifc.changes) break; if(list_name_check(list, tmp, NULL, FALSE)) { uifc.helpbuf= "`Entry Name Already Exists`\n\n" "An entry with that name already exists in the directory.\n" "Please choose a unique name.\n"; uifc.msg("Entry Name Already Exists!"); check_exit(FALSE); break; } listcount++; list[listcount]=list[listcount-1]; list[listcount-1]=(struct bbslist *)malloc(sizeof(struct bbslist)); memcpy(list[listcount-1],&defaults,sizeof(struct bbslist)); list[listcount-1]->id=listcount-1; strcpy(list[listcount-1]->name,tmp); uifc.changes=0; list[listcount-1]->conn_type--; uifc.helpbuf=conn_type_help; if(uifc.list(WIN_SAV,0,0,0,&(list[listcount-1]->conn_type),NULL,"Connection Type",&(conn_types[1]))>=0) { list[listcount-1]->conn_type++; if(list[listcount-1]->conn_type!=CONN_TYPE_MODEM && list[listcount-1]->conn_type!=CONN_TYPE_SERIAL && list[listcount-1]->conn_type!=CONN_TYPE_SHELL ) { /* Set the port too */ j=conn_ports[list[listcount-1]->conn_type]; if(j<1 || j>65535) j=list[listcount-1]->port; list[listcount-1]->port=j; } uifc.changes=1; } else { if (check_exit(FALSE)) break; } if(uifc.changes) { uifc.changes=0; uifc.helpbuf=address_help; uifc.input(WIN_MID|WIN_SAV,0,0 ,list[listcount-1]->conn_type==CONN_TYPE_MODEM ? "Phone Number" :list[listcount-1]->conn_type==CONN_TYPE_SERIAL ? "Device Name" :list[listcount-1]->conn_type==CONN_TYPE_SHELL ? "Command" :"Address" ,list[listcount-1]->addr,LIST_ADDR_MAX,K_EDIT); check_exit(FALSE); } if(quitting || !uifc.changes) { FREE_AND_NULL(list[listcount-1]); list[listcount-1]=list[listcount]; listcount--; } else { add_bbs(settings.list_path,list[listcount-1]); load_bbslist(list, BBSLIST_SIZE, &defaults, settings.list_path, sizeof(settings.list_path), shared_list, sizeof(shared_list), &listcount, &opt, &bar, strdup(list[listcount-1]->name)); oldopt=-1; } break; case MSK_DEL: if(list[opt]==NULL || !list[opt]->name[0]) { uifc.helpbuf= "`Calming down`\n\n" "~ Some handy tips on calming down ~\n" "Close your eyes, imagine yourself alone on a brilliant white beach...\n" "Picture the palm trees up towards the small town...\n" "Glory in the deep blue of the perfectly clean ocean...\n" "Feel the plush comfort of your beach towel...\n" "Enjoy the shade of your satellite internet feed which envelops\n" "your head, keeping you cool...\n" "Set your TEMPEST rated laptop aside on the beach, knowing it's\n" "completely impervious to anything on the beach...\n" "Reach over to your fridge, grab a cold one...\n" "Watch the seagulls in their dance...\n"; uifc.msg("It's gone, calm down man!"); check_exit(FALSE); break; } if(safe_mode) { uifc.helpbuf= "`Cannot edit list in safe mode`\n\n" "SyncTERM is currently running in safe mode. This means you cannot remove from the\n" "directory."; uifc.msg("Cannot edit list in safe mode"); check_exit(FALSE); break; } if(list[opt]->type==SYSTEM_BBSLIST) { uifc.helpbuf= "`Cannot delete from system list`\n\n" "This entry was loaded from the system-wide list and cannot be deleted."; uifc.msg("Cannot delete system list entries"); check_exit(FALSE); break; } sprintf(str,"Delete %s?",list[opt]->name); i=0; if(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,str,YesNo)!=0) break; del_bbs(settings.list_path,list[opt]); load_bbslist(list, BBSLIST_SIZE, &defaults, settings.list_path, sizeof(settings.list_path), shared_list, sizeof(shared_list), &listcount, NULL, NULL, NULL); oldopt=-1; break; case MSK_EDIT: if(safe_mode) { uifc.helpbuf= "`Cannot edit list in safe mode`\n\n" "SyncTERM is currently running in safe mode. This means you cannot edit the\n" "directory."; uifc.msg("Cannot edit list in safe mode"); check_exit(FALSE); break; } if(edit_list(list, list[opt],settings.list_path,FALSE)) { load_bbslist(list, BBSLIST_SIZE, &defaults, settings.list_path, sizeof(settings.list_path), shared_list, sizeof(shared_list), &listcount, &opt, &bar, strdup(list[opt]->name)); oldopt=-1; } break; } } else { if(connected) { if(safe_mode) { uifc.helpbuf= "`Cannot edit list in safe mode`\n\n" "SyncTERM is currently running in safe mode. This means you cannot edit the\n" "directory."; uifc.msg("Cannot edit list in safe mode"); check_exit(FALSE); } else if(edit_list(list, list[opt],settings.list_path,FALSE)) { load_bbslist(list, BBSLIST_SIZE, &defaults, settings.list_path, sizeof(settings.list_path), shared_list, sizeof(shared_list), &listcount, &opt, &bar, strdup(list[opt]->name)); oldopt=-1; } } else { memcpy(&retlist,list[val],sizeof(struct bbslist)); free_list(&list[0],listcount); free(list); return(&retlist); } } } } else { for(;at_settings && !quitting;) { uifc.helpbuf= "`SyncTERM Settings Menu`\n\n" "~ Default Connection Settings ~\n" " Modify the settings that are used by default for new entries\n\n" "~ Current Screen Mode ~\n" " Set the current screen size/mode\n\n" "~ Font Management ~\n" " Configure additional font files\n\n" "~ Program Settings ~\n" " Modify hardware and screen/video settings\n\n" "~ " ALT_KEY_NAMEP "-B ~\n" " View scrollback of last session\n"; if(oldopt != -2) settitle(syncterm_version); oldopt=-2; val=uifc.list(WIN_T2B|WIN_RHT|WIN_EXTKEYS|WIN_DYN|WIN_UNGETMOUSE|WIN_ACT|WIN_ESC ,0,0,0,&sopt,&sbar,"SyncTERM Settings",connected?connected_settings_menu:settings_menu); if(connected && val >= 1) val++; switch(val) { case -2-0x3000: /* ALT-B - Scrollback */ if(!connected) { viewofflinescroll(); uifc.list((listcount=0) { i++; uifcbail(); textmode(screen_to_ciolib(i)); init_uifc(TRUE, TRUE); uifc.list((listcountname):NULL); oldopt=-1; break; } } } } } cterm_emulation_t get_emulation(struct bbslist *bbs) { if (bbs == NULL) return CTERM_EMULATION_ANSI_BBS; switch(bbs->screen_mode) { case SCREEN_MODE_C64: case SCREEN_MODE_C128_40: case SCREEN_MODE_C128_80: return CTERM_EMULATION_PETASCII; case SCREEN_MODE_ATARI: case SCREEN_MODE_ATARI_XEP80: return CTERM_EMULATION_ATASCII; default: return CTERM_EMULATION_ANSI_BBS; } } const char * get_emulation_str(cterm_emulation_t emu) { switch (emu) { case CTERM_EMULATION_ANSI_BBS: return "syncterm"; case CTERM_EMULATION_PETASCII: return "PETSCII"; case CTERM_EMULATION_ATASCII: return "ATASCII"; } return "none"; } void get_term_size(struct bbslist *bbs, int *cols, int *rows) { int cmode = find_vmode(screen_to_ciolib(bbs->screen_mode)); if (cmode < 0) { // This shouldn't happen, but if it does, make something up. *cols = 80; *rows = 24; return; } if(vparams[cmode].cols < 80) { if (cols) *cols=40; } else { if(vparams[cmode].cols < 132) { if (cols) *cols=80; } else { if (cols) *cols=132; } } if (rows) { *rows=vparams[cmode].rows; if(!bbs->nostatus) (*rows)--; if(*rows<24) *rows=24; } }